develop (#68)
* docs(AGENTS.md): 添加 Git Commit 必须使用中文描述的规范 * docs: 更新测试数据生成器功能设计文档 * refactor: 删除死代码 * docs: 统一测试数据生成器文档,移除 SQL/TypeScript 导出,修复接口命名不一致 * feat: 实现测试数据生成器功能 * docs: 更新 README 和 AGENTS 文档,补充测试数据生成器模块说明
This commit is contained in:
@@ -0,0 +1,165 @@
|
||||
/**
|
||||
* 测试数据生成器 Web Worker
|
||||
* 在后台线程中执行数据生成,避免阻塞 UI
|
||||
*/
|
||||
|
||||
import { getGeneratorById } from '@/lib/generators';
|
||||
import type {
|
||||
FieldConfig,
|
||||
WorkerMessage,
|
||||
GenerateResult,
|
||||
GenerateProgress,
|
||||
} from '@/types/testDataGenerator';
|
||||
|
||||
// 生成结果缓存
|
||||
let generatedData: Record<string, unknown>[] = [];
|
||||
let isCancelled = false;
|
||||
|
||||
/**
|
||||
* Worker 消息处理器
|
||||
*/
|
||||
self.onmessage = async (e: MessageEvent<WorkerMessage>) => {
|
||||
const data = e.data;
|
||||
const { type } = data;
|
||||
|
||||
switch (type) {
|
||||
case 'start':
|
||||
isCancelled = false;
|
||||
await handleStart(data.payload);
|
||||
break;
|
||||
case 'cancel':
|
||||
isCancelled = true;
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 处理开始生成消息
|
||||
*/
|
||||
async function handleStart(payload: {
|
||||
fields: FieldConfig[];
|
||||
count: number;
|
||||
csvMode: boolean;
|
||||
}): Promise<void> {
|
||||
const { fields, count } = payload;
|
||||
generatedData = [];
|
||||
|
||||
try {
|
||||
// 验证所有生成器是否存在
|
||||
for (const field of fields) {
|
||||
const generator = getGeneratorById(field.generatorId);
|
||||
if (!generator) {
|
||||
self.postMessage({
|
||||
type: 'error',
|
||||
payload: { error: `生成器 "${field.generatorId}" 不存在` },
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const startTime = Date.now();
|
||||
const warnings: string[] = [];
|
||||
|
||||
// 生成数据
|
||||
for (let i = 0; i < count; i++) {
|
||||
if (isCancelled) {
|
||||
self.postMessage({
|
||||
type: 'complete',
|
||||
payload: {
|
||||
success: false,
|
||||
error: '生成已取消',
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const record: Record<string, unknown> = {};
|
||||
|
||||
for (const field of fields) {
|
||||
const generator = getGeneratorById(field.generatorId);
|
||||
if (!generator) continue;
|
||||
|
||||
// 空值率控制
|
||||
if (!field.required && Math.random() * 100 < field.nullRate) {
|
||||
record[field.name] = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
// 唯一性约束处理
|
||||
if (field.unique) {
|
||||
let value: string | undefined;
|
||||
const maxRetries = 100;
|
||||
let retryCount = 0;
|
||||
|
||||
// 小数据量:随机生成 + 重试
|
||||
if (count <= 1000) {
|
||||
while (retryCount < maxRetries) {
|
||||
value = generator.generate(field.params);
|
||||
const isUnique = !generatedData.some((item) => item[field.name] === value);
|
||||
if (isUnique) break;
|
||||
retryCount++;
|
||||
value = undefined;
|
||||
}
|
||||
} else {
|
||||
// 大数据量:使用索引生成策略
|
||||
if (generator.generateAtIndex) {
|
||||
value = generator.generateAtIndex(field.params, i);
|
||||
} else {
|
||||
// 回退到随机生成
|
||||
value = generator.generate(field.params);
|
||||
}
|
||||
}
|
||||
|
||||
if (value !== undefined) {
|
||||
record[field.name] = value;
|
||||
} else {
|
||||
warnings.push(`字段 "${field.name}" 第 ${i + 1} 行唯一值生成失败`);
|
||||
record[field.name] = generator.generate(field.params);
|
||||
}
|
||||
} else {
|
||||
record[field.name] = generator.generate(field.params);
|
||||
}
|
||||
} catch (error) {
|
||||
warnings.push(`字段 "${field.name}" 第 ${i + 1} 行生成错误: ${error}`);
|
||||
record[field.name] = null;
|
||||
}
|
||||
}
|
||||
|
||||
generatedData.push(record);
|
||||
|
||||
// 进度回调(每1000条)
|
||||
if ((i + 1) % 1000 === 0 || i === count - 1) {
|
||||
const progress: GenerateProgress = {
|
||||
progress: Math.round(((i + 1) / count) * 100),
|
||||
generated: i + 1,
|
||||
total: count,
|
||||
estimatedTimeLeft: Math.round(((Date.now() - startTime) / (i + 1)) * (count - i - 1)),
|
||||
};
|
||||
self.postMessage({ type: 'progress', payload: progress });
|
||||
}
|
||||
}
|
||||
|
||||
const duration = Date.now() - startTime;
|
||||
const successCount = generatedData.filter((item) => Object.keys(item).length > 0).length;
|
||||
|
||||
const result: GenerateResult = {
|
||||
success: true,
|
||||
data: generatedData,
|
||||
warnings: warnings.length > 0 ? warnings : undefined,
|
||||
stats: {
|
||||
total: count,
|
||||
success: successCount,
|
||||
failed: count - successCount,
|
||||
duration,
|
||||
},
|
||||
};
|
||||
|
||||
self.postMessage({ type: 'complete', payload: result });
|
||||
} catch (error) {
|
||||
self.postMessage({
|
||||
type: 'error',
|
||||
payload: { error: `生成失败: ${error}` },
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user