Files
AIclinicalresearch/docs/02-通用能力层/Postgres-Only异步任务处理指南.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

621 lines
16 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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.
# Postgres-Only 蠑よュ・莉サ蜉。螟<EFBDA1>炊謖<E7828A>
> **譁<>。」迚域悽<E59F9F>?* v1.0
> **蛻帛サコ譌・譛滂シ?* 2025-12-22
> **扈エ謚、閠<EFBDA4><EFBFBD>** 蟷ウ蜿ー譫カ譫<EFBDB6>屬髦<E5B1AC>
> **騾ら畑蝨コ譎ッ<E8AD8E>?* 髟ソ譌カ髣エ莉サ蜉。<E89C89><EFBDA1>>30遘抵シ峨€∝、ァ譁<EFBDA7>サカ螟<EFBDB6>炊縲∝錘蜿ーWorker
> **蜿り€<E3828A>ョ樒鴫<E6A892><E9B4AB>** DC Tool C Excel隗」譫舌€、SL譁<4C>鍵遲幃€€C Tool B謨ー謐ョ謠仙叙
---
## <20>搭 讎りソー
譛ャ譁<EFBFBD>。」蝓コ莠?**DC Tool C Excel隗」譫仙粥閭ス** 逧<>ョ梧紛螳櫁キオ<EFBDB7>€サ扈<EFBDBB> Postgres-Only 譫カ譫<EFBDB6>ク句シよュ・莉サ蜉。螟<EFBDA1>炊逧<E7828A><E980A7><EFBFBD>㊥讓。蠑上€?
### 譬ク蠢<EFBDB8>サキ蛟?
1. 笨?**驕ソ蜈工TTP雜<50>慮**<2A>壻ク贋シ<E8B48B>謗・蜿?遘定ソ泌屓<E6B38C>瑚ァ」譫仙惠蜷主床螳梧<E89EB3><E6A2A7><EFBFBD>30-60遘抵シ<E68AB5>
2. 笨?**逕ィ謌キ菴馴ェ御シ倡ァ€**<2A>壼ョ樊慮霑帛コヲ蜿埼ヲ茨シ御ク埼怙隕∝そ遲?
3. 笨?**隨ヲ蜷井コ大次逕溯ァ<E6BAAF><EFBDA7>?*<2A>啀latform-Only讓。蠑擾シ継g-boss髦溷<E9ABA6>
4. 笨?**諤ァ閭ス莨伜喧**<2A>喞lean data郛灘ュ假シ碁∩蜈埼㍾螟崎ョ。邂暦シ<E69AA6>-99%閠玲慮<E78EB2>?
---
## <20><EFBFBD>?譫カ譫<EFBDB6>ョセ隶。
### 荳牙アよ楔譫<E6A594>
```
笏娯楳笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏?
笏? 蜑咲ォッ螻ゑシ<E38291>eact + React Query<72>? 笏?
笏? - 荳贋シ<E8B48B><EFBFBD>サカ<EFBDBB>育ォ句叉霑泌<E99C91>?sessionId + jobId<49>? 笏?
笏? - 霓ョ隸「迥カ諤<EFBDB6><EFBFBD>seQuery + refetchInterval<61><EFBFBD>蜉ィ荳イ陦鯉シ<E9AF89> 笏?
笏? - 逶大成 status='ready'<27>悟刈霓ス謨ー謐? 笏?
笏披楳笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏?
竊?HTTP
笏娯楳笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏?
笏? 蜷守ォッ螻ゑシ<E38291>astify + Prisma<6D>? 笏?
笏? - 蠢ォ騾滉ク贋シ<E8B48B>蛻ー OSS<53>?-3遘抵シ<E68AB5> 笏?
笏? - 蛻帛サコ Session<6F>育憾諤<E686BE>シ嗔rocessing<6E>? 笏?
笏? - 謗ィ騾∽ササ蜉。蛻ー pg-boss<73>育ォ句叉霑泌屓<E6B38C><E5B193> 笏?
笏? - 謠蝉セ帷憾諤∵衍隸?API 笏?
笏披楳笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏?
竊?pg-boss
笏娯楳笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏?
笏? Worker螻ゑシ<E38291>g-boss + Platform螻ゑシ<E38291> 笏?
笏? - 莉朱弌蛻怜叙莉サ蜉。<E89C89><EFBFBD>蜉ィ荳イ陦鯉シ<E9AF89> 笏?
笏? - 謇ァ陦瑚€玲慮謫堺ス懶シ郁ァ」譫舌€∵ク<E288B5>エ励€∫サ溯ョ。<EFBDAE><EFBDA1> 笏?
笏? - 菫晏ュ倡サ捺棡<E68DBA><E6A3A1>lean data 蛻?OSS<53>? 笏?
笏? - 譖エ譁ー Session<6F>亥。ォ蜈<EFBDAB><E89C88>謨ー謐ョ<E8AC90>? 笏?
笏披楳笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏?
```
---
## <20>噫 螳梧紛螳樊命豁・鬪、
### 豁・鬪、1<EFBDA4>壽焚謐ョ蠎鉄chema隶セ隶。
```prisma
// 荳壼苅陦ィ蜿ェ蟄倅ク壼苅菫。諱ッ<E8ABB1>御ク榊ュ倅ササ蜉。邂。逅<EFBDA1>ソ。諱ッ
model YourBusinessTable {
id String @id
userId String
fileKey String // OSS蜴溷ァ区枚莉カ
// 笨?諤ァ閭ス莨伜喧<E4BC9C>壻ソ晏ュ伜、<E4BC9C>炊扈捺<E68988>?
cleanDataKey String? // 貂<><EFBFBD>/螟<>炊蜷守噪謨ー謐ョ<E8AC90>磯∩蜈埼㍾螟崎ョ。邂暦シ<E69AA6>
// 謨ー謐ョ蜈<EFBDAE>ソ。諱ッ<E8ABB1>亥シよュ・蝪ォ蜈<EFBDAB>シ?
totalRows Int?
totalCols Int?
columns Json?
// 譌カ髣エ謌?
createdAt DateTime
updatedAt DateTime
expiresAt DateTime
@@schema("your_schema")
}
```
**蜈ウ髞ョ轤?*<2A>?
- 笶?荳崎ヲ∵キサ蜉<EFBDBB> `status`progress`縲errorMessage` 遲我ササ蜉。邂。逅<EFBDA1>ュ玲ョ?
- 笨?霑吩コ帛ュ玲ョオ逕?pg-boss 逧?`job` 陦ィ邂。逅?
---
### 豁・鬪、2<EFBDA4>售ervice螻?- 蠢ォ騾滉ク贋シ?謗ィ騾∽ササ蜉?
```typescript
// backend/src/modules/your-module/services/YourService.ts
import { storage } from '@/common/storage';
import { jobQueue } from '@/common/jobs';
import { prisma } from '@/config/database';
export class YourService {
/**
* 蛻帛サコ莉サ蜉。蟷カ謗ィ騾∝芦髦溷<E9ABA6><E6BAB7><EFBFBD>ostgres-Only譫カ譫<EFBDB6>シ?
*
* 笨?Platform-Only 讓。蠑擾シ?
* - 遶句叉荳贋シ<E8B48B><EFBFBD>サカ蛻?OSS
* - 蛻帛サコ荳壼苅隶ー蠖包シ亥<EFBDBC>謨ー謐ョ荳コnull<6C>?
* - 謗ィ騾∽ササ蜉。蛻ー髦溷<E9ABA6>
* - 遶句叉霑泌屓<E6B38C>井ク埼仆蝪櫁ッキ豎ゑシ?
*/
async createTask(userId: string, fileName: string, fileBuffer: Buffer) {
// 1. 鬪瑚ッ∵枚莉カ
if (fileBuffer.length > MAX_FILE_SIZE) {
throw new Error('譁<>サカ螟ェ螟ァ');
}
// 2. 笞?遶句叉荳贋シ<E8B48B>蛻?OSS<53>?-3遘抵シ<E68AB5>
const fileKey = `path/${userId}/${Date.now()}-${fileName}`;
await storage.upload(fileKey, fileBuffer);
// 3. 笞?蛻帛サコ荳壼苅隶ー蠖包シ亥<EFBDBC>謨ー謐ョ荳コnull<6C>檎ュ姥orker蝪ォ蜈<EFBDAB>シ?
const record = await prisma.yourTable.create({
data: {
userId,
fileName,
fileKey,
// 笞<><E7AC9E><EFBFBD><>炊扈捺棡蟄玲ョオ荳?null
totalRows: null,
columns: null,
expiresAt: new Date(Date.now() + 10 * 60 * 1000),
},
});
// 4. 笞?謗ィ騾∽ササ蜉。蛻ー pg-boss<73><73>latform-Only<6C>?
const job = await jobQueue.push('your_module_process', {
recordId: record.id,
fileKey,
userId,
});
// 5. 笞?遶句叉霑泌屓<E6B38C>€サ閠玲慮<3遘抵シ<E68AB5>
return {
...record,
jobId: job.id, // 笨?霑泌屓 jobId 萓帛燕遶ッ霓ョ隸?
};
}
}
```
---
### 豁・鬪、3<EFBDA4>啗orker螻?- 蜷主床螟<E5BA8A>
```typescript
// backend/src/modules/your-module/workers/yourWorker.ts
import { jobQueue } from '@/common/jobs';
import { storage } from '@/common/storage';
import { prisma } from '@/config/database';
import { logger } from '@/common/logging';
interface YourJob {
recordId: string;
fileKey: string;
userId: string;
}
/**
* 豕ィ蜀<EFBDA8> Worker 蛻ー髦溷<E9ABA6>?
*/
export function registerYourWorker() {
logger.info('[YourWorker] Registering worker');
// 笞<><E7AC9E><EFBFBD> 髦溷<E9ABA6>蜷咲ァー<EFBDA7>壼宵閭ス逕ィ蟄玲ッ阪€∵焚蟄励€∽ク句<EFBDB8>郤ソ縲∬ソ槫ュ礼ャヲ
jobQueue.process<YourJob>('your_module_process', async (job) => {
const { recordId, fileKey } = job.data;
logger.info('[YourWorker] Processing job', { jobId: job.id, recordId });
try {
// 1. 莉?OSS 荳玖スス譁<EFBDBD>サカ
const buffer = await storage.download(fileKey);
// 2. 謇ァ陦瑚€玲慮謫堺ス懶シ郁ァ」譫舌€∝、<E2889D>炊縲∬ョ。邂暦シ<E69AA6>
const result = await yourLongTimeProcess(buffer);
const { processedData, totalRows, columns } = result;
// 3. 笨?菫晏ュ伜、<E4BC9C>炊扈捺棡蛻?OSS<53>磯∩蜈埼㍾螟崎ョ。邂暦シ<E69AA6>
const cleanDataKey = `${fileKey}_clean.json`;
const cleanDataBuffer = Buffer.from(JSON.stringify(processedData), 'utf-8');
await storage.upload(cleanDataKey, cleanDataBuffer);
logger.info('[YourWorker] Clean data saved', {
size: `${(cleanDataBuffer.length / 1024).toFixed(2)} KB`
});
// 4. 譖エ譁ー荳壼苅隶ー蠖包シ亥。ォ蜈<EFBDAB><E89C88>謨ー謐ョ<E8AC90>?
await prisma.yourTable.update({
where: { id: recordId },
data: {
cleanDataKey, // 笨?菫晏ュ<E6998F> clean data 菴咲スョ
totalRows,
columns,
updatedAt: new Date(),
},
});
logger.info('[YourWorker] 笨?Job completed', { jobId: job.id });
return { success: true, recordId, totalRows };
} catch (error: any) {
logger.error('[YourWorker] 笶?Job failed', {
jobId: job.id,
error: error.message
});
throw error; // 隶?pg-boss 螟<>炊驥崎ッ<E5B48E>
}
});
logger.info('[YourWorker] 笨?Worker registered: your_module_process');
}
```
---
### 豁・鬪、4<EFBDA4>咾ontroller螻?- 迥カ諤∵衍隸「API
```typescript
// backend/src/modules/your-module/controllers/YourController.ts
import { jobQueue } from '@/common/jobs';
export class YourController {
/**
* 闔キ蜿紋ササ蜉。迥カ諤<EFBDB6><EFBFBD>latform-Only讓。蠑擾シ?
*
* GET /api/v1/your-module/tasks/:id/status
* Query: jobId (蜿ッ騾?
*/
async getTaskStatus(request, reply) {
const { id: recordId } = request.params;
const { jobId } = request.query;
// 1. 譟・隸「荳壼苅隶ー蠖<EFBDB0>
const record = await prisma.yourTable.findUnique({
where: { id: recordId }
});
if (!record) {
return reply.code(404).send({ success: false, error: '隶ー蠖穂ク榊ュ伜<EFBDAD>? });
}
// 2. 蛻、譁ュ迥カ諤?
// - 螯よ棡 totalRows 荳堺クコ null<6C>瑚ッエ譏主、<E4B8BB>炊螳梧<E89EB3>?
// - 蜷ヲ蛻呎衍隸「 job 迥カ諤?
if (record.totalRows !== null) {
return reply.send({
success: true,
data: {
recordId,
status: 'ready', // 笨?螟<>炊螳梧<E89EB3>
progress: 100,
record,
},
});
}
// 3. 螟<>炊荳ュ<E88DB3>梧衍隸「 pg-boss
if (!jobId) {
return reply.send({
success: true,
data: {
recordId,
status: 'processing',
progress: 50,
},
});
}
// 4. 莉?pg-boss 譟・隸「 job 迥カ諤?
const job = await jobQueue.getJob(jobId);
const status = job?.status === 'completed' ? 'ready' :
job?.status === 'failed' ? 'error' : 'processing';
const progress = status === 'ready' ? 100 :
status === 'error' ? 0 : 70;
return reply.send({
success: true,
data: {
recordId,
jobId,
status,
progress,
record,
},
});
}
}
```
---
### 豁・鬪、5<EFBDA4>壼燕遶?- React Query 霓ョ隸「
```typescript
// frontend-v2/src/modules/your-module/hooks/useTaskStatus.ts
import { useQuery } from '@tanstack/react-query';
import * as api from '../api';
/**
* 莉サ蜉。迥カ諤∬スョ隸?Hook
*
* 迚ケ轤ケ<E8BDA4>?
* - 閾ェ蜉ィ荳イ陦瑚スョ隸「<E99AB8><EFBDA2>eact Query 蜀<>スョ髦イ蟷カ蜿托シ<E68998>
* - 閾ェ蜉ィ貂<EFBDA8><EFBFBD>育サ<E882B2>サカ蜊ク霓ス譌カ蛛懈ュ「<EFBDAD>?
* - 譚。莉カ蛛懈ュ「<EFBDAD>亥ョ梧<EFBDAE>?螟ア雍・譌カ閾ェ蜉ィ蛛懈ュ「<EFBDAD><EFBDA2>
*/
export function useTaskStatus({
recordId,
jobId,
enabled = true,
}) {
const { data, isLoading, error } = useQuery({
queryKey: ['taskStatus', recordId, jobId],
queryFn: () => api.getTaskStatus(recordId, jobId),
enabled: enabled && !!recordId && !!jobId,
refetchInterval: (query) => {
const status = query.state.data?.data?.status;
// 笨?螳梧<E89EB3>謌門、ア雍・譌カ蛛懈ュ「霓ョ隸「
if (status === 'ready' || status === 'error') {
return false;
}
// 笨?螟<>炊荳ュ譌カ豈?遘定スョ隸「<E99AB8><EFBFBD>蜉ィ荳イ陦鯉シ?
return 2000;
},
staleTime: 0, // 蟋狗サ郁ァ<E98381>クコ霑<EFBDBA><EFBFBD>檎。ョ菫晁スョ隸?
retry: 1,
});
const statusInfo = data?.data;
const status = statusInfo?.status || 'processing';
const progress = statusInfo?.progress || 0;
return {
status,
progress,
isReady: status === 'ready',
isError: status === 'error',
isLoading,
error,
};
}
```
---
### 豁・鬪、6<EFBDA4>壼燕遶ッ扈<EFBDAF>サ?- 菴ソ逕ィHook
```typescript
// frontend-v2/src/modules/your-module/pages/YourPage.tsx
import { useTaskStatus } from '../hooks/useTaskStatus';
const YourPage = () => {
const [pollingInfo, setPollingInfo] = useState<{
recordId: string;
jobId: string;
} | null>(null);
// 笨?菴ソ逕ィ React Query Hook 閾ェ蜉ィ霓ョ隸「
const { status, progress, isReady } = useTaskStatus({
recordId: pollingInfo?.recordId || null,
jobId: pollingInfo?.jobId || null,
enabled: !!pollingInfo,
});
// 笨?逶大成迥カ諤∝序蛹?
useEffect(() => {
if (isReady && pollingInfo) {
console.log('?<EFBFBD><EFBFBD><EFBFBD>?);
// 蛛懈ュ「霓ョ隸「
setPollingInfo(null);
// 蜉<>霓ス謨ー謐ョ
loadData(pollingInfo.recordId);
}
}, [isReady, pollingInfo]);
// 荳贋シ<E8B48B><EFBFBD>サカ
const handleUpload = async (file) => {
const result = await api.uploadFile(file);
const { recordId, jobId } = result.data;
// 笨?蜷ッ蜉ィ霓ョ隸「<E99AB8>郁ョセ鄂ョ迥カ諤<EFBDB6>シ軍eact Query閾ェ蜉ィ蠑€蟋具シ<E585B7>
setPollingInfo({ recordId, jobId });
};
return (
<div>
{/* 霑帛コヲ譚?*/}
{pollingInfo && (
<div className="progress-bar">
<div style={{ width: `${progress}%` }} />
<span>{progress}%</span>
</div>
)}
{/* 荳贋シ<E8B48B>謖蛾聴 */}
<button onClick={() => handleUpload(file)}><EFBFBD></button>
</div>
);
};
```
---
## <20>識 蜈ウ髞ョ謚€譛ッ轤ケ
### 1. 髦溷<E9ABA6>蜷咲ァー隗<EFBDB0>
**髞呵ッッ**<EFBFBD>?
```typescript
?'asl:screening:batch' // 蛹<>性蜀貞捷<E8B29E>継g-boss荳肴髪謖?
?'dc.toolc.parse' // 蛹<>性轤ケ蜿キ<E89CBF>御ク肴耳闕<E880B3>
```
**豁」遑ョ**<EFBFBD>?
```typescript
?'asl_screening_batch' // 荳句<E88DB3>郤?
?'dc_toolc_parse_excel' // 荳句<E88DB3>郤?
```
---
### 2. Worker豕ィ蜀梧慮譛コ
```typescript
// backend/src/index.ts
await jobQueue.start(); // 竊?蠢<>。サ蜈亥星蜉ィ髦溷<E9ABA6>?
registerYourWorker(); // 竊?蜀肴ウィ蜀?Worker
registerOtherWorker();
// 笨?遲牙セ<E78999>3遘抵シ檎。ョ菫晏シよュ・豕ィ蜀悟ョ梧<EFBDAE>
await new Promise(resolve => setTimeout(resolve, 3000));
logger.info('笨?All workers registered');
```
---
### 3. clean data 郛灘ュ俶惻蛻カ
**逶ョ逧<EFBDAE>**<EFBFBD>夐∩蜈埼㍾螟崎ョ。邂暦シ域€ァ閭ス謠仙合99%<25>?
```typescript
// Worker 菫晏ュ<E6998F> clean data
const cleanDataKey = `${fileKey}_clean.json`;
await storage.upload(cleanDataKey, JSON.stringify(processedData));
await prisma.update({
where: { id },
data: {
cleanDataKey, // 竊?隶ー蠖穂ス咲スョ
totalRows,
columns,
}
});
// Service 隸サ蜿匁焚謐ョ<E8AC90>井シ伜<EFBDBC>?clean data<74>?
async getFullData(recordId) {
const record = await prisma.findUnique({ where: { id: recordId } });
// 笨?莨伜<E88EA8>隸サ蜿<EFBDBB> clean data<74>?1遘抵シ<E68AB5>
if (record.cleanDataKey) {
const buffer = await storage.download(record.cleanDataKey);
return JSON.parse(buffer.toString('utf-8'));
}
// 笞<><E7AC9E><EFBFBD> Fallback<63>夐㍾譁ー隗」譫撰シ亥<EFBDBC>螳ケ譌ァ謨ー謐ョ<E8AC90><EFBDAE>
const buffer = await storage.download(record.fileKey);
return parseFile(buffer);
}
// 笞<><E7AC9E><EFBFBD> 驥崎ヲ<E5B48E>シ壽桃菴懷錘隕∝酔豁・譖エ譁?clean data
async saveProcessedData(recordId, newData) {
const record = await getRecord(recordId);
// 隕<>尠蜴滓枚莉?
await storage.upload(record.fileKey, toExcel(newData));
// 笨?蜷梧慮譖エ譁ー clean data
if (record.cleanDataKey) {
await storage.upload(record.cleanDataKey, JSON.stringify(newData));
}
// 譖エ譁ー蜈<EFBDB0>焚謐?
await prisma.update({ where: { id: recordId }, data: { ... } });
}
```
---
### 4. React Query 霓ョ隸「<E99AB8>域耳闕撰シ<E692B0>
**莨倡せ**<EFBFBD>?
- 笨?閾ェ蜉ィ荳イ陦鯉シ磯亟蟷カ蜿鷹」取垓<E58F96>?
- 笨?閾ェ蜉ィ蜴サ驥搾シ亥酔荳€queryKey蜿ェ譛我ク€荳ェ隸キ豎ゑシ<E38291>
- 笨?閾ェ蜉ィ貂<EFBDA8><EFBFBD>育サ<E882B2>サカ蜊ク霓ス譌カ蛛懈ュ「<EFBDAD>?
- 笨?譚。莉カ蛛懈ュ「<EFBDAD>亥勘諤∵而蛻カ<E89BBB><EFBDB6>
**荳崎ヲ∽スソ逕ィ setInterval**<EFBFBD>?
```typescript
?const pollInterval = setInterval(() => {
api.getStatus(); // 蜿ッ閭ス蟷カ蜿<EFBDB6>
}, 2000);
```
---
## <20>投 諤ァ閭ス蟇ケ豈<EFBDB9>
### DC Tool C 螳樣刔謨ー謐ョ<E8AC90>?339陦古?51蛻玲枚莉カ<E88E89><EFBDB6>
| 謖<><E8AC96><EFBFBD> | 蜷梧ュ・螟<EFBDA5>炊 | 蠑よュ・螟<EFBDA5>炊 | 謾ケ蝟<EFBDB9> |
|------|---------|---------|------|
| **荳贋シ<E8B48B>閠玲慮** | 47遘抵シ磯仆蝪橸シ?| 3遘抵シ育ォ句叉霑泌屓<E6B38C>?| 笨?-94% |
| **HTTP雜<50>** | 笶?扈丞クク雜<EFBDB8>慮 | 笨?荳堺シ夊カ<E5A48A>慮 | 笨?100% |
| **getPreviewData** | 43遘抵シ磯㍾螟崎ァ」譫撰シ?| 0.5遘抵シ育シ灘ュ假シ?| 笨?-99% |
| **getFullData** | 43遘抵シ磯㍾螟崎ァ」譫撰シ?| 0.5遘抵シ育シ灘ュ假シ?| 笨?-99% |
| **QuickAction謫堺ス<E5A0BA>** | 43遘?+ Python | 0.5遘?+ Python | 笨?-95% |
| **蟷カ蜿題ッキ豎<EFBDB7>** | 15+荳?| 1荳ェ<E88DB3>井クイ陦鯉シ?| 笨?-93% |
---
## 笞<><E7AC9E><EFBFBD> 蟶ク隗<EFBDB8>琉鬚<E79089>
### Q1: Worker 豕ィ蜀御コ<E5BEA1><EFBFBD>ク榊キ・菴懶シ<E687B6>
**譽€譟?*<2A>?
- 髦溷<E9ABA6>蜷咲ァー譏ッ蜷ヲ蛹<EFBDA6>性蜀貞捷<E8B29E><E68DB7>:`<60>会シ滓隼荳コ荳句<E88DB3>郤ソ<E983A4><EFBDBF>_`<EFBFBD>?
- 邇ッ蠅<EFBDAF>序驥<E5BA8F> `QUEUE_TYPE=pgboss` 譏ッ蜷ヲ隶セ鄂ョ<E98482>?
- Worker 豕ィ蜀梧弍蜷ヲ蝨?`jobQueue.start()` 荵句錘<E58FA5>?
### Q2: 霓ョ隸「鬟取垓<E58F96>亥、壻クェ蟷カ蜿題ッキ豎ゑシ会シ?
**隗」蜀ウ**<EFBFBD>壻スソ逕?React Query<72>御ク崎ヲ∫畑 setInterval
### Q3: 蟇シ蜃コ謨ー謐ョ荳榊ッケ<EFBDAF>域弍蜴溷ァ区焚謐ョ<E8AC90>会シ<E4BC9A>
**蜴溷屏**<EFBFBD>啻saveProcessedData` 豐。譛画峩譁ー clean data
**隗」蜀ウ**<2A>壼酔譌カ譖エ譁?fileKey 蜥?cleanDataKey
---
## <20>答 蜿り€<E3828A>ョ樒<EFBDAE>?
| 讓。蝮<EFBDA1> | Worker | 蜑咲ォッHook | 譁<>。」 |
|------|--------|---------|------|
| **DC Tool C** | `parseExcelWorker.ts` | `useSessionStatus.ts` | 譛ャ謖<EFBDAC>漉蝓コ遑€ |
| **ASL 譎コ閭ス譁<EFBDBD>鍵** | `screeningWorker.ts` | `useScreeningTask.ts` | [ASL讓。蝮礼憾諤(../03-荳壼苅讓。蝮<EFBDA1>/ASL-AI譎コ閭ス譁<EFBDBD>鍵/00-讓。蝮怜ス灘燕迥カ諤∽ク主シ€蜿第欠蜊?md) |
| **DC Tool B** | `extractionWorker.ts` | - | [DC讓。蝮礼憾諤(../03-荳壼苅讓。蝮<EFBDA1>/DC-謨ー謐ョ貂<EFBDAE>エ玲紛逅<E7B49B>/00-讓。蝮怜ス灘燕迥カ諤∽ク主シ€蜿第欠蜊?md) |
---
## 笨?譽€譟・貂<EFBDA5><E8B282>?
蝨ィ螳樊命蠑よュ・莉サ蜉。蜑搾シ瑚ッキ遑ョ隶、<EFBFBD>?
- [ ] 荳壼苅陦ィ蜿ェ蟄倅ク壼苅菫。諱ッ<E8ABB1>井ク榊桁蜷?status 遲牙ュ玲ョオ<EFBDAE><EFBDB5>
- [ ] 髦溷<E9ABA6>蜷咲ァー菴ソ逕ィ荳句<E88DB3>郤ソ<E983A4>井ク榊性蜀貞捷<E8B29E>?
- [ ] 邇ッ蠅<EFBDAF>序驥<E5BA8F> `QUEUE_TYPE=pgboss` 蟾イ隶セ鄂?
- [ ] Worker 蝨?`jobQueue.start()` 荵句錘豕ィ蜀<EFBDA8>
- [ ] 蜑咲ォッ菴ソ逕ィ React Query 霓ョ隸「
- [ ] Service 莨伜<E88EA8>隸サ蜿<EFBDBB> clean data
- [ ] saveProcessedData 蜷梧ュ・譖エ譁ー clean data
---
**扈エ謚、閠?*: 蟷ウ蜿ー譫カ譫<EFBDB6>屬髦<E5B1AC>
**譛€蜷取峩譁?*: 2025-12-22
**譁<>。」迥カ諤?*: 笨?蟾イ螳梧<E89EB3>?