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

16 KiB
Raw Blame History

Postgres-Only 蠑よュ・莉サ蜉。螟<EFBDA1>炊謖<E7828A>

*<EFBFBD>。」迚域悽<EFBFBD>? v1.0
*蛻帛サコ譌・譛滂シ? 2025-12-22
扈エ謚、閠<EFBFBD><EFBFBD> 蟷ウ蜿ー譫カ譫<EFBDB6>屬髦<E5B1AC>
*騾ら畑蝨コ譎ッ<EFBFBD>? 髟ソ譌カ髣エ莉サ蜉。<E89C89><EFBDA1>>30遘抵シ峨€∝、ァ譁<EFBDA7>サカ螟<EFBDB6>炊縲∝錘蜿ーWorker
蜿り€<EFBFBD>ョ樒鴫<EFBFBD><EFBFBD> DC Tool C Excel隗」譫舌€、SL譁<4C>鍵遲幃€€C Tool B謨ー謐ョ謠仙叙


<EFBFBD>搭 讎りソー

譛ャ譁<EFBFBD>。」蝓コ莠?DC Tool C Excel隗」譫仙粥閭ス<>ョ梧紛螳櫁キオ<EFBDB7>€サ扈<EFBDBB> Postgres-Only 譫カ譫<EFBDB6>ク句シよュ・莉サ蜉。螟<EFBDA1>炊逧<E7828A><E980A7><EFBFBD>㊥讓。蠑上€?

譬ク蠢<EFBFBD>サキ蛟?

  1. 笨?驕ソ蜈工TTP雜<EFBFBD><EFBFBD>壻ク贋シ<EFBFBD>謗・蜿?遘定ソ泌屓<E6B38C>瑚ァ」譫仙惠蜷主床螳梧<E89EB3><E6A2A7><EFBFBD>30-60遘抵シ<E68AB5>
  2. 笨?逕ィ謌キ菴馴ェ御シ倡ァ€<EFBFBD>壼ョ樊慮霑帛コヲ蜿埼ヲ茨シ御ク埼怙隕∝そ遲?
  3. 笨?**隨ヲ蜷井コ大次逕溯ァ<E6BAAF><EFBDA7>?*<2A>啀latform-Only讓。蠑擾シ継g-boss髦溷<E9ABA6>
  4. 笨?諤ァ閭ス莨伜喧<EFBFBD>喞lean data郛灘ュ假シ碁∩蜈埼㍾螟崎ョ。邂暦シ<E69AA6>-99%閠玲慮<E78EB2>?

<EFBFBD><EFBFBD>?譫カ譫<EFBDB6>ョセ隶。

荳牙アよ楔譫<EFBFBD>

笏娯楳笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏?
笏? 蜑咲ォッ螻ゑシ<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>?                          笏?
笏披楳笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏?

<EFBFBD>噫 螳梧紛螳樊命豁・鬪、

豁・鬪、1<EFBFBD>壽焚謐ョ蠎鉄chema隶セ隶。

// 荳壼苅陦ィ蜿ェ蟄倅ク壼苅菫。諱ッ<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> statusprogresserrorMessage 遲我ササ蜉。邂。逅<EFBDA1>ュ玲ョ?
  • 笨?霑吩コ帛ュ玲ョオ逕?pg-boss 逧?job 陦ィ邂。逅?

豁・鬪、2<EFBFBD>售ervice螻?- 蠢ォ騾滉ク贋シ?謗ィ騾∽ササ蜉?

// 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<EFBFBD>啗orker螻?- 蜷主床螟<E5BA8A>

// 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<EFBFBD>咾ontroller螻?- 迥カ諤∵衍隸「API

// 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<EFBFBD>壼燕遶?- React Query 霓ョ隸「

// 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<EFBFBD>壼燕遶ッ扈<EFBFBD>サ?- 菴ソ逕ィHook

// 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>
  );
};

<EFBFBD>識 蜈ウ髞ョ謚€譛ッ轤ケ

1. 髦溷<E9ABA6>蜷咲ァー隗<EFBDB0>

髞呵ッッ<EFBFBD>?

?'asl:screening:batch'  // 蛹<>性蜀貞捷<E8B29E>継g-boss荳肴髪謖?
?'dc.toolc.parse'       // 蛹<>性轤ケ蜿キ<E89CBF>御ク肴耳闕<E880B3>

豁」遑ョ<EFBFBD>?

?'asl_screening_batch'  // 荳句<E88DB3>郤?
?'dc_toolc_parse_excel' // 荳句<E88DB3>郤?

2. Worker豕ィ蜀梧慮譛コ

// 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 郛灘ュ俶惻蛻カ

逶ョ逧<EFBFBD><EFBFBD>夐∩蜈埼㍾螟崎ョ。邂暦シ域€ァ閭ス謠仙合99%<25>?

