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%)
22 KiB
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>?
<EFBFBD>㴓 銝<><E98A9D><EFBFBD><EFBFBD><EFBFBD>𤑳𤌍<F0A491B3><F0A48C8D><EFBFBD><EFBFBD>鞉<EFBFBD>
1.1 <20>詨<EFBFBD><E8A9A8>格<EFBFBD>
**<2A><>撠誯𡡒<E8AAAF>舫<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>
| <EFBFBD>蠘<EFBFBD>璅∪<EFBFBD> | <EFBFBD>嗆<EFBFBD>? | 霂湔<EFBFBD> |
|---|---|---|
| <EFBFBD>?隡<><E99AA1>敺桐縑<E6A190>券<EFBFBD><E588B8><EFBFBD><EFBFBD>? | 摰峕<EFBFBD> | WechatService.ts嚗?14銵䕘<E98AB5> |
| <EFBFBD>?隡<><E99AA1>敺桐縑<E6A190>噼<EFBFBD>憭<EFBFBD><E686AD> | 摰峕<EFBFBD> | WechatCallbackController.ts嚗?01銵䕘<E98AB5> |
| <EFBFBD>?韐冽綉Worker<65>餉<EFBFBD> | 摰峕<EFBFBD> | iit_quality_check Worker |
| <EFBFBD>?Worker瘜典<E7989C><E585B8>箏<EFBFBD> | 摰峕<EFBFBD> | initIitManager() <20>典鍳<E585B8>冽𧒄靚<F0A79284>鍂 |
| <EFBFBD>?蝡臬<E89DA1>蝡舀<E89DA1>霂? | <EFBFBD>朞<EFBFBD> | REDCap <20>?Node.js <20>?隡<><E99AA1>敺桐縑 |
| <EFBFBD>?<3F>臬<EFBFBD><E887AC>滨蔭<E6BBA8><E894AD>﹝ | 摰峕<EFBFBD> | WECHAT_ENV_CONFIG.md嚗?01銵䕘<E98AB5> |
<EFBFBD><EFBFBD> 鈭䎚<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>**嚗?
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>?*嚗?
- <EFBFBD>?Access Token蝻枏<E89DBB><E69E8F>箏<EFBFBD>嚗<EFBFBD><E59A97>摮条<E691AE>摮矋<E691AE>2撠𤩺𧒄<F0A4A9BA>㗇<EFBFBD><E39787><EFBFBD><EFBFBD>
- <EFBFBD>?隡<><E99AA1>敺桐縑API靚<49>鍂嚗Ǒ/cgi-bin/message/send`嚗?
- <EFBFBD>?摰峕㟲<E5B395><E39FB2><EFBFBD>霂臬<E99C82><E887AC><EFBFBD><EFBFBD><EFBFBD>亙<EFBFBD>霈啣<E99C88>
- <EFBFBD>?<3F>舀<EFBFBD>銝厩<E98A9D>瘨<EFBFBD><E798A8>蝐餃<E89D90>嚗ōext/markdown/textcard嚗?
**瘚贝<E7989A>撉諹<E69289>**嚗?
- <EFBFBD>?雿輻鍂隡<E98D82><E99AA1>敺桐縑摰䀹䲮撘<E4B2AE><E69298>穃極<E7A983>瑟<EFBFBD>霂𤏪<E99C82>
access_token+ 瘨<><E798A8>API嚗? - <EFBFBD>?<3F><>𧋦瘨<F0A78BA6><E798A8>瘚贝<E7989A><E8B49D>朞<EFBFBD>
- <EFBFBD>?Textcard<72>∠<EFBFBD>瘨<EFBFBD><E798A8>瘚贝<E7989A><E8B49D>朞<EFBFBD>
- <EFBFBD>?Markdown瘨<6E><E798A8>瘚贝<E7989A><E8B49D>朞<EFBFBD>
- <EFBFBD>?<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>**嚗?
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>?*嚗?
- <EFBFBD>?隡<><E99AA1>敺桐縑瘨<E7B891><E798A8><EFBFBD>㰘圾撖<E59CBE><E69296>
@wecom/crypto嚗? - <EFBFBD>?XML瘨<4C><E798A8>閫<EFBFBD><E996AB>嚗Ǒxml2js`嚗?
- <EFBFBD>?蝑曉<E89D91>撉諹<E69289>嚗𠄎HA1嚗?
- <EFBFBD>?撘<>郊<EFBFBD>𧼮<EFBFBD>璅∪<E79285>嚗<EFBFBD><E59A97><EFBFBD>唾<EFBFBD><E594BE>?success"嚗<><E59A97><EFBFBD>啣<EFBFBD><E595A3><EFBFBD><EFBFBD>
- <EFBFBD>?雿輻鍂
setImmediate蝖桐<E89D96>撘<EFBFBD>郊<EFBFBD>扯<EFBFBD> - <EFBFBD>?LLM<4C>誩㦛霂<E3A69B><E99C82>嚗㇄ify嚗? 憭鋫gent頝舐眏
**瘚贝<E7989A>撉諹<E69289>**嚗?
- <EFBFBD>?隡<><E99AA1>敺桐縑<E6A190>噼<EFBFBD>URL撉諹<E69289><E8ABB9>朞<EFBFBD>
- <EFBFBD>?natapp<70><70><EFBFBD>蝛輸<E89D9B>誯<EFBFBD>蝵格<E89DB5><E6A0BC><EFBFBD><EFBFBD>
http://iit.nat100.top嚗? - <EFBFBD>?瘨<><E798A8><EFBFBD>㰘圾撖<E59CBE><E69296>霂閖<E99C82>朞<EFBFBD>
- <EFBFBD>賂<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>**嚗?
// 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>
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>**嚗?
- <EFBFBD>?Worker<65>𣂼<EFBFBD>瘜典<E7989C><E585B8>郡g-boss
- <EFBFBD>?REDCap DET閫血<E996AB> <20>?隞餃𦛚<E9A483>券<EFBFBD>?<3F>?Worker<65>扯<EFBFBD>
- <EFBFBD>?韐冽綉璉<E7B689><E79289>仿<EFBFBD>餉<EFBFBD><E9A489>扯<EFBFBD>甇<EFBFBD>虜
- <EFBFBD>?隡<><E99AA1>敺桐縑<E6A190>𡁶䰻<F0A181B6>煾<EFBFBD><E785BE><EFBFBD><EFBFBD>?
- <EFBFBD>?摰∟恣<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`
// <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>**嚗?
- <EFBFBD>?<3F>臬𢆡<E887AC>亙<EFBFBD><E4BA99>曄內 "IIT Manager workers registered"
- <EFBFBD>?
iit_quality_checkWorker<65>𣂼<EFBFBD>憭<EFBFBD><E686AD>隞餃𦛚 - <EFBFBD>?
iit_redcap_pollWorker撌脫釣<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>**嚗?
// <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';
<EFBFBD>桅<EFBFBD>2嚗䫤action摮埈挾銝滚<EFBFBD><EFBFBD>剁<EFBFBD>摨𥪯蛹action_type`嚗?
**靽桀<E99DBD>**嚗?
// <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>**嚗?
- <EFBFBD>?Worker<65>扯<EFBFBD><E689AF>䭾㺭<E4ADBE>桀<EFBFBD><E6A180>躰秤
- <EFBFBD>?摰∟恣<E2889F>亙<EFBFBD>霈啣<E99C88><E595A3>𣂼<EFBFBD>
- <EFBFBD>?韐冽綉隞餃𦛚摰峕㟲瘚<E39FB2><E7989A><EFBFBD>朞<EFBFBD>
<EFBFBD>妒 銝剹<E98A9D><E589B9>垢<EFBFBD>啁垢瘚贝<E7989A>
3.1 瘚贝<E7989A><E8B49D>臬<EFBFBD>
| 蝏<EFBFBD>辣 | <EFBFBD>滨蔭 | <EFBFBD>嗆<EFBFBD>? |
|---|---|---|
| REDCap | Docker 15.8.0 + 瘚贝<E7989A>憿寧𤌍(PID 16) | <EFBFBD>?餈鞱<E9A488>銝? |
| Node.js Backend | Fastify + pg-boss + Prisma | <EFBFBD>?餈鞱<E9A488>銝? |
| PostgreSQL | Docker + iit_schema | <EFBFBD>?餈鞱<E9A488>銝? |
| 隡<EFBFBD><EFBFBD>敺桐縑 | <EFBFBD>芸遣摨𠉛鍂 + 瘚贝<E7989A><E8B49D>冽<EFBFBD>(FengZhiBo) | <EFBFBD>?撌脤<E6928C>蝵? |
| natapp | <EFBFBD><EFBFBD><EFBFBD>蝛輸<EFBFBD>?iit.nat100.top) | <EFBFBD>?撌脤<E6928C>蝵? |
3.2 瘚贝<E7989A>瘚<EFBFBD><E7989A>
瘚贝<EFBFBD>甇仿炊嚗?
- <EFBFBD>?REDCap<61>𥕦遣<F0A595A6>啗扇敶𤏪<E695B6>ID: 9嚗?
- <EFBFBD>?REDCap DET摰墧𧒄閫血<E996AB>Webhook嚗?蝘鍦辣餈<E8BEA3><E9A488>
- <EFBFBD>?Node.js WebhookController<65>交𤣰霂瑟<E99C82>嚗?10ms<6D>滚<EFBFBD>嚗?
- <EFBFBD>?<3F>券<EFBFBD><E588B8>遙<EFBFBD>∪<EFBFBD>
iit_quality_check<20>笔<EFBFBD> - <EFBFBD>?Worker<65>扯<EFBFBD>韐冽綉璉<E7B689><E79289>?
- <EFBFBD>?<3F>煾<EFBFBD><E785BE><EFBFBD>銝𡁜凝靽⊿<E99DBD>𡁶䰻
- <EFBFBD>?<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 }
隡<EFBFBD><EFBFBD>敺桐縑<EFBFBD>交𤣰<EFBFBD><EFBFBD>捆嚗?
<EFBFBD><EFBFBD> IIT Manager <20>唳旿敶訫<E695B6><E8A8AB>𡁶䰻
憿寧𤌍嚗魩est0102
霈啣<E99C88>ID嚗?
銵典<E98AB5>嚗飱emographics
<0A>園𡢿嚗?026-01-03 14:02:08
<0A>働 韐冽綉撱箄悅嚗?
1. <20>?<3F>唳旿敶訫<E695B6><E8A8AB>𦠜𧒄
2. <20>?霈啣<E99C88>ID<49>㗇<EFBFBD>
3. <20>?銵典<E98AB5>嚗飱emographics
<0A>?<3F>唳旿韐券<E99F90><E588B8>臬末
<0A>俥 憒<><E68692><EFBFBD>煾䔮嚗諹窈<E8ABB9>𧼮<EFBFBD>"撣桀𨭌"<22>亦<EFBFBD><E4BAA6>游<EFBFBD><E6B8B8>蠘<EFBFBD>
3.3 瘚贝<E7989A>蝏𤘪<E89D8F>
| 瘚贝<EFBFBD>憿? | <EFBFBD><EFBFBD><EFBFBD> | 摰鮋<EFBFBD> | <EFBFBD>嗆<EFBFBD>? |
|---|---|---|---|
| REDCap閫血<EFBFBD> | 靽嘥<EFBFBD><EFBFBD>𡒊<EFBFBD><EFBFBD>唾圻<EFBFBD>? | 0蝘鍦辣餈? | <EFBFBD>? |
| Webhook<EFBFBD>交𤣰 | <10ms<6D>滚<EFBFBD> | 5.8ms | <EFBFBD>? |
| 隞餃𦛚<EFBFBD>券<EFBFBD>? | <EFBFBD>𣂼<EFBFBD><EFBFBD>券<EFBFBD><EFBFBD><EFBFBD><EFBFBD>笔<EFBFBD> | <EFBFBD>𣂼<EFBFBD> | <EFBFBD>? |
| Worker<EFBFBD>扯<EFBFBD> | Worker憭<EFBFBD><EFBFBD>隞餃𦛚 | <EFBFBD>𣂼<EFBFBD><EFBFBD>扯<EFBFBD> | <EFBFBD>? |
| 韐冽綉璉<EFBFBD><EFBFBD>? | 餈𥪜<EFBFBD>韐冽綉蝏𤘪<EFBFBD> | 3<EFBFBD>∪遣霈? | <EFBFBD>? |
| 隡<EFBFBD><EFBFBD>敺桐縑<EFBFBD>券<EFBFBD>? | <EFBFBD>煾<EFBFBD><EFBFBD><EFBFBD>𡁶䰻<EFBFBD>𣂼<EFBFBD> | <EFBFBD>𣂼<EFBFBD> | <EFBFBD>? |
| <EFBFBD>𧢲㦤<EFBFBD>交𤣰 | <EFBFBD>交𤣰<EFBFBD>圈<EFBFBD>𡁶䰻 | <EFBFBD>𣂼<EFBFBD><EFBFBD>交𤣰 | <EFBFBD>? |
| 摰∟恣<EFBFBD>亙<EFBFBD> | 霈啣<EFBFBD><EFBFBD>唳㺭<EFBFBD>桀<EFBFBD> | <EFBFBD>𣂼<EFBFBD>霈啣<EFBFBD> | <EFBFBD>? |
| 敺芰㴓<EFBFBD>煾<EFBFBD>? | <EFBFBD>芸<EFBFBD><EFBFBD><EFBFBD><EFBFBD>甈? | <EFBFBD>芸<EFBFBD><EFBFBD><EFBFBD><EFBFBD>甈? | <EFBFBD>? |
**<2A>喲睸<E596B2><E79DB8><EFBFBD>**嚗?
- <EFBFBD>?蝡臬<E89DA1>蝡臬辣餈<E8BEA3><E9A488><2蝘𡜐<E89D98>REDCap靽嘥<E99DBD> <20>?隡<><E99AA1>敺桐縑<E6A190>交𤣰嚗?
- <EFBFBD>?Webhook<6F>滚<EFBFBD><E6BB9A>園𡢿嚗?.8ms
- <EFBFBD>?Worker<65>扯<EFBFBD><E689AF>園𡢿嚗鰺50ms
- <EFBFBD>?瘨<><E798A8><EFBFBD>煾<EFBFBD><E785BE><EFBFBD><EFBFBD>毺<EFBFBD>嚗?00%嚗<><E59A97>霂?甈∴<E79488><E288B4>券<EFBFBD><E588B8>𣂼<EFBFBD>嚗?
- <EFBFBD>?<3F>惩儐<E683A9>臬<EFBFBD><E887AC><EFBFBD>䔮憸?
<EFBFBD>圲 <20>䜘<EFBFBD><E49C98>葩<EFBFBD>嗆綳<E59786>賭<EFBFBD><E8B3AD><EFBFBD><EFBFBD>臬<EFBFBD>箏𦛚
4.1 銝湔𧒄<E6B994>芣鴌嚗㇈VP<56>嗆挾嚗?
| 摨誩噡 | 銝湔𧒄<EFBFBD>芣鴌 | <EFBFBD>笔<EFBFBD> | 霈∪<EFBFBD><EFBFBD>寡<EFBFBD><EFBFBD>園𡢿 | <EFBFBD>寡<EFBFBD><EFBFBD>寞<EFBFBD> |
|---|---|---|---|---|
| 1 | *UserID蝖祉<EFBFBD><EFBFBD>? | 蝞<EFBFBD><EFBFBD>𡝗<EFBFBD>霂閙<EFBFBD>蝔? | Phase 2 | 隞𡡞★<EFBFBD>桅<EFBFBD>蝵株”霂餃<EFBFBD> notification_config.wechat_user_id |
| 2 | 摰𡁏𧒄頧株砭蝳<EFBFBD>鍂 | MVP銝漤<EFBFBD>閬<EFBFBD><EFBFBD>Webhook撌脰雲憭? | Phase 2 | 摰䂿緵 jobQueue.schedule() <20>碶蝙<E7A2B6>?node-cron |
| 3 | *韐冽綉<EFBFBD>餉<EFBFBD>蝞<EFBFBD><EFBFBD>? | 隞<EFBFBD>抅蝖<EFBFBD>璉<EFBFBD><EFBFBD>伐<EFBFBD><EFBFBD>鼦I韐冽綉 | Phase 1.5 | <EFBFBD><EFBFBD><EFBFBD>Dify RAG + 閫<><E996AB>撘閙<E69298> |
| 4 | 摰∟恣<EFBFBD>亙<EFBFBD>摮埈挾 | notification_config 摮埈挾<E59F88>芸<EFBFBD>撱? |
Phase 2 | 瘛餃<EFBFBD>JSONB摮埈挾摮睃<EFBFBD>隡<EFBFBD><EFBFBD>敺桐縑<EFBFBD>滨蔭 |
| 5 | Access Token蝻枏<E89DBB> | <EFBFBD><EFBFBD><EFBFBD>蝻枏<EFBFBD>嚗屸<EFBFBD><EFBFBD>臭腺憭? | Phase 2 | 雿輻鍂Redis<EFBFBD>𡝗㺭<EFBFBD>桀<EFBFBD>蝻枏<EFBFBD> |
**霂衣<E99C82>霂湔<E99C82>**嚗?
1. UserID蝖祉<E89D96><E7A589><EFBFBD><EFBFBD><EFBFBD>臬<EFBFBD><E887AC>㗛<EFBFBD>嚗?
敶枏<EFBFBD>摰䂿緵嚗?
// <20>湔𦻖隞𡒊㴓憓<E3B493><E68693><EFBFBD>讛繮<E8AE9B>?
const piUserId = process.env.WECHAT_TEST_USER_ID || 'FengZhiBo';
**<2A>桅<EFBFBD>**嚗?
- <EFBFBD>?<3F>䭾<EFBFBD><E4ADBE>舀<EFBFBD>憭𡁻★<F0A181BB>柴<EFBFBD><E69FB4><EFBFBD>PI
- <EFBFBD>?<3F>煺漣<E785BA>臬<EFBFBD><E887AC><EFBFBD>閬<EFBFBD><E996AC>銝芷★<E88AB7>桅<EFBFBD>蝵桐<E89DB5><E6A190>𣬚<EFBFBD>UserID
**霈∪<E99C88><E288AA>寡<EFBFBD>**嚗㇊hase 2嚗㚁<E59A97>
// 隞𡡞★<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';
**<2A>唳旿摨廍chema<6D>寡<EFBFBD>**嚗?
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>鍂
敶枏<EFBFBD>摰䂿緵嚗?
// <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)');
**<2A>桅<EFBFBD>**嚗?
- <EFBFBD>𩤃<EFBFBD>
jobQueue.schedule()<20>寞<EFBFBD>銝滚<E98A9D><E6BB9A>剁<EFBFBD>PgBossQueue<20>芸<EFBFBD><E88AB8>堆<EFBFBD> - <EFBFBD>𩤃<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>
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>
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>?
敶枏<EFBFBD>摰䂿緵嚗<EFBFBD>抅蝖<EFBFBD>璉<EFBFBD><EFBFBD>伐<EFBFBD>嚗?
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>**嚗?
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>**嚗?
- <EFBFBD>?
projects銵函撩撠?notification_config摮埈挾 - <EFBFBD>?UserID<49><44>𧒄隞𡒊㴓憓<E3B493><E68693><EFBFBD>讛粉<E8AE9B>?
**霈∪<E99C88><E288AA>寡<EFBFBD>**嚗㇊hase 2嚗㚁<E59A97>
-- 瘛餃<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 '隡<><E99AA1>敺桐縑<E6A190>𡁶䰻<F0A181B6>滨蔭嚗㇎serID<49><44><EFBFBD><EFBFBD>沉D<E6B289><44><EFBFBD>𡁶䰻蝐餃<E89D90>蝑㚁<E89D91>';
-- <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>
敶枏<EFBFBD>摰䂿緵嚗<EFBFBD><EFBFBD>摮条<EFBFBD>摮矋<EFBFBD>嚗?
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;
}
}
**<2A>桅<EFBFBD>**嚗?
- <EFBFBD>𩤃<EFBFBD> <20>滚鍳<E6BB9A>滚𦛚<E6BB9A>𡒊<EFBFBD>摮䀝腺憭?
- <EFBFBD>𩤃<EFBFBD> 憭𡁜<E686AD>靘钅<E99D98>蝵脫𧒄<E884AB>䭾<EFBFBD><E4ADBE>曹澈蝻枏<E89DBB>
**霈∪<E99C88><E288AA>寡<EFBFBD>**嚗㇊hase 2嚗㚁<E59A97>
<EFBFBD>寞<EFBFBD>A嚗帋蝙<EFBFBD>沖edis
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>
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>
| 摨誩噡 | <EFBFBD><EFBFBD><EFBFBD>臬<EFBFBD>箏𦛚 | 敶勗<EFBFBD> | 隡睃<EFBFBD>蝥? | 霈∪<EFBFBD><EFBFBD>園𡢿 |
|---|---|---|---|---|
| 1 | *韐冽綉<EFBFBD>餉<EFBFBD>蝞<EFBFBD><EFBFBD>? | <EFBFBD>鼦I<EFBFBD>賢<EFBFBD>嚗䔶遠<EFBFBD>潭<EFBFBD><EFBFBD>? | <EFBFBD>𣞁 擃? | Phase 1.5 |
| 2 | *UserID蝖祉<EFBFBD><EFBFBD>? | <EFBFBD>䭾<EFBFBD>憭𡁻★<EFBFBD>桅<EFBFBD>蝵? | <EFBFBD><EFBFBD> 銝? | Phase 2 |
| 3 | notification_config摮埈挾蝻箏仃 | <EFBFBD>䭾<EFBFBD><EFBFBD>菜暑<EFBFBD>滨蔭<EFBFBD>𡁶䰻 | <EFBFBD><EFBFBD> 銝? | Phase 2 |
| 4 | *摰𡁏𧒄頧株砭<EFBFBD>芸<EFBFBD><EFBFBD>? | <EFBFBD>惩<EFBFBD>摨閙㦤<EFBFBD>? | <EFBFBD>椬 雿? | Phase 2 |
| 5 | Access Token<65><6E><EFBFBD>蝻枏<E89DBB> | <EFBFBD>滚鍳銝W仃 | <EFBFBD>椬 雿? | Phase 2 |
| 6 | *<EFBFBD>躰秤憭<EFBFBD><EFBFBD>銝滚<EFBFBD><EFBFBD>? | <EFBFBD>典<EFBFBD>撘<EFBFBD>虜<EFBFBD>芣<EFBFBD><EFBFBD>? | <EFBFBD><EFBFBD> 銝? | Phase 2 |
| 7 | <EFBFBD>亙<EFBFBD>蝥批<EFBFBD>瘛瑚僚 | info/debug/error瘛瑞鍂 | <EFBFBD>叚 <20><><EFBFBD> | Phase 3 |
4.3 憌𡡞埯銝𡒊<E98A9D>閫<EFBFBD>綳<EFBFBD>?
| 憌𡡞埯 | 敶勗<EFBFBD> | 璁<EFBFBD><EFBFBD> | 蝻栞圾<EFBFBD>芣鴌 | <EFBFBD>嗆<EFBFBD>? |
|---|---|---|---|---|
| Webhook憭梯揖撖潸稲<EFBFBD>唳旿銝W仃 | <EFBFBD>𣞁 擃? | <EFBFBD>椬 銝? | 摰𡁏𧒄頧株砭<EFBFBD>𨅯<EFBFBD> | <EFBFBD>賂<EFBFBD> <20><>𧊋摰䂿緵 |
| 隡<EFBFBD><EFBFBD>敺桐縑API<EFBFBD>鞉<EFBFBD> | <EFBFBD><EFBFBD> 銝? | <EFBFBD>叚 雿? | <EFBFBD>鞉<EFBFBD><EFBFBD>批<EFBFBD> + <20>滩<EFBFBD><E6BBA9>箏<EFBFBD> | <EFBFBD>賂<EFBFBD> 敺<><E695BA><EFBFBD>? |
| Access Token餈<6E><E9A488> | <EFBFBD><EFBFBD> 銝? | <EFBFBD>椬 銝? | <EFBFBD>芸𢆡<EFBFBD>瑟鰵<EFBFBD>箏<EFBFBD> | <EFBFBD>?撌脣<E6928C><E884A3>? |
| <EFBFBD>唳旿摨栞<EFBFBD><EFBFBD>亙仃韐? | <EFBFBD>𣞁 擃? | <EFBFBD>叚 雿? | 餈墧𦻖瘙?+ <20>滩<EFBFBD> | <EFBFBD>?撌脣<E6928C><E884A3>? |
| Worker<EFBFBD>扯<EFBFBD>憭梯揖 | <EFBFBD><EFBFBD> 銝? | <EFBFBD>椬 銝? | pg-boss<73>芸𢆡<E88AB8>滩<EFBFBD> | <EFBFBD>?撌脣<E6928C><E884A3>? |
<EFBFBD><EFBFBD> 鈭𢛵<E988AD><F0A29BB5>誨<EFBFBD><E8AAA8><EFBFBD>霈?
5.1 <20>詨<EFBFBD>隞<EFBFBD><E99A9E><EFBFBD>?
| 璅∪<EFBFBD> | <EFBFBD><EFBFBD>辣 | 銵峕㺭 | 霂湔<EFBFBD> |
|---|---|---|---|
| 隡<EFBFBD><EFBFBD>敺桐縑<EFBFBD>券<EFBFBD>? | WechatService.ts |
314 | Access Token + 瘨<><E798A8><EFBFBD>券<EFBFBD>? |
| 隡<EFBFBD><EFBFBD>敺桐縑<EFBFBD>噼<EFBFBD> | WechatCallbackController.ts |
501 | URL撉諹<EFBFBD> + 瘨<><E798A8><EFBFBD>交𤣰 |
| 韐冽綉Worker | index.ts |
336 | Worker瘜典<EFBFBD> + 韐冽綉<E586BD>餉<EFBFBD> |
| 頝舐眏<EFBFBD>滨蔭 | routes/index.ts |
203 | 隡<EFBFBD><EFBFBD>敺桐縑頝舐眏 |
| <EFBFBD>臬<EFBFBD><EFBFBD>滨蔭<EFBFBD><EFBFBD>﹝ | WECHAT_ENV_CONFIG.md |
401 | 隡<EFBFBD><EFBFBD>敺桐縑<EFBFBD>滨蔭<EFBFBD><EFBFBD><EFBFBD> |
| <EFBFBD>餉恣 | - | 1,755 | Day 3<>啣<EFBFBD>隞<EFBFBD><E99A9E> |
5.2 蝝航恣隞<E681A3><E99A9E><EFBFBD>𧶏<EFBFBD>Day 1-3嚗?
| <EFBFBD>嗆挾 | 隞<EFBFBD><EFBFBD><EFBFBD>? | 霂湔<EFBFBD> |
|---|---|---|
| Day 1 | 223銵? | <EFBFBD>唳旿摨廍chema + 璅∪<E79285>撉冽沲 |
| Day 2 | 2,200銵? | REDCap<EFBFBD><EFBFBD><EFBFBD> + Worker瘜典<E7989C> |
| Day 3 | 1,755銵? | 隡<EFBFBD><EFBFBD>敺桐縑<EFBFBD><EFBFBD><EFBFBD> + 蝡臬<E89DA1>蝡舀<E89DA1>霂? |
| <EFBFBD>餉恣 | *4,178銵? | MVP<EFBFBD>詨<EFBFBD>隞<EFBFBD><EFBFBD> |
<EFBFBD>㴓 <20>准<EFBFBD><E58786><EFBFBD>霂閗<E99C82><E99697>?
6.1 <20>蠘<EFBFBD>瘚贝<E7989A>
| 瘚贝<EFBFBD><EFBFBD>箸艶 | <EFBFBD>嗆<EFBFBD>? | 霂湔<EFBFBD> |
|---|---|---|
| <EFBFBD>?REDCap DET閫血<E996AB> | <EFBFBD>朞<EFBFBD> | 0蝘鍦辣餈? |
| <EFBFBD>?Webhook<6F>交𤣰 | <EFBFBD>朞<EFBFBD> | <10ms<6D>滚<EFBFBD> |
| <EFBFBD>?隞餃𦛚<E9A483>券<EFBFBD>? | <EFBFBD>朞<EFBFBD> | <EFBFBD>券<EFBFBD><EFBFBD><EFBFBD>pg-boss<73>笔<EFBFBD> |
| <EFBFBD>?Worker<65>扯<EFBFBD> | <EFBFBD>朞<EFBFBD> | 韐冽綉<EFBFBD>餉<EFBFBD><EFBFBD>扯<EFBFBD> |
| <EFBFBD>?隡<><E99AA1>敺桐縑<E6A190>券<EFBFBD><E588B8><EFBFBD><EFBFBD><EFBFBD>𧋦嚗? | <EFBFBD>朞<EFBFBD> | <EFBFBD>𧢲㦤<EFBFBD>交𤣰<EFBFBD>𣂼<EFBFBD> |
| <EFBFBD>?隡<><E99AA1>敺桐縑<E6A190>券<EFBFBD><E588B8><EFBFBD><EFBFBD>∠<EFBFBD>嚗? | <EFBFBD>朞<EFBFBD> | <EFBFBD>𧢲㦤<EFBFBD>交𤣰<EFBFBD>𣂼<EFBFBD> |
| <EFBFBD>?隡<><E99AA1>敺桐縑<E6A190>券<EFBFBD><E588B8><EFBFBD>Markdown嚗? | <EFBFBD>朞<EFBFBD> | <EFBFBD>𧢲㦤<EFBFBD>交𤣰<EFBFBD>𣂼<EFBFBD> |
| <EFBFBD>?摰∟恣<E2889F>亙<EFBFBD>霈啣<E99C88> | <EFBFBD>朞<EFBFBD> | <EFBFBD>唳旿摨栞扇敶閙<EFBFBD><EFBFBD>? |
| <EFBFBD>?敺芰㴓<E88AB0>煾<EFBFBD><E785BE>䔮憸? | 靽桀<EFBFBD> | <EFBFBD>芸<EFBFBD><EFBFBD><EFBFBD><EFBFBD>甈? |
| <EFBFBD>賂<EFBFBD> 隡<><E99AA1>敺桐縑<E6A190>噼<EFBFBD>瘨<EFBFBD><E798A8> | <EFBFBD>芣<EFBFBD>霂? | URL撉諹<EFBFBD><EFBFBD>朞<EFBFBD>嚗𣬚鍂<EFBFBD>瑟<EFBFBD><EFBFBD>臬<EFBFBD>瘚贝<EFBFBD> |
6.2 <20>扯<EFBFBD>瘚贝<E7989A>
| <EFBFBD><EFBFBD><EFBFBD> | <EFBFBD>格<EFBFBD> | 摰鮋<EFBFBD> | <EFBFBD>嗆<EFBFBD>? |
|---|---|---|---|
| Webhook<EFBFBD>滚<EFBFBD><EFBFBD>園𡢿 | <10ms | 5.8ms | <EFBFBD>?頞<>枂憸<E69E82><E686B8> |
| Worker<EFBFBD>扯<EFBFBD><EFBFBD>園𡢿 | <100ms | ~50ms | <EFBFBD>?頞<>枂憸<E69E82><E686B8> |
| 蝡臬<EFBFBD>蝡臬辣餈? | <5蝘? | <2蝘? | <EFBFBD>?頞<>枂憸<E69E82><E686B8> |
| 瘨<EFBFBD><EFBFBD><EFBFBD>煾<EFBFBD><EFBFBD><EFBFBD><EFBFBD>毺<EFBFBD> | >99% | 100% | <EFBFBD>?頞<>枂憸<E69E82><E686B8> |
<EFBFBD><EFBFBD> 銝<><E98A9D><EFBFBD><EFBFBD>獢<EFBFBD>凒<EFBFBD>?
7.1 <20>啣<EFBFBD><E595A3><EFBFBD>﹝
- **
WECHAT_ENV_CONFIG.md**嚗?01銵䕘<E98AB5>- 隡<EFBFBD><EFBFBD>敺桐縑<EFBFBD>臬<EFBFBD><EFBFBD>㗛<EFBFBD><EFBFBD>滨蔭<EFBFBD><EFBFBD><EFBFBD>
- IP<EFBFBD>賢<EFBFBD><EFBFBD>閖<EFBFBD>蝵?
- natapp<EFBFBD><EFBFBD><EFBFBD>蝛輸<EFBFBD>誯<EFBFBD>蝵?
- URL撉諹<EFBFBD>甇仿炊
- 撣貉<EFBFBD><EFBFBD>桅<EFBFBD><EFBFBD>埝䰻
7.2 <20>湔鰵<E6B994><E9B0B5>﹝
-
00-璅∪<E79285>敶枏<E695B6><E69E8F>嗆<EFBFBD><E59786><EFBFBD>撘<EFBFBD><E69298>烐<EFBFBD><E78390>?md- <EFBFBD>湔鰵撘<EFBFBD><EFBFBD>𤏸<EFBFBD>摨佗<EFBFBD>Day 3摰峕<E691B0>嚗?
- <EFBFBD>湔鰵隞<EFBFBD><EFBFBD>蝏蠘恣
- <EFBFBD>湔鰵瘚贝<EFBFBD>蝏𤘪<EFBFBD>
-
MVP撘<EFBFBD><EFBFBD>睲遙<EFBFBD>⊥<EFBFBD><EFBFBD>?md- <EFBFBD><EFBFBD>扇Day 3隞餃𦛚銝箏歇摰峕<E691B0>
- <EFBFBD>湔鰵隞餃𦛚<EFBFBD>嗆<EFBFBD>?
-
<EFBFBD><EFBFBD>撠𦬅VP<EFBFBD>剔㴓撘<EFBFBD><EFBFBD>𤏸恣<EFBFBD>?md- <EFBFBD>湔鰵撘<EFBFBD><EFBFBD>𤏸<EFBFBD>摨?
- <EFBFBD><EFBFBD>扇<EFBFBD>詨<EFBFBD><EFBFBD>剔㴓撌脫<EFBFBD><EFBFBD>?
<EFBFBD><EFBFBD> <20>怒<EFBFBD><E68092><EFBFBD>銝<EFBFBD>甇亥恣<E4BAA5>𡜐<EFBFBD>Day 4嚗?
8.1 隡睃<E99AA1>銝𤾸<E98A9D><F0A4BEB8>?
| 隞餃𦛚 | 隡睃<EFBFBD>蝥? | 憸<EFBFBD>摯<EFBFBD>園𡢿 |
|---|---|---|
| 摰<EFBFBD><EFBFBD><EFBFBD>躰秤憭<EFBFBD><EFBFBD> | <EFBFBD><EFBFBD> 銝? | 2撠𤩺𧒄 |
| 隡睃<EFBFBD><EFBFBD>亙<EFBFBD><EFBFBD>澆<EFBFBD> | <EFBFBD>椬 雿? | 1撠𤩺𧒄 |
| 瘛餃<EFBFBD><EFBFBD>烐綉<EFBFBD><EFBFBD><EFBFBD> | <EFBFBD>椬 雿? | 2撠𤩺𧒄 |
| <EFBFBD>扯<EFBFBD>隡睃<EFBFBD> | <EFBFBD>椬 雿? | 1撠𤩺𧒄 |
8.2 Phase 1.5嚗鋫I韐冽綉<EFBFBD>賢<EFBFBD>
| 隞餃𦛚 | 隡睃<EFBFBD>蝥? | 憸<EFBFBD>摯<EFBFBD>園𡢿 |
|---|---|---|
| <EFBFBD><EFBFBD><EFBFBD>Dify RAG | <EFBFBD>𣞁 擃? | 4撠𤩺𧒄 |
| 銝𠹺<EFBFBD><EFBFBD>𠉛弦<EFBFBD>寞<EFBFBD> | <EFBFBD>𣞁 擃? | 1撠𤩺𧒄 |
| <EFBFBD><EFBFBD><EFBFBD>韐冽綉閫<EFBFBD><EFBFBD> | <EFBFBD>𣞁 擃? | 2撠𤩺𧒄 |
| 瘚贝<EFBFBD>AI韐冽綉 | <EFBFBD>𣞁 擃? | 1撠𤩺𧒄 |
<EFBFBD>?銋腈<E98A8B><E88588><EFBFBD>餌<EFBFBD>
9.1 <20>詨<EFBFBD><E8A9A8>𣂼停
- <EFBFBD>?**MVP<56>剔㴓<E58994>㯄<EFBFBD>?*嚗鑹EDCap <20>?Node.js <20>?隡<><E99AA1>敺桐縑摰峕㟲瘚<E39FB2><E7989A>
- <EFBFBD>?**隡<><E99AA1>敺桐縑<E6A190><E7B891><EFBFBD>**嚗𡁏綫<F0A1818F><E7B6AB><EFBFBD><EFBFBD>?+ <20>噼<EFBFBD>憭<EFBFBD><E686AD> + URL撉諹<E69289>
- <EFBFBD>?**韐冽綉Worker摰<72><E691B0>**嚗朞捶<E69C9E>扳<EFBFBD><E689B3>?+ <20>𡁶䰻<F0A181B6>券<EFBFBD>?+ 摰∟恣<E2889F>亙<EFBFBD>
- <EFBFBD>?**蝡臬<E89DA1>蝡舀<E89DA1>霂閖<E99C82>朞<EFBFBD>**嚗𡁜<E59A97>瘚?2蝘鍦辣餈<E8BEA3><E9A488>100%<25>𣂼<EFBFBD><F0A382BC>?
- <EFBFBD>?**<2A><>﹝雿梶頂摰<E9A082><E691B0>**嚗𡁶㴓憓<E3B493><E68693>蝵?+ 撘<><E69298>𤏸扇敶?+ 餈𥕦漲頝蠘葵
9.2 <20>喲睸<E596B2>唳旿
- <EFBFBD><EFBFBD> **<2A>啣<EFBFBD>隞<EFBFBD><E99A9E>**嚗?,755銵䕘<E98AB5>擃䁅捶<E48185>讐<EFBFBD>鈭找誨<E689BE><E8AAA8><EFBFBD>
- <EFBFBD>梧<EFBFBD> **撘<><E69298>烐𧒄<E78390>?*嚗?憭抬<E686AD>8撠𤩺𧒄嚗?
- <EFBFBD>?**瘚贝<E7989A><E8B49D>朞<EFBFBD><E69C9E>?*嚗?00%嚗?/9<>蠘<EFBFBD>瘚贝<E7989A>嚗?
- <EFBFBD><EFBFBD> <EFBFBD>扯<EFBFBD>銵函緵嚗𡁶垢<EFBFBD>啁垢<2蝘𡜐<E89D98>頞<EFBFBD>枂憸<E69E82><E686B8>
- <EFBFBD><EFBFBD> **<2A><>﹝摰<EFB99D><E691B0>摨?*嚗?01銵屸<E98AB5>蝵格<E89DB5><E6A0BC>?+ 撘<><E69298>𤏸扇敶?
9.3 <20><><EFBFBD>臭漁<E887AD>?
- **撘<>郊Worker<65>嗆<EFBFBD>**嚗𡁶泵<F0A181B6>㇊ostgres-Only<6C><79>雿唾<E99BBF>撘?
- **隡<><E99AA1>敺桐縑瘨<E7B891><E798A8><EFBFBD>㰘圾撖?*嚗𡁜<E59A97><F0A1819C>游<EFBFBD><E6B8B8>啁倌<E59581>漤<EFBFBD>霂<EFBFBD><E99C82><EFBFBD>㰘圾撖?
- **撘<>郊<EFBFBD>𧼮<EFBFBD>璅∪<E79285>**嚗䫤setImmediate` 蝖桐<E89D96>5蝘鍦<E89D98><E98DA6>滚<EFBFBD>
- **摰峕㟲<E5B395><E39FB2><EFBFBD>霂臬<E99C82><E887AC>?*嚗𡁜恣霈⊥𠯫敹堒仃韐乩<E99F90>敶勗<E695B6>銝餅<E98A9D>蝔?
- **pg-boss<73>滩<EFBFBD><E6BBA9>箏<EFBFBD>**嚗朞䌊<E69C9E>券<EFBFBD>霂?甈∴<E79488>蝖桐<E89D96><E6A190>舫<EFBFBD><E888AB>?
9.4 MVP隞瑕<E99A9E>潮<EFBFBD>霂?
<EFBFBD>?摰墧𧒄<EFBFBD>毺䰻嚗䥪I<EFBFBD>𣳇<EFBFBD><EFBFBD>餃<EFBFBD>REDCap嚗䔶<EFBFBD>銝𡁜凝靽∪朖<EFBFBD>園<EFBFBD>𡁶䰻
<0A>?銝餃𢆡<EFBFBD>𡁶䰻嚗𡁏㺭<EFBFBD>桀<EFBFBD><EFBFBD>亙<EFBFBD><2蝘埝綫<E59F9D><E7B6AB><EFBFBD><EFBFBD>園<EFBFBD>瞍?
<0A>?<EFBFBD>𤘪<EFBFBD>撅?*嚗𡁻𡡒<F0A181BB>舀<EFBFBD><E88880>𡁜<EFBFBD>嚗<EFBFBD>虾敹恍<E695B9><E6818D>溶<EFBFBD>鼦I韐冽綉<E586BD><E7B689>遙<EFBFBD>⊥<EFBFBD><E28AA5>垍<EFBFBD>
<0A>?<EFBFBD>煺漣撠梁貌**嚗帋誨<E5B88B><E8AAA8>捶<EFBFBD>誯<EFBFBD>嚗峕<E59A97>扯<EFBFBD>蝔喳<E89D94>嚗<EFBFBD>虾<EFBFBD>湔𦻖<E6B994>函蔡
**蝏湔擪<E6B994>?*嚗䥑IT Manager撘<72><E69298>穃𣪧<E7A983>?
**<2A><><EFBFBD>擧凒<E693A7>?*嚗?026-01-03
**<2A><>﹝<EFBFBD>嗆<EFBFBD>?*嚗尠<E59A97> 撌脣<E6928C><E884A3>?