Files
AIclinicalresearch/frontend-v2/src/modules/dc/hooks/useAssets.ts
HaHafeng 61cdc97eeb feat(platform): Fix pg-boss queue conflict and add safety standards
Summary:
- Fix pg-boss queue conflict (duplicate key violation on queue_pkey)
- Add global error listener to prevent process crash
- Reduce connection pool from 10 to 4
- Add graceful shutdown handling (SIGTERM/SIGINT)
- Fix researchWorker recursive call bug in catch block
- Make screeningWorker idempotent using upsert

Security Standards (v1.1):
- Prohibit recursive retry in Worker catch blocks
- Prohibit payload bloat (only store fileKey/ID in job.data)
- Require Worker idempotency (upsert + unique constraint)
- Recommend task-specific expireInSeconds settings
- Document graceful shutdown pattern

New Features:
- PKB signed URL endpoint for document preview/download
- pg_bigm installation guide for Docker
- Dockerfile.postgres-with-extensions for pgvector + pg_bigm

Documentation:
- Update Postgres-Only async task processing guide (v1.1)
- Add troubleshooting SQL queries
- Update safety checklist

Tested: Local verification passed
2026-01-23 22:07:26 +08:00

176 lines
2.6 KiB
TypeScript

/**
* DC模块 - 数据资产Hook
*
* 管理数据资产库的状态和数据获取
*/
import { useState, useEffect } from 'react';
import type { Asset, AssetTabType } from '../types/portal';
// Mock数据
const mockAssets: Asset[] = [
{
id: 'asset-001',
name: '2025糖尿病研究_AI提取结果.xlsx',
type: 'processed',
source: 'tool-b',
rowCount: 150,
tags: ['糖尿病', 'AI结构化'],
modifiedAt: '2025-12-01T11:45:00Z',
fileSize: 245760,
fileKey: 'dc/outputs/task-001-result.xlsx'
},
{
id: 'asset-002',
name: '高血压病历原始数据.xlsx',
type: 'raw',
source: 'upload',
rowCount: 320,
tags: ['高血压', '原始数据'],
modifiedAt: '2025-12-02T09:00:00Z',
fileSize: 512000,
fileKey: 'dc/uploads/hypertension-raw.xlsx'
},
{
id: 'asset-003',
name: '多中心数据合并结果.xlsx',
type: 'processed',
source: 'tool-a',
rowCount: 580,
tags: ['多中心', '数据合并'],
modifiedAt: '2025-11-30T16:20:00Z',
fileSize: 1048576,
fileKey: 'dc/outputs/merged-data.xlsx'
}
];
export const useAssets = (activeTab: AssetTabType) => {
const [assets, setAssets] = useState<Asset[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
// 获取资产列表
const fetchAssets = async () => {
try {
setLoading(true);
// TODO: 替换为真实API调用
// const response = await fetch(`/api/v1/dc/assets?type=${activeTab}`);
// const data = await response.json();
// 模拟API延迟
await new Promise(resolve => setTimeout(resolve, 300));
// 根据Tab筛选
let filteredAssets = mockAssets;
if (activeTab === 'processed') {
filteredAssets = mockAssets.filter(a => a.type === 'processed');
} else if (activeTab === 'raw') {
filteredAssets = mockAssets.filter(a => a.type === 'raw');
}
setAssets(filteredAssets);
setError(null);
} catch (err) {
setError(err instanceof Error ? err.message : '获取资产列表失败');
} finally {
setLoading(false);
}
};
useEffect(() => {
fetchAssets();
}, [activeTab]);
// 刷新资产列表
const refresh = () => {
fetchAssets();
};
// 删除资产
const deleteAsset = async (id: string) => {
// TODO: 实现删除逻辑
console.log('Delete asset:', id);
setAssets(assets.filter(a => a.id !== id));
};
return {
assets,
loading,
error,
refresh,
deleteAsset
};
};