@@ -0,0 +1,246 @@
package com.bruce.dashboard.service.impl ;
import com.bruce.agent.dto.response.AgentDefinitionResponse ;
import com.bruce.agent.service.IAgentDefinitionService ;
import com.bruce.dashboard.service.IStudioDashboardService ;
import com.bruce.dashboard.vo.StudioDashboardChecklistItemVO ;
import com.bruce.dashboard.vo.StudioDashboardLifecycleStepVO ;
import com.bruce.dashboard.vo.StudioDashboardMetricsVO ;
import com.bruce.dashboard.vo.StudioDashboardRecentRunVO ;
import com.bruce.dashboard.vo.StudioDashboardVO ;
import com.bruce.mcp.service.IMcpServerService ;
import com.bruce.modelprovider.service.IModelWorkspaceService ;
import com.bruce.modelprovider.vo.ModelWorkspaceVO ;
import com.bruce.observability.service.IObservabilityRunService ;
import com.bruce.observability.vo.ObservabilityRunSummaryVO ;
import com.bruce.rag.service.IKnowledgeWorkspaceService ;
import com.bruce.rag.service.IRagStoreService ;
import com.bruce.rag.vo.KnowledgeWorkspaceVO ;
import com.bruce.skill.service.ISkillDefinitionService ;
import com.bruce.workflow.service.IProjectService ;
import com.bruce.workflow.service.IWorkflowDefinitionService ;
import com.bruce.workflow.vo.ProjectVO ;
import com.bruce.workflow.vo.WorkflowDefinitionVO ;
import lombok.RequiredArgsConstructor ;
import lombok.extern.slf4j.Slf4j ;
import org.springframework.beans.factory.ObjectProvider ;
import org.springframework.stereotype.Service ;
import java.math.BigDecimal ;
import java.math.RoundingMode ;
import java.util.ArrayList ;
import java.util.List ;
/**
* Studio 首页聚合实现。
* <p>
* 该服务只汇总现有模块已经稳定的主数据和运行摘要,不引入新的存储表。
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class StudioDashboardServiceImpl implements IStudioDashboardService {
private final IProjectService projectService ;
private final IWorkflowDefinitionService workflowDefinitionService ;
private final IObservabilityRunService observabilityRunService ;
private final IModelWorkspaceService modelWorkspaceService ;
private final IRagStoreService ragStoreService ;
private final IKnowledgeWorkspaceService knowledgeWorkspaceService ;
private final ObjectProvider < IAgentDefinitionService > agentDefinitionServiceProvider ;
private final IMcpServerService mcpServerService ;
private final ISkillDefinitionService skillDefinitionService ;
@Override
public StudioDashboardVO getDashboard ( ) {
log . info ( " Studio 首页聚合开始 " ) ;
List < ProjectVO > projects = projectService . listProjects ( ) ;
ProjectVO currentProject = projects . isEmpty ( ) ? null : projects . get ( 0 ) ;
List < WorkflowDefinitionVO > workflows = currentProject = = null ? List . of ( ) : workflowDefinitionService . listByProjectId ( currentProject . getId ( ) ) ;
List < ObservabilityRunSummaryVO > recentRuns = observabilityRunService . listRecentRuns ( ) ;
ModelWorkspaceVO modelWorkspace = modelWorkspaceService . getWorkspace ( ) ;
IAgentDefinitionService agentDefinitionService = agentDefinitionServiceProvider . getIfAvailable ( ) ;
List < AgentDefinitionResponse > agents = agentDefinitionService = = null ? List . of ( ) : agentDefinitionService . listResponses ( ) ;
int mcpServerCount = mcpServerService . listServers ( ) . size ( ) ;
int skillCount = skillDefinitionService . listDefinitions ( ) . size ( ) ;
KnowledgeWorkspaceVO knowledgeWorkspace = null ;
if ( ! ragStoreService . listResponses ( ) . isEmpty ( ) ) {
Long firstStoreId = ragStoreService . listResponses ( ) . get ( 0 ) . getId ( ) ;
knowledgeWorkspace = knowledgeWorkspaceService . getWorkspace ( firstStoreId ) ;
}
StudioDashboardVO dashboard = new StudioDashboardVO ( ) ;
dashboard . setProjectName ( currentProject = = null ? " Common Agent Studio " : currentProject . getProjectName ( ) ) ;
dashboard . setEnvironment ( currentProject = = null ? " Dev " : currentProject . getEnvironment ( ) ) ;
dashboard . setPublishStatus ( currentProject = = null ? " DRAFT " : currentProject . getPublishStatus ( ) ) ;
dashboard . setLifecycleSteps ( buildLifecycleSteps ( knowledgeWorkspace , workflows , agents , recentRuns ) ) ;
dashboard . setReadinessChecklist ( buildChecklist ( knowledgeWorkspace , workflows , agents , modelWorkspace , mcpServerCount , skillCount ) ) ;
dashboard . setMetrics ( buildMetrics ( recentRuns ) ) ;
dashboard . setRecentRuns ( buildRecentRuns ( recentRuns , workflows , agents ) ) ;
dashboard . setWarningTitle ( buildWarningTitle ( modelWorkspace , workflows ) ) ;
dashboard . setWarningMessage ( buildWarningMessage ( modelWorkspace , workflows , knowledgeWorkspace ) ) ;
log . info ( " Studio 首页聚合结束, projectName={}, workflowCount={}, runCount={} " ,
dashboard . getProjectName ( ) , workflows . size ( ) , recentRuns . size ( ) ) ;
return dashboard ;
}
private List < StudioDashboardLifecycleStepVO > buildLifecycleSteps ( KnowledgeWorkspaceVO knowledgeWorkspace ,
List < WorkflowDefinitionVO > workflows ,
List < AgentDefinitionResponse > agents ,
List < ObservabilityRunSummaryVO > recentRuns ) {
List < StudioDashboardLifecycleStepVO > steps = new ArrayList < > ( ) ;
steps . add ( step ( " 知识接入 " , " 上传、解析、切片、向量化 " , knowledgeWorkspace ! = null & & knowledgeWorkspace . getDocumentCount ( ) > 0 ? " done " : " idle " ) ) ;
steps . add ( step ( " 能力编排 " , " Workflow 连接模型、工具与 Skill " , workflows . isEmpty ( ) ? " idle " : " running " ) ) ;
steps . add ( step ( " 对话调试 " , " 验证引用、成本、延迟与回答质量 " , agents . isEmpty ( ) ? " idle " : " running " ) ) ;
steps . add ( step ( " 发布观测 " , " 版本快照、运行追踪、异常排查 " , recentRuns . isEmpty ( ) ? " idle " : " done " ) ) ;
return steps ;
}
private List < StudioDashboardChecklistItemVO > buildChecklist ( KnowledgeWorkspaceVO knowledgeWorkspace ,
List < WorkflowDefinitionVO > workflows ,
List < AgentDefinitionResponse > agents ,
ModelWorkspaceVO modelWorkspace ,
int mcpServerCount ,
int skillCount ) {
List < StudioDashboardChecklistItemVO > items = new ArrayList < > ( ) ;
items . add ( checkItem ( " 知识库已绑定 Embedding 模型 " , knowledgeWorkspace ! = null & & knowledgeWorkspace . getEmbeddingModelId ( ) ! = null ) ) ;
items . add ( checkItem ( " Workflow 已存在可编辑草稿 " , ! workflows . isEmpty ( ) ) ) ;
items . add ( checkItem ( " Agent 已绑定默认知识库与能力 " , ! agents . isEmpty ( ) ) ) ;
items . add ( checkItem ( " MCP / Skill 基础能力已接入 " , mcpServerCount > 0 & & skillCount > 0 ) ) ;
items . add ( checkItem ( " 模型路由已配置至少一个启用规则 " , modelWorkspace . getEnabledRouteRuleCount ( ) ! = null & & modelWorkspace . getEnabledRouteRuleCount ( ) > 0 ) ) ;
return items ;
}
private StudioDashboardMetricsVO buildMetrics ( List < ObservabilityRunSummaryVO > recentRuns ) {
StudioDashboardMetricsVO metrics = new StudioDashboardMetricsVO ( ) ;
metrics . setTodayRunCount ( recentRuns . size ( ) ) ;
long successCount = recentRuns . stream ( ) . filter ( run - > " SUCCESS " . equals ( run . getStatus ( ) ) ) . count ( ) ;
double successRate = recentRuns . isEmpty ( ) ? 100D : successCount * 100 . 0 / recentRuns . size ( ) ;
metrics . setSuccessRate ( roundDouble ( successRate ) ) ;
metrics . setP50Latency ( formatP50Latency ( recentRuns ) ) ;
metrics . setEstimatedCost ( formatCost ( recentRuns ) ) ;
return metrics ;
}
private List < StudioDashboardRecentRunVO > buildRecentRuns ( List < ObservabilityRunSummaryVO > recentRuns ,
List < WorkflowDefinitionVO > workflows ,
List < AgentDefinitionResponse > agents ) {
return recentRuns . stream ( ) . limit ( 5 ) . map ( run - > {
StudioDashboardRecentRunVO item = new StudioDashboardRecentRunVO ( ) ;
item . setId ( run . getRequestId ( ) ) ;
item . setName ( resolveRunName ( run . getWorkflowId ( ) , workflows , agents ) ) ;
item . setType ( run . getWorkflowId ( ) = = null ? " Agent " : " Workflow " ) ;
item . setStatus ( formatRunStatus ( run . getStatus ( ) ) ) ;
item . setLatency ( formatDuration ( run . getDurationMs ( ) ) ) ;
item . setCost ( " ¥ " + roundBigDecimal ( run . getEstimatedCost ( ) = = null ? BigDecimal . ZERO : run . getEstimatedCost ( ) ) ) ;
return item ;
} ) . toList ( ) ;
}
private String buildWarningTitle ( ModelWorkspaceVO modelWorkspace , List < WorkflowDefinitionVO > workflows ) {
if ( modelWorkspace . getEnabledRouteRuleCount ( ) = = null | | modelWorkspace . getEnabledRouteRuleCount ( ) = = 0 ) {
return " 发布前仍需补齐模型路由 " ;
}
if ( workflows . isEmpty ( ) ) {
return " 发布前仍需创建至少一个 Workflow " ;
}
return " 生产发布前仍需确认路由兜底 " ;
}
private String buildWarningMessage ( ModelWorkspaceVO modelWorkspace ,
List < WorkflowDefinitionVO > workflows ,
KnowledgeWorkspaceVO knowledgeWorkspace ) {
if ( modelWorkspace . getRecentFailedCallCount ( ) ! = null & & modelWorkspace . getRecentFailedCallCount ( ) > 0 ) {
return " 最近存在失败模型调用,建议先补齐 fallback 模型并复核错误上下文。 " ;
}
if ( knowledgeWorkspace ! = null & & knowledgeWorkspace . getPendingTaskCount ( ) ! = null & & knowledgeWorkspace . getPendingTaskCount ( ) > 0 ) {
return " 当前知识库仍有待索引文档,建议完成索引后再进行发布联调。 " ;
}
if ( workflows . isEmpty ( ) ) {
return " 当前项目尚无可试跑 Workflow, 建议先完成最小链路编排。 " ;
}
return " AGENT_PLAN 任务建议补齐 fallback 模型和最大延迟阈值后再发布。 " ;
}
private StudioDashboardLifecycleStepVO step ( String name , String description , String status ) {
StudioDashboardLifecycleStepVO step = new StudioDashboardLifecycleStepVO ( ) ;
step . setName ( name ) ;
step . setDescription ( description ) ;
step . setStatus ( status ) ;
return step ;
}
private StudioDashboardChecklistItemVO checkItem ( String label , boolean done ) {
StudioDashboardChecklistItemVO item = new StudioDashboardChecklistItemVO ( ) ;
item . setLabel ( label ) ;
item . setDone ( done ) ;
return item ;
}
private String resolveRunName ( Long workflowId , List < WorkflowDefinitionVO > workflows , List < AgentDefinitionResponse > agents ) {
if ( workflowId ! = null ) {
return workflows . stream ( )
. filter ( workflow - > workflowId . equals ( workflow . getId ( ) ) )
. map ( WorkflowDefinitionVO : : getWorkflowName )
. findFirst ( )
. orElse ( " Workflow 运行 " ) ;
}
return agents . isEmpty ( ) ? " Agent 调试会话 " : agents . get ( 0 ) . getAgentName ( ) ;
}
private String formatRunStatus ( String status ) {
if ( " SUCCESS " . equals ( status ) ) {
return " 成功 " ;
}
if ( " FAILED " . equals ( status ) ) {
return " 失败 " ;
}
if ( " RUNNING " . equals ( status ) ) {
return " 运行中 " ;
}
return status = = null ? " - " : status ;
}
private String formatDuration ( Integer durationMs ) {
if ( durationMs = = null ) {
return " - " ;
}
if ( durationMs > = 1000 ) {
return roundBigDecimal ( BigDecimal . valueOf ( durationMs ) . divide ( BigDecimal . valueOf ( 1000 ) , 2 , RoundingMode . HALF_UP ) ) + " s " ;
}
return durationMs + " ms " ;
}
private String formatP50Latency ( List < ObservabilityRunSummaryVO > recentRuns ) {
if ( recentRuns . isEmpty ( ) ) {
return " - " ;
}
List < Integer > durations = recentRuns . stream ( )
. map ( ObservabilityRunSummaryVO : : getDurationMs )
. filter ( value - > value ! = null & & value > 0 )
. sorted ( )
. toList ( ) ;
if ( durations . isEmpty ( ) ) {
return " - " ;
}
Integer p50 = durations . get ( durations . size ( ) / 2 ) ;
return formatDuration ( p50 ) ;
}
private String formatCost ( List < ObservabilityRunSummaryVO > recentRuns ) {
BigDecimal total = recentRuns . stream ( )
. map ( ObservabilityRunSummaryVO : : getEstimatedCost )
. filter ( cost - > cost ! = null & & cost . compareTo ( BigDecimal . ZERO ) > 0 )
. reduce ( BigDecimal . ZERO , BigDecimal : : add ) ;
return " ¥ " + roundBigDecimal ( total ) ;
}
private Double roundDouble ( double value ) {
return BigDecimal . valueOf ( value ) . setScale ( 2 , RoundingMode . HALF_UP ) . doubleValue ( ) ;
}
private String roundBigDecimal ( BigDecimal value ) {
return value . setScale ( 2 , RoundingMode . HALF_UP ) . stripTrailingZeros ( ) . toPlainString ( ) ;
}
}