服务层重构与优化:补全所有ServiceImpl实现类,修复RestTemplate注入,完善DTO与配置,保证编译与启动通过

This commit is contained in:
2025-07-24 14:15:31 +08:00
parent 873b8e55da
commit cf4d73ceff
95 changed files with 5889 additions and 2282 deletions
@@ -1,11 +1,10 @@
package com.emotionmuseum.ai.controller;
import com.emotionmuseum.ai.dto.*;
import com.emotionmuseum.ai.entity.Conversation;
import com.emotionmuseum.ai.entity.Message;
import com.emotionmuseum.ai.request.*;
import com.emotionmuseum.ai.response.*;
import com.emotionmuseum.ai.service.AiChatService;
import com.emotionmuseum.ai.service.ConversationDbService;
import com.emotionmuseum.common.dto.PageQuery;
import com.emotionmuseum.common.response.BasePageResponse;
import com.emotionmuseum.common.result.Result;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
@@ -16,7 +15,6 @@ import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import jakarta.validation.Valid;
import java.util.List;
/**
* AI聊天控制器
@@ -40,17 +38,15 @@ public class AiChatController {
public Result<CreateConversationResponse> createConversation(
@Valid @RequestBody CreateConversationRequest request) {
log.info("收到创建会话请求: userId={}, title={}", request.getUserId(), request.getTitle());
CreateConversationResponse response = aiChatService.createConversation(request);
return Result.success(response);
}
@Operation(summary = "发送聊天消息")
@PostMapping("/send")
public Result<ChatResponse> sendMessage(@Valid @RequestBody ChatRequest request) {
public Result<AiChatResponse> sendMessage(@Valid @RequestBody AiChatRequest request) {
log.info("收到聊天请求: userId={}, message={}", request.getUserId(), request.getMessage());
ChatResponse response = aiChatService.chat(request);
AiChatResponse response = aiChatService.chat(request);
return Result.success(response);
}
@@ -58,16 +54,14 @@ public class AiChatController {
@PostMapping("/emotion/analyze")
public Result<EmotionAnalysisResponse> analyzeEmotion(@Valid @RequestBody EmotionAnalysisRequest request) {
log.info("收到情绪分析请求: userId={}, text={}", request.getUserId(), request.getText());
EmotionAnalysisResponse response = aiChatService.analyzeEmotion(request);
return Result.success(response);
}
@Operation(summary = "流式聊天")
@PostMapping("/stream")
public Result<String> streamChat(@Valid @RequestBody ChatRequest request) {
public Result<String> streamChat(@Valid @RequestBody AiChatRequest request) {
log.info("收到流式聊天请求: userId={}", request.getUserId());
String response = aiChatService.streamChat(request);
return Result.success(response);
}
@@ -76,16 +70,14 @@ public class AiChatController {
@GetMapping("/health")
public Result<Boolean> healthCheck() {
log.info("AI服务健康检查");
boolean isHealthy = aiChatService.healthCheck();
return Result.success(isHealthy);
}
@Operation(summary = "获取AI服务信息")
@GetMapping("/info")
public Result<Object> getServiceInfo() {
public Result<String> getServiceInfo() {
log.info("获取AI服务信息");
return Result.success("Emotion Museum AI Service - Powered by Spring AI & Coze");
}
@@ -1,6 +1,7 @@
package com.emotionmuseum.ai.controller;
import com.emotionmuseum.ai.dto.*;
import com.emotionmuseum.ai.request.*;
import com.emotionmuseum.ai.response.*;
import com.emotionmuseum.ai.service.GuestChatService;
import com.emotionmuseum.common.result.Result;
import io.swagger.v3.oas.annotations.Operation;
@@ -0,0 +1,64 @@
package com.emotionmuseum.ai.request;
import com.emotionmuseum.common.request.BaseRequest;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import java.util.List;
/**
* AI聊天请求
*
* @author emotion-museum
* @since 2025-07-24
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Schema(description = "AI聊天请求")
public class AiChatRequest extends BaseRequest {
private static final long serialVersionUID = 1L;
@Schema(description = "用户ID", example = "user_123")
@NotBlank(message = "用户ID不能为空")
private String userId;
@Schema(description = "消息内容", example = "我今天感觉有点焦虑,不知道该怎么办")
@NotBlank(message = "消息内容不能为空")
@Size(max = 2000, message = "消息内容不能超过2000字符")
private String message;
@Schema(description = "对话ID(可选)", example = "conv_123456")
private String conversationId;
@Schema(description = "消息类型", example = "text")
private String type = "text";
@Schema(description = "聊天历史(可选)")
private List<ChatMessage> history;
@Schema(description = "是否需要情绪分析", example = "true")
private Boolean needEmotionAnalysis = true;
@Schema(description = "上下文信息")
private String context;
/**
* 聊天消息
*/
@Data
@Schema(description = "聊天消息")
public static class ChatMessage {
@Schema(description = "角色", example = "user")
private String role; // user, assistant
@Schema(description = "消息内容")
private String content;
@Schema(description = "时间戳")
private Long timestamp;
}
}
@@ -0,0 +1,38 @@
package com.emotionmuseum.ai.request;
import com.emotionmuseum.common.request.BasePageRequest;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 会话列表请求
*
* @author emotion-museum
* @since 2025-07-24
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Schema(description = "会话列表请求")
public class ConversationListRequest extends BasePageRequest {
private static final long serialVersionUID = 1L;
@Schema(description = "用户ID", example = "user_123")
private String userId;
@Schema(description = "会话类型", example = "emotion_chat")
private String type;
@Schema(description = "会话状态", example = "active")
private String status;
@Schema(description = "用户类型", example = "guest")
private String userType;
@Schema(description = "开始时间", example = "2025-01-01 00:00:00")
private String startTime;
@Schema(description = "结束时间", example = "2025-12-31 23:59:59")
private String endTime;
}
@@ -0,0 +1,38 @@
package com.emotionmuseum.ai.request;
import com.emotionmuseum.common.request.BaseRequest;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import jakarta.validation.constraints.NotBlank;
/**
* 创建会话请求
*
* @author emotion-museum
* @since 2025-07-24
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Schema(description = "创建会话请求")
public class CreateConversationRequest extends BaseRequest {
private static final long serialVersionUID = 1L;
@Schema(description = "用户ID", example = "user_123")
@NotBlank(message = "用户ID不能为空")
private String userId;
@Schema(description = "会话标题", example = "今日心情分享")
private String title;
@Schema(description = "会话类型", example = "emotion_chat")
private String type = "emotion_chat";
@Schema(description = "初始消息", example = "你好,我想聊聊今天的心情")
private String initialMessage;
@Schema(description = "上下文信息")
private String context;
}
@@ -0,0 +1,41 @@
package com.emotionmuseum.ai.request;
import com.emotionmuseum.common.request.BaseRequest;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
/**
* 情绪分析请求
*
* @author emotion-museum
* @since 2025-07-24
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Schema(description = "情绪分析请求")
public class EmotionAnalysisRequest extends BaseRequest {
private static final long serialVersionUID = 1L;
@Schema(description = "用户ID", example = "user_123")
@NotBlank(message = "用户ID不能为空")
private String userId;
@Schema(description = "待分析文本", example = "我今天感觉很沮丧,工作压力很大")
@NotBlank(message = "待分析文本不能为空")
@Size(max = 1000, message = "待分析文本不能超过1000字符")
private String text;
@Schema(description = "分析类型", example = "detailed")
private String analysisType = "detailed"; // simple, detailed
@Schema(description = "语言", example = "zh")
private String language = "zh";
@Schema(description = "上下文信息")
private String context;
}
@@ -0,0 +1,43 @@
package com.emotionmuseum.ai.request;
import com.emotionmuseum.common.request.BaseRequest;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
/**
* 访客聊天请求
*
* @author emotion-museum
* @since 2025-07-24
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Schema(description = "访客聊天请求")
public class GuestChatRequest extends BaseRequest {
private static final long serialVersionUID = 1L;
@Schema(description = "消息内容", example = "你好,我想聊聊今天的心情")
@NotBlank(message = "消息内容不能为空")
@Size(max = 2000, message = "消息内容不能超过2000字符")
private String message;
@Schema(description = "会话ID(可选,如果不提供则创建新会话)", example = "conv_123456")
private String conversationId;
@Schema(description = "会话标题(创建新会话时使用)", example = "今日心情分享")
private String title;
@Schema(description = "消息类型", example = "text")
private String messageType = "text";
@Schema(description = "是否流式响应", example = "false")
private Boolean stream = false;
@Schema(description = "附加上下文信息")
private String context;
}
@@ -0,0 +1,100 @@
package com.emotionmuseum.ai.response;
import com.emotionmuseum.common.response.BaseResponse;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
/**
* AI聊天响应
*
* @author emotion-museum
* @since 2025-07-24
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Schema(description = "AI聊天响应")
public class AiChatResponse extends BaseResponse {
private static final long serialVersionUID = 1L;
@Schema(description = "消息ID")
private String messageId;
@Schema(description = "对话ID")
private String conversationId;
@Schema(description = "AI回复内容")
private String content;
@Schema(description = "消息类型", example = "text")
private String type = "text";
@Schema(description = "发送者", example = "assistant")
private String sender = "assistant";
@Schema(description = "响应时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime responseTime;
@Schema(description = "情绪分析结果")
private EmotionAnalysisResponse emotionAnalysis;
@Schema(description = "使用情况")
private Usage usage;
@Schema(description = "元数据")
private Map<String, Object> metadata;
@Schema(description = "是否为多条消息")
private Boolean multipleMessages = false;
@Schema(description = "消息数量")
private Integer messageCount = 1;
@Schema(description = "所有消息ID列表(当拆分为多条消息时)")
private List<String> messageIds;
/**
* 使用情况
*/
@Data
@Schema(description = "使用情况")
public static class Usage {
@Schema(description = "输入Token数")
private Integer promptTokens;
@Schema(description = "输出Token数")
private Integer completionTokens;
@Schema(description = "总Token数")
private Integer totalTokens;
}
/**
* 情绪分析响应
*/
@Data
@Schema(description = "情绪分析响应")
public static class EmotionAnalysisResponse {
@Schema(description = "情绪类型")
private String emotionType;
@Schema(description = "情绪强度")
private Double intensity;
@Schema(description = "情绪描述")
private String description;
@Schema(description = "建议")
private String suggestion;
@Schema(description = "置信度")
private Double confidence;
}
}
@@ -0,0 +1,61 @@
package com.emotionmuseum.ai.response;
import com.emotionmuseum.common.response.BaseResponse;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.Builder;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import java.time.LocalDateTime;
/**
* 会话列表响应
*
* @author emotion-museum
* @since 2025-07-24
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Schema(description = "会话列表响应")
public class ConversationListResponse extends BaseResponse {
private static final long serialVersionUID = 1L;
@Schema(description = "会话ID")
private String conversationId;
@Schema(description = "会话标题")
private String title;
@Schema(description = "会话类型")
private String type;
@Schema(description = "会话状态")
private String status;
@Schema(description = "用户ID")
private String userId;
@Schema(description = "用户类型")
private String userType;
@Schema(description = "消息数量")
private Integer messageCount;
@Schema(description = "最后活跃时间")
private LocalDateTime lastActiveTime;
@Schema(description = "创建时间")
private LocalDateTime createTime;
@Schema(description = "主要情绪")
private String primaryEmotion;
@Schema(description = "情绪强度")
private Double emotionIntensity;
@Schema(description = "Coze会话ID")
private String cozeConversationId;
}
@@ -0,0 +1,53 @@
package com.emotionmuseum.ai.response;
import com.emotionmuseum.common.response.BaseResponse;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDateTime;
import java.util.Map;
/**
* 创建会话响应
*
* @author emotion-museum
* @since 2025-07-24
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Schema(description = "创建会话响应")
public class CreateConversationResponse extends BaseResponse {
private static final long serialVersionUID = 1L;
@Schema(description = "会话ID")
private String conversationId;
@Schema(description = "用户ID")
private String userId;
@Schema(description = "会话标题")
private String title;
@Schema(description = "会话类型")
private String type;
@Schema(description = "会话状态", example = "active")
private String status = "active";
@Schema(description = "创建时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;
@Schema(description = "更新时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime updateTime;
@Schema(description = "Coze会话ID")
private String cozeConversationId;
@Schema(description = "元数据")
private Map<String, Object> metadata;
}
@@ -0,0 +1,69 @@
package com.emotionmuseum.ai.response;
import com.emotionmuseum.common.response.BaseResponse;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
/**
* 情绪分析响应
*
* @author emotion-museum
* @since 2025-07-24
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Schema(description = "情绪分析响应")
public class EmotionAnalysisResponse extends BaseResponse {
private static final long serialVersionUID = 1L;
@Schema(description = "主要情绪", example = "焦虑")
private String primaryEmotion;
@Schema(description = "情绪强度", example = "0.75")
private Double intensity;
@Schema(description = "情绪极性", example = "negative")
private String polarity; // positive, negative, neutral
@Schema(description = "置信度", example = "0.85")
private Double confidence;
@Schema(description = "情绪分布")
private List<EmotionScore> emotions;
@Schema(description = "关键词")
private List<String> keywords;
@Schema(description = "建议")
private String suggestion;
@Schema(description = "分析时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime analysisTime;
@Schema(description = "额外信息")
private Map<String, Object> metadata;
/**
* 情绪得分
*/
@Data
@Schema(description = "情绪得分")
public static class EmotionScore {
@Schema(description = "情绪名称")
private String emotion;
@Schema(description = "得分")
private Double score;
@Schema(description = "描述")
private String description;
}
}
@@ -0,0 +1,111 @@
package com.emotionmuseum.ai.response;
import com.emotionmuseum.common.response.BaseResponse;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.Builder;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import java.time.LocalDateTime;
/**
* 访客聊天响应
*
* @author emotion-museum
* @since 2025-07-24
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Schema(description = "访客聊天响应")
public class GuestChatResponse extends BaseResponse {
private static final long serialVersionUID = 1L;
@Schema(description = "访客用户ID")
private String guestUserId;
@Schema(description = "访客昵称")
private String guestNickname;
@Schema(description = "会话ID")
private String conversationId;
@Schema(description = "会话标题")
private String conversationTitle;
@Schema(description = "用户消息ID")
private String userMessageId;
@Schema(description = "AI回复消息ID")
private String aiMessageId;
@Schema(description = "用户消息内容")
private String userMessage;
@Schema(description = "AI回复内容")
private String aiReply;
@Schema(description = "消息时间戳")
private LocalDateTime messageTimestamp;
@Schema(description = "会话状态")
private String conversationStatus;
@Schema(description = "是否为新会话")
private Boolean isNewConversation;
@Schema(description = "Coze聊天ID")
private String cozeChatId;
@Schema(description = "情绪分析结果")
private EmotionAnalysisResult emotionAnalysis;
@Schema(description = "Token使用情况")
private TokenUsage tokenUsage;
@Schema(description = "错误信息(如果有)")
private String errorMessage;
/**
* 情绪分析结果内部类
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(description = "情绪分析结果")
public static class EmotionAnalysisResult {
@Schema(description = "主要情绪")
private String primaryEmotion;
@Schema(description = "情绪得分")
private Double emotionScore;
@Schema(description = "置信度")
private Double confidence;
@Schema(description = "情绪趋势")
private String emotionTrend;
}
/**
* Token使用情况内部类
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(description = "Token使用情况")
public static class TokenUsage {
@Schema(description = "输入Token数")
private Integer promptTokens;
@Schema(description = "输出Token数")
private Integer completionTokens;
@Schema(description = "总Token数")
private Integer totalTokens;
}
}