248 lines
6.5 KiB
JavaScript
248 lines
6.5 KiB
JavaScript
/**
|
||
* core/task-queue — 任务队列系统
|
||
*
|
||
* 任务结构:
|
||
* task_id — 唯一任务标识
|
||
* source — 来源(broadcast / maintenance / dev / auto)
|
||
* type — 类型(system-task / dev-task / maintenance-task / auto-task)
|
||
* priority — 优先级(high / normal / low)
|
||
* status — 状态(pending / running / completed / failed)
|
||
* executor — 执行者(zhuyuan)
|
||
*
|
||
* 任务类型:
|
||
* system-task — 系统级任务(广播、状态同步)
|
||
* dev-task — 开发任务(代码变更、功能开发)
|
||
* maintenance-task — 维护任务(结构修复、文档更新)
|
||
* auto-task — 自动生成任务(自检发现的问题)
|
||
*
|
||
* 任务来源:
|
||
* - Notion 广播
|
||
* - 系统维护任务
|
||
* - 开发任务
|
||
* - 自动开发循环生成
|
||
*
|
||
* 执行流程:
|
||
* 任务进入队列 → 按优先级去重 → 执行器运行 → 执行结果写回
|
||
*
|
||
* 调用方式:
|
||
* node core/task-queue [status|run|cleanup]
|
||
*/
|
||
|
||
const fs = require('fs');
|
||
const path = require('path');
|
||
|
||
const ROOT = path.resolve(__dirname, '../..');
|
||
const QUEUE_PATH = path.join(ROOT, 'core/task-queue/queue.json');
|
||
|
||
/**
|
||
* 加载当前队列
|
||
*/
|
||
function loadQueue() {
|
||
if (!fs.existsSync(QUEUE_PATH)) {
|
||
return { version: '5.1', tasks: [], last_updated: null };
|
||
}
|
||
try {
|
||
return JSON.parse(fs.readFileSync(QUEUE_PATH, 'utf-8'));
|
||
} catch {
|
||
return { version: '5.1', tasks: [], last_updated: null };
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 保存队列
|
||
*/
|
||
function saveQueue(queue) {
|
||
queue.last_updated = new Date().toISOString();
|
||
fs.writeFileSync(QUEUE_PATH, JSON.stringify(queue, null, 2), 'utf-8');
|
||
}
|
||
|
||
/**
|
||
* 添加任务到队列
|
||
*/
|
||
function enqueue(task) {
|
||
const queue = loadQueue();
|
||
|
||
// 去重:相同 task_id 只保留最新
|
||
const existing = queue.tasks.findIndex(t => t.task_id === task.task_id);
|
||
if (existing !== -1) {
|
||
const original = queue.tasks[existing];
|
||
queue.tasks[existing] = {
|
||
...original,
|
||
...task,
|
||
queued_at: original.queued_at,
|
||
updated_at: new Date().toISOString()
|
||
};
|
||
console.log(`🔄 更新已有任务: ${task.task_id}`);
|
||
} else {
|
||
queue.tasks.push({
|
||
...task,
|
||
queued_at: new Date().toISOString()
|
||
});
|
||
console.log(`➕ 新增任务: ${task.task_id}`);
|
||
}
|
||
|
||
saveQueue(queue);
|
||
return task;
|
||
}
|
||
|
||
/**
|
||
* 批量入队
|
||
*/
|
||
function enqueueBatch(tasks) {
|
||
return tasks.map(t => enqueue(t));
|
||
}
|
||
|
||
/**
|
||
* 获取下一个待执行任务(按优先级)
|
||
*/
|
||
function dequeue() {
|
||
const queue = loadQueue();
|
||
const priorityOrder = { high: 0, normal: 1, low: 2 };
|
||
|
||
const pending = queue.tasks
|
||
.filter(t => t.status === 'pending')
|
||
.sort((a, b) => (priorityOrder[a.priority] || 1) - (priorityOrder[b.priority] || 1));
|
||
|
||
if (pending.length === 0) return null;
|
||
|
||
const task = pending[0];
|
||
task.status = 'running';
|
||
task.started_at = new Date().toISOString();
|
||
saveQueue(queue);
|
||
|
||
return task;
|
||
}
|
||
|
||
/**
|
||
* 标记任务完成
|
||
*/
|
||
function complete(taskId, result = null) {
|
||
const queue = loadQueue();
|
||
const task = queue.tasks.find(t => t.task_id === taskId);
|
||
|
||
if (!task) {
|
||
console.warn(`⚠️ 任务不存在: ${taskId}`);
|
||
return null;
|
||
}
|
||
|
||
task.status = 'completed';
|
||
task.completed_at = new Date().toISOString();
|
||
if (result) task.result = result;
|
||
|
||
saveQueue(queue);
|
||
console.log(`✅ 任务完成: ${taskId}`);
|
||
return task;
|
||
}
|
||
|
||
/**
|
||
* 标记任务失败
|
||
*/
|
||
function fail(taskId, error = null) {
|
||
const queue = loadQueue();
|
||
const task = queue.tasks.find(t => t.task_id === taskId);
|
||
|
||
if (!task) {
|
||
console.warn(`⚠️ 任务不存在: ${taskId}`);
|
||
return null;
|
||
}
|
||
|
||
task.status = 'failed';
|
||
task.failed_at = new Date().toISOString();
|
||
if (error) task.error = String(error);
|
||
|
||
saveQueue(queue);
|
||
console.log(`❌ 任务失败: ${taskId}`);
|
||
return task;
|
||
}
|
||
|
||
/**
|
||
* 获取队列状态
|
||
*/
|
||
function status() {
|
||
const queue = loadQueue();
|
||
const tasks = queue.tasks;
|
||
|
||
const stats = {
|
||
total: tasks.length,
|
||
pending: tasks.filter(t => t.status === 'pending').length,
|
||
running: tasks.filter(t => t.status === 'running').length,
|
||
completed: tasks.filter(t => t.status === 'completed').length,
|
||
failed: tasks.filter(t => t.status === 'failed').length,
|
||
last_updated: queue.last_updated,
|
||
by_type: {
|
||
'system-task': tasks.filter(t => t.type === 'system-task').length,
|
||
'dev-task': tasks.filter(t => t.type === 'dev-task').length,
|
||
'maintenance-task': tasks.filter(t => t.type === 'maintenance-task').length,
|
||
'auto-task': tasks.filter(t => t.type === 'auto-task').length,
|
||
'glada-task': tasks.filter(t => t.type === 'glada-task').length
|
||
}
|
||
};
|
||
|
||
return stats;
|
||
}
|
||
|
||
/**
|
||
* 清理已完成任务(保留最近 50 条)
|
||
*/
|
||
function cleanup() {
|
||
const queue = loadQueue();
|
||
const completed = queue.tasks.filter(t =>
|
||
t.status === 'completed' || t.status === 'failed'
|
||
);
|
||
|
||
if (completed.length > 50) {
|
||
const keep = completed
|
||
.sort((a, b) => (b.completed_at || b.failed_at || '').localeCompare(a.completed_at || a.failed_at || ''))
|
||
.slice(0, 50);
|
||
const keepIds = new Set(keep.map(t => t.task_id));
|
||
const active = queue.tasks.filter(t =>
|
||
t.status === 'pending' || t.status === 'running'
|
||
);
|
||
queue.tasks = [...active, ...keep];
|
||
saveQueue(queue);
|
||
console.log(`🧹 清理完成,保留 ${queue.tasks.length} 条任务`);
|
||
}
|
||
}
|
||
|
||
// CLI 入口
|
||
if (require.main === module) {
|
||
const cmd = process.argv[2] || 'status';
|
||
|
||
switch (cmd) {
|
||
case 'status': {
|
||
const s = status();
|
||
console.log('📊 任务队列状态:');
|
||
console.log(` 总计: ${s.total}`);
|
||
console.log(` 待处理: ${s.pending}`);
|
||
console.log(` 执行中: ${s.running}`);
|
||
console.log(` 已完成: ${s.completed}`);
|
||
console.log(` 失败: ${s.failed}`);
|
||
console.log(` 更新时间: ${s.last_updated || '无'}`);
|
||
if (s.total > 0) {
|
||
console.log(' 任务类型分布:');
|
||
for (const [type, count] of Object.entries(s.by_type)) {
|
||
if (count > 0) console.log(` ${type}: ${count}`);
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
case 'run': {
|
||
const task = dequeue();
|
||
if (task) {
|
||
console.log(`🚀 取出任务: ${task.task_id}`);
|
||
} else {
|
||
console.log('✅ 队列为空,无待执行任务');
|
||
}
|
||
break;
|
||
}
|
||
case 'cleanup': {
|
||
cleanup();
|
||
break;
|
||
}
|
||
default:
|
||
console.log('用法: node core/task-queue [status|run|cleanup]');
|
||
}
|
||
}
|
||
|
||
module.exports = { loadQueue, enqueue, enqueueBatch, dequeue, complete, fail, status, cleanup };
|