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%)
807 lines
22 KiB
Markdown
807 lines
22 KiB
Markdown
# 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>䰻霂V<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>嗉蔭霂g<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>嗉蔭霂V<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>𥕦遣蝝W<E89D9D>嚗<EFBFBD><E59A97><EFBFBD><EFBFBD>䰻霂g<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>滚鍳銝W仃 | <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>唳旿銝W仃 | <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>?
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|