This commit is contained in:
2025-07-25 16:18:33 +08:00
parent c09cbc3f01
commit a4c6140ed5
50 changed files with 2249 additions and 1599 deletions
@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
import com.emotion.common.Result;
import com.emotion.entity.EmotionRecord;
import com.emotion.service.EmotionRecordService;
import com.emotion.util.CurrentUserUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
@@ -11,7 +12,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
@@ -23,7 +23,7 @@ import java.util.*;
* @date 2025-07-22
*/
@RestController
@RequestMapping("/api/emotion-records")
@RequestMapping("/emotion-records")
@Tag(name = "情绪记录管理", description = "用户情绪记录的增删改查功能")
public class EmotionRecordController {
@@ -64,16 +64,18 @@ public class EmotionRecordController {
/**
* 获取用户情绪记录列表
*/
@Operation(summary = "获取用户情绪记录列表", description = "分页获取指定用户的情绪记录,按创建时间倒序")
@GetMapping("/user/{userId}")
@Operation(summary = "获取用户情绪记录列表", description = "分页获取当前用户的情绪记录,按创建时间倒序")
@GetMapping("/user")
public Result<IPage<EmotionRecord>> getRecordList(
@Parameter(description = "用户ID") @PathVariable String userId,
@Parameter(description = "页码,从1开始") @RequestParam(defaultValue = "1") Integer current,
@Parameter(description = "每页大小") @RequestParam(defaultValue = "10") Integer size) {
log.info("获取用户情绪记录列表: userId={}, current={}, size={}", userId, current, size);
try {
// 从上下文中获取当前用户ID
String userId = CurrentUserUtil.requireCurrentUserId();
log.info("获取用户情绪记录列表: userId={}, current={}, size={}", userId, current, size);
IPage<EmotionRecord> page = emotionRecordService.getByUserIdWithPage(userId, current, size);
log.info("获取用户情绪记录成功: userId={}, total={}, records={}",
@@ -81,12 +83,44 @@ public class EmotionRecordController {
return Result.success(page);
} catch (IllegalStateException e) {
log.warn("用户认证失败: {}", e.getMessage());
return Result.error(e.getMessage());
} catch (Exception e) {
log.error("获取用户情绪记录失败: userId={}", userId, e);
log.error("获取用户情绪记录失败", e);
return Result.error("获取情绪记录失败: " + e.getMessage());
}
}
/**
* 获取用户最近情绪记录
*/
@Operation(summary = "获取用户最近情绪记录", description = "获取当前用户最近的情绪记录列表")
@GetMapping("/user/recent")
public Result<List<EmotionRecord>> getRecentRecords(
@Parameter(description = "限制数量") @RequestParam(defaultValue = "5") Integer limit) {
try {
// 从上下文中获取当前用户ID
String userId = CurrentUserUtil.requireCurrentUserId();
log.info("获取用户最近情绪记录: userId={}, limit={}", userId, limit);
List<EmotionRecord> records = emotionRecordService.getRecentByUserId(userId, limit);
log.info("获取用户最近情绪记录成功: userId={}, records={}", userId, records.size());
return Result.success(records);
} catch (IllegalStateException e) {
log.warn("用户认证失败: {}", e.getMessage());
return Result.error(e.getMessage());
} catch (Exception e) {
log.error("获取用户最近情绪记录失败", e);
return Result.error("获取最近记录失败: " + e.getMessage());
}
}
/**
* 获取情绪记录详情
*/
@@ -2,8 +2,8 @@ package com.emotion.controller;
import com.emotion.common.Result;
import com.emotion.service.AIChatService;
import com.emotion.util.CurrentUserUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
@@ -13,13 +13,13 @@ import java.util.Map;
/**
* 情绪总结控制器
*
*
* @author emotion-museum
* @date 2025-07-25
*/
@Slf4j
@RestController
@RequestMapping("/api/emotion-summary")
@RequestMapping("/emotion-summary")
@Tag(name = "情绪总结管理", description = "用户情绪记录总结和分析功能")
public class EmotionSummaryController {
@@ -27,39 +27,46 @@ public class EmotionSummaryController {
private AIChatService aiChatService;
@Operation(summary = "生成用户当天的情绪记录总结", description = "基于用户当天的聊天记录生成情绪分析和记录")
@PostMapping("/generate/{userId}")
public Result<Map<String, Object>> generateEmotionSummary(
@Parameter(description = "用户ID") @PathVariable String userId) {
log.info("收到生成情绪记录总结请求: userId={}", userId);
@PostMapping("/generate")
public Result<Map<String, Object>> generateEmotionSummary() {
try {
// 从上下文中获取当前用户ID
String userId = CurrentUserUtil.requireCurrentUserId();
log.info("收到生成情绪记录总结请求: userId={}", userId);
// 调用AI服务生成情绪总结
Map<String, Object> result = aiChatService.generateEmotionSummary(userId);
if ((Boolean) result.get("success")) {
log.info("情绪记录总结生成成功: userId={}", userId);
return Result.success(result, "情绪记录总结生成成功");
return Result.success("情绪记录总结生成成功", result);
} else {
String message = (String) result.get("message");
log.warn("情绪记录总结生成失败: userId={}, message={}", userId, message);
return Result.error(message);
}
} catch (IllegalStateException e) {
log.warn("用户认证失败: {}", e.getMessage());
return Result.error(e.getMessage());
} catch (Exception e) {
log.error("生成情绪记录总结时发生异常: userId={}", userId, e);
log.error("生成情绪记录总结时发生异常", e);
return Result.error("生成情绪记录总结失败: " + e.getMessage());
}
}
@Operation(summary = "获取用户情绪记录总结状态", description = "检查用户今天是否已经生成过情绪记录")
@GetMapping("/status/{userId}")
public Result<Map<String, Object>> getEmotionSummaryStatus(
@Parameter(description = "用户ID") @PathVariable String userId) {
log.info("查询用户情绪记录总结状态: userId={}", userId);
@GetMapping("/status")
public Result<Map<String, Object>> getEmotionSummaryStatus() {
try {
// 从上下文中获取当前用户ID
String userId = CurrentUserUtil.requireCurrentUserId();
log.info("查询用户情绪记录总结状态: userId={}", userId);
// 这里可以添加检查用户今天是否已经生成过情绪记录的逻辑
// 暂时返回基本状态信息
Map<String, Object> status = Map.of(
@@ -67,11 +74,14 @@ public class EmotionSummaryController {
"canGenerate", true,
"message", "可以生成情绪记录总结"
);
return Result.success(status);
} catch (IllegalStateException e) {
log.warn("用户认证失败: {}", e.getMessage());
return Result.error(e.getMessage());
} catch (Exception e) {
log.error("查询情绪记录总结状态时发生异常: userId={}", userId, e);
log.error("查询情绪记录总结状态时发生异常", e);
return Result.error("查询状态失败: " + e.getMessage());
}
}
@@ -8,6 +8,7 @@ import com.emotion.dto.request.MessageCreateRequest;
import com.emotion.dto.response.MessageResponse;
import com.emotion.entity.Message;
import com.emotion.service.MessageService;
import com.emotion.util.CurrentUserUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@@ -126,36 +127,76 @@ public class MessageController {
/**
* 根据用户ID分页查询消息
*/
@GetMapping("/user/{userId}/page")
public Result<PageResult<MessageResponse>> getPageByUserId(@PathVariable String userId,
@Valid PageRequest request) {
IPage<Message> page = messageService.getByUserIdWithPage(userId, Math.toIntExact(request.getCurrent()), Math.toIntExact(request.getSize()));
List<MessageResponse> responses = page.getRecords().stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
@GetMapping("/user/page")
public Result<PageResult<MessageResponse>> getPageByUserId(@Valid PageRequest request) {
PageResult<MessageResponse> pageResult = new PageResult<>();
pageResult.setCurrent(page.getCurrent());
pageResult.setSize(page.getSize());
pageResult.setTotal(page.getTotal());
pageResult.setPages(page.getPages());
pageResult.setRecords(responses);
try {
// 从上下文中获取当前用户ID
String userId = CurrentUserUtil.requireCurrentUserId();
return Result.success(pageResult);
IPage<Message> page = messageService.getByUserIdWithPage(userId, Math.toIntExact(request.getCurrent()),
Math.toIntExact(request.getSize()));
List<MessageResponse> responses = page.getRecords().stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
PageResult<MessageResponse> pageResult = new PageResult<>();
pageResult.setCurrent(page.getCurrent());
pageResult.setSize(page.getSize());
pageResult.setTotal(page.getTotal());
pageResult.setPages(page.getPages());
pageResult.setRecords(responses);
return Result.success(pageResult);
} catch (IllegalStateException e) {
return Result.error(e.getMessage());
}
}
/**
* 根据用户ID和关键词搜索消息
*/
@GetMapping("/user/{userId}/search")
public Result<List<MessageResponse>> searchByUserId(@PathVariable String userId,
@GetMapping("/user/search")
public Result<List<MessageResponse>> searchByUserId(
@RequestParam String keyword,
@RequestParam(defaultValue = "50") Integer limit) {
List<Message> messages = messageService.searchByUserIdAndKeyword(userId, keyword, limit);
List<MessageResponse> responses = messages.stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
return Result.success(responses);
try {
// 从上下文中获取当前用户ID
String userId = CurrentUserUtil.requireCurrentUserId();
List<Message> messages = messageService.searchByUserIdAndKeyword(userId, keyword, limit);
List<MessageResponse> responses = messages.stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
return Result.success(responses);
} catch (IllegalStateException e) {
return Result.error(e.getMessage());
}
}
/**
* 获取用户最近的聊天记录
*/
@GetMapping("/user/recent")
public Result<List<MessageResponse>> getRecentMessages(
@RequestParam(defaultValue = "10") Integer limit) {
try {
// 从上下文中获取当前用户ID
String userId = CurrentUserUtil.requireCurrentUserId();
List<Message> messages = messageService.getRecentByUserId(userId, limit);
List<MessageResponse> responses = messages.stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
return Result.success(responses);
} catch (IllegalStateException e) {
return Result.error(e.getMessage());
}
}
/**
@@ -145,5 +145,28 @@ public class Message extends BaseEntity {
@TableField("metadata")
private String metadata;
/**
* 用户ID (注册用户或访客用户)
*/
@TableField("user_id")
private String userId;
/**
* 用户类型 (registered/guest)
*/
@TableField("user_type")
private String userType;
/**
* Coze消息角色 (user/assistant/system)
*/
@TableField("coze_role")
private String cozeRole;
/**
* Coze消息内容类型 (text/image/file等)
*/
@TableField("coze_content_type")
private String cozeContentType;
}
@@ -1,6 +1,7 @@
package com.emotion.interceptor;
import com.emotion.util.JwtUtil;
import com.emotion.util.UserContextHolder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -64,19 +65,32 @@ public class JwtAuthInterceptor implements HandlerInterceptor {
return false;
}
// 从token中获取用户信息并设置到请求属性中
// 从token中获取用户信息并设置到请求属性和上下文
String userId = jwtUtil.getUserIdFromToken(token);
String username = jwtUtil.getUsernameFromToken(token);
// 设置到请求属性中(兼容现有代码)
request.setAttribute("userId", userId);
request.setAttribute("username", username);
request.setAttribute("token", token);
// 设置到线程本地上下文中(推荐使用)
UserContextHolder.setCurrentUserId(userId);
UserContextHolder.setCurrentUsername(username);
UserContextHolder.setCurrentToken(token);
log.debug("Token验证成功,用户: {} ({})", username, userId);
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// 请求完成后清除用户上下文,防止内存泄漏
UserContextHolder.clear();
log.debug("请求完成,已清除用户上下文");
}
/**
* 判断是否为公开接口(不需要认证)
*/
@@ -88,11 +102,8 @@ public class JwtAuthInterceptor implements HandlerInterceptor {
"/api/auth/captcha",
"/api/auth/refresh-token",
"/api/health",
"/api/ws/chat",
"/api/emotion-records", // 情绪记录接口
"/api/emotion-summary", // 情绪总结接口
"/message", // 消息接口
"/swagger-ui",
"/api/ws/chat",
"/swagger-ui",
"/v3/api-docs",
"/actuator"
};
@@ -68,4 +68,16 @@ public interface MessageMapper extends BaseMapper<Message> {
List<Message> searchByUserIdAndKeyword(@Param("userId") String userId,
@Param("keyword") String keyword,
@Param("limit") Integer limit);
/**
* 根据用户ID获取最近的消息
*/
@Select("SELECT m.* FROM message m " +
"INNER JOIN conversation c ON m.conversation_id = c.id " +
"WHERE c.user_id = #{userId} " +
"AND m.is_deleted = 0 " +
"ORDER BY m.create_time DESC " +
"LIMIT #{limit}")
List<Message> getRecentByUserId(@Param("userId") String userId,
@Param("limit") Integer limit);
}
@@ -55,7 +55,12 @@ public interface MessageService extends IService<Message> {
* 根据用户ID和关键词搜索消息
*/
List<Message> searchByUserIdAndKeyword(String userId, String keyword, Integer limit);
/**
* 根据用户ID获取最近的消息
*/
List<Message> getRecentByUserId(String userId, Integer limit);
/**
* 查询会话的最后一条消息
*/
@@ -4,6 +4,7 @@ import com.emotion.dto.websocket.ChatRequest;
import com.emotion.dto.websocket.ConnectRequest;
import com.emotion.dto.websocket.WebSocketMessage;
import com.emotion.entity.Message;
import com.emotion.entity.Conversation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.simp.SimpMessagingTemplate;
@@ -219,50 +220,84 @@ public class WebSocketService {
// 使用线程池异步处理AI响应
new Thread(() -> {
try {
String userId = request.getSenderId();
String conversationId = request.getConversationId();
// 如果没有会话ID,创建新会话
if (conversationId == null || conversationId.trim().isEmpty()) {
conversationId = createNewConversation(userId, request);
request.setConversationId(conversationId);
}
// 确保会话存在并更新活跃时间
ensureConversationExists(conversationId, userId, request);
// 保存用户消息到数据库
Message userMessage = new Message();
userMessage.setConversationId(request.getConversationId());
userMessage.setCreateBy(request.getSenderId());
userMessage.setConversationId(conversationId);
userMessage.setUserId(userId);
userMessage
.setUserType(request.getSenderType() == ChatRequest.SenderType.USER ? "registered" : "guest");
userMessage.setContent(request.getContent());
userMessage.setType(request.getMessageType().name());
userMessage.setSender(request.getSenderType().name());
userMessage.setType("text");
userMessage.setSender("user");
userMessage.setCozeRole("user");
userMessage.setCozeContentType("text");
messageService.createMessage(userMessage);
// 调用AI服务
String aiReply = aiChatService.sendChatMessage(
request.getConversationId(),
request.getContent(),
request.getSenderId()
conversationId,
request.getContent(),
userId
);
// 构建AI回复消息
WebSocketMessage aiMessage = WebSocketMessage.builder()
.messageId(UUID.randomUUID().toString())
.conversationId(request.getConversationId())
.type(WebSocketMessage.MessageType.TEXT)
.content(aiReply)
.senderId("ai")
.senderType(WebSocketMessage.SenderType.AI)
.status(WebSocketMessage.MessageStatus.SENT)
.createTime(LocalDateTime.now())
.build();
// 保存AI回复到数据库
Message aiDbMessage = new Message();
aiDbMessage.setConversationId(request.getConversationId());
aiDbMessage.setCreateBy("ai");
aiDbMessage.setContent(aiReply);
aiDbMessage.setType("text");
aiDbMessage.setSender("ai");
messageService.createMessage(aiDbMessage);
// 发送AI回复
messagingTemplate.convertAndSendToUser(request.getSenderId(), "/queue/messages", aiMessage);
// 如果AI回复包含换行符,分割成多条消息
String[] replyParts = aiReply.split("\\n\\n|\\n");
if (request.getConversationId() != null) {
messagingTemplate.convertAndSend("/topic/conversation/" + request.getConversationId(), aiMessage);
for (String part : replyParts) {
if (part.trim().isEmpty())
continue;
// 构建AI回复消息
WebSocketMessage aiMessage = WebSocketMessage.builder()
.messageId(UUID.randomUUID().toString())
.conversationId(conversationId)
.type(WebSocketMessage.MessageType.TEXT)
.content(part.trim())
.senderId("ai")
.senderType(WebSocketMessage.SenderType.AI)
.status(WebSocketMessage.MessageStatus.SENT)
.createTime(LocalDateTime.now())
.build();
// 保存AI回复到数据库
Message aiDbMessage = new Message();
aiDbMessage.setConversationId(conversationId);
aiDbMessage.setUserId(userId);
aiDbMessage.setUserType(
request.getSenderType() == ChatRequest.SenderType.USER ? "registered" : "guest");
aiDbMessage.setContent(part.trim());
aiDbMessage.setType("text");
aiDbMessage.setSender("ai");
aiDbMessage.setCozeRole("assistant");
aiDbMessage.setCozeContentType("text");
messageService.createMessage(aiDbMessage);
// 发送AI回复
messagingTemplate.convertAndSendToUser(userId, "/queue/messages", aiMessage);
if (conversationId != null) {
messagingTemplate.convertAndSend("/topic/conversation/" + conversationId, aiMessage);
}
// 添加短暂延迟,模拟自然对话
Thread.sleep(500);
}
// 更新会话的最后活跃时间和消息数量
updateConversationActivity(conversationId);
} catch (Exception e) {
log.error("AI响应处理失败", e);
sendErrorMessage(request.getSenderId(), "AI服务暂时不可用,请稍后重试");
@@ -293,4 +328,85 @@ public class WebSocketService {
public int getOnlineUserCount() {
return onlineUsers.size();
}
/**
* 创建新会话
*/
private String createNewConversation(String userId, ChatRequest request) {
try {
String conversationId = "conv_" + System.currentTimeMillis() + "_" + UUID.randomUUID().toString().substring(0, 8);
Conversation conversation = Conversation.builder()
.userId(userId)
.userType(request.getSenderType() == ChatRequest.SenderType.USER ? "registered" : "guest")
.title("新对话")
.type("chat")
.conversationStatus("active")
.startTime(LocalDateTime.now())
.lastActiveTime(LocalDateTime.now())
.messageCount(0)
.build();
// 设置ID
conversation.setId(conversationId);
conversationService.save(conversation);
log.info("创建新会话: conversationId={}, userId={}", conversationId, userId);
return conversationId;
} catch (Exception e) {
log.error("创建新会话失败: userId={}", userId, e);
throw new RuntimeException("创建会话失败", e);
}
}
/**
* 确保会话存在并更新活跃时间
*/
private void ensureConversationExists(String conversationId, String userId, ChatRequest request) {
try {
Conversation conversation = conversationService.getById(conversationId);
if (conversation == null) {
// 如果会话不存在,创建一个
conversation = Conversation.builder()
.userId(userId)
.userType(request.getSenderType() == ChatRequest.SenderType.USER ? "registered" : "guest")
.title("对话")
.type("chat")
.conversationStatus("active")
.startTime(LocalDateTime.now())
.lastActiveTime(LocalDateTime.now())
.messageCount(0)
.build();
// 设置ID
conversation.setId(conversationId);
conversationService.save(conversation);
log.info("创建会话: conversationId={}, userId={}", conversationId, userId);
} else {
// 更新最后活跃时间
conversation.setLastActiveTime(LocalDateTime.now());
conversationService.updateById(conversation);
}
} catch (Exception e) {
log.error("确保会话存在失败: conversationId={}, userId={}", conversationId, userId, e);
}
}
/**
* 更新会话活跃状态
*/
private void updateConversationActivity(String conversationId) {
try {
Conversation conversation = conversationService.getById(conversationId);
if (conversation != null) {
conversation.setLastActiveTime(LocalDateTime.now());
conversation.setMessageCount((conversation.getMessageCount() != null ? conversation.getMessageCount() : 0) + 1);
conversationService.updateById(conversation);
}
} catch (Exception e) {
log.error("更新会话活跃状态失败: conversationId={}", conversationId, e);
}
}
}
@@ -6,11 +6,13 @@ import com.emotion.entity.Message;
import com.emotion.entity.Conversation;
import com.emotion.entity.CozeApiCall;
import com.emotion.entity.EmotionRecord;
import com.emotion.entity.EmotionAnalysis;
import com.emotion.service.AIChatService;
import com.emotion.service.MessageService;
import com.emotion.service.ConversationService;
import com.emotion.service.CozeApiCallService;
import com.emotion.service.EmotionRecordService;
import com.emotion.service.EmotionAnalysisService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
@@ -59,6 +61,9 @@ public class AiChatServiceImpl implements AIChatService {
@Autowired
private EmotionRecordService emotionRecordService;
@Autowired
private EmotionAnalysisService emotionAnalysisService;
@Value("${emotion.coze.api.token:}")
private String cozeApiToken;
@@ -941,7 +946,7 @@ public class AiChatServiceImpl implements AIChatService {
log.info("情绪分析总结生成完成: {}", emotionSummary);
// 解析AI返回的情绪分析结果
EmotionAnalysisResult analysisResult = parseEmotionSummary(emotionSummary);
EmotionAnalysis analysisResult = parseEmotionSummary(emotionSummary);
// 创建情绪记录
EmotionRecord emotionRecord = createEmotionRecord(userId, analysisResult, chatHistory);
@@ -1011,37 +1016,39 @@ public class AiChatServiceImpl implements AIChatService {
/**
* 解析情绪分析总结结果
*/
private EmotionAnalysisResult parseEmotionSummary(String summary) {
private EmotionAnalysis parseEmotionSummary(String summary) {
try {
// 尝试从AI回复中提取JSON
String jsonStr = extractJsonFromSummary(summary);
if (jsonStr != null) {
JSONObject json = JSON.parseObject(jsonStr);
EmotionAnalysisResult result = new EmotionAnalysisResult();
result.setPrimaryEmotion(json.getString("primaryEmotion"));
result.setIntensity(json.getDoubleValue("intensity"));
result.setTriggers(json.getString("triggers"));
result.setEmotionTrend(json.getString("emotionTrend"));
result.setSuggestions(json.getString("suggestions"));
result.setSummary(json.getString("summary"));
return result;
return EmotionAnalysis.builder()
.primaryEmotion(json.getString("primaryEmotion"))
.intensity(BigDecimal.valueOf(json.getDoubleValue("intensity")))
.keywords(json.getString("triggers"))
.suggestion(json.getString("suggestions"))
.text(summary)
.polarity(determinePolarity(json.getString("primaryEmotion")))
.confidence(BigDecimal.valueOf(0.85))
.analysisTime(LocalDateTime.now())
.build();
}
} catch (Exception e) {
log.warn("解析情绪分析结果失败,使用默认值: {}", e.getMessage());
}
// 如果解析失败,返回默认结果
EmotionAnalysisResult defaultResult = new EmotionAnalysisResult();
defaultResult.setPrimaryEmotion("平静");
defaultResult.setIntensity(0.5);
defaultResult.setTriggers("日常对话");
defaultResult.setEmotionTrend("相对稳定");
defaultResult.setSuggestions("保持当前的积极状态");
defaultResult.setSummary(summary);
return defaultResult;
return EmotionAnalysis.builder()
.primaryEmotion("平静")
.intensity(BigDecimal.valueOf(0.5))
.keywords("日常对话")
.suggestion("保持当前的积极状态")
.text(summary)
.polarity("neutral")
.confidence(BigDecimal.valueOf(0.5))
.analysisTime(LocalDateTime.now())
.build();
}
/**
@@ -1063,14 +1070,14 @@ public class AiChatServiceImpl implements AIChatService {
/**
* 创建情绪记录
*/
private EmotionRecord createEmotionRecord(String userId, EmotionAnalysisResult analysisResult, String chatHistory) {
private EmotionRecord createEmotionRecord(String userId, EmotionAnalysis analysisResult, String chatHistory) {
EmotionRecord record = EmotionRecord.builder()
.userId(userId)
.recordDate(LocalDate.now())
.emotionType(analysisResult.getPrimaryEmotion())
.intensity(BigDecimal.valueOf(analysisResult.getIntensity()))
.triggers(analysisResult.getTriggers())
.description(analysisResult.getSummary())
.intensity(analysisResult.getIntensity())
.triggers(analysisResult.getKeywords())
.description(analysisResult.getText())
.notes("基于当天聊天记录自动生成的情绪分析")
.tags("AI分析,聊天记录,情绪总结")
.build();
@@ -1078,37 +1085,14 @@ public class AiChatServiceImpl implements AIChatService {
emotionRecordService.save(record);
log.info("情绪记录创建成功: recordId={}", record.getId());
// 设置情绪分析记录的关联ID并保存
analysisResult.setUserId(userId);
analysisResult.setMessageId(record.getId()); // 关联到情绪记录ID
emotionAnalysisService.save(analysisResult);
log.info("情绪分析记录创建成功: analysisId={}", analysisResult.getId());
return record;
}
/**
* 情绪分析结果内部类
*/
public static class EmotionAnalysisResult {
private String primaryEmotion;
private Double intensity;
private String triggers;
private String emotionTrend;
private String suggestions;
private String summary;
// Getters and Setters
public String getPrimaryEmotion() { return primaryEmotion; }
public void setPrimaryEmotion(String primaryEmotion) { this.primaryEmotion = primaryEmotion; }
public Double getIntensity() { return intensity; }
public void setIntensity(Double intensity) { this.intensity = intensity; }
public String getTriggers() { return triggers; }
public void setTriggers(String triggers) { this.triggers = triggers; }
public String getEmotionTrend() { return emotionTrend; }
public void setEmotionTrend(String emotionTrend) { this.emotionTrend = emotionTrend; }
public String getSuggestions() { return suggestions; }
public void setSuggestions(String suggestions) { this.suggestions = suggestions; }
public String getSummary() { return summary; }
public void setSummary(String summary) { this.summary = summary; }
}
}
@@ -195,4 +195,10 @@ public class MessageServiceImpl extends ServiceImpl<MessageMapper, Message> impl
// 通过conversation表关联查询用户的消息,根据关键词搜索
return this.baseMapper.searchByUserIdAndKeyword(userId, keyword, limit);
}
@Override
public List<Message> getRecentByUserId(String userId, Integer limit) {
// 获取用户最近的消息,按时间倒序
return this.baseMapper.getRecentByUserId(userId, limit);
}
}
@@ -0,0 +1,87 @@
package com.emotion.util;
import lombok.extern.slf4j.Slf4j;
/**
* 当前用户工具类
* 提供便捷的方法获取当前登录用户信息
*
* @author emotion-museum
* @date 2025-07-25
*/
@Slf4j
public class CurrentUserUtil {
/**
* 获取当前用户ID
*
* @return 当前用户ID,如果未登录则返回null
*/
public static String getCurrentUserId() {
return UserContextHolder.getCurrentUserId();
}
/**
* 获取当前用户名
*
* @return 当前用户名,如果未登录则返回null
*/
public static String getCurrentUsername() {
return UserContextHolder.getCurrentUsername();
}
/**
* 获取当前用户Token
*
* @return 当前用户Token,如果未登录则返回null
*/
public static String getCurrentToken() {
return UserContextHolder.getCurrentToken();
}
/**
* 检查当前是否有用户登录
*
* @return 是否有用户登录
*/
public static boolean isUserLoggedIn() {
return UserContextHolder.hasUserContext();
}
/**
* 获取当前用户ID,如果未登录则抛出异常
*
* @return 当前用户ID
* @throws IllegalStateException 如果用户未登录
*/
public static String requireCurrentUserId() {
String userId = getCurrentUserId();
if (userId == null || userId.trim().isEmpty()) {
throw new IllegalStateException("用户未登录或认证失败");
}
return userId;
}
/**
* 获取当前用户名,如果未登录则抛出异常
*
* @return 当前用户名
* @throws IllegalStateException 如果用户未登录
*/
public static String requireCurrentUsername() {
String username = getCurrentUsername();
if (username == null || username.trim().isEmpty()) {
throw new IllegalStateException("用户未登录或认证失败");
}
return username;
}
/**
* 获取当前用户上下文摘要信息
*
* @return 用户上下文摘要
*/
public static String getContextSummary() {
return UserContextHolder.getContextSummary();
}
}
@@ -37,6 +37,11 @@ public class UserContextHolder {
*/
private static final ThreadLocal<String> REQUEST_ID_HOLDER = new ThreadLocal<>();
/**
* Token线程本地变量
*/
private static final ThreadLocal<String> TOKEN_HOLDER = new ThreadLocal<>();
/**
* 设置当前用户ID
*
@@ -125,12 +130,31 @@ public class UserContextHolder {
/**
* 获取请求ID
*
*
* @return 请求ID
*/
public static String getRequestId() {
return REQUEST_ID_HOLDER.get();
}
/**
* 设置当前Token
*
* @param token Token
*/
public static void setCurrentToken(String token) {
TOKEN_HOLDER.set(token);
log.debug("设置当前Token: {}", token != null ? "***" : null);
}
/**
* 获取当前Token
*
* @return Token
*/
public static String getCurrentToken() {
return TOKEN_HOLDER.get();
}
/**
* 设置用户上下文信息
@@ -188,6 +212,13 @@ public class UserContextHolder {
REQUEST_ID_HOLDER.remove();
}
/**
* 清除当前Token
*/
public static void clearToken() {
TOKEN_HOLDER.remove();
}
/**
* 清除所有用户上下文信息
*/
@@ -197,6 +228,7 @@ public class UserContextHolder {
clearUserType();
clearClientIp();
clearRequestId();
clearToken();
log.debug("清除所有用户上下文信息");
}