后台管理功能补充
This commit is contained in:
@@ -2,9 +2,11 @@ package com.emotion.controller;
|
|||||||
|
|
||||||
import com.emotion.common.PageResult;
|
import com.emotion.common.PageResult;
|
||||||
import com.emotion.common.Result;
|
import com.emotion.common.Result;
|
||||||
|
import com.emotion.dto.request.AiConfigCallStatsRequest;
|
||||||
import com.emotion.dto.request.AdminCreateRequest;
|
import com.emotion.dto.request.AdminCreateRequest;
|
||||||
import com.emotion.dto.request.AdminPageRequest;
|
import com.emotion.dto.request.AdminPageRequest;
|
||||||
import com.emotion.dto.request.AdminUpdateRequest;
|
import com.emotion.dto.request.AdminUpdateRequest;
|
||||||
|
import com.emotion.dto.response.AiConfigCallStatsResponse;
|
||||||
import com.emotion.dto.response.AdminResponse;
|
import com.emotion.dto.response.AdminResponse;
|
||||||
import com.emotion.dto.response.DashboardStatsResponse;
|
import com.emotion.dto.response.DashboardStatsResponse;
|
||||||
import com.emotion.service.AdminService;
|
import com.emotion.service.AdminService;
|
||||||
@@ -166,4 +168,14 @@ public class AdminController {
|
|||||||
List<DashboardStatsResponse.RecentLogin> recentLogins = dashboardService.getRecentLogins(limit);
|
List<DashboardStatsResponse.RecentLogin> recentLogins = dashboardService.getRecentLogins(limit);
|
||||||
return Result.success("获取成功", recentLogins);
|
return Result.success("获取成功", recentLogins);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取 AI 配置调用次数统计
|
||||||
|
*/
|
||||||
|
@Operation(summary = "获取AI配置调用次数统计", description = "按 t_ai_config 的 workflow_id 关联 t_coze_api_call 统计调用次数并按次数倒序返回")
|
||||||
|
@GetMapping(value = "/dashboard/aiConfigCallStats")
|
||||||
|
public Result<AiConfigCallStatsResponse> getAiConfigCallStats(@Validated AiConfigCallStatsRequest request) {
|
||||||
|
AiConfigCallStatsResponse response = dashboardService.getAiConfigCallStats(request);
|
||||||
|
return Result.success("获取成功", response);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -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<AiConfigCallStatsItem> items;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1,9 +1,14 @@
|
|||||||
package com.emotion.mapper;
|
package com.emotion.mapper;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.emotion.dto.response.AiConfigCallStatsItem;
|
||||||
import com.emotion.entity.AiConfig;
|
import com.emotion.entity.AiConfig;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
import org.apache.ibatis.annotations.Select;
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AI配置Mapper接口
|
* AI配置Mapper接口
|
||||||
*
|
*
|
||||||
@@ -12,4 +17,34 @@ import org.apache.ibatis.annotations.Mapper;
|
|||||||
*/
|
*/
|
||||||
@Mapper
|
@Mapper
|
||||||
public interface AiConfigMapper extends BaseMapper<AiConfig> {
|
public interface AiConfigMapper extends BaseMapper<AiConfig> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按 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<AiConfigCallStatsItem> selectAiConfigCallStats(@Param("limit") Integer limit);
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
package com.emotion.service;
|
package com.emotion.service;
|
||||||
|
|
||||||
|
import com.emotion.dto.request.AiConfigCallStatsRequest;
|
||||||
|
import com.emotion.dto.response.AiConfigCallStatsResponse;
|
||||||
import com.emotion.dto.response.DashboardStatsResponse;
|
import com.emotion.dto.response.DashboardStatsResponse;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -61,4 +63,12 @@ public interface DashboardService {
|
|||||||
* @return 最近登录用户列表
|
* @return 最近登录用户列表
|
||||||
*/
|
*/
|
||||||
List<DashboardStatsResponse.RecentLogin> getRecentLogins(int limit);
|
List<DashboardStatsResponse.RecentLogin> getRecentLogins(int limit);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取 AI 配置调用次数统计(按调用次数倒序)
|
||||||
|
*
|
||||||
|
* @param request 统计请求
|
||||||
|
* @return AI配置调用次数统计响应
|
||||||
|
*/
|
||||||
|
AiConfigCallStatsResponse getAiConfigCallStats(AiConfigCallStatsRequest request);
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,12 @@
|
|||||||
package com.emotion.service.impl;
|
package com.emotion.service.impl;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
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.dto.response.DashboardStatsResponse;
|
||||||
import com.emotion.entity.*;
|
import com.emotion.entity.*;
|
||||||
|
import com.emotion.mapper.AiConfigMapper;
|
||||||
import com.emotion.service.*;
|
import com.emotion.service.*;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@@ -56,6 +60,9 @@ public class DashboardServiceImpl implements DashboardService {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private AdminService adminService;
|
private AdminService adminService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private AiConfigMapper aiConfigMapper;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private AchievementService achievementService;
|
private AchievementService achievementService;
|
||||||
|
|
||||||
@@ -464,6 +471,26 @@ public class DashboardServiceImpl implements DashboardService {
|
|||||||
return recentLogins;
|
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<AiConfigCallStatsItem> items = aiConfigMapper.selectAiConfigCallStats(limit);
|
||||||
|
return AiConfigCallStatsResponse.builder()
|
||||||
|
.items(items)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 格式化时间描述
|
* 格式化时间描述
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -67,6 +67,28 @@ export interface DashboardStats {
|
|||||||
updateTime: string
|
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',
|
method: 'get',
|
||||||
params: { limit }
|
params: { limit }
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取 AI 配置调用次数统计(按调用次数倒序)
|
||||||
|
* @param limit 返回条数,默认 10
|
||||||
|
*/
|
||||||
|
export function getAiConfigCallStats(limit: number = 10) {
|
||||||
|
return request<AiConfigCallStatsResponse>({
|
||||||
|
url: '/admin/dashboard/aiConfigCallStats',
|
||||||
|
method: 'get',
|
||||||
|
params: { limit }
|
||||||
|
})
|
||||||
}
|
}
|
||||||
@@ -235,7 +235,27 @@
|
|||||||
</el-col>
|
</el-col>
|
||||||
|
|
||||||
<el-col :span="6">
|
<el-col :span="6">
|
||||||
<AiConfigQuickActions />
|
<el-card>
|
||||||
|
<template #header>
|
||||||
|
<span>AI配置调用排行</span>
|
||||||
|
</template>
|
||||||
|
<el-table
|
||||||
|
:data="aiConfigCallStats"
|
||||||
|
style="width: 100%"
|
||||||
|
size="small"
|
||||||
|
height="300"
|
||||||
|
:default-sort="{ prop: 'callCount', order: 'descending' }"
|
||||||
|
>
|
||||||
|
<el-table-column prop="configName" label="配置" min-width="140" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="callCount" label="调用次数" width="100" sortable />
|
||||||
|
<el-table-column label="状态" width="90">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-tag v-if="row.isEnabled === 1" type="success" size="small">启用</el-tag>
|
||||||
|
<el-tag v-else type="info" size="small">禁用</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</el-card>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
</div>
|
</div>
|
||||||
@@ -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 { User, UserFilled, TrendCharts, ChatDotRound, Setting, CircleCheck, CircleClose, Star } from '@element-plus/icons-vue'
|
||||||
import * as echarts from 'echarts'
|
import * as echarts from 'echarts'
|
||||||
import { countEnabledConfigs, countDisabledConfigs, countDefaultConfigs } from '@/api/aiconfig'
|
import { countEnabledConfigs, countDisabledConfigs, countDefaultConfigs } from '@/api/aiconfig'
|
||||||
import { getDashboardStats, getUserGrowthTrends, getRecentLogins, type DashboardStats, type UserGrowthTrend, type RecentLogin } from '@/api/dashboard'
|
import { getDashboardStats, getUserGrowthTrends, getRecentLogins, getAiConfigCallStats, type DashboardStats, type UserGrowthTrend, type RecentLogin, type AiConfigCallStatsItem } from '@/api/dashboard'
|
||||||
import AiConfigQuickActions from '@/components/AiConfigQuickActions.vue'
|
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
|
|
||||||
const userChartRef = ref<HTMLElement>()
|
const userChartRef = ref<HTMLElement>()
|
||||||
@@ -296,6 +315,7 @@ const aiStats = reactive({
|
|||||||
|
|
||||||
const recentLogins = ref<RecentLogin[]>([])
|
const recentLogins = ref<RecentLogin[]>([])
|
||||||
const userGrowthTrends = ref<UserGrowthTrend[]>([])
|
const userGrowthTrends = ref<UserGrowthTrend[]>([])
|
||||||
|
const aiConfigCallStats = ref<AiConfigCallStatsItem[]>([])
|
||||||
|
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
|
|
||||||
@@ -338,6 +358,9 @@ const fetchDashboardData = async () => {
|
|||||||
|
|
||||||
// 获取AI配置统计(保持原有逻辑)
|
// 获取AI配置统计(保持原有逻辑)
|
||||||
await fetchAiStats()
|
await fetchAiStats()
|
||||||
|
|
||||||
|
// 获取AI配置调用排行
|
||||||
|
await fetchAiConfigCallStats()
|
||||||
|
|
||||||
// 更新图表数据
|
// 更新图表数据
|
||||||
updateUserChart()
|
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
|
let userChart: any = null
|
||||||
|
|
||||||
const initUserChart = () => {
|
const initUserChart = () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user