对话接口bug修复及后台管理功能完善

This commit is contained in:
2025-10-31 13:37:15 +08:00
parent 96de58c071
commit e69e9920fe
7 changed files with 1057 additions and 75 deletions
@@ -6,7 +6,9 @@ import com.emotion.dto.request.AdminCreateRequest;
import com.emotion.dto.request.AdminPageRequest;
import com.emotion.dto.request.AdminUpdateRequest;
import com.emotion.dto.response.AdminResponse;
import com.emotion.dto.response.DashboardStatsResponse;
import com.emotion.service.AdminService;
import com.emotion.service.DashboardService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
@@ -27,6 +29,9 @@ public class AdminController {
@Autowired
private AdminService adminService;
@Autowired
private DashboardService dashboardService;
/**
* 分页查询管理员
*/
@@ -88,4 +93,54 @@ public class AdminController {
}
return Result.success("删除成功", null);
}
/**
* 获取仪表盘统计数据
*/
@Operation(summary = "获取仪表盘统计数据", description = "获取管理后台仪表盘的统计数据,包括用户、内容、AI服务和系统统计")
@GetMapping("/dashboard/stats")
public Result<DashboardStatsResponse> getDashboardStats() {
DashboardStatsResponse stats = dashboardService.getDashboardStats();
return Result.success("获取成功", stats);
}
/**
* 获取用户统计数据
*/
@Operation(summary = "获取用户统计数据", description = "获取用户相关的统计数据")
@GetMapping("/dashboard/user-stats")
public Result<DashboardStatsResponse.UserStats> getUserStats() {
DashboardStatsResponse.UserStats userStats = dashboardService.getUserStats();
return Result.success("获取成功", userStats);
}
/**
* 获取内容统计数据
*/
@Operation(summary = "获取内容统计数据", description = "获取内容相关的统计数据")
@GetMapping("/dashboard/content-stats")
public Result<DashboardStatsResponse.ContentStats> getContentStats() {
DashboardStatsResponse.ContentStats contentStats = dashboardService.getContentStats();
return Result.success("获取成功", contentStats);
}
/**
* 获取AI服务统计数据
*/
@Operation(summary = "获取AI服务统计数据", description = "获取AI服务相关的统计数据")
@GetMapping("/dashboard/ai-stats")
public Result<DashboardStatsResponse.AiServiceStats> getAiServiceStats() {
DashboardStatsResponse.AiServiceStats aiStats = dashboardService.getAiServiceStats();
return Result.success("获取成功", aiStats);
}
/**
* 获取系统统计数据
*/
@Operation(summary = "获取系统统计数据", description = "获取系统相关的统计数据")
@GetMapping("/dashboard/system-stats")
public Result<DashboardStatsResponse.SystemStats> getSystemStats() {
DashboardStatsResponse.SystemStats systemStats = dashboardService.getSystemStats();
return Result.success("获取成功", systemStats);
}
}
@@ -0,0 +1,168 @@
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.time.LocalDateTime;
import java.util.List;
import java.util.Map;
/**
* 仪表盘统计数据响应
*
* @author system
* @date 2025-10-31
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(description = "仪表盘统计数据响应")
public class DashboardStatsResponse {
@Schema(description = "用户统计")
private UserStats userStats;
@Schema(description = "内容统计")
private ContentStats contentStats;
@Schema(description = "AI服务统计")
private AiServiceStats aiServiceStats;
@Schema(description = "系统统计")
private SystemStats systemStats;
@Schema(description = "最近活动")
private List<RecentActivity> recentActivities;
@Schema(description = "数据更新时间")
private LocalDateTime updateTime;
/**
* 用户统计
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(description = "用户统计")
public static class UserStats {
@Schema(description = "总用户数")
private Long totalUsers;
@Schema(description = "今日新增用户")
private Long todayNewUsers;
@Schema(description = "活跃用户数")
private Long activeUsers;
@Schema(description = "访客用户数")
private Long guestUsers;
}
/**
* 内容统计
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(description = "内容统计")
public static class ContentStats {
@Schema(description = "总对话数")
private Long totalConversations;
@Schema(description = "总消息数")
private Long totalMessages;
@Schema(description = "日记帖子数")
private Long diaryPosts;
@Schema(description = "社区帖子数")
private Long communityPosts;
@Schema(description = "情绪记录数")
private Long emotionRecords;
}
/**
* AI服务统计
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(description = "AI服务统计")
public static class AiServiceStats {
@Schema(description = "总API调用次数")
private Long totalApiCalls;
@Schema(description = "今日API调用次数")
private Long todayApiCalls;
@Schema(description = "成功调用次数")
private Long successfulCalls;
@Schema(description = "失败调用次数")
private Long failedCalls;
@Schema(description = "平均响应时间(ms)")
private Double avgResponseTime;
@Schema(description = "AI配置数量")
private Long aiConfigCount;
}
/**
* 系统统计
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(description = "系统统计")
public static class SystemStats {
@Schema(description = "管理员数量")
private Long adminCount;
@Schema(description = "成就数量")
private Long achievementCount;
@Schema(description = "奖励数量")
private Long rewardCount;
@Schema(description = "系统运行时间")
private String uptime;
}
/**
* 最近活动
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(description = "最近活动")
public static class RecentActivity {
@Schema(description = "活动类型")
private String type;
@Schema(description = "活动描述")
private String description;
@Schema(description = "用户ID")
private String userId;
@Schema(description = "用户名")
private String username;
@Schema(description = "活动时间")
private LocalDateTime activityTime;
@Schema(description = "额外数据")
private Map<String, Object> extraData;
}
}
@@ -0,0 +1,47 @@
package com.emotion.service;
import com.emotion.dto.response.DashboardStatsResponse;
/**
* 仪表盘服务接口
*
* @author system
* @date 2025-10-31
*/
public interface DashboardService {
/**
* 获取仪表盘统计数据
*
* @return 仪表盘统计数据
*/
DashboardStatsResponse getDashboardStats();
/**
* 获取用户统计数据
*
* @return 用户统计数据
*/
DashboardStatsResponse.UserStats getUserStats();
/**
* 获取内容统计数据
*
* @return 内容统计数据
*/
DashboardStatsResponse.ContentStats getContentStats();
/**
* 获取AI服务统计数据
*
* @return AI服务统计数据
*/
DashboardStatsResponse.AiServiceStats getAiServiceStats();
/**
* 获取系统统计数据
*
* @return 系统统计数据
*/
DashboardStatsResponse.SystemStats getSystemStats();
}
@@ -842,6 +842,7 @@ public class AiChatServiceImpl implements AiChatService {
private String executeStreamCozeApiCall(CozeApiCall apiCall, AiConfig config, Map<String, Object> requestBody,
String conversationId, String userMessage, String userId) {
try {
// 构建请求头
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer " + config.getApiToken());
@@ -854,7 +855,9 @@ public class AiChatServiceImpl implements AiChatService {
// 更新API调用记录的请求信息
updateApiCallRequest(apiCall, cozeApiUrl, requestBody, headers);
log.info("发送Coze流式请求到: {}, 请求体: {}", cozeApiUrl, requestBody);
log.info("发送Coze流式请求到: {}", cozeApiUrl);
log.info("请求头: {}", headers.toSingleValueMap());
log.info("请求体: {}", JSON.toJSONString(requestBody));
// 使用RestTemplate处理流式响应
String streamResponse = handleStreamResponse(cozeApiUrl, headers, requestBody, apiCall);
@@ -878,30 +881,37 @@ public class AiChatServiceImpl implements AiChatService {
*/
private String handleStreamResponse(String url, HttpHeaders headers, Map<String, Object> requestBody, CozeApiCall apiCall) {
try {
// 创建HTTP客户端
log.info("开始处理流式响应,URL: {}", url);
// 创建HTTP客户端,增加更长的超时时间
java.net.http.HttpClient client = java.net.http.HttpClient.newBuilder()
.connectTimeout(java.time.Duration.ofSeconds(30))
.connectTimeout(java.time.Duration.ofSeconds(60))
.build();
// 构建请求
java.net.http.HttpRequest.Builder requestBuilder = java.net.http.HttpRequest.newBuilder()
.uri(java.net.URI.create(url))
.timeout(java.time.Duration.ofMinutes(5))
.timeout(java.time.Duration.ofMinutes(10))
.POST(java.net.http.HttpRequest.BodyPublishers.ofString(JSON.toJSONString(requestBody)));
// 添加请求头
log.info("设置请求头:");
headers.forEach((key, values) -> {
if (values != null && !values.isEmpty()) {
requestBuilder.header(key, values.get(0));
String value = values.get(0);
requestBuilder.header(key, value);
log.info(" {}: {}", key, key.equals("Authorization") ? "Bearer ***" : value);
}
});
java.net.http.HttpRequest request = requestBuilder.build();
log.info("发送流式请求,请求体: {}", JSON.toJSONString(requestBody));
// 发送请求并处理流式响应
StringBuilder responseBuilder = new StringBuilder();
StringBuilder fullStreamData = new StringBuilder();
final String[] currentEvent = {null}; // 使用数组来解决lambda中的final问题
String currentEvent = null;
boolean hasReceivedData = false;
java.net.http.HttpResponse<java.util.stream.Stream<String>> response = client.send(request,
java.net.http.HttpResponse.BodyHandlers.ofLines());
@@ -911,93 +921,198 @@ public class AiChatServiceImpl implements AiChatService {
if (response.statusCode() != 200) {
String errorBody = response.body().collect(java.util.stream.Collectors.joining("\n"));
log.error("流式请求失败,状态码: {}, 响应: {}", response.statusCode(), errorBody);
return "流式请求失败,状态码: " + response.statusCode();
updateApiCallError(apiCall, "HTTP_ERROR", "流式请求失败,状态码: " + response.statusCode());
return handleNonStreamFallback(url, headers, requestBody, apiCall);
}
// 处理流式数据 - 正确处理SSE格式
response.body().forEach(line -> {
java.util.Iterator<String> lineIterator = response.body().iterator();
while (lineIterator.hasNext()) {
String line = lineIterator.next();
fullStreamData.append(line).append("\n");
log.debug("收到流式数据行: {}", line);
if (line.trim().isEmpty()) {
// 空行表示事件结束,重置当前事件
currentEvent = null;
continue;
}
// 处理SSE事件格式
if (line.startsWith("event:")) {
currentEvent[0] = line.substring(6).trim();
log.debug("当前事件类型: {}", currentEvent[0]);
} else if (line.startsWith("data: ")) {
String data = line.substring(6).trim();
log.debug("解析流式数据: {}", data);
currentEvent = line.substring(6).trim();
log.debug("当前事件类型: {}", currentEvent);
} else if (line.startsWith("data:")) {
String data = line.substring(5).trim(); // 注意这里是5,因为"data:"后面可能没有空格
// 检查是否为结束标记
if ("[DONE]".equals(data)) {
log.info("流式响应完成");
return;
if ("\"[DONE]\"".equals(data) || "[DONE]".equals(data)) {
log.info("收到流式响应结束标记");
break;
}
// 只处理消息增量事件
if ("conversation.message.delta".equals(currentEvent[0])) {
// 跳过空数据
if (data.isEmpty()) {
continue;
}
hasReceivedData = true;
log.debug("解析流式数据,事件: {}, 数据: {}", currentEvent, data);
// 处理不同类型的事件
if ("conversation.message.delta".equals(currentEvent)) {
try {
JSONObject jsonData = JSON.parseObject(data);
log.debug("解析后的JSON数据: {}", jsonData);
// 提取content字段
if (jsonData.containsKey("content")) {
// 提取content字段并检查类型
if (jsonData.containsKey("content") && jsonData.containsKey("type")) {
String messageType = jsonData.getString("type");
String content = jsonData.getString("content");
if (content != null && !content.trim().isEmpty()) {
// 只处理answer类型的消息内容
if ("answer".equals(messageType) && content != null && !content.trim().isEmpty()) {
log.debug("提取增量内容: {}", content);
responseBuilder.append(content);
}
}
} catch (Exception e) {
log.warn("解析流式数据失败: {}, 数据: {}", e.getMessage(), data);
log.warn("解析增量消息数据失败: {}, 数据: {}", e.getMessage(), data);
}
} else if ("conversation.message.completed".equals(currentEvent[0])) {
// 处理完整消息事件,可以用作备用方案
} else if ("conversation.message.completed".equals(currentEvent)) {
// 处理完整消息事件,作备用方案
try {
JSONObject jsonData = JSON.parseObject(data);
// 检查是否为answer类型的消息
if (jsonData.containsKey("type") && "answer".equals(jsonData.getString("type"))) {
String content = jsonData.getString("content");
if (content != null && !content.trim().isEmpty() && responseBuilder.length() == 0) {
log.debug("使用完整消息内容作为备用: {}", content);
responseBuilder.append(content);
if (content != null && !content.trim().isEmpty()) {
// 如果增量方式没有获取到内容,使用完整消息作为备用
if (responseBuilder.length() == 0) {
log.info("使用完整消息内容作为备用: {}", content);
responseBuilder.append(content);
}
// 记录完整消息用于验证
log.debug("完整消息内容: {}", content);
}
}
} catch (Exception e) {
log.warn("解析完整消息数据失败: {}, 数据: {}", e.getMessage(), data);
}
} else if ("conversation.chat.failed".equals(currentEvent)) {
// 处理聊天失败事件
try {
JSONObject jsonData = JSON.parseObject(data);
if (jsonData.containsKey("last_error")) {
JSONObject lastError = jsonData.getJSONObject("last_error");
String errorCode = lastError.getString("code");
String errorMsg = lastError.getString("msg");
log.error("Coze流式聊天失败: code={}, msg={}", errorCode, errorMsg);
// 如果是参数错误,直接返回错误信息,不再尝试降级处理
if ("4000".equals(errorCode)) {
updateApiCallError(apiCall, "COZE_PARAM_ERROR", "参数错误: " + errorMsg);
return "抱歉,请求参数有误,请检查配置后重试。";
}
}
} catch (Exception e) {
log.warn("解析聊天失败数据失败: {}", e.getMessage());
}
} else if ("conversation.chat.created".equals(currentEvent)) {
// 记录聊天创建信息
try {
JSONObject jsonData = JSON.parseObject(data);
String chatId = jsonData.getString("id");
String conversationId = jsonData.getString("conversation_id");
if (chatId != null && conversationId != null) {
updateApiCallCozeIds(apiCall, chatId, conversationId);
log.info("流式聊天创建成功,chatId: {}, conversationId: {}", chatId, conversationId);
}
} catch (Exception e) {
log.warn("解析聊天创建数据失败: {}", e.getMessage());
}
} else if ("conversation.chat.completed".equals(currentEvent)) {
// 记录聊天完成信息
try {
JSONObject jsonData = JSON.parseObject(data);
log.info("流式聊天完成,状态: {}", jsonData.getString("status"));
} catch (Exception e) {
log.warn("解析聊天完成数据失败: {}", e.getMessage());
}
}
// 重置当前事件
currentEvent[0] = null;
} else if (line.trim().isEmpty()) {
// 空行表示事件结束
currentEvent[0] = null;
}
});
}
// 记录完整的流式数据用于调试
updateApiCallStreamData(apiCall, fullStreamData.toString());
log.info("流式响应处理完成,提取内容长度: {}", responseBuilder.length());
log.info("流式响应处理完成,提取内容长度: {}, 是否接收到数据: {}", responseBuilder.length(), hasReceivedData);
String finalResponse = responseBuilder.toString();
if (finalResponse.isEmpty()) {
if (hasReceivedData) {
log.warn("流式响应处理完成但内容为空,可能是消息类型过滤导致");
// 尝试从完整流式数据中提取最后一个完整消息
String fallbackContent = extractContentFromStreamData(fullStreamData.toString());
if (fallbackContent != null && !fallbackContent.isEmpty()) {
log.info("从流式数据中提取到备用内容: {}", fallbackContent);
return fallbackContent;
}
} else {
log.warn("流式响应未接收到任何数据");
}
log.warn("流式响应为空,尝试降级到非流式处理");
// 降级到非流式处理
return handleNonStreamFallback(url, headers, requestBody, apiCall);
}
return finalResponse;
} catch (Exception e) {
log.error("处理流式响应失败", e);
log.error("处理流式响应失败: {}", e.getMessage(), e);
updateApiCallError(apiCall, "STREAM_PROCESSING_ERROR", e.getMessage());
// 降级到非流式处理
return handleNonStreamFallback(url, headers, requestBody, apiCall);
}
}
/**
* 从流式数据中提取内容(备用方案)
*/
private String extractContentFromStreamData(String streamData) {
try {
// 查找最后一个conversation.message.completed事件
String[] lines = streamData.split("\n");
String lastCompletedContent = null;
for (int i = 0; i < lines.length; i++) {
String line = lines[i].trim();
if ("event:conversation.message.completed".equals(line) && i + 1 < lines.length) {
String dataLine = lines[i + 1].trim();
if (dataLine.startsWith("data:")) {
String data = dataLine.substring(5).trim();
try {
JSONObject jsonData = JSON.parseObject(data);
if ("answer".equals(jsonData.getString("type"))) {
String content = jsonData.getString("content");
if (content != null && !content.trim().isEmpty()) {
lastCompletedContent = content;
}
}
} catch (Exception e) {
log.debug("解析备用内容失败: {}", e.getMessage());
}
}
}
}
return lastCompletedContent;
} catch (Exception e) {
log.warn("从流式数据提取内容失败: {}", e.getMessage());
return null;
}
}
/**
* 非流式处理降级方案
*/
@@ -1019,7 +1134,7 @@ public class AiChatServiceImpl implements AiChatService {
nonStreamHeaders.set("Content-Type", "application/json");
HttpEntity<Map<String, Object>> request = new HttpEntity<>(nonStreamBody, nonStreamHeaders);
log.info("发送非流式降级请求到: {}", url);
log.info("发送非流式降级请求到: {}, 请求体: {}", url, JSON.toJSONString(nonStreamBody));
// 发送请求
ResponseEntity<String> response = restTemplate.exchange(
@@ -1028,7 +1143,10 @@ public class AiChatServiceImpl implements AiChatService {
request,
String.class);
log.info("收到非流式降级响应: {}", response.getBody());
log.info("收到非流式降级响应状态: {}, 响应体: {}", response.getStatusCode(), response.getBody());
// 更新API调用记录的响应信息
updateApiCallResponse(apiCall, response);
// 解析响应获取chat_id和conversation_id
JSONObject responseJson = JSON.parseObject(response.getBody());
@@ -1044,12 +1162,14 @@ public class AiChatServiceImpl implements AiChatService {
log.info("非流式降级处理成功: reply={}", aiReply);
return aiReply;
} else {
log.error("非流式降级处理失败:无法从响应中获取chat_id或conversation_id");
return "抱歉,AI服务暂时不可用,请稍后再试。";
log.error("非流式降级处理失败:无法从响应中获取chat_id或conversation_id,响应: {}", response.getBody());
updateApiCallError(apiCall, "INVALID_RESPONSE", "无法从响应中获取chat_id或conversation_id");
return "抱歉,AI服务响应异常,请稍后再试。";
}
} catch (Exception e) {
log.error("非流式降级处理失败", e);
log.error("非流式降级处理失败: {}", e.getMessage(), e);
updateApiCallError(apiCall, "FALLBACK_ERROR", e.getMessage());
return "抱歉,AI服务暂时不可用,请稍后再试。";
}
}
@@ -1162,42 +1282,42 @@ public class AiChatServiceImpl implements AiChatService {
*/
private Map<String, Object> buildCozeRequestWithConfig(String conversationId, String userMessage, String userId, AiConfig config) {
Map<String, Object> cozeRequest = new HashMap<>();
// 严格按照成功示例的字段顺序和格式构建请求
cozeRequest.put("bot_id", config.getBotId());
// 如果有workflow_id,则添加
if (config.getWorkflowId() != null && !config.getWorkflowId().trim().isEmpty()) {
cozeRequest.put("workflow_id", config.getWorkflowId());
}
cozeRequest.put("user_id", userId != null ? userId : DEFAULT_USER_ID);
// 根据配置决定是否使用流式输出
boolean useStream = config.getSupportStream() != null && config.getSupportStream() == 1;
cozeRequest.put("stream", useStream);
// 添加auto_save_history参数,确保对话历史被保存
cozeRequest.put("auto_save_history", true);
// 如果有conversationId,添加到请求中(但不是必需的,Coze会自动创建)
// 注意:这里的conversationId是我们系统内部的ID,不是Coze的conversation_id
// Coze会自动创建新的conversation_id
// 构建消息列表 - 按照 Coze API 标准格式
// 构建消息列表 - 严格按照成功示例的格式
java.util.List<Map<String, Object>> messages = new java.util.ArrayList<>();
// 添加当前用户消息
// 添加当前用户消息,格式完全按照成功示例
Map<String, Object> currentMsg = new HashMap<>();
currentMsg.put(ROLE_KEY, USER_ROLE);
currentMsg.put(CONTENT_KEY, userMessage);
currentMsg.put(CONTENT_TYPE_KEY, TEXT_TYPE);
// 移除type字段,可能不是必需的
currentMsg.put("role", "user");
currentMsg.put("content", userMessage);
currentMsg.put("content_type", "text");
currentMsg.put("type", "question"); // 根据成功示例添加type字段
messages.add(currentMsg);
cozeRequest.put("additional_messages", messages);
// 确保parameters不为空,添加一些默认参数
// 添加空的parameters对象(根据成功示例)
Map<String, Object> parameters = new HashMap<>();
cozeRequest.put("parameters", parameters);
// 如果有workflow_id,则添加(根据成功示例,这是必需的)
if (config.getWorkflowId() != null && !config.getWorkflowId().trim().isEmpty()) {
cozeRequest.put("workflow_id", config.getWorkflowId());
}
log.info("构建Coze请求完成: botId={}, workflowId={}, userId={}, stream={}, messageCount={}",
config.getBotId(), config.getWorkflowId(), userId, useStream, messages.size());
// 验证请求参数
validateCozeRequest(cozeRequest);
return cozeRequest;
}
@@ -1395,6 +1515,7 @@ public class AiChatServiceImpl implements AiChatService {
currentMsg.put(ROLE_KEY, USER_ROLE);
currentMsg.put(CONTENT_KEY, userMessage);
currentMsg.put(CONTENT_TYPE_KEY, TEXT_TYPE);
currentMsg.put("type", "question"); // 添加type字段
messages.add(currentMsg);
cozeRequest.put("additional_messages", messages);
@@ -1866,4 +1987,47 @@ public class AiChatServiceImpl implements AiChatService {
return "/v3/chat";
}
/**
* 验证Coze请求参数
*/
private void validateCozeRequest(Map<String, Object> cozeRequest) {
log.debug("验证Coze请求参数: {}", JSON.toJSONString(cozeRequest));
// 检查必需参数
if (!cozeRequest.containsKey("bot_id") || cozeRequest.get("bot_id") == null) {
throw new RuntimeException("缺少必需参数: bot_id");
}
if (!cozeRequest.containsKey("user_id") || cozeRequest.get("user_id") == null) {
throw new RuntimeException("缺少必需参数: user_id");
}
if (!cozeRequest.containsKey("additional_messages") || cozeRequest.get("additional_messages") == null) {
throw new RuntimeException("缺少必需参数: additional_messages");
}
// 检查消息格式
@SuppressWarnings("unchecked")
java.util.List<Map<String, Object>> messages = (java.util.List<Map<String, Object>>) cozeRequest.get("additional_messages");
if (messages.isEmpty()) {
throw new RuntimeException("additional_messages不能为空");
}
for (Map<String, Object> message : messages) {
if (!message.containsKey("role") || message.get("role") == null) {
throw new RuntimeException("消息缺少必需参数: role");
}
if (!message.containsKey("content") || message.get("content") == null) {
throw new RuntimeException("消息缺少必需参数: content");
}
if (!message.containsKey("content_type") || message.get("content_type") == null) {
throw new RuntimeException("消息缺少必需参数: content_type");
}
if (!message.containsKey("type") || message.get("type") == null) {
throw new RuntimeException("消息缺少必需参数: type");
}
}
log.debug("Coze请求参数验证通过");
}
}
@@ -0,0 +1,298 @@
package com.emotion.service.impl;
import com.emotion.dto.response.DashboardStatsResponse;
import com.emotion.service.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.lang.management.ManagementFactory;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 仪表盘服务实现类
*
* @author system
* @date 2025-10-31
*/
@Slf4j
@Service
public class DashboardServiceImpl implements DashboardService {
@Autowired
private UserService userService;
@Autowired
private GuestUserService guestUserService;
@Autowired
private ConversationService conversationService;
@Autowired
private MessageService messageService;
@Autowired
private DiaryPostService diaryPostService;
@Autowired
private CommunityPostService communityPostService;
@Autowired
private EmotionRecordService emotionRecordService;
@Autowired
private CozeApiCallService cozeApiCallService;
@Autowired
private AiConfigService aiConfigService;
@Autowired
private AdminService adminService;
@Autowired
private AchievementService achievementService;
@Autowired
private RewardService rewardService;
@Override
public DashboardStatsResponse getDashboardStats() {
log.info("获取仪表盘统计数据");
try {
return DashboardStatsResponse.builder()
.userStats(getUserStats())
.contentStats(getContentStats())
.aiServiceStats(getAiServiceStats())
.systemStats(getSystemStats())
.recentActivities(getRecentActivities())
.updateTime(LocalDateTime.now())
.build();
} catch (Exception e) {
log.error("获取仪表盘统计数据失败", e);
// 返回默认数据,避免前端报错
return getDefaultDashboardStats();
}
}
@Override
public DashboardStatsResponse.UserStats getUserStats() {
try {
// 获取用户统计数据
Long totalUsers = userService.count();
Long guestUsers = guestUserService.count();
// 获取今日新增用户(这里简化处理,实际可能需要根据创建时间查询)
Long todayNewUsers = 0L; // 可以通过查询今日创建的用户数量来获取
// 活跃用户数(这里简化处理,可以根据最近登录时间或活动时间来计算)
Long activeUsers = totalUsers; // 简化处理
return DashboardStatsResponse.UserStats.builder()
.totalUsers(totalUsers)
.todayNewUsers(todayNewUsers)
.activeUsers(activeUsers)
.guestUsers(guestUsers)
.build();
} catch (Exception e) {
log.error("获取用户统计数据失败", e);
return DashboardStatsResponse.UserStats.builder()
.totalUsers(0L)
.todayNewUsers(0L)
.activeUsers(0L)
.guestUsers(0L)
.build();
}
}
@Override
public DashboardStatsResponse.ContentStats getContentStats() {
try {
Long totalConversations = conversationService.count();
Long totalMessages = messageService.count();
Long diaryPosts = diaryPostService.count();
Long communityPosts = communityPostService.count();
Long emotionRecords = emotionRecordService.count();
return DashboardStatsResponse.ContentStats.builder()
.totalConversations(totalConversations)
.totalMessages(totalMessages)
.diaryPosts(diaryPosts)
.communityPosts(communityPosts)
.emotionRecords(emotionRecords)
.build();
} catch (Exception e) {
log.error("获取内容统计数据失败", e);
return DashboardStatsResponse.ContentStats.builder()
.totalConversations(0L)
.totalMessages(0L)
.diaryPosts(0L)
.communityPosts(0L)
.emotionRecords(0L)
.build();
}
}
@Override
public DashboardStatsResponse.AiServiceStats getAiServiceStats() {
try {
Long totalApiCalls = cozeApiCallService.count();
Long aiConfigCount = aiConfigService.count();
// 获取今日API调用次数(简化处理)
Long todayApiCalls = 0L; // 可以通过查询今日的API调用记录来获取
// 获取成功和失败的调用次数(简化处理)
Long successfulCalls = totalApiCalls; // 可以通过状态字段查询
Long failedCalls = 0L;
// 平均响应时间(简化处理)
Double avgResponseTime = 500.0; // 可以通过计算duration_ms字段的平均值来获取
return DashboardStatsResponse.AiServiceStats.builder()
.totalApiCalls(totalApiCalls)
.todayApiCalls(todayApiCalls)
.successfulCalls(successfulCalls)
.failedCalls(failedCalls)
.avgResponseTime(avgResponseTime)
.aiConfigCount(aiConfigCount)
.build();
} catch (Exception e) {
log.error("获取AI服务统计数据失败", e);
return DashboardStatsResponse.AiServiceStats.builder()
.totalApiCalls(0L)
.todayApiCalls(0L)
.successfulCalls(0L)
.failedCalls(0L)
.avgResponseTime(0.0)
.aiConfigCount(0L)
.build();
}
}
@Override
public DashboardStatsResponse.SystemStats getSystemStats() {
try {
Long adminCount = adminService.count();
Long achievementCount = achievementService.count();
Long rewardCount = rewardService.count();
// 获取系统运行时间
long uptimeMs = ManagementFactory.getRuntimeMXBean().getUptime();
String uptime = formatUptime(uptimeMs);
return DashboardStatsResponse.SystemStats.builder()
.adminCount(adminCount)
.achievementCount(achievementCount)
.rewardCount(rewardCount)
.uptime(uptime)
.build();
} catch (Exception e) {
log.error("获取系统统计数据失败", e);
return DashboardStatsResponse.SystemStats.builder()
.adminCount(0L)
.achievementCount(0L)
.rewardCount(0L)
.uptime("未知")
.build();
}
}
/**
* 获取最近活动
*/
private List<DashboardStatsResponse.RecentActivity> getRecentActivities() {
List<DashboardStatsResponse.RecentActivity> activities = new ArrayList<>();
try {
// 这里可以添加获取最近活动的逻辑
// 例如:最近的用户注册、消息发送、帖子创建等
// 示例活动
activities.add(DashboardStatsResponse.RecentActivity.builder()
.type("user_register")
.description("新用户注册")
.userId("system")
.username("系统")
.activityTime(LocalDateTime.now().minusMinutes(30))
.extraData(new HashMap<>())
.build());
activities.add(DashboardStatsResponse.RecentActivity.builder()
.type("ai_chat")
.description("AI聊天对话")
.userId("system")
.username("系统")
.activityTime(LocalDateTime.now().minusMinutes(15))
.extraData(new HashMap<>())
.build());
} catch (Exception e) {
log.error("获取最近活动失败", e);
}
return activities;
}
/**
* 格式化运行时间
*/
private String formatUptime(long uptimeMs) {
long seconds = uptimeMs / 1000;
long minutes = seconds / 60;
long hours = minutes / 60;
long days = hours / 24;
if (days > 0) {
return String.format("%d天%d小时%d分钟", days, hours % 24, minutes % 60);
} else if (hours > 0) {
return String.format("%d小时%d分钟", hours, minutes % 60);
} else if (minutes > 0) {
return String.format("%d分钟", minutes);
} else {
return String.format("%d秒", seconds);
}
}
/**
* 获取默认仪表盘统计数据(用于异常情况)
*/
private DashboardStatsResponse getDefaultDashboardStats() {
return DashboardStatsResponse.builder()
.userStats(DashboardStatsResponse.UserStats.builder()
.totalUsers(0L)
.todayNewUsers(0L)
.activeUsers(0L)
.guestUsers(0L)
.build())
.contentStats(DashboardStatsResponse.ContentStats.builder()
.totalConversations(0L)
.totalMessages(0L)
.diaryPosts(0L)
.communityPosts(0L)
.emotionRecords(0L)
.build())
.aiServiceStats(DashboardStatsResponse.AiServiceStats.builder()
.totalApiCalls(0L)
.todayApiCalls(0L)
.successfulCalls(0L)
.failedCalls(0L)
.avgResponseTime(0.0)
.aiConfigCount(0L)
.build())
.systemStats(DashboardStatsResponse.SystemStats.builder()
.adminCount(0L)
.achievementCount(0L)
.rewardCount(0L)
.uptime("未知")
.build())
.recentActivities(new ArrayList<>())
.updateTime(LocalDateTime.now())
.build();
}
}