refactor: 统一ID生成方式,使用新的idGenerator模块 refactor: 重构模型ID生成逻辑,允许空ID并在创建前自动生成 fix(roomSchema): 使机房ID字段变为可选并更新前端表单验证 style: 格式化代码并优化导入语句 test: 添加操作日志集成测试文件 docs: 添加错误处理模块文档
176 lines
4.1 KiB
JavaScript
176 lines
4.1 KiB
JavaScript
const { EventEmitter } = require('events');
|
|
const path = require('path');
|
|
const fs = require('fs');
|
|
|
|
class ImportJobManager extends EventEmitter {
|
|
constructor() {
|
|
super();
|
|
this.jobs = new Map();
|
|
this.JOB_STATUS = {
|
|
PENDING: 'pending',
|
|
PROCESSING: 'processing',
|
|
COMPLETED: 'completed',
|
|
FAILED: 'failed',
|
|
CANCELLED: 'cancelled',
|
|
};
|
|
}
|
|
|
|
createJob(jobId, type, totalItems) {
|
|
const job = {
|
|
jobId,
|
|
type,
|
|
status: this.JOB_STATUS.PENDING,
|
|
totalItems,
|
|
processedItems: 0,
|
|
successCount: 0,
|
|
failedCount: 0,
|
|
skippedCount: 0,
|
|
updatedCount: 0,
|
|
startTime: null,
|
|
endTime: null,
|
|
error: null,
|
|
canCancel: true,
|
|
result: null,
|
|
createdAt: new Date(),
|
|
};
|
|
this.jobs.set(jobId, job);
|
|
return job;
|
|
}
|
|
|
|
getJob(jobId) {
|
|
return this.jobs.get(jobId);
|
|
}
|
|
|
|
updateJob(jobId, updates) {
|
|
const job = this.jobs.get(jobId);
|
|
if (!job) return null;
|
|
|
|
Object.assign(job, updates);
|
|
this.emit('jobUpdate', job);
|
|
return job;
|
|
}
|
|
|
|
startJob(jobId) {
|
|
const job = this.jobs.get(jobId);
|
|
if (!job) return null;
|
|
|
|
job.status = this.JOB_STATUS.PROCESSING;
|
|
job.startTime = new Date();
|
|
this.emit('jobStart', job);
|
|
return job;
|
|
}
|
|
|
|
incrementProgress(jobId, success = 0, failed = 0, skipped = 0, updated = 0) {
|
|
const job = this.jobs.get(jobId);
|
|
if (!job) return null;
|
|
|
|
job.processedItems += success + failed + skipped + updated;
|
|
job.successCount += success;
|
|
job.failedCount += failed;
|
|
job.skippedCount += skipped;
|
|
job.updatedCount += updated;
|
|
this.emit('jobProgress', job);
|
|
return job;
|
|
}
|
|
|
|
completeJob(jobId, result = null) {
|
|
const job = this.jobs.get(jobId);
|
|
if (!job) return null;
|
|
|
|
job.status = this.JOB_STATUS.COMPLETED;
|
|
job.endTime = new Date();
|
|
job.canCancel = false;
|
|
job.result = result;
|
|
this.emit('jobComplete', job);
|
|
return job;
|
|
}
|
|
|
|
failJob(jobId, error) {
|
|
const job = this.jobs.get(jobId);
|
|
if (!job) return null;
|
|
|
|
job.status = this.JOB_STATUS.FAILED;
|
|
job.endTime = new Date();
|
|
job.canCancel = false;
|
|
job.error = error;
|
|
this.emit('jobFailed', job);
|
|
return job;
|
|
}
|
|
|
|
cancelJob(jobId) {
|
|
const job = this.jobs.get(jobId);
|
|
if (!job || !job.canCancel) return null;
|
|
|
|
job.status = this.JOB_STATUS.CANCELLED;
|
|
job.endTime = new Date();
|
|
job.canCancel = false;
|
|
this.emit('jobCancelled', job);
|
|
return job;
|
|
}
|
|
|
|
getJobProgress(jobId) {
|
|
const job = this.jobs.get(jobId);
|
|
if (!job) return null;
|
|
|
|
const progress = {
|
|
jobId: job.jobId,
|
|
status: job.status,
|
|
totalItems: job.totalItems,
|
|
processedItems: job.processedItems,
|
|
progressPercent:
|
|
job.totalItems > 0 ? Math.round((job.processedItems / job.totalItems) * 100) : 0,
|
|
successCount: job.successCount,
|
|
failedCount: job.failedCount,
|
|
skippedCount: job.skippedCount,
|
|
updatedCount: job.updatedCount,
|
|
canCancel: job.canCancel,
|
|
error: job.error,
|
|
startTime: job.startTime,
|
|
endTime: job.endTime,
|
|
elapsedTime: job.startTime ? Date.now() - new Date(job.startTime).getTime() : null,
|
|
};
|
|
|
|
return progress;
|
|
}
|
|
|
|
listJobs() {
|
|
return Array.from(this.jobs.values()).map(job => ({
|
|
jobId: job.jobId,
|
|
type: job.type,
|
|
status: job.status,
|
|
totalItems: job.totalItems,
|
|
processedItems: job.processedItems,
|
|
progressPercent:
|
|
job.totalItems > 0 ? Math.round((job.processedItems / job.totalItems) * 100) : 0,
|
|
canCancel: job.canCancel,
|
|
startTime: job.startTime,
|
|
endTime: job.endTime,
|
|
createdAt: job.createdAt,
|
|
}));
|
|
}
|
|
|
|
cleanupOldJobs(maxAgeMs = 24 * 60 * 60 * 1000) {
|
|
const now = Date.now();
|
|
for (const [jobId, job] of this.jobs.entries()) {
|
|
const jobEndTime = job.endTime || job.createdAt;
|
|
if (now - new Date(jobEndTime).getTime() > maxAgeMs) {
|
|
this.jobs.delete(jobId);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const importJobManager = new ImportJobManager();
|
|
|
|
setInterval(
|
|
() => {
|
|
importJobManager.cleanupOldJobs();
|
|
},
|
|
60 * 60 * 1000
|
|
);
|
|
|
|
module.exports = {
|
|
importJobManager,
|
|
ImportJobManager,
|
|
};
|