docs(common): Add platform infrastructure technical debt list
Summary: - Document future optimization opportunities based on Tool C practice - 7 technical debts identified with priority and effort estimation - Focus on: Clean data service, Data version management, Generic polling Hook Content: - TD-COMMON-001: Generic polling Hook for frontend (P2, 0.5d) - TD-COMMON-002: Clean data caching service (P1, 1d, High priority) - TD-COMMON-003: Generic data cleaning algorithms (P2, 0.5d) - TD-COMMON-004: Worker registration helper (P3, 0.3d) - TD-COMMON-005: Data version management system (P1, 3d, High priority) - TD-COMMON-006: Ghost column/row detection library (P2, 0.5d) - TD-COMMON-007: Generic progress bar component (P2, 0.3d) Recommendation: - Priority 1: Clean data service + Data version management - Priority 2: Generic polling Hook - Current architecture is already good, these are enhancements Status: Documentation complete, ready for future implementation
This commit is contained in:
774
docs/02-通用能力层/通用能力层技术债务清单.md
Normal file
774
docs/02-通用能力层/通用能力层技术债务清单.md
Normal file
@@ -0,0 +1,774 @@
|
||||
# 通用能力层技术债务清单
|
||||
|
||||
> **文档版本:** v1.0
|
||||
> **创建日期:** 2025-12-22
|
||||
> **维护者:** 平台架构团队
|
||||
> **文档目的:** 记录通用能力层待优化项,指导未来迭代
|
||||
|
||||
---
|
||||
|
||||
## 📋 概述
|
||||
|
||||
本文档基于 **DC Tool C 异步架构实践**(2025-12-22),总结发现的可以抽象为通用能力的模式。
|
||||
|
||||
**核心思想**:
|
||||
- ✅ **当前分层已经很好**(通用能力层提供完整的基础设施)
|
||||
- ✅ **业务模块正确使用了这些能力**
|
||||
- ⏭️ **未来可以进一步抽象**(锦上添花,非必需)
|
||||
|
||||
---
|
||||
|
||||
## 🎯 技术债务清单
|
||||
|
||||
### TD-COMMON-001: 前端通用轮询Hook
|
||||
|
||||
**优先级**:⭐⭐⭐ P2(中)
|
||||
**工作量**:0.5天
|
||||
**预期收益**:代码复用性提升,模块间统一
|
||||
|
||||
#### 问题描述
|
||||
|
||||
**当前状态**:
|
||||
- Tool C 实现:`dc/tool-c/hooks/useSessionStatus.ts`
|
||||
- ASL 实现:`asl/hooks/useScreeningTask.ts`
|
||||
- 代码重复度:70%
|
||||
|
||||
**重复的逻辑**:
|
||||
```typescript
|
||||
// 每个模块都要写类似的代码
|
||||
useQuery({
|
||||
queryKey: ['taskStatus', id],
|
||||
queryFn: () => api.getStatus(id),
|
||||
enabled: !!id,
|
||||
refetchInterval: (query) => {
|
||||
const status = query.state.data?.status;
|
||||
if (status === 'ready' || status === 'error') return false;
|
||||
return 2000;
|
||||
},
|
||||
staleTime: 0,
|
||||
});
|
||||
```
|
||||
|
||||
#### 解决方案
|
||||
|
||||
**抽象为通用Hook**:
|
||||
|
||||
```typescript
|
||||
// frontend-v2/src/common/hooks/useAsyncTaskPolling.ts(新建)
|
||||
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
interface UseAsyncTaskPollingOptions<T> {
|
||||
/** 任务ID */
|
||||
taskId: string | null;
|
||||
|
||||
/** 状态查询API函数 */
|
||||
queryFn: (taskId: string) => Promise<T>;
|
||||
|
||||
/** 状态提取函数 */
|
||||
getStatus: (data: T) => 'pending' | 'processing' | 'ready' | 'error' | string;
|
||||
|
||||
/** 进度提取函数(可选) */
|
||||
getProgress?: (data: T) => number;
|
||||
|
||||
/** 是否启用 */
|
||||
enabled?: boolean;
|
||||
|
||||
/** 轮询间隔(毫秒),默认2000 */
|
||||
pollingInterval?: number;
|
||||
}
|
||||
|
||||
export function useAsyncTaskPolling<T>({
|
||||
taskId,
|
||||
queryFn,
|
||||
getStatus,
|
||||
getProgress,
|
||||
enabled = true,
|
||||
pollingInterval = 2000,
|
||||
}: UseAsyncTaskPollingOptions<T>) {
|
||||
const { data, isLoading, error } = useQuery({
|
||||
queryKey: ['asyncTask', taskId],
|
||||
queryFn: () => queryFn(taskId!),
|
||||
enabled: enabled && !!taskId,
|
||||
refetchInterval: (query) => {
|
||||
if (!query.state.data) return false;
|
||||
|
||||
const status = getStatus(query.state.data);
|
||||
|
||||
// 完成或失败时停止轮询
|
||||
if (status === 'ready' || status === 'completed' || status === 'error' || status === 'failed') {
|
||||
return false;
|
||||
}
|
||||
|
||||
return pollingInterval;
|
||||
},
|
||||
staleTime: 0,
|
||||
retry: 1,
|
||||
});
|
||||
|
||||
const status = data ? getStatus(data) : 'pending';
|
||||
const progress = getProgress && data ? getProgress(data) : 0;
|
||||
|
||||
return {
|
||||
data,
|
||||
status,
|
||||
progress,
|
||||
isReady: status === 'ready' || status === 'completed',
|
||||
isError: status === 'error' || status === 'failed',
|
||||
isProcessing: status === 'processing' || status === 'pending',
|
||||
isLoading,
|
||||
error,
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
**使用示例**:
|
||||
|
||||
```typescript
|
||||
// Tool C 使用
|
||||
const { status, progress, isReady } = useAsyncTaskPolling({
|
||||
taskId: sessionId,
|
||||
queryFn: (sid) => api.getSessionStatus(sid, jobId),
|
||||
getStatus: (res) => res.data.status,
|
||||
getProgress: (res) => res.data.progress,
|
||||
});
|
||||
|
||||
// ASL 使用
|
||||
const { status, isReady } = useAsyncTaskPolling({
|
||||
taskId: projectId,
|
||||
queryFn: (pid) => aslApi.getScreeningTask(pid),
|
||||
getStatus: (res) => res.data.status,
|
||||
getProgress: (res) => Math.round((res.data.processedItems / res.data.totalItems) * 100),
|
||||
});
|
||||
```
|
||||
|
||||
#### 影响范围
|
||||
|
||||
- Tool C: `useSessionStatus.ts` 可简化
|
||||
- ASL: `useScreeningTask.ts` 可简化
|
||||
- Tool B: 未来前端可直接使用
|
||||
- 其他模块: 直接复用
|
||||
|
||||
---
|
||||
|
||||
### TD-COMMON-002: Clean Data 缓存服务
|
||||
|
||||
**优先级**:⭐⭐⭐⭐ P1(高)
|
||||
**工作量**:1天
|
||||
**预期收益**:性能提升99%,所有模块受益
|
||||
|
||||
#### 问题描述
|
||||
|
||||
**当前状态**:
|
||||
- Tool C 实现:保存 `${fileKey}_clean.json`
|
||||
- ASL、Tool B:未实现,仍然每次重新解析
|
||||
|
||||
**重复计算问题**:
|
||||
- ASL 文献筛选:每次从 OSS 下载 PDF,重新解析(5-10秒/篇)
|
||||
- Tool B 数据提取:每次重新读取 Excel
|
||||
- Tool C 操作:已优化(clean data缓存)
|
||||
|
||||
#### 解决方案
|
||||
|
||||
**抽象为通用服务**:
|
||||
|
||||
```typescript
|
||||
// backend/src/common/services/DataCacheService.ts(新建)
|
||||
|
||||
import { storage } from '../storage';
|
||||
import { prisma } from '../../config/database';
|
||||
import { logger } from '../logging';
|
||||
|
||||
/**
|
||||
* 数据缓存服务(通用)
|
||||
*
|
||||
* 用途:
|
||||
* - Worker 解析后保存处理结果
|
||||
* - Service 优先读取缓存,避免重复计算
|
||||
* - 操作后同步更新缓存
|
||||
*/
|
||||
export class DataCacheService {
|
||||
/**
|
||||
* 保存清洗/处理后的数据
|
||||
*
|
||||
* @param originalKey 原始文件key
|
||||
* @param cleanData 清洗后的数据
|
||||
* @param suffix 后缀(默认 '_clean.json')
|
||||
* @returns clean data 的 OSS key
|
||||
*/
|
||||
async saveCleanData(
|
||||
originalKey: string,
|
||||
cleanData: any,
|
||||
suffix: string = '_clean.json'
|
||||
): Promise<string> {
|
||||
const cleanDataKey = `${originalKey}${suffix}`;
|
||||
|
||||
logger.info('[DataCacheService] Saving clean data', {
|
||||
originalKey,
|
||||
cleanDataKey,
|
||||
rows: Array.isArray(cleanData) ? cleanData.length : 'N/A',
|
||||
});
|
||||
|
||||
// 序列化并上传
|
||||
const buffer = Buffer.from(JSON.stringify(cleanData), 'utf-8');
|
||||
await storage.upload(cleanDataKey, buffer);
|
||||
|
||||
logger.info('[DataCacheService] Clean data saved', {
|
||||
size: `${(buffer.length / 1024).toFixed(2)} KB`
|
||||
});
|
||||
|
||||
return cleanDataKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取清洗后的数据
|
||||
*
|
||||
* @param cleanDataKey clean data 的 OSS key
|
||||
* @returns 清洗后的数据
|
||||
*/
|
||||
async getCleanData(cleanDataKey: string): Promise<any> {
|
||||
logger.info('[DataCacheService] Loading clean data', { cleanDataKey });
|
||||
|
||||
const buffer = await storage.download(cleanDataKey);
|
||||
const data = JSON.parse(buffer.toString('utf-8'));
|
||||
|
||||
logger.info('[DataCacheService] Clean data loaded', {
|
||||
rows: Array.isArray(data) ? data.length : 'N/A',
|
||||
});
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新清洗后的数据
|
||||
*
|
||||
* @param cleanDataKey clean data 的 OSS key
|
||||
* @param newData 新数据
|
||||
*/
|
||||
async updateCleanData(cleanDataKey: string, newData: any): Promise<void> {
|
||||
logger.info('[DataCacheService] Updating clean data', {
|
||||
cleanDataKey,
|
||||
rows: Array.isArray(newData) ? newData.length : 'N/A',
|
||||
});
|
||||
|
||||
const buffer = Buffer.from(JSON.stringify(newData), 'utf-8');
|
||||
await storage.upload(cleanDataKey, buffer);
|
||||
|
||||
logger.info('[DataCacheService] Clean data updated');
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除清洗后的数据
|
||||
*
|
||||
* @param cleanDataKey clean data 的 OSS key
|
||||
*/
|
||||
async deleteCleanData(cleanDataKey: string): Promise<void> {
|
||||
try {
|
||||
await storage.delete(cleanDataKey);
|
||||
logger.info('[DataCacheService] Clean data deleted', { cleanDataKey });
|
||||
} catch (error: any) {
|
||||
logger.warn('[DataCacheService] Clean data deletion failed', {
|
||||
cleanDataKey,
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const dataCacheService = new DataCacheService();
|
||||
```
|
||||
|
||||
**使用示例**:
|
||||
|
||||
```typescript
|
||||
// Worker 中
|
||||
const cleanDataKey = await dataCacheService.saveCleanData(fileKey, cleanedData);
|
||||
await prisma.update({ where: { id }, data: { cleanDataKey } });
|
||||
|
||||
// Service 中
|
||||
if (record.cleanDataKey) {
|
||||
return await dataCacheService.getCleanData(record.cleanDataKey);
|
||||
}
|
||||
|
||||
// 操作后更新
|
||||
await dataCacheService.updateCleanData(record.cleanDataKey, newData);
|
||||
```
|
||||
|
||||
#### 影响范围
|
||||
|
||||
- Tool C: 简化现有代码
|
||||
- ASL: 文献解析结果缓存(提升99%)
|
||||
- Tool B: 数据提取结果缓存
|
||||
- 所有模块: 统一的缓存机制
|
||||
|
||||
---
|
||||
|
||||
### TD-COMMON-003: 智能清洗算法通用化
|
||||
|
||||
**优先级**:⭐⭐ P2(中)
|
||||
**工作量**:0.5天
|
||||
**预期收益**:代码复用,避免重复实现
|
||||
|
||||
#### 问题描述
|
||||
|
||||
**当前状态**:
|
||||
- Tool C 实现:`intelligentCleanData`(边界检测+安全阀)
|
||||
- 其他模块:未实现类似功能
|
||||
|
||||
**可以通用化的算法**:
|
||||
1. 幽灵列检测(边界检测)
|
||||
2. 幽灵行过滤
|
||||
3. 安全阀(最大列数、单元格数限制)
|
||||
|
||||
#### 解决方案
|
||||
|
||||
```typescript
|
||||
// backend/src/common/utils/dataCleaningUtils.ts(新建)
|
||||
|
||||
export interface CleaningOptions {
|
||||
maxCols?: number; // 最大列数,默认3000
|
||||
maxCells?: number; // 最大单元格数,默认500万
|
||||
removeEmptyRows?: boolean; // 是否删除空行,默认true
|
||||
removeEmptyCols?: boolean; // 是否删除空列,默认true
|
||||
}
|
||||
|
||||
export function intelligentCleanData(
|
||||
data: any[],
|
||||
options: CleaningOptions = {}
|
||||
): any[] {
|
||||
// 实现边界检测、幽灵列/行清洗、安全阀
|
||||
// ...
|
||||
}
|
||||
|
||||
export function isValidValue(value: any): boolean {
|
||||
// 统一的空值判断
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
#### 影响范围
|
||||
|
||||
- Tool C: 复用通用实现
|
||||
- 其他上传Excel的功能: 直接使用
|
||||
|
||||
---
|
||||
|
||||
### TD-COMMON-004: Worker注册辅助工具
|
||||
|
||||
**优先级**:⭐ P3(低)
|
||||
**工作量**:0.3天
|
||||
**预期收益**:降低Worker注册代码重复
|
||||
|
||||
#### 问题描述
|
||||
|
||||
**当前状态**:
|
||||
- 每个模块都要手写 Worker 注册代码
|
||||
- 错误处理逻辑重复
|
||||
|
||||
#### 解决方案
|
||||
|
||||
```typescript
|
||||
// backend/src/common/jobs/WorkerHelper.ts(新建)
|
||||
|
||||
import { jobQueue } from './index';
|
||||
import { logger } from '../logging';
|
||||
import type { JobHandler } from './types';
|
||||
|
||||
interface RegisterWorkerOptions {
|
||||
queueName: string;
|
||||
handler: JobHandler;
|
||||
description?: string;
|
||||
onStart?: (job: any) => void;
|
||||
onComplete?: (job: any, result: any) => void;
|
||||
onError?: (job: any, error: any) => void;
|
||||
}
|
||||
|
||||
export function registerWorker(options: RegisterWorkerOptions) {
|
||||
const { queueName, handler, description } = options;
|
||||
|
||||
logger.info(`[WorkerHelper] Registering worker: ${queueName}`, { description });
|
||||
|
||||
jobQueue.process(queueName, async (job) => {
|
||||
try {
|
||||
options.onStart?.(job);
|
||||
|
||||
const result = await handler(job);
|
||||
|
||||
options.onComplete?.(job, result);
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
options.onError?.(job, error);
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
logger.info(`[WorkerHelper] ✅ Worker registered: ${queueName}`);
|
||||
}
|
||||
```
|
||||
|
||||
**使用示例**:
|
||||
|
||||
```typescript
|
||||
// 简化的注册代码
|
||||
registerWorker({
|
||||
queueName: 'dc_toolc_parse_excel',
|
||||
description: 'Excel解析Worker',
|
||||
handler: async (job) => {
|
||||
// 业务逻辑
|
||||
return result;
|
||||
},
|
||||
onStart: (job) => console.log(`开始处理: ${job.id}`),
|
||||
onComplete: (job, result) => console.log(`完成: ${result}`),
|
||||
onError: (job, error) => console.error(`失败: ${error}`),
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### TD-COMMON-005: 数据版本管理系统
|
||||
|
||||
**优先级**:⭐⭐⭐⭐ P1(高)
|
||||
**工作量**:3天
|
||||
**预期收益**:支持链式操作、undo功能、导出历史版本
|
||||
|
||||
#### 问题描述
|
||||
|
||||
**当前限制**:
|
||||
- 操作不是累积的(每次基于原始数据)
|
||||
- 无法回退到某个操作前的状态
|
||||
- 无法导出中间版本的数据
|
||||
|
||||
**用户期望的工作流**:
|
||||
```
|
||||
上传(v0: 100行)
|
||||
↓
|
||||
筛选(v1: 50行)← 可以回退到这里
|
||||
↓
|
||||
数值映射(v2: 50行,有映射)← 可以回退到这里
|
||||
↓
|
||||
Pivot(v3: 不同结构)
|
||||
↓
|
||||
导出:可以导出 v0、v1、v2、v3 任意版本
|
||||
```
|
||||
|
||||
#### 解决方案
|
||||
|
||||
**Prisma Schema设计**:
|
||||
|
||||
```prisma
|
||||
// 版本管理表(通用)
|
||||
model DataVersion {
|
||||
id String @id @default(uuid())
|
||||
|
||||
// 关联信息(多态关联)
|
||||
entityType String // 'dc_toolc_session' | 'asl_project' | ...
|
||||
entityId String // Session ID | Project ID | ...
|
||||
|
||||
// 版本信息
|
||||
versionNumber Int // 0=原始, 1=第1次操作后, 2=第2次操作后...
|
||||
dataKey String // OSS中的数据文件key
|
||||
|
||||
// 操作记录
|
||||
operation String? // 'upload' | 'filter' | 'pivot' | 'recode' ...
|
||||
operationParams Json? // 操作参数
|
||||
|
||||
// 元数据
|
||||
totalRows Int
|
||||
totalCols Int
|
||||
columns Json
|
||||
|
||||
// 时间戳
|
||||
createdAt DateTime @default(now())
|
||||
createdBy String // 用户ID
|
||||
|
||||
@@unique([entityType, entityId, versionNumber])
|
||||
@@index([entityType, entityId])
|
||||
@@map("data_versions")
|
||||
@@schema("platform_schema")
|
||||
}
|
||||
|
||||
// 业务表添加字段
|
||||
model DcToolCSession {
|
||||
// ...现有字段
|
||||
|
||||
currentVersion Int @default(0) // 当前版本号
|
||||
}
|
||||
```
|
||||
|
||||
**Service实现**:
|
||||
|
||||
```typescript
|
||||
// backend/src/common/services/DataVersionService.ts(新建)
|
||||
|
||||
export class DataVersionService {
|
||||
/**
|
||||
* 创建新版本
|
||||
*/
|
||||
async createVersion(
|
||||
entityType: string,
|
||||
entityId: string,
|
||||
versionNumber: number,
|
||||
data: any[],
|
||||
operation?: string,
|
||||
params?: any
|
||||
): Promise<string> {
|
||||
// 保存数据到 OSS
|
||||
const dataKey = `versions/${entityType}/${entityId}/v${versionNumber}.json`;
|
||||
await storage.upload(dataKey, JSON.stringify(data));
|
||||
|
||||
// 创建版本记录
|
||||
await prisma.dataVersion.create({
|
||||
data: {
|
||||
entityType,
|
||||
entityId,
|
||||
versionNumber,
|
||||
dataKey,
|
||||
operation,
|
||||
operationParams: params,
|
||||
totalRows: data.length,
|
||||
columns: Object.keys(data[0] || {}),
|
||||
createdBy: userId,
|
||||
}
|
||||
});
|
||||
|
||||
return dataKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取指定版本
|
||||
*/
|
||||
async getVersion(
|
||||
entityType: string,
|
||||
entityId: string,
|
||||
versionNumber: number
|
||||
): Promise<any[]> {
|
||||
const version = await prisma.dataVersion.findUnique({
|
||||
where: {
|
||||
entityType_entityId_versionNumber: {
|
||||
entityType,
|
||||
entityId,
|
||||
versionNumber,
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (!version) throw new Error('版本不存在');
|
||||
|
||||
const buffer = await storage.download(version.dataKey);
|
||||
return JSON.parse(buffer.toString('utf-8'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 回退到指定版本
|
||||
*/
|
||||
async rollbackToVersion(
|
||||
entityType: string,
|
||||
entityId: string,
|
||||
versionNumber: number
|
||||
): Promise<void> {
|
||||
// 更新当前版本号
|
||||
await this.updateCurrentVersion(entityType, entityId, versionNumber);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取版本历史
|
||||
*/
|
||||
async listVersions(entityType: string, entityId: string) {
|
||||
return await prisma.dataVersion.findMany({
|
||||
where: { entityType, entityId },
|
||||
orderBy: { versionNumber: 'asc' }
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**QuickAction 集成**:
|
||||
|
||||
```typescript
|
||||
// QuickAction 执行后自动创建新版本
|
||||
const result = await python.execute(fullData, params);
|
||||
|
||||
// 读取当前版本号
|
||||
const currentVersion = session.currentVersion || 0;
|
||||
|
||||
// 创建新版本
|
||||
await dataVersionService.createVersion(
|
||||
'dc_toolc_session',
|
||||
sessionId,
|
||||
currentVersion + 1,
|
||||
result.data,
|
||||
action, // 'filter' | 'pivot' | ...
|
||||
params
|
||||
);
|
||||
|
||||
// 更新当前版本号
|
||||
await prisma.update({
|
||||
where: { id: sessionId },
|
||||
data: { currentVersion: currentVersion + 1 }
|
||||
});
|
||||
```
|
||||
|
||||
#### 影响范围
|
||||
|
||||
- Tool C: 支持链式操作、undo、导出历史
|
||||
- ASL: 文献筛选的多阶段结果管理
|
||||
- Tool B: 数据提取的版本管理
|
||||
- 所有模块: 统一的版本管理能力
|
||||
|
||||
---
|
||||
|
||||
### TD-COMMON-006: 幽灵列/行检测算法库
|
||||
|
||||
**优先级**:⭐⭐ P2(中)
|
||||
**工作量**:0.5天
|
||||
**预期收益**:Excel处理质量提升
|
||||
|
||||
#### 问题描述
|
||||
|
||||
**当前状态**:
|
||||
- Tool C 实现:边界检测算法
|
||||
- 其他模块:未实现
|
||||
|
||||
**Excel格式污染问题**:
|
||||
- 用户刷颜色到16384列 → 解析出16384列(实际只有151列有效)
|
||||
- 用户删除数据未清理 → 解析出大量空行
|
||||
|
||||
#### 解决方案
|
||||
|
||||
```typescript
|
||||
// backend/src/common/utils/excelCleaning.ts(新建)
|
||||
|
||||
export interface CleaningResult {
|
||||
cleanedData: any[];
|
||||
originalRows: number;
|
||||
originalCols: number;
|
||||
finalRows: number;
|
||||
finalCols: number;
|
||||
removedRows: number;
|
||||
removedCols: number;
|
||||
warnings: string[];
|
||||
}
|
||||
|
||||
export function cleanExcelData(
|
||||
rawData: any[],
|
||||
options?: CleaningOptions
|
||||
): CleaningResult {
|
||||
// 实现通用的清洗算法
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### TD-COMMON-007: 前端进度条组件
|
||||
|
||||
**优先级**:⭐⭐ P2(中)
|
||||
**工作量**:0.3天
|
||||
**预期收益**:UI统一,用户体验提升
|
||||
|
||||
#### 问题描述
|
||||
|
||||
**当前状态**:
|
||||
- Tool C 实现:内联进度条(蓝色,Header下方)
|
||||
- 其他模块:未实现或不统一
|
||||
|
||||
#### 解决方案
|
||||
|
||||
```typescript
|
||||
// frontend-v2/src/common/components/AsyncProgressBar.tsx(新建)
|
||||
|
||||
interface AsyncProgressBarProps {
|
||||
visible: boolean;
|
||||
progress: number; // 0-100
|
||||
message: string;
|
||||
status: 'uploading' | 'processing' | 'completed' | 'error';
|
||||
}
|
||||
|
||||
export const AsyncProgressBar: React.FC<AsyncProgressBarProps> = ({
|
||||
visible,
|
||||
progress,
|
||||
message,
|
||||
status,
|
||||
}) => {
|
||||
if (!visible) return null;
|
||||
|
||||
return (
|
||||
<div className="bg-blue-50 border-b border-blue-200 px-6 py-3">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<span className="text-sm font-medium text-blue-900">{message}</span>
|
||||
<span className="text-sm text-blue-700">{progress}%</span>
|
||||
</div>
|
||||
<div className="w-full bg-blue-200 rounded-full h-2">
|
||||
<div
|
||||
className="bg-blue-600 h-2 rounded-full transition-all duration-300"
|
||||
style={{ width: `${progress}%` }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📅 实施计划
|
||||
|
||||
| 技术债务 | 优先级 | 工作量 | 建议时机 | 收益 |
|
||||
|---------|--------|--------|---------|------|
|
||||
| TD-COMMON-001 前端轮询Hook | P2 | 0.5天 | 下次迭代 | 代码统一 |
|
||||
| TD-COMMON-002 Clean Data服务 | **P1** | 1天 | **下次迭代** | **性能提升99%** |
|
||||
| TD-COMMON-003 智能清洗算法 | P2 | 0.5天 | 需要时 | 质量提升 |
|
||||
| TD-COMMON-004 Worker注册辅助 | P3 | 0.3天 | 可选 | 代码简化 |
|
||||
| TD-COMMON-005 数据版本管理 | **P1** | 3天 | **下次迭代** | **链式操作** |
|
||||
| TD-COMMON-006 幽灵列/行检测 | P2 | 0.5天 | 需要时 | 质量提升 |
|
||||
| TD-COMMON-007 进度条组件 | P2 | 0.3天 | 需要时 | UI统一 |
|
||||
|
||||
**推荐优先级**:
|
||||
1. ⭐⭐⭐⭐⭐ TD-COMMON-002(Clean Data服务)
|
||||
2. ⭐⭐⭐⭐⭐ TD-COMMON-005(数据版本管理)
|
||||
3. ⭐⭐⭐ TD-COMMON-001(前端轮询Hook)
|
||||
|
||||
---
|
||||
|
||||
## 📊 当前架构评价
|
||||
|
||||
### ✅ 已经很好的部分
|
||||
|
||||
1. ✅ **通用能力层基础设施完整**
|
||||
- jobQueue(pg-boss队列)
|
||||
- CheckpointService(断点续传)
|
||||
- 任务拆分工具
|
||||
- storage、logger、cache
|
||||
|
||||
2. ✅ **业务模块正确使用通用能力**
|
||||
- Tool C、ASL、Tool B 统一使用 jobQueue
|
||||
- Platform-Only模式
|
||||
- 不在业务表中存储任务管理信息
|
||||
|
||||
3. ✅ **分层清晰,职责明确**
|
||||
- 通用能力层:基础设施
|
||||
- 业务模块:具体实现
|
||||
|
||||
### ⏭️ 可以改进的部分(非必需)
|
||||
|
||||
1. ⏭️ 前端轮询模式可以更统一
|
||||
2. ⏭️ clean data缓存可以更通用
|
||||
3. ⏭️ 数据版本管理待建立
|
||||
|
||||
**结论**:当前架构已经符合规范,未来优化是锦上添花!
|
||||
|
||||
---
|
||||
|
||||
## 📚 参考文档
|
||||
|
||||
- [Postgres-Only异步任务处理指南](./Postgres-Only异步任务处理指南.md) - 完整实践
|
||||
- [云原生开发规范](../04-开发规范/08-云原生开发规范.md) - 开发规范
|
||||
- [DC Tool C状态](../03-业务模块/DC-数据清洗整理/00-工具C当前状态与开发指南.md) - Day 10实践
|
||||
|
||||
---
|
||||
|
||||
**维护者**: 平台架构团队
|
||||
**最后更新**: 2025-12-22
|
||||
**文档状态**: ✅ 初始版本
|
||||
|
||||
Reference in New Issue
Block a user