feat: 展示通知 outbox 状态

- services: 增加 /notification-outbox 真实接口读取与摘要清洗

- store: 超级管理员加载最近通知 outbox

- settings: 增加通知 outbox 运维表格
This commit is contained in:
湛兮
2026-06-12 03:22:38 +08:00
parent 4988c5be38
commit cc72a24c7d
4 changed files with 170 additions and 1 deletions
+41
View File
@@ -25,6 +25,7 @@ import type {
LoginPayload,
LoginResult,
MemberSummary,
NotificationOutboxMessage,
PipelineStep,
PlatformMessageSummary,
ProjectConfig,
@@ -417,6 +418,12 @@ export const devopsApi = {
return Array.isArray(data) ? data.map(sanitizeAuditLogEntry) : []
},
async getNotificationOutbox(): Promise<NotificationOutboxMessage[]> {
const { data } = await http.get<NotificationOutboxMessage[]>('/notification-outbox')
lastDataSource = 'backend'
return Array.isArray(data) ? data.map(sanitizeNotificationOutboxMessage) : []
},
async getAgentCards(): Promise<AgentCard[]> {
return agentCards
},
@@ -848,6 +855,25 @@ function sanitizeAuditLogEntry(record: AuditLogEntry): AuditLogEntry {
}
}
function sanitizeNotificationOutboxMessage(
record: NotificationOutboxMessage,
): NotificationOutboxMessage {
return {
id: safeText(record.id, 'outbox-unknown'),
deployRunId: optionalSafeText(record.deployRunId),
channel: safeText(record.channel, 'none'),
template: safeText(record.template, 'unknown-template'),
payloadSummary: safeRecord(record.payloadSummary),
status: toNotificationOutboxStatus(record.status),
attemptCount: Number.isFinite(record.attemptCount) ? record.attemptCount : 0,
nextAttemptAt: optionalSafeText(record.nextAttemptAt),
lastError: optionalSafeText(record.lastError),
idempotencyKey: safeText(record.idempotencyKey, 'unknown-idempotency'),
createdAt: safeText(record.createdAt, ''),
updatedAt: safeText(record.updatedAt, ''),
}
}
function toPipelineStep(step: BackendDeployRunStep): PipelineStep {
const bpmnNodeId = step.bpmnNodeId || step.id || 'unknown-step'
const name = step.name || bpmnNodeId
@@ -916,6 +942,21 @@ function toStepStatus(status?: BackendRunStatus): StepStatus {
return 'pending'
}
function toNotificationOutboxStatus(
status?: NotificationOutboxMessage['status'],
): NotificationOutboxMessage['status'] {
if (
status === 'pending' ||
status === 'sent' ||
status === 'failed' ||
status === 'dead'
) {
return status
}
return 'failed'
}
function stepOwner(nodeId: string): PipelineStep['owner'] {
if (nodeId.includes('gitea')) {
return 'Gitea'