// 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>?

?const pollInterval = setInterval(() => {
  api.getStatus();  // 蜿ッ閭ス蟷カ蜿<EFBDB6>
}, 2000);

<EFBFBD>投 諤ァ閭ス蟇ケ豈<EFBDB9>

DC Tool C 螳樣刔謨ー謐ョ<E8AC90>?339陦古?51蛻玲枚莉カ<E88E89><EFBDB6>

<EFBFBD><EFBFBD><EFBFBD> 蜷梧ュ・螟<EFBFBD> 蠑よュ・螟<EFBFBD> 謾ケ蝟<EFBFBD>
荳贋シ<EFBFBD>閠玲慮 47遘抵シ磯仆蝪橸シ? 3遘抵シ育ォ句叉霑泌屓<EFBFBD>? 笨?-94%
HTTP雜<EFBFBD> 笶?扈丞クク雜<EFBDB8> 笨?荳堺シ夊カ<E5A48A> 笨?100%
getPreviewData 43遘抵シ磯㍾螟崎ァ」譫撰シ? 0.5遘抵シ育シ灘ュ假シ? 笨?-99%
getFullData 43遘抵シ磯㍾螟崎ァ」譫撰シ? 0.5遘抵シ育シ灘ュ假シ? 笨?-99%
QuickAction謫堺ス<EFBFBD> 43遘?+ Python 0.5遘?+ Python 笨?-95%
蟷カ蜿題ッキ豎<EFBFBD> 15+荳? 1荳ェ<EFBFBD>井クイ陦鯉シ? 笨?-93%

<EFBFBD><EFBFBD><EFBFBD> 蟶ク隗<EFBDB8>琉鬚<E79089>

Q1: Worker 豕ィ蜀御コ<E5BEA1><EFBFBD>ク榊キ・菴懶シ<E687B6>

**譽€譟?*<2A>?

  • 髦溷<EFBFBD>蜷咲ァー譏ッ蜷ヲ蛹<EFBFBD>性蜀貞捷<EFBFBD><EFBFBD>:<EFBFBD>会シ滓隼荳コ荳句<EFBFBD>郤ソ<EFBFBD><EFBFBD>_<EFBFBD>?
  • 邇ッ蠅<EFBFBD>序驥<EFBFBD> QUEUE_TYPE=pgboss 譏ッ蜷ヲ隶セ鄂ョ<E98482>?
  • Worker 豕ィ蜀梧弍蜷ヲ蝨?jobQueue.start() 荵句錘<E58FA5>?

Q2: 霓ョ隸「鬟取垓<E58F96>亥、壻クェ蟷カ蜿題ッキ豎ゑシ会シ?

隗」蜀ウ<EFBFBD>壻スソ逕?React Query<72>御ク崎ヲ∫畑 setInterval

Q3: 蟇シ蜃コ謨ー謐ョ荳榊ッケ<EFBDAF>域弍蜴溷ァ区焚謐ョ<E8AC90>会シ<E4BC9A>

蜴溷屏<EFBFBD>啻saveProcessedData` 豐。譛画峩譁ー clean data
隗」蜀ウ<EFBFBD>壼酔譌カ譖エ譁?fileKey 蜥?cleanDataKey


<EFBFBD>答 蜿り€<E3828A>ョ樒<EFBDAE>?

讓。蝮<EFBFBD> Worker 蜑咲ォッHook <EFBFBD>。」
DC Tool C parseExcelWorker.ts useSessionStatus.ts 譛ャ謖<EFBFBD>漉蝓コ遑€
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>?

  • 荳壼苅陦ィ蜿ェ蟄倅ク壼苅菫。諱ッ<EFBFBD>井ク榊桁蜷?status 遲牙ュ玲ョオ<EFBDAE><EFBDB5>
  • 髦溷<EFBFBD>蜷咲ァー菴ソ逕ィ荳句<EFBFBD>郤ソ<EFBFBD>井ク榊性蜀貞捷<EFBFBD>?
  • 邇ッ蠅<EFBFBD>序驥<EFBFBD> QUEUE_TYPE=pgboss 蟾イ隶セ鄂?
  • Worker 蝨?jobQueue.start() 荵句錘豕ィ蜀<EFBDA8>
  • 蜑咲ォッ菴ソ逕ィ React Query 霓ョ隸「
  • Service 莨伜<E88EA8>隸サ蜿<EFBDBB> clean data
  • saveProcessedData 蜷梧ュ・譖エ譁ー clean data

**扈エ謚、閠?: 蟷ウ蜿ー譫カ譫<EFBDB6>屬髦<E5B1AC>
**譛€蜷取峩譁?
: 2025-12-22
**譁<>。」迥カ諤?*: 笨?蟾イ螳梧<E89EB3>?