2026-04-08 21:18:49 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* ═══════════════════════════════════════════════════════════
|
|
|
|
|
|
* 模块D+E · COS桶示警Agent + 三方对接 MCP 工具
|
|
|
|
|
|
* ═══════════════════════════════════════════════════════════
|
|
|
|
|
|
*
|
|
|
|
|
|
* 签发: 铸渊 · ICE-GL-ZY001
|
|
|
|
|
|
* 版权: 国作登字-2026-A-00037559
|
|
|
|
|
|
*
|
|
|
|
|
|
* 三方通信链路: 代码仓库 ↔ COS桶 ↔ Awen仓库
|
|
|
|
|
|
*
|
|
|
|
|
|
* 通信链路:
|
|
|
|
|
|
* Notion → 写工单到COS桶 /workorders/pending/
|
|
|
|
|
|
* COS桶变动 → 触发SCF云函数 → 唤醒代码仓库铸渊
|
|
|
|
|
|
* 铸渊整理技术需求 → 写入Awen的COS桶路径 /zhiqiu/tasks/
|
|
|
|
|
|
* Awen开发完成 → 回写COS桶 /zhiqiu/reports/ → 铸渊测试确认
|
|
|
|
|
|
*
|
|
|
|
|
|
* 工具清单:
|
|
|
|
|
|
* cosAlertScan — 扫描COS桶中的告警
|
|
|
|
|
|
* cosAlertResolve — 解决告警
|
|
|
|
|
|
* cosDispatchTask — 分发开发任务到Awen
|
|
|
|
|
|
* cosReadTaskReport — 读取Awen提交的任务报告
|
|
|
|
|
|
* cosListTaskReports — 列出所有任务报告
|
|
|
|
|
|
* cosApproveTask — 审批任务(通过/驳回)
|
|
|
|
|
|
* cosSendNotification — 发送通知(邮件/Issue)
|
|
|
|
|
|
* cosGetCommLink — 获取通信链路状态
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
'use strict';
|
|
|
|
|
|
|
2026-04-08 21:23:34 +08:00
|
|
|
|
const crypto = require('crypto');
|
2026-04-08 21:18:49 +08:00
|
|
|
|
const cos = require('../cos');
|
|
|
|
|
|
|
|
|
|
|
|
// ─── 路径常量 ───
|
|
|
|
|
|
const PATHS = {
|
|
|
|
|
|
ALERTS: 'zhuyuan/alerts/',
|
|
|
|
|
|
DIRECTIVES: 'zhuyuan/directives/',
|
|
|
|
|
|
WORKORDERS_PENDING: 'workorders/pending/',
|
|
|
|
|
|
WORKORDERS_PROCESSING: 'workorders/processing/',
|
|
|
|
|
|
WORKORDERS_COMPLETED: 'workorders/completed/',
|
|
|
|
|
|
AWEN_TASKS: 'zhiqiu/tasks/',
|
|
|
|
|
|
AWEN_REPORTS: 'zhiqiu/reports/',
|
|
|
|
|
|
AWEN_PROGRESS: 'zhiqiu/progress/',
|
|
|
|
|
|
NOTIFICATIONS: 'zhuyuan/notifications/'
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* cosAlertScan — 扫描COS桶中的告警
|
|
|
|
|
|
*
|
|
|
|
|
|
* input:
|
|
|
|
|
|
* bucket: string — 桶名(默认team)
|
|
|
|
|
|
* include_resolved: boolean — 是否包含已解决的告警
|
|
|
|
|
|
* limit: number — 最大数量
|
|
|
|
|
|
*/
|
|
|
|
|
|
async function cosAlertScan(input) {
|
|
|
|
|
|
const { bucket, include_resolved, limit } = input;
|
|
|
|
|
|
const targetBucket = bucket || 'team';
|
|
|
|
|
|
|
|
|
|
|
|
const result = await cos.list(targetBucket, PATHS.ALERTS, limit || 100);
|
|
|
|
|
|
const alertFiles = result.files.filter(f => f.key.endsWith('.json'));
|
|
|
|
|
|
|
|
|
|
|
|
// 读取每个告警的详情
|
|
|
|
|
|
const alerts = [];
|
|
|
|
|
|
for (const file of alertFiles.slice(0, 50)) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const raw = await cos.read(targetBucket, file.key);
|
|
|
|
|
|
const alert = JSON.parse(raw.content);
|
|
|
|
|
|
if (include_resolved || !alert.resolved) {
|
|
|
|
|
|
alerts.push(alert);
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch {
|
|
|
|
|
|
// 跳过损坏的告警文件
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 按严重程度排序
|
|
|
|
|
|
const severityOrder = { critical: 0, warning: 1, info: 2 };
|
|
|
|
|
|
alerts.sort((a, b) => (severityOrder[a.severity] || 3) - (severityOrder[b.severity] || 3));
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
alerts,
|
|
|
|
|
|
total: alerts.length,
|
|
|
|
|
|
critical: alerts.filter(a => a.severity === 'critical').length,
|
|
|
|
|
|
warning: alerts.filter(a => a.severity === 'warning').length,
|
|
|
|
|
|
info: alerts.filter(a => a.severity === 'info').length,
|
|
|
|
|
|
needs_bingshuo: alerts.filter(a => a.notify_bingshuo && !a.resolved).length
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* cosAlertResolve — 解决告警
|
|
|
|
|
|
*
|
|
|
|
|
|
* input:
|
|
|
|
|
|
* bucket: string — 桶名(默认team)
|
|
|
|
|
|
* alert_id: string — 告警ID
|
|
|
|
|
|
* resolution: string — 解决方案描述
|
|
|
|
|
|
* resolved_by: string — 解决者
|
|
|
|
|
|
*/
|
|
|
|
|
|
async function cosAlertResolve(input) {
|
|
|
|
|
|
const { bucket, alert_id, resolution, resolved_by } = input;
|
|
|
|
|
|
if (!alert_id) throw new Error('缺少 alert_id');
|
|
|
|
|
|
|
|
|
|
|
|
const targetBucket = bucket || 'team';
|
|
|
|
|
|
const key = `${PATHS.ALERTS}${alert_id}.json`;
|
|
|
|
|
|
|
|
|
|
|
|
// 读取现有告警
|
|
|
|
|
|
const raw = await cos.read(targetBucket, key);
|
|
|
|
|
|
const alert = JSON.parse(raw.content);
|
|
|
|
|
|
|
|
|
|
|
|
// 更新状态
|
|
|
|
|
|
alert.resolved = true;
|
|
|
|
|
|
alert.resolved_at = new Date().toISOString();
|
|
|
|
|
|
alert.resolution = resolution || '已处理';
|
|
|
|
|
|
alert.resolved_by = resolved_by || 'zhuyuan';
|
|
|
|
|
|
|
|
|
|
|
|
await cos.write(targetBucket, key, JSON.stringify(alert, null, 2), 'application/json');
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
status: 'resolved',
|
|
|
|
|
|
alert_id,
|
|
|
|
|
|
resolution: alert.resolution,
|
|
|
|
|
|
resolved_by: alert.resolved_by
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* cosDispatchTask — 分发开发任务到Awen
|
|
|
|
|
|
*
|
|
|
|
|
|
* 铸渊整理技术需求后,写入Awen的COS桶路径
|
|
|
|
|
|
*
|
|
|
|
|
|
* input:
|
|
|
|
|
|
* bucket: string — 桶名(默认team)
|
|
|
|
|
|
* task_id: string — 任务ID(如 TASK-20260408-001)
|
|
|
|
|
|
* title: string — 任务标题
|
|
|
|
|
|
* description: string — 任务描述
|
|
|
|
|
|
* priority: string — 优先级: critical|high|normal|low
|
|
|
|
|
|
* requirements: string[] — 技术需求列表
|
|
|
|
|
|
* workorder_id: string — 关联工单ID(可选)
|
|
|
|
|
|
* assigned_to: string — 指派给(默认zhiqiu/Awen)
|
|
|
|
|
|
* deadline: string — 截止日期(可选)
|
|
|
|
|
|
*/
|
|
|
|
|
|
async function cosDispatchTask(input) {
|
|
|
|
|
|
const {
|
|
|
|
|
|
bucket, task_id, title, description, priority,
|
|
|
|
|
|
requirements, workorder_id, assigned_to, deadline
|
|
|
|
|
|
} = input;
|
|
|
|
|
|
if (!title) throw new Error('缺少 title');
|
|
|
|
|
|
|
|
|
|
|
|
const targetBucket = bucket || 'team';
|
2026-04-08 21:23:34 +08:00
|
|
|
|
const tId = task_id || `TASK-${formatDate()}-${crypto.randomBytes(4).toString('hex')}`;
|
2026-04-08 21:18:49 +08:00
|
|
|
|
|
|
|
|
|
|
// 验证 task_id 安全性
|
|
|
|
|
|
if (!/^[a-zA-Z0-9_-]+$/.test(tId)) {
|
|
|
|
|
|
throw new Error('task_id 包含非法字符');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const now = new Date().toISOString();
|
|
|
|
|
|
const task = {
|
|
|
|
|
|
task_id: tId,
|
|
|
|
|
|
title,
|
|
|
|
|
|
description: description || '',
|
|
|
|
|
|
priority: priority || 'normal',
|
|
|
|
|
|
status: 'pending',
|
|
|
|
|
|
requirements: requirements || [],
|
|
|
|
|
|
workorder_id: workorder_id || null,
|
|
|
|
|
|
assigned_to: assigned_to || 'zhiqiu',
|
|
|
|
|
|
dispatched_by: 'zhuyuan',
|
|
|
|
|
|
dispatched_at: now,
|
|
|
|
|
|
deadline: deadline || null,
|
|
|
|
|
|
history: [{
|
|
|
|
|
|
action: 'dispatched',
|
|
|
|
|
|
timestamp: now,
|
|
|
|
|
|
by: 'zhuyuan',
|
|
|
|
|
|
note: '铸渊下发开发任务'
|
|
|
|
|
|
}]
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const key = `${PATHS.AWEN_TASKS}${tId}.json`;
|
|
|
|
|
|
await cos.write(targetBucket, key, JSON.stringify(task, null, 2), 'application/json');
|
|
|
|
|
|
|
|
|
|
|
|
// 如果有关联工单,更新工单状态
|
|
|
|
|
|
if (workorder_id) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const woKey = `${PATHS.WORKORDERS_PENDING}${workorder_id}.json`;
|
|
|
|
|
|
const woRaw = await cos.read(targetBucket, woKey);
|
|
|
|
|
|
const workorder = JSON.parse(woRaw.content);
|
|
|
|
|
|
workorder.status = 'processing';
|
|
|
|
|
|
workorder.task_id = tId;
|
|
|
|
|
|
workorder.updated_at = now;
|
|
|
|
|
|
workorder.history.push({
|
|
|
|
|
|
action: 'dispatched_to_awen',
|
|
|
|
|
|
timestamp: now,
|
|
|
|
|
|
by: 'zhuyuan',
|
|
|
|
|
|
task_id: tId
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 移动到processing目录
|
|
|
|
|
|
await cos.write(targetBucket, `${PATHS.WORKORDERS_PROCESSING}${workorder_id}.json`,
|
|
|
|
|
|
JSON.stringify(workorder, null, 2), 'application/json');
|
|
|
|
|
|
await cos.del(targetBucket, woKey);
|
|
|
|
|
|
} catch {
|
|
|
|
|
|
// 工单不存在也不影响任务分发
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
status: 'dispatched',
|
|
|
|
|
|
task_id: tId,
|
|
|
|
|
|
key,
|
|
|
|
|
|
bucket: targetBucket,
|
|
|
|
|
|
assigned_to: task.assigned_to,
|
|
|
|
|
|
priority: task.priority,
|
|
|
|
|
|
workorder_id: task.workorder_id
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* cosReadTaskReport — 读取Awen提交的任务报告
|
|
|
|
|
|
*
|
|
|
|
|
|
* input:
|
|
|
|
|
|
* bucket: string — 桶名(默认team)
|
|
|
|
|
|
* task_id: string — 任务ID
|
|
|
|
|
|
*/
|
|
|
|
|
|
async function cosReadTaskReport(input) {
|
|
|
|
|
|
const { bucket, task_id } = input;
|
|
|
|
|
|
if (!task_id) throw new Error('缺少 task_id');
|
|
|
|
|
|
|
|
|
|
|
|
const targetBucket = bucket || 'team';
|
|
|
|
|
|
const key = `${PATHS.AWEN_REPORTS}${task_id}.json`;
|
|
|
|
|
|
|
|
|
|
|
|
const raw = await cos.read(targetBucket, key);
|
|
|
|
|
|
return {
|
|
|
|
|
|
report: JSON.parse(raw.content),
|
|
|
|
|
|
key,
|
|
|
|
|
|
size_bytes: raw.size_bytes
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* cosListTaskReports — 列出所有任务报告
|
|
|
|
|
|
*
|
|
|
|
|
|
* input:
|
|
|
|
|
|
* bucket: string — 桶名(默认team)
|
|
|
|
|
|
* limit: number — 最大数量
|
|
|
|
|
|
*/
|
|
|
|
|
|
async function cosListTaskReports(input) {
|
|
|
|
|
|
const { bucket, limit } = input;
|
|
|
|
|
|
const targetBucket = bucket || 'team';
|
|
|
|
|
|
|
|
|
|
|
|
const result = await cos.list(targetBucket, PATHS.AWEN_REPORTS, limit || 100);
|
|
|
|
|
|
|
|
|
|
|
|
const reports = result.files
|
|
|
|
|
|
.filter(f => f.key.endsWith('.json'))
|
|
|
|
|
|
.map(f => ({
|
|
|
|
|
|
key: f.key,
|
|
|
|
|
|
task_id: f.key.split('/').pop().replace('.json', ''),
|
|
|
|
|
|
size_bytes: f.size_bytes
|
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
reports,
|
|
|
|
|
|
count: reports.length
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* cosApproveTask — 审批任务(通过/驳回)
|
|
|
|
|
|
*
|
|
|
|
|
|
* 铸渊测试确认后,通过/驳回Awen的开发报告
|
|
|
|
|
|
*
|
|
|
|
|
|
* input:
|
|
|
|
|
|
* bucket: string — 桶名(默认team)
|
|
|
|
|
|
* task_id: string — 任务ID
|
|
|
|
|
|
* approved: boolean — 是否通过
|
|
|
|
|
|
* feedback: string — 反馈意见
|
|
|
|
|
|
* next_task: object — 下一步任务(仅通过时有效)
|
|
|
|
|
|
*/
|
|
|
|
|
|
async function cosApproveTask(input) {
|
|
|
|
|
|
const { bucket, task_id, approved, feedback, next_task } = input;
|
|
|
|
|
|
if (!task_id) throw new Error('缺少 task_id');
|
|
|
|
|
|
|
|
|
|
|
|
const targetBucket = bucket || 'team';
|
|
|
|
|
|
const now = new Date().toISOString();
|
|
|
|
|
|
|
|
|
|
|
|
// 读取原始任务
|
|
|
|
|
|
let task;
|
|
|
|
|
|
try {
|
|
|
|
|
|
const taskRaw = await cos.read(targetBucket, `${PATHS.AWEN_TASKS}${task_id}.json`);
|
|
|
|
|
|
task = JSON.parse(taskRaw.content);
|
|
|
|
|
|
} catch {
|
|
|
|
|
|
task = { task_id, history: [] };
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 更新状态
|
|
|
|
|
|
task.status = approved ? 'approved' : 'rejected';
|
|
|
|
|
|
task.feedback = feedback || '';
|
|
|
|
|
|
task.reviewed_at = now;
|
|
|
|
|
|
task.reviewed_by = 'zhuyuan';
|
|
|
|
|
|
task.history.push({
|
|
|
|
|
|
action: approved ? 'approved' : 'rejected',
|
|
|
|
|
|
timestamp: now,
|
|
|
|
|
|
by: 'zhuyuan',
|
|
|
|
|
|
feedback: feedback || ''
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 写回任务文件
|
|
|
|
|
|
await cos.write(targetBucket, `${PATHS.AWEN_TASKS}${task_id}.json`,
|
|
|
|
|
|
JSON.stringify(task, null, 2), 'application/json');
|
|
|
|
|
|
|
|
|
|
|
|
// 写入审批回执
|
|
|
|
|
|
const receiptKey = `${PATHS.AWEN_PROGRESS}${task_id}-review.json`;
|
|
|
|
|
|
await cos.write(targetBucket, receiptKey, JSON.stringify({
|
|
|
|
|
|
task_id,
|
|
|
|
|
|
approved,
|
|
|
|
|
|
feedback: feedback || '',
|
|
|
|
|
|
reviewed_at: now,
|
|
|
|
|
|
reviewed_by: 'zhuyuan',
|
|
|
|
|
|
next_task: next_task || null
|
|
|
|
|
|
}, null, 2), 'application/json');
|
|
|
|
|
|
|
|
|
|
|
|
// 如果通过且有下一步任务,自动分发
|
|
|
|
|
|
let nextTaskResult = null;
|
|
|
|
|
|
if (approved && next_task) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
nextTaskResult = await cosDispatchTask({
|
|
|
|
|
|
bucket: targetBucket,
|
|
|
|
|
|
title: next_task.title,
|
|
|
|
|
|
description: next_task.description,
|
|
|
|
|
|
priority: next_task.priority || 'normal',
|
|
|
|
|
|
requirements: next_task.requirements || [],
|
|
|
|
|
|
workorder_id: task.workorder_id
|
|
|
|
|
|
});
|
|
|
|
|
|
} catch (err) {
|
|
|
|
|
|
nextTaskResult = { error: err.message };
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 如果通过且有关联工单,将工单移到completed
|
|
|
|
|
|
if (approved && task.workorder_id) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const woKey = `${PATHS.WORKORDERS_PROCESSING}${task.workorder_id}.json`;
|
|
|
|
|
|
const woRaw = await cos.read(targetBucket, woKey);
|
|
|
|
|
|
const workorder = JSON.parse(woRaw.content);
|
|
|
|
|
|
workorder.status = 'completed';
|
|
|
|
|
|
workorder.completed_at = now;
|
|
|
|
|
|
workorder.history.push({
|
|
|
|
|
|
action: 'completed',
|
|
|
|
|
|
timestamp: now,
|
|
|
|
|
|
by: 'zhuyuan',
|
|
|
|
|
|
task_id
|
|
|
|
|
|
});
|
|
|
|
|
|
await cos.write(targetBucket, `${PATHS.WORKORDERS_COMPLETED}${task.workorder_id}.json`,
|
|
|
|
|
|
JSON.stringify(workorder, null, 2), 'application/json');
|
|
|
|
|
|
await cos.del(targetBucket, woKey);
|
|
|
|
|
|
} catch { /* ignore */ }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
status: approved ? 'approved' : 'rejected',
|
|
|
|
|
|
task_id,
|
|
|
|
|
|
feedback: feedback || '',
|
|
|
|
|
|
receipt_key: receiptKey,
|
|
|
|
|
|
next_task: nextTaskResult
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* cosSendNotification — 发送通知
|
|
|
|
|
|
*
|
|
|
|
|
|
* 将通知写入COS桶,由外部Agent(GitHub Actions/SCF)处理发送
|
|
|
|
|
|
*
|
|
|
|
|
|
* input:
|
|
|
|
|
|
* bucket: string — 桶名(默认team)
|
|
|
|
|
|
* notification_type: string — 通知类型: email|issue|cos_alert
|
|
|
|
|
|
* recipient: string — 接收者
|
|
|
|
|
|
* subject: string — 主题
|
|
|
|
|
|
* body: string — 内容
|
|
|
|
|
|
* metadata: object — 附加信息
|
|
|
|
|
|
*/
|
|
|
|
|
|
async function cosSendNotification(input) {
|
|
|
|
|
|
const { bucket, notification_type, recipient, subject, body, metadata } = input;
|
|
|
|
|
|
if (!notification_type || !subject) throw new Error('缺少 notification_type 或 subject');
|
|
|
|
|
|
|
|
|
|
|
|
const targetBucket = bucket || 'team';
|
|
|
|
|
|
const notificationId = `NOTIF-${Date.now()}`;
|
|
|
|
|
|
const now = new Date().toISOString();
|
|
|
|
|
|
|
|
|
|
|
|
const notification = {
|
|
|
|
|
|
notification_id: notificationId,
|
|
|
|
|
|
type: notification_type,
|
|
|
|
|
|
recipient: recipient || 'bingshuo',
|
|
|
|
|
|
subject,
|
|
|
|
|
|
body: body || '',
|
|
|
|
|
|
metadata: metadata || {},
|
|
|
|
|
|
status: 'pending',
|
|
|
|
|
|
created_at: now,
|
|
|
|
|
|
sent_at: null
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const key = `${PATHS.NOTIFICATIONS}${notificationId}.json`;
|
|
|
|
|
|
await cos.write(targetBucket, key, JSON.stringify(notification, null, 2), 'application/json');
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
notification_id: notificationId,
|
|
|
|
|
|
type: notification_type,
|
|
|
|
|
|
key,
|
|
|
|
|
|
status: 'queued',
|
|
|
|
|
|
note: '通知已入队,等待发送Agent处理'
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* cosGetCommLink — 获取通信链路状态
|
|
|
|
|
|
*
|
|
|
|
|
|
* 检查三方通信链路的健康状态
|
|
|
|
|
|
*
|
|
|
|
|
|
* input:
|
|
|
|
|
|
* bucket: string — 桶名(默认team)
|
|
|
|
|
|
*/
|
|
|
|
|
|
async function cosGetCommLink(input) {
|
|
|
|
|
|
const { bucket } = input;
|
|
|
|
|
|
const targetBucket = bucket || 'team';
|
|
|
|
|
|
|
|
|
|
|
|
// 检查各路径是否可访问
|
|
|
|
|
|
const checks = {};
|
|
|
|
|
|
for (const [name, path] of Object.entries(PATHS)) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const result = await cos.list(targetBucket, path, 5);
|
|
|
|
|
|
checks[name] = {
|
|
|
|
|
|
status: 'accessible',
|
|
|
|
|
|
files: result.files.length
|
|
|
|
|
|
};
|
|
|
|
|
|
} catch (err) {
|
|
|
|
|
|
checks[name] = {
|
|
|
|
|
|
status: 'error',
|
|
|
|
|
|
error: err.message
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 统计待处理项
|
|
|
|
|
|
const pending = {
|
|
|
|
|
|
alerts: checks.ALERTS?.files || 0,
|
|
|
|
|
|
workorders: checks.WORKORDERS_PENDING?.files || 0,
|
|
|
|
|
|
task_reports: checks.AWEN_REPORTS?.files || 0,
|
|
|
|
|
|
notifications: checks.NOTIFICATIONS?.files || 0
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
comm_link: 'zhuyuan-cos-awen',
|
|
|
|
|
|
bucket: targetBucket,
|
|
|
|
|
|
paths: checks,
|
|
|
|
|
|
pending,
|
|
|
|
|
|
health: Object.values(checks).every(c => c.status === 'accessible') ? 'healthy' : 'degraded',
|
|
|
|
|
|
timestamp: new Date().toISOString()
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ─── 辅助 ───
|
|
|
|
|
|
|
|
|
|
|
|
function formatDate() {
|
|
|
|
|
|
const d = new Date();
|
|
|
|
|
|
return `${d.getFullYear()}${String(d.getMonth() + 1).padStart(2, '0')}${String(d.getDate()).padStart(2, '0')}`;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
module.exports = {
|
|
|
|
|
|
cosAlertScan,
|
|
|
|
|
|
cosAlertResolve,
|
|
|
|
|
|
cosDispatchTask,
|
|
|
|
|
|
cosReadTaskReport,
|
|
|
|
|
|
cosListTaskReports,
|
|
|
|
|
|
cosApproveTask,
|
|
|
|
|
|
cosSendNotification,
|
|
|
|
|
|
cosGetCommLink
|
|
|
|
|
|
};
|