| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161 |
- import { Injectable } from '@angular/core';
- import { CreatorDashboardAction, CreatorDashboardSummary } from '../models/creator-dashboard.model';
- import { BatchJobService } from './batch-job.service';
- import { ContentAssetService } from './content-asset.service';
- import { GenerationTaskService } from './generation-task.service';
- import { IndexedDbJsonStorage } from './indexed-db-json-storage';
- import { TaskRecoveryService } from './task-recovery.service';
- import { TopicPoolService } from './topic-pool.service';
- @Injectable({ providedIn: 'root' })
- export class CreatorDashboardService {
- constructor(
- private generationTasks: GenerationTaskService,
- private topicPool: TopicPoolService,
- private contentAssets: ContentAssetService,
- private batchJobs: BatchJobService,
- private taskRecovery: TaskRecoveryService,
- ) {}
- getSummary(): CreatorDashboardSummary {
- const tasks = this.generationTasks.tasks;
- const topics = this.topicPool.listTopics();
- const assets = this.contentAssets.assets;
- const jobs = this.batchJobs.listJobs();
- const runningTasks = tasks.filter((task) => task.status === 'running' || task.status === 'waiting_external').length;
- const recoverableTasks = tasks.filter((task) => task.recoverable && task.status !== 'completed' && task.status !== 'cancelled').length;
- const failedTasks = tasks.filter((task) => task.status === 'failed').length;
- const pendingTopics = topics.filter((topic) => topic.status === 'idea' || topic.status === 'script_ready').length;
- const readyAssets = assets.filter((asset) => asset.type === 'video' && asset.source?.isFinal && !asset.archived).length;
- const activeBatchJobs = jobs.filter((job) => job.status === 'queued' || job.status === 'running' || job.status === 'paused').length;
- const storageWarnings = IndexedDbJsonStorage.getAuditTrail()
- .filter((event) => event.status === 'fallback' || event.status === 'failed')
- .length;
- const lastRecoveryRunRecord = this.taskRecovery.getLastRun();
- const lastRecoveryRun = lastRecoveryRunRecord
- ? {
- runId: lastRecoveryRunRecord.runId,
- finishedAt: lastRecoveryRunRecord.finishedAt,
- scanned: lastRecoveryRunRecord.summary.scanned,
- recoverable: lastRecoveryRunRecord.summary.recoverable,
- completed: lastRecoveryRunRecord.summary.completed,
- failed: lastRecoveryRunRecord.summary.failed,
- unrecoverable: lastRecoveryRunRecord.summary.unrecoverable,
- }
- : undefined;
- return {
- runningTasks,
- recoverableTasks,
- failedTasks,
- pendingTopics,
- readyAssets,
- activeBatchJobs,
- lastRecoveryRun,
- storageWarnings,
- suggestedActions: this.buildActions({
- runningTasks,
- recoverableTasks,
- failedTasks,
- pendingTopics,
- readyAssets,
- activeBatchJobs,
- lastRecoveryRun,
- storageWarnings,
- }),
- };
- }
- private buildActions(input: Omit<CreatorDashboardSummary, 'suggestedActions'>): CreatorDashboardAction[] {
- const actions: CreatorDashboardAction[] = [];
- if (input.failedTasks > 0) {
- actions.push({
- id: 'failed-tasks',
- priority: 'high',
- title: `处理 ${input.failedTasks} 个失败任务`,
- description: '先清理失败项,避免批量生产继续堆积不可用结果。',
- target: 'tasks',
- });
- }
- if (input.storageWarnings > 0) {
- actions.push({
- id: 'storage-warnings',
- priority: 'high',
- title: `检查 ${input.storageWarnings} 条存储回退记录`,
- description: 'IndexedDB 读写出现过回退或失败,建议先确认任务、批量和素材数据是否完整。',
- target: 'tasks',
- payload: { storageAudit: true },
- });
- }
- if (input.lastRecoveryRun && (
- input.lastRecoveryRun.completed > 0
- || input.lastRecoveryRun.failed > 0
- || input.lastRecoveryRun.unrecoverable > 0
- )) {
- actions.push({
- id: 'last-recovery-run',
- priority: input.lastRecoveryRun.failed > 0 || input.lastRecoveryRun.unrecoverable > 0 ? 'high' : 'medium',
- title: `查看最近恢复扫描`,
- description: `已扫描 ${input.lastRecoveryRun.scanned} 个任务,完成 ${input.lastRecoveryRun.completed} 个,失败 ${input.lastRecoveryRun.failed} 个,不可恢复 ${input.lastRecoveryRun.unrecoverable} 个。`,
- target: 'tasks',
- payload: { recoveryRunId: input.lastRecoveryRun.runId },
- });
- }
- if (input.recoverableTasks > 0) {
- actions.push({
- id: 'recoverable-tasks',
- priority: 'high',
- title: `检查 ${input.recoverableTasks} 个可恢复任务`,
- description: '有任务仍在等待上游结果,适合优先确认是否已恢复归档。',
- target: 'tasks',
- });
- }
- if (input.pendingTopics > 0) {
- actions.push({
- id: 'pending-topics',
- priority: 'medium',
- title: `生产 ${input.pendingTopics} 个待用选题`,
- description: '选题池已有可用方向,可以进入批量生产形成成片。',
- target: 'batch-production',
- });
- }
- if (input.readyAssets > 0) {
- actions.push({
- id: 'ready-assets',
- priority: 'medium',
- title: `整理 ${input.readyAssets} 条待发布成片`,
- description: '素材库里有可复用成片,适合补元数据或记录发布表现。',
- target: 'library',
- });
- }
- if (input.activeBatchJobs > 0) {
- actions.push({
- id: 'active-batch',
- priority: 'medium',
- title: `跟进 ${input.activeBatchJobs} 个批量任务`,
- description: '批量队列仍有运行、排队或暂停项,需要确认下一步。',
- target: 'batch-production',
- });
- }
- if (!actions.length) {
- actions.push({
- id: 'start-topic-video',
- priority: 'low',
- title: '开始一条主题生视频',
- description: '当前没有紧急阻塞,可以从一个新主题直接进入生产。',
- target: 'topic-to-video',
- });
- }
- return actions.slice(0, 4);
- }
- }
|