Files
AIclinicalresearch/docs/03-业务模块/IIT Manager Agent/06-开发记录/Day3-企业微信集成与端到端测试完成记录.md
HaHafeng 1b53ab9d52 feat(aia): Complete AIA V2.0 with universal streaming capabilities
Major Changes:
- Add StreamingService with OpenAI Compatible format
- Upgrade Chat component V2 with Ant Design X integration
- Implement AIA module with 12 intelligent agents
- Update API routes to unified /api/v1 prefix
- Update system documentation

Backend (~1300 lines):
- common/streaming: OpenAI Compatible adapter
- modules/aia: 12 agents, conversation service, streaming integration
- Update route versions (RVW, PKB to v1)

Frontend (~3500 lines):
- modules/aia: AgentHub + ChatWorkspace (100% prototype restoration)
- shared/Chat: AIStreamChat, ThinkingBlock, useAIStream Hook
- Update API endpoints to v1

Documentation:
- AIA module status guide
- Universal capabilities catalog
- System overview updates
- All module documentation sync

Tested: Stream response verified, authentication working
Status: AIA V2.0 core completed (85%)
2026-01-14 19:15:01 +08:00

807 lines
22 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Day 3 隡<><E99AA1>敺桐縑<E6A190><E7B891><EFBFBD>銝𡒊垢<F0A1928A>啁垢瘚贝<E7989A>摰峕<E691B0>霈啣<E99C88>
> **<2A><EFBFBD>**嚗?026-01-03
> **撘<><E69298>煾𧫴畾?*嚗鐝VP Week 1 - Day 3
> **<2A><EFBFBD><E8A9A8><EFBFBD>**嚗𡁏<E59A97><F0A1818F>?REDCap <20>?Node.js <20>?隡<><E99AA1>敺桐縑 <20><><EFBFBD><EFBFBD>湧𡡒<E6B9A7>?
> **摰鮋<E691B0>摰峕<E691B0>**嚗尠<E59A97> 蝡臬<E89DA1>蝡舀<E89DA1>霂閖<E99C82><EFBFBD>嚗愢VP<56>剔㴓<E58994><EFBFBD>?
---
## <20>㴓 銝<><E98A9D><EFBFBD><EFBFBD><EFBFBD>𤑳𤌍<F0A491B3><F0A48C8D><EFBFBD><EFBFBD><EFBFBD>
### 1.1 <20><EFBFBD><E8A9A8><EFBFBD>
**<EFBFBD><EFBFBD>撠誯𡡒<EFBFBD><EFBFBD>霂?*嚗?
```
REDCap敶訫<EFBFBD><EFBFBD>唳旿 <20>?Node.js摰墧𧒄<E5A2A7>閗繮 <20>?隡<><E99AA1>敺桐縑<E6A190><EFBFBD><E7AE84>𡁶䰻
<20>?
韐冽綉<E586BD><E7B689><EFBFBD> <20>?<3F><EFBFBD><E588B8><EFBFBD>𡁶䰻 <20>?PI<50>交𤣰
```
### 1.2 摰峕<E691B0><E5B395><EFBFBD>
| <20><EFBFBD><E79285> | <20><EFBFBD>?| 霂湔<E99C82> |
|---------|------|------|
| <20>?隡<><E99AA1>敺桐縑<E6A190><EFBFBD><E588B8><EFBFBD><EFBFBD>?| 摰峕<E691B0> | `WechatService.ts`嚗?14銵䕘<E98AB5> |
| <20>?隡<><E99AA1>敺桐縑<E6A190><EFBFBD><EFBFBD><E686AD> | 摰峕<E691B0> | `WechatCallbackController.ts`嚗?01銵䕘<E98AB5> |
| <20>?韐冽綉Worker<65><EFBFBD> | 摰峕<E691B0> | `iit_quality_check` Worker |
| <20>?Worker瘜典<E7989C><E585B8><EFBFBD> | 摰峕<E691B0> | `initIitManager()` <20>典鍳<E585B8>冽𧒄靚<F0A79284>鍂 |
| <20>?蝡臬<E89DA1>蝡舀<E89DA1>霂?| <20><EFBFBD> | REDCap <20>?Node.js <20>?隡<><E99AA1>敺桐縑 |
| <20>?<3F><EFBFBD><E887AC>滨蔭<E6BBA8><E894AD>﹝ | 摰峕<E691B0> | `WECHAT_ENV_CONFIG.md`嚗?01銵䕘<E98AB5> |
---
## <20><> 鈭䎚<E988AD><E48E9A><EFBFBD><EFBFBD><EFBFBD><E6A0BC><EFBFBD><E887AC>?
### 2.1 隡<><E99AA1>敺桐縑<E6A190><EFBFBD><E588B8><EFBFBD><EFBFBD><EFBFBD>WechatService嚗?
**<EFBFBD><EFBFBD>辣**嚗䫤backend/src/modules/iit-manager/services/WechatService.ts`嚗?14銵䕘<E98AB5>
**<2A><EFBFBD><E8A9A8><EFBFBD>**嚗?
```typescript
class WechatService {
// <20><EFBFBD>Access Token嚗<6E><E59A97>摮?撠𤩺𧒄嚗?
async getAccessToken(): Promise<string>
// <20><EFBFBD><E785BE><EFBFBD><EFBFBD><EFBFBD><E7A586>?
async sendTextMessage(userId: string, content: string): Promise<void>
// <20><EFBFBD><E785BE>arkdown瘨<6E><E798A8><EFBFBD><EFBFBD>格凒<E6A0BC><EFBFBD><E5959C><EFBFBD>扳𥁒<E689B3>𠺪<EFBFBD>
async sendMarkdownMessage(userId: string, content: string): Promise<void>
// <20><EFBFBD><E785BE>extcard<72><EFBFBD><EFBFBD><E798A8><EFBFBD><EFBFBD><EFBFBD>𡁶䰻嚗?
async sendTextcardMessage(userId: string, card: TextcardMessage): Promise<void>
}
```
**<2A>喲睸<E596B2><E79DB8><EFBFBD>?*嚗?
- <20>?Access Token蝻枏<E89DBB><E69E8F><EFBFBD><EFBFBD><E59A97>摮条<E691AE>摮矋<E691AE>2撠𤩺𧒄<F0A4A9BA><EFBFBD><E39787><EFBFBD><EFBFBD>
- <20>?隡<><E99AA1>敺桐縑API靚<49>鍂嚗Ǒ/cgi-bin/message/send`嚗?
- <20>?摰峕㟲<E5B395><E39FB2><EFBFBD>霂臬<E99C82><E887AC><EFBFBD><EFBFBD><EFBFBD><EFBFBD>霈啣<E99C88>
- <20>?<3F><EFBFBD>銝厩<E98A9D><EFBFBD><E798A8>蝐餃<E89D90>嚗ōext/markdown/textcard嚗?
**瘚贝<E7989A>撉諹<E69289>**嚗?
- <20>?雿輻鍂隡<E98D82><E99AA1>敺桐縑摰䀹䲮撘<E4B2AE><E69298>穃極<E7A983><EFBFBD>霂𤏪<E99C82>`access_token` + 瘨<><E798A8>API嚗?
- <20>?<3F><>𧋦瘨<F0A78BA6><E798A8>瘚贝<E7989A><E8B49D><EFBFBD>
- <20>?Textcard<72><EFBFBD><EFBFBD><E798A8>瘚贝<E7989A><E8B49D><EFBFBD>
- <20>?Markdown瘨<6E><E798A8>瘚贝<E7989A><E8B49D><EFBFBD>
- <20>?<3F>𧢲㦤蝡臭<E89DA1>銝𡁜凝靽⊥<E99DBD><E28AA5><EFBFBD>𦻖<EFBFBD><EFBFBD><E59786>厩掩<E58EA9>𧢲<EFBFBD><F0A7A2B2>?
---
### 2.2 隡<><E99AA1>敺桐縑<E6A190><EFBFBD><EFBFBD><E686AD>嚗ÁechatCallbackController嚗?
**<EFBFBD><EFBFBD>辣**嚗䫤backend/src/modules/iit-manager/controllers/WechatCallbackController.ts`嚗?01銵䕘<E98AB5>
**<2A><EFBFBD><E8A9A8><EFBFBD>**嚗?
```typescript
class WechatCallbackController {
// URL撉諹<E69289><EFBFBD><E59A97>銝𡁜凝靽⊿<E99DBD>甈⊿<E79488>蝵殷<E89DB5>
async verifyUrl(request, reply): Promise<void>
// <20>交𤣰<E4BAA4><EFBFBD><EFBFBD><E798A8><EFBFBD><E59A97>甇亙<E79487>憭齿芋撘𧶏<E69298>
async handleCallback(request, reply): Promise<void>
// 瘨<><E798A8><EFBFBD><E996AB>
private decryptMessage(encryptedMsg: string): any
// 瘨<><E798A8><EFBFBD><EFBFBD>
private encryptMessage(msg: string): string
// <20><><EFBFBD>蝑曉<E89D91>
private generateSignature(token, timestamp, nonce, encrypt): string
}
```
**<2A>喲睸<E596B2><E79DB8><EFBFBD>?*嚗?
- <20>?隡<><E99AA1>敺桐縑瘨<E7B891><E798A8><EFBFBD>㰘圾撖<E59CBE><E69296>`@wecom/crypto`嚗?
- <20>?XML瘨<4C><E798A8><EFBFBD><E996AB>嚗Ǒxml2js`嚗?
- <20>?蝑曉<E89D91>撉諹<E69289>嚗𠄎HA1嚗?
- <20>?撘<><EFBFBD>𧼮<EFBFBD><E79285><EFBFBD><E59A97><EFBFBD><EFBFBD><E594BE>?success"嚗<><E59A97><EFBFBD><EFBFBD><E595A3><EFBFBD><EFBFBD>
- <20>?雿輻鍂 `setImmediate` 蝖桐<E89D96><EFBFBD><EFBFBD><EFBFBD>
- <20>?LLM<4C>誩㦛霂<E3A69B><E99C82>嚗㇄ify嚗? 憭鋫gent頝舐眏
**瘚贝<E7989A>撉諹<E69289>**嚗?
- <20>?隡<><E99AA1>敺桐縑<E6A190><EFBFBD>URL撉諹<E69289><E8ABB9><EFBFBD>
- <20>?natapp<70><70><EFBFBD>蝛輸<E89D9B><EFBFBD>蝵格<E89DB5><E6A0BC><EFBFBD><EFBFBD>`http://iit.nat100.top`嚗?
- <20>?瘨<><E798A8><EFBFBD>㰘圾撖<E59CBE><E69296>霂閖<E99C82><EFBFBD>
- <20><EFBFBD> <20><EFBFBD><EFBFBD><E798A8><EFBFBD><E686AD><EFBFBD><EFBFBD><EFBFBD><E59A97><EFBFBD>𡒊賒<F0A1928A><EFBFBD>嚗?
---
### 2.3 韐冽綉Worker<65><EFBFBD><EFBFBD><E691B0>
**<EFBFBD><EFBFBD>辣**嚗䫤backend/src/modules/iit-manager/index.ts`
**<2A><EFBFBD><E8A9A8><EFBFBD>**嚗?
```typescript
// Worker瘜典<E7989C>
jobQueue.process<IitQualityCheckJob>('iit_quality_check', async (job) => {
const { projectId, recordId, instrument } = job.data;
// 1. <20><EFBFBD>憿寧𤌍靽⊥<E99DBD>
const project = await prisma.$queryRaw`...`;
// 2. <20><EFBFBD>UserID嚗<44>㴓憓<E3B493><E68693><EFBFBD><EFBFBD><E8AD8D><EFBFBD><EFBFBD>
const piUserId = process.env.WECHAT_TEST_USER_ID || 'FengZhiBo';
// 3. <20><EFBFBD>韐冽綉璉<E7B689><E79289>?
const qualityCheckResult = await performQualityCheck(...);
// 4. <20><EFBFBD><E785BE><EFBFBD>銝𡁜凝靽⊿<E99DBD>𡁶䰻
await wechatService.sendMarkdownMessage(piUserId, message);
// 5. 霈啣<E99C88>摰∟恣<E2889F><EFBFBD>
await prisma.$executeRaw`INSERT INTO audit_logs ...`;
return { status: 'success' };
});
```
**韐冽綉<E586BD><EFBFBD>**嚗<><E59A97><EFBFBD>𣇉<EFBFBD>嚗㚁<E59A97>
```typescript
async function performQualityCheck(projectId, recordId, instrument) {
const issues = [];
const recommendations = [];
// <20><EFBFBD><EFBFBD><E79289>?
if (!recordId || recordId.trim() === '') {
issues.push('霈啣<E99C88>ID<49><EFBFBD>');
}
if (!instrument || instrument.trim() === '') {
issues.push('銵典<E98AB5><E585B8>滨妍<E6BBA8><EFBFBD>');
}
// <20><EFBFBD><E59786><EFBFBD><E689B3><EFBFBD>隞𤾸恣霈⊥𠯫敹𡑒繮<F0A19192><EFBFBD>
const recentLogs = await prisma.$queryRaw`
SELECT created_at FROM audit_logs
WHERE action_type = 'redcap_data_received' ...
`;
const timeDiff = Date.now() - recentLogs[0].created_at.getTime();
if (timeDiff < 3600000) {
recommendations.push('<27>?<3F>唳旿敶訫<E695B6><E8A8AB>𦠜𧒄');
}
return { issues, recommendations };
}
```
**瘚贝<E7989A>撉諹<E69289>**嚗?
- <20>?Worker<65>𣂼<EFBFBD>瘜典<E7989C><E585B8>郡g-boss
- <20>?REDCap DET閫血<E996AB> <20>?隞餃𦛚<E9A483><EFBFBD>?<3F>?Worker<65><EFBFBD>
- <20>?韐冽綉璉<E7B689><E79289>仿<EFBFBD><EFBFBD><E9A489><EFBFBD><EFBFBD>
- <20>?隡<><E99AA1>敺桐縑<E6A190>𡁶䰻<F0A181B6><EFBFBD><E785BE><EFBFBD><EFBFBD>?
- <20>?摰∟恣<E2889F><EFBFBD>霈啣<E99C88><E595A3>𣂼<EFBFBD>
---
### 2.4 Worker瘜典<E7989C><E585B8><EFBFBD>靽桀<E99DBD>
**<2A><EFBFBD>**嚗帋<E59A97><E5B88B>?`initIitManager()` <20>賣㺭<E8B3A3>芾◤靚<E297A4>鍂嚗<E98D82><EFBFBD>幃orker<65>芣釣<E88AA3>?
**靽桀<E99DBD>**嚗䫤backend/src/index.ts`
```typescript
// <20>?靽桀<E99DBD><E6A180><EFBFBD>Worker<65>芣釣<E88AA3><EFBFBD>
async function start() {
await jobQueue.start();
registerParseExcelWorker();
logger.info('<27>?DC Tool C parse excel worker registered');
// <20>?敹䁅扇靚<E68987>鍂 initIitManager()
await new Promise(resolve => setTimeout(resolve, 3000));
}
// <20>?靽桀<E99DBD><E6A180>𠬍<EFBFBD>Worker甇<72>瘜典<E7989C>嚗?
async function start() {
await jobQueue.start();
registerParseExcelWorker();
logger.info('<27>?DC Tool C parse excel worker registered');
// <20>?瘜典<E7989C>IIT Manager Workers
await initIitManager();
logger.info('<27>?IIT Manager workers registered');
await new Promise(resolve => setTimeout(resolve, 3000));
}
```
**撉諹<E69289>**嚗?
- <20>?<3F>臬𢆡<E887AC><EFBFBD><E4BA99>曄內 "IIT Manager workers registered"
- <20>?`iit_quality_check` Worker<65>𣂼<EFBFBD><EFBFBD><E686AD>隞餃𦛚
- <20>?`iit_redcap_poll` Worker撌脫釣<E884AB><EFBFBD>摰𡁏𧒄隞餃𦛚撌脫<E6928C><E884AB><EFBFBD><E597A5><EFBFBD>
---
### 2.5 <20>唳旿摨枏<E691A8>畾萄<E795BE>靽桀<E99DBD>
**<EFBFBD><EFBFBD>1**嚗䫤notification_config` 摮埈挾銝滚<E98A9D><E6BB9A>?
**<2A><EFBFBD>**嚗阳orker隞<72><E99A9E><EFBFBD>亥砭鈭<E7A0AD><EFBFBD><EFBFBD>銵其葉銝滚<E98A9D><E6BB9A><EFBFBD>摮埈挾
**靽桀<E99DBD>**嚗?
```typescript
// <20>?銋见<E98A8B><EFBFBD>䰻霂<E99C82>摮睃銁<E79D83><E98A81><EFBFBD>畾蛛<E795BE>
SELECT id, name, redcap_project_id, notification_config
FROM iit_schema.projects
WHERE id = ${projectId}
// <20>?<3F>啣銁嚗<E98A81><EFBFBD>亥砭摮睃銁<E79D83><E98A81><EFBFBD>畾蛛<E795BE>
SELECT id, name, redcap_project_id
FROM iit_schema.projects
WHERE id = ${projectId}
// UserID<49>湔𦻖隞𡒊㴓憓<E3B493><E68693><EFBFBD>讛繮<E8AE9B><EFBFBD>瘚贝<E7989A><E79285>嚗?
const piUserId = process.env.WECHAT_TEST_USER_ID || 'FengZhiBo';
```
**<2A><EFBFBD>2**嚗䫤action` 摮埈挾銝滚<E98A9D><E6BB9A><EFBFBD>摨𥪯蛹 `action_type`嚗?
**靽桀<E99DBD>**嚗?
```typescript
// <20>?銋见<E98A8B>
INSERT INTO iit_schema.audit_logs (project_id, action, entity_id, details)
WHERE action = 'redcap_data_received'
// <20>?<3F>啣銁
INSERT INTO iit_schema.audit_logs (project_id, action_type, entity_id, details)
WHERE action_type = 'redcap_data_received'
```
**撉諹<E69289>**嚗?
- <20>?Worker<65><EFBFBD><E689AF>䭾㺭<E4ADBE><EFBFBD><E6A180>躰秤
- <20>?摰∟恣<E2889F><EFBFBD>霈啣<E99C88><E595A3>𣂼<EFBFBD>
- <20>?韐冽綉隞餃𦛚摰峕㟲瘚<E39FB2><E7989A><EFBFBD><EFBFBD>
---
## <20>妒 銝剹<E98A9D><E589B9><EFBFBD>啁垢瘚贝<E7989A>
### 3.1 瘚贝<E7989A><E8B49D><EFBFBD>
| 蝏<>辣 | <20>滨蔭 | <20><EFBFBD>?|
|------|------|------|
| REDCap | Docker 15.8.0 + 瘚贝<E7989A>憿寧𤌍(PID 16) | <20>?餈鞱<E9A488>銝?|
| Node.js Backend | Fastify + pg-boss + Prisma | <20>?餈鞱<E9A488>銝?|
| PostgreSQL | Docker + iit_schema | <20>?餈鞱<E9A488>銝?|
| 隡<><E99AA1>敺桐縑 | <20>芸遣摨𠉛鍂 + 瘚贝<E7989A><E8B49D><EFBFBD>(FengZhiBo) | <20>?撌脤<E6928C>蝵?|
| natapp | <20><><EFBFBD>蝛輸<E89D9B>?iit.nat100.top) | <20>?撌脤<E6928C>蝵?|
### 3.2 瘚贝<E7989A><EFBFBD><E7989A>
**瘚贝<E7989A>甇仿炊**嚗?
1. <20>?REDCap<61>𥕦遣<F0A595A6>啗扇敶𤏪<E695B6>ID: 9嚗?
2. <20>?REDCap DET摰墧𧒄閫血<E996AB>Webhook嚗?蝘鍦辣餈<E8BEA3><E9A488>
3. <20>?Node.js WebhookController<65>交𤣰霂瑟<E99C82>嚗?10ms<6D><EFBFBD>嚗?
4. <20>?<3F><EFBFBD><E588B8><EFBFBD><EFBFBD> `iit_quality_check` <20><EFBFBD>
5. <20>?Worker<65><EFBFBD>韐冽綉璉<E7B689><E79289>?
6. <20>?<3F><EFBFBD><E785BE><EFBFBD>銝𡁜凝靽⊿<E99DBD>𡁶䰻
7. <20>?<3F>𧢲㦤蝡臭<E89DA1>銝𡁜凝靽⊥𦻖<E28AA5><EFBFBD>𡁶䰻
**瘚贝<E7989A>霈啣<E99C88>**嚗㇆D: 9嚗㚁<E59A97>
```
2026-01-03 14:02:07.995 [aiclinical-backend] info: REDCap Webhook received
{ project_id: "16", record: "9", instrument: "demographics" }
2026-01-03 14:02:08.026 [aiclinical-backend] info: Record data fetched from REDCap
{ recordCount: 1 }
[PgBossQueue] Job pushed: 7a5da656-85d2-4885-bc2d-625fce74d926 (type: iit_quality_check)
2026-01-03 14:02:08.037 [aiclinical-backend] info: <20><> Quality check job started
{ jobId: "7a5da656...", projectId: "40062738...", recordId: "9" }
2026-01-03 14:02:08.039 [aiclinical-backend] info: <20>㨩 Preparing to send WeChat notification
{ piUserId: "FengZhiBo", source: "env_variable_direct" }
2026-01-03 14:02:08.042 [aiclinical-backend] info: <20><> Quality check completed
{ issuesCount: 0, recommendationsCount: 3 }
2026-01-03 14:02:08.045 [aiclinical-backend] info: <20>?摰∟恣<E2889F><EFBFBD>霈啣<E99C88><E595A3>𣂼<EFBFBD>
{ recordId: "9" }
2026-01-03 14:02:08.048 [aiclinical-backend] info: <20>?Quality check completed and notification sent
{ piUserId: "FengZhiBo", hasIssues: false }
```
**隡<><E99AA1>敺桐縑<E6A190>交𤣰<E4BAA4><F0A4A3B0>捆**嚗?
```
<EFBFBD><EFBFBD> IIT Manager <20>唳旿敶訫<E695B6><E8A8AB>𡁶䰻
憿寧𤌍嚗魩est0102
霈啣<EFBFBD>ID嚗?
銵典<EFBFBD>嚗飱emographics
<EFBFBD>園𡢿嚗?026-01-03 14:02:08
<EFBFBD>働 韐冽綉撱箄悅嚗?
1. <20>?<3F>唳旿敶訫<E695B6><E8A8AB>𦠜𧒄
2. <20>?霈啣<E99C88>ID<49><EFBFBD>
3. <20>?銵典<E98AB5>嚗飱emographics
<EFBFBD>?<3F>唳旿韐券<E99F90><E588B8>臬末
<EFBFBD>俥 憒<><E68692><EFBFBD>煾䔮嚗諹窈<E8ABB9>𧼮<EFBFBD>"撣桀𨭌"<22><EFBFBD><E4BAA6><EFBFBD><E6B8B8><EFBFBD>
```
### 3.3 瘚贝<E7989A>蝏𤘪<E89D8F>
| 瘚贝<E7989A>憿?| <20><><EFBFBD> | 摰鮋<E691B0> | <20><EFBFBD>?|
|-------|------|------|------|
| REDCap閫血<E996AB> | 靽嘥<E99DBD><E598A5>𡒊<EFBFBD><F0A1928A>唾圻<E594BE>?| 0蝘鍦辣餈?| <20>?|
| Webhook<6F>交𤣰 | <10ms<6D><EFBFBD> | 5.8ms | <20>?|
| 隞餃𦛚<E9A483><EFBFBD>?| <20>𣂼<EFBFBD><F0A382BC><EFBFBD><E588B8><EFBFBD><EFBFBD><EFBFBD> | <20>𣂼<EFBFBD> | <20>?|
| Worker<65><EFBFBD> | Worker憭<72><E686AD>隞餃𦛚 | <20>𣂼<EFBFBD><F0A382BC><EFBFBD> | <20>?|
| 韐冽綉璉<E7B689><E79289>?| 餈𥪜<E9A488>韐冽綉蝏𤘪<E89D8F> | 3<>遣霈?| <20>?|
| 隡<><E99AA1>敺桐縑<E6A190><EFBFBD>?| <20><EFBFBD><E785BE><EFBFBD>𡁶䰻<F0A181B6>𣂼<EFBFBD> | <20>𣂼<EFBFBD> | <20>?|
| <20>𧢲㦤<F0A7A2B2>交𤣰 | <20>交𤣰<E4BAA4><EFBFBD>𡁶䰻 | <20>𣂼<EFBFBD><F0A382BC>交𤣰 | <20>?|
| 摰∟恣<E2889F><EFBFBD> | 霈啣<E99C88><E595A3>唳㺭<E594B3><EFBFBD> | <20>𣂼<EFBFBD>霈啣<E99C88> | <20>?|
| 敺芰㴓<E88AB0><EFBFBD>?| <20><EFBFBD><E88AB8><EFBFBD><EFBFBD>甈?| <20><EFBFBD><E88AB8><EFBFBD><EFBFBD>甈?| <20>?|
**<EFBFBD>喲睸<EFBFBD><EFBFBD><EFBFBD>**嚗?
- <20>?蝡臬<E89DA1>蝡臬辣餈<E8BEA3><E9A488><2蝘𡜐<E89D98>REDCap靽嘥<E99DBD> <20>?隡<><E99AA1>敺桐縑<E6A190>交𤣰嚗?
- <20>?Webhook<6F><EFBFBD><E6BB9A>園𡢿嚗?.8ms
- <20>?Worker<65><EFBFBD><E689AF>園𡢿嚗鰺50ms
- <20>?瘨<><E798A8><EFBFBD><EFBFBD><E785BE><EFBFBD><EFBFBD><EFBFBD>嚗?00%嚗<><E59A97>霂?甈∴<E79488><E288B4><EFBFBD><E588B8>𣂼<EFBFBD>嚗?
- <20>?<3F>惩儐<E683A9><EFBFBD><E887AC><EFBFBD>䔮憸?
---
## <20><20><EFBFBD><E49C98><EFBFBD>嗆綳<E59786><EFBFBD><E8B3AD><EFBFBD><EFBFBD><EFBFBD>箏𦛚
### 4.1 銝湔𧒄<E6B994>芣鴌嚗㇈VP<56>嗆挾嚗?
| 摨誩噡 | 銝湔𧒄<E6B994>芣鴌 | <20><EFBFBD> | 霈<E99C88><E288AA><EFBFBD><E5AFA1>園𡢿 | <20><EFBFBD><E5AFA1><EFBFBD> |
|------|---------|------|------------|---------|
| 1 | **UserID蝖祉<E89D96><E7A589>?* | 蝞<><E89D9E>𡝗<EFBFBD>霂閙<E99C82>蝔?| Phase 2 | 隞𡡞★<F0A1A19E><EFBFBD>蝵株”霂餃<E99C82> `notification_config.wechat_user_id` |
| 2 | **摰𡁏𧒄頧株砭蝳<E7A0AD>** | MVP銝漤<E98A9D><EFBFBD><E996AC>Webhook撌脰雲憭?| Phase 2 | 摰䂿緵 `jobQueue.schedule()` <20>碶蝙<E7A2B6>?`node-cron` |
| 3 | **韐冽綉<E586BD><EFBFBD><EFBFBD><E89D9E>?* | 隞<>抅蝖<E68A85><EFBFBD><E79289><EFBFBD><E4BC90>鼦I韐冽綉 | Phase 1.5 | <20><><EFBFBD>Dify RAG + 閫<><E996AB>撘閙<E69298> |
| 4 | **摰∟恣<E2889F><EFBFBD>摮埈挾** | `notification_config` 摮埈挾<E59F88><EFBFBD>撱?| Phase 2 | 瘛餃<E7989B>JSONB摮埈挾摮睃<E691AE><EFBFBD><E99AA1>敺桐縑<E6A190>滨蔭 |
| 5 | **Access Token蝻枏<E89DBB>** | <20><><EFBFBD>蝻枏<E89DBB>嚗屸<E59A97><E5B1B8>臭腺憭?| Phase 2 | 雿輻鍂Redis<69>𡝗㺭<F0A19D97><EFBFBD>蝻枏<E89DBB> |
**霂衣<E99C82>霂湔<E99C82>**嚗?
#### 1. UserID蝖祉<E89D96><E7A589><EFBFBD><EFBFBD><EFBFBD><EFBFBD><E887AC><EFBFBD>嚗?
**敶枏<E695B6>摰䂿緵**嚗?
```typescript
// <20>湔𦻖隞𡒊㴓憓<E3B493><E68693><EFBFBD>讛繮<E8AE9B>?
const piUserId = process.env.WECHAT_TEST_USER_ID || 'FengZhiBo';
```
**<EFBFBD><EFBFBD>**嚗?
- <20>?<3F><EFBFBD><E4ADBE><EFBFBD>憭𡁻★<F0A181BB><EFBFBD><E69FB4><EFBFBD>PI
- <20>?<3F>煺漣<E785BA><EFBFBD><E887AC><EFBFBD><EFBFBD><E996AC>銝芷★<E88AB7><EFBFBD>蝵桐<E89DB5><E6A190>𣬚<EFBFBD>UserID
**霈<E99C88><E288AA><EFBFBD>**嚗㇊hase 2嚗㚁<E59A97>
```typescript
// 隞𡡞★<F0A1A19E><EFBFBD>蝵株”霂餃<E99C82>
const project = await prisma.iitProject.findUnique({
where: { id: projectId },
select: { notificationConfig: true }
});
const piUserId = project.notificationConfig?.wechat_user_id
|| process.env.WECHAT_TEST_USER_ID
|| 'FengZhiBo';
```
**<EFBFBD>唳旿摨廍chema<EFBFBD><EFBFBD>**嚗?
```sql
ALTER TABLE iit_schema.projects
ADD COLUMN notification_config JSONB DEFAULT '{}'::jsonb;
-- 蝷箔<E89DB7><E7AE94>唳旿
{
"wechat_user_id": "FengZhiBo",
"wechat_department_id": 1,
"notification_types": ["data_entry", "quality_issue", "task_reminder"]
}
```
---
#### 2. 摰𡁏𧒄頧株砭蝳<E7A0AD>
**敶枏<E695B6>摰䂿緵**嚗?
```typescript
// <20><EFBFBD> <20><>𧒄蝳<F0A79284>鍂摰𡁏𧒄頧株砭嚗㇈VP<56>嗆挾嚗𡦀ebhook撌脰雲憭<E99BB2><E686AD>
// TODO: Phase 2 - 摰䂿緵摰𡁏𧒄頧株砭雿靝蛹銵亙<E98AB5><E4BA99><EFBFBD>
// await syncManager.initScheduledJob();
logger.info('IIT Manager: Scheduled job registration skipped (using Webhook only for MVP)');
```
**<EFBFBD><EFBFBD>**嚗?
- <20>𩤃<EFBFBD> `jobQueue.schedule()` <20><EFBFBD>銝滚<E98A9D><E6BB9A><EFBFBD>`PgBossQueue` <20><EFBFBD><E88AB8><EFBFBD>
- <20>𩤃<EFBFBD> MVP<56>嗆挾銝漤<E98A9D><EFBFBD><E996AC><EFBFBD>嗉蔭霂<E99C82>REDCap DET撌脰雲憭<E99BB2><E686AD>
**霈<E99C88><E288AA><EFBFBD><E5AFA1><EFBFBD>**嚗㇊hase 2嚗㚁<E59A97>
**<EFBFBD><EFBFBD>A嚗帋蝙<EFBFBD>?`node-cron`<EFBFBD><EFBFBD><EFBFBD>**
```typescript
import cron from 'node-cron';
// 瘥?<3F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>甈?
cron.schedule('*/5 * * * *', async () => {
logger.info('<27>?REDCap摰𡁏𧒄頧株砭撘<E7A0AD>憪?);
const syncManager = new SyncManager();
await syncManager.handlePoll();
}, {
timezone: 'Asia/Shanghai'
});
```
**<EFBFBD><EFBFBD>B嚗𡁏<EFBFBD>撅?`PgBossQueue` 摰䂿緵 `schedule` <20><EFBFBD>**
```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 <20><EFBFBD> cron 銵刻噢撘?
return await this.boss.send(name, data, {
...options,
startAfter: new Date(),
singletonKey: name,
priority: 1
});
}
}
```
**隡睃<E99AA1>蝥?*嚗帋<E59A97>嚗Áebhook頞喳<E9A09E><E596B3><EFBFBD><EFBFBD><E59A97><EFBFBD>嗉蔭霂<E99C82>雿靝蛹<E99D9D>𨅯<EFBFBD>嚗?
---
#### 3. 韐冽綉<E586BD><EFBFBD><EFBFBD><E89D9E>?
**敶枏<E695B6>摰䂿緵**嚗<>抅蝖<E68A85><EFBFBD><E79289><EFBFBD>嚗?
```typescript
async function performQualityCheck(projectId, recordId, instrument) {
const issues = [];
const recommendations = [];
// <20>?<3F><EFBFBD><EFBFBD><E79289>?
if (!recordId) issues.push('霈啣<E99C88>ID<49><EFBFBD>');
if (!instrument) issues.push('銵典<E98AB5><E585B8>滨妍<E6BBA8><EFBFBD>');
// <20>?<3F><EFBFBD><E59786><EFBFBD><E689B3>?
const timeDiff = Date.now() - lastUpdate;
if (timeDiff < 3600000) {
recommendations.push('<27>?<3F>唳旿敶訫<E695B6><E8A8AB>𦠜𧒄');
}
return { issues, recommendations };
}
```
**霈<E99C88><E288AA><EFBFBD>**嚗㇊hase 1.5嚗㚁<EFBFBD>
**AI韐冽綉<E586BD><EFBFBD>**嚗?
```typescript
async function performQualityCheck(projectId, recordId, instrument) {
// 1. <20><EFBFBD>憿寧𤌍<E5AFA7><F0A48C8D><EFBFBD><EFBFBD><E689AF>?
const project = await prisma.iitProject.findUnique({
where: { id: projectId },
select: { cachedRules: true }
});
// 2. <20><EFBFBD>霈啣<E99C88><E595A3>唳旿
const recordData = await redcapAdapter.exportRecords([recordId]);
// 3. 靚<>鍂Dify韐冽綉Agent
const difyResponse = await fetch('https://api.dify.ai/v1/chat-messages', {
method: 'POST',
body: JSON.stringify({
inputs: {
rules: project.cachedRules,
record: recordData[0],
instrument
},
query: '霂瑟<E99C82><E7919F><EFBFBD><E4BAA5>∟扇敶閧<E695B6><E996A7>唳旿韐券<E99F90>',
user: `iit_${projectId}`
})
});
const result = await difyResponse.json();
// 4. 閫<><E996AB>AI餈𥪜<E9A488><F0A5AA9C><EFBFBD><EFBFBD><EFBFBD><E68A92>?
return {
issues: result.issues || [],
recommendations: result.recommendations || [],
aiSuggestions: result.suggestions || []
};
}
```
**隡睃<E99AA1>蝥?*嚗𡁻<E59A97><EFBFBD>瓲敹<E793B2><EFBFBD><EFBFBD><E6BDAD><EFBFBD>
---
#### 4. 摰∟恣<E2889F><EFBFBD>摮埈挾
**敶枏<E695B6><E69E8F><EFBFBD>**嚗?
- <20>?`projects` 銵函撩撠?`notification_config` 摮埈挾
- <20>?UserID<49><44>𧒄隞𡒊㴓憓<E3B493><E68693><EFBFBD>讛粉<E8AE9B>?
**霈<E99C88><E288AA><EFBFBD>**嚗㇊hase 2嚗㚁<E59A97>
```sql
-- 瘛餃<E7989B><E9A483>𡁶䰻<F0A181B6>滨蔭摮埈挾
ALTER TABLE iit_schema.projects
ADD COLUMN notification_config JSONB DEFAULT '{}'::jsonb;
-- 瘛餃<E7989B>瘜券<E7989C>
COMMENT ON COLUMN iit_schema.projects.notification_config IS '<EFBFBD><EFBFBD>敺桐縑<EFBFBD>𡁶䰻<EFBFBD>滨蔭嚗㇎serID<EFBFBD><EFBFBD><EFBFBD><EFBFBD>沉D<EFBFBD><EFBFBD><EFBFBD>𡁶䰻蝐餃<EFBFBD>蝑㚁<EFBFBD>';
-- <20>𥕦遣蝝<E89D9D><EFBFBD><E59A97><EFBFBD><EFBFBD>䰻霂<E99C82>
CREATE INDEX idx_projects_notification_config
ON iit_schema.projects USING GIN (notification_config);
```
**隡睃<E99AA1>蝥?*嚗帋葉嚗<E89189><EFBFBD><EFBFBD>憿寧𤌍<E5AFA7><EFBFBD>嚗?
---
#### 5. Access Token蝻枏<E89DBB>
**敶枏<E695B6>摰䂿緵**嚗<><E59A97>摮条<E691AE>摮矋<E691AE>嚗?
```typescript
class WechatService {
private accessTokenCache: {
token: string | null;
expiresAt: number | null;
} = {
token: null,
expiresAt: null,
};
async getAccessToken(): Promise<string> {
// 璉<><E79289><EFBFBD>摮?
if (this.accessTokenCache.token &&
this.accessTokenCache.expiresAt &&
Date.now() < this.accessTokenCache.expiresAt) {
return this.accessTokenCache.token;
}
// <20>齿鰵<E9BDBF><EFBFBD>
const response = await fetch(...);
this.accessTokenCache = {
token: response.access_token,
expiresAt: Date.now() + 7000 * 1000 // <20>𣂼<EFBFBD>5<EFBFBD><35><EFBFBD><EFBFBD><E9A488>
};
return this.accessTokenCache.token;
}
}
```
**<EFBFBD><EFBFBD>**嚗?
- <20>𩤃<EFBFBD> <20>滚鍳<E6BB9A>滚𦛚<E6BB9A>𡒊<EFBFBD>摮䀝腺憭?
- <20>𩤃<EFBFBD> 憭𡁜<E686AD>靘钅<E99D98>蝵脫𧒄<E884AB><EFBFBD><E4ADBE>曹澈蝻枏<E89DBB>
**霈<E99C88><E288AA><EFBFBD>**嚗㇊hase 2嚗㚁<E59A97>
**<EFBFBD><EFBFBD>A嚗帋蝙<EFBFBD>沖edis**
```typescript
import { redis } from '../../common/cache/redis.js';
async getAccessToken(): Promise<string> {
// 隞竃edis<69><EFBFBD>
const cached = await redis.get('wechat:access_token');
if (cached) return cached;
// <20>齿鰵<E9BDBF><EFBFBD>撟嗥<E6929F>摮?
const response = await fetch(...);
await redis.setex('wechat:access_token', 7000, response.access_token);
return response.access_token;
}
```
**<EFBFBD><EFBFBD>B嚗帋蝙<EFBFBD>冽㺭<EFBFBD><EFBFBD>**
```sql
CREATE TABLE iit_schema.wechat_tokens (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
token_type VARCHAR(50) NOT NULL, -- 'access_token'
token TEXT NOT NULL,
expires_at TIMESTAMPTZ NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE (token_type)
);
```
**隡睃<E99AA1>蝥?*嚗帋<E59A97><EFBFBD><E59A97>摰硺<E691B0><E7A1BA>函蔡<E587BD>舀𦻖<E88880><EFBFBD><E6A2B9><EFBFBD><EFBFBD>蝻枏<E89DBB>撌脰雲憭<E99BB2><E686AD>
---
### 4.2 <20><><EFBFBD><EFBFBD>箏𦛚皜<F0A69B9A><E79A9C>
| 摨誩噡 | <20><><EFBFBD><EFBFBD>箏𦛚 | 敶勗<E695B6> | 隡睃<E99AA1>蝥?| 霈<E99C88><E288AA>園𡢿 |
|------|---------|------|-------|---------|
| 1 | **韐冽綉<E586BD><EFBFBD><EFBFBD><E89D9E>?* | <20>鼦I<E9BCA6><EFBFBD>嚗䔶遠<E494B6><EFBFBD><E6BDAD>?| <20>𣞁 擃?| Phase 1.5 |
| 2 | **UserID蝖祉<E89D96><E7A589>?* | <20><EFBFBD>憭𡁻★<F0A181BB><EFBFBD>蝵?| <20><> 銝?| Phase 2 |
| 3 | **notification_config摮埈挾蝻箏仃** | <20><EFBFBD><E4ADBE>菜暑<E88F9C>滨蔭<E6BBA8>𡁶䰻 | <20><> 銝?| Phase 2 |
| 4 | **摰𡁏𧒄頧株砭<E6A0AA><EFBFBD><E88AB8>?* | <20><EFBFBD>摨閙㦤<E99699>?| <20>椬 雿?| Phase 2 |
| 5 | **Access Token<65><6E><EFBFBD>蝻枏<E89DBB>** | <20>滚鍳銝仃 | <20>椬 雿?| Phase 2 |
| 6 | **<EFBFBD>躰秤憭<EFBFBD><EFBFBD>銝滚<EFBFBD><EFBFBD>?* | <20><EFBFBD><EFBFBD><EFBFBD><EFBFBD><E88AA3>?| <20><> 銝?| Phase 2 |
| 7 | **<EFBFBD><EFBFBD>蝥批<EFBFBD>瘛瑚僚** | info/debug/error瘛瑞鍂 | <20><20><><EFBFBD> | Phase 3 |
---
### 4.3 憌𡡞埯銝𡒊<E98A9D><EFBFBD><EFBFBD>?
| 憌𡡞埯 | 敶勗<E695B6> | 璁<><E79281> | 蝻栞圾<E6A09E>芣鴌 | <20><EFBFBD>?|
|------|------|------|---------|------|
| Webhook憭梯揖撖潸稲<E6BDB8>唳旿銝仃 | <20>𣞁 擃?| <20>椬 銝?| 摰𡁏𧒄頧株砭<E6A0AA>𨅯<EFBFBD> | <20><EFBFBD> <20><>𧊋摰䂿緵 |
| 隡<><E99AA1>敺桐縑API<50><EFBFBD> | <20><> 銝?| <20>叚 雿?| <20><EFBFBD><E99E89><EFBFBD> + <20><EFBFBD><E6BBA9><EFBFBD> | <20><EFBFBD><><E695BA><EFBFBD>?|
| Access Token餈<6E><E9A488> | <20><> 銝?| <20>椬 銝?| <20>芸𢆡<E88AB8>瑟鰵<E7919F><EFBFBD> | <20>?撌脣<E6928C><E884A3>?|
| <20>唳旿摨栞<E691A8><E6A09E>亙仃韐?| <20>𣞁 擃?| <20>叚 雿?| 餈墧𦻖瘙?+ <20><EFBFBD> | <20>?撌脣<E6928C><E884A3>?|
| Worker<65><EFBFBD>憭梯揖 | <20><> 銝?| <20>椬 銝?| pg-boss<73>芸𢆡<E88AB8><EFBFBD> | <20>?撌脣<E6928C><E884A3>?|
---
## <20><> 鈭𢛵<E988AD><F0A29BB5><EFBFBD><E8AAA8><EFBFBD>霈?
### 5.1 <20><EFBFBD><EFBFBD><E99A9E><EFBFBD>?
| 璅<E79285> | <20><>辣 | 銵峕㺭 | 霂湔<E99C82> |
|------|------|------|------|
| 隡<><E99AA1>敺桐縑<E6A190><EFBFBD>?| `WechatService.ts` | 314 | Access Token + 瘨<><E798A8><EFBFBD><EFBFBD>?|
| 隡<><E99AA1>敺桐縑<E6A190><EFBFBD> | `WechatCallbackController.ts` | 501 | URL撉諹<E69289> + 瘨<><E798A8><EFBFBD>交𤣰 |
| 韐冽綉Worker | `index.ts` | 336 | Worker瘜典<E7989C> + 韐冽綉<E586BD><EFBFBD> |
| 頝舐眏<E88890>滨蔭 | `routes/index.ts` | 203 | 隡<><E99AA1>敺桐縑頝舐眏 |
| <20><EFBFBD><E887AC>滨蔭<E6BBA8><E894AD>﹝ | `WECHAT_ENV_CONFIG.md` | 401 | 隡<><E99AA1>敺桐縑<E6A190>滨蔭<E6BBA8><E894AD><EFBFBD> |
| **<EFBFBD>餉恣** | - | **1,755** | Day 3<><EFBFBD><EFBFBD><E99A9E> |
### 5.2 蝝航恣隞<E681A3><E99A9E><EFBFBD>𧶏<EFBFBD>Day 1-3嚗?
| <20>嗆挾 | 隞<><E99A9E><EFBFBD>?| 霂湔<E99C82> |
|------|-------|------|
| Day 1 | 223銵?| <20>唳旿摨廍chema + 璅<E79285>撉冽沲 |
| Day 2 | 2,200銵?| REDCap<61><70><EFBFBD> + Worker瘜典<E7989C> |
| Day 3 | 1,755銵?| 隡<><E99AA1>敺桐縑<E6A190><E7B891><EFBFBD> + 蝡臬<E89DA1>蝡舀<E89DA1>霂?|
| **<EFBFBD>餉恣** | **4,178銵?* | MVP<56><EFBFBD><EFBFBD><E99A9E> |
---
## <20><20><EFBFBD><E58786><EFBFBD>霂閗<E99C82><E99697>?
### 6.1 <20><EFBFBD>瘚贝<E7989A>
| 瘚贝<E7989A><E8B49D>箸艶 | <20><EFBFBD>?| 霂湔<E99C82> |
|---------|------|------|
| <20>?REDCap DET閫血<E996AB> | <20><EFBFBD> | 0蝘鍦辣餈?|
| <20>?Webhook<6F>交𤣰 | <20><EFBFBD> | <10ms<6D><EFBFBD> |
| <20>?隞餃𦛚<E9A483><EFBFBD>?| <20><EFBFBD> | <20><EFBFBD><E588B8><EFBFBD>pg-boss<73><EFBFBD> |
| <20>?Worker<65><EFBFBD> | <20><EFBFBD> | 韐冽綉<E586BD><EFBFBD><E9A489><EFBFBD> |
| <20>?隡<><E99AA1>敺桐縑<E6A190><EFBFBD><E588B8><EFBFBD><EFBFBD><EFBFBD>𧋦嚗?| <20><EFBFBD> | <20>𧢲㦤<F0A7A2B2>交𤣰<E4BAA4>𣂼<EFBFBD> |
| <20>?隡<><E99AA1>敺桐縑<E6A190><EFBFBD><E588B8><EFBFBD><EFBFBD><EFBFBD>嚗?| <20><EFBFBD> | <20>𧢲㦤<F0A7A2B2>交𤣰<E4BAA4>𣂼<EFBFBD> |
| <20>?隡<><E99AA1>敺桐縑<E6A190><EFBFBD><E588B8><EFBFBD>Markdown嚗?| <20><EFBFBD> | <20>𧢲㦤<F0A7A2B2>交𤣰<E4BAA4>𣂼<EFBFBD> |
| <20>?摰∟恣<E2889F><EFBFBD>霈啣<E99C88> | <20><EFBFBD> | <20>唳旿摨栞扇敶閙<E695B6><E99699>?|
| <20>?敺芰㴓<E88AB0><EFBFBD><E785BE>䔮憸?| 靽桀<E99DBD> | <20><EFBFBD><E88AB8><EFBFBD><EFBFBD>甈?|
| <20><EFBFBD><><E99AA1>敺桐縑<E6A190><EFBFBD><EFBFBD><E798A8> | <20><EFBFBD>霂?| URL撉諹<E69289><E8ABB9><EFBFBD>嚗𣬚鍂<F0A3AC9A><EFBFBD><E7919F><EFBFBD>瘚贝<E7989A> |
### 6.2 <20><EFBFBD>瘚贝<E7989A>
| <20><><EFBFBD> | <20><EFBFBD> | 摰鮋<E691B0> | <20><EFBFBD>?|
|------|------|------|------|
| Webhook<6F><EFBFBD><E6BB9A>園𡢿 | <10ms | 5.8ms | <20>?頞<>枂憸<E69E82><E686B8> |
| Worker<65><EFBFBD><E689AF>園𡢿 | <100ms | ~50ms | <20>?頞<>枂憸<E69E82><E686B8> |
| 蝡臬<E89DA1>蝡臬辣餈?| <5蝘?| <2蝘?| <20>?頞<>枂憸<E69E82><E686B8> |
| 瘨<><E798A8><EFBFBD><EFBFBD><E785BE><EFBFBD><EFBFBD><EFBFBD> | >99% | 100% | <20>?頞<>枂憸<E69E82><E686B8> |
---
## <20><><><E98A9D><EFBFBD><EFBFBD><EFBFBD><EFBFBD>?
### 7.1 <20><EFBFBD><E595A3><EFBFBD>
1. **`WECHAT_ENV_CONFIG.md`**嚗?01銵䕘<E98AB5>
-<><E99AA1>敺桐縑<E6A190><EFBFBD><E887AC><EFBFBD><E3979B>滨蔭<E6BBA8><E894AD><EFBFBD>
- IP<49><EFBFBD><E8B3A2><EFBFBD>蝵?
- natapp<70><70><EFBFBD>蝛輸<E89D9B><EFBFBD>蝵?
- URL撉諹<E69289>甇仿炊
- 撣貉<E692A3><E8B289><EFBFBD><E6A185>埝䰻
### 7.2 <20>湔鰵<E6B994><E9B0B5>
1. **`00-璅<E79285>敶枏<E695B6><E69E8F><EFBFBD><E59786><EFBFBD><EFBFBD><E69298><EFBFBD><E78390>?md`**
- <20>湔鰵撘<E9B0B5><E69298>𤏸<EFBFBD>摨佗<E691A8>Day 3摰峕<E691B0>嚗?
- <20>湔鰵隞<E9B0B5><E99A9E>蝏蠘恣
- <20>湔鰵瘚贝<E7989A>蝏𤘪<E89D8F>
2. **`MVP撘<50><E69298>睲遙<E79DB2><EFBFBD><E28AA5>?md`**
- <20><>扇Day 3隞餃𦛚銝箏歇摰峕<E691B0>
- <20>湔鰵隞餃𦛚<E9A483><EFBFBD>?
3. **`<60><>撠𦬅VP<56>剔㴓撘<E3B493><E69298>𤏸恣<F0A48FB8>?md`**
- <20>湔鰵撘<E9B0B5><E69298>𤏸<EFBFBD>摨?
- <20><><EFBFBD><EFBFBD><E8A9A8>剔㴓撌脫<E6928C><E884AB>?
---
## <20><> <20><EFBFBD><E68092><EFBFBD><EFBFBD>甇亥恣<E4BAA5>𡜐<EFBFBD>Day 4嚗?
### 8.1 隡睃<E99AA1>銝𤾸<E98A9D><F0A4BEB8>?
| 隞餃𦛚 | 隡睃<E99AA1>蝥?| 憸<><EFBFBD>園𡢿 |
|------|-------|---------|
| 摰<><E691B0><EFBFBD>躰秤憭<E7A7A4><E686AD> | <20><> 銝?| 2撠𤩺𧒄 |
| 隡睃<E99AA1><E79D83><EFBFBD><E4BA99><EFBFBD> | <20>椬 雿?| 1撠𤩺𧒄 |
| 瘛餃<E7989B><E9A483>烐綉<E78390><E7B689><EFBFBD> | <20>椬 雿?| 2撠𤩺𧒄 |
| <20><EFBFBD>隡睃<E99AA1> | <20>椬 雿?| 1撠𤩺𧒄 |
### 8.2 Phase 1.5嚗鋫I韐冽綉<EFBFBD><EFBFBD>
| 隞餃𦛚 | 隡睃<E99AA1>蝥?| 憸<><EFBFBD>園𡢿 |
|------|-------|---------|
| <20><><EFBFBD>Dify RAG | <20>𣞁 擃?| 4撠𤩺𧒄 |
| 銝𠹺<E98A9D><F0A0B9BA>𠉛弦<F0A0899B><EFBFBD> | <20>𣞁 擃?| 1撠𤩺𧒄 |
| <20><><EFBFBD>韐冽綉閫<E7B689><E996AB> | <20>𣞁 擃?| 2撠𤩺𧒄 |
| 瘚贝<E7989A>AI韐冽綉 | <20>𣞁 擃?| 1撠𤩺𧒄 |
---
## <20>?銋腈<E98A8B><E88588><EFBFBD><EFBFBD>
### 9.1 <20><EFBFBD><E8A9A8>𣂼停
1. <20>?**MVP<56>剔㴓<E58994><EFBFBD>?*嚗鑹EDCap <20>?Node.js <20>?隡<><E99AA1>敺桐縑摰峕㟲瘚<E39FB2><E7989A>
2. <20>?**隡<><E99AA1>敺桐縑<E6A190><E7B891><EFBFBD>**嚗𡁏綫<F0A1818F><E7B6AB><EFBFBD><EFBFBD>?+ <20><EFBFBD><EFBFBD><E686AD> + URL撉諹<E69289>
3. <20>?**韐冽綉Worker摰<72><E691B0>**嚗朞捶<E69C9E><EFBFBD><E689B3>?+ <20>𡁶䰻<F0A181B6><EFBFBD>?+ 摰∟恣<E2889F><EFBFBD>
4. <20>?**蝡臬<E89DA1>蝡舀<E89DA1>霂閖<E99C82><EFBFBD>**嚗𡁜<E59A97>瘚?2蝘鍦辣餈<E8BEA3><E9A488>100%<25>𣂼<EFBFBD><F0A382BC>?
5. <20>?**<2A><>﹝雿梶頂摰<E9A082><E691B0>**嚗𡁶㴓憓<E3B493><E68693>蝵?+ 撘<><E69298>𤏸扇敶?+ 餈𥕦漲頝蠘葵
### 9.2 <20>喲睸<E596B2>唳旿
- <20><> **<EFBFBD><EFBFBD><EFBFBD><EFBFBD>**嚗?,755銵䕘<E98AB5>擃䁅捶<E48185><EFBFBD>鈭找誨<E689BE><E8AAA8><EFBFBD>
- <20><EFBFBD> **撘<><E69298>烐𧒄<E78390>?*嚗?憭抬<E686AD>8撠𤩺𧒄嚗?
- <20>?**瘚贝<E7989A><E8B49D><EFBFBD><E69C9E>?*嚗?00%嚗?/9<><EFBFBD>瘚贝<E7989A>嚗?
- <20><> **<EFBFBD><EFBFBD>銵函緵**嚗𡁶垢<F0A181B6>啁垢<2蝘𡜐<E89D98><EFBFBD>枂憸<E69E82><E686B8>
- <20><> **<EFBFBD><EFBFBD>﹝摰<EFBFBD><EFBFBD>摨?*嚗?01銵屸<E98AB5>蝵格<E89DB5><E6A0BC>?+ 撘<><E69298>𤏸扇敶?
### 9.3 <20><><EFBFBD>臭漁<E887AD>?
1. **撘<>郊Worker<65><EFBFBD>**嚗𡁶泵<F0A181B6>㇊ostgres-Only<6C><79>雿唾<E99BBF>撘?
2. **隡<><E99AA1>敺桐縑瘨<E7B891><E798A8><EFBFBD>㰘圾撖?*嚗𡁜<E59A97><F0A1819C><EFBFBD><E6B8B8>啁倌<E59581><EFBFBD><EFBFBD><E99C82><EFBFBD>㰘圾撖?
3. **撘<><EFBFBD>𧼮<EFBFBD><E79285>**嚗䫤setImmediate` 蝖桐<E89D96>5蝘鍦<E89D98><E98DA6><EFBFBD>
4. **摰峕㟲<E5B395><E39FB2><EFBFBD>霂臬<E99C82><E887AC>?*嚗𡁜恣霈⊥𠯫敹堒仃韐乩<E99F90>敶勗<E695B6>銝餅<E98A9D>蝔?
5. **pg-boss<73><EFBFBD><E6BBA9><EFBFBD>**嚗朞䌊<E69C9E><EFBFBD>霂?甈∴<E79488>蝖桐<E89D96><E6A190><EFBFBD><E888AB>?
### 9.4 MVP隞瑕<E99A9E><EFBFBD>霂?
<EFBFBD>?**摰墧𧒄<E5A2A7>毺䰻**嚗䥪I<E4A5AA>𣳇<EFBFBD><F0A3B387><EFBFBD>REDCap嚗䔶<E59A97>銝𡁜凝靽<E288AA><EFBFBD>𡁶䰻
<EFBFBD>?**銝餃𢆡<E9A483>𡁶䰻**嚗𡁏㺭<F0A1818F><EFBFBD><E6A180><EFBFBD><2蝘埝綫<E59F9D><E7B6AB><EFBFBD><EFBFBD><EFBFBD>瞍?
<EFBFBD>?**<2A>𤘪<EFBFBD>撅?*嚗𡁻𡡒<F0A181BB><EFBFBD><E88880>𡁜<EFBFBD><EFBFBD>虾敹恍<E695B9><E6818D><EFBFBD>鼦I韐冽綉<E586BD><E7B689><EFBFBD><EFBFBD><E28AA5><EFBFBD>
<EFBFBD>?**<2A>煺漣撠梁貌**嚗帋誨<E5B88B><E8AAA8><EFBFBD><EFBFBD>嚗峕<E59A97><EFBFBD>蝔喳<E89D94><EFBFBD><EFBFBD>湔𦻖<E6B994>函蔡
---
**蝏湔擪<E6B994>?*嚗䥑IT Manager撘<72><E69298>穃𣪧<E7A983>?
**<EFBFBD><EFBFBD><EFBFBD>擧凒<EFBFBD>?*嚗?026-01-03
**<EFBFBD><EFBFBD><EFBFBD><EFBFBD>?*嚗尠<E59A97> 撌脣<E6928C><E884A3>?