From f4bc9f6dab4ba03276d4025f1135d2a47e909c8d Mon Sep 17 00:00:00 2001 From: peanut_hzm Date: Thu, 25 Dec 2025 00:13:42 +0800 Subject: [PATCH] =?UTF-8?q?=E5=90=8E=E5=8F=B0=E7=AE=A1=E7=90=86=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E8=A1=A5=E5=85=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../emotion/controller/AdminController.java | 12 +++++ .../dto/request/AiConfigCallStatsRequest.java | 29 ++++++++++++ .../dto/response/AiConfigCallStatsItem.java | 45 +++++++++++++++++++ .../response/AiConfigCallStatsResponse.java | 29 ++++++++++++ .../com/emotion/mapper/AiConfigMapper.java | 35 +++++++++++++++ .../com/emotion/service/DashboardService.java | 10 +++++ .../service/impl/DashboardServiceImpl.java | 27 +++++++++++ web-admin/src/api/dashboard.ts | 34 ++++++++++++++ web-admin/src/views/Dashboard.vue | 41 +++++++++++++++-- 9 files changed, 259 insertions(+), 3 deletions(-) create mode 100644 backend-single/src/main/java/com/emotion/dto/request/AiConfigCallStatsRequest.java create mode 100644 backend-single/src/main/java/com/emotion/dto/response/AiConfigCallStatsItem.java create mode 100644 backend-single/src/main/java/com/emotion/dto/response/AiConfigCallStatsResponse.java diff --git a/backend-single/src/main/java/com/emotion/controller/AdminController.java b/backend-single/src/main/java/com/emotion/controller/AdminController.java index dc131f2..77e9530 100644 --- a/backend-single/src/main/java/com/emotion/controller/AdminController.java +++ b/backend-single/src/main/java/com/emotion/controller/AdminController.java @@ -2,9 +2,11 @@ package com.emotion.controller; import com.emotion.common.PageResult; import com.emotion.common.Result; +import com.emotion.dto.request.AiConfigCallStatsRequest; import com.emotion.dto.request.AdminCreateRequest; import com.emotion.dto.request.AdminPageRequest; import com.emotion.dto.request.AdminUpdateRequest; +import com.emotion.dto.response.AiConfigCallStatsResponse; import com.emotion.dto.response.AdminResponse; import com.emotion.dto.response.DashboardStatsResponse; import com.emotion.service.AdminService; @@ -166,4 +168,14 @@ public class AdminController { List recentLogins = dashboardService.getRecentLogins(limit); return Result.success("获取成功", recentLogins); } + + /** + * 获取 AI 配置调用次数统计 + */ + @Operation(summary = "获取AI配置调用次数统计", description = "按 t_ai_config 的 workflow_id 关联 t_coze_api_call 统计调用次数并按次数倒序返回") + @GetMapping(value = "/dashboard/aiConfigCallStats") + public Result getAiConfigCallStats(@Validated AiConfigCallStatsRequest request) { + AiConfigCallStatsResponse response = dashboardService.getAiConfigCallStats(request); + return Result.success("获取成功", response); + } } diff --git a/backend-single/src/main/java/com/emotion/dto/request/AiConfigCallStatsRequest.java b/backend-single/src/main/java/com/emotion/dto/request/AiConfigCallStatsRequest.java new file mode 100644 index 0000000..edbe865 --- /dev/null +++ b/backend-single/src/main/java/com/emotion/dto/request/AiConfigCallStatsRequest.java @@ -0,0 +1,29 @@ +package com.emotion.dto.request; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; + +/** + * AI配置调用次数统计请求 + * 用于管理后台仪表盘:按 t_ai_config 配置统计 t_coze_api_call 调用次数 + * + * @author system + * @date 2025-12-24 + */ +@Data +@Schema(description = "AI配置调用次数统计请求") +public class AiConfigCallStatsRequest { + + /** + * 返回条数限制(默认 20,最大 200) + */ + @Min(1) + @Max(200) + @Schema(description = "返回条数限制(默认20,最大200)", example = "20") + private Integer limit; +} + + diff --git a/backend-single/src/main/java/com/emotion/dto/response/AiConfigCallStatsItem.java b/backend-single/src/main/java/com/emotion/dto/response/AiConfigCallStatsItem.java new file mode 100644 index 0000000..6cc9eaf --- /dev/null +++ b/backend-single/src/main/java/com/emotion/dto/response/AiConfigCallStatsItem.java @@ -0,0 +1,45 @@ +package com.emotion.dto.response; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +/** + * AI配置调用次数统计项 + * 通过 workflowId 关联 t_ai_config 与 t_coze_api_call,统计调用次数并排序展示 + * + * @author system + * @date 2025-12-24 + */ +@Data +@Schema(description = "AI配置调用次数统计项") +public class AiConfigCallStatsItem { + + @Schema(description = "AI配置ID") + private String configId; + + @Schema(description = "配置名称") + private String configName; + + @Schema(description = "配置键值") + private String configKey; + + @Schema(description = "服务提供商") + private String provider; + + @Schema(description = "配置类型") + private String configType; + + @Schema(description = "Workflow ID") + private String workflowId; + + @Schema(description = "是否启用:0-禁用,1-启用") + private Integer isEnabled; + + @Schema(description = "是否默认:0-否,1-是") + private Integer isDefault; + + @Schema(description = "调用次数") + private Long callCount; +} + + diff --git a/backend-single/src/main/java/com/emotion/dto/response/AiConfigCallStatsResponse.java b/backend-single/src/main/java/com/emotion/dto/response/AiConfigCallStatsResponse.java new file mode 100644 index 0000000..215a82c --- /dev/null +++ b/backend-single/src/main/java/com/emotion/dto/response/AiConfigCallStatsResponse.java @@ -0,0 +1,29 @@ +package com.emotion.dto.response; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * AI配置调用次数统计响应 + * 用于管理后台仪表盘展示:各个 AI 配置的接口调用次数(按次数排序) + * + * @author system + * @date 2025-12-24 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Schema(description = "AI配置调用次数统计响应") +public class AiConfigCallStatsResponse { + + @Schema(description = "统计列表(按调用次数倒序)") + private List items; +} + + diff --git a/backend-single/src/main/java/com/emotion/mapper/AiConfigMapper.java b/backend-single/src/main/java/com/emotion/mapper/AiConfigMapper.java index 6fadd53..3145221 100644 --- a/backend-single/src/main/java/com/emotion/mapper/AiConfigMapper.java +++ b/backend-single/src/main/java/com/emotion/mapper/AiConfigMapper.java @@ -1,9 +1,14 @@ package com.emotion.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.emotion.dto.response.AiConfigCallStatsItem; import com.emotion.entity.AiConfig; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Mapper; +import java.util.List; + /** * AI配置Mapper接口 * @@ -12,4 +17,34 @@ import org.apache.ibatis.annotations.Mapper; */ @Mapper public interface AiConfigMapper extends BaseMapper { + + /** + * 按 AI 配置统计 Coze 接口调用次数(通过 workflow_id 关联) + * + * @param limit 返回条数限制 + * @return 统计列表(按调用次数倒序) + */ + @Select({ + "SELECT", + " c.id AS configId,", + " c.config_name AS configName,", + " c.config_key AS configKey,", + " c.provider AS provider,", + " c.config_type AS configType,", + " c.workflow_id AS workflowId,", + " c.is_enabled AS isEnabled,", + " c.is_default AS isDefault,", + " COALESCE(COUNT(a.id), 0) AS callCount", + "FROM t_ai_config c", + "LEFT JOIN t_coze_api_call a", + " ON a.workflow_id = c.workflow_id", + " AND a.is_deleted = 0", + "WHERE c.is_deleted = 0", + "GROUP BY", + " c.id, c.config_name, c.config_key, c.provider, c.config_type,", + " c.workflow_id, c.is_enabled, c.is_default", + "ORDER BY callCount DESC", + "LIMIT #{limit}" + }) + List selectAiConfigCallStats(@Param("limit") Integer limit); } \ No newline at end of file diff --git a/backend-single/src/main/java/com/emotion/service/DashboardService.java b/backend-single/src/main/java/com/emotion/service/DashboardService.java index 950b018..7c19543 100644 --- a/backend-single/src/main/java/com/emotion/service/DashboardService.java +++ b/backend-single/src/main/java/com/emotion/service/DashboardService.java @@ -1,5 +1,7 @@ package com.emotion.service; +import com.emotion.dto.request.AiConfigCallStatsRequest; +import com.emotion.dto.response.AiConfigCallStatsResponse; import com.emotion.dto.response.DashboardStatsResponse; import java.util.List; @@ -61,4 +63,12 @@ public interface DashboardService { * @return 最近登录用户列表 */ List getRecentLogins(int limit); + + /** + * 获取 AI 配置调用次数统计(按调用次数倒序) + * + * @param request 统计请求 + * @return AI配置调用次数统计响应 + */ + AiConfigCallStatsResponse getAiConfigCallStats(AiConfigCallStatsRequest request); } \ No newline at end of file diff --git a/backend-single/src/main/java/com/emotion/service/impl/DashboardServiceImpl.java b/backend-single/src/main/java/com/emotion/service/impl/DashboardServiceImpl.java index 92194b8..43e1d5d 100644 --- a/backend-single/src/main/java/com/emotion/service/impl/DashboardServiceImpl.java +++ b/backend-single/src/main/java/com/emotion/service/impl/DashboardServiceImpl.java @@ -1,8 +1,12 @@ package com.emotion.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.emotion.dto.request.AiConfigCallStatsRequest; +import com.emotion.dto.response.AiConfigCallStatsResponse; +import com.emotion.dto.response.AiConfigCallStatsItem; import com.emotion.dto.response.DashboardStatsResponse; import com.emotion.entity.*; +import com.emotion.mapper.AiConfigMapper; import com.emotion.service.*; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -56,6 +60,9 @@ public class DashboardServiceImpl implements DashboardService { @Autowired private AdminService adminService; + @Autowired + private AiConfigMapper aiConfigMapper; + @Autowired private AchievementService achievementService; @@ -464,6 +471,26 @@ public class DashboardServiceImpl implements DashboardService { return recentLogins; } + @Override + public AiConfigCallStatsResponse getAiConfigCallStats(AiConfigCallStatsRequest request) { + Integer limit = request != null ? request.getLimit() : null; + if (limit == null) { + limit = 20; + } + // 防御性限制,避免过大查询 + if (limit > 200) { + limit = 200; + } + if (limit < 1) { + limit = 1; + } + + List items = aiConfigMapper.selectAiConfigCallStats(limit); + return AiConfigCallStatsResponse.builder() + .items(items) + .build(); + } + /** * 格式化时间描述 */ diff --git a/web-admin/src/api/dashboard.ts b/web-admin/src/api/dashboard.ts index fe03c87..662c159 100644 --- a/web-admin/src/api/dashboard.ts +++ b/web-admin/src/api/dashboard.ts @@ -67,6 +67,28 @@ export interface DashboardStats { updateTime: string } +/** + * AI配置调用次数统计项类型定义 + */ +export interface AiConfigCallStatsItem { + configId: string + configName: string + configKey: string + provider: string + configType: string + workflowId: string + isEnabled: number + isDefault: number + callCount: number +} + +/** + * AI配置调用次数统计响应类型定义 + */ +export interface AiConfigCallStatsResponse { + items: AiConfigCallStatsItem[] +} + /** * 获取仪表盘统计数据 */ @@ -137,4 +159,16 @@ export function getRecentLogins(limit: number = 10) { method: 'get', params: { limit } }) +} + +/** + * 获取 AI 配置调用次数统计(按调用次数倒序) + * @param limit 返回条数,默认 10 + */ +export function getAiConfigCallStats(limit: number = 10) { + return request({ + url: '/admin/dashboard/aiConfigCallStats', + method: 'get', + params: { limit } + }) } \ No newline at end of file diff --git a/web-admin/src/views/Dashboard.vue b/web-admin/src/views/Dashboard.vue index 04cf9ca..754bd91 100644 --- a/web-admin/src/views/Dashboard.vue +++ b/web-admin/src/views/Dashboard.vue @@ -235,7 +235,27 @@ - + + + + + + + + + + @@ -246,8 +266,7 @@ import { ref, reactive, onMounted } from 'vue' import { User, UserFilled, TrendCharts, ChatDotRound, Setting, CircleCheck, CircleClose, Star } from '@element-plus/icons-vue' import * as echarts from 'echarts' import { countEnabledConfigs, countDisabledConfigs, countDefaultConfigs } from '@/api/aiconfig' -import { getDashboardStats, getUserGrowthTrends, getRecentLogins, type DashboardStats, type UserGrowthTrend, type RecentLogin } from '@/api/dashboard' -import AiConfigQuickActions from '@/components/AiConfigQuickActions.vue' +import { getDashboardStats, getUserGrowthTrends, getRecentLogins, getAiConfigCallStats, type DashboardStats, type UserGrowthTrend, type RecentLogin, type AiConfigCallStatsItem } from '@/api/dashboard' import { ElMessage } from 'element-plus' const userChartRef = ref() @@ -296,6 +315,7 @@ const aiStats = reactive({ const recentLogins = ref([]) const userGrowthTrends = ref([]) +const aiConfigCallStats = ref([]) const loading = ref(false) @@ -338,6 +358,9 @@ const fetchDashboardData = async () => { // 获取AI配置统计(保持原有逻辑) await fetchAiStats() + + // 获取AI配置调用排行 + await fetchAiConfigCallStats() // 更新图表数据 updateUserChart() @@ -368,6 +391,18 @@ const fetchAiStats = async () => { } } +/** + * 获取AI配置调用次数统计(按调用次数倒序) + */ +const fetchAiConfigCallStats = async () => { + try { + const res = await getAiConfigCallStats(10) + aiConfigCallStats.value = res.data?.items || [] + } catch (error) { + console.error('获取AI配置调用统计失败:', error) + } +} + let userChart: any = null const initUserChart = () => {