feat(admin): Add user management and upgrade to module permission system
Features - User Management (Phase 4.1): - Database: Add user_modules table for fine-grained module permissions - Database: Add 4 user permissions (view/create/edit/delete) to role_permissions - Backend: UserService (780 lines) - CRUD with tenant isolation - Backend: UserController + UserRoutes (648 lines) - 13 API endpoints - Backend: Batch import users from Excel - Frontend: UserListPage (412 lines) - list/filter/search/pagination - Frontend: UserFormPage (341 lines) - create/edit with module config - Frontend: UserDetailPage (393 lines) - details/tenant/module management - Frontend: 3 modal components (592 lines) - import/assign/configure - API: GET/POST/PUT/DELETE /api/admin/users/* endpoints Architecture Upgrade - Module Permission System: - Backend: Add getUserModules() method in auth.service - Backend: Login API returns modules array in user object - Frontend: AuthContext adds hasModule() method - Frontend: Navigation filters modules based on user.modules - Frontend: RouteGuard checks requiredModule instead of requiredVersion - Frontend: Remove deprecated version-based permission system - UX: Only show accessible modules in navigation (clean UI) - UX: Smart redirect after login (avoid 403 for regular users) Fixes: - Fix UTF-8 encoding corruption in ~100 docs files - Fix pageSize type conversion in userService (String to Number) - Fix authUser undefined error in TopNavigation - Fix login redirect logic with role-based access check - Update Git commit guidelines v1.2 with UTF-8 safety rules Database Changes: - CREATE TABLE user_modules (user_id, tenant_id, module_code, is_enabled) - ADD UNIQUE CONSTRAINT (user_id, tenant_id, module_code) - INSERT 4 permissions + role assignments - UPDATE PUBLIC tenant with 8 module subscriptions Technical: - Backend: 5 new files (~2400 lines) - Frontend: 10 new files (~2500 lines) - Docs: 1 development record + 2 status updates + 1 guideline update - Total: ~4900 lines of code Status: User management 100% complete, module permission system operational
This commit is contained in:
@@ -1,31 +1,31 @@
|
||||
# Dify鐭ヨ瘑搴撻泦鎴愬紑鍙戣<EFBFBD>褰?
|
||||
# Dify知识库集成开发记录
|
||||
|
||||
**寮€鍙戞棩鏈?*: 2026-01-04
|
||||
**寮€鍙戦樁娈?*: Phase 1.5 - AI瀵硅瘽鑳藉姏
|
||||
**浠诲姟**: 闆嗘垚Dify鐭ヨ瘑搴撳疄鐜扮爺绌舵柟妗堟枃妗f煡璇?
|
||||
**鐘舵€?*: 鉁?宸插畬鎴?
|
||||
**开发日期**: 2026-01-04
|
||||
**开发阶段**: Phase 1.5 - AI对话能力
|
||||
**任务**: 集成Dify知识库实现研究方案文档查询
|
||||
**状态**: ✅ 已完成
|
||||
|
||||
---
|
||||
|
||||
## 馃搵 寮€鍙戠洰鏍?
|
||||
## 📋 开发目标
|
||||
|
||||
鍦↖IT Manager Agent涓<EFBFBD>泦鎴怐ify鐭ヨ瘑搴撹兘鍔涳紝浣緼I鑳藉<EFBFBD>鏌ヨ<EFBFBD>鐮旂┒鏂规<EFBFBD>銆佷鸡鐞嗘枃浠躲€丆RF琛ㄦ牸绛夋枃妗o紝骞朵笌宸叉湁鐨凴EDCap瀹炴椂鏁版嵁鏌ヨ<EFBFBD>鑳藉姏缁撳悎锛屽疄鐜?*娣峰悎妫€绱<E282AC>紙Hybrid Retrieval锛?*銆?
|
||||
在IIT Manager Agent中集成Dify知识库能力,使AI能够查询研究方案、伦理文件、CRF表格等文档,并与已有的REDCap实时数据查询能力结合,实现**混合检索(Hybrid Retrieval)**。
|
||||
|
||||
## 馃幆 鎶€鏈<E282AC>柟妗?
|
||||
## 🎯 技术方案
|
||||
|
||||
### 方案选择
|
||||
|
||||
| 缁村害 | 鏂规<E98F82>A锛氬崟椤圭洰鍗曠煡璇嗗簱 | 鏂规<E98F82>B锛氶」鐩<E3808D>垎绫诲<E7BBAB>鐭ヨ瘑搴?|
|
||||
| 维度 | 方案A:单项目单知识库 | 方案B:项目分类多知识库 |
|
||||
|------|---------------------|---------------------|
|
||||
| **鐭ヨ瘑搴撴暟閲?* | 1涓狪IT椤圭洰 鈫?1涓狣ify Dataset | 1涓狪IT椤圭洰 鈫?澶氫釜Dataset锛堟柟妗堛€佷鸡鐞嗐€丆RF锛?|
|
||||
| **澶嶆潅搴?* | 鉁?绠€鍗?| 鉂?澶嶆潅 |
|
||||
| **MVP閫傜敤鎬?* | 鉁?楂?| 鉂?浣?|
|
||||
| **閫夋嫨** | **鉁?閲囩敤** | 鉂?鏆備笉閲囩敤 |
|
||||
| **知识库数量** | 1个IIT项目 → 1个Dify Dataset | 1个IIT项目 → 多个Dataset(方案、伦理、CRF) |
|
||||
| **复杂度** | ✅ 简单 | ❌ 复杂 |
|
||||
| **MVP适用性** | ✅ 高 | ❌ 低 |
|
||||
| **选择** | **✅ 采用** | ❌ 暂不采用 |
|
||||
|
||||
### 文档上传方式
|
||||
|
||||
- **采用方案**: 通过Dify Web界面手动上传
|
||||
- **鍘熷洜**: MVP闃舵<EFBFBD>鏂囨。鏇存柊棰戠巼浣庯紝鎵嬪姩涓婁紶鏇寸伒娲?
|
||||
- **原因**: MVP阶段文档更新频率低,手动上传更灵活
|
||||
- **未来优化**: 后续可开发API自动上传能力
|
||||
|
||||
### 项目关联方式
|
||||
@@ -35,7 +35,7 @@
|
||||
|
||||
---
|
||||
|
||||
## 馃洜锔?鎶€鏈<E282AC>疄鐜?
|
||||
## 🛠️ 技术实现
|
||||
|
||||
### 1. 数据库Schema验证
|
||||
|
||||
@@ -57,14 +57,14 @@ model IitProject {
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 鍒涘缓Dify鐭ヨ瘑搴?
|
||||
### 2. 创建Dify知识库
|
||||
|
||||
**操作步骤**:
|
||||
1. 鐧诲綍Dify鎺у埗鍙?
|
||||
1. 登录Dify控制台
|
||||
2. 创建知识库:`Dify_test0102`
|
||||
3. 涓婁紶鏂囨。锛?
|
||||
3. 上传文档:
|
||||
- `新生儿及婴儿胆汁淤积症中西医协同队列研究方案1210-.docx`
|
||||
- `閲嶅ぇ鐤戦毦-鐥呬緥鎶ュ憡琛<E686A1>紙CRF锛変慨鏀?208.docx`
|
||||
- `重大疑难-病例报告表(CRF)修改1208.docx`
|
||||
4. 等待文档处理完成
|
||||
|
||||
**Dataset ID**: `b49595b2-bf71-4e47-9988-4aa2816d3c6f`
|
||||
@@ -115,14 +115,14 @@ private detectIntent(message: string): {
|
||||
```typescript
|
||||
private async queryDifyKnowledge(query: string): Promise<string> {
|
||||
try {
|
||||
// 1. 鑾峰彇椤圭洰閰嶇疆锛堝寘鍚玠ifyDatasetId锛?
|
||||
// 1. 获取项目配置(包含difyDatasetId)
|
||||
const project = await prisma.iitProject.findFirst({
|
||||
where: { status: 'active' },
|
||||
select: { name: true, difyDatasetId: true }
|
||||
});
|
||||
|
||||
if (!project?.difyDatasetId) {
|
||||
logger.warn('[ChatService] 椤圭洰鏈<EFBFBD>厤缃瓺ify鐭ヨ瘑搴?);
|
||||
logger.warn('[ChatService] 项目未配置Dify知识库');
|
||||
return '';
|
||||
}
|
||||
|
||||
@@ -138,7 +138,7 @@ private async queryDifyKnowledge(query: string): Promise<string> {
|
||||
}
|
||||
);
|
||||
|
||||
// 3. 鏍煎紡鍖栨<EFBFBD>绱㈢粨鏋?
|
||||
// 3. 格式化检索结果
|
||||
if (!retrievalResult.records || retrievalResult.records.length === 0) {
|
||||
return '';
|
||||
}
|
||||
@@ -148,20 +148,20 @@ private async queryDifyKnowledge(query: string): Promise<string> {
|
||||
const score = (record.score * 100).toFixed(1);
|
||||
const documentName = record.segment?.document?.name || '未知文档';
|
||||
const content = record.segment?.content || '';
|
||||
formattedKnowledge += `\n[鏂囨。${index + 1}] ${documentName} (鐩稿叧搴? ${score}%)\n`;
|
||||
formattedKnowledge += `\n[文档${index + 1}] ${documentName} (相关度: ${score}%)\n`;
|
||||
formattedKnowledge += `${content}\n`;
|
||||
formattedKnowledge += `---\n`;
|
||||
});
|
||||
|
||||
return formattedKnowledge;
|
||||
} catch (error: any) {
|
||||
logger.error('[ChatService] Dify妫€绱㈠け璐?, { query, error: error.message });
|
||||
return `銆愮煡璇嗗簱鏌ヨ<EFBFBD>澶辫触銆? ${error.message}`;
|
||||
logger.error('[ChatService] Dify检索失败', { query, error: error.message });
|
||||
return `【知识库查询失败】: ${error.message}`;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### (3) 鏇存柊涓诲<EFBFBD>璇濇祦绋?
|
||||
#### (3) 更新主对话流程
|
||||
|
||||
```typescript
|
||||
async handleMessage(userId: string, userMessage: string): Promise<string> {
|
||||
@@ -180,16 +180,16 @@ async handleMessage(userId: string, userMessage: string): Promise<string> {
|
||||
toolResult = await this.countRedcapRecords();
|
||||
}
|
||||
|
||||
// 4. 濡傛灉闇€瑕佹煡璇㈡枃妗o紙Dify鐭ヨ瘑搴擄級锛屾墽琛屾<EFBFBD>绱?
|
||||
// 4. 如果需要查询文档(Dify知识库),执行检索
|
||||
let difyKnowledge: string = '';
|
||||
if (intent === 'query_protocol') {
|
||||
difyKnowledge = await this.queryDifyKnowledge(userMessage);
|
||||
}
|
||||
|
||||
// 5. 鑾峰彇涓婁笅鏂囷紙鏈€杩?杞<><E69D9E>璇濓級
|
||||
// 5. 获取上下文(最近2轮对话)
|
||||
const context = sessionMemory.getContext(userId);
|
||||
|
||||
// 6. 鏋勫缓LLM娑堟伅锛堝寘鍚<EFBFBD>煡璇㈢粨鏋?+ Dify鐭ヨ瘑搴擄級
|
||||
// 6. 构建LLM消息(包含查询结果 + Dify知识库)
|
||||
const messages = this.buildMessagesWithData(
|
||||
userMessage,
|
||||
context,
|
||||
@@ -222,7 +222,7 @@ private buildMessagesWithData(
|
||||
}
|
||||
];
|
||||
|
||||
// 娣诲姞鍘嗗彶涓婁笅鏂囷紙鏈€杩?杞<>級
|
||||
// 添加历史上下文(最近2轮)
|
||||
if (context?.length > 0) {
|
||||
messages.push(...context);
|
||||
}
|
||||
@@ -235,7 +235,7 @@ private buildMessagesWithData(
|
||||
currentUserMessage += `\n\n## 📊 REDCap查询结果\n${JSON.stringify(toolResult, null, 2)}`;
|
||||
}
|
||||
|
||||
// 娉ㄥ叆Dify鐭ヨ瘑搴撳唴瀹?
|
||||
// 注入Dify知识库内容
|
||||
if (difyKnowledge) {
|
||||
currentUserMessage += `\n\n## 📚 知识库相关文档\n${difyKnowledge}`;
|
||||
}
|
||||
@@ -253,40 +253,40 @@ private buildMessagesWithData(
|
||||
|
||||
```typescript
|
||||
private getSystemPromptWithData(): string {
|
||||
return `浣犳槸IIT Manager Agent锛屼竴涓<EFBFBD>笓涓氱殑鐮旂┒鑰呬复搴婅瘯楠屽姪鎵嬨€?
|
||||
return `你是IIT Manager Agent,一个专业的研究者临床试验助手。
|
||||
|
||||
銆愭牳蹇冭兘鍔涖€?
|
||||
- **瀹炴椂鏁版嵁鏌ヨ<EFBFBD>**锛氶€氳繃REDCap API鏌ヨ<E98F8C>鎮h€匔RF鏁版嵁锛堝叆缁勩€佽<E282AC>瑙嗐€佷笉鑹<E7AC89>簨浠剁瓑锛?
|
||||
- **鐮旂┒鏂规<EFBFBD>鏌ヨ<EFBFBD>**锛氶€氳繃Dify鐭ヨ瘑搴撴<E690B4>绱㈢爺绌舵柟妗堛€佷鸡鐞嗘枃浠躲€丆RF琛ㄦ牸绛夋枃妗?
|
||||
【核心能力】
|
||||
- **实时数据查询**:通过REDCap API查询患者CRF数据(入组、访视、不良事件等)
|
||||
- **研究方案查询**:通过Dify知识库检索研究方案、伦理文件、CRF表格等文档
|
||||
|
||||
銆愬叧閿<EFBFBD>師鍒欍€?
|
||||
1. **鏁版嵁鐪熷疄鎬х<EFBFBD>涓€**锛氭墍鏈夊洖绛斿繀椤诲熀浜庣郴缁熸彁渚涚殑鐪熷疄鏁版嵁锛圧EDCap鎴朌ify锛夛紝缁濅笉缂栭€犳暟鎹?
|
||||
【关键原则】
|
||||
1. **数据真实性第一**:所有回答必须基于系统提供的真实数据(REDCap或Dify),绝不编造数据
|
||||
2. **明确数据来源**:区分REDCap实时数据和文档知识库
|
||||
3. **专业严谨**:使用临床研究术语,保持客观准确
|
||||
4. **绠€娲侀珮鏁?*锛氫紒涓氬井淇″満鏅<E6BA80>紝鎺у埗鍥炲<E98DA5>闀垮害
|
||||
4. **简洁高效**:企业微信场景,控制回复长度
|
||||
|
||||
銆愭暟鎹<EFBFBD>幏鍙栬<EFBFBD>鍒欍€?
|
||||
- 濡傛灉绯荤粺鎻愪緵浜?馃搳 REDCap鏌ヨ<E98F8C>缁撴灉"锛屽繀椤诲熀浜庤<E6B59C>鏁版嵁鍥炵瓟
|
||||
- 濡傛灉绯荤粺鎻愪緵浜?馃摎 鐭ヨ瘑搴撶浉鍏虫枃妗?锛屽繀椤诲熀浜庤<E6B59C>鏂囨。鍥炵瓟
|
||||
- 濡傛灉鏈<EFBFBD>彁渚涙暟鎹<EFBFBD>紝鏄庣‘鍛婄煡鐢ㄦ埛"鏈<>煡璇㈠埌鐩稿叧鏁版嵁"锛屼笉寰楃紪閫?
|
||||
【数据获取规则】
|
||||
- 如果系统提供了"📊 REDCap查询结果",必须基于该数据回答
|
||||
- 如果系统提供了"📚 知识库相关文档",必须基于该文档回答
|
||||
- 如果未提供数据,明确告知用户"未查询到相关数据",不得编造
|
||||
`;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 馃悰 闂<><E99782>鎺掓煡涓庤В鍐?
|
||||
## 🐛 问题排查与解决
|
||||
|
||||
### 闂<EFBFBD><EFBFBD>1: AI涓嶆煡璇<EFBFBD>ify锛岃嚜宸辩紪閫犵瓟妗?
|
||||
### 问题1: AI不查询Dify,自己编造答案
|
||||
|
||||
**现象**:
|
||||
- 鐢ㄦ埛鍦ㄤ紒涓氬井淇¢棶锛?杩欎釜鐮旂┒鐨勭撼鍏ユ爣鍑嗘槸浠€涔堬紵"
|
||||
- AI鍥炵瓟浜嗚矊浼煎悎鐞嗙殑鍐呭<EFBFBD>锛屼絾Dify鎺у埗鍙版樉绀?*娌℃湁鏌ヨ<E98F8C>璁板綍**
|
||||
- AI鏄庢樉鍦ㄧ紪閫狅紙Hallucination锛?
|
||||
- 用户在企业微信问:"这个研究的纳入标准是什么?"
|
||||
- AI回答了貌似合理的内容,但Dify控制台显示**没有查询记录**
|
||||
- AI明显在编造(Hallucination)
|
||||
|
||||
**排查过程**:
|
||||
|
||||
#### 绗<EFBFBD>竴姝ワ細妫€鏌ユ剰鍥捐瘑鍒?
|
||||
#### 第一步:检查意图识别
|
||||
|
||||
怀疑:`detectIntent`方法没有识别出`query_protocol`意图
|
||||
|
||||
@@ -298,11 +298,11 @@ if (/(研究方案|伦理|知情同意|CRF|病例报告表|纳入|排除|标准)
|
||||
}
|
||||
```
|
||||
|
||||
**鍙戠幇**: 鍏抽敭璇嶅垪琛ㄤ腑鏈?绾冲叆"鍜?鏍囧噯"锛屼絾缂哄皯"**鍏ラ€?*"锛?
|
||||
**发现**: 关键词列表中有"纳入"和"标准",但缺少"**入选**"!
|
||||
|
||||
鐢ㄦ埛闂<EFBFBD>殑鏄?绾冲叆鏍囧噯"锛屼絾瀹為檯鏂囨。涓<E38082>洿澶氫娇鐢?鍏ラ€夋爣鍑?鐨勮〃杩般€?
|
||||
用户问的是"纳入标准",但实际文档中更多使用"入选标准"的表述。
|
||||
|
||||
**瑙e喅**: 鎵╁厖鍏抽敭璇嶅垪琛?
|
||||
**解决**: 扩充关键词列表
|
||||
|
||||
```typescript
|
||||
if (/(研究方案|伦理|知情同意|CRF|病例报告表|纳入|入选|排除|标准|入组标准|治疗方案|试验设计|研究目的|研究流程|观察指标|诊断标准|疾病标准)/.test(message)) {
|
||||
@@ -316,10 +316,10 @@ if (/(研究方案|伦理|知情同意|CRF|病例报告表|纳入|入选|排除|
|
||||
|
||||
**目的**: 追踪Dify检索结果是否正确注入到LLM
|
||||
|
||||
**鍙戠幇**: Dify纭<EFBFBD>疄琚<EFBFBD>皟鐢ㄤ簡锛屼絾杩斿洖鐨勫唴瀹规槸`undefined`锛?
|
||||
**发现**: Dify确实被调用了,但返回的内容是`undefined`!
|
||||
|
||||
```
|
||||
[鏂囨。1] undefined (鐩稿叧搴? 76.0%)
|
||||
[文档1] undefined (相关度: 76.0%)
|
||||
undefined
|
||||
---
|
||||
```
|
||||
@@ -334,9 +334,9 @@ undefined
|
||||
"records": [
|
||||
{
|
||||
"segment": {
|
||||
"content": "绾冲叆涓庢帓闄ゆ爣鍑?..",
|
||||
"content": "纳入与排除标准...",
|
||||
"document": {
|
||||
"name": "閲嶅ぇ鐤戦毦-鐥呬緥鎶ュ憡琛<E686A1>紙CRF锛変慨鏀?208.docx"
|
||||
"name": "重大疑难-病例报告表(CRF)修改1208.docx"
|
||||
}
|
||||
},
|
||||
"score": 0.7604317
|
||||
@@ -348,11 +348,11 @@ undefined
|
||||
**问题根因**: ChatService中使用了错误的字段路径!
|
||||
|
||||
```typescript
|
||||
// 鉂?閿欒<E996BF>鐨勮<E990A8>闂<EFBFBD>柟寮?
|
||||
// ❌ 错误的访问方式
|
||||
const documentName = record.document_name; // undefined
|
||||
const content = record.content; // undefined
|
||||
|
||||
// 鉁?姝g‘鐨勮<E990A8>闂<EFBFBD>柟寮?
|
||||
// ✅ 正确的访问方式
|
||||
const documentName = record.segment?.document?.name;
|
||||
const content = record.segment?.content;
|
||||
```
|
||||
@@ -364,7 +364,7 @@ retrievalResult.records.forEach((record, index) => {
|
||||
const score = (record.score * 100).toFixed(1);
|
||||
const documentName = record.segment?.document?.name || '未知文档';
|
||||
const content = record.segment?.content || '';
|
||||
formattedKnowledge += `\n[鏂囨。${index + 1}] ${documentName} (鐩稿叧搴? ${score}%)\n`;
|
||||
formattedKnowledge += `\n[文档${index + 1}] ${documentName} (相关度: ${score}%)\n`;
|
||||
formattedKnowledge += `${content}\n`;
|
||||
formattedKnowledge += `---\n`;
|
||||
});
|
||||
@@ -374,12 +374,12 @@ retrievalResult.records.forEach((record, index) => {
|
||||
|
||||
| 问题 | 根因 | 解决方案 |
|
||||
|------|------|---------|
|
||||
| AI缂栭€犵瓟妗?| 鎰忓浘璇嗗埆鍏抽敭璇嶄笉鍏?| 鎵╁厖`detectIntent`鍏抽敭璇嶅垪琛<E59EAA>紝娣诲姞"鍏ラ€?绛夊尰瀛︽湳璇?|
|
||||
| AI编造答案 | 意图识别关键词不全 | 扩充`detectIntent`关键词列表,添加"入选"等医学术语 |
|
||||
| Dify内容为undefined | 错误的API响应字段路径 | 修正为`record.segment.document.name`和`record.segment.content` |
|
||||
|
||||
---
|
||||
|
||||
## 鉁?娴嬭瘯楠岃瘉
|
||||
## ✅ 测试验证
|
||||
|
||||
### 测试1: 研究方案查询(Dify检索)
|
||||
|
||||
@@ -389,56 +389,56 @@ retrievalResult.records.forEach((record, index) => {
|
||||
```
|
||||
根据研究方案文档,本研究的排除标准包括:
|
||||
|
||||
1. 骞撮緞灏忎簬18宀佹垨澶т簬75宀?
|
||||
1. 年龄小于18岁或大于75岁
|
||||
2. 合并其他严重器官功能障碍
|
||||
3. 既往有相关药物过敏史
|
||||
4. 妊娠或哺乳期妇女
|
||||
5. 杩?涓<>湀鍐呭弬涓庤繃鍏朵粬涓村簥璇曢獙
|
||||
5. 近3个月内参与过其他临床试验
|
||||
|
||||
濡傞渶鏌ョ湅瀹屾暣鎺掗櫎鏍囧噯鏉℃<EFBFBD>锛屽缓璁<EFBFBD>偍鐧诲綍绯荤粺鏌ラ槄璇︾粏鐮旂┒鏂规<EFBFBD>鏂囨。銆?
|
||||
如需查看完整排除标准条款,建议您登录系统查阅详细研究方案文档。
|
||||
```
|
||||
|
||||
**日志验证**:
|
||||
```
|
||||
[ChatService] 意图识别 { intent: 'query_protocol' }
|
||||
[ChatService] Dify妫€绱㈡垚鍔?{ recordCount: 5 }
|
||||
[ChatService] Dify检索成功 { recordCount: 5 }
|
||||
[ChatService] 处理消息 { hasDifyKnowledge: true }
|
||||
```
|
||||
|
||||
鉁?**缁撴灉**: 鎴愬姛璇嗗埆鎰忓浘锛岃皟鐢―ify锛岃繑鍥炴<EFBFBD>纭<EFBFBD>瓟妗?
|
||||
✅ **结果**: 成功识别意图,调用Dify,返回正确答案
|
||||
|
||||
### 测试2: CRF表格查询(Dify检索)
|
||||
|
||||
**闂<EFBFBD><EFBFBD>**: "CRF琛ㄦ牸涓<EFBFBD>湁鍝<EFBFBD>簺瑙傚療鎸囨爣锛?
|
||||
**问题**: "CRF表格中有哪些观察指标?"
|
||||
|
||||
**AI回答**:
|
||||
```
|
||||
鏍规嵁鐮旂┒鏂规<EFBFBD>鏂囨。锛孋RF琛ㄦ牸涓<EFBFBD>殑瑙傚療鎸囨爣鍖呮嫭锛?
|
||||
根据研究方案文档,CRF表格中的观察指标包括:
|
||||
|
||||
1. 鍩虹嚎浜哄彛瀛﹁祫鏂?
|
||||
2. 涓昏<EFBFBD>鐤楁晥鎸囨爣锛堝<EFBFBD>鐥囩姸璇勫垎锛?
|
||||
3. 娆¤<EFBFBD>鐤楁晥鎸囨爣锛堝<EFBFBD>鐢熸椿璐ㄩ噺璇勫垎锛?
|
||||
4. 瀹夊叏鎬ф寚鏍囷紙涓嶈壇浜嬩欢璁板綍锛?
|
||||
5. 瀹為獙瀹ゆ<EFBFBD>鏌ョ粨鏋?
|
||||
1. 基线人口学资料
|
||||
2. 主要疗效指标(如症状评分)
|
||||
3. 次要疗效指标(如生活质量评分)
|
||||
4. 安全性指标(不良事件记录)
|
||||
5. 实验室检查结果
|
||||
|
||||
鍏蜂綋鎸囨爣瀹氫箟鍜岄噰闆嗘椂鐐硅<EFBFBD>鏌ラ槄璇︾粏CRF琛ㄦ牸鏂囨。銆?
|
||||
具体指标定义和采集时点请查阅详细CRF表格文档。
|
||||
```
|
||||
|
||||
鉁?**缁撴灉**: 鎴愬姛妫€绱<EFBFBD>RF鏂囨。
|
||||
✅ **结果**: 成功检索CRF文档
|
||||
|
||||
### 娴嬭瘯3: 鎮h€呰<E282AC>褰曟煡璇<E785A1>紙REDCap鏌ヨ<EFBFBD>锛?
|
||||
### 测试3: 患者记录查询(REDCap查询)
|
||||
|
||||
**闂<EFBFBD><EFBFBD>**: "ID 7鐨勬偅鑰呮儏鍐?
|
||||
**问题**: "ID 7的患者情况"
|
||||
|
||||
**AI回答**:
|
||||
```
|
||||
根据REDCap数据,ID 7的患者情况如下:
|
||||
|
||||
**鍩烘湰淇℃伅**锛氫紒涓氬井淇?锛岀敺鎬э紝8宀侊紝韬<E7B49D>珮130cm锛屼綋閲?0kg锛孊MI 29.6銆?
|
||||
**鑱旂郴鏂瑰紡**锛氶偖绠眊ofeng117@163.com銆?
|
||||
**鐮旂┒鐘舵€?*锛氭不鐤楀<E990A4>璁板綍涓?娴嬭瘯7777"锛孌DCD琛ㄥ崟宸插畬鎴愩€?
|
||||
**基本信息**:企业微信7,男性,8岁,身高130cm,体重50kg,BMI 29.6。
|
||||
**联系方式**:邮箱gofeng117@163.com。
|
||||
**研究状态**:治疗室记录为"测试7777",DDCD表单已完成。
|
||||
|
||||
濡傞渶鏌ョ湅瀹屾暣璁板綍鎴栨洿澶氳<EFBFBD>瑙嗘暟鎹<EFBFBD>紝寤鸿<EFBFBD>鎮ㄧ櫥褰昍EDCap绯荤粺銆?
|
||||
如需查看完整记录或更多访视数据,建议您登录REDCap系统。
|
||||
```
|
||||
|
||||
**日志验证**:
|
||||
@@ -448,20 +448,20 @@ REDCap API: exportRecords success { recordCount: 1 }
|
||||
[ChatService] 处理消息 { hasToolResult: true, hasDifyKnowledge: false }
|
||||
```
|
||||
|
||||
鉁?**缁撴灉**: 鎴愬姛璇嗗埆涓篟EDCap鏌ヨ<EFBFBD>锛岃繑鍥炲疄鏃舵暟鎹?
|
||||
✅ **结果**: 成功识别为REDCap查询,返回实时数据
|
||||
|
||||
### 娴嬭瘯4: 缁熻<E7BC81>鏌ヨ<E98F8C>锛圧EDCap鏌ヨ<E98F8C>锛?
|
||||
### 测试4: 统计查询(REDCap查询)
|
||||
|
||||
**闂<EFBFBD><EFBFBD>**: "鐩<EFBFBD>墠鍏ョ粍浜嗗<EFBFBD>灏戜汉锛?
|
||||
**问题**: "目前入组了多少人?"
|
||||
|
||||
**AI回答**:
|
||||
```
|
||||
鏍规嵁REDCap瀹炴椂鏁版嵁锛?*test0102**椤圭洰鐩<EFBFBD>墠鍏辨湁 **11浣?* 鎮h€呭叆缁勩€?
|
||||
根据REDCap实时数据,**test0102**项目目前共有 **11位** 患者入组。
|
||||
|
||||
璁板綍ID涓?鑷?1銆傚<E98A86>闇€鏌ョ湅鎮h€呰<E282AC>鎯咃紝寤鸿<E5AFA4>鎮ㄧ櫥褰昍EDCap绯荤粺銆?
|
||||
记录ID为1至11。如需查看患者详情,建议您登录REDCap系统。
|
||||
```
|
||||
|
||||
鉁?**缁撴灉**: 鎴愬姛缁熻<EFBFBD>鍏ョ粍浜烘暟
|
||||
✅ **结果**: 成功统计入组人数
|
||||
|
||||
### 测试5: 混合查询(Dify检索)
|
||||
|
||||
@@ -469,12 +469,12 @@ REDCap API: exportRecords success { recordCount: 1 }
|
||||
|
||||
**AI回答**:
|
||||
```
|
||||
鏍规嵁鐮旂┒鏂规<EFBFBD>鏂囨。锛屾湰鐮旂┒鐨?*涓昏<E6B693>鐮旂┒鐩<E29492>殑**鏄<>瘎浼癧鍏蜂綋骞查<E9AA9E>鎺<EFBFBD>柦]鍦╗鐩<E29597>爣浜虹兢]涓<>殑鏈夋晥鎬у拰瀹夊叏鎬с€?
|
||||
根据研究方案文档,本研究的**主要研究目的**是评估[具体干预措施]在[目标人群]中的有效性和安全性。
|
||||
|
||||
鐢变簬褰撳墠妫€绱㈠埌鐨勬枃妗e唴瀹逛笉瀹屾暣锛屾棤娉曟彁渚涙洿璇︾粏鐨勫叿浣撴弿杩般€傚缓璁<EFBFBD>偍鏌ラ槄瀹屾暣鐨勭爺绌舵柟妗堟枃妗d互鑾峰彇鍑嗙‘淇℃伅銆?
|
||||
由于当前检索到的文档内容不完整,无法提供更详细的具体描述。建议您查阅完整的研究方案文档以获取准确信息。
|
||||
```
|
||||
|
||||
鉁?**缁撴灉**: 鎴愬姛妫€绱<EFBFBD>紝AI鑳借瘑鍒<EFBFBD>俊鎭<EFBFBD>笉瀹屾暣鐨勬儏鍐?
|
||||
✅ **结果**: 成功检索,AI能识别信息不完整的情况
|
||||
|
||||
---
|
||||
|
||||
@@ -484,62 +484,62 @@ REDCap API: exportRecords success { recordCount: 1 }
|
||||
|
||||
```
|
||||
用户提问(企业微信)
|
||||
鈫?
|
||||
鎰忓浘璇嗗埆锛坉etectIntent锛?
|
||||
鈫?
|
||||
鈹屸攢鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹<EFBFBD>攢鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹<EFBFBD>攢鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹?
|
||||
鈹?query_protocol鈹?query_record 鈹?count_records鈹?
|
||||
鈹? (鏂囨。鏌ヨ<E98F8C>) 鈹? (璁板綍鏌ヨ<E98F8C>) 鈹? (缁熻<E7BC81>鏌ヨ<E98F8C>) 鈹?
|
||||
鈹斺攢鈹€鈹€鈹€鈹€鈹€鈹€鈹<EFBFBD>攢鈹€鈹€鈹€鈹€鈹€鈹€鈹粹攢鈹€鈹€鈹€鈹€鈹€鈹€鈹<EFBFBD>攢鈹€鈹€鈹€鈹€鈹€鈹€鈹粹攢鈹€鈹€鈹€鈹€鈹€鈹<EFBFBD>攢鈹€鈹€鈹€鈹€鈹€鈹€鈹?
|
||||
鈫? 鈫? 鈫?
|
||||
↓
|
||||
意图识别(detectIntent)
|
||||
↓
|
||||
┌───────────────┬───────────────┬──────────────┐
|
||||
│ query_protocol│ query_record │ count_records│
|
||||
│ (文档查询) │ (记录查询) │ (统计查询) │
|
||||
└───────┬───────┴───────┬───────┴──────┬───────┘
|
||||
↓ ↓ ↓
|
||||
Dify API REDCap API REDCap API
|
||||
(鐭ヨ瘑搴? (鎮h€呮暟鎹? (鎮h€呮暟鎹?
|
||||
鈫? 鈫? 鈫?
|
||||
(知识库) (患者数据) (患者数据)
|
||||
↓ ↓ ↓
|
||||
文档片段 JSON数据 JSON数据
|
||||
鈫? 鈫? 鈫?
|
||||
鈹斺攢鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹粹攢鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹?
|
||||
鈫?
|
||||
↓ ↓ ↓
|
||||
└───────────────┴──────────────┘
|
||||
↓
|
||||
构建LLM Prompt
|
||||
(System + Context + Data)
|
||||
鈫?
|
||||
↓
|
||||
DeepSeek-V3
|
||||
鈫?
|
||||
↓
|
||||
AI回答
|
||||
鈫?
|
||||
↓
|
||||
企业微信自动回复
|
||||
```
|
||||
|
||||
### 核心技术栈
|
||||
|
||||
| 灞傜骇 | 鎶€鏈?| 鐢ㄩ€?|
|
||||
| 层级 | 技术 | 用途 |
|
||||
|------|------|------|
|
||||
| **AI鎺ㄧ悊** | DeepSeek-V3 | 鑷<EFBFBD>劧璇<EFBFBD>█鐞嗚В涓庣敓鎴?|
|
||||
| **RAG骞冲彴** | Dify | 鏂囨。瀛樺偍銆佸垎鍧椼€佸悜閲忓寲銆佽<EFBFBD>涔夋<EFBFBD>绱?|
|
||||
| **鏁版嵁婧?* | REDCap | 涓村簥璇曢獙瀹炴椂鏁版嵁 |
|
||||
| **鏁版嵁搴?* | PostgreSQL | 椤圭洰閰嶇疆銆佺敤鎴锋槧灏?|
|
||||
| **ORM** | Prisma | 鏁版嵁搴撹<EFBFBD>闂?|
|
||||
| **浼氳瘽绠$悊** | SessionMemory | 涓婁笅鏂囩淮鎶わ紙鏈€杩?杞<>級 |
|
||||
| **閫氫俊** | 浼佷笟寰<EFBFBD>俊 | 娑堟伅鎺ユ敹涓庡彂閫?|
|
||||
| **AI推理** | DeepSeek-V3 | 自然语言理解与生成 |
|
||||
| **RAG平台** | Dify | 文档存储、分块、向量化、语义检索 |
|
||||
| **数据源** | REDCap | 临床试验实时数据 |
|
||||
| **数据库** | PostgreSQL | 项目配置、用户映射 |
|
||||
| **ORM** | Prisma | 数据库访问 |
|
||||
| **会话管理** | SessionMemory | 上下文维护(最近3轮) |
|
||||
| **通信** | 企业微信 | 消息接收与发送 |
|
||||
|
||||
### 关键设计模式
|
||||
|
||||
1. **意图驱动路由 (Intent-Based Routing)**
|
||||
- 鏍规嵁鐢ㄦ埛闂<E59F9B><E99782>鍏抽敭璇嶈瘑鍒<E79891>剰鍥?
|
||||
- 鍔ㄦ€佽皟鐢ㄤ笉鍚岀殑鏁版嵁婧愶紙Dify vs REDCap锛?
|
||||
- 根据用户问题关键词识别意图
|
||||
- 动态调用不同的数据源(Dify vs REDCap)
|
||||
|
||||
2. **娣峰悎妫€绱?(Hybrid Retrieval)**
|
||||
- 缁撴瀯鍖栨暟鎹<E69A9F>煡璇<E785A1>紙REDCap锛?
|
||||
- 闈炵粨鏋勫寲鏂囨。妫€绱<E282AC>紙Dify锛?
|
||||
2. **混合检索 (Hybrid Retrieval)**
|
||||
- 结构化数据查询(REDCap)
|
||||
- 非结构化文档检索(Dify)
|
||||
- 两者结果统一注入LLM Prompt
|
||||
|
||||
3. **RAG (Retrieval Augmented Generation)**
|
||||
- 妫€绱㈢浉鍏虫枃妗g墖娈?
|
||||
- 娉ㄥ叆鍒癓LM涓婁笅鏂?
|
||||
- 鍑忓皯骞昏<E9AA9E>锛圚allucination锛?
|
||||
- 检索相关文档片段
|
||||
- 注入到LLM上下文
|
||||
- 减少幻觉(Hallucination)
|
||||
|
||||
4. **会话记忆 (Session Memory)**
|
||||
- 淇濈暀鏈€杩?杞<><E69D9E>璇?
|
||||
- 鏀<>寔澶氳疆瀵硅瘽涓婁笅鏂?
|
||||
- 保留最近3轮对话
|
||||
- 支持多轮对话上下文
|
||||
|
||||
---
|
||||
|
||||
@@ -549,13 +549,13 @@ REDCap API: exportRecords success { recordCount: 1 }
|
||||
|
||||
| 操作 | 平均耗时 | 备注 |
|
||||
|------|---------|------|
|
||||
| Dify妫€绱?| ~1.5s | 璇<EFBFBD>箟妫€绱?Top 5 |
|
||||
| Dify检索 | ~1.5s | 语义检索 Top 5 |
|
||||
| REDCap单条查询 | ~1.2s | HTTP API |
|
||||
| REDCap缁熻<EFBFBD>鏌ヨ<EFBFBD> | ~1.3s | 瀵煎嚭鎵€鏈夎<EFBFBD>褰?|
|
||||
| REDCap统计查询 | ~1.3s | 导出所有记录 |
|
||||
| LLM推理 | ~3.5s | DeepSeek-V3, 500 tokens |
|
||||
| **鎬诲搷搴旀椂闂?* | ~5-6s | 鍚<EFBFBD>綉缁滀紶杈?|
|
||||
| **总响应时间** | ~5-6s | 含网络传输 |
|
||||
|
||||
### Token娑堣€?
|
||||
### Token消耗
|
||||
|
||||
| 场景 | Input Tokens | Output Tokens | Total |
|
||||
|------|-------------|---------------|-------|
|
||||
@@ -567,13 +567,13 @@ REDCap API: exportRecords success { recordCount: 1 }
|
||||
|
||||
## 🎯 后续优化方向
|
||||
|
||||
### 鐭<EFBFBD>湡浼樺寲锛?-2鍛<32>級
|
||||
### 短期优化(1-2周)
|
||||
|
||||
1. **扩展关键词库**
|
||||
- 收集实际用户提问
|
||||
- 琛ュ厖閬楁紡鐨勫尰瀛︽湳璇?
|
||||
- 补充遗漏的医学术语
|
||||
|
||||
2. **浼樺寲妫€绱㈣川閲?*
|
||||
2. **优化检索质量**
|
||||
- 调整Dify的`top_k`参数
|
||||
- 试验不同的`search_method`
|
||||
|
||||
@@ -581,56 +581,55 @@ REDCap API: exportRecords success { recordCount: 1 }
|
||||
- 优化System Prompt
|
||||
- 增加引用来源展示
|
||||
|
||||
### 涓<EFBFBD>湡浼樺寲锛?-2涓<32>湀锛?
|
||||
### 中期优化(1-2个月)
|
||||
|
||||
1. **瀹炵幇澶氶」鐩<EFBFBD>敮鎸?*
|
||||
1. **实现多项目支持**
|
||||
- 用户绑定多个项目
|
||||
- 项目切换机制
|
||||
|
||||
2. **文档API上传**
|
||||
- 寮€鍙戣嚜鍔ㄤ笂浼犳帴鍙?
|
||||
- 瀹氭椂鏇存柊鐭ヨ瘑搴?
|
||||
- 开发自动上传接口
|
||||
- 定时更新知识库
|
||||
|
||||
3. **妫€绱㈢粨鏋滅紦瀛?*
|
||||
3. **检索结果缓存**
|
||||
- Redis缓存高频问题
|
||||
- 减少Dify调用次数
|
||||
|
||||
### 闀挎湡浼樺寲锛?-6涓<36>湀锛?
|
||||
### 长期优化(3-6个月)
|
||||
|
||||
1. **澶氱煡璇嗗簱鑱斿悎妫€绱?*
|
||||
- 鎸夋枃妗g被鍨嬪垎绫伙紙鏂规<EFBFBD>銆佷鸡鐞嗐€丆RF锛?
|
||||
1. **多知识库联合检索**
|
||||
- 按文档类型分类(方案、伦理、CRF)
|
||||
- 智能路由到对应知识库
|
||||
|
||||
2. **娣峰悎妫€绱㈠<EFBFBD>寮?*
|
||||
2. **混合检索增强**
|
||||
- 同时查询REDCap和Dify
|
||||
- 铻嶅悎缁撴瀯鍖?闈炵粨鏋勫寲鏁版嵁
|
||||
- 融合结构化+非结构化数据
|
||||
|
||||
3. **对话质量监控**
|
||||
- 鐢ㄦ埛婊℃剰搴﹁瘎鍒?
|
||||
- 绛旀<EFBFBD>鍑嗙‘鎬у<EFBFBD>璁?
|
||||
- 用户满意度评分
|
||||
- 答案准确性审计
|
||||
|
||||
---
|
||||
|
||||
## 📚 相关文档
|
||||
|
||||
- [IIT Manager Agent 鎶€鏈<EFBFBD>矾寰勪笌鏋舵瀯璁捐<EFBFBD>](../02-鎶€鏈<EFBFBD><EFBFBD>璁?IIT%20Manager%20Agent%20鎶€鏈<EFBFBD>矾寰勪笌鏋舵瀯璁捐<EFBFBD>.md)
|
||||
- [IIT Manager Agent 技术路径与架构设计](../02-技术设计/IIT%20Manager%20Agent%20技术路径与架构设计.md)
|
||||
- [IIT Manager Agent 技术债务清单](../07-技术债务/IIT%20Manager%20Agent%20技术债务清单.md)
|
||||
- [Phase1.5-AI瀵硅瘽鑳藉姏寮€鍙戣<EFBFBD>鍒抅(../04-寮€鍙戣<E98D99>鍒?Phase1.5-AI瀵硅瘽鑳藉姏寮€鍙戣<E98D99>鍒?md)
|
||||
- [Phase1.5-AI对话能力开发计划](../04-开发计划/Phase1.5-AI对话能力开发计划.md)
|
||||
|
||||
---
|
||||
|
||||
## 馃懃 寮€鍙戜汉鍛?
|
||||
## 👥 开发人员
|
||||
|
||||
- **寮€鍙戣€?*: AI Assistant + FengZhiBo
|
||||
- **开发者**: AI Assistant + FengZhiBo
|
||||
- **测试**: FengZhiBo(企业微信真实环境)
|
||||
- **文档**: AI Assistant
|
||||
|
||||
---
|
||||
|
||||
**鉁?寮€鍙戝畬鎴愭椂闂?*: 2026-01-04
|
||||
**鉁?娴嬭瘯鐘舵€?*: 鍏ㄩ儴閫氳繃
|
||||
**鉁?閮ㄧ讲鐘舵€?*: 宸查儴缃插埌寮€鍙戠幆澧?
|
||||
|
||||
**✅ 开发完成时间**: 2026-01-04
|
||||
**✅ 测试状态**: 全部通过
|
||||
**✅ 部署状态**: 已部署到开发环境
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,41 +1,41 @@
|
||||
# Day 2 - REDCap 瀹炴椂闆嗘垚寮€鍙戝畬鎴愯<EFBFBD>褰?
|
||||
# Day 2 - REDCap 实时集成开发完成记录
|
||||
|
||||
> **寮€鍙戞棩鏈?*: 2026-01-02
|
||||
> **寮€鍙戣€?*: AI Assistant + 鐢ㄦ埛
|
||||
> **开发日期**: 2026-01-02
|
||||
> **开发者**: AI Assistant + 用户
|
||||
> **文档版本**: v1.0
|
||||
> **寮€鍙戦樁娈?*: Day 2 - REDCap瀵规帴涓庡疄鏃跺悓姝?
|
||||
> **开发阶段**: Day 2 - REDCap对接与实时同步
|
||||
|
||||
---
|
||||
|
||||
## 馃搵 寮€鍙戞<E98D99>杩?
|
||||
## 📋 开发概述
|
||||
|
||||
Day 2鐨勬牳蹇冪洰鏍囨槸瀹炵幇 **IIT Manager Agent 涓?REDCap 鐨勫疄鏃舵暟鎹<EFBFBD>泦鎴?*锛岄噰鐢?REDCap 鍘熺敓鐨?**Data Entry Trigger (DET)** + **REST API** 鎶€鏈<EFBFBD>柟妗堬紝瀹炵幇闆跺欢杩熺殑鏁版嵁鍚屾<EFBFBD>鍜屽弻鍚戦€氫俊銆?
|
||||
Day 2的核心目标是实现 **IIT Manager Agent 与 REDCap 的实时数据集成**,采用 REDCap 原生的 **Data Entry Trigger (DET)** + **REST API** 技术方案,实现零延迟的数据同步和双向通信。
|
||||
|
||||
### 鏍稿績浠峰€?
|
||||
### 核心价值
|
||||
|
||||
1. 鉁?**瀹炴椂鎬?*: Webhook鍝嶅簲鏃堕棿<10ms锛屾暟鎹<E69A9F>綍鍏ュ悗绔嬪嵆瑙﹀彂
|
||||
2. 鉁?**鍙<>潬鎬?*: DET + 瀹氭椂杞<E6A482><E69D9E>鍙屼繚闄╂満鍒?
|
||||
3. 鉁?**浜戝師鐢?*: 瀹屽叏鍩轰簬Postgres-Only鏋舵瀯锛屼娇鐢╬g-boss闃熷垪
|
||||
4. 鉁?**鏍囧噯鍖?*: 绗﹀悎鍥㈤槦寮€鍙戣<E98D99>鑼冿紙闃熷垪鍚嶇О銆乄orker娉ㄥ唽绛夛級
|
||||
1. ✅ **实时性**: Webhook响应时间<10ms,数据录入后立即触发
|
||||
2. ✅ **可靠性**: DET + 定时轮询双保险机制
|
||||
3. ✅ **云原生**: 完全基于Postgres-Only架构,使用pg-boss队列
|
||||
4. ✅ **标准化**: 符合团队开发规范(队列名称、Worker注册等)
|
||||
|
||||
---
|
||||
|
||||
## 馃幆 瀹屾垚鐨勫姛鑳芥ā鍧?
|
||||
## 🎯 完成的功能模块
|
||||
|
||||
### 1. RedcapAdapter (API閫傞厤鍣?
|
||||
### 1. RedcapAdapter (API适配器)
|
||||
|
||||
**文件**: `backend/src/modules/iit-manager/adapters/RedcapAdapter.ts`
|
||||
|
||||
**核心功能**:
|
||||
- 鉁?`testConnection()` - 杩炴帴娴嬭瘯
|
||||
- 鉁?`exportMetadata()` - 瀵煎嚭瀛楁<EFBFBD>瀹氫箟锛?7涓<37>瓧娈碉級
|
||||
- 鉁?`exportRecords()` - 瀵煎嚭璁板綍锛堟敮鎸佸叏閲?澧為噺/鎸囧畾璁板綍锛?
|
||||
- 鉁?`importRecords()` - 瀵煎叆璁板綍锛圥hase 2棰勭暀锛?
|
||||
- ✅ `testConnection()` - 连接测试
|
||||
- ✅ `exportMetadata()` - 导出字段定义(17个字段)
|
||||
- ✅ `exportRecords()` - 导出记录(支持全量/增量/指定记录)
|
||||
- ✅ `importRecords()` - 导入记录(Phase 2预留)
|
||||
|
||||
**关键参数**:
|
||||
- `baseUrl`: `http://localhost:8080`
|
||||
- `apiToken`: `FCB30F9CBD12EE9E8E9B3E3A0106701B`
|
||||
- `timeout`: 30绉?
|
||||
- `timeout`: 30秒
|
||||
|
||||
**实际性能**:
|
||||
```
|
||||
@@ -46,22 +46,22 @@ Day 2的核心目标是实现 **IIT Manager Agent
|
||||
|
||||
---
|
||||
|
||||
### 2. WebhookController (Webhook鎺ユ敹鍣?
|
||||
### 2. WebhookController (Webhook接收器)
|
||||
|
||||
**文件**: `backend/src/modules/iit-manager/controllers/WebhookController.ts`
|
||||
|
||||
**核心逻辑**:
|
||||
```typescript
|
||||
1. 绔嬪嵆杩斿洖200 OK锛?10ms锛?
|
||||
1. 立即返回200 OK(<10ms)
|
||||
2. 验证project_id是否在配置中
|
||||
3. 骞傜瓑鎬ф<EFBFBD>鏌ワ紙闃叉<EFBFBD>閲嶅<EFBFBD>澶勭悊锛?
|
||||
4. 璁板綍瀹¤<EFBFBD>鏃ュ織锛圵EBHOOK_RECEIVED锛?
|
||||
5. 鎺ㄩ€佸埌璐ㄦ帶闃熷垪锛坕it_quality_check锛?
|
||||
3. 幂等性检查(防止重复处理)
|
||||
4. 记录审计日志(WEBHOOK_RECEIVED)
|
||||
5. 推送到质控队列(iit_quality_check)
|
||||
```
|
||||
|
||||
**REDCap DET格式支持**:
|
||||
- 鉁?娣诲姞浜?`application/x-www-form-urlencoded` 瑙f瀽鍣?
|
||||
- 鉁?鏀<>寔REDCap鍘熺敓POST鏍煎紡
|
||||
- ✅ 添加了 `application/x-www-form-urlencoded` 解析器
|
||||
- ✅ 支持REDCap原生POST格式
|
||||
|
||||
**Webhook字段**:
|
||||
```json
|
||||
@@ -77,19 +77,19 @@ Day 2的核心目标是实现 **IIT Manager Agent
|
||||
|
||||
---
|
||||
|
||||
### 3. SyncManager (杞<EFBFBD><EFBFBD>绠$悊鍣?
|
||||
### 3. SyncManager (轮询管理器)
|
||||
|
||||
**文件**: `backend/src/modules/iit-manager/services/SyncManager.ts`
|
||||
|
||||
**核心功能**:
|
||||
- 鉁?`initScheduledJob()` - 鍒濆<EFBFBD>鍖栧畾鏃朵换鍔★紙姣?鍒嗛挓锛?
|
||||
- 鉁?`handlePoll()` - 杞<EFBFBD><EFBFBD>鎵€鏈塧ctive椤圭洰
|
||||
- 鉁?`manualSync()` - 鎵嬪姩鍚屾<EFBFBD>鎸囧畾椤圭洰
|
||||
- 鉁?`fullSync()` - 鍏ㄩ噺鍚屾<EFBFBD>
|
||||
- ✅ `initScheduledJob()` - 初始化定时任务(每5分钟)
|
||||
- ✅ `handlePoll()` - 轮询所有active项目
|
||||
- ✅ `manualSync()` - 手动同步指定项目
|
||||
- ✅ `fullSync()` - 全量同步
|
||||
|
||||
**增量同步策略**:
|
||||
```typescript
|
||||
// 鍩轰簬lastSyncAt鏃堕棿鎴?
|
||||
// 基于lastSyncAt时间戳
|
||||
dateRangeBegin: lastSyncAt || createdAt - 24h
|
||||
```
|
||||
|
||||
@@ -108,20 +108,20 @@ dateRangeBegin: lastSyncAt || createdAt - 24h
|
||||
|
||||
1. **iit_redcap_poll** (定时轮询)
|
||||
- 队列名称: `iit_redcap_poll`
|
||||
- 瑙﹀彂棰戠巼: 姣?鍒嗛挓锛圕ron: `*/5 * * * *`锛?
|
||||
- 骞跺彂鏁? 1
|
||||
- 鍔熻兘: 杞<><E69D9E>鎵€鏈塧ctive椤圭洰鐨勫<E990A8>閲忔暟鎹?
|
||||
- 触发频率: 每5分钟(Cron: `*/5 * * * *`)
|
||||
- 并发数: 1
|
||||
- 功能: 轮询所有active项目的增量数据
|
||||
|
||||
2. **iit_quality_check** (质控任务)
|
||||
- 队列名称: `iit_quality_check`
|
||||
- 瑙﹀彂鏂瑰紡: Webhook/SyncManager鎺ㄩ€?
|
||||
- 鍔熻兘: 璐ㄦ帶閫昏緫锛圥hase 1.5瀹炵幇锛?
|
||||
- 触发方式: Webhook/SyncManager推送
|
||||
- 功能: 质控逻辑(Phase 1.5实现)
|
||||
|
||||
**关键修复**:
|
||||
- 鉂?鍒濆<E98D92>: `await jobQueue.work('iit:redcap:poll', ...)`
|
||||
- 鉁?淇<><E6B787>: `jobQueue.process('iit_redcap_poll', ...)`
|
||||
- 鉂?鍒濆<E98D92>: 闃熷垪鍚嶇О浣跨敤鍐掑彿 `iit:quality-check`
|
||||
- 鉁?淇<><E6B787>: 浣跨敤涓嬪垝绾?`iit_quality_check`
|
||||
- ❌ 初始: `await jobQueue.work('iit:redcap:poll', ...)`
|
||||
- ✅ 修复: `jobQueue.process('iit_redcap_poll', ...)`
|
||||
- ❌ 初始: 队列名称使用冒号 `iit:quality-check`
|
||||
- ✅ 修复: 使用下划线 `iit_quality_check`
|
||||
|
||||
---
|
||||
|
||||
@@ -131,13 +131,13 @@ dateRangeBegin: lastSyncAt || createdAt - 24h
|
||||
|
||||
**注册的API端点**:
|
||||
|
||||
| 绔<EFBFBD>偣 | 鏂规硶 | 鍔熻兘 | 鐘舵€?|
|
||||
| 端点 | 方法 | 功能 | 状态 |
|
||||
|------|------|------|------|
|
||||
| `/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 | 鎵嬪姩鍚屾<EFBFBD> | 鉁?|
|
||||
| `/api/v1/iit/projects/:id/full-sync` | POST | 鍏ㄩ噺鍚屾<EFBFBD> | 鉁?|
|
||||
| `/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 | 全量同步 | ✅ |
|
||||
|
||||
---
|
||||
|
||||
@@ -150,7 +150,7 @@ dateRangeBegin: lastSyncAt || createdAt - 24h
|
||||
Error [ERR_MODULE_NOT_FOUND]: Cannot find package '@/common'
|
||||
```
|
||||
|
||||
**鍘熷洜**: 椤圭洰浣跨敤鐩稿<EFBFBD>璺<EFBFBD>緞锛岃€屼笉鏄<EFBFBD>矾寰勫埆鍚?`@/common`
|
||||
**原因**: 项目使用相对路径,而不是路径别名 `@/common`
|
||||
|
||||
**解决方案**:
|
||||
```typescript
|
||||
@@ -167,7 +167,7 @@ import { jobQueue } from '../../../common/jobs/index.js';
|
||||
|
||||
---
|
||||
|
||||
### 闂<EFBFBD><EFBFBD>2: 鏁版嵁搴撳瓧娈靛悕绉颁笉涓€鑷?
|
||||
### 问题2: 数据库字段名称不一致
|
||||
|
||||
**错误信息**:
|
||||
```sql
|
||||
@@ -176,16 +176,16 @@ ERROR: column "redcapProjectId" does not exist
|
||||
|
||||
**原因**: Prisma使用camelCase,但PostgreSQL实际使用snake_case
|
||||
|
||||
**鏁版嵁搴撶湡瀹炴儏鍐?*:
|
||||
**数据库真实情况**:
|
||||
```sql
|
||||
-- 表名: iit_schema.projects (not IitProject)
|
||||
-- 瀛楁<EFBFBD>鍚? redcap_project_id (not redcapProjectId)
|
||||
-- 字段名: redcap_project_id (not redcapProjectId)
|
||||
```
|
||||
|
||||
**解决方案**:
|
||||
1. 鉁?TypeScript浠g爜涓<EFBFBD>户缁<EFBFBD>娇鐢≒risma鐨刢amelCase锛堣嚜鍔ㄦ槧灏勶級
|
||||
2. 鉁?鍘熷<E98D98>SQL璇<4C>彞鏀逛负snake_case
|
||||
3. 鉁?JSON瀛楁<EFBFBD>浣跨敤 `'{}'::jsonb` 绫诲瀷杞<EFBFBD>崲
|
||||
1. ✅ TypeScript代码中继续使用Prisma的camelCase(自动映射)
|
||||
2. ✅ 原始SQL语句改为snake_case
|
||||
3. ✅ JSON字段使用 `'{}'::jsonb` 类型转换
|
||||
|
||||
**修改文件**:
|
||||
- 测试脚本中的SQL语句
|
||||
@@ -200,18 +200,18 @@ ERROR: column "redcapProjectId" does not exist
|
||||
Name can only contain alphanumeric characters, underscores, hyphens, or periods
|
||||
```
|
||||
|
||||
**鍘熷洜**: 鍒濆<EFBFBD>浣跨敤杩炲瓧绗?`-`锛屼絾瀹為檯娴嬭瘯鍙戠幇涓嶇ǔ瀹?
|
||||
**原因**: 初始使用连字符 `-`,但实际测试发现不稳定
|
||||
|
||||
**团队标准**:
|
||||
```typescript
|
||||
鉂?'iit:quality-check' // 鍐掑彿
|
||||
鉂?'iit-quality-check' // 杩炲瓧绗︼紙涓嶇ǔ瀹氾級
|
||||
鉁?'iit_quality_check' // 涓嬪垝绾匡紙鎺ㄨ崘锛?
|
||||
❌ 'iit:quality-check' // 冒号
|
||||
❌ 'iit-quality-check' // 连字符(不稳定)
|
||||
✅ 'iit_quality_check' // 下划线(推荐)
|
||||
```
|
||||
|
||||
**修改**:
|
||||
- `iit:quality-check` 鈫?`iit_quality_check`
|
||||
- `iit:redcap:poll` 鈫?`iit_redcap_poll`
|
||||
- `iit:quality-check` → `iit_quality_check`
|
||||
- `iit:redcap:poll` → `iit_redcap_poll`
|
||||
|
||||
**依据文档**: `AIclinicalresearch\docs\02-通用能力层\Postgres-Only异步任务处理指南.md`
|
||||
|
||||
@@ -219,20 +219,20 @@ Name can only contain alphanumeric characters, underscores, hyphens, or periods
|
||||
|
||||
### 问题4: Worker注册方法错误
|
||||
|
||||
**閿欒<EFBFBD>**: 浣跨敤浜?`await jobQueue.work()`
|
||||
**错误**: 使用了 `await jobQueue.work()`
|
||||
|
||||
**正确方法**: 使用 `jobQueue.process()`
|
||||
|
||||
**对比**:
|
||||
```typescript
|
||||
// 鉂?閿欒<E996BF>
|
||||
// ❌ 错误
|
||||
await jobQueue.work(
|
||||
'iit_redcap_poll',
|
||||
{ teamSize: 1, teamConcurrency: 1 },
|
||||
async (job) => { ... }
|
||||
);
|
||||
|
||||
// 鉁?姝g‘
|
||||
// ✅ 正确
|
||||
jobQueue.process('iit_redcap_poll', async (job) => {
|
||||
// ...
|
||||
return { success: true };
|
||||
@@ -241,7 +241,7 @@ jobQueue.process('iit_redcap_poll', async (job) => {
|
||||
|
||||
---
|
||||
|
||||
### 闂<EFBFBD><EFBFBD>5: REDCap DET鏍煎紡涓嶅吋瀹?
|
||||
### 问题5: REDCap DET格式不兼容
|
||||
|
||||
**错误信息**:
|
||||
```
|
||||
@@ -250,7 +250,7 @@ Unsupported Media Type: application/x-www-form-urlencoded
|
||||
|
||||
**原因**: REDCap DET发送的是表单格式,而不是JSON
|
||||
|
||||
**瑙e喅鏂规<EFBFBD>**: 娣诲姞Content-Type瑙f瀽鍣?
|
||||
**解决方案**: 添加Content-Type解析器
|
||||
```typescript
|
||||
fastify.addContentTypeParser(
|
||||
'application/x-www-form-urlencoded',
|
||||
@@ -274,23 +274,23 @@ fastify.addContentTypeParser(
|
||||
|
||||
### 问题6: Docker网络访问问题
|
||||
|
||||
**闂<EFBFBD><EFBFBD>**: REDCap瀹瑰櫒鏃犳硶閫氳繃 `localhost` 璁块棶瀹夸富鏈烘湇鍔?
|
||||
**问题**: REDCap容器无法通过 `localhost` 访问宿主机服务
|
||||
|
||||
**DET URL配置**:
|
||||
```
|
||||
鉂?http://localhost:3001/api/v1/iit/webhooks/redcap
|
||||
鉁?http://host.docker.internal:3001/api/v1/iit/webhooks/redcap
|
||||
❌ 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连接测试
|
||||
|
||||
@@ -298,11 +298,11 @@ docker exec redcap-apache curl http://host.docker.internal:3001/api/v1/iit/healt
|
||||
|
||||
**结果**:
|
||||
```
|
||||
鉁?杩炴帴鎴愬姛
|
||||
鉁?鍏冩暟鎹<E69A9F><E98EB9>鍑? 17涓<37>瓧娈?
|
||||
鉁?璁板綍瀵煎嚭: 6鏉¤<E98F89>褰?(ID: 1,2,3,4,5,6)
|
||||
鉁?澧為噺鍚屾<E98D9A>: 鎴愬姛鑾峰彇鏈€杩?灏忔椂鏁版嵁
|
||||
鉁?鎸囧畾璁板綍: 鎴愬姛瀵煎嚭鍗曟潯璁板綍
|
||||
✅ 连接成功
|
||||
✅ 元数据导出: 17个字段
|
||||
✅ 记录导出: 6条记录 (ID: 1,2,3,4,5,6)
|
||||
✅ 增量同步: 成功获取最近1小时数据
|
||||
✅ 指定记录: 成功导出单条记录
|
||||
```
|
||||
|
||||
---
|
||||
@@ -313,11 +313,11 @@ docker exec redcap-apache curl http://host.docker.internal:3001/api/v1/iit/healt
|
||||
|
||||
**结果**:
|
||||
```
|
||||
鉁?鍋ュ悍妫€鏌? ok
|
||||
鉁?Webhook鍙戦€佹垚鍔? 200
|
||||
鉁?鍝嶅簲鏃堕棿: <10ms (浼樼<EFBFBD>)
|
||||
鉁?骞傜瓑鎬ф<E98EAC>鏌? 閫氳繃
|
||||
鉁?鏃犳晥璇锋眰鎷︽埅: 400閿欒<EFBFBD>
|
||||
✅ 健康检查: ok
|
||||
✅ Webhook发送成功: 200
|
||||
✅ 响应时间: <10ms (优秀)
|
||||
✅ 幂等性检查: 通过
|
||||
✅ 无效请求拦截: 400错误
|
||||
```
|
||||
|
||||
---
|
||||
@@ -326,21 +326,21 @@ docker exec redcap-apache curl http://host.docker.internal:3001/api/v1/iit/healt
|
||||
|
||||
**测试脚本**: `test-redcap-integration.ts`
|
||||
|
||||
**缁撴灉**: **12/12 閫氳繃** 鉁?
|
||||
**结果**: **12/12 通过** ✅
|
||||
|
||||
```
|
||||
鉁?1. 鍚庣<EFBFBD>鏈嶅姟杩愯<EFBFBD>姝e父
|
||||
鉁?2. 椤圭洰閰嶇疆瀛樺湪
|
||||
鉁?3. REDCap API杩炴帴鎴愬姛
|
||||
鉁?4. 鍏冩暟鎹<EFBFBD>幏鍙栨垚鍔?
|
||||
鉁?5. 璁板綍鑾峰彇鎴愬姛
|
||||
鉁?6. Webhook鍝嶅簲閫熷害<100ms
|
||||
鉁?7. 寮傛<EFBFBD>澶勭悊绛夊緟瀹屾垚
|
||||
鉁?8. 瀹¤<EFBFBD>鏃ュ織璁板綍瀹屾暣
|
||||
鉁?9. 澧為噺鍚屾<EFBFBD>鎴愬姛
|
||||
鉁?10. 鎵嬪姩鍚屾<EFBFBD>鎴愬姛 猸?
|
||||
鉁?11. Webhook鍋ュ悍妫€鏌?
|
||||
鉁?12. 骞傜瓑鎬ф祴璇曢€氳繃
|
||||
✅ 1. 后端服务运行正常
|
||||
✅ 2. 项目配置存在
|
||||
✅ 3. REDCap API连接成功
|
||||
✅ 4. 元数据获取成功
|
||||
✅ 5. 记录获取成功
|
||||
✅ 6. Webhook响应速度<100ms
|
||||
✅ 7. 异步处理等待完成
|
||||
✅ 8. 审计日志记录完整
|
||||
✅ 9. 增量同步成功
|
||||
✅ 10. 手动同步成功 ⭐
|
||||
✅ 11. Webhook健康检查
|
||||
✅ 12. 幂等性测试通过
|
||||
```
|
||||
|
||||
---
|
||||
@@ -361,7 +361,7 @@ docker exec redcap-apache curl http://host.docker.internal:3001/api/v1/iit/healt
|
||||
WEBHOOK_RECEIVED | 6 | instrument: demographics, project_id: 16
|
||||
```
|
||||
|
||||
**楠岃瘉缁撴灉**: 鉁?鏁版嵁瀹屽叏涓€鑷?
|
||||
**验证结果**: ✅ 数据完全一致
|
||||
|
||||
---
|
||||
|
||||
@@ -369,24 +369,24 @@ docker exec redcap-apache curl http://host.docker.internal:3001/api/v1/iit/healt
|
||||
|
||||
### API响应时间
|
||||
|
||||
| 鎿嶄綔 | 鑰楁椂 | 鏁版嵁閲?|
|
||||
| 操作 | 耗时 | 数据量 |
|
||||
|------|------|--------|
|
||||
| exportMetadata | 260-560ms | 17涓<EFBFBD>瓧娈?|
|
||||
| exportRecords (鍏ㄩ噺) | 450-1,400ms | 6鏉¤<EFBFBD>褰?|
|
||||
| exportMetadata | 260-560ms | 17个字段 |
|
||||
| exportRecords (全量) | 450-1,400ms | 6条记录 |
|
||||
| exportRecords (增量) | 300-800ms | 变化记录 |
|
||||
| Webhook响应 | **<10ms** | 立即返回 |
|
||||
|
||||
### 瀹炴椂鎬ч獙璇?
|
||||
### 实时性验证
|
||||
|
||||
| 鎸囨爣 | 鐩<>爣 | 瀹為檯 | 鐘舵€?|
|
||||
| 指标 | 目标 | 实际 | 状态 |
|
||||
|------|------|------|------|
|
||||
| Webhook鍝嶅簲鏃堕棿 | <100ms | **7ms** | 鉁?浼樼<E6B5BC> |
|
||||
| DET瑙﹀彂寤惰繜 | 0绉?| **0绉?* | 鉁?瀹岀編 |
|
||||
| 鏁版嵁鍚屾<EFBFBD>鍑嗙‘鎬?| 100% | **100%** | 鉁?瀹岀編 |
|
||||
| Webhook响应时间 | <100ms | **7ms** | ✅ 优秀 |
|
||||
| DET触发延迟 | 0秒 | **0秒** | ✅ 完美 |
|
||||
| 数据同步准确性 | 100% | **100%** | ✅ 完美 |
|
||||
|
||||
---
|
||||
|
||||
## 馃梽锔?鏁版嵁搴撻厤缃?
|
||||
## 🗄️ 数据库配置
|
||||
|
||||
### REDCap项目配置
|
||||
|
||||
@@ -418,7 +418,7 @@ INSERT INTO iit_schema.projects (
|
||||
|
||||
### REDCap项目信息
|
||||
|
||||
| 瀛楁<EFBFBD> | 鍊?|
|
||||
| 字段 | 值 |
|
||||
|------|-----|
|
||||
| **项目名称** | test0102 |
|
||||
| **Project ID** | 16 (int) |
|
||||
@@ -435,8 +435,8 @@ INSERT INTO iit_schema.projects (
|
||||
- height, weight, bmi
|
||||
- comments, demographics_complete
|
||||
|
||||
**2. ddcd (鑷<EFBFBD>畾涔夎〃鍗?**
|
||||
- zhiliaoshi (娌荤枟瀹?
|
||||
**2. ddcd (自定义表单)**
|
||||
- zhiliaoshi (治疗室)
|
||||
- shifou (是否)
|
||||
- ddcd_complete
|
||||
|
||||
@@ -480,37 +480,37 @@ INSERT INTO iit_schema.projects (
|
||||
|
||||
### 1. 技术选型验证
|
||||
|
||||
**鉁?REDCap DET + REST API鏂规<EFBFBD>浼樺娍**:
|
||||
- 闆跺紑鍙戞垚鏈<EFBFBD>紙REDCap鍘熺敓鏀<EFBFBD>寔锛?
|
||||
- 闆跺欢杩燂紙瀹炴椂瑙﹀彂锛?
|
||||
**✅ REDCap DET + REST API方案优势**:
|
||||
- 零开发成本(REDCap原生支持)
|
||||
- 零延迟(实时触发)
|
||||
- 高可靠(Webhook + 轮询双保险)
|
||||
- 鏄撶淮鎶わ紙鏍囧噯HTTP鎺ュ彛锛?
|
||||
- 易维护(标准HTTP接口)
|
||||
|
||||
**鉂?鏀惧純鐨勬柟妗?*:
|
||||
**❌ 放弃的方案**:
|
||||
- External Module(需要PHP开发)
|
||||
- 绾<EFBFBD>疆璇<EFBFBD>紙寤惰繜楂橈紝璧勬簮娴<EFBFBD>垂锛?
|
||||
- 纯轮询(延迟高,资源浪费)
|
||||
|
||||
---
|
||||
|
||||
### 2. 寮€鍙戣<EFBFBD>鑼冮伒寰?
|
||||
### 2. 开发规范遵循
|
||||
|
||||
**鉁?绗﹀悎鍥㈤槦鏍囧噯**:
|
||||
- 闃熷垪鍚嶇О: 浣跨敤涓嬪垝绾匡紙`iit_quality_check`锛?
|
||||
**✅ 符合团队标准**:
|
||||
- 队列名称: 使用下划线(`iit_quality_check`)
|
||||
- Worker注册: 使用 `jobQueue.process()`
|
||||
- 瀹¤<EFBFBD>鏃ュ織: 璁板綍鎵€鏈夊叧閿<E58FA7>搷浣?
|
||||
- 閿欒<EFBFBD>澶勭悊: 缁熶竴寮傚父鎹曡幏鍜屾棩蹇楄<E8B987>褰?
|
||||
- 审计日志: 记录所有关键操作
|
||||
- 错误处理: 统一异常捕获和日志记录
|
||||
|
||||
**鍙傝€冩枃妗?*:
|
||||
**参考文档**:
|
||||
- `Postgres-Only异步任务处理指南.md`
|
||||
- `REDCap对接技术方案与实施指南.md`
|
||||
|
||||
---
|
||||
|
||||
### 3. 璋冭瘯鎶€宸?
|
||||
### 3. 调试技巧
|
||||
|
||||
**Docker容器调试**:
|
||||
```bash
|
||||
# 娴嬭瘯缃戠粶杩為€氭€?
|
||||
# 测试网络连通性
|
||||
docker exec redcap-apache curl http://host.docker.internal:3001/api/v1/iit/health
|
||||
|
||||
# 查询审计日志
|
||||
@@ -522,7 +522,7 @@ docker exec ai-clinical-postgres psql -U postgres -d ai_clinical_research \
|
||||
-c "SELECT name, redcap_project_id, status FROM iit_schema.projects;"
|
||||
```
|
||||
|
||||
**REDCap鏁版嵁搴撴煡璇?*:
|
||||
**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;"
|
||||
@@ -534,26 +534,26 @@ docker exec redcap-mysql mysql -u redcap_user -predcap_pass_dev_456 redcap \
|
||||
|
||||
| 文档名称 | 路径 | 说明 |
|
||||
|---------|------|------|
|
||||
| REDCap瀵规帴鎶€鏈<EFBFBD>柟妗?| `04-寮€鍙戣<E98D99>鍒?REDCap瀵规帴鎶€鏈<E282AC>柟妗堜笌瀹炴柦鎸囧崡.md` | Day 2鎶€鏈<EFBFBD>柟妗?|
|
||||
| MVP寮€鍙戜换鍔℃竻鍗?| `04-寮€鍙戣<E98D99>鍒?MVP寮€鍙戜换鍔℃竻鍗?md` | 鏁翠綋寮€鍙戣<E98D99>鍒?|
|
||||
| Postgres-Only寮傛<EFBFBD>浠诲姟澶勭悊鎸囧崡 | `docs/02-閫氱敤鑳藉姏灞?Postgres-Only寮傛<EFBFBD>浠诲姟澶勭悊鎸囧崡.md` | 闃熷垪寮€鍙戣<E98D99>鑼?|
|
||||
| 妯″潡褰撳墠鐘舵€?| `00-妯″潡褰撳墠鐘舵€佷笌寮€鍙戞寚鍗?md` | 妯″潡鏁翠綋鐘舵€?|
|
||||
| REDCap对接技术方案 | `04-开发计划/REDCap对接技术方案与实施指南.md` | Day 2技术方案 |
|
||||
| MVP开发任务清单 | `04-开发计划/MVP开发任务清单.md` | 整体开发计划 |
|
||||
| Postgres-Only异步任务处理指南 | `docs/02-通用能力层/Postgres-Only异步任务处理指南.md` | 队列开发规范 |
|
||||
| 模块当前状态 | `00-模块当前状态与开发指南.md` | 模块整体状态 |
|
||||
|
||||
---
|
||||
|
||||
## 馃殌 涓嬩竴姝ヨ<E5A79D>鍒?
|
||||
## 🚀 下一步计划
|
||||
|
||||
### Phase 1.5 - 质控逻辑实现
|
||||
|
||||
**鐩<EFBFBD>爣**: 瀹炵幇AI椹卞姩鐨勬暟鎹<EFBFBD>川鎺?
|
||||
**目标**: 实现AI驱动的数据质控
|
||||
|
||||
**核心功能**:
|
||||
1. 实现 `iit_quality_check` Worker的质控逻辑
|
||||
2. 璋冪敤Dify宸ヤ綔娴佽繘琛屾暟鎹<EFBFBD>獙璇?
|
||||
3. 鐢熸垚璐ㄦ帶寤鸿<EFBFBD>锛坰hadow state锛?
|
||||
4. 杩斿洖缁撴灉缁欑爺绌惰€?
|
||||
2. 调用Dify工作流进行数据验证
|
||||
3. 生成质控建议(shadow state)
|
||||
4. 返回结果给研究者
|
||||
|
||||
**棰勪及宸ヤ綔閲?*: 1-2澶?
|
||||
**预估工作量**: 1-2天
|
||||
|
||||
---
|
||||
|
||||
@@ -565,26 +565,26 @@ docker exec redcap-mysql mysql -u redcap_user -predcap_pass_dev_456 redcap \
|
||||
1. 完善 `importRecords()` API
|
||||
2. 实现shadow state审批流程
|
||||
3. 经研究者确认后同步到REDCap
|
||||
4. 澶勭悊鍐茬獊鍜岀増鏈<EFBFBD>帶鍒?
|
||||
4. 处理冲突和版本控制
|
||||
|
||||
**棰勪及宸ヤ綔閲?*: 2-3澶?
|
||||
**预估工作量**: 2-3天
|
||||
|
||||
---
|
||||
|
||||
## 鉁?楠屾敹娓呭崟
|
||||
## ✅ 验收清单
|
||||
|
||||
- [x] RedcapAdapter API閫傞厤鍣ㄥ畬鎴?
|
||||
- [x] WebhookController Webhook鎺ユ敹鍣ㄥ畬鎴?
|
||||
- [x] SyncManager 杞<EFBFBD><EFBFBD>绠$悊鍣ㄥ畬鎴?
|
||||
- [x] Worker娉ㄥ唽鍜岄槦鍒楅厤缃<EFBFBD>畬鎴?
|
||||
- [x] RedcapAdapter API适配器完成
|
||||
- [x] WebhookController Webhook接收器完成
|
||||
- [x] SyncManager 轮询管理器完成
|
||||
- [x] Worker注册和队列配置完成
|
||||
- [x] 路由配置和Content-Type解析完成
|
||||
- [x] 鍗曞厓娴嬭瘯鑴氭湰閫氳繃锛坱est-redcap-api.ts锛?
|
||||
- [x] Webhook娴嬭瘯閫氳繃锛坱est-redcap-webhook.ts锛?
|
||||
- [x] 闆嗘垚娴嬭瘯閫氳繃锛坱est-redcap-integration.ts锛?2/12锛?
|
||||
- [x] 鐪熷疄鍦烘櫙娴嬭瘯閫氳繃锛堟柊澧?缂栬緫璁板綍锛?
|
||||
- [x] 单元测试脚本通过(test-redcap-api.ts)
|
||||
- [x] Webhook测试通过(test-redcap-webhook.ts)
|
||||
- [x] 集成测试通过(test-redcap-integration.ts,12/12)
|
||||
- [x] 真实场景测试通过(新增+编辑记录)
|
||||
- [x] 审计日志记录完整
|
||||
- [x] 鎬ц兘鎸囨爣杈炬爣锛圵ebhook<10ms锛?
|
||||
- [x] 绗﹀悎鍥㈤槦寮€鍙戣<EFBFBD>鑼?
|
||||
- [x] 性能指标达标(Webhook<10ms)
|
||||
- [x] 符合团队开发规范
|
||||
- [x] 文档更新完成
|
||||
|
||||
---
|
||||
@@ -594,7 +594,7 @@ docker exec redcap-mysql mysql -u redcap_user -predcap_pass_dev_456 redcap \
|
||||
### A. 环境信息
|
||||
|
||||
```yaml
|
||||
寮€鍙戠幆澧?
|
||||
开发环境:
|
||||
- OS: Windows 11
|
||||
- Node.js: v22.18.0
|
||||
- PostgreSQL: 16.1 (Docker)
|
||||
@@ -604,8 +604,8 @@ docker exec redcap-mysql mysql -u redcap_user -predcap_pass_dev_456 redcap \
|
||||
|
||||
Docker容器:
|
||||
- redcap-apache: REDCap应用
|
||||
- redcap-mysql: REDCap鏁版嵁搴?
|
||||
- ai-clinical-postgres: IIT鏁版嵁搴?
|
||||
- redcap-mysql: REDCap数据库
|
||||
- ai-clinical-postgres: IIT数据库
|
||||
```
|
||||
|
||||
### B. 关键代码文件清单
|
||||
@@ -613,27 +613,26 @@ Docker容器:
|
||||
```
|
||||
backend/src/modules/iit-manager/
|
||||
├── adapters/
|
||||
鈹? 鈹斺攢鈹€ RedcapAdapter.ts (271琛?
|
||||
│ └── RedcapAdapter.ts (271行)
|
||||
├── controllers/
|
||||
鈹? 鈹斺攢鈹€ WebhookController.ts (327琛?
|
||||
│ └── WebhookController.ts (327行)
|
||||
├── services/
|
||||
鈹? 鈹斺攢鈹€ SyncManager.ts (398琛?
|
||||
│ └── 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琛?
|
||||
│ └── index.ts (203行)
|
||||
├── index.ts (91行)
|
||||
├── test-redcap-api.ts (189行)
|
||||
├── test-redcap-webhook.ts (274行)
|
||||
└── test-redcap-integration.ts (449行)
|
||||
```
|
||||
|
||||
**鎬讳唬鐮侀噺**: ~2,200琛?
|
||||
**总代码量**: ~2,200行
|
||||
|
||||
---
|
||||
|
||||
**鏂囨。缁存姢鑰?*: 寮€鍙戝洟闃?
|
||||
**鏈€鍚庢洿鏂?*: 2026-01-02
|
||||
**鐘舵€?*: 鉁?Day 2 寮€鍙戝畬鎴?
|
||||
|
||||
**文档维护者**: 开发团队
|
||||
**最后更新**: 2026-01-02
|
||||
**状态**: ✅ Day 2 开发完成
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
# Day 3 企业微信集成与端到端测试完成记录
|
||||
|
||||
> **鏃ユ湡**锛?026-01-03
|
||||
> **寮€鍙戦樁娈?*锛歁VP Week 1 - Day 3
|
||||
> **鏍稿績鐩<EFBFBD>爣**锛氭墦閫?REDCap 鈫?Node.js 鈫?浼佷笟寰<E7AC9F>俊 鐨勫畬鏁撮棴鐜?
|
||||
> **瀹為檯瀹屾垚**锛氣渽 绔<>埌绔<E59F8C>祴璇曢€氳繃锛孧VP闂<50>幆鎵撻€?
|
||||
> **日期**:2026-01-03
|
||||
> **开发阶段**:MVP Week 1 - Day 3
|
||||
> **核心目标**:打通 REDCap → Node.js → 企业微信 的完整闭环
|
||||
> **实际完成**:✅ 端到端测试通过,MVP闭环打通
|
||||
|
||||
---
|
||||
|
||||
@@ -11,69 +11,69 @@
|
||||
|
||||
### 1.1 核心目标
|
||||
|
||||
**鏈€灏忛棴鐜<EFBFBD>獙璇?*锛?
|
||||
**最小闭环验证**:
|
||||
```
|
||||
REDCap褰曞叆鏁版嵁 鈫?Node.js瀹炴椂鎹曡幏 鈫?浼佷笟寰<E7AC9F>俊鏅鸿兘閫氱煡
|
||||
鈫?
|
||||
璐ㄦ帶鍒嗘瀽 鈫?鎺ㄩ€侀€氱煡 鈫?PI鎺ユ敹
|
||||
REDCap录入数据 → Node.js实时捕获 → 企业微信智能通知
|
||||
↓
|
||||
质控分析 → 推送通知 → PI接收
|
||||
```
|
||||
|
||||
### 1.2 完成成果
|
||||
|
||||
| 鍔熻兘妯″潡 | 鐘舵€?| 璇存槑 |
|
||||
| 功能模块 | 状态 | 说明 |
|
||||
|---------|------|------|
|
||||
| 鉁?浼佷笟寰<E7AC9F>俊鎺ㄩ€佹湇鍔?| 瀹屾垚 | `WechatService.ts`锛?14琛岋級 |
|
||||
| 鉁?浼佷笟寰<E7AC9F>俊鍥炶皟澶勭悊 | 瀹屾垚 | `WechatCallbackController.ts`锛?01琛岋級 |
|
||||
| 鉁?璐ㄦ帶Worker閫昏緫 | 瀹屾垚 | `iit_quality_check` Worker |
|
||||
| 鉁?Worker娉ㄥ唽鏈哄埗 | 瀹屾垚 | `initIitManager()` 鍦ㄥ惎鍔ㄦ椂璋冪敤 |
|
||||
| 鉁?绔<>埌绔<E59F8C>祴璇?| 閫氳繃 | REDCap 鈫?Node.js 鈫?浼佷笟寰<E7AC9F>俊 |
|
||||
| 鉁?鐜<><E9909C>閰嶇疆鏂囨。 | 瀹屾垚 | `WECHAT_ENV_CONFIG.md`锛?01琛岋級 |
|
||||
| ✅ 企业微信推送服务 | 完成 | `WechatService.ts`(314行) |
|
||||
| ✅ 企业微信回调处理 | 完成 | `WechatCallbackController.ts`(501行) |
|
||||
| ✅ 质控Worker逻辑 | 完成 | `iit_quality_check` Worker |
|
||||
| ✅ Worker注册机制 | 完成 | `initIitManager()` 在启动时调用 |
|
||||
| ✅ 端到端测试 | 通过 | REDCap → Node.js → 企业微信 |
|
||||
| ✅ 环境配置文档 | 完成 | `WECHAT_ENV_CONFIG.md`(401行) |
|
||||
|
||||
---
|
||||
|
||||
## 馃摑 浜屻€佸叧閿<E58FA7>妧鏈<E5A6A7>疄鐜?
|
||||
## 📝 二、关键技术实现
|
||||
|
||||
### 2.1 浼佷笟寰<EFBFBD>俊鎺ㄩ€佹湇鍔★紙WechatService锛?
|
||||
### 2.1 企业微信推送服务(WechatService)
|
||||
|
||||
**鏂囦欢**锛歚backend/src/modules/iit-manager/services/WechatService.ts`锛?14琛岋級
|
||||
**文件**:`backend/src/modules/iit-manager/services/WechatService.ts`(314行)
|
||||
|
||||
**鏍稿績鍔熻兘**锛?
|
||||
**核心功能**:
|
||||
```typescript
|
||||
class WechatService {
|
||||
// 鑾峰彇Access Token锛堢紦瀛?灏忔椂锛?
|
||||
// 获取Access Token(缓存2小时)
|
||||
async getAccessToken(): Promise<string>
|
||||
|
||||
// 鍙戦€佹枃鏈<EFBFBD>秷鎭?
|
||||
// 发送文本消息
|
||||
async sendTextMessage(userId: string, content: string): Promise<void>
|
||||
|
||||
// 发送Markdown消息(项目更新、质控报告)
|
||||
async sendMarkdownMessage(userId: string, content: string): Promise<void>
|
||||
|
||||
// 鍙戦€乀extcard鍗$墖娑堟伅锛堥」鐩<EFBFBD>€氱煡锛?
|
||||
// 发送Textcard卡片消息(项目通知)
|
||||
async sendTextcardMessage(userId: string, card: TextcardMessage): Promise<void>
|
||||
}
|
||||
```
|
||||
|
||||
**鍏抽敭鎶€鏈?*锛?
|
||||
- 鉁?Access Token缂撳瓨鏈哄埗锛堝唴瀛樼紦瀛橈紝2灏忔椂鏈夋晥鏈燂級
|
||||
- 鉁?浼佷笟寰<E7AC9F>俊API璋冪敤锛坄/cgi-bin/message/send`锛?
|
||||
- 鉁?瀹屾暣鐨勯敊璇<E6958A><E79287>鐞嗗拰鏃ュ織璁板綍
|
||||
- 鉁?鏀<>寔涓夌<E6B693>娑堟伅绫诲瀷锛坱ext/markdown/textcard锛?
|
||||
**关键技术**:
|
||||
- ✅ Access Token缓存机制(内存缓存,2小时有效期)
|
||||
- ✅ 企业微信API调用(`/cgi-bin/message/send`)
|
||||
- ✅ 完整的错误处理和日志记录
|
||||
- ✅ 支持三种消息类型(text/markdown/textcard)
|
||||
|
||||
**娴嬭瘯楠岃瘉**锛?
|
||||
- 鉁?浣跨敤浼佷笟寰<E7AC9F>俊瀹樻柟寮€鍙戝伐鍏锋祴璇曪紙`access_token` + 娑堟伅API锛?
|
||||
- 鉁?鏂囨湰娑堟伅娴嬭瘯閫氳繃
|
||||
- 鉁?Textcard鍗$墖娑堟伅娴嬭瘯閫氳繃
|
||||
- 鉁?Markdown娑堟伅娴嬭瘯閫氳繃
|
||||
- 鉁?鎵嬫満绔<E6BA80>紒涓氬井淇℃垚鍔熸帴鏀舵墍鏈夌被鍨嬫秷鎭?
|
||||
**测试验证**:
|
||||
- ✅ 使用企业微信官方开发工具测试(`access_token` + 消息API)
|
||||
- ✅ 文本消息测试通过
|
||||
- ✅ Textcard卡片消息测试通过
|
||||
- ✅ Markdown消息测试通过
|
||||
- ✅ 手机端企业微信成功接收所有类型消息
|
||||
|
||||
---
|
||||
|
||||
### 2.2 浼佷笟寰<EFBFBD>俊鍥炶皟澶勭悊锛圵echatCallbackController锛?
|
||||
### 2.2 企业微信回调处理(WechatCallbackController)
|
||||
|
||||
**鏂囦欢**锛歚backend/src/modules/iit-manager/controllers/WechatCallbackController.ts`锛?01琛岋級
|
||||
**文件**:`backend/src/modules/iit-manager/controllers/WechatCallbackController.ts`(501行)
|
||||
|
||||
**鏍稿績鍔熻兘**锛?
|
||||
**核心功能**:
|
||||
```typescript
|
||||
class WechatCallbackController {
|
||||
// URL验证(企业微信首次配置)
|
||||
@@ -93,19 +93,19 @@ class WechatCallbackController {
|
||||
}
|
||||
```
|
||||
|
||||
**鍏抽敭鎶€鏈?*锛?
|
||||
- 鉁?浼佷笟寰<E7AC9F>俊娑堟伅鍔犺В瀵嗭紙`@wecom/crypto`锛?
|
||||
- 鉁?XML娑堟伅瑙f瀽锛坄xml2js`锛?
|
||||
- 鉁?绛惧悕楠岃瘉锛圫HA1锛?
|
||||
- 鉁?寮傛<E5AFAE>鍥炲<E98DA5>妯″紡锛堢珛鍗宠繑鍥?success"锛屽悗鍙板<E98D99>鐞嗭級
|
||||
- 鉁?浣跨敤 `setImmediate` 纭<EFBFBD>繚寮傛<EFBFBD>鎵ц<EFBFBD>
|
||||
- 鉁?LLM鎰忓浘璇嗗埆锛圖ify锛? 澶欰gent璺<EFBFBD>敱
|
||||
**关键技术**:
|
||||
- ✅ 企业微信消息加解密(`@wecom/crypto`)
|
||||
- ✅ XML消息解析(`xml2js`)
|
||||
- ✅ 签名验证(SHA1)
|
||||
- ✅ 异步回复模式(立即返回"success",后台处理)
|
||||
- ✅ 使用 `setImmediate` 确保异步执行
|
||||
- ✅ LLM意图识别(Dify)+ 多Agent路由
|
||||
|
||||
**娴嬭瘯楠岃瘉**锛?
|
||||
- 鉁?浼佷笟寰<E7AC9F>俊鍥炶皟URL楠岃瘉閫氳繃
|
||||
- 鉁?natapp鍐呯綉绌块€忛厤缃<EFBFBD>垚鍔燂紙`http://iit.nat100.top`锛?
|
||||
- 鉁?娑堟伅鍔犺В瀵嗘祴璇曢€氳繃
|
||||
- 鈴革笍 鐢ㄦ埛娑堟伅澶勭悊閫昏緫锛堝緟鍚庣画鎵╁睍锛?
|
||||
**测试验证**:
|
||||
- ✅ 企业微信回调URL验证通过
|
||||
- ✅ natapp内网穿透配置成功(`http://iit.nat100.top`)
|
||||
- ✅ 消息加解密测试通过
|
||||
- ⏸️ 用户消息处理逻辑(待后续扩展)
|
||||
|
||||
---
|
||||
|
||||
@@ -113,7 +113,7 @@ class WechatCallbackController {
|
||||
|
||||
**文件**:`backend/src/modules/iit-manager/index.ts`
|
||||
|
||||
**鏍稿績鍔熻兘**锛?
|
||||
**核心功能**:
|
||||
```typescript
|
||||
// Worker注册
|
||||
jobQueue.process<IitQualityCheckJob>('iit_quality_check', async (job) => {
|
||||
@@ -125,7 +125,7 @@ jobQueue.process<IitQualityCheckJob>('iit_quality_check', async (job) => {
|
||||
// 2. 获取UserID(环境变量优先)
|
||||
const piUserId = process.env.WECHAT_TEST_USER_ID || 'FengZhiBo';
|
||||
|
||||
// 3. 鎵ц<EFBFBD>璐ㄦ帶妫€鏌?
|
||||
// 3. 执行质控检查
|
||||
const qualityCheckResult = await performQualityCheck(...);
|
||||
|
||||
// 4. 发送企业微信通知
|
||||
@@ -144,7 +144,7 @@ async function performQualityCheck(projectId, recordId, instrument) {
|
||||
const issues = [];
|
||||
const recommendations = [];
|
||||
|
||||
// 鍩虹<EFBFBD>妫€鏌?
|
||||
// 基础检查
|
||||
if (!recordId || recordId.trim() === '') {
|
||||
issues.push('记录ID无效');
|
||||
}
|
||||
@@ -161,102 +161,102 @@ async function performQualityCheck(projectId, recordId, instrument) {
|
||||
|
||||
const timeDiff = Date.now() - recentLogs[0].created_at.getTime();
|
||||
if (timeDiff < 3600000) {
|
||||
recommendations.push('鉁?鏁版嵁褰曞叆鍙婃椂');
|
||||
recommendations.push('✅ 数据录入及时');
|
||||
}
|
||||
|
||||
return { issues, recommendations };
|
||||
}
|
||||
```
|
||||
|
||||
**娴嬭瘯楠岃瘉**锛?
|
||||
- 鉁?Worker鎴愬姛娉ㄥ唽鍒皃g-boss
|
||||
- 鉁?REDCap DET瑙﹀彂 鈫?浠诲姟鎺ㄩ€?鈫?Worker鎵ц<EFBFBD>
|
||||
- 鉁?璐ㄦ帶妫€鏌ラ€昏緫鎵ц<E98EB5>姝e父
|
||||
- 鉁?浼佷笟寰<E7AC9F>俊閫氱煡鍙戦€佹垚鍔?
|
||||
- 鉁?瀹¤<E780B9>鏃ュ織璁板綍鎴愬姛
|
||||
**测试验证**:
|
||||
- ✅ Worker成功注册到pg-boss
|
||||
- ✅ REDCap DET触发 → 任务推送 → Worker执行
|
||||
- ✅ 质控检查逻辑执行正常
|
||||
- ✅ 企业微信通知发送成功
|
||||
- ✅ 审计日志记录成功
|
||||
|
||||
---
|
||||
|
||||
### 2.4 Worker注册机制修复
|
||||
|
||||
**闂<EFBFBD><EFBFBD>**锛氫箣鍓?`initIitManager()` 鍑芥暟鏈<E69A9F><E98F88>璋冪敤锛屽<E9949B>鑷碬orker鏈<72>敞鍐?
|
||||
**问题**:之前 `initIitManager()` 函数未被调用,导致Worker未注册
|
||||
|
||||
**修复**:`backend/src/index.ts`
|
||||
|
||||
```typescript
|
||||
// 鉁?淇<><E6B787>鍓嶏紙Worker鏈<72>敞鍐岋級
|
||||
// ✅ 修复前(Worker未注册)
|
||||
async function start() {
|
||||
await jobQueue.start();
|
||||
|
||||
registerParseExcelWorker();
|
||||
logger.info('鉁?DC Tool C parse excel worker registered');
|
||||
logger.info('✅ DC Tool C parse excel worker registered');
|
||||
|
||||
// 鉂?蹇樿<E8B987>璋冪敤 initIitManager()
|
||||
// ❌ 忘记调用 initIitManager()
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 3000));
|
||||
}
|
||||
|
||||
// 鉁?淇<><E6B787>鍚庯紙Worker姝g‘娉ㄥ唽锛?
|
||||
// ✅ 修复后(Worker正确注册)
|
||||
async function start() {
|
||||
await jobQueue.start();
|
||||
|
||||
registerParseExcelWorker();
|
||||
logger.info('鉁?DC Tool C parse excel worker registered');
|
||||
logger.info('✅ DC Tool C parse excel worker registered');
|
||||
|
||||
// 鉁?娉ㄥ唽IIT Manager Workers
|
||||
// ✅ 注册IIT Manager Workers
|
||||
await initIitManager();
|
||||
logger.info('鉁?IIT Manager workers registered');
|
||||
logger.info('✅ IIT Manager workers registered');
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 3000));
|
||||
}
|
||||
```
|
||||
|
||||
**楠岃瘉**锛?
|
||||
- 鉁?鍚<>姩鏃ュ織鏄剧ず "IIT Manager workers registered"
|
||||
- 鉁?`iit_quality_check` Worker鎴愬姛澶勭悊浠诲姟
|
||||
- 鉁?`iit_redcap_poll` Worker宸叉敞鍐岋紙瀹氭椂浠诲姟宸叉殏鏃剁<EFBFBD>鐢<EFBFBD>級
|
||||
**验证**:
|
||||
- ✅ 启动日志显示 "IIT Manager workers registered"
|
||||
- ✅ `iit_quality_check` Worker成功处理任务
|
||||
- ✅ `iit_redcap_poll` Worker已注册(定时任务已暂时禁用)
|
||||
|
||||
---
|
||||
|
||||
### 2.5 数据库字段名修复
|
||||
|
||||
**闂<EFBFBD><EFBFBD>1**锛歚notification_config` 瀛楁<E7809B>涓嶅瓨鍦?
|
||||
**问题1**:`notification_config` 字段不存在
|
||||
|
||||
**原因**:Worker代码查询了数据库表中不存在的字段
|
||||
|
||||
**淇<EFBFBD><EFBFBD>**锛?
|
||||
**修复**:
|
||||
```typescript
|
||||
// 鉂?涔嬪墠锛堟煡璇<E785A1>笉瀛樺湪鐨勫瓧娈碉級
|
||||
// ❌ 之前(查询不存在的字段)
|
||||
SELECT id, name, redcap_project_id, notification_config
|
||||
FROM iit_schema.projects
|
||||
WHERE id = ${projectId}
|
||||
|
||||
// 鉁?鐜板湪锛堝彧鏌ヨ<E98F8C>瀛樺湪鐨勫瓧娈碉級
|
||||
// ✅ 现在(只查询存在的字段)
|
||||
SELECT id, name, redcap_project_id
|
||||
FROM iit_schema.projects
|
||||
WHERE id = ${projectId}
|
||||
|
||||
// UserID鐩存帴浠庣幆澧冨彉閲忚幏鍙栵紙娴嬭瘯妯″紡锛?
|
||||
// UserID直接从环境变量获取(测试模式)
|
||||
const piUserId = process.env.WECHAT_TEST_USER_ID || 'FengZhiBo';
|
||||
```
|
||||
|
||||
**闂<EFBFBD><EFBFBD>2**锛歚action` 瀛楁<E7809B>涓嶅瓨鍦<E793A8>紙搴斾负 `action_type`锛?
|
||||
**问题2**:`action` 字段不存在(应为 `action_type`)
|
||||
|
||||
**淇<EFBFBD><EFBFBD>**锛?
|
||||
**修复**:
|
||||
```typescript
|
||||
// 鉂?涔嬪墠
|
||||
// ❌ 之前
|
||||
INSERT INTO iit_schema.audit_logs (project_id, action, entity_id, details)
|
||||
WHERE action = 'redcap_data_received'
|
||||
|
||||
// 鉁?鐜板湪
|
||||
// ✅ 现在
|
||||
INSERT INTO iit_schema.audit_logs (project_id, action_type, entity_id, details)
|
||||
WHERE action_type = 'redcap_data_received'
|
||||
```
|
||||
|
||||
**楠岃瘉**锛?
|
||||
- 鉁?Worker鎵ц<EFBFBD>鏃犳暟鎹<EFBFBD>簱閿欒<EFBFBD>
|
||||
- 鉁?瀹¤<E780B9>鏃ュ織璁板綍鎴愬姛
|
||||
- 鉁?璐ㄦ帶浠诲姟瀹屾暣娴佺▼閫氳繃
|
||||
**验证**:
|
||||
- ✅ Worker执行无数据库错误
|
||||
- ✅ 审计日志记录成功
|
||||
- ✅ 质控任务完整流程通过
|
||||
|
||||
---
|
||||
|
||||
@@ -264,24 +264,24 @@ WHERE action_type = 'redcap_data_received'
|
||||
|
||||
### 3.1 测试环境
|
||||
|
||||
| 缁勪欢 | 閰嶇疆 | 鐘舵€?|
|
||||
| 组件 | 配置 | 状态 |
|
||||
|------|------|------|
|
||||
| REDCap | Docker 15.8.0 + 娴嬭瘯椤圭洰(PID 16) | 鉁?杩愯<E69DA9>涓?|
|
||||
| Node.js Backend | Fastify + pg-boss + Prisma | 鉁?杩愯<E69DA9>涓?|
|
||||
| PostgreSQL | Docker + iit_schema | 鉁?杩愯<E69DA9>涓?|
|
||||
| 浼佷笟寰<EFBFBD>俊 | 鑷<>缓搴旂敤 + 娴嬭瘯鐢ㄦ埛(FengZhiBo) | 鉁?宸查厤缃?|
|
||||
| natapp | 鍐呯綉绌块€?iit.nat100.top) | 鉁?宸查厤缃?|
|
||||
| REDCap | Docker 15.8.0 + 测试项目(PID 16) | ✅ 运行中 |
|
||||
| Node.js Backend | Fastify + pg-boss + Prisma | ✅ 运行中 |
|
||||
| PostgreSQL | Docker + iit_schema | ✅ 运行中 |
|
||||
| 企业微信 | 自建应用 + 测试用户(FengZhiBo) | ✅ 已配置 |
|
||||
| natapp | 内网穿透(iit.nat100.top) | ✅ 已配置 |
|
||||
|
||||
### 3.2 测试流程
|
||||
|
||||
**娴嬭瘯姝ラ<EFBFBD>**锛?
|
||||
1. 鉁?REDCap鍒涘缓鏂拌<EFBFBD>褰曪紙ID: 9锛?
|
||||
2. 鉁?REDCap DET瀹炴椂瑙﹀彂Webhook锛?绉掑欢杩燂級
|
||||
3. 鉁?Node.js WebhookController鎺ユ敹璇锋眰锛?10ms鍝嶅簲锛?
|
||||
4. 鉁?鎺ㄩ€佷换鍔″埌 `iit_quality_check` 闃熷垪
|
||||
5. 鉁?Worker鎵ц<EFBFBD>璐ㄦ帶妫€鏌?
|
||||
6. 鉁?鍙戦€佷紒涓氬井淇¢€氱煡
|
||||
7. 鉁?鎵嬫満绔<E6BA80>紒涓氬井淇℃帴鏀堕€氱煡
|
||||
**测试步骤**:
|
||||
1. ✅ REDCap创建新记录(ID: 9)
|
||||
2. ✅ REDCap DET实时触发Webhook(0秒延迟)
|
||||
3. ✅ Node.js WebhookController接收请求(<10ms响应)
|
||||
4. ✅ 推送任务到 `iit_quality_check` 队列
|
||||
5. ✅ Worker执行质控检查
|
||||
6. ✅ 发送企业微信通知
|
||||
7. ✅ 手机端企业微信接收通知
|
||||
|
||||
**测试记录**(ID: 9):
|
||||
```
|
||||
@@ -302,80 +302,80 @@ WHERE action_type = 'redcap_data_received'
|
||||
2026-01-03 14:02:08.042 [aiclinical-backend] info: 📋 Quality check completed
|
||||
{ issuesCount: 0, recommendationsCount: 3 }
|
||||
|
||||
2026-01-03 14:02:08.045 [aiclinical-backend] info: 鉁?瀹¤<E780B9>鏃ュ織璁板綍鎴愬姛
|
||||
2026-01-03 14:02:08.045 [aiclinical-backend] info: ✅ 审计日志记录成功
|
||||
{ recordId: "9" }
|
||||
|
||||
2026-01-03 14:02:08.048 [aiclinical-backend] info: 鉁?Quality check completed and notification sent
|
||||
2026-01-03 14:02:08.048 [aiclinical-backend] info: ✅ Quality check completed and notification sent
|
||||
{ piUserId: "FengZhiBo", hasIssues: false }
|
||||
```
|
||||
|
||||
**浼佷笟寰<EFBFBD>俊鎺ユ敹鍐呭<EFBFBD>**锛?
|
||||
**企业微信接收内容**:
|
||||
```
|
||||
📊 IIT Manager 数据录入通知
|
||||
|
||||
项目:test0102
|
||||
璁板綍ID锛?
|
||||
记录ID:9
|
||||
表单:demographics
|
||||
鏃堕棿锛?026-01-03 14:02:08
|
||||
时间:2026-01-03 14:02:08
|
||||
|
||||
馃挕 璐ㄦ帶寤鸿<E5AFA4>锛?
|
||||
1. 鉁?鏁版嵁褰曞叆鍙婃椂
|
||||
2. 鉁?璁板綍ID鏈夋晥
|
||||
3. 鉁?琛ㄥ崟锛歞emographics
|
||||
💡 质控建议:
|
||||
1. ✅ 数据录入及时
|
||||
2. ✅ 记录ID有效
|
||||
3. ✅ 表单:demographics
|
||||
|
||||
鉁?鏁版嵁璐ㄩ噺鑹<E599BA>ソ
|
||||
✅ 数据质量良好
|
||||
|
||||
💬 如有疑问,请回复"帮助"查看更多功能
|
||||
```
|
||||
|
||||
### 3.3 测试结果
|
||||
|
||||
| 娴嬭瘯椤?| 鏈熸湜 | 瀹為檯 | 鐘舵€?|
|
||||
| 测试项 | 期望 | 实际 | 状态 |
|
||||
|-------|------|------|------|
|
||||
| REDCap瑙﹀彂 | 淇濆瓨鍚庣珛鍗宠Е鍙?| 0绉掑欢杩?| 鉁?|
|
||||
| Webhook鎺ユ敹 | <10ms鍝嶅簲 | 5.8ms | 鉁?|
|
||||
| 浠诲姟鎺ㄩ€?| 鎴愬姛鎺ㄩ€佸埌闃熷垪 | 鎴愬姛 | 鉁?|
|
||||
| Worker鎵ц<EFBFBD> | Worker澶勭悊浠诲姟 | 鎴愬姛鎵ц<E98EB5> | 鉁?|
|
||||
| 璐ㄦ帶妫€鏌?| 杩斿洖璐ㄦ帶缁撴灉 | 3鏉″缓璁?| 鉁?|
|
||||
| 浼佷笟寰<EFBFBD>俊鎺ㄩ€?| 鍙戦€侀€氱煡鎴愬姛 | 鎴愬姛 | 鉁?|
|
||||
| 鎵嬫満鎺ユ敹 | 鎺ユ敹鍒伴€氱煡 | 鎴愬姛鎺ユ敹 | 鉁?|
|
||||
| 瀹¤<EFBFBD>鏃ュ織 | 璁板綍鍒版暟鎹<E69A9F>簱 | 鎴愬姛璁板綍 | 鉁?|
|
||||
| 寰<EFBFBD>幆鍙戦€?| 鍙<>彂閫佷竴娆?| 鍙<>彂閫佷竴娆?| 鉁?|
|
||||
| REDCap触发 | 保存后立即触发 | 0秒延迟 | ✅ |
|
||||
| Webhook接收 | <10ms响应 | 5.8ms | ✅ |
|
||||
| 任务推送 | 成功推送到队列 | 成功 | ✅ |
|
||||
| Worker执行 | Worker处理任务 | 成功执行 | ✅ |
|
||||
| 质控检查 | 返回质控结果 | 3条建议 | ✅ |
|
||||
| 企业微信推送 | 发送通知成功 | 成功 | ✅ |
|
||||
| 手机接收 | 接收到通知 | 成功接收 | ✅ |
|
||||
| 审计日志 | 记录到数据库 | 成功记录 | ✅ |
|
||||
| 循环发送 | 只发送一次 | 只发送一次 | ✅ |
|
||||
|
||||
**鍏抽敭鎸囨爣**锛?
|
||||
- 鉁?绔<>埌绔<E59F8C>欢杩燂細<2绉掞紙REDCap淇濆瓨 鈫?浼佷笟寰<E7AC9F>俊鎺ユ敹锛?
|
||||
- 鉁?Webhook鍝嶅簲鏃堕棿锛?.8ms
|
||||
- 鉁?Worker鎵ц<EFBFBD>鏃堕棿锛殈50ms
|
||||
- 鉁?娑堟伅鍙戦€佹垚鍔熺巼锛?00%锛堟祴璇?娆★紝鍏ㄩ儴鎴愬姛锛?
|
||||
- 鉁?鏃犲惊鐜<E6838A>彂閫侀棶棰?
|
||||
**关键指标**:
|
||||
- ✅ 端到端延迟:<2秒(REDCap保存 → 企业微信接收)
|
||||
- ✅ Webhook响应时间:5.8ms
|
||||
- ✅ Worker执行时间:~50ms
|
||||
- ✅ 消息发送成功率:100%(测试5次,全部成功)
|
||||
- ✅ 无循环发送问题
|
||||
|
||||
---
|
||||
|
||||
## 🔧 四、临时措施与技术债务
|
||||
|
||||
### 4.1 涓存椂鎺<EFBFBD>柦锛圡VP闃舵<EFBFBD>锛?
|
||||
### 4.1 临时措施(MVP阶段)
|
||||
|
||||
| 序号 | 临时措施 | 原因 | 计划改进时间 | 改进方案 |
|
||||
|------|---------|------|------------|---------|
|
||||
| 1 | **UserID纭<EFBFBD>紪鐮?* | 绠€鍖栨祴璇曟祦绋?| Phase 2 | 浠庨」鐩<E3808D>厤缃<E58EA4>〃璇诲彇 `notification_config.wechat_user_id` |
|
||||
| 2 | **瀹氭椂杞<EFBFBD><EFBFBD>绂佺敤** | MVP涓嶉渶瑕侊紝Webhook宸茶冻澶?| Phase 2 | 瀹炵幇 `jobQueue.schedule()` 鎴栦娇鐢?`node-cron` |
|
||||
| 3 | **璐ㄦ帶閫昏緫绠€鍖?* | 浠呭熀纭€妫€鏌ワ紝鏃燗I璐ㄦ帶 | Phase 1.5 | 闆嗘垚Dify RAG + 瑙勫垯寮曟搸 |
|
||||
| 4 | **瀹¤<EFBFBD>鏃ュ織瀛楁<EFBFBD>** | `notification_config` 瀛楁<EFBFBD>鏈<EFBFBD>垱寤?| Phase 2 | 娣诲姞JSONB瀛楁<EFBFBD>瀛樺偍浼佷笟寰<EFBFBD>俊閰嶇疆 |
|
||||
| 5 | **Access Token缂撳瓨** | 鍐呭瓨缂撳瓨锛岄噸鍚<EFBFBD>涪澶?| Phase 2 | 浣跨敤Redis鎴栨暟鎹<EFBFBD>簱缂撳瓨 |
|
||||
| 1 | **UserID硬编码** | 简化测试流程 | Phase 2 | 从项目配置表读取 `notification_config.wechat_user_id` |
|
||||
| 2 | **定时轮询禁用** | MVP不需要,Webhook已足够 | Phase 2 | 实现 `jobQueue.schedule()` 或使用 `node-cron` |
|
||||
| 3 | **质控逻辑简化** | 仅基础检查,无AI质控 | Phase 1.5 | 集成Dify RAG + 规则引擎 |
|
||||
| 4 | **审计日志字段** | `notification_config` 字段未创建 | Phase 2 | 添加JSONB字段存储企业微信配置 |
|
||||
| 5 | **Access Token缓存** | 内存缓存,重启丢失 | Phase 2 | 使用Redis或数据库缓存 |
|
||||
|
||||
**璇︾粏璇存槑**锛?
|
||||
**详细说明**:
|
||||
|
||||
#### 1. UserID纭<EFBFBD>紪鐮侊紙鐜<EFBFBD><EFBFBD>鍙橀噺锛?
|
||||
#### 1. UserID硬编码(环境变量)
|
||||
|
||||
**褰撳墠瀹炵幇**锛?
|
||||
**当前实现**:
|
||||
```typescript
|
||||
// 鐩存帴浠庣幆澧冨彉閲忚幏鍙?
|
||||
// 直接从环境变量获取
|
||||
const piUserId = process.env.WECHAT_TEST_USER_ID || 'FengZhiBo';
|
||||
```
|
||||
|
||||
**闂<EFBFBD><EFBFBD>**锛?
|
||||
- 鉂?鏃犳硶鏀<E7A1B6>寔澶氶」鐩<E3808D>€佸<E282AC>PI
|
||||
- 鉂?鐢熶骇鐜<E9AA87><E9909C>闇€瑕佹瘡涓<E798A1>」鐩<E3808D>厤缃<E58EA4>笉鍚岀殑UserID
|
||||
**问题**:
|
||||
- ❌ 无法支持多项目、多PI
|
||||
- ❌ 生产环境需要每个项目配置不同的UserID
|
||||
|
||||
**计划改进**(Phase 2):
|
||||
```typescript
|
||||
@@ -390,7 +390,7 @@ const piUserId = project.notificationConfig?.wechat_user_id
|
||||
|| 'FengZhiBo';
|
||||
```
|
||||
|
||||
**鏁版嵁搴揝chema鏀硅繘**锛?
|
||||
**数据库Schema改进**:
|
||||
```sql
|
||||
ALTER TABLE iit_schema.projects
|
||||
ADD COLUMN notification_config JSONB DEFAULT '{}'::jsonb;
|
||||
@@ -407,7 +407,7 @@ ADD COLUMN notification_config JSONB DEFAULT '{}'::jsonb;
|
||||
|
||||
#### 2. 定时轮询禁用
|
||||
|
||||
**褰撳墠瀹炵幇**锛?
|
||||
**当前实现**:
|
||||
```typescript
|
||||
// ⏸️ 暂时禁用定时轮询(MVP阶段,Webhook已足够)
|
||||
// TODO: Phase 2 - 实现定时轮询作为补充机制
|
||||
@@ -416,19 +416,19 @@ ADD COLUMN notification_config JSONB DEFAULT '{}'::jsonb;
|
||||
logger.info('IIT Manager: Scheduled job registration skipped (using Webhook only for MVP)');
|
||||
```
|
||||
|
||||
**闂<EFBFBD><EFBFBD>**锛?
|
||||
**问题**:
|
||||
- ⚠️ `jobQueue.schedule()` 方法不存在(`PgBossQueue` 未实现)
|
||||
- ⚠️ MVP阶段不需要定时轮询(REDCap DET已足够)
|
||||
|
||||
**计划改进方案**(Phase 2):
|
||||
|
||||
**鏂规<EFBFBD>A锛氫娇鐢?`node-cron`锛堟帹鑽愶級**
|
||||
**方案A:使用 `node-cron`(推荐)**
|
||||
```typescript
|
||||
import cron from 'node-cron';
|
||||
|
||||
// 姣?鍒嗛挓鎵ц<E98EB5>涓€娆?
|
||||
// 每5分钟执行一次
|
||||
cron.schedule('*/5 * * * *', async () => {
|
||||
logger.info('鈴?REDCap瀹氭椂杞<EFBFBD><EFBFBD>寮€濮?);
|
||||
logger.info('⏰ REDCap定时轮询开始');
|
||||
const syncManager = new SyncManager();
|
||||
await syncManager.handlePoll();
|
||||
}, {
|
||||
@@ -436,13 +436,13 @@ cron.schedule('*/5 * * * *', async () => {
|
||||
});
|
||||
```
|
||||
|
||||
**鏂规<EFBFBD>B锛氭墿灞?`PgBossQueue` 瀹炵幇 `schedule` 鏂规硶**
|
||||
**方案B:扩展 `PgBossQueue` 实现 `schedule` 方法**
|
||||
```typescript
|
||||
class PgBossQueue implements JobQueue {
|
||||
async schedule(name: string, cron: string, data: any, options?: any): Promise<string> {
|
||||
if (!this.boss) throw new Error('Queue not started');
|
||||
|
||||
// pg-boss 鏀<EFBFBD>寔 cron 琛ㄨ揪寮?
|
||||
// pg-boss 支持 cron 表达式
|
||||
return await this.boss.send(name, data, {
|
||||
...options,
|
||||
startAfter: new Date(),
|
||||
@@ -453,26 +453,26 @@ class PgBossQueue implements JobQueue {
|
||||
}
|
||||
```
|
||||
|
||||
**浼樺厛绾?*锛氫綆锛圵ebhook瓒冲<E79392>鍙<EFBFBD>潬锛屽畾鏃惰疆璇<E79686>粎浣滀负鍏滃簳锛?
|
||||
**优先级**:低(Webhook足够可靠,定时轮询仅作为兜底)
|
||||
|
||||
---
|
||||
|
||||
#### 3. 璐ㄦ帶閫昏緫绠€鍖?
|
||||
#### 3. 质控逻辑简化
|
||||
|
||||
**褰撳墠瀹炵幇**锛堝熀纭€妫€鏌ワ級锛?
|
||||
**当前实现**(基础检查):
|
||||
```typescript
|
||||
async function performQualityCheck(projectId, recordId, instrument) {
|
||||
const issues = [];
|
||||
const recommendations = [];
|
||||
|
||||
// 鉁?鍩虹<E98DA9>妫€鏌?
|
||||
// ✅ 基础检查
|
||||
if (!recordId) issues.push('记录ID无效');
|
||||
if (!instrument) issues.push('表单名称无效');
|
||||
|
||||
// 鉁?鏃舵晥鎬ф<E98EAC>鏌?
|
||||
// ✅ 时效性检查
|
||||
const timeDiff = Date.now() - lastUpdate;
|
||||
if (timeDiff < 3600000) {
|
||||
recommendations.push('鉁?鏁版嵁褰曞叆鍙婃椂');
|
||||
recommendations.push('✅ 数据录入及时');
|
||||
}
|
||||
|
||||
return { issues, recommendations };
|
||||
@@ -481,10 +481,10 @@ async function performQualityCheck(projectId, recordId, instrument) {
|
||||
|
||||
**计划改进**(Phase 1.5):
|
||||
|
||||
**AI璐ㄦ帶閫昏緫**锛?
|
||||
**AI质控逻辑**:
|
||||
```typescript
|
||||
async function performQualityCheck(projectId, recordId, instrument) {
|
||||
// 1. 鑾峰彇椤圭洰鐨勮川鎺ц<EFBFBD>鍒?
|
||||
// 1. 获取项目的质控规则
|
||||
const project = await prisma.iitProject.findUnique({
|
||||
where: { id: projectId },
|
||||
select: { cachedRules: true }
|
||||
@@ -509,7 +509,7 @@ async function performQualityCheck(projectId, recordId, instrument) {
|
||||
|
||||
const result = await difyResponse.json();
|
||||
|
||||
// 4. 瑙f瀽AI杩斿洖鐨勮川鎺х粨鏋?
|
||||
// 4. 解析AI返回的质控结果
|
||||
return {
|
||||
issues: result.issues || [],
|
||||
recommendations: result.recommendations || [],
|
||||
@@ -518,15 +518,15 @@ async function performQualityCheck(projectId, recordId, instrument) {
|
||||
}
|
||||
```
|
||||
|
||||
**浼樺厛绾?*锛氶珮锛堟牳蹇冧环鍊兼墍鍦<E5A28D>級
|
||||
**优先级**:高(核心价值所在)
|
||||
|
||||
---
|
||||
|
||||
#### 4. 审计日志字段
|
||||
|
||||
**褰撳墠闂<EFBFBD><EFBFBD>**锛?
|
||||
- 鉂?`projects` 琛ㄧ己灏?`notification_config` 瀛楁<EFBFBD>
|
||||
- 鉂?UserID鏆傛椂浠庣幆澧冨彉閲忚<EFBFBD>鍙?
|
||||
**当前问题**:
|
||||
- ❌ `projects` 表缺少 `notification_config` 字段
|
||||
- ❌ UserID暂时从环境变量读取
|
||||
|
||||
**计划改进**(Phase 2):
|
||||
```sql
|
||||
@@ -542,13 +542,13 @@ CREATE INDEX idx_projects_notification_config
|
||||
ON iit_schema.projects USING GIN (notification_config);
|
||||
```
|
||||
|
||||
**浼樺厛绾?*锛氫腑锛堝奖鍝嶅<E98D9D>椤圭洰鏀<E6B4B0>寔锛?
|
||||
**优先级**:中(影响多项目支持)
|
||||
|
||||
---
|
||||
|
||||
#### 5. Access Token缓存
|
||||
|
||||
**褰撳墠瀹炵幇**锛堝唴瀛樼紦瀛橈級锛?
|
||||
**当前实现**(内存缓存):
|
||||
```typescript
|
||||
class WechatService {
|
||||
private accessTokenCache: {
|
||||
@@ -560,7 +560,7 @@ class WechatService {
|
||||
};
|
||||
|
||||
async getAccessToken(): Promise<string> {
|
||||
// 妫€鏌ョ紦瀛?
|
||||
// 检查缓存
|
||||
if (this.accessTokenCache.token &&
|
||||
this.accessTokenCache.expiresAt &&
|
||||
Date.now() < this.accessTokenCache.expiresAt) {
|
||||
@@ -579,8 +579,8 @@ class WechatService {
|
||||
}
|
||||
```
|
||||
|
||||
**闂<EFBFBD><EFBFBD>**锛?
|
||||
- 鈿狅笍 閲嶅惎鏈嶅姟鍚庣紦瀛樹涪澶?
|
||||
**问题**:
|
||||
- ⚠️ 重启服务后缓存丢失
|
||||
- ⚠️ 多实例部署时无法共享缓存
|
||||
|
||||
**计划改进**(Phase 2):
|
||||
@@ -594,7 +594,7 @@ async getAccessToken(): Promise<string> {
|
||||
const cached = await redis.get('wechat:access_token');
|
||||
if (cached) return cached;
|
||||
|
||||
// 閲嶆柊鑾峰彇骞剁紦瀛?
|
||||
// 重新获取并缓存
|
||||
const response = await fetch(...);
|
||||
await redis.setex('wechat:access_token', 7000, response.access_token);
|
||||
|
||||
@@ -614,177 +614,176 @@ CREATE TABLE iit_schema.wechat_tokens (
|
||||
);
|
||||
```
|
||||
|
||||
**浼樺厛绾?*锛氫綆锛堝崟瀹炰緥閮ㄧ讲鍙<E8AEB2>帴鍙楋紝鍐呭瓨缂撳瓨宸茶冻澶燂級
|
||||
**优先级**:低(单实例部署可接受,内存缓存已足够)
|
||||
|
||||
---
|
||||
|
||||
### 4.2 技术债务清单
|
||||
|
||||
| 搴忓彿 | 鎶€鏈<E282AC>€哄姟 | 褰卞搷 | 浼樺厛绾?| 璁″垝鏃堕棿 |
|
||||
| 序号 | 技术债务 | 影响 | 优先级 | 计划时间 |
|
||||
|------|---------|------|-------|---------|
|
||||
| 1 | **璐ㄦ帶閫昏緫绠€鍖?* | 鏃燗I鑳藉姏锛屼环鍊兼湁闄?| 馃敶 楂?| Phase 1.5 |
|
||||
| 2 | **UserID纭<EFBFBD>紪鐮?* | 鏃犳硶澶氶」鐩<E3808D>儴缃?| 馃煚 涓?| Phase 2 |
|
||||
| 3 | **notification_config瀛楁<EFBFBD>缂哄け** | 鏃犳硶鐏垫椿閰嶇疆閫氱煡 | 馃煚 涓?| Phase 2 |
|
||||
| 4 | **瀹氭椂杞<EFBFBD><EFBFBD>鏈<EFBFBD>疄鐜?* | 鏃犲厹搴曟満鍒?| 馃煛 浣?| Phase 2 |
|
||||
| 5 | **Access Token鍐呭瓨缂撳瓨** | 閲嶅惎涓㈠け | 馃煛 浣?| Phase 2 |
|
||||
| 6 | **閿欒<EFBFBD>澶勭悊涓嶅畬鏁?* | 閮ㄥ垎寮傚父鏈<E788B6>崟鑾?| 馃煚 涓?| Phase 2 |
|
||||
| 1 | **质控逻辑简化** | 无AI能力,价值有限 | 🔴 高 | Phase 1.5 |
|
||||
| 2 | **UserID硬编码** | 无法多项目部署 | 🟠 中 | Phase 2 |
|
||||
| 3 | **notification_config字段缺失** | 无法灵活配置通知 | 🟠 中 | Phase 2 |
|
||||
| 4 | **定时轮询未实现** | 无兜底机制 | 🟡 低 | Phase 2 |
|
||||
| 5 | **Access Token内存缓存** | 重启丢失 | 🟡 低 | Phase 2 |
|
||||
| 6 | **错误处理不完整** | 部分异常未捕获 | 🟠 中 | Phase 2 |
|
||||
| 7 | **日志级别混乱** | info/debug/error混用 | 🟢 极低 | Phase 3 |
|
||||
|
||||
---
|
||||
|
||||
### 4.3 椋庨櫓涓庣紦瑙f帾鏂?
|
||||
### 4.3 风险与缓解措施
|
||||
|
||||
| 椋庨櫓 | 褰卞搷 | 姒傜巼 | 缂撹В鎺<D092>柦 | 鐘舵€?|
|
||||
| 风险 | 影响 | 概率 | 缓解措施 | 状态 |
|
||||
|------|------|------|---------|------|
|
||||
| Webhook澶辫触瀵艰嚧鏁版嵁涓㈠け | 馃敶 楂?| 馃煛 涓?| 瀹氭椂杞<E6A482><E69D9E>鍏滃簳 | 鈴革笍 鏆傛湭瀹炵幇 |
|
||||
| 浼佷笟寰<EFBFBD>俊API闄愭祦 | 馃煚 涓?| 馃煝 浣?| 闄愭祦鎺у埗 + 閲嶈瘯鏈哄埗 | 鈴革笍 寰呭疄鐜?|
|
||||
| Access Token杩囨湡 | 馃煚 涓?| 馃煛 涓?| 鑷<>姩鍒锋柊鏈哄埗 | 鉁?宸插疄鐜?|
|
||||
| 鏁版嵁搴撹繛鎺ュけ璐?| 馃敶 楂?| 馃煝 浣?| 杩炴帴姹?+ 閲嶈瘯 | 鉁?宸插疄鐜?|
|
||||
| Worker鎵ц<EFBFBD>澶辫触 | 馃煚 涓?| 馃煛 涓?| pg-boss鑷<73>姩閲嶈瘯 | 鉁?宸插疄鐜?|
|
||||
| Webhook失败导致数据丢失 | 🔴 高 | 🟡 中 | 定时轮询兜底 | ⏸️ 暂未实现 |
|
||||
| 企业微信API限流 | 🟠 中 | 🟢 低 | 限流控制 + 重试机制 | ⏸️ 待实现 |
|
||||
| Access Token过期 | 🟠 中 | 🟡 中 | 自动刷新机制 | ✅ 已实现 |
|
||||
| 数据库连接失败 | 🔴 高 | 🟢 低 | 连接池 + 重试 | ✅ 已实现 |
|
||||
| Worker执行失败 | 🟠 中 | 🟡 中 | pg-boss自动重试 | ✅ 已实现 |
|
||||
|
||||
---
|
||||
|
||||
## 馃搳 浜斻€佷唬鐮佺粺璁?
|
||||
## 📊 五、代码统计
|
||||
|
||||
### 5.1 鏍稿績浠g爜閲?
|
||||
### 5.1 核心代码量
|
||||
|
||||
| 模块 | 文件 | 行数 | 说明 |
|
||||
|------|------|------|------|
|
||||
| 浼佷笟寰<EFBFBD>俊鎺ㄩ€?| `WechatService.ts` | 314 | Access Token + 娑堟伅鎺ㄩ€?|
|
||||
| 企业微信推送 | `WechatService.ts` | 314 | Access Token + 消息推送 |
|
||||
| 企业微信回调 | `WechatCallbackController.ts` | 501 | URL验证 + 消息接收 |
|
||||
| 质控Worker | `index.ts` | 336 | Worker注册 + 质控逻辑 |
|
||||
| 路由配置 | `routes/index.ts` | 203 | 企业微信路由 |
|
||||
| 环境配置文档 | `WECHAT_ENV_CONFIG.md` | 401 | 企业微信配置指南 |
|
||||
| **总计** | - | **1,755** | Day 3新增代码 |
|
||||
|
||||
### 5.2 绱<EFBFBD><EFBFBD>浠g爜閲忥紙Day 1-3锛?
|
||||
### 5.2 累计代码量(Day 1-3)
|
||||
|
||||
| 闃舵<EFBFBD> | 浠g爜閲?| 璇存槑 |
|
||||
| 阶段 | 代码量 | 说明 |
|
||||
|------|-------|------|
|
||||
| Day 1 | 223琛?| 鏁版嵁搴揝chema + 妯″潡楠ㄦ灦 |
|
||||
| Day 2 | 2,200琛?| REDCap闆嗘垚 + Worker娉ㄥ唽 |
|
||||
| Day 3 | 1,755琛?| 浼佷笟寰<E7AC9F>俊闆嗘垚 + 绔<>埌绔<E59F8C>祴璇?|
|
||||
| **鎬昏<EFBFBD>** | **4,178琛?* | MVP鏍稿績浠g爜 |
|
||||
| Day 1 | 223行 | 数据库Schema + 模块骨架 |
|
||||
| Day 2 | 2,200行 | REDCap集成 + Worker注册 |
|
||||
| Day 3 | 1,755行 | 企业微信集成 + 端到端测试 |
|
||||
| **总计** | **4,178行** | MVP核心代码 |
|
||||
|
||||
---
|
||||
|
||||
## 馃幆 鍏<>€佹祴璇曡<E79287>鐩?
|
||||
## 🎯 六、测试覆盖
|
||||
|
||||
### 6.1 功能测试
|
||||
|
||||
| 娴嬭瘯鍦烘櫙 | 鐘舵€?| 璇存槑 |
|
||||
| 测试场景 | 状态 | 说明 |
|
||||
|---------|------|------|
|
||||
| 鉁?REDCap DET瑙﹀彂 | 閫氳繃 | 0绉掑欢杩?|
|
||||
| 鉁?Webhook鎺ユ敹 | 閫氳繃 | <10ms鍝嶅簲 |
|
||||
| 鉁?浠诲姟鎺ㄩ€?| 閫氳繃 | 鎺ㄩ€佸埌pg-boss闃熷垪 |
|
||||
| 鉁?Worker鎵ц<EFBFBD> | 閫氳繃 | 璐ㄦ帶閫昏緫鎵ц<E98EB5> |
|
||||
| 鉁?浼佷笟寰<E7AC9F>俊鎺ㄩ€侊紙鏂囨湰锛?| 閫氳繃 | 鎵嬫満鎺ユ敹鎴愬姛 |
|
||||
| 鉁?浼佷笟寰<E7AC9F>俊鎺ㄩ€侊紙鍗$墖锛?| 閫氳繃 | 鎵嬫満鎺ユ敹鎴愬姛 |
|
||||
| 鉁?浼佷笟寰<E7AC9F>俊鎺ㄩ€侊紙Markdown锛?| 閫氳繃 | 鎵嬫満鎺ユ敹鎴愬姛 |
|
||||
| 鉁?瀹¤<E780B9>鏃ュ織璁板綍 | 閫氳繃 | 鏁版嵁搴撹<E690B4>褰曟垚鍔?|
|
||||
| 鉁?寰<>幆鍙戦€侀棶棰?| 淇<><E6B787> | 鍙<>彂閫佷竴娆?|
|
||||
| 鈴革笍 浼佷笟寰<E7AC9F>俊鍥炶皟娑堟伅 | 鏈<>祴璇?| URL楠岃瘉閫氳繃锛岀敤鎴锋秷鎭<E7A7B7>緟娴嬭瘯 |
|
||||
| ✅ REDCap DET触发 | 通过 | 0秒延迟 |
|
||||
| ✅ Webhook接收 | 通过 | <10ms响应 |
|
||||
| ✅ 任务推送 | 通过 | 推送到pg-boss队列 |
|
||||
| ✅ Worker执行 | 通过 | 质控逻辑执行 |
|
||||
| ✅ 企业微信推送(文本) | 通过 | 手机接收成功 |
|
||||
| ✅ 企业微信推送(卡片) | 通过 | 手机接收成功 |
|
||||
| ✅ 企业微信推送(Markdown) | 通过 | 手机接收成功 |
|
||||
| ✅ 审计日志记录 | 通过 | 数据库记录成功 |
|
||||
| ✅ 循环发送问题 | 修复 | 只发送一次 |
|
||||
| ⏸️ 企业微信回调消息 | 未测试 | URL验证通过,用户消息待测试 |
|
||||
|
||||
### 6.2 性能测试
|
||||
|
||||
| 鎸囨爣 | 鐩<>爣 | 瀹為檯 | 鐘舵€?|
|
||||
| 指标 | 目标 | 实际 | 状态 |
|
||||
|------|------|------|------|
|
||||
| Webhook鍝嶅簲鏃堕棿 | <10ms | 5.8ms | 鉁?瓒呭嚭棰勬湡 |
|
||||
| Worker鎵ц<EFBFBD>鏃堕棿 | <100ms | ~50ms | 鉁?瓒呭嚭棰勬湡 |
|
||||
| 绔<EFBFBD>埌绔<EFBFBD>欢杩?| <5绉?| <2绉?| 鉁?瓒呭嚭棰勬湡 |
|
||||
| 娑堟伅鍙戦€佹垚鍔熺巼 | >99% | 100% | 鉁?瓒呭嚭棰勬湡 |
|
||||
| Webhook响应时间 | <10ms | 5.8ms | ✅ 超出预期 |
|
||||
| Worker执行时间 | <100ms | ~50ms | ✅ 超出预期 |
|
||||
| 端到端延迟 | <5秒 | <2秒 | ✅ 超出预期 |
|
||||
| 消息发送成功率 | >99% | 100% | ✅ 超出预期 |
|
||||
|
||||
---
|
||||
|
||||
## 馃摎 涓冦€佹枃妗f洿鏂?
|
||||
## 📚 七、文档更新
|
||||
|
||||
### 7.1 新增文档
|
||||
|
||||
1. **`WECHAT_ENV_CONFIG.md`**锛?01琛岋級
|
||||
1. **`WECHAT_ENV_CONFIG.md`**(401行)
|
||||
- 企业微信环境变量配置指南
|
||||
- IP鐧藉悕鍗曢厤缃?
|
||||
- natapp鍐呯綉绌块€忛厤缃?
|
||||
- IP白名单配置
|
||||
- natapp内网穿透配置
|
||||
- URL验证步骤
|
||||
- 常见问题排查
|
||||
|
||||
### 7.2 更新文档
|
||||
|
||||
1. **`00-妯″潡褰撳墠鐘舵€佷笌寮€鍙戞寚鍗?md`**
|
||||
- 鏇存柊寮€鍙戣繘搴︼紙Day 3瀹屾垚锛?
|
||||
1. **`00-模块当前状态与开发指南.md`**
|
||||
- 更新开发进度(Day 3完成)
|
||||
- 更新代码统计
|
||||
- 更新测试结果
|
||||
|
||||
2. **`MVP寮€鍙戜换鍔℃竻鍗?md`**
|
||||
2. **`MVP开发任务清单.md`**
|
||||
- 标记Day 3任务为已完成
|
||||
- 鏇存柊浠诲姟鐘舵€?
|
||||
- 更新任务状态
|
||||
|
||||
3. **`鏈€灏廙VP闂<EFBFBD>幆寮€鍙戣<EFBFBD>鍒?md`**
|
||||
- 鏇存柊寮€鍙戣繘搴?
|
||||
- 鏍囪<EFBFBD>鏍稿績闂<EFBFBD>幆宸叉墦閫?
|
||||
3. **`最小MVP闭环开发计划.md`**
|
||||
- 更新开发进度
|
||||
- 标记核心闭环已打通
|
||||
|
||||
---
|
||||
|
||||
## 馃殌 鍏<>€佷笅涓€姝ヨ<E5A79D>鍒掞紙Day 4锛?
|
||||
## 🚀 八、下一步计划(Day 4)
|
||||
|
||||
### 8.1 浼樺寲涓庡畬鍠?
|
||||
### 8.1 优化与完善
|
||||
|
||||
| 浠诲姟 | 浼樺厛绾?| 棰勪及鏃堕棿 |
|
||||
| 任务 | 优先级 | 预估时间 |
|
||||
|------|-------|---------|
|
||||
| 瀹屽杽閿欒<EFBFBD>澶勭悊 | 馃煚 涓?| 2灏忔椂 |
|
||||
| 浼樺寲鏃ュ織鏍煎紡 | 馃煛 浣?| 1灏忔椂 |
|
||||
| 娣诲姞鐩戞帶鎸囨爣 | 馃煛 浣?| 2灏忔椂 |
|
||||
| 鎬ц兘浼樺寲 | 馃煛 浣?| 1灏忔椂 |
|
||||
| 完善错误处理 | 🟠 中 | 2小时 |
|
||||
| 优化日志格式 | 🟡 低 | 1小时 |
|
||||
| 添加监控指标 | 🟡 低 | 2小时 |
|
||||
| 性能优化 | 🟡 低 | 1小时 |
|
||||
|
||||
### 8.2 Phase 1.5:AI质控能力
|
||||
|
||||
| 浠诲姟 | 浼樺厛绾?| 棰勪及鏃堕棿 |
|
||||
| 任务 | 优先级 | 预估时间 |
|
||||
|------|-------|---------|
|
||||
| 闆嗘垚Dify RAG | 馃敶 楂?| 4灏忔椂 |
|
||||
| 涓婁紶鐮旂┒鏂规<EFBFBD> | 馃敶 楂?| 1灏忔椂 |
|
||||
| 鐢熸垚璐ㄦ帶瑙勫垯 | 馃敶 楂?| 2灏忔椂 |
|
||||
| 娴嬭瘯AI璐ㄦ帶 | 馃敶 楂?| 1灏忔椂 |
|
||||
| 集成Dify RAG | 🔴 高 | 4小时 |
|
||||
| 上传研究方案 | 🔴 高 | 1小时 |
|
||||
| 生成质控规则 | 🔴 高 | 2小时 |
|
||||
| 测试AI质控 | 🔴 高 | 1小时 |
|
||||
|
||||
---
|
||||
|
||||
## 鉁?涔濄€佹€荤粨
|
||||
## ✅ 九、总结
|
||||
|
||||
### 9.1 核心成就
|
||||
|
||||
1. 鉁?**MVP闂<EFBFBD>幆鎵撻€?*锛歊EDCap 鈫?Node.js 鈫?浼佷笟寰<E7AC9F>俊瀹屾暣娴佺▼
|
||||
2. 鉁?**浼佷笟寰<E7AC9F>俊闆嗘垚**锛氭帹閫佹湇鍔?+ 鍥炶皟澶勭悊 + URL楠岃瘉
|
||||
3. 鉁?**璐ㄦ帶Worker瀹屽杽**锛氳川鎺ф<E98EBA>鏌?+ 閫氱煡鎺ㄩ€?+ 瀹¤<E780B9>鏃ュ織
|
||||
4. 鉁?**绔<>埌绔<E59F8C>祴璇曢€氳繃**锛氬疄娴?2绉掑欢杩燂紝100%鎴愬姛鐜?
|
||||
5. 鉁?**鏂囨。浣撶郴瀹屽杽**锛氱幆澧冮厤缃?+ 寮€鍙戣<E98D99>褰?+ 杩涘害璺熻釜
|
||||
1. ✅ **MVP闭环打通**:REDCap → Node.js → 企业微信完整流程
|
||||
2. ✅ **企业微信集成**:推送服务 + 回调处理 + URL验证
|
||||
3. ✅ **质控Worker完善**:质控检查 + 通知推送 + 审计日志
|
||||
4. ✅ **端到端测试通过**:实测<2秒延迟,100%成功率
|
||||
5. ✅ **文档体系完善**:环境配置 + 开发记录 + 进度跟踪
|
||||
|
||||
### 9.2 关键数据
|
||||
|
||||
- 馃摑 **鏂板<E98F82>浠g爜**锛?,755琛岋紙楂樿川閲忕敓浜т唬鐮侊級
|
||||
- 鈴憋笍 **寮€鍙戞椂闂?*锛?澶╋紙8灏忔椂锛?
|
||||
- 鉁?**娴嬭瘯閫氳繃鐜?*锛?00%锛?/9鍔熻兘娴嬭瘯锛?
|
||||
- 📝 **新增代码**:1,755行(高质量生产代码)
|
||||
- ⏱️ **开发时间**:1天(8小时)
|
||||
- ✅ **测试通过率**:100%(9/9功能测试)
|
||||
- 🚀 **性能表现**:端到端<2秒,超出预期
|
||||
- 馃摎 **鏂囨。瀹屽杽搴?*锛?01琛岄厤缃<E58EA4>寚鍗?+ 寮€鍙戣<E98D99>褰?
|
||||
- 📚 **文档完善度**:401行配置指南 + 开发记录
|
||||
|
||||
### 9.3 鎶€鏈<EFBFBD>寒鐐?
|
||||
### 9.3 技术亮点
|
||||
|
||||
1. **寮傛<EFBFBD>Worker鏋舵瀯**锛氱<E9949B>鍚圥ostgres-Only鏈€浣宠寖寮?
|
||||
2. **浼佷笟寰<EFBFBD>俊娑堟伅鍔犺В瀵?*锛氬畬鏁村疄鐜扮<E9909C>鍚嶉獙璇佸拰鍔犺В瀵?
|
||||
1. **异步Worker架构**:符合Postgres-Only最佳范式
|
||||
2. **企业微信消息加解密**:完整实现签名验证和加解密
|
||||
3. **异步回复模式**:`setImmediate` 确保5秒内响应
|
||||
4. **瀹屾暣鐨勯敊璇<EFBFBD><EFBFBD>鐞?*锛氬<E9949B>璁℃棩蹇楀け璐ヤ笉褰卞搷涓绘祦绋?
|
||||
5. **pg-boss閲嶈瘯鏈哄埗**锛氳嚜鍔ㄩ噸璇?娆★紝纭<E7B49D>繚鍙<E7B99A>潬鎬?
|
||||
4. **完整的错误处理**:审计日志失败不影响主流程
|
||||
5. **pg-boss重试机制**:自动重试3次,确保可靠性
|
||||
|
||||
### 9.4 MVP浠峰€奸獙璇?
|
||||
### 9.4 MVP价值验证
|
||||
|
||||
鉁?**瀹炴椂鎰熺煡**锛歅I鏃犻渶鐧诲綍REDCap锛屼紒涓氬井淇″嵆鏃堕€氱煡
|
||||
鉁?**涓诲姩閫氱煡**锛氭暟鎹<E69A9F>綍鍏ュ悗<2绉掓帹閫侊紝闆堕仐婕?
|
||||
鉁?**鏄撴墿灞?*锛氶棴鐜<E6A3B4>墦閫氬悗锛屽彲蹇<E5BDB2>€熸坊鍔燗I璐ㄦ帶銆佷换鍔℃彁閱掔瓑
|
||||
鉁?**鐢熶骇灏辩华**锛氫唬鐮佽川閲忛珮锛屾€ц兘绋冲畾锛屽彲鐩存帴閮ㄧ讲
|
||||
✅ **实时感知**:PI无需登录REDCap,企业微信即时通知
|
||||
✅ **主动通知**:数据录入后<2秒推送,零遗漏
|
||||
✅ **易扩展**:闭环打通后,可快速添加AI质控、任务提醒等
|
||||
✅ **生产就绪**:代码质量高,性能稳定,可直接部署
|
||||
|
||||
---
|
||||
|
||||
**缁存姢鑰?*锛欼IT Manager寮€鍙戝洟闃?
|
||||
**鏈€鍚庢洿鏂?*锛?026-01-03
|
||||
**鏂囨。鐘舵€?*锛氣渽 宸插畬鎴?
|
||||
|
||||
**维护者**:IIT Manager开发团队
|
||||
**最后更新**:2026-01-03
|
||||
**文档状态**:✅ 已完成
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,65 +1,65 @@
|
||||
# Day 3 - 隡<EFBFBD><EFBFBD>敺桐縑<EFBFBD><EFBFBD><EFBFBD>撘<EFBFBD><EFBFBD>穃<EFBFBD><EFBFBD>鞱扇敶?
|
||||
# Day 3 - 企业微信集成开发完成记录
|
||||
|
||||
**撘<EFBFBD><EFBFBD>烐𠯫<EFBFBD>?*嚗?026-01-02
|
||||
**撘<EFBFBD><EFBFBD>𤏸<EFBFBD>?*嚗鋫I + <20>冽<EFBFBD><E586BD>譍<EFBFBD>
|
||||
**开发日期**:2026-01-02
|
||||
**开发者**:AI + 用户协作
|
||||
**版本**:v1.3
|
||||
**<EFBFBD>嗆<EFBFBD>?*嚗尠<E59A97> 隡<><E99AA1>敺桐縑URL撉諹<E69289><E8ABB9>𣂼<EFBFBD>嚗<EFBFBD>抅蝖<E68A85><E89D96><EFBFBD><EFBFBD>摰峕<E691B0>
|
||||
**状态**:✅ 企业微信URL验证成功,基础集成完成
|
||||
|
||||
---
|
||||
|
||||
## <EFBFBD><EFBFBD> 撘<><E69298>烐<EFBFBD>閫?
|
||||
## 📊 开发概览
|
||||
|
||||
### 目标
|
||||
摰䂿緵隡<EFBFBD><EFBFBD>敺桐縑<EFBFBD><EFBFBD><EFBFBD>嚗<EFBFBD><EFBFBD><EFBFBD>祆<EFBFBD><EFBFBD>舀綫<EFBFBD><EFBFBD><EFBFBD><EFBFBD>噼<EFBFBD>憭<EFBFBD><EFBFBD>嚗䔶蛹 IIT Manager Agent 撱箇<EFBFBD>銝?PI <20><><EFBFBD><EFBFBD>𡁏<EFBFBD><F0A1818F>瓐<EFBFBD>?
|
||||
实现企业微信集成,包括消息推送和回调处理,为 IIT Manager Agent 建立与 PI 的沟通渠道。
|
||||
|
||||
### 成果
|
||||
- <EFBFBD>?隡<><E99AA1>敺桐縑瘨<E7B891><E798A8><EFBFBD>券<EFBFBD><E588B8><EFBFBD><EFBFBD>∴<EFBFBD>WechatService嚗?
|
||||
- <EFBFBD>?隡<><E99AA1>敺桐縑<E6A190>噼<EFBFBD>憭<EFBFBD><E686AD>嚗ÁechatCallbackController嚗?
|
||||
- <EFBFBD>?URL撉諹<EFBFBD>瘚贝<EFBFBD><EFBFBD>朞<EFBFBD>
|
||||
- <EFBFBD>?摰<><E691B0>韐冽綉Worker嚗峕𣈲<E5B395><F0A388B2><EFBFBD>銝𡁜凝靽⊥綫<E28AA5>?
|
||||
- <EFBFBD>?natapp<EFBFBD><EFBFBD><EFBFBD>蝛輸<EFBFBD>誯<EFBFBD>蝵格<EFBFBD><EFBFBD>?
|
||||
- ✅ 企业微信消息推送服务(WechatService)
|
||||
- ✅ 企业微信回调处理(WechatCallbackController)
|
||||
- ✅ URL验证测试通过
|
||||
- ✅ 完善质控Worker,支持企业微信推送
|
||||
- ✅ natapp内网穿透配置成功
|
||||
|
||||
### 进度
|
||||
- 璅∪<EFBFBD><EFBFBD>港<EFBFBD>摰峕<EFBFBD>摨佗<EFBFBD>**35% <EFBFBD>?50%**
|
||||
- 隡<EFBFBD><EFBFBD>敺桐縑<EFBFBD><EFBFBD><EFBFBD>嚗?*0% <EFBFBD>?80%**嚗㇎RL撉諹<EFBFBD>摰峕<EFBFBD>嚗<EFBFBD><EFBFBD>蝡臬<EFBFBD>蝡舀<EFBFBD>霂𤏪<EFBFBD>
|
||||
- 模块整体完成度:**35% → 50%**
|
||||
- 企业微信集成:**0% → 80%**(URL验证完成,待端到端测试)
|
||||
|
||||
---
|
||||
|
||||
## <EFBFBD><EFBFBD>儭?<3F>嗆<EFBFBD>霈曇恣
|
||||
## 🏗️ 架构设计
|
||||
|
||||
### 核心组件
|
||||
|
||||
```
|
||||
<EFBFBD>𢞖<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>?
|
||||
<EFBFBD>? 隡<EFBFBD><EFBFBD>敺桐縑<EFBFBD><EFBFBD><EFBFBD><EFBFBD>嗆<EFBFBD> <EFBFBD>?
|
||||
<EFBFBD>鎿<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>?
|
||||
<EFBFBD>? <EFBFBD>?
|
||||
<EFBFBD>? REDCap DET <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>? WebhookController <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>? JobQueue <EFBFBD>?
|
||||
<EFBFBD>? <EFBFBD>? <EFBFBD>? <EFBFBD>?
|
||||
<EFBFBD>? Audit Logs iit_quality_check <EFBFBD>?
|
||||
<EFBFBD>? Worker <EFBFBD>?
|
||||
<EFBFBD>? <EFBFBD>? <EFBFBD>?
|
||||
<EFBFBD>? WechatService <EFBFBD>?
|
||||
<EFBFBD>? <EFBFBD>? <EFBFBD>?
|
||||
<EFBFBD>? 隡<EFBFBD><EFBFBD>敺桐縑 API <EFBFBD>?
|
||||
<EFBFBD>? <EFBFBD>? <EFBFBD>?
|
||||
<EFBFBD>? PI <EFBFBD>𧢲㦤 <EFBFBD>?
|
||||
<EFBFBD>? <EFBFBD>?
|
||||
<EFBFBD>? 隡<><E99AA1>敺桐縑瘨<E7B891><E798A8> <20><><EFBFBD><EFBFBD><EFBFBD>? WechatCallbackController <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>? AI憭<49><E686AD> <EFBFBD>?
|
||||
<EFBFBD>? <EFBFBD>? <EFBFBD>?
|
||||
<EFBFBD>? WechatService <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>? 銝餃𢆡<E9A483>券<EFBFBD><E588B8><EFBFBD>憭? <EFBFBD>?
|
||||
<EFBFBD>? <EFBFBD>?
|
||||
<EFBFBD>婙<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>?
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ 企业微信集成架构 │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ REDCap DET ──→ WebhookController ──→ JobQueue │
|
||||
│ ↓ ↓ │
|
||||
│ Audit Logs iit_quality_check │
|
||||
│ Worker │
|
||||
│ ↓ │
|
||||
│ WechatService │
|
||||
│ ↓ │
|
||||
│ 企业微信 API │
|
||||
│ ↓ │
|
||||
│ PI 手机 │
|
||||
│ │
|
||||
│ 企业微信消息 ──→ WechatCallbackController ──→ AI处理 │
|
||||
│ ↓ │
|
||||
│ WechatService ──→ 主动推送回复 │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 技术选型
|
||||
|
||||
| 蝏<EFBFBD>辣 | <20><><EFBFBD>舀䲮獢?| <20>笔<EFBFBD> |
|
||||
| 组件 | 技术方案 | 原因 |
|
||||
|------|----------|------|
|
||||
| 瘨<EFBFBD><EFBFBD><EFBFBD>㰘圾撖?| @wecom/crypto | 隡<EFBFBD><EFBFBD>敺桐縑摰䀹䲮<EFBFBD>刻<EFBFBD>摨?|
|
||||
| XML閫<EFBFBD><EFBFBD> | xml2js | <EFBFBD>鞟<EFBFBD>蝔喳<EFBFBD><EFBFBD><EFBFBD>ML閫<EFBFBD><EFBFBD>摨?|
|
||||
| <EFBFBD><EFBFBD><EFBFBD>蝛輸<EFBFBD>?| natapp | <EFBFBD>砍𧑐撘<EFBFBD><EFBFBD>𤏸<EFBFBD>霂?|
|
||||
| 撘<EFBFBD>郊憭<EFBFBD><EFBFBD> | setImmediate | 閫<EFBFBD><EFBFBD>5蝘坿<EFBFBD><EFBFBD>園<EFBFBD><EFBFBD>?|
|
||||
| 消息加解密 | @wecom/crypto | 企业微信官方推荐库 |
|
||||
| XML解析 | xml2js | 成熟稳定的XML解析库 |
|
||||
| 内网穿透 | natapp | 本地开发调试 |
|
||||
| 异步处理 | setImmediate | 规避5秒超时限制 |
|
||||
|
||||
---
|
||||
|
||||
@@ -68,18 +68,18 @@
|
||||
### 1. WechatService.ts(企业微信推送服务)
|
||||
|
||||
**文件路径**:`backend/src/modules/iit-manager/services/WechatService.ts`
|
||||
**隞<EFBFBD><EFBFBD>銵峕㺭**嚗?14銵?
|
||||
**代码行数**:314行
|
||||
|
||||
**<EFBFBD>詨<EFBFBD><EFBFBD>蠘<EFBFBD>**嚗?
|
||||
**核心功能**:
|
||||
```typescript
|
||||
class WechatService {
|
||||
// 1. Access Token 蝞∠<EFBFBD>嚗<EFBFBD><EFBFBD>摮?+ <20>芸𢆡<E88AB8>瑟鰵嚗?
|
||||
// 1. Access Token 管理(缓存 + 自动刷新)
|
||||
async getAccessToken(): Promise<string>
|
||||
|
||||
// 2. <EFBFBD>煾<EFBFBD><EFBFBD><EFBFBD><EFBFBD>祆<EFBFBD><EFBFBD>?
|
||||
// 2. 发送文本消息
|
||||
async sendTextMessage(userId: string, content: string): Promise<void>
|
||||
|
||||
// 3. <EFBFBD>煾<EFBFBD>?Markdown 瘨<EFBFBD><EFBFBD>
|
||||
// 3. 发送 Markdown 消息
|
||||
async sendMarkdownMessage(userId: string, content: string): Promise<void>
|
||||
|
||||
// 4. 审计日志记录
|
||||
@@ -87,13 +87,13 @@ class WechatService {
|
||||
}
|
||||
```
|
||||
|
||||
**<EFBFBD><EFBFBD><EFBFBD>臭漁<EFBFBD>?*嚗?
|
||||
- <20>?Access Token 蝻枏<EFBFBD><EFBFBD>箏<EFBFBD>嚗?200蝘𡜐<E89D98><F0A19C90>𣂼<EFBFBD>5<EFBFBD><35><EFBFBD><EFBFBD>瑟鰵嚗?
|
||||
- <20>?摰峕㟲<E5B395><E39FB2><EFBFBD>霂臬<E99C82><E887AC><EFBFBD><EFBFBD><EFBFBD>滩<EFBFBD><E6BBA9>箏<EFBFBD>
|
||||
- <20>?霂衣<E99C82><E8A1A3><EFBFBD>𠯫敹𡑒扇敶𤏪<E695B6>corpId<EFBFBD><EFBFBD>gentId蝑㚁<EFBFBD>
|
||||
- <20>?摰∟恣<E2889F>亙<EFBFBD><E4BA99>芸𢆡霈啣<E99C88>
|
||||
**技术亮点**:
|
||||
- ✅ Access Token 缓存机制(7200秒,提前5分钟刷新)
|
||||
- ✅ 完整的错误处理和重试机制
|
||||
- ✅ 详细的日志记录(corpId、agentId等)
|
||||
- ✅ 审计日志自动记录
|
||||
|
||||
**<EFBFBD>臬<EFBFBD><EFBFBD>㗛<EFBFBD>**嚗?
|
||||
**环境变量**:
|
||||
```env
|
||||
WECHAT_CORP_ID=ww6ab493470ab4f377
|
||||
WECHAT_AGENT_ID=1000002
|
||||
@@ -105,21 +105,21 @@ WECHAT_CORP_SECRET=AZIVxMtoLb0rEszXS81e4dBRl-I9kgTjygIS0cFfENU
|
||||
### 2. WechatCallbackController.ts(企业微信回调处理)
|
||||
|
||||
**文件路径**:`backend/src/modules/iit-manager/controllers/WechatCallbackController.ts`
|
||||
**隞<EFBFBD><EFBFBD>銵峕㺭**嚗?01銵?
|
||||
**代码行数**:501行
|
||||
|
||||
**<EFBFBD>詨<EFBFBD><EFBFBD>蠘<EFBFBD>**嚗?
|
||||
**核心功能**:
|
||||
```typescript
|
||||
class WechatCallbackController {
|
||||
// 1. URL 撉諹<EFBFBD>嚗𠃑ET 霂瑟<E99C82>嚗?
|
||||
// 1. URL 验证(GET 请求)
|
||||
async handleVerification(request, reply): Promise<void>
|
||||
|
||||
// 2. 瘨<EFBFBD><EFBFBD><EFBFBD>交𤣰嚗㇊OST 霂瑟<E99C82> + 撘<>郊憭<E9838A><E686AD>嚗?
|
||||
// 2. 消息接收(POST 请求 + 异步处理)
|
||||
async handleCallback(request, reply): Promise<void>
|
||||
|
||||
// 3. 异步消息处理
|
||||
private async processMessageAsync(...): Promise<void>
|
||||
|
||||
// 4. <EFBFBD>冽<EFBFBD>瘨<EFBFBD><EFBFBD>憭<EFBFBD><EFBFBD>嚗<EFBFBD><EFBFBD><EFBFBD>株<EFBFBD><EFBFBD>寥<EFBFBD> + 銝𡁜𦛚<F0A1819C>餉<EFBFBD>嚗?
|
||||
// 4. 用户消息处理(关键词匹配 + 业务逻辑)
|
||||
private async processUserMessage(message): Promise<void>
|
||||
|
||||
// 5. 签名验证
|
||||
@@ -127,14 +127,14 @@ class WechatCallbackController {
|
||||
}
|
||||
```
|
||||
|
||||
**<EFBFBD><EFBFBD><EFBFBD>臭漁<EFBFBD>?*嚗?
|
||||
- <EFBFBD>?**撘<>郊<EFBFBD>𧼮<EFBFBD>璅∪<E79285>**嚗𡁶<E59A97><F0A181B6>唾<EFBFBD><E594BE>裇"success"`嚗<><E59A97><EFBFBD>啣<EFBFBD>甇亙<E79487><E4BA99><EFBFBD><EFBFBD>閫<EFBFBD><E996AB>5蝘坿<E89D98><E59DBF>塚<EFBFBD>
|
||||
- <EFBFBD>?**瘨<><E798A8>閫<EFBFBD><E996AB>**嚗帋蝙<E5B88B>?`@wecom/crypto` <EFBFBD>?`decrypt(encodingAESKey, encrypt)` <EFBFBD>賣㺭
|
||||
- <EFBFBD>?**蝑曉<E89D91>撉諹<E69289>**嚗帋蝙<E5B88B>?`@wecom/crypto` <EFBFBD>?`getSignature(token, timestamp, nonce, data)` <EFBFBD>賣㺭
|
||||
- <EFBFBD>?**<2A>誩㦛霂<E3A69B><E99C82>**嚗𡁏𣈲<F0A1818F>?瘙<><E79899>?<3F>?撣桀𨭌"<22>?<3F>唳<EFBFBD><E594B3>?蝑匧<E89D91><E58CA7>株<EFBFBD>
|
||||
- <EFBFBD>?**銝餃𢆡<E9A483>券<EFBFBD>?*嚗𡁜<E59A97><F0A1819C><EFBFBD><EFBFBD><EFBFBD>𣂼<EFBFBD>銝餃𢆡靚<F0A286A1>鍂 WechatService <EFBFBD>券<EFBFBD><EFBFBD><EFBFBD>憭?
|
||||
**技术亮点**:
|
||||
- ✅ **异步回复模式**:立即返回`"success"`,后台异步处理(规避5秒超时)
|
||||
- ✅ **消息解密**:使用 `@wecom/crypto` 的 `decrypt(encodingAESKey, encrypt)` 函数
|
||||
- ✅ **签名验证**:使用 `@wecom/crypto` 的 `getSignature(token, timestamp, nonce, data)` 函数
|
||||
- ✅ **意图识别**:支持"汇总"、"帮助"、"新患者"等关键词
|
||||
- ✅ **主动推送**:处理完成后主动调用 WechatService 推送回复
|
||||
|
||||
**<EFBFBD>臬<EFBFBD><EFBFBD>㗛<EFBFBD>**嚗?
|
||||
**环境变量**:
|
||||
```env
|
||||
WECHAT_TOKEN=oXlRBm1YnvMy2SbDLbvAdDd5Gq3oBGq
|
||||
WECHAT_ENCODING_AES_KEY=v88eT3O9bMW897h4btr7v7qvQImlMf31edTQCmuhOhO
|
||||
@@ -142,9 +142,9 @@ WECHAT_ENCODING_AES_KEY=v88eT3O9bMW897h4btr7v7qvQImlMf31edTQCmuhOhO
|
||||
|
||||
---
|
||||
|
||||
### 3. 頝舐眏<EFBFBD>滨蔭嚗ǐoutes/index.ts嚗?
|
||||
### 3. 路由配置(routes/index.ts)
|
||||
|
||||
**<EFBFBD>啣<EFBFBD>頝舐眏**嚗?
|
||||
**新增路由**:
|
||||
```typescript
|
||||
// GET: URL验证(企业微信配置回调URL时使用)
|
||||
fastify.get('/api/v1/iit/wechat/callback',
|
||||
@@ -159,19 +159,19 @@ fastify.post('/api/v1/iit/wechat/callback',
|
||||
|
||||
---
|
||||
|
||||
### 4. 摰<EFBFBD><EFBFBD>韐冽綉Worker嚗ǎndex.ts嚗?
|
||||
### 4. 完善质控Worker(index.ts)
|
||||
|
||||
**文件路径**:`backend/src/modules/iit-manager/index.ts`
|
||||
**新增功能**:质控完成后自动推送企业微信通知
|
||||
|
||||
**隞<EFBFBD><EFBFBD><EFBFBD>餉<EFBFBD>**嚗?
|
||||
**代码逻辑**:
|
||||
```typescript
|
||||
jobQueue.process('iit_quality_check', async (job) => {
|
||||
// 1. 获取项目配置
|
||||
const project = await prisma.$queryRaw`...`;
|
||||
const piUserId = project.notification_config.wechat_user_id;
|
||||
|
||||
// 2. <EFBFBD>扯<EFBFBD>韐冽綉璉<EFBFBD><EFBFBD>?
|
||||
// 2. 执行质控检查
|
||||
const qualityCheckResult = await performSimpleQualityCheck(...);
|
||||
|
||||
// 3. 构建企业微信通知消息
|
||||
@@ -182,21 +182,21 @@ jobQueue.process('iit_quality_check', async (job) => {
|
||||
});
|
||||
```
|
||||
|
||||
**<EFBFBD>𡁶䰻瘨<EFBFBD><EFBFBD><EFBFBD>澆<EFBFBD>**嚗?
|
||||
**通知消息格式**:
|
||||
```
|
||||
📊 IIT Manager 数据录入通知
|
||||
|
||||
项目:test0102
|
||||
记录ID:xxx
|
||||
表单:xxx
|
||||
<EFBFBD>園𡢿嚗?026-01-02 23:55:00
|
||||
时间:2026-01-02 23:55:00
|
||||
|
||||
<EFBFBD>働 韐冽綉撱箄悅 (3憿?嚗?
|
||||
1. <EFBFBD>?<3F>唳旿敶訫<E695B6><E8A8AB>𦠜𧒄嚗?<3F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
2. <EFBFBD>?霈啣<E99C88>ID<49>㗇<EFBFBD>
|
||||
3. <EFBFBD>?銵典<E98AB5>嚗飱emographics
|
||||
💡 质控建议 (3项):
|
||||
1. ✅ 数据录入及时(5分钟内)
|
||||
2. ✅ 记录ID有效
|
||||
3. ✅ 表单:demographics
|
||||
|
||||
<EFBFBD>?<3F>唳旿韐券<E99F90><E588B8>臬末嚗峕<E59A97><E5B395>擧遬<E693A7>桅<EFBFBD>
|
||||
✅ 数据质量良好,无明显问题
|
||||
|
||||
💬 如有疑问,请回复"帮助"查看更多功能
|
||||
```
|
||||
@@ -205,38 +205,38 @@ jobQueue.process('iit_quality_check', async (job) => {
|
||||
|
||||
## 🔧 开发过程与问题解决
|
||||
|
||||
### <EFBFBD>桅<EFBFBD>1嚗𡁶㴓憓<EFBFBD><EFBFBD><EFBFBD>誩<EFBFBD>蝘唬<EFBFBD>銝<EFBFBD><EFBFBD>?<3F>𩤃<EFBFBD>
|
||||
### 问题1:环境变量名称不一致 ⚠️
|
||||
|
||||
**<EFBFBD>啗情**嚗?
|
||||
**现象**:
|
||||
```
|
||||
hasSecret: false
|
||||
Error: 隡<EFBFBD><EFBFBD>敺桐縑<EFBFBD>滨蔭銝滚<EFBFBD><EFBFBD>湛<EFBFBD>霂瑟<EFBFBD><EFBFBD>亦㴓憓<EFBFBD><EFBFBD><EFBFBD>?
|
||||
Error: 企业微信配置不完整,请检查环境变量
|
||||
```
|
||||
|
||||
**<EFBFBD>笔<EFBFBD>**嚗?
|
||||
**原因**:
|
||||
- 环境变量:`WECHAT_CORP_SECRET`
|
||||
- 代码读取:`WECHAT_AGENT_SECRET`
|
||||
|
||||
**閫<EFBFBD><EFBFBD><EFBFBD>寞<EFBFBD>**嚗?
|
||||
**解决方案**:
|
||||
```typescript
|
||||
// 靽格㺿<EFBFBD>?
|
||||
// 修改前
|
||||
agentSecret: process.env.WECHAT_AGENT_SECRET
|
||||
|
||||
// 靽格㺿<EFBFBD>?
|
||||
// 修改后
|
||||
agentSecret: process.env.WECHAT_CORP_SECRET
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### <EFBFBD>桅<EFBFBD>2嚗鋣wecom/crypto 撖澆<EFBFBD><EFBFBD>孵<EFBFBD><EFBFBD>躰秤 <20>?
|
||||
### 问题2:@wecom/crypto 导入方式错误 ❌
|
||||
|
||||
**<EFBFBD>啗情**嚗?
|
||||
**现象**:
|
||||
```
|
||||
TypeError: WXBizMsgCrypt is not a constructor
|
||||
```
|
||||
|
||||
**<EFBFBD>笔<EFBFBD>**嚗?
|
||||
`@wecom/crypto` 銝齿糓銝<EFBFBD>銝芰掩嚗諹<EFBFBD>峕糓撖澆枂鈭?銝芰𡠺蝡讠<E89DA1><E8AEA0>賣㺭嚗?
|
||||
**原因**:
|
||||
`@wecom/crypto` 不是一个类,而是导出了4个独立的函数:
|
||||
```javascript
|
||||
{
|
||||
decrypt: [Function: decrypt],
|
||||
@@ -246,33 +246,33 @@ TypeError: WXBizMsgCrypt is not a constructor
|
||||
}
|
||||
```
|
||||
|
||||
**閫<EFBFBD><EFBFBD><EFBFBD>寞<EFBFBD>**嚗?
|
||||
**解决方案**:
|
||||
```typescript
|
||||
// 靽格㺿<EFBFBD>㵪<EFBFBD><EFBFBD>躰秤嚗?
|
||||
// 修改前(错误)
|
||||
import WXBizMsgCrypt from '@wecom/crypto';
|
||||
this.wxcrypt = new WXBizMsgCrypt(token, aesKey, corpId);
|
||||
|
||||
// 靽格㺿<EFBFBD>𠬍<EFBFBD>甇<EFBFBD>&嚗?
|
||||
// 修改后(正确)
|
||||
const require = createRequire(import.meta.url);
|
||||
const { decrypt, encrypt, getSignature } = require('@wecom/crypto');
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### <EFBFBD>桅<EFBFBD>3嚗飱ecrypt <EFBFBD>賣㺭<EFBFBD><EFBFBD>㺭<EFBFBD>躰秤 <20>?
|
||||
### 问题3:decrypt 函数参数错误 ❌
|
||||
|
||||
**<EFBFBD>啗情**嚗?
|
||||
**现象**:
|
||||
```
|
||||
Error: invalid encodingAESKey
|
||||
```
|
||||
|
||||
**<EFBFBD>笔<EFBFBD>**嚗?
|
||||
<EFBFBD>朞<EFBFBD>瘚贝<EFBFBD><EFBFBD>𡁏𧋦<EFBFBD>𤑳緵嚗䈣decrypt` <20>賣㺭<E8B3A3>芷<EFBFBD>閬?**2銝芸<E98A9D><E88AB8>?*嚗?
|
||||
**原因**:
|
||||
通过测试脚本发现,`decrypt` 函数只需要 **2个参数**:
|
||||
```javascript
|
||||
function decrypt(encodingAESKey, encrypt) { ... }
|
||||
```
|
||||
|
||||
**閫<EFBFBD><EFBFBD><EFBFBD>寞<EFBFBD>**嚗?
|
||||
**解决方案**:
|
||||
```typescript
|
||||
// 修改前(错误 - 4个参数)
|
||||
const result = decrypt(this.token, this.encodingAESKey, this.corpId, echostr);
|
||||
@@ -285,51 +285,51 @@ const result = decrypt(this.encodingAESKey, echostr);
|
||||
|
||||
### 问题4:Token字符识别错误 ⚠️
|
||||
|
||||
**<EFBFBD>啗情**嚗?
|
||||
**现象**:
|
||||
```
|
||||
⚠️ 签名验证失败
|
||||
expected: 0b7cf05d6cb23ab9ce2efca6fdc659f32051eabe
|
||||
calculated: 6f79cabd3e9eea5eb10f55abdcf087ce6393d51d
|
||||
```
|
||||
|
||||
**<EFBFBD>笔<EFBFBD>**嚗?
|
||||
**原因**:
|
||||
Token的第3个字符容易混淆:
|
||||
- `oX1R...`嚗<>㺭摮?嚗?
|
||||
- `oXlR...`嚗<><E59A97><EFBFBD>坔<EFBFBD>瘥徃嚗?
|
||||
- `oX1R...`(数字1)
|
||||
- `oXlR...`(小写字母l)
|
||||
|
||||
<EFBFBD>𡒊垢<EFBFBD>亙<EFBFBD><EFBFBD>曄內<EFBFBD><EFBFBD>糓 `oXlR...`嚗<><E59A97><EFBFBD>耱嚗㚁<E59A97><E39A81>諹<EFBFBD>霂訫極<E8A8AB>瑚葉<E7919A>航<EFBFBD>颲枏<E9A2B2>鈭<EFBFBD>㺭摮?<3F>?
|
||||
后端日志显示的是 `oXlR...`(小写l),而调试工具中可能输入了数字1。
|
||||
|
||||
**閫<EFBFBD><EFBFBD><EFBFBD>寞<EFBFBD>**嚗?
|
||||
- <20>湔𦻖隞?`.env` <20><>辣憭滚<E686AD>蝎䁅斐嚗屸<E59A97><E5B1B8>齿<EFBFBD><E9BDBF>刻<EFBFBD><E588BB>?
|
||||
**解决方案**:
|
||||
- 直接从 `.env` 文件复制粘贴,避免手动输入
|
||||
- 确认 Token 为:`oXlRBm1YnvMy2SbDLbvAdDd5Gq3oBGq`
|
||||
|
||||
---
|
||||
|
||||
### 问题5:EncodingAESKey 更新 🔄
|
||||
|
||||
**<EFBFBD>啗情**嚗?
|
||||
<EFBFBD>抒<EFBFBD> EncodingAESKey <EFBFBD>航<EFBFBD><EFBFBD>澆<EFBFBD><EFBFBD>厰䔮憸睃紡<EFBFBD>渲圾撖<EFBFBD>仃韐乓<EFBFBD>?
|
||||
**现象**:
|
||||
旧的 EncodingAESKey 可能格式有问题导致解密失败。
|
||||
|
||||
**閫<EFBFBD><EFBFBD><EFBFBD>寞<EFBFBD>**嚗?
|
||||
**解决方案**:
|
||||
在企业微信管理后台重新生成:
|
||||
```
|
||||
旧值:zE4tcdBeekCHPUV015jCh9RVUydnCITINqSmCzg9xtO
|
||||
<EFBFBD>啣<EFBFBD>潘<EFBFBD>v88eT3O9bMW897h4btr7v7qvQImlMf31edTQCmuhOhO嚗?3雿㵪<E99BBF><E3B5AA>澆<EFBFBD>甇<EFBFBD>&嚗?
|
||||
新值:v88eT3O9bMW897h4btr7v7qvQImlMf31edTQCmuhOhO(43位,格式正确)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### <EFBFBD>桅<EFBFBD>6嚗䭰atapp <20><><EFBFBD>蝛輸<E89D9B>誯<EFBFBD>蝵?<3F><>
|
||||
### 问题6:natapp 内网穿透配置 🌐
|
||||
|
||||
**<EFBFBD><EFBFBD>瘙?*嚗?
|
||||
<EFBFBD>砍𧑐撘<EFBFBD><EFBFBD>𤑳㴓憓<EFBFBD><EFBFBD>閬<EFBFBD><EFBFBD>蝵?HTTPS URL <20>其<EFBFBD>隡<EFBFBD><E99AA1>敺桐縑<E6A190>噼<EFBFBD><E599BC>?
|
||||
**需求**:
|
||||
本地开发环境需要公网 HTTPS URL 用于企业微信回调。
|
||||
|
||||
**閫<EFBFBD><EFBFBD><EFBFBD>寞<EFBFBD>**嚗?
|
||||
**解决方案**:
|
||||
1. 使用 natapp 服务
|
||||
2. <20>滨蔭<E6BBA8>折<EFBFBD>嚗䫤http://iit.nat100.top` <EFBFBD>?`127.0.0.1:3001`
|
||||
2. 配置隧道:`http://iit.nat100.top` → `127.0.0.1:3001`
|
||||
3. natapp 自动提供 HTTPS 支持
|
||||
|
||||
**撉諹<EFBFBD>**嚗?
|
||||
**验证**:
|
||||
```bash
|
||||
curl https://iit.nat100.top/api/v1/iit/health
|
||||
# 返回:{"status":"ok","module":"iit-manager",...}
|
||||
@@ -339,11 +339,11 @@ curl https://iit.nat100.top/api/v1/iit/health
|
||||
|
||||
## 🧪 测试验证
|
||||
|
||||
### 瘚贝<EFBFBD>1嚗帋<EFBFBD>銝𡁜凝靽∪<EFBFBD><EFBFBD>𤏸<EFBFBD><EFBFBD><EFBFBD>霂訫極<EFBFBD>琿<EFBFBD>霂?<3F>?
|
||||
### 测试1:企业微信开发者调试工具验证 ✅
|
||||
|
||||
**撌亙<EFBFBD>**嚗帋<E59A97>銝𡁜凝靽∠恣<E288A0><E681A3><EFBFBD><EFBFBD>?<3F>?撘<><E69298>𤏸<EFBFBD><F0A48FB8>極<EFBFBD>?<3F>?瘚贝<E7989A><E8B49D>噼<EFBFBD>璅∪<E79285>
|
||||
**工具**:企业微信管理后台 → 开发者工具 → 测试回调模式
|
||||
|
||||
**<EFBFBD>滨蔭**嚗?
|
||||
**配置**:
|
||||
```
|
||||
URL: https://iit.nat100.top/api/v1/iit/wechat/callback
|
||||
Token: oXlRBm1YnvMy2SbDLbvAdDd5Gq3oBGq
|
||||
@@ -352,33 +352,33 @@ EchoStr: test12345678901234567890123
|
||||
ToUserName: ww6ab493470ab4f377
|
||||
```
|
||||
|
||||
**瘚贝<EFBFBD>蝏𤘪<EFBFBD>**嚗?
|
||||
**测试结果**:
|
||||
```
|
||||
<EFBFBD>?餈𥪜<E9A488><F0A5AA9C>嗆<EFBFBD><E59786><EFBFBD>request: <EFBFBD>𣂼<EFBFBD>
|
||||
<EFBFBD>?餈𥪜<E9A488>蝏𤘪<E89D8F>嚗?23456789012345678901 25嚗<EFBFBD>圾撖<EFBFBD><EFBFBD><EFBFBD>?3雿滚<E99BBF>蝚佗<E89D9A>
|
||||
<EFBFBD>?HTTP<EFBFBD>嗆<EFBFBD><EFBFBD><EFBFBD>嚗?00
|
||||
✅ 返回状态:request: 成功
|
||||
✅ 返回结果:123456789012345678901 25(解密后的23位字符)
|
||||
✅ HTTP状态码:200
|
||||
```
|
||||
|
||||
**<EFBFBD>𡒊垢<EFBFBD>亙<EFBFBD>**嚗?
|
||||
**后端日志**:
|
||||
```
|
||||
📥 收到企业微信 URL 验证请求
|
||||
nonce: "95zbplrrko5"
|
||||
echostrLength: 88
|
||||
<EFBFBD>?URL 撉諹<EFBFBD><EFBFBD>𣂼<EFBFBD>
|
||||
✅ URL 验证成功
|
||||
decryptedLength: 23
|
||||
statusCode: 200
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 瘚贝<EFBFBD>2嚗䭰atapp <20>折<EFBFBD>餈鮋<E9A488>𡁏<EFBFBD>扳<EFBFBD>霂?<3F>?
|
||||
### 测试2:natapp 隧道连通性测试 ✅
|
||||
|
||||
**瘚贝<EFBFBD><EFBFBD>賭誘**嚗?
|
||||
**测试命令**:
|
||||
```bash
|
||||
curl https://iit.nat100.top/api/v1/iit/health
|
||||
```
|
||||
|
||||
**餈𥪜<EFBFBD>蝏𤘪<EFBFBD>**嚗?
|
||||
**返回结果**:
|
||||
```json
|
||||
{
|
||||
"status": "ok",
|
||||
@@ -392,7 +392,7 @@ curl https://iit.nat100.top/api/v1/iit/health
|
||||
|
||||
## 📋 配置清单
|
||||
|
||||
### <EFBFBD>𡒊垢<EFBFBD>臬<EFBFBD><EFBFBD>㗛<EFBFBD>嚗Ê̄ackend/.env嚗?
|
||||
### 后端环境变量(backend/.env)
|
||||
|
||||
```env
|
||||
# ==========================================
|
||||
@@ -404,14 +404,14 @@ WECHAT_CORP_ID=ww6ab493470ab4f377
|
||||
WECHAT_AGENT_ID=1000002
|
||||
WECHAT_CORP_SECRET=AZIVxMtoLb0rEszXS81e4dBRl-I9kgTjygIS0cFfENU
|
||||
|
||||
# 隡<EFBFBD><EFBFBD>敺桐縑<EFBFBD>噼<EFBFBD><EFBFBD>滨蔭嚗<EFBFBD><EFBFBD><EFBFBD>臬<EFBFBD>閫<EFBFBD><EFBFBD>嚗?
|
||||
# 企业微信回调配置(消息加解密)
|
||||
WECHAT_TOKEN=oXlRBm1YnvMy2SbDLbvAdDd5Gq3oBGq
|
||||
WECHAT_ENCODING_AES_KEY=v88eT3O9bMW897h4btr7v7qvQImlMf31edTQCmuhOhO
|
||||
```
|
||||
|
||||
### 企业微信应用配置
|
||||
|
||||
**摨𠉛鍂靽⊥<EFBFBD>**嚗?
|
||||
**应用信息**:
|
||||
- 企业ID:`ww6ab493470ab4f377`
|
||||
- 应用名称:`IIT Manager Agent`
|
||||
- AgentID:`1000002`
|
||||
@@ -423,9 +423,9 @@ Token: oXlRBm1YnvMy2SbDLbvAdDd5Gq3oBGq
|
||||
EncodingAESKey: v88eT3O9bMW897h4btr7v7qvQImlMf31edTQCmuhOhO
|
||||
```
|
||||
|
||||
**<EFBFBD>臭縑<EFBFBD>笔<EFBFBD>**嚗?
|
||||
**可信域名**:
|
||||
```
|
||||
iit.xunzhengyixue.com嚗𠄎AE<EFBFBD>煺漣<EFBFBD>臬<EFBFBD>嚗?
|
||||
iit.xunzhengyixue.com(SAE生产环境)
|
||||
```
|
||||
|
||||
### natapp 配置
|
||||
@@ -433,8 +433,8 @@ iit.xunzhengyixue.com(SAE生产环境
|
||||
```
|
||||
隧道状态:Online
|
||||
公网URL:http://iit.nat100.top
|
||||
<EFBFBD>砍𧑐蝡臬藁嚗?27.0.0.1:3001
|
||||
HTTPS嚗朞䌊<EFBFBD>冽𣈲<EFBFBD>?
|
||||
本地端口:127.0.0.1:3001
|
||||
HTTPS:自动支持
|
||||
```
|
||||
|
||||
---
|
||||
@@ -443,64 +443,64 @@ HTTPS:自动支
|
||||
|
||||
| 文件 | 代码行数 | 主要功能 |
|
||||
|------|---------|---------|
|
||||
| WechatService.ts | 314銵?| 隡<><E99AA1>敺桐縑瘨<E7B891><E798A8><EFBFBD>券<EFBFBD>?|
|
||||
| WechatCallbackController.ts | 501銵?| 隡<><E99AA1>敺桐縑<E6A190>噼<EFBFBD>憭<EFBFBD><E686AD> |
|
||||
| index.ts嚗<EFBFBD>捶<EFBFBD>劬orker嚗?| +80銵?| 韐冽綉摰峕<E691B0><E5B395>擧綫<E693A7><E7B6AB><EFBFBD>𡁶䰻 |
|
||||
| routes/index.ts | +48銵?| 隡<><E99AA1>敺桐縑頝舐眏瘜典<E7989C> |
|
||||
| **<EFBFBD>餉恣** | **~943銵?* | 隡<EFBFBD><EFBFBD>敺桐縑<EFBFBD><EFBFBD><EFBFBD><EFBFBD>詨<EFBFBD>隞<EFBFBD><EFBFBD> |
|
||||
| WechatService.ts | 314行 | 企业微信消息推送 |
|
||||
| WechatCallbackController.ts | 501行 | 企业微信回调处理 |
|
||||
| index.ts(质控Worker) | +80行 | 质控完成后推送通知 |
|
||||
| routes/index.ts | +48行 | 企业微信路由注册 |
|
||||
| **总计** | **~943行** | 企业微信集成核心代码 |
|
||||
|
||||
---
|
||||
|
||||
## <EFBFBD>?撌脣<E6928C><E884A3>鞟<EFBFBD><E99E9F>蠘<EFBFBD>
|
||||
## ✅ 已完成的功能
|
||||
|
||||
- [x] 隡<EFBFBD><EFBFBD>敺桐縑 Access Token 蝞∠<EFBFBD>嚗<EFBFBD><EFBFBD>摮?<3F>瑟鰵嚗?
|
||||
- [x] 企业微信 Access Token 管理(缓存+刷新)
|
||||
- [x] 发送文本消息到企业微信
|
||||
- [x] <EFBFBD>煾<EFBFBD>?Markdown 瘨<EFBFBD><EFBFBD><EFBFBD>唬<EFBFBD>銝𡁜凝靽?
|
||||
- [x] 隡<EFBFBD><EFBFBD>敺桐縑 URL 撉諹<E69289>嚗𠃑ET霂瑟<E99C82>憭<EFBFBD><E686AD>嚗?
|
||||
- [x] 隡<EFBFBD><EFBFBD>敺桐縑瘨<EFBFBD><EFBFBD><EFBFBD>交𤣰嚗㇊OST霂瑟<EFBFBD>憭<EFBFBD><EFBFBD>嚗?
|
||||
- [x] 瘨<EFBFBD><EFBFBD>閫<EFBFBD><EFBFBD>嚗<EFBFBD>蝙<EFBFBD>?@wecom/crypto嚗?
|
||||
- [x] 蝑曉<EFBFBD>撉諹<EFBFBD>嚗<EFBFBD>蝙<EFBFBD>?@wecom/crypto嚗?
|
||||
- [x] 撘<EFBFBD>郊<EFBFBD>𧼮<EFBFBD>璅∪<EFBFBD>嚗<EFBFBD><EFBFBD><EFBFBD>?蝘坿<E89D98><E59DBF>塚<EFBFBD>
|
||||
- [x] 发送 Markdown 消息到企业微信
|
||||
- [x] 企业微信 URL 验证(GET请求处理)
|
||||
- [x] 企业微信消息接收(POST请求处理)
|
||||
- [x] 消息解密(使用 @wecom/crypto)
|
||||
- [x] 签名验证(使用 @wecom/crypto)
|
||||
- [x] 异步回复模式(规避5秒超时)
|
||||
- [x] 关键词意图识别(汇总、帮助、新患者)
|
||||
- [x] 质控Worker推送企业微信通知
|
||||
- [x] 审计日志记录
|
||||
- [x] natapp <EFBFBD><EFBFBD><EFBFBD>蝛輸<EFBFBD>誯<EFBFBD>蝵?
|
||||
- [x] natapp 内网穿透配置
|
||||
|
||||
---
|
||||
|
||||
## <EFBFBD>?敺<><E695BA><EFBFBD>鞟<EFBFBD><E99E9F>蠘<EFBFBD>
|
||||
## ⏳ 待完成的功能
|
||||
|
||||
- [ ] 保存正式的企业微信回调URL配置
|
||||
- [ ] <EFBFBD>滨蔭<EFBFBD>唳旿摨㮖葉<EFBFBD>?`wechat_user_id`嚗㇊I<EFBFBD><EFBFBD><EFBFBD>銝𡁜凝靽|serID嚗?
|
||||
- [ ] 蝡臬<EFBFBD>蝡舀<EFBFBD>霂𤏪<EFBFBD>REDCap <20>?隡<>凝<EFBFBD>券<EFBFBD><E588B8><EFBFBD>
|
||||
- [ ] LLM<EFBFBD>誩㦛霂<EFBFBD><EFBFBD>嚗<EFBFBD><EFBFBD>蝥批<EFBFBD><EFBFBD>株<EFBFBD><EFBFBD>寥<EFBFBD>嚗?
|
||||
- [ ] 配置数据库中的 `wechat_user_id`(PI的企业微信UserID)
|
||||
- [ ] 端到端测试(REDCap → 企微推送)
|
||||
- [ ] LLM意图识别(升级关键词匹配)
|
||||
- [ ] 对话功能完善(更多业务场景)
|
||||
- [ ] IP白名单配置(部署到SAE时)
|
||||
|
||||
---
|
||||
|
||||
## <EFBFBD><EFBFBD> 銝衤<E98A9D>甇亥恣<E4BAA5>?
|
||||
## 🚀 下一步计划
|
||||
|
||||
### Day 3 下午/晚上(可选)
|
||||
|
||||
1. **靽嘥<EFBFBD>隡<EFBFBD><EFBFBD>敺桐縑甇<EFBFBD><EFBFBD><EFBFBD>滨蔭**嚗?<3F><><EFBFBD>嚗?
|
||||
1. **保存企业微信正式配置**(5分钟)
|
||||
- 在企业微信管理后台保存回调URL配置
|
||||
- 勾选需要接收的消息类型
|
||||
|
||||
2. **<EFBFBD>滨蔭憿寧𤌍<EFBFBD>𡁶䰻**嚗?0<><30><EFBFBD>嚗?
|
||||
- <EFBFBD>瑕<EFBFBD> PI <EFBFBD><EFBFBD><EFBFBD>銝𡁜凝靽?UserID
|
||||
- <EFBFBD>湔鰵<EFBFBD>唳旿摨?`projects` 銵函<EFBFBD> `notification_config` 摮埈挾
|
||||
2. **配置项目通知**(10分钟)
|
||||
- 获取 PI 的企业微信 UserID
|
||||
- 更新数据库 `projects` 表的 `notification_config` 字段
|
||||
|
||||
3. **蝡臬<EFBFBD>蝡舀<EFBFBD>霂?*嚗?0<><30><EFBFBD>嚗?
|
||||
- <EFBFBD>?REDCap 銝剖<EFBFBD><EFBFBD>交<EFBFBD>霂閙㺭<EFBFBD>?
|
||||
3. **端到端测试**(30分钟)
|
||||
- 在 REDCap 中录入测试数据
|
||||
- 验证企业微信收到实时通知
|
||||
- 瘚贝<EFBFBD>撖寡<EFBFBD><EFBFBD>蠘<EFBFBD>嚗<EFBFBD><EFBFBD><EFBFBD>?撣桀𨭌"<22>?瘙<><E79899>?蝑匧<E89D91><E58CA7>株<EFBFBD>嚗?
|
||||
- 测试对话功能(发送"帮助"、"汇总"等关键词)
|
||||
|
||||
### Day 4(后续优化)
|
||||
|
||||
1. **LLM意图识别**
|
||||
- <EFBFBD>亙<EFBFBD> DeepSeek <EFBFBD>硋<EFBFBD>隞?LLM
|
||||
- 摰䂿緵<EFBFBD><EFBFBD>迤<EFBFBD>?AI Agent 撖寡<EFBFBD>
|
||||
- 接入 DeepSeek 或其他 LLM
|
||||
- 实现真正的 AI Agent 对话
|
||||
|
||||
2. **功能完善**
|
||||
- 更多对话场景(数据查询、统计分析)
|
||||
@@ -514,12 +514,12 @@ HTTPS:自动支
|
||||
|
||||
---
|
||||
|
||||
## <EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>獢?
|
||||
## 📖 参考文档
|
||||
|
||||
- [企业微信API文档](https://developer.work.weixin.qq.com/document/path/90664)
|
||||
- [企业微信消息加解密说明](https://developer.work.weixin.qq.com/document/path/90968)
|
||||
- [@wecom/crypto GitHub](https://github.com/wecomteam/crypto)
|
||||
- [<EFBFBD><EFBFBD>撠𦬅VP<EFBFBD>剔㴓撘<EFBFBD><EFBFBD>𤏸恣<EFBFBD>哋(../04-撘<><E69298>𤏸恣<F0A48FB8>?<3F><>撠𦬅VP<56>剔㴓撘<E3B493><E69298>𤏸恣<F0A48FB8>?md)
|
||||
- [最小MVP闭环开发计划](../04-开发计划/最小MVP闭环开发计划.md)
|
||||
|
||||
---
|
||||
|
||||
@@ -527,20 +527,19 @@ HTTPS:自动支
|
||||
|
||||
Day 3 的开发工作虽然遇到了多个技术问题,但最终成功完成了企业微信集成的核心功能。通过调试工具的验证,证明了:
|
||||
|
||||
1. <EFBFBD>?**<2A><><EFBFBD>舀䲮獢<E4B2AE>虾銵?*嚗鋣wecom/crypto 摨𤘪迤撣詨極雿?
|
||||
2. <EFBFBD>?**<2A>嗆<EFBFBD>霈曇恣<E69B87><E681A3><EFBFBD>**嚗𡁜<E59A97>甇亙<E79487>憭齿芋撘𤩺<E69298><F0A4A9BA><EFBFBD><EFBFBD><EFBFBD>輯<EFBFBD><E8BCAF>園䔮憸?
|
||||
3. <EFBFBD>?**隞<><E99A9E>韐券<E99F90><E588B8>臬末**嚗朞祕蝏<E7A595><E89D8F><EFBFBD>亙<EFBFBD><E4BA99>屸<EFBFBD>霂臬<E99C82><E887AC>?
|
||||
4. <EFBFBD>?**撘<><E69298>烐<EFBFBD>蝔见<E89D94><E8A781>?*嚗𡁻䔮憸䀹<E686B8><E480B9>乒<EFBFBD>瘚贝<E7989A>撉諹<E69289><E8ABB9>埝<EFBFBD>獢<EFBFBD>扇敶?
|
||||
1. ✅ **技术方案可行**:@wecom/crypto 库正常工作
|
||||
2. ✅ **架构设计合理**:异步回复模式有效规避超时问题
|
||||
3. ✅ **代码质量良好**:详细的日志和错误处理
|
||||
4. ✅ **开发流程完善**:问题排查→测试验证→文档记录
|
||||
|
||||
**頝萘氖摰峕㟲<EFBFBD>?MVP <20>剔㴓<E58994>芸榆<E88AB8><E6A686><EFBFBD>𡒊<EFBFBD>蝡臬<E89DA1>蝡舀<E89DA1>霂蓥<E99C82>嚗?*
|
||||
**距离完整的 MVP 闭环只差最后的端到端测试了!**
|
||||
|
||||
---
|
||||
|
||||
**霈啣<EFBFBD>鈭?*嚗鋫I Assistant
|
||||
**摰⊥瓲鈭?*嚗𡁜<E59A97><F0A1819C>穃𣪧<E7A983>?
|
||||
**记录人**:AI Assistant
|
||||
**审核人**:开发团队
|
||||
**文档版本**:v1.0
|
||||
**<EFBFBD><EFBFBD><EFBFBD>擧凒<EFBFBD>?*嚗?026-01-02 23:55:00
|
||||
|
||||
**最后更新**:2026-01-02 23:55:00
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
# **IIT Manager Agent 技术方案审查与补丁 (V1.1)**
|
||||
|
||||
## **1\. <EFBFBD>嗆<EFBFBD>靽格迤嚗朞圾<EFBFBD>喳龫<EFBFBD>W<EFBFBD>蝵𤏸<EFBFBD><EFBFBD>𡁏<EFBFBD>?*
|
||||
## **1\. 架构修正:解决医院内网连通性**
|
||||
|
||||
<EFBFBD><EFBFBD>笆 **憌𡡞埯銝<EFBFBD> (蝵𤑳<E89DB5>餈鮋<E9A488>𡁏<EFBFBD>?**嚗<>遣霈桀銁 3.1 REDCap <20><><EFBFBD> 銝剖<E98A9D><E58996>?**"瘛瑕<E7989B><E79195>峕郊璅∪<E79285>"**<EFBFBD>?
|
||||
### **<2A>啣<EFBFBD>璅∪<E79285>嚗锭yncManager (<28>峕郊蝞∠<E89D9E><E288A0>?**
|
||||
针对 **风险一 (网络连通性)**,建议在 3.1 REDCap 集成 中增加 **"混合同步模式"**。
|
||||
|
||||
### **新增模块:SyncManager (同步管理器)**
|
||||
|
||||
// backend/src/modules/iit-manager/services/SyncManager.ts
|
||||
|
||||
@@ -20,13 +21,13 @@ export class SyncManager {
|
||||
}
|
||||
|
||||
/\*\*
|
||||
\* 頧株砭隞餃𦛚憭<EFBFBD><EFBFBD><EFBFBD>?
|
||||
\* 轮询任务处理器
|
||||
\*/
|
||||
async handlePoll(projectId: string) {
|
||||
// 1\. 获取上次同步时间
|
||||
const lastSync \= await this.getLastSyncTime(projectId);
|
||||
|
||||
// 2\. 靚<EFBFBD>鍂 REDCap API <EFBFBD>瑕<EFBFBD><EFBFBD>冽迨銋见<EFBFBD>靽格㺿<EFBFBD><EFBFBD>扇敶?
|
||||
// 2\. 调用 REDCap API 获取在此之后修改的记录
|
||||
// API: 'export', content: 'record', dateRangeBegin: lastSync
|
||||
const records \= await this.redcapAdapter.fetchModifiedRecords(projectId, lastSync);
|
||||
|
||||
@@ -41,31 +42,39 @@ export class SyncManager {
|
||||
}
|
||||
}
|
||||
|
||||
**靽格㺿撱箄悅**嚗?
|
||||
* <20>?MVP <20>嗆挾嚗?*<EFBFBD>∪<EFBFBD>摰䂿緵頧株砭 (Polling)**<2A><><EFBFBD>閬<EFBFBD><E996AC><EFBFBD>駁堺<E9A781><E5A0BA><EFBFBD>蝏𦦵㴓憓<E3B493><E68693>?
|
||||
## **2\. <20>蠘<EFBFBD>銵亙<E98AB5>嚗𡁜<E59A97><F0A1819C>脫㺭<E884AB>桀<EFBFBD><E6A180>𤩺醌<F0A4A9BA>?*
|
||||
**修改建议**:
|
||||
|
||||
<EFBFBD><EFBFBD>笆 **憌𡡞埯鈭?(摮㗛<E691AE><E3979B>唳旿)**嚗<>遣霈桀⏚<E6A180>函緵<E587BD>厩<EFBFBD> CheckpointService 摰䂿緵<E482BF>券<EFBFBD><E588B8>急<EFBFBD><E680A5>?
|
||||
### **<2A>啣<EFBFBD> API嚗𡁜<E59A97><F0A1819C>讛捶<E8AE9B>扯圻<E689AF>?*
|
||||
* 在 MVP 阶段,**务必实现轮询 (Polling)**。不要赌医院的网络环境。
|
||||
|
||||
## **2\. 功能补充:历史数据全量扫描**
|
||||
|
||||
针对 **风险二 (存量数据)**,建议利用现有的 CheckpointService 实现全量扫描。
|
||||
|
||||
### **新增 API:全量质控触发**
|
||||
|
||||
**Endpoint**: POST /api/v1/iit/projects/:id/scan-all
|
||||
|
||||
**<EFBFBD>餉<EFBFBD>摰䂿緵 (憭滨鍂<E6BBA8>唳<EFBFBD> ASL/DC 璅∪<E79285><E288AA><EFBFBD>鸌憭<E9B88C><E686AD>蝏誯<E89D8F>)**嚗?
|
||||
1. 靚<>鍂 REDCap API 隞<><E99A9E>頧賣<E9A0A7><E8B3A3>?record\_id (頧駁<E9A0A7>蝥?<3F>?
|
||||
2. 撠?ID <20>𡑒”<F0A19192><E2809D><EFBFBD> (Chunk)嚗峕<E59A97><E5B395>?50 銝?ID<49>?
|
||||
3. <EFBFBD>拍鍂 pg-boss <20>券<EFBFBD>?iit:quality-check:batch 隞餃𦛚<E9A483>?
|
||||
4. Worker <20>𣂷葵<F0A382B7>匧<EFBFBD>摰峕㟲<E5B395>唳旿撟嗉<E6929F>銵?Agent<6E>?
|
||||
**逻辑实现 (复用现有 ASL/DC 模块的批处理经验)**:
|
||||
|
||||
1. 调用 REDCap API 仅下载所有 record\_id (轻量级)。
|
||||
2. 将 ID 列表分片 (Chunk),每片 50 个 ID。
|
||||
3. 利用 pg-boss 推送 iit:quality-check:batch 任务。
|
||||
4. Worker 逐个拉取完整数据并运行 Agent。
|
||||
|
||||
## **3\. 前端技术栈明确**
|
||||
|
||||
<EFBFBD>寞<EFBFBD>銝剜<EFBFBD><EFBFBD>唬<EFBFBD> "敺桐縑撠讐<E692A0>摨?嚗䔶<E59A97><E494B6>芣<EFBFBD>蝖格<E89D96><E6A0BC>舀<EFBFBD><E88880><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>唬<EFBFBD>隞祉緵<E7A589>厩<EFBFBD> React <EFBFBD>箏<EFBFBD>嚗?
|
||||
* **<2A>刻<EFBFBD><E588BB>寞<EFBFBD>**嚗帋蝙<E5B88B>?**Taro** (React 霂剜<E99C82>) 撘<><E69298>穃<EFBFBD>蝔见<E89D94><E8A781>?
|
||||
* **<2A><>眏**嚗?
|
||||
1. <20>臭誑霈拙<E99C88>蝡臬𣪧<E887AC>笔<EFBFBD><E7AC94>?React <20>亥<EFBFBD>嚗𠃍ooks, Components嚗剹<E59A97>?
|
||||
2. <EFBFBD>臭誑憭滨鍂 shared/components 銝剔<E98A9D><E58994>典<EFBFBD><E585B8>餉<EFBFBD>嚗<EFBFBD><E59A97><EFBFBD>唳旿<E594B3>澆<EFBFBD><E6BE86>吔<EFBFBD><E59094>?
|
||||
3. Taro <20>舀<EFBFBD>銝<EFBFBD><E98A9D>桃<EFBFBD>霂睲蛹 敺桐縑撠讐<E692A0>摨?\+ H5 (<28>其<EFBFBD>隡<EFBFBD>凝靘扯器<E689AF>?嚗?*銝<>敼潔舅<E6BD94>?*<2A>?
|
||||
## **4\. <20>唳旿摨?Schema 敺株<E695BA>**
|
||||
方案中提到了 "微信小程序",但未明确技术栈。考虑到你们现有的 React 基因:
|
||||
|
||||
* **推荐方案**:使用 **Taro** (React 语法) 开发小程序。
|
||||
* **理由**:
|
||||
1. 可以让前端团队复用 React 知识(Hooks, Components)。
|
||||
2. 可以复用 shared/components 中的部分逻辑(如数据格式化)。
|
||||
3. Taro 支持一键编译为 微信小程序 \+ H5 (用于企微侧边栏),**一鱼两吃**。
|
||||
|
||||
## **4\. 数据库 Schema 微调**
|
||||
|
||||
在 IitUserMapping 表中,建议增加 Token 字段,用于小程序 Session 维护。
|
||||
|
||||
<EFBFBD>?IitUserMapping 銵其葉嚗<E89189>遣霈桀<E99C88><E6A180>?Token 摮埈挾嚗𣬚鍂鈭𤾸<E988AD>蝔见<E89D94> Session 蝏湔擪<E6B994>?
|
||||
model IitUserMapping {
|
||||
// ... 现有字段
|
||||
|
||||
@@ -76,17 +85,21 @@ model IitUserMapping {
|
||||
@@index(\[miniProgramOpenId\])
|
||||
}
|
||||
|
||||
## **5\. Dify RAG <EFBFBD>扯<EFBFBD>隡睃<EFBFBD> (憸<><E686B8>頧?**
|
||||
## **5\. Dify RAG 性能优化 (预加载)**
|
||||
|
||||
PRD 提到 "Protocol 往往很长"。
|
||||
|
||||
* **风险**:每次质控都让 Dify 重新检索整个 PDF,速度慢且 Token 消耗大。
|
||||
* **优化**:在 ProtocolService 中增加 **"关键规则缓存"**。
|
||||
* 在上传 Protocol 后,让 Agent 预先提取出 "入排标准" (Inclusion/Exclusion Criteria) 并存入 PostgreSQL JSONB 字段。
|
||||
* 在做基础质控时,优先匹配 DB 里的规则,匹配不到再由 Agent 去 RAG 检索。
|
||||
|
||||
PRD <20>𣂼<EFBFBD> "Protocol 敺<>敺<EFBFBD>敺<EFBFBD>鵭"<22>?
|
||||
* **憌𡡞埯**嚗𡁏<E59A97>甈∟捶<E2889F>折<EFBFBD>霈?Dify <20>齿鰵璉<E9B0B5>蝝X㟲銝?PDF嚗屸<E59A97>笔漲<E7AC94>V<EFBFBD> Token 瘨<><E798A8>堒之<E5A092>?
|
||||
* **隡睃<E99AA1>**嚗𡁜銁 ProtocolService 銝剖<E98A9D><E58996>?**"<22>喲睸閫<E79DB8><E996AB>蝻枏<E89DBB>"**<2A>?
|
||||
* <20>其<EFBFBD>隡?Protocol <20>𠬍<EFBFBD>霈?Agent 憸<><E686B8><EFBFBD>𣂼<EFBFBD><F0A382BC>?"<22>交<EFBFBD><E4BAA4><EFBFBD><EFBFBD>" (Inclusion/Exclusion Criteria) 撟嗅<E6929F><E59785>?PostgreSQL JSONB 摮埈挾<E59F88>?
|
||||
* <20>典<EFBFBD><E585B8>箇<EFBFBD>韐冽綉<E586BD>塚<EFBFBD>隡睃<E99AA1><E79D83>寥<EFBFBD> DB <20>𣬚<EFBFBD>閫<EFBFBD><E996AB>嚗<EFBFBD>龪<EFBFBD>滢<EFBFBD><E6BBA2>啣<EFBFBD><E595A3>?Agent <20>?RAG 璉<>蝝U<E89D9D>?
|
||||
## **结论**
|
||||
|
||||
**甇斗䲮獢?V1.0 <20>臭誑<E887AD>朞<EFBFBD>霂<EFBFBD>恣嚗䔶<E59A97>敹<EFBFBD>◆銵亙<E98AB5>銝𡃏膩 "Plan B" (頧株砭<EFBFBD>箏<EFBFBD>)**<EFBFBD>?
|
||||
**撘<><E69298>睲<EFBFBD><E79DB2><EFBFBD>漣靚<E6BCA3>㟲撱箄悅**嚗?
|
||||
1. **Day 1**: <20>唳旿摨?& <20>箇<EFBFBD><E7AE87>嗆<EFBFBD> (銝滚<E98A9D>)
|
||||
2. **Day 2**: **隡睃<E99AA1>摰䂿緵 REDCap API Adapter (<28>匧<EFBFBD><E58CA7>賢<EFBFBD>)**嚗諹<E59A97>䔶<EFBFBD><E494B6>?Webhook (<28>券<EFBFBD><E588B8><EFBFBD><EFBFBD>?<3F><><EFBFBD>銝?API <20>匧<EFBFBD><E58CA7>游虾<E6B8B8>改<EFBFBD>銝磰<E98A9D>閫<EFBFBD><E996AB><EFBFBD><EFBFBD>蟮<EFBFBD>唳旿<E594B3>桅<EFBFBD><E6A185>?
|
||||
3. **Day 3**: Webhook 銵亙<E98AB5>摰䂿緵 (雿靝蛹<E99D9D>單𧒄<E596AE>批<EFBFBD>撘?<3F>
|
||||
**此方案 V1.0 可以通过评审,但必须补充上述 "Plan B" (轮询机制)**。
|
||||
|
||||
**开发优先级调整建议**:
|
||||
|
||||
1. **Day 1**: 数据库 & 基础架构 (不变)
|
||||
2. **Day 2**: **优先实现 REDCap API Adapter (拉取能力)**,而不是 Webhook (推送能力)。因为 API 拉取更可控,且能解决历史数据问题。
|
||||
3. **Day 3**: Webhook 补充实现 (作为即时性增强)。
|
||||
@@ -1,20 +1,20 @@
|
||||
# Phase 1.5: AI瀵硅瘽闆嗘垚REDCap鐪熷疄鏁版嵁鏌ヨ<EFBFBD> - 寮€鍙戝畬鎴愯<E98EB4>褰?
|
||||
# Phase 1.5: AI对话集成REDCap真实数据查询 - 开发完成记录
|
||||
|
||||
**寮€鍙戞棩鏈?*: 2026-01-03
|
||||
**寮€鍙戜汉鍛?*: AI Clinical Research Team
|
||||
**开发日期**: 2026-01-03
|
||||
**开发人员**: AI Clinical Research Team
|
||||
**版本**: Phase 1.5
|
||||
**鐘舵€?*: 鉁?宸插畬鎴?
|
||||
**状态**: ✅ 已完成
|
||||
|
||||
---
|
||||
|
||||
## 馃搵 **寮€鍙戠洰鏍?*
|
||||
## 📋 **开发目标**
|
||||
|
||||
瀹炵幇AI鍦ㄤ紒涓氬井淇′腑鍩轰簬REDCap鐪熷疄鏁版嵁涓嶱I杩涜<EFBFBD>鏅鸿兘瀵硅瘽锛岃В鍐矻LM骞昏<EFBFBD>闂<EFBFBD><EFBFBD>銆?
|
||||
实现AI在企业微信中基于REDCap真实数据与PI进行智能对话,解决LLM幻觉问题。
|
||||
|
||||
### **鏍稿績闇€姹?*
|
||||
### **核心需求**
|
||||
1. AI能够查询REDCap真实数据
|
||||
2. AI不编造数据,基于事实回答
|
||||
3. 鏀<EFBFBD>寔澶氳疆瀵硅瘽涓婁笅鏂囪<EFBFBD>蹇?
|
||||
3. 支持多轮对话上下文记忆
|
||||
4. 提供即时"正在查询"反馈
|
||||
|
||||
---
|
||||
@@ -22,69 +22,69 @@
|
||||
## 🎯 **完成功能**
|
||||
|
||||
### **1. 意图识别**
|
||||
- 鉁?**鏌ヨ<E98F8C>鐗瑰畾璁板綍**: 璇嗗埆璁板綍ID锛堝<EFBFBD>"ID 7"銆?璁板綍7"锛?
|
||||
- 鉁?**缁熻<E7BC81>璁板綍鏁?*: 璇嗗埆"澶氬皯"銆?鍑犱釜"銆?鍑犳潯"绛夊叧閿<E58FA7>瘝
|
||||
- 鉁?**椤圭洰淇℃伅**: 璇嗗埆"椤圭洰鍚嶇О"銆?椤圭洰鎯呭喌"绛?
|
||||
- 鉁?**鏅<>€氬<E282AC>璇?*: 榛樿<E6A69B>澶勭悊鍏朵粬瀵硅瘽
|
||||
- ✅ **查询特定记录**: 识别记录ID(如"ID 7"、"记录7")
|
||||
- ✅ **统计记录数**: 识别"多少"、"几个"、"几条"等关键词
|
||||
- ✅ **项目信息**: 识别"项目名称"、"项目情况"等
|
||||
- ✅ **普通对话**: 默认处理其他对话
|
||||
|
||||
**瀹炵幇鏂瑰紡**: 鍏抽敭璇嶅尮閰?+ 姝e垯琛ㄨ揪寮?
|
||||
**实现方式**: 关键词匹配 + 正则表达式
|
||||
|
||||
### **2. REDCap数据查询**
|
||||
- 鉁?**queryRedcapRecord()**: 鏌ヨ<EFBFBD>鐗瑰畾璁板綍鐨勮<EFBFBD>缁嗕俊鎭?
|
||||
- 鉁?**countRedcapRecords()**: 缁熻<EFBFBD>鎬昏<EFBFBD>褰曟暟
|
||||
- 鉁?**getProjectInfo()**: 鑾峰彇椤圭洰鍩烘湰淇℃伅
|
||||
- ✅ **queryRedcapRecord()**: 查询特定记录的详细信息
|
||||
- ✅ **countRedcapRecords()**: 统计总记录数
|
||||
- ✅ **getProjectInfo()**: 获取项目基本信息
|
||||
|
||||
**鏁版嵁鏉ユ簮**: 鏁版嵁搴?`iit_schema.projects` 琛?鈫?RedcapAdapter 鈫?REDCap API
|
||||
**数据来源**: 数据库 `iit_schema.projects` 表 → RedcapAdapter → REDCap API
|
||||
|
||||
### **3. 数据注入LLM**
|
||||
- 鉁?灏嗘煡璇㈢粨鏋滄敞鍏<E6959E>ystem娑堟伅
|
||||
- 鉁?鏂扮殑System Prompt寮鸿皟"鍩轰簬鐪熷疄鏁版嵁锛屼笉缂栭€?
|
||||
- 鉁?閿欒<E996BF>澶勭悊锛氭煡璇㈠け璐ユ椂鍙嬪ソ鎻愮ず
|
||||
- ✅ 将查询结果注入System消息
|
||||
- ✅ 新的System Prompt强调"基于真实数据,不编造"
|
||||
- ✅ 错误处理:查询失败时友好提示
|
||||
|
||||
### **4. 涓婁笅鏂囪<EFBFBD>蹇?*
|
||||
- 鉁?SessionMemory淇濆瓨鏈€杩?杞<><E69D9E>璇?
|
||||
- 鉁?鏀<>寔澶氳疆瀵硅瘽鐞嗚В锛堝<E9949B>"浠?鎸囦唬涔嬪墠鎻愬埌鐨勬偅鑰咃級
|
||||
### **4. 上下文记忆**
|
||||
- ✅ SessionMemory保存最近3轮对话
|
||||
- ✅ 支持多轮对话理解(如"他"指代之前提到的患者)
|
||||
|
||||
### **5. 即时反馈**
|
||||
- 鉁?鏀跺埌娑堟伅鍚庣珛鍗冲洖澶?馃<> 姝e湪鏌ヨ<E98F8C>锛岃<E9949B>绋嶅€?.."
|
||||
- 鉁?鏌ヨ<E98F8C>瀹屾垚鍚庢帹閫佹渶缁堢粨鏋?
|
||||
- ✅ 收到消息后立即回复"🫡 正在查询,请稍候..."
|
||||
- ✅ 查询完成后推送最终结果
|
||||
|
||||
---
|
||||
|
||||
## 馃彈锔?**鎶€鏈<E282AC>灦鏋?*
|
||||
## 🏗️ **技术架构**
|
||||
|
||||
### **鏁版嵁娴?*
|
||||
### **数据流**
|
||||
```
|
||||
用户消息(企业微信)
|
||||
鈫?
|
||||
↓
|
||||
WechatCallbackController.handleMessage()
|
||||
鈫?
|
||||
↓
|
||||
ChatService.handleMessage()
|
||||
鈫?
|
||||
↓
|
||||
1. detectIntent() - 意图识别
|
||||
鈫?
|
||||
↓
|
||||
2. queryRedcapRecord() / countRedcapRecords() / getProjectInfo()
|
||||
鈫?
|
||||
↓
|
||||
prisma.iitProject.findFirst() - 从数据库获取项目配置
|
||||
鈫?
|
||||
new RedcapAdapter(url, token) - 鍒涘缓API閫傞厤鍣?
|
||||
鈫?
|
||||
↓
|
||||
new RedcapAdapter(url, token) - 创建API适配器
|
||||
↓
|
||||
redcap.exportRecords() - 调用REDCap API
|
||||
鈫?
|
||||
↓
|
||||
返回真实数据
|
||||
鈫?
|
||||
↓
|
||||
3. buildMessagesWithData() - 构建LLM消息(注入数据)
|
||||
鈫?
|
||||
↓
|
||||
4. llm.chat() - 调用DeepSeek-V3
|
||||
鈫?
|
||||
↓
|
||||
5. sessionMemory.addMessage() - 记录到上下文
|
||||
鈫?
|
||||
↓
|
||||
AI回复(企业微信)
|
||||
```
|
||||
|
||||
### **关键代码**
|
||||
- **ChatService.ts**: 390行,新增REDCap查询方法
|
||||
- **鎰忓浘璇嗗埆**: detectIntent()锛屾<EFBFBD>鍒欏尮閰?
|
||||
- **意图识别**: detectIntent(),正则匹配
|
||||
- **数据注入**: buildMessagesWithData()
|
||||
- **System Prompt**: getSystemPromptWithData()
|
||||
|
||||
@@ -94,140 +94,140 @@ AI回复(企业微信)
|
||||
|
||||
### **测试环境**
|
||||
- **项目**: test0102 (REDCap PID: 16)
|
||||
- **璁板綍鏁?*: 10鏉★紙ID 1-10锛?
|
||||
- **记录数**: 10条(ID 1-10)
|
||||
- **测试用户**: FengZhiBo
|
||||
- **测试时间**: 2026-01-03 22:28
|
||||
|
||||
### **娴嬭瘯鍦烘櫙1: 鏌ヨ<E98F8C>鐗瑰畾璁板綍** 鉁?
|
||||
### **测试场景1: 查询特定记录** ✅
|
||||
**用户输入**:
|
||||
```
|
||||
甯<EFBFBD>垜鏌ヨ<EFBFBD>涓€涓?璁板綍 7鐨?鎵€鏈変俊鎭?
|
||||
帮我查询一下 记录 7的 所有信息
|
||||
```
|
||||
|
||||
**AI回复** (基于真实数据):
|
||||
```
|
||||
鏍规嵁REDCap鏌ヨ<EFBFBD>缁撴灉锛岃<EFBFBD>褰?鐨勪俊鎭<E4BF8A><E98EAD>涓嬶細
|
||||
根据REDCap查询结果,记录7的信息如下:
|
||||
|
||||
**基本信息**
|
||||
- 濮撳悕锛氫紒涓氬井淇? 浼佷笟寰<E7AC9F>俊7
|
||||
- 鍑虹敓鏃ユ湡锛?017-01-04
|
||||
- 骞撮緞锛?宀?
|
||||
- 姓名:企业微信7 企业微信7
|
||||
- 出生日期:2017-01-04
|
||||
- 年龄:8岁
|
||||
- 性别:男
|
||||
- 韬<EFBFBD>珮锛?30 cm
|
||||
- 浣撻噸锛?0 kg
|
||||
- BMI锛?9.6
|
||||
- 身高:130 cm
|
||||
- 体重:50 kg
|
||||
- BMI:29.6
|
||||
|
||||
**联系方式**
|
||||
- 鍦板潃锛氫紒涓氬井淇?
|
||||
- 地址:企业微信7
|
||||
- 邮箱:gofeng117@163.com
|
||||
|
||||
**其他信息**
|
||||
- 姘戞棌锛氳タ鐝<EFBFBD>墮瑁旀垨鎷変竵瑁?
|
||||
- 民族:西班牙裔或拉丁裔
|
||||
- 治疗史:测试7777
|
||||
- 是否完成:是
|
||||
|
||||
鎵€鏈夊瓧娈靛潎宸插~鍐欏畬鎴愩€傚<EFBFBD>闇€鏌ョ湅瀹屾暣琛ㄥ崟锛屽缓璁<EFBFBD>櫥褰昍EDCap绯荤粺銆?
|
||||
所有字段均已填写完成。如需查看完整表单,建议登录REDCap系统。
|
||||
```
|
||||
|
||||
**鏁版嵁楠岃瘉**: 鉁?瀹屽叏鍖归厤REDCap鏁版嵁搴?
|
||||
**数据验证**: ✅ 完全匹配REDCap数据库
|
||||
|
||||
### **测试场景2: 统计查询** (预期)
|
||||
**用户输入**: "我们系统中已经有几条记录了?"
|
||||
**棰勬湡鍥炲<EFBFBD>**: "褰撳墠椤圭洰test0102宸叉湁 **10鏉?* 鎮h€呮暟鎹<E69A9F><E98EB9>褰?
|
||||
**预期回复**: "当前项目test0102已有 **10条** 患者数据记录"
|
||||
|
||||
### **测试场景3: 项目信息** (预期)
|
||||
**用户输入**: "咱们当前的项目名称是什么?"
|
||||
**棰勬湡鍥炲<EFBFBD>**: "褰撳墠椤圭洰鍚嶇О涓?**test0102**"
|
||||
**预期回复**: "当前项目名称为 **test0102**"
|
||||
|
||||
### **性能指标**
|
||||
- ⏱️ **API查询**: 300-700ms
|
||||
- 鈴憋笍 **LLM鍝嶅簲**: 2-3绉?
|
||||
- 鈴憋笍 **鎬诲搷搴旀椂闂?*: 3-5绉?
|
||||
- 馃搳 **Token娑堣€?*: 绾?00 tokens/娆?
|
||||
- ⏱️ **LLM响应**: 2-3秒
|
||||
- ⏱️ **总响应时间**: 3-5秒
|
||||
- 📊 **Token消耗**: 约500 tokens/次
|
||||
|
||||
---
|
||||
|
||||
## 🆚 **对比:解决LLM幻觉**
|
||||
|
||||
### **涔嬪墠锛堢紪閫犳暟鎹<EFBFBD>級** 鉂?
|
||||
### **之前(编造数据)** ❌
|
||||
```
|
||||
AI: "ID 7鐨勫叆缁勬棩鏈熶负 **2023-10-26**锛堝嵆鍩虹嚎璁胯<EFBFBD>鏃ユ湡锛?
|
||||
鉂?瀹屽叏缂栭€?
|
||||
鉂?涓庣湡瀹炴暟鎹<E69A9F>笉绗?
|
||||
鉂?椤圭洰鍚嶇О缂栭€犱负"IIT-2023-001: XX骞查<EFBFBD>瀵筜Y鐤剧梾..."
|
||||
AI: "ID 7的入组日期为 **2023-10-26**(即基线访视日期)"
|
||||
❌ 完全编造
|
||||
❌ 与真实数据不符
|
||||
❌ 项目名称编造为"IIT-2023-001: XX干预对YY疾病..."
|
||||
```
|
||||
|
||||
### **鐜板湪锛堢湡瀹炴暟鎹<EFBFBD>級** 鉁?
|
||||
### **现在(真实数据)** ✅
|
||||
```
|
||||
AI: "鍑虹敓鏃ユ湡锛?017-01-04
|
||||
骞撮緞锛?宀?
|
||||
韬<EFBFBD>珮锛?30 cm
|
||||
浣撻噸锛?0 kg"
|
||||
鉁?100%鐪熷疄
|
||||
鉁?鏉ヨ嚜REDCap鏁版嵁搴?
|
||||
鉁?椤圭洰鍚嶇О涓簍est0102锛堢湡瀹烇級
|
||||
AI: "出生日期:2017-01-04
|
||||
年龄:8岁
|
||||
身高:130 cm
|
||||
体重:50 kg"
|
||||
✅ 100%真实
|
||||
✅ 来自REDCap数据库
|
||||
✅ 项目名称为test0102(真实)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 馃挕 **鎶€鏈<EFBFBD>寒鐐?*
|
||||
## 💡 **技术亮点**
|
||||
|
||||
### **1. 架构设计**
|
||||
- 鉁?**浠庢暟鎹<E69A9F>簱璇诲彇閰嶇疆**: 涓嶄娇鐢ㄧ幆澧冨彉閲忥紝鏀<EFBFBD>寔澶氶」鐩?
|
||||
- 鉁?**澶嶇敤閫氱敤鑳藉姏灞?*: LLMFactory闆堕厤缃<EFBFBD>泦鎴?
|
||||
- 鉁?**鍒嗗眰娓呮櫚**: Controller 鈫?Service 鈫?Adapter 鈫?API
|
||||
- ✅ **从数据库读取配置**: 不使用环境变量,支持多项目
|
||||
- ✅ **复用通用能力层**: LLMFactory零配置集成
|
||||
- ✅ **分层清晰**: Controller → Service → Adapter → API
|
||||
|
||||
### **2. 意图识别**
|
||||
- 鉁?**绠€鍗曟湁鏁?*: 鍏抽敭璇嶅尮閰?+ 姝e垯琛ㄨ揪寮?
|
||||
- 鉁?**鎵╁睍鎬уソ**: 鏄撲簬娣诲姞鏂版剰鍥?
|
||||
- 鉁?**鎬ц兘浼樼<E6B5BC>**: <1ms璇嗗埆鏃堕棿
|
||||
- ✅ **简单有效**: 关键词匹配 + 正则表达式
|
||||
- ✅ **扩展性好**: 易于添加新意图
|
||||
- ✅ **性能优秀**: <1ms识别时间
|
||||
|
||||
### **3. 数据安全**
|
||||
- 鉁?**Token鍔犲瘑瀛樺偍**: 鏁版嵁搴撲腑鍔犲瘑
|
||||
- 鉁?**鍔ㄦ€佽幏鍙?*: 姣忔<E5A7A3>鏌ヨ<E98F8C>鏃朵粠鏁版嵁搴撹<E690B4>鍙?
|
||||
- 鉁?**鏉冮檺鎺у埗**: 鍩轰簬椤圭洰鐘舵€佽繃婊?
|
||||
- ✅ **Token加密存储**: 数据库中加密
|
||||
- ✅ **动态获取**: 每次查询时从数据库读取
|
||||
- ✅ **权限控制**: 基于项目状态过滤
|
||||
|
||||
### **4. 用户体验**
|
||||
- 鉁?**鍗虫椂鍙嶉<E98D99>**: "姝e湪鏌ヨ<EFBFBD>"娑堟伅
|
||||
- 鉁?**鍑嗙‘鍥炵瓟**: 鍩轰簬鐪熷疄鏁版嵁
|
||||
- 鉁?**涓婁笅鏂囪繛璐?*: 鏀<>寔澶氳疆瀵硅瘽
|
||||
- ✅ **即时反馈**: "正在查询"消息
|
||||
- ✅ **准确回答**: 基于真实数据
|
||||
- ✅ **上下文连贯**: 支持多轮对话
|
||||
|
||||
---
|
||||
|
||||
## 📊 **代码统计**
|
||||
|
||||
### **新增文件**
|
||||
1. `SessionMemory.ts` - 170琛?(涓婁笅鏂囪<E98F82>蹇?
|
||||
2. `test-redcap-query-from-db.ts` - 250琛?(娴嬭瘯鑴氭湰)
|
||||
3. `check-test-project-in-db.ts` - 74琛?(椤圭洰妫€鏌?
|
||||
1. `SessionMemory.ts` - 170行 (上下文记忆)
|
||||
2. `test-redcap-query-from-db.ts` - 250行 (测试脚本)
|
||||
3. `check-test-project-in-db.ts` - 74行 (项目检查)
|
||||
|
||||
### **修改文件**
|
||||
1. `ChatService.ts` - 鏂板<EFBFBD>200琛?(REDCap闆嗘垚)
|
||||
1. `ChatService.ts` - 新增200行 (REDCap集成)
|
||||
2. `WechatCallbackController.ts` - 新增即时反馈
|
||||
3. `routes/index.ts` - 鏂板<EFBFBD>鏍硅矾鐢?
|
||||
3. `routes/index.ts` - 新增根路由
|
||||
|
||||
### **删除文件**
|
||||
1. `test-redcap-query-for-ai.ts` (使用环境变量,已废弃)
|
||||
2. `check-env-config.ts` (鐜<EFBFBD><EFBFBD>鍙橀噺妫€鏌ワ紝宸插簾寮?
|
||||
2. `check-env-config.ts` (环境变量检查,已废弃)
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ **当前限制**
|
||||
|
||||
### **1. 意图识别**
|
||||
- 鉂?浠呮敮鎸佸叧閿<E58FA7>瘝鍖归厤
|
||||
- 鉂?涓嶆敮鎸佸<E98EB8>鏉傛煡璇㈢粍鍚?
|
||||
- 鉂?涓嶆敮鎸佹ā绯婂尮閰?
|
||||
- ❌ 仅支持关键词匹配
|
||||
- ❌ 不支持复杂查询组合
|
||||
- ❌ 不支持模糊匹配
|
||||
|
||||
### **2. 数据查询**
|
||||
- 鉂?浠呮敮鎸佸崟椤圭洰锛堥粯璁<E7B2AF>ctive椤圭洰锛?
|
||||
- 鉂?涓嶆敮鎸佸瓧娈靛悕涓<E68295>枃鏄犲皠
|
||||
- 鉂?涓嶆敮鎸佸<E98EB8>鏉傝繃婊ゆ潯浠?
|
||||
- ❌ 仅支持单项目(默认active项目)
|
||||
- ❌ 不支持字段名中文映射
|
||||
- ❌ 不支持复杂过滤条件
|
||||
|
||||
### **3. 涓婁笅鏂囪<EFBFBD>蹇?*
|
||||
- 鉂?浠呬繚瀛樻渶杩?杞<><E69D9E>璇?
|
||||
- 鉂?鍩轰簬鍐呭瓨锛屾湇鍔¢噸鍚<E599B8>涪澶?
|
||||
- 鉂?涓嶆敮鎸佽法浼氳瘽璁板繂
|
||||
### **3. 上下文记忆**
|
||||
- ❌ 仅保存最近3轮对话
|
||||
- ❌ 基于内存,服务重启丢失
|
||||
- ❌ 不支持跨会话记忆
|
||||
|
||||
---
|
||||
|
||||
@@ -235,9 +235,9 @@ AI: "出生日期
|
||||
|
||||
### **短期优化**
|
||||
1. **扩展意图识别**: 支持更多查询模式
|
||||
2. **瀛楁<EFBFBD>鏄犲皠**: 涓<EFBFBD>枃瀛楁<EFBFBD>鍚?鈫?REDCap瀛楁<EFBFBD>鍚?
|
||||
2. **字段映射**: 中文字段名 → REDCap字段名
|
||||
3. **错误优化**: 更友好的错误提示
|
||||
4. **澶氶」鐩<EFBFBD>敮鎸?*: 鐢ㄦ埛閫夋嫨鏌ヨ<E98F8C>鍝<EFBFBD>釜椤圭洰
|
||||
4. **多项目支持**: 用户选择查询哪个项目
|
||||
|
||||
### **中期升级 (Phase 2)**
|
||||
1. **Function Calling**: 升级为LLM自主决策调用工具
|
||||
@@ -246,52 +246,52 @@ AI: "出生日期
|
||||
4. **性能监控**: 记录查询耗时、错误率
|
||||
|
||||
### **长期规划**
|
||||
1. **Dify鐭ヨ瘑搴?*: 鏌ヨ<E98F8C>鐮旂┒鏂规<E98F82>銆佷鸡鐞嗘枃浠?
|
||||
1. **Dify知识库**: 查询研究方案、伦理文件
|
||||
2. **智能质控**: AI分析数据质量问题
|
||||
3. **H5前端**: 更丰富的交互体验
|
||||
4. **澶氭ā鎬?*: 鏀<>寔鍥剧墖銆佹枃妗d笂浼?
|
||||
4. **多模态**: 支持图片、文档上传
|
||||
|
||||
---
|
||||
|
||||
## 📝 **技术债务**
|
||||
|
||||
### **1. 临时措施**
|
||||
- 鈿狅笍 浣跨敤鍏抽敭璇嶅尮閰嶏紙搴斿崌绾т负Function Calling锛?
|
||||
- 鈿狅笍 SessionMemory鍩轰簬鍐呭瓨锛堝簲鏀逛负Redis锛?
|
||||
- 鈿狅笍 榛樿<E6A69B>鏌ヨ<E98F8C>绗<EFBFBD>竴涓猘ctive椤圭洰锛堝簲鏀<E7B0B2>寔椤圭洰閫夋嫨锛?
|
||||
- ⚠️ 使用关键词匹配(应升级为Function Calling)
|
||||
- ⚠️ SessionMemory基于内存(应改为Redis)
|
||||
- ⚠️ 默认查询第一个active项目(应支持项目选择)
|
||||
|
||||
### **2. 寰呭疄鐜板姛鑳?*
|
||||
- [ ] 瀛楁<EFBFBD>鍚嶄腑鏂囨槧灏?
|
||||
### **2. 待实现功能**
|
||||
- [ ] 字段名中文映射
|
||||
- [ ] 复杂查询条件
|
||||
- [ ] 数据缓存机制
|
||||
- [ ] 权限控制
|
||||
|
||||
---
|
||||
|
||||
## 馃幆 **閲岀▼纰戞剰涔?*
|
||||
## 🎯 **里程碑意义**
|
||||
|
||||
### **鎶€鏈<EFBFBD>獊鐮?*
|
||||
1. 鉁?**瑙e喅LLM骞昏<E9AA9E>**: AI涓嶅啀缂栭€犳暟鎹?
|
||||
2. 鉁?**鏁版嵁闂<E5B581>幆**: 鏁版嵁搴?鈫?REDCap 鈫?AI 鈫?鐢ㄦ埛
|
||||
3. 鉁?**鏋舵瀯楠岃瘉**: 浠庢暟鎹<EFBFBD>簱璇诲彇閰嶇疆鐨勬柟妗堝彲琛?
|
||||
### **技术突破**
|
||||
1. ✅ **解决LLM幻觉**: AI不再编造数据
|
||||
2. ✅ **数据闭环**: 数据库 → REDCap → AI → 用户
|
||||
3. ✅ **架构验证**: 从数据库读取配置的方案可行
|
||||
|
||||
### **涓氬姟浠峰€?*
|
||||
1. 鉁?**鎻愬崌鏁堢巼**: PI鏃犻渶鐧诲綍REDCap鍗冲彲鏌ヨ<EFBFBD>鏁版嵁
|
||||
2. 鉁?**澧炲己淇′换**: AI鍩轰簬浜嬪疄鍥炵瓟锛屽彲淇¤禆
|
||||
3. 鉁?**鏀瑰杽浣撻獙**: 浼佷笟寰<EFBFBD>俊鐩存帴瀵硅瘽锛屼究鎹?
|
||||
### **业务价值**
|
||||
1. ✅ **提升效率**: PI无需登录REDCap即可查询数据
|
||||
2. ✅ **增强信任**: AI基于事实回答,可信赖
|
||||
3. ✅ **改善体验**: 企业微信直接对话,便捷
|
||||
|
||||
### **团队成长**
|
||||
1. 鉁?**鏋舵瀯鑳藉姏**: 鐞嗚В鍒嗗眰鏋舵瀯鐨勯噸瑕佹€?
|
||||
2. 鉁?**闂<><E99782>瑙e喅**: 浠庣幆澧冨彉閲忓埌鏁版嵁搴撻厤缃<EFBFBD>殑婕旇繘
|
||||
3. 鉁?**娴嬭瘯椹卞姩**: 鍏堟祴璇昍EDCap API锛屽啀闆嗘垚AI
|
||||
1. ✅ **架构能力**: 理解分层架构的重要性
|
||||
2. ✅ **问题解决**: 从环境变量到数据库配置的演进
|
||||
3. ✅ **测试驱动**: 先测试REDCap API,再集成AI
|
||||
|
||||
---
|
||||
|
||||
## 🙏 **致谢**
|
||||
|
||||
感谢团队成员的辛勤付出:
|
||||
- **闇€姹傚垎鏋?*: 鏄庣‘AI瀵硅瘽鐨勬牳蹇冧环鍊?
|
||||
- **鏋舵瀯璁捐<EFBFBD>**: 閫夋嫨浠庢暟鎹<E69A9F>簱璇诲彇閰嶇疆鐨勬柟妗?
|
||||
- **需求分析**: 明确AI对话的核心价值
|
||||
- **架构设计**: 选择从数据库读取配置的方案
|
||||
- **代码实现**: 高质量的代码和清晰的注释
|
||||
- **测试验证**: 完整的测试用例和真实场景验证
|
||||
|
||||
@@ -299,17 +299,16 @@ AI: "出生日期
|
||||
|
||||
## 📚 **相关文档**
|
||||
|
||||
- [Phase 1.5寮€鍙戣<EFBFBD>鍒抅(../04-寮€鍙戣<E98D99>鍒?Phase1.5-AI瀵硅瘽鑳藉姏寮€鍙戣<E98D99>鍒?md)
|
||||
- [MVP浠诲姟娓呭崟](../04-寮€鍙戣<EFBFBD>鍒?MVP寮€鍙戜换鍔℃竻鍗?md)
|
||||
- [妯″潡褰撳墠鐘舵€乚(../00-妯″潡褰撳墠鐘舵€佷笌寮€鍙戞寚鍗?md)
|
||||
- [Phase 1.5开发计划](../04-开发计划/Phase1.5-AI对话能力开发计划.md)
|
||||
- [MVP任务清单](../04-开发计划/MVP开发任务清单.md)
|
||||
- [模块当前状态](../00-模块当前状态与开发指南.md)
|
||||
- [Day 3开发记录](./Day3-企业微信集成与端到端测试完成记录.md)
|
||||
|
||||
---
|
||||
|
||||
**鏂囨。缁存姢**: 寮€鍙戝洟闃?
|
||||
**鏈€鍚庢洿鏂?*: 2026-01-03
|
||||
**涓嬩竴姝?*: Phase 2 - Function Calling + Dify鐭ヨ瘑搴?
|
||||
|
||||
**文档维护**: 开发团队
|
||||
**最后更新**: 2026-01-03
|
||||
**下一步**: Phase 2 - Function Calling + Dify知识库
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,61 +1,61 @@
|
||||
# IIT Manager Agent 鎶€鏈<EFBFBD>柟妗?V1.1 鏇存柊瀹屾垚鎶ュ憡
|
||||
# IIT Manager Agent 技术方案 V1.1 更新完成报告
|
||||
|
||||
> **鏇存柊鏃ユ湡锛?* 2025-12-31
|
||||
> **鏇存柊浜哄憳锛?* AI鍔╂墜
|
||||
> **瀹℃煡渚濇嵁锛?* `IIT Manager Agent 鎶€鏈<EFBFBD>柟妗堝<EFBFBD>鏌ヤ笌琛ヤ竵.md`
|
||||
> **更新日期:** 2025-12-31
|
||||
> **更新人员:** AI助手
|
||||
> **审查依据:** `IIT Manager Agent 技术方案审查与补丁.md`
|
||||
|
||||
---
|
||||
|
||||
## 鉁?鏇存柊瀹屾垚
|
||||
## ✅ 更新完成
|
||||
|
||||
宸叉垚鍔熷皢鎶€鏈<EFBFBD>柟妗堜粠 V1.0 鍗囩骇鍒?V1.1锛屾暣鍚堜簡鏋舵瀯璇勫<EFBFBD>鐨勬墍鏈変慨姝f剰瑙併€?
|
||||
已成功将技术方案从 V1.0 升级到 V1.1,整合了架构评审的所有修正意见。
|
||||
|
||||
**鏇存柊鏂囦欢**锛?
|
||||
- `02-鎶€鏈<EFBFBD><EFBFBD>璁?IIT Manager Agent 瀹屾暣鎶€鏈<EFBFBD>紑鍙戞柟妗?(V1.1).md`锛?100+琛岋級
|
||||
**更新文件**:
|
||||
- `02-技术设计/IIT Manager Agent 完整技术开发方案 (V1.1).md`(2100+行)
|
||||
|
||||
---
|
||||
|
||||
## 馃敟 鏍稿績淇<E7B8BE><E6B787>锛?澶ц嚧鍛介棶棰樺凡瑙e喅锛?
|
||||
## 🔥 核心修正(3大致命问题已解决)
|
||||
|
||||
### 1. 鉁?缃戠粶杩為€氭€ч<E282AC>闄╋紙鑷村懡绾э級- 宸蹭慨姝?
|
||||
### 1. ✅ 网络连通性风险(致命级)- 已修正
|
||||
|
||||
**闂<EFBFBD><EFBFBD>**锛?
|
||||
- V1.0瀹屽叏渚濊禆Webhook鎺ㄩ€?
|
||||
**问题**:
|
||||
- V1.0完全依赖Webhook推送
|
||||
- 医院内网REDCap无法主动访问公网SAE
|
||||
- Webhook鏈哄埗浼氬畬鍏ㄥけ鏁?
|
||||
- Webhook机制会完全失效
|
||||
|
||||
**淇<EFBFBD><EFBFBD>鏂规<EFBFBD>**锛?
|
||||
- 鉁?鏂板<E98F82> `SyncManager`锛堟贩鍚堝悓姝ユā寮忥級
|
||||
- 鉁?浼樺厛浣跨敤Webhook锛堝疄鏃舵€э級
|
||||
- 鉁?瀹氭椂杞<E6A482><E69D9E>浣滀负鍏滃簳锛堝彲闈犳€э級
|
||||
- 鉁?鏅鸿兘鑷<E58598>€傚簲锛氳嚜鍔ㄩ€夋嫨鏈€浣虫ā寮?
|
||||
**修正方案**:
|
||||
- ✅ 新增 `SyncManager`(混合同步模式)
|
||||
- ✅ 优先使用Webhook(实时性)
|
||||
- ✅ 定时轮询作为兜底(可靠性)
|
||||
- ✅ 智能自适应:自动选择最佳模式
|
||||
|
||||
**浠g爜澧炲姞**锛殈400琛岋紙瀹屾暣瀹炵幇锛?
|
||||
**代码增加**:~400行(完整实现)
|
||||
|
||||
### 2. 鉁?鍘嗗彶鏁版嵁缂哄け锛堝姛鑳界骇锛? 宸茶ˉ鍏?
|
||||
### 2. ✅ 历史数据缺失(功能级)- 已补充
|
||||
|
||||
**闂<EFBFBD><EFBFBD>**锛?
|
||||
**问题**:
|
||||
- V1.0只监听新录入数据
|
||||
- 医院存量数据(如500个患者)无法质控
|
||||
|
||||
**淇<EFBFBD><EFBFBD>鏂规<EFBFBD>**锛?
|
||||
- 鉁?鏂板<E98F82> `BulkScanService`锛堝叏閲忔壂鎻忥級
|
||||
- 鉁?鏀<>寔<50鏉$洿鎺ュ<E98EBA>鐞嗭紝鈮?0鏉¢槦鍒楀<E98D92>鐞?
|
||||
- 鉁?鏀<>寔鏂<E5AF94>偣缁<E581A3>紶锛堥暱鏃堕棿浠诲姟锛?
|
||||
- 鉁?鏂板<E98F82>API锛歚POST /api/v1/iit/projects/:id/scan-all`
|
||||
**修正方案**:
|
||||
- ✅ 新增 `BulkScanService`(全量扫描)
|
||||
- ✅ 支持<50条直接处理,≥50条队列处理
|
||||
- ✅ 支持断点续传(长时间任务)
|
||||
- ✅ 新增API:`POST /api/v1/iit/projects/:id/scan-all`
|
||||
|
||||
**浠g爜澧炲姞**锛殈350琛岋紙瀹屾暣瀹炵幇锛?
|
||||
**代码增加**:~350行(完整实现)
|
||||
|
||||
### 3. 鉁?鍓嶇<E98D93>鎶€鏈<E282AC>爤涓嶆槑纭<E6A791>紙瑙勮寖绾э級- 宸叉槑纭?
|
||||
### 3. ✅ 前端技术栈不明确(规范级)- 已明确
|
||||
|
||||
**闂<EFBFBD><EFBFBD>**锛?
|
||||
- V1.0鎻愬埌"寰<>俊灏忕▼搴?锛屼絾鏈<E7B5BE>槑纭<E6A791>妧鏈<E5A6A7>爤
|
||||
**问题**:
|
||||
- V1.0提到"微信小程序",但未明确技术栈
|
||||
|
||||
**淇<EFBFBD><EFBFBD>鏂规<EFBFBD>**锛?
|
||||
- 鉁?鏄庣‘浣跨敤 **Taro 4.x**锛圧eact璇<74>硶锛?
|
||||
- 鉁?鏀<>寔涓€娆″紑鍙戯紝澶氱<E6BEB6>杩愯<E69DA9>锛堝皬绋嬪簭 + H5锛?
|
||||
- 鉁?鍙<><E98D99>鐢?shared/components 閫昏緫
|
||||
- 鉁?鍥㈤槦鐔熸倝React Hooks璇<EFBFBD>硶
|
||||
**修正方案**:
|
||||
- ✅ 明确使用 **Taro 4.x**(React语法)
|
||||
- ✅ 支持一次开发,多端运行(小程序 + H5)
|
||||
- ✅ 可复用 shared/components 逻辑
|
||||
- ✅ 团队熟悉React Hooks语法
|
||||
|
||||
---
|
||||
|
||||
@@ -68,7 +68,7 @@ model IitProject {
|
||||
// ... 原有字段
|
||||
|
||||
// 🔥 V1.1 新增
|
||||
cachedRules Json? // Protocol鍏抽敭瑙勫垯缂撳瓨锛堟€ц兘浼樺寲锛?
|
||||
cachedRules Json? // Protocol关键规则缓存(性能优化)
|
||||
lastSyncAt DateTime? // 上次同步时间(增量拉取)
|
||||
|
||||
@@schema("iit")
|
||||
@@ -97,24 +97,24 @@ model IitUserMapping {
|
||||
### V1.0 原计划(有风险)
|
||||
|
||||
```
|
||||
Day 1: 鏁版嵁搴?
|
||||
Day 2-3: REDCap EM锛圵ebhook鎺ㄩ€侊級鈫?渚濊禆鍖婚櫌缃戠粶
|
||||
Day 4-5: Node.js Webhook鎺ユ敹鍣?
|
||||
Day 1: 数据库
|
||||
Day 2-3: REDCap EM(Webhook推送)← 依赖医院网络
|
||||
Day 4-5: Node.js Webhook接收器
|
||||
```
|
||||
|
||||
### V1.1 淇<EFBFBD><EFBFBD>璁″垝锛堟洿鍙<EFBFBD>潬锛?
|
||||
### V1.1 修正计划(更可靠)
|
||||
|
||||
```
|
||||
Day 1: 鏁版嵁搴?
|
||||
Day 2: 馃敟 REDCap API Adapter锛堟媺鍙栬兘鍔涳級鈫?浼樺厛锛屼富鍔ㄦ媺鍙?
|
||||
Day 2: 馃敟 SyncManager锛堣疆璇㈠厹搴曪級鈫?鏍稿績鍙<E7B8BE>潬鎬?
|
||||
Day 3: 馃敟 鍏ㄩ噺鎵<E599BA>弿鍔熻兘 鈫?鏀<>寔鍘嗗彶鏁版嵁
|
||||
Day 4: REDCap EM锛圵ebhook鎺ㄩ€侊級鈫?浣滀负澧炲己锛岃€岄潪鏍稿績
|
||||
Day 1: 数据库
|
||||
Day 2: 🔥 REDCap API Adapter(拉取能力)← 优先,主动拉取
|
||||
Day 2: 🔥 SyncManager(轮询兜底)← 核心可靠性
|
||||
Day 3: 🔥 全量扫描功能 ← 支持历史数据
|
||||
Day 4: REDCap EM(Webhook推送)← 作为增强,而非核心
|
||||
```
|
||||
|
||||
**璋冩暣鐞嗙敱**锛?
|
||||
**调整理由**:
|
||||
1. API拉取更可控(不依赖医院网络)
|
||||
2. 鑳借В鍐冲巻鍙叉暟鎹<EFBFBD>棶棰?
|
||||
2. 能解决历史数据问题
|
||||
3. Webhook作为增强,而非核心依赖
|
||||
|
||||
---
|
||||
@@ -123,17 +123,17 @@ Day 4: REDCap EM(Webhook推送)
|
||||
|
||||
### Dify RAG性能优化
|
||||
|
||||
**浼樺寲鍓?*锛?
|
||||
**优化前**:
|
||||
- 每次质控都调用Dify检索整个Protocol
|
||||
- 速度慢,Token消耗大
|
||||
|
||||
**浼樺寲鍚?*锛?
|
||||
- Protocol涓婁紶鏃讹紝棰勬彁鍙栧叧閿<EFBFBD><EFBFBD>鍒?
|
||||
- 缂撳瓨鍒癭cachedRules`瀛楁<EFBFBD>锛圝SONB锛?
|
||||
- 绠€鍗曡<EFBFBD>鍒欑洿鎺ュ垽鏂<EFBFBD>紙鏃犻渶璋冪敤Dify锛?
|
||||
**优化后**:
|
||||
- Protocol上传时,预提取关键规则
|
||||
- 缓存到`cachedRules`字段(JSONB)
|
||||
- 简单规则直接判断(无需调用Dify)
|
||||
- 复杂规则才调用Dify RAG
|
||||
|
||||
**鎬ц兘鎻愬崌**锛?
|
||||
**性能提升**:
|
||||
- 简单规则检查:<100ms(原1-2秒)
|
||||
- Token消耗降低:80%(只检索复杂规则)
|
||||
|
||||
@@ -143,88 +143,88 @@ Day 4: REDCap EM(Webhook推送)
|
||||
|
||||
| 修改内容 | 代码行数 | 文档章节 |
|
||||
|---------|---------|---------|
|
||||
| SyncManager锛堟贩鍚堝悓姝ワ級 | ~400琛?| 3.1.4 |
|
||||
| BulkScanService锛堝叏閲忔壂鎻忥級 | ~350琛?| 3.1.5 |
|
||||
| SyncManager(混合同步) | ~400行 | 3.1.4 |
|
||||
| BulkScanService(全量扫描) | ~350行 | 3.1.5 |
|
||||
| 数据库Schema更新 | +4字段 | 4.1 |
|
||||
| API端点新增 | +1端点 | 5.1 |
|
||||
| 寮€鍙戣<EFBFBD>鍒掕皟鏁?| 閲嶆帓浼樺厛绾?| 7.1 |
|
||||
| 开发计划调整 | 重排优先级 | 7.1 |
|
||||
| 前端技术栈明确 | Taro 4.x | 7.2 |
|
||||
| V1.1更新总结 | 完整记录 | 文档末尾 |
|
||||
|
||||
**鎬绘柊澧炰唬鐮?*锛殈750琛?
|
||||
**鏂囨。鏇存柊**锛殈300琛?
|
||||
**总新增代码**:~750行
|
||||
**文档更新**:~300行
|
||||
|
||||
---
|
||||
|
||||
## 鉁?楠屾敹妫€鏌ユ竻鍗?
|
||||
## ✅ 验收检查清单
|
||||
|
||||
- [x] SyncManager完整实现(智能同步、轮询、幂等性)
|
||||
- [x] BulkScanService完整实现(全量扫描、断点续传)
|
||||
- [x] 鏁版嵁搴揝chema鏇存柊锛?涓<>柊瀛楁<E7809B>锛?
|
||||
- [x] API绔<EFBFBD>偣鏂板<EFBFBD>锛坰can-all锛?
|
||||
- [x] 数据库Schema更新(4个新字段)
|
||||
- [x] API端点新增(scan-all)
|
||||
- [x] 开发计划调整(优先级重排)
|
||||
- [x] 鍓嶇<EFBFBD>鎶€鏈<EFBFBD>爤鏄庣‘锛圱aro锛?
|
||||
- [x] 鎬ц兘浼樺寲鏂规<EFBFBD>锛圖ify缂撳瓨锛?
|
||||
- [x] 前端技术栈明确(Taro)
|
||||
- [x] 性能优化方案(Dify缓存)
|
||||
- [x] V1.1更新总结(完整记录)
|
||||
- [x] 鏂囦欢閲嶅懡鍚嶏紙V1.0 鈫?V1.1锛?
|
||||
- [x] 文件重命名(V1.0 → V1.1)
|
||||
|
||||
---
|
||||
|
||||
## 🎯 关键成就
|
||||
|
||||
### 鏋舵瀯鍙<EFBFBD>潬鎬?
|
||||
### 架构可靠性
|
||||
|
||||
**V1.0**锛?
|
||||
- 鉂?渚濊禆Webhook锛堝尰闄㈠唴缃戜細澶辨晥锛?
|
||||
- 鉂?鍙<>洃鍚<E6B483>柊鏁版嵁锛堝巻鍙叉暟鎹<E69A9F>棤娉曡川鎺э級
|
||||
- 鉂?Webhook涓㈠け = 鏁版嵁閬楁紡
|
||||
**V1.0**:
|
||||
- ❌ 依赖Webhook(医院内网会失效)
|
||||
- ❌ 只监听新数据(历史数据无法质控)
|
||||
- ❌ Webhook丢失 = 数据遗漏
|
||||
|
||||
**V1.1**锛?
|
||||
- 鉁?娣峰悎鍚屾<E98D9A>锛圵ebhook + 杞<><E69D9E>鍙屼繚闄╋級
|
||||
- 鉁?鏀<>寔鍘嗗彶鏁版嵁锛堝叏閲忔壂鎻忥級
|
||||
- 鉁?鍙<>潬鎬э細99.9%锛堜笉渚濊禆鍖婚櫌缃戠粶锛?
|
||||
**V1.1**:
|
||||
- ✅ 混合同步(Webhook + 轮询双保险)
|
||||
- ✅ 支持历史数据(全量扫描)
|
||||
- ✅ 可靠性:99.9%(不依赖医院网络)
|
||||
|
||||
### 寮€鍙戞晥鐜?
|
||||
### 开发效率
|
||||
|
||||
- 鉁?瀹屽叏澶嶇敤骞冲彴鑳藉姏锛坰torage/logger/jobQueue/cache锛?
|
||||
- 鉁?Postgres-Only鏋舵瀯锛堟棤闇€Redis锛?
|
||||
- 鉁?鏂<>偣缁<E581A3>紶锛圕heckpointService閫氱敤鍖栵級
|
||||
- 鉁?浠g爜澶嶇敤鐜囷細>80%
|
||||
- ✅ 完全复用平台能力(storage/logger/jobQueue/cache)
|
||||
- ✅ Postgres-Only架构(无需Redis)
|
||||
- ✅ 断点续传(CheckpointService通用化)
|
||||
- ✅ 代码复用率:>80%
|
||||
|
||||
### 医疗合规
|
||||
|
||||
- 鉁?褰卞瓙鐘舵€佹満鍒讹紙AI鍙<49>缓璁<E7BC93>紝浜虹被纭<E8A2AB>潈锛?
|
||||
- 鉁?瀹屾暣瀹¤<E780B9>鏃ュ織锛堢<E9949B>鍚團DA 21 CFR Part 11锛?
|
||||
- 鉁?鍙<>拷婧<E68BB7>紙鎵€鏈夋搷浣滄湁trace_id锛?
|
||||
- ✅ 影子状态机制(AI只建议,人类确权)
|
||||
- ✅ 完整审计日志(符合FDA 21 CFR Part 11)
|
||||
- ✅ 可追溯(所有操作有trace_id)
|
||||
|
||||
---
|
||||
|
||||
## 馃搶 涓嬩竴姝ュ缓璁?
|
||||
## 📌 下一步建议
|
||||
|
||||
### 立即行动
|
||||
|
||||
1. **企业微信注册**(今天)
|
||||
- 娉ㄥ唽寮€鍙戣€呰处鍙?
|
||||
- 注册开发者账号
|
||||
- 创建测试应用
|
||||
- 获取API凭证
|
||||
|
||||
2. **技术栈确认**(今天)
|
||||
- Node.js 22 鉁?
|
||||
- PostgreSQL 15 鉁?
|
||||
- Taro 4.x锛堝皬绋嬪簭锛?鉁?
|
||||
- Node.js 22 ✅
|
||||
- PostgreSQL 15 ✅
|
||||
- Taro 4.x(小程序) ✅
|
||||
|
||||
3. **创建项目看板**(今天)
|
||||
- 鎸塚1.1浼樺厛绾ф帓鍒椾换鍔?
|
||||
- 按V1.1优先级排列任务
|
||||
- 每日站会同步进度
|
||||
|
||||
### MVP寮€鍙戝惎鍔<EFBFBD>紙鏄庡ぉ锛?
|
||||
### MVP开发启动(明天)
|
||||
|
||||
**Week 1 浼樺厛绾?*锛圴1.1鐗堬級锛?
|
||||
**Week 1 优先级**(V1.1版):
|
||||
1. Day 1: 数据库初始化 + 企微测试
|
||||
2. Day 2: REDCap API Adapter + SyncManager 鈫?鏍稿績
|
||||
3. Day 3: 鍏ㄩ噺鎵<EFBFBD>弿鍔熻兘 鈫?鏀<>寔鍘嗗彶鏁版嵁
|
||||
4. Day 4: REDCap EM + Webhook 鈫?澧炲己
|
||||
5. Day 5: 浼佸井閫傞厤鍣?+ 绔<>埌绔<E59F8C>祴璇?
|
||||
2. Day 2: REDCap API Adapter + SyncManager ← 核心
|
||||
3. Day 3: 全量扫描功能 ← 支持历史数据
|
||||
4. Day 4: REDCap EM + Webhook ← 增强
|
||||
5. Day 5: 企微适配器 + 端到端测试
|
||||
|
||||
---
|
||||
|
||||
@@ -232,24 +232,23 @@ Day 4: REDCap EM(Webhook推送)
|
||||
|
||||
**架构评审意见**:✅ **通过**
|
||||
|
||||
**鏍稿績淇<EFBFBD><EFBFBD>**锛?
|
||||
- 鉁?鑷村懡椋庨櫓锛堢綉缁滆繛閫氭€э級锛氬凡瑙e喅
|
||||
- 鉁?鍔熻兘缂哄け锛堝巻鍙叉暟鎹<E69A9F>級锛氬凡琛ュ厖
|
||||
- 鉁?鎶€鏈<E282AC>爤涓嶆槑锛堝墠绔<E5A2A0>級锛氬凡鏄庣‘
|
||||
**核心修正**:
|
||||
- ✅ 致命风险(网络连通性):已解决
|
||||
- ✅ 功能缺失(历史数据):已补充
|
||||
- ✅ 技术栈不明(前端):已明确
|
||||
|
||||
**鏂规<EFBFBD>鐘舵€?*锛?
|
||||
**方案状态**:
|
||||
- 🚀 **Ready to Code**
|
||||
- 馃幆 **鍙<>洿鎺ユ墽琛?*
|
||||
- 馃搵 **绗﹀悎浜戝師鐢熻<E990A2>鑼?*
|
||||
- 🎯 **可直接执行**
|
||||
- 📋 **符合云原生规范**
|
||||
- 💪 **医疗合规就绪**
|
||||
|
||||
---
|
||||
|
||||
**鎶ュ憡瀹屾垚鏃ユ湡**锛?025-12-31
|
||||
**缁存姢鑰?*锛氭灦鏋勫洟闃?
|
||||
**瀹℃煡鐘舵€?*锛氣渽 閫氳繃
|
||||
**鍙<EFBFBD>墽琛屾€?*锛氣渽 鍙<>珛鍗冲惎鍔∕VP寮€鍙?
|
||||
|
||||
**报告完成日期**:2025-12-31
|
||||
**维护者**:架构团队
|
||||
**审查状态**:✅ 通过
|
||||
**可执行性**:✅ 可立即启动MVP开发
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user