接口优化

This commit is contained in:
2025-09-08 17:54:12 +08:00
parent e20030f10d
commit d42d689bd7
84 changed files with 6403 additions and 4310 deletions
@@ -52,4 +52,12 @@ public class PageResult<T> {
public static <T> PageResult<T> of(IPage<T> page) {
return new PageResult<>(page);
}
}
/**
* 设置数据列表
*/
public PageResult<T> setRecords(List<T> records) {
this.records = records;
return this;
}
}
@@ -1,99 +1,88 @@
package com.emotion.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.emotion.common.PageResult;
import com.emotion.common.Result;
import com.emotion.dto.request.PageRequest;
import com.emotion.dto.response.BaseResponse;
import com.emotion.entity.Achievement;
import com.emotion.dto.request.achievement.*;
import com.emotion.dto.response.achievement.AchievementResponse;
import com.emotion.service.AchievementService;
import org.springframework.beans.BeanUtils;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.time.format.DateTimeFormatter;
import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;
/**
* 成就控制器
*
* @author emotion-museum
* @date 2025-07-23
* @date 2025-09-08
*/
@RestController
@RequestMapping("/achievement")
@Tag(name = "成就管理", description = "成就的增删改查功能")
public class AchievementController {
@Autowired
private AchievementService achievementService;
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
/**
* 分页查询成就
*/
@Operation(summary = "分页查询成就", description = "分页查询成就列表")
@GetMapping("/page")
public Result<PageResult<AchievementResponse>> getPage(@Validated PageRequest request) {
IPage<Achievement> page = achievementService.getPage(request);
List<AchievementResponse> responses = page.getRecords().stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
PageResult<AchievementResponse> pageResult = new PageResult<>();
pageResult.setCurrent(page.getCurrent());
pageResult.setSize(page.getSize());
pageResult.setTotal(page.getTotal());
pageResult.setPages(page.getPages());
pageResult.setRecords(responses);
public Result<PageResult<AchievementResponse>> getPage(@Validated AchievementPageRequest request) {
PageResult<AchievementResponse> pageResult = achievementService.getPageWithResponse(request);
return Result.success(pageResult);
}
/**
* 根据ID获取成就
*/
@GetMapping("/{id}")
public Result<AchievementResponse> getById(@PathVariable String id) {
Achievement achievement = achievementService.getById(id);
if (achievement == null) {
@Operation(summary = "根据ID获取成就", description = "根据ID获取成就详情")
@GetMapping("/detail")
public Result<AchievementResponse> getById(@RequestParam String id) {
AchievementResponse response = achievementService.getAchievementResponseById(id);
if (response == null) {
return Result.notFound("成就不存在");
}
return Result.success(convertToResponse(achievement));
return Result.success(response);
}
/**
* 创建成就
*/
@PostMapping
public Result<AchievementResponse> create(@RequestBody Achievement achievement) {
boolean saved = achievementService.save(achievement);
if (!saved) {
@Operation(summary = "创建成就", description = "创建新的成就")
@PostMapping("/create")
public Result<AchievementResponse> create(@RequestBody @Validated AchievementCreateRequest request) {
AchievementResponse response = achievementService.createAchievementWithResponse(request);
if (response == null) {
return Result.error("创建失败");
}
return Result.success(convertToResponse(achievement));
return Result.success("创建成功", response);
}
/**
* 更新成就
*/
@PutMapping("/{id}")
public Result<AchievementResponse> update(@PathVariable String id, @RequestBody Achievement achievement) {
achievement.setId(id);
boolean updated = achievementService.updateById(achievement);
if (!updated) {
@Operation(summary = "更新成就", description = "更新指定成就")
@PutMapping("/update")
public Result<AchievementResponse> update(@RequestBody @Validated AchievementUpdateRequest request) {
AchievementResponse response = achievementService.updateAchievementWithResponse(request);
if (response == null) {
return Result.error("更新失败");
}
Achievement updatedAchievement = achievementService.getById(id);
return Result.success(convertToResponse(updatedAchievement));
return Result.success("更新成功", response);
}
/**
* 删除成就
*/
@DeleteMapping("/{id}")
public Result<Void> delete(@PathVariable String id) {
@Operation(summary = "删除成就", description = "删除指定成就")
@DeleteMapping("/delete")
public Result<Void> delete(@RequestParam String id) {
boolean deleted = achievementService.removeById(id);
if (!deleted) {
return Result.error("删除失败");
@@ -104,55 +93,48 @@ public class AchievementController {
/**
* 根据分类查询成就
*/
@GetMapping("/category/{category}")
public Result<List<AchievementResponse>> getByCategory(@PathVariable String category) {
List<Achievement> achievements = achievementService.getByCategory(category);
List<AchievementResponse> responses = achievements.stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
@Operation(summary = "根据分类查询成就", description = "根据分类查询成就列表")
@GetMapping("/byCategory")
public Result<List<AchievementResponse>> getByCategory(@RequestParam String category) {
List<AchievementResponse> responses = achievementService.getByCategoryWithResponse(category);
return Result.success(responses);
}
/**
* 根据稀有度查询成就
*/
@GetMapping("/rarity/{rarity}")
public Result<List<AchievementResponse>> getByRarity(@PathVariable String rarity) {
List<Achievement> achievements = achievementService.getByRarity(rarity);
List<AchievementResponse> responses = achievements.stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
@Operation(summary = "根据稀有度查询成就", description = "根据稀有度查询成就列表")
@GetMapping("/byRarity")
public Result<List<AchievementResponse>> getByRarity(@RequestParam String rarity) {
List<AchievementResponse> responses = achievementService.getByRarityWithResponse(rarity);
return Result.success(responses);
}
/**
* 查询已解锁的成就
*/
@Operation(summary = "查询已解锁的成就", description = "查询已解锁的成就列表")
@GetMapping("/unlocked")
public Result<List<AchievementResponse>> getUnlockedAchievements() {
List<Achievement> achievements = achievementService.getUnlockedAchievements();
List<AchievementResponse> responses = achievements.stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
List<AchievementResponse> responses = achievementService.getUnlockedAchievementsWithResponse();
return Result.success(responses);
}
/**
* 查询未解锁的成就
*/
@Operation(summary = "查询未解锁的成就", description = "查询未解锁的成就列表")
@GetMapping("/locked")
public Result<List<AchievementResponse>> getLockedAchievements() {
List<Achievement> achievements = achievementService.getLockedAchievements();
List<AchievementResponse> responses = achievements.stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
List<AchievementResponse> responses = achievementService.getLockedAchievementsWithResponse();
return Result.success(responses);
}
/**
* 统计已解锁成就数量
*/
@GetMapping("/count/unlocked")
@Operation(summary = "统计已解锁成就数量", description = "统计已解锁成就数量")
@GetMapping("/unlockedCount")
public Result<Long> countUnlockedAchievements() {
Long count = achievementService.countUnlockedAchievements();
return Result.success(count);
@@ -161,7 +143,8 @@ public class AchievementController {
/**
* 统计未解锁成就数量
*/
@GetMapping("/count/locked")
@Operation(summary = "统计未解锁成就数量", description = "统计未解锁成就数量")
@GetMapping("/lockedCount")
public Result<Long> countLockedAchievements() {
Long count = achievementService.countLockedAchievements();
return Result.success(count);
@@ -170,9 +153,10 @@ public class AchievementController {
/**
* 解锁成就
*/
@PutMapping("/{id}/unlock")
public Result<Void> unlockAchievement(@PathVariable String id) {
boolean unlocked = achievementService.unlockAchievement(id, java.time.LocalDateTime.now());
@Operation(summary = "解锁成就", description = "解锁指定成就")
@PutMapping("/unlock")
public Result<Void> unlockAchievement(@RequestBody @Validated AchievementUnlockRequest request) {
boolean unlocked = achievementService.unlockAchievement(request.getId(), LocalDateTime.now());
if (!unlocked) {
return Result.error("解锁失败");
}
@@ -182,9 +166,10 @@ public class AchievementController {
/**
* 更新成就进度
*/
@PutMapping("/{id}/progress")
public Result<Void> updateProgress(@PathVariable String id, @RequestParam Double progress) {
boolean updated = achievementService.updateProgress(id, progress);
@Operation(summary = "更新成就进度", description = "更新指定成就的进度")
@PutMapping("/updateProgress")
public Result<Void> updateProgress(@RequestBody @Validated AchievementProgressUpdateRequest request) {
boolean updated = achievementService.updateProgress(request.getId(), request.getProgress());
if (!updated) {
return Result.error("更新进度失败");
}
@@ -192,36 +177,12 @@ public class AchievementController {
}
/**
* 转换为响应对象
* 查询最近解锁的成就
*/
private AchievementResponse convertToResponse(Achievement achievement) {
AchievementResponse response = new AchievementResponse();
BeanUtils.copyProperties(achievement, response);
response.setId(achievement.getId());
if (achievement.getCreateTime() != null) {
response.setCreateTime(achievement.getCreateTime().format(DATE_TIME_FORMATTER));
}
if (achievement.getUpdateTime() != null) {
response.setUpdateTime(achievement.getUpdateTime().format(DATE_TIME_FORMATTER));
}
return response;
@Operation(summary = "查询最近解锁的成就", description = "查询最近解锁的成就列表")
@GetMapping("/recent")
public Result<List<AchievementResponse>> getRecentlyUnlocked(@RequestParam(defaultValue = "10") Integer limit) {
List<AchievementResponse> responses = achievementService.getRecentlyUnlockedWithResponse(limit);
return Result.success(responses);
}
/**
* 成就响应类
*/
@lombok.Data
@lombok.EqualsAndHashCode(callSuper = true)
public static class AchievementResponse extends BaseResponse {
private String title;
private String description;
private String category;
private String rarity;
private String conditionType;
private String conditionValue;
private Double progress;
private String unlockedTime;
private Integer isHidden;
private String iconUrl;
}
}
}
@@ -5,6 +5,8 @@ import com.emotion.dto.request.AiChatRequest;
import com.emotion.dto.request.AiSummaryRequest;
import com.emotion.dto.request.GuestChatRequest;
import com.emotion.dto.request.ConversationCreateRequest;
import com.emotion.dto.request.ChatStatsRequest;
import com.emotion.dto.request.GuestUserInfoRequest;
import com.emotion.dto.response.AiChatResponse;
import com.emotion.dto.response.AiSummaryResponse;
import com.emotion.dto.response.AiStatusResponse;
@@ -12,19 +14,14 @@ import com.emotion.dto.response.ChatStatsResponse;
import com.emotion.dto.response.GuestChatResponse;
import com.emotion.dto.response.GuestUserInfoResponse;
import com.emotion.dto.response.ConversationResponse;
import com.emotion.entity.Conversation;
import com.emotion.service.AiChatService;
import com.emotion.service.MessageService;
import com.emotion.service.ConversationService;
import com.emotion.util.UserContextUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import java.time.format.DateTimeFormatter;
import java.util.Map;
/**
* AI聊天控制器
@@ -40,34 +37,16 @@ public class AiChatController {
@Autowired
private AiChatService aiChatService;
@Autowired
private MessageService messageService;
@Autowired
private ConversationService conversationService;
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
/**
* 发送聊天消息
*/
@PostMapping("/chat")
public Result<AiChatResponse> sendChatMessage(@Valid @RequestBody AiChatRequest request) {
log.info("收到AI聊天请求: conversationId={}, userId={}, message={}",
request.getConversationId(), request.getUserId(), request.getMessage());
// 调用AI服务
String aiReply = aiChatService.sendChatMessage(request.getConversationId(), request.getMessage(),
request.getUserId());
// 构建响应
AiChatResponse response = new AiChatResponse();
response.setConversationId(request.getConversationId());
response.setUserMessage(request.getMessage());
response.setAiReply(aiReply);
response.setUserId(request.getUserId());
response.setTimestamp(System.currentTimeMillis());
log.info("收到AI聊天请求: conversationId={}, userId={}, message={}",
request.getConversationId(), request.getUserId(), request.getMessage());
// 调用AI服务
AiChatResponse response = aiChatService.sendChatMessage(request);
return Result.success(response);
}
@@ -77,18 +56,9 @@ public class AiChatController {
@PostMapping("/summary")
public Result<AiSummaryResponse> generateSummary(@Valid @RequestBody AiSummaryRequest request) {
log.info("收到对话总结请求: conversationId={}, userId={}", request.getConversationId(), request.getUserId());
// 调用AI总结服务
String summary = aiChatService.generateConversationSummary(request.getConversationId(),
request.getUserId());
// 构建响应
AiSummaryResponse response = new AiSummaryResponse();
response.setConversationId(request.getConversationId());
response.setSummary(summary);
response.setUserId(request.getUserId());
response.setTimestamp(System.currentTimeMillis());
// 调用AI总结服务
AiSummaryResponse response = aiChatService.generateConversationSummary(request);
return Result.success(response);
}
@@ -99,15 +69,7 @@ public class AiChatController {
public Result<AiStatusResponse> getServiceStatus() {
log.info("获取AI服务状态");
boolean available = aiChatService.isServiceAvailable();
String status = aiChatService.getServiceStatus();
// 构建响应
AiStatusResponse response = new AiStatusResponse();
response.setAvailable(available);
response.setStatus(status);
response.setTimestamp(System.currentTimeMillis());
AiStatusResponse response = aiChatService.getServiceStatus();
return Result.success(response);
}
@@ -115,124 +77,48 @@ public class AiChatController {
* 获取聊天记录统计
*/
@GetMapping("/stats")
public Result<ChatStatsResponse> getChatStats(@RequestParam(required = false) String userId,
@RequestParam(required = false) String conversationId) {
log.info("获取聊天统计: userId={}, conversationId={}", userId, conversationId);
public Result<ChatStatsResponse> getChatStats(@Valid ChatStatsRequest request) {
log.info("获取聊天统计: userId={}, conversationId={}", request.getUserId(), request.getConversationId());
// 构建响应
ChatStatsResponse response = new ChatStatsResponse();
if (userId != null && !userId.trim().isEmpty()) {
Long userConversationCount = conversationService.countByUserId(userId);
Long activeConversationCount = conversationService.countActiveByUserId(userId);
response.setUserConversationCount(userConversationCount);
response.setActiveConversationCount(activeConversationCount);
}
if (conversationId != null && !conversationId.trim().isEmpty()) {
Long conversationMessageCount = messageService.countByConversationId(conversationId);
response.setConversationMessageCount(conversationMessageCount);
}
response.setTimestamp(System.currentTimeMillis());
return Result.success(response);
}
/**
* 访客聊天(不需要登录)
*/
@PostMapping("/guest/chat")
public Result<GuestChatResponse> guestChat(@Valid @RequestBody GuestChatRequest request,
HttpServletRequest httpRequest) {
String clientIp = getClientIp(httpRequest);
log.info("访客聊天请求: {}, IP: {}", request.getMessage(), clientIp);
// 调用AI服务
Map<String, Object> aiResponse = aiChatService.guestChat(request.getMessage(), clientIp);
// 构建响应
GuestChatResponse response = new GuestChatResponse();
response.setMessage((String) aiResponse.get("message"));
response.setMessageId((String) aiResponse.get("messageId"));
response.setTimestamp((Long) aiResponse.get("timestamp"));
response.setError((Boolean) aiResponse.get("error"));
return Result.success("发送成功", response);
}
/**
* 获取访客用户信息
*/
@GetMapping("/guest/user/info")
public Result<GuestUserInfoResponse> getGuestUserInfo(HttpServletRequest request) {
String clientIp = getClientIp(request);
log.info("获取访客用户信息: IP={}", clientIp);
// 调用AI服务
Map<String, Object> userInfo = aiChatService.getGuestUserInfo(clientIp);
// 构建响应
GuestUserInfoResponse response = new GuestUserInfoResponse();
response.setId((String) userInfo.get("id"));
response.setUsername((String) userInfo.get("username"));
response.setNickname((String) userInfo.get("nickname"));
response.setType((String) userInfo.get("type"));
response.setClientIp((String) userInfo.get("clientIp"));
response.setUserCreateTime((Long) userInfo.get("createTime"));
return Result.success(response);
}
/**
* 创建对话
*/
@PostMapping("/conversation/create")
public Result<ConversationResponse> createConversation(@Valid @RequestBody ConversationCreateRequest request,
HttpServletRequest httpRequest) {
log.info("创建对话请求: userId={}, title={}", request.getUserId(), request.getTitle());
String clientIp = getClientIp(httpRequest);
// 调用AI服务创建对话
Map<String, Object> aiConversation = aiChatService.createConversation(request.getUserId(),
request.getTitle());
// 创建数据库对话记录
Conversation conversation = conversationService.createConversation(
request.getUserId(),
request.getTitle(),
request.getType() != null ? request.getType() : "user");
// 构建响应
ConversationResponse response = new ConversationResponse();
BeanUtils.copyProperties(conversation, response);
response.setId(conversation.getId());
if (conversation.getCreateTime() != null) {
response.setCreateTime(conversation.getCreateTime().format(DATE_TIME_FORMATTER));
}
if (conversation.getUpdateTime() != null) {
response.setUpdateTime(conversation.getUpdateTime().format(DATE_TIME_FORMATTER));
}
return Result.success("创建成功", response);
ChatStatsResponse response = aiChatService.getChatStats(request);
return Result.success(response);
}
/**
* 获取客户端IP
* 访客聊天(不需要登录)
*/
private String getClientIp(HttpServletRequest request) {
String xForwardedFor = request.getHeader("X-Forwarded-For");
if (xForwardedFor != null && !xForwardedFor.isEmpty() && !"unknown".equalsIgnoreCase(xForwardedFor)) {
return xForwardedFor.split(",")[0].trim();
}
@PostMapping("/guestChat")
public Result<GuestChatResponse> guestChat(@Valid @RequestBody GuestChatRequest request,
HttpServletRequest httpRequest) {
String clientIp = UserContextUtils.getClientIpAddress(httpRequest);
log.info("访客聊天请求: {}, IP: {}", request.getMessage(), clientIp);
String xRealIp = request.getHeader("X-Real-IP");
if (xRealIp != null && !xRealIp.isEmpty() && !"unknown".equalsIgnoreCase(xRealIp)) {
return xRealIp;
}
return request.getRemoteAddr();
GuestChatResponse response = aiChatService.guestChat(request, clientIp);
return Result.success("发送成功", response);
}
}
/**
* 获取访客用户信息
*/
@GetMapping("/guestUserInfo")
public Result<GuestUserInfoResponse> getGuestUserInfo(HttpServletRequest request) {
String clientIp = UserContextUtils.getClientIpAddress(request);
log.info("获取访客用户信息: IP={}", clientIp);
GuestUserInfoResponse response = aiChatService.getGuestUserInfo(clientIp);
return Result.success(response);
}
/**
* 创建对话
*/
@PostMapping("/createConversation")
public Result<ConversationResponse> createConversation(@Valid @RequestBody ConversationCreateRequest request,
HttpServletRequest httpRequest) {
log.info("创建对话请求: userId={}, title={}", request.getUserId(), request.getTitle());
String clientIp = UserContextUtils.getClientIpAddress(httpRequest);
ConversationResponse response = aiChatService.createConversation(request, clientIp);
return Result.success("创建成功", response);
}
}
@@ -9,6 +9,7 @@ import com.emotion.dto.response.CaptchaResponse;
import com.emotion.dto.response.UserInfoResponse;
import com.emotion.service.AuthService;
import com.emotion.service.TokenService;
import com.emotion.util.UserContextUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@@ -52,10 +53,9 @@ public class AuthController {
/**
* 获取当前用户信息
*/
@GetMapping("/user/info")
@GetMapping("/userInfo")
public Result<UserInfoResponse> getCurrentUserInfo(HttpServletRequest request) {
String token = extractToken(request);
UserInfoResponse userInfo = tokenService.getUserInfoByToken(token);
UserInfoResponse userInfo = tokenService.getUserInfoByToken(request);
return Result.success(userInfo);
}
@@ -73,15 +73,14 @@ public class AuthController {
*/
@PostMapping("/logout")
public Result<Void> logout(HttpServletRequest request) {
String token = extractToken(request);
authService.logoutByToken(token);
authService.logoutByToken(request);
return Result.success();
}
/**
* 刷新访问令牌
*/
@PostMapping("/refresh")
@PostMapping("/refreshToken")
public Result<AuthResponse> refreshToken(@Valid @RequestBody RefreshTokenRequest request) {
AuthResponse response = authService.refreshToken(request.getRefreshToken());
return Result.success("令牌刷新成功", response);
@@ -90,14 +89,9 @@ public class AuthController {
/**
* 验证访问令牌
*/
@GetMapping("/validate")
@GetMapping("/validateToken")
public Result<Boolean> validateToken(HttpServletRequest request) {
String token = extractToken(request);
if (token == null) {
return Result.success(false);
}
boolean isValid = authService.validateToken(token);
boolean isValid = authService.validateToken(request);
return Result.success(isValid);
}
@@ -106,15 +100,14 @@ public class AuthController {
*/
@GetMapping("/username")
public Result<String> getUsernameFromToken(HttpServletRequest request) {
String token = extractToken(request);
String username = tokenService.getUsernameByToken(token);
String username = tokenService.getUsernameByToken(request);
return Result.success(username);
}
/**
* 检查账号是否存在
*/
@GetMapping("/check-account")
@GetMapping("/checkAccount")
public Result<Boolean> checkAccount(@RequestParam String account) {
boolean exists = authService.existsByAccount(account);
return Result.success(exists);
@@ -123,7 +116,7 @@ public class AuthController {
/**
* 检查邮箱是否存在
*/
@GetMapping("/check-email")
@GetMapping("/checkEmail")
public Result<Boolean> checkEmail(@RequestParam String email) {
boolean exists = authService.existsByEmail(email);
return Result.success(exists);
@@ -132,27 +125,9 @@ public class AuthController {
/**
* 检查手机号是否存在
*/
@GetMapping("/check-phone")
@GetMapping("/checkPhone")
public Result<Boolean> checkPhone(@RequestParam String phone) {
boolean exists = authService.existsByPhone(phone);
return Result.success(exists);
}
/**
* 从请求中提取访问令牌
*/
private String extractToken(HttpServletRequest request) {
String authHeader = request.getHeader("Authorization");
if (authHeader != null && authHeader.startsWith("Bearer ")) {
return authHeader.substring(7);
}
// 也可以从请求参数中获取
String tokenParam = request.getParameter("token");
if (tokenParam != null && !tokenParam.trim().isEmpty()) {
return tokenParam.trim();
}
return null;
}
}
@@ -1,6 +1,6 @@
package com.emotion.controller;
import com.emotion.dto.websocket.ChatRequest;
import com.emotion.dto.request.WebSocketRequest;
import com.emotion.dto.websocket.ConnectRequest;
import com.emotion.service.WebSocketService;
import lombok.extern.slf4j.Slf4j;
@@ -14,14 +14,15 @@ import javax.validation.Valid;
import java.security.Principal;
/**
* 优化的WebSocket聊天控制器
* 使用规范的请求对象封装参数
* WebSocket聊天控制器
* 使用规范的请求对象封装参数,符合项目开发规则
*
* @author emotion-museum
* @date 2025-07-23
* @date 2025-09-08
*/
@Slf4j
@Controller
@MessageMapping("/chat")
public class ChatWebSocketController {
@Autowired
@@ -29,111 +30,55 @@ public class ChatWebSocketController {
/**
* 处理聊天消息
* @param chatRequest 聊天请求对象
* @param headerAccessor 消息头访问器
* @param principal 用户主体
*
* @param webSocketRequest WebSocket请求对象
* @param headerAccessor 消息头访问器
* @param principal 用户主体
*/
@MessageMapping("/chat.send")
public void handleChatMessage(@Valid @Payload ChatRequest chatRequest,
SimpMessageHeaderAccessor headerAccessor,
Principal principal) {
try {
log.info("收到WebSocket聊天消息: {}", chatRequest);
// 获取会话ID
String sessionId = headerAccessor.getSessionId();
// 如果请求中没有发送者ID,尝试从Principal获取
if (chatRequest.getSenderId() == null && principal != null) {
chatRequest.setSenderId(principal.getName());
}
// 如果还是没有发送者ID,使用会话ID作为访客ID
if (chatRequest.getSenderId() == null) {
chatRequest.setSenderId("guest_" + sessionId);
chatRequest.setSenderType(ChatRequest.SenderType.GUEST);
}
// 设置时间戳
if (chatRequest.getTimestamp() == null) {
chatRequest.setTimestamp(System.currentTimeMillis());
}
// 处理聊天消息
webSocketService.handleChatMessage(chatRequest, sessionId, principal);
} catch (Exception e) {
log.error("处理WebSocket聊天消息失败", e);
}
@MessageMapping("/send")
public void handleChatMessage(@Valid @Payload WebSocketRequest webSocketRequest,
SimpMessageHeaderAccessor headerAccessor,
Principal principal) {
String sessionId = headerAccessor.getSessionId();
webSocketService.handleChatMessage(webSocketRequest, sessionId, principal);
}
/**
* 处理用户连接
*
* @param connectRequest 连接请求对象
* @param headerAccessor 消息头访问器
* @param principal 用户主体
* @param principal 用户主体
*/
@MessageMapping("/chat.connect")
@MessageMapping("/connect")
public void connectUser(@Payload ConnectRequest connectRequest,
SimpMessageHeaderAccessor headerAccessor,
Principal principal) {
try {
String sessionId = headerAccessor.getSessionId();
log.info("用户连接WebSocket: connectRequest={}, sessionId={}, principal={}",
connectRequest, sessionId, principal);
// 如果请求中没有用户ID,尝试从Principal获取
if (connectRequest.getUserId() == null && principal != null) {
connectRequest.setUserId(principal.getName());
}
// 设置连接时间戳
if (connectRequest.getTimestamp() == null) {
connectRequest.setTimestamp(System.currentTimeMillis());
}
// 处理用户连接
webSocketService.handleUserConnect(connectRequest, sessionId, principal);
} catch (Exception e) {
log.error("处理用户WebSocket连接失败", e);
}
SimpMessageHeaderAccessor headerAccessor,
Principal principal) {
String sessionId = headerAccessor.getSessionId();
webSocketService.handleUserConnect(connectRequest, sessionId, principal);
}
/**
* 处理用户断开连接
*
* @param headerAccessor 消息头访问器
* @param principal 用户主体
* @param principal 用户主体
*/
@MessageMapping("/chat.disconnect")
@MessageMapping("/disconnect")
public void disconnectUser(SimpMessageHeaderAccessor headerAccessor, Principal principal) {
try {
String sessionId = headerAccessor.getSessionId();
log.info("用户断开WebSocket连接: sessionId={}, principal={}", sessionId, principal);
// 处理用户断开连接
webSocketService.handleUserDisconnect(sessionId, principal);
} catch (Exception e) {
log.error("处理用户WebSocket断开连接失败", e);
}
String sessionId = headerAccessor.getSessionId();
webSocketService.handleUserDisconnect(sessionId, principal);
}
/**
* 处理心跳消息
*
* @param headerAccessor 消息头访问器
* @param principal 用户主体
* @param principal 用户主体
*/
@MessageMapping("/chat.heartbeat")
@MessageMapping("/heartbeat")
public void heartbeat(SimpMessageHeaderAccessor headerAccessor, Principal principal) {
try {
String sessionId = headerAccessor.getSessionId();
// 处理心跳消息
webSocketService.handleHeartbeat(sessionId, principal);
} catch (Exception e) {
log.error("处理WebSocket心跳失败", e);
}
String sessionId = headerAccessor.getSessionId();
webSocketService.handleHeartbeat(sessionId, principal);
}
}
}
@@ -1,22 +1,17 @@
package com.emotion.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.emotion.common.PageResult;
import com.emotion.common.Result;
import com.emotion.dto.request.PageRequest;
import com.emotion.dto.response.BaseResponse;
import com.emotion.entity.Comment;
import com.emotion.dto.request.comment.CommentCreateRequest;
import com.emotion.dto.request.comment.CommentUpdateRequest;
import com.emotion.dto.request.comment.CommentPageRequest;
import com.emotion.dto.response.comment.CommentResponse;
import com.emotion.service.CommentService;
import org.springframework.beans.BeanUtils;
import com.emotion.util.UserContextUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.constraints.NotBlank;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.stream.Collectors;
/**
* 评论控制器
*
@@ -30,114 +25,44 @@ public class CommentController {
@Autowired
private CommentService commentService;
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
/**
* 分页查询评论
*/
@GetMapping("/page")
public Result<PageResult<CommentResponse>> getPage(@Validated PageRequest request) {
IPage<Comment> page = commentService.getPage(request);
List<CommentResponse> responses = page.getRecords().stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
PageResult<CommentResponse> pageResult = new PageResult<>();
pageResult.setCurrent(page.getCurrent());
pageResult.setSize(page.getSize());
pageResult.setTotal(page.getTotal());
pageResult.setPages(page.getPages());
pageResult.setRecords(responses);
@GetMapping(value = "/page")
public Result<PageResult<CommentResponse>> getCommentPage(@Validated CommentPageRequest request) {
PageResult<CommentResponse> pageResult = commentService.getPage(request);
return Result.success(pageResult);
}
/**
* 根据帖子ID分页查询评论
*/
@GetMapping("/post/{postId}/page")
public Result<PageResult<CommentResponse>> getPageByPostId(@PathVariable String postId, @Validated PageRequest request) {
IPage<Comment> page = commentService.getPageByPostId(request, postId);
List<CommentResponse> responses = page.getRecords().stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
PageResult<CommentResponse> 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);
}
/**
* 根据用户ID分页查询评论
*/
@GetMapping("/user/{userId}/page")
public Result<PageResult<CommentResponse>> getPageByUserId(@PathVariable String userId, @Validated PageRequest request) {
IPage<Comment> page = commentService.getPageByUserId(request, userId);
List<CommentResponse> responses = page.getRecords().stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
PageResult<CommentResponse> 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);
}
/**
* 根据ID获取评论
*/
@GetMapping("/{id}")
public Result<CommentResponse> getById(@PathVariable String id) {
Comment comment = commentService.getById(id);
if (comment == null) {
return Result.notFound("评论不存在");
}
return Result.success(convertToResponse(comment));
}
/**
* 创建评论
*/
@PostMapping
public Result<CommentResponse> create(@RequestBody @Validated CommentCreateRequest request) {
Comment comment = commentService.createComment(
request.getPostId(),
request.getUserId(),
request.getContent(),
request.getReplyToId()
);
return Result.success(convertToResponse(comment));
@PostMapping(value = "/create")
public Result<CommentResponse> createComment(@RequestBody @Validated CommentCreateRequest request) {
// 从上下文中获取当前用户ID,而不是直接使用请求中的用户ID
String currentUserId = UserContextUtils.getCurrentUserId();
if (currentUserId != null) {
request.setUserId(currentUserId);
}
CommentResponse response = commentService.create(request);
return Result.success(response);
}
/**
* 更新评论
*/
@PutMapping("/{id}")
public Result<CommentResponse> update(@PathVariable String id, @RequestBody Comment comment) {
comment.setId(id);
boolean updated = commentService.updateById(comment);
if (!updated) {
return Result.error("更新失败");
}
Comment updatedComment = commentService.getById(id);
return Result.success(convertToResponse(updatedComment));
@PutMapping(value = "/update")
public Result<CommentResponse> updateComment(@RequestBody @Validated CommentUpdateRequest request) {
CommentResponse response = commentService.update(request);
return Result.success(response);
}
/**
* 删除评论
*/
@DeleteMapping("/{id}")
public Result<Void> delete(@PathVariable String id) {
boolean deleted = commentService.removeById(id);
@DeleteMapping(value = "/delete")
public Result<Void> deleteComment(@RequestParam String id) {
boolean deleted = commentService.delete(id);
if (!deleted) {
return Result.error("删除失败");
}
@@ -145,105 +70,14 @@ public class CommentController {
}
/**
* 根据帖子ID查询顶级评论
* 根据ID获取评论
*/
@GetMapping("/post/{postId}/top-level")
public Result<List<CommentResponse>> getTopLevelCommentsByPostId(@PathVariable String postId) {
List<Comment> comments = commentService.getTopLevelCommentsByPostId(postId);
List<CommentResponse> responses = comments.stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
return Result.success(responses);
}
/**
* 根据评论ID查询回复
*/
@GetMapping("/{commentId}/replies")
public Result<List<CommentResponse>> getRepliesByCommentId(@PathVariable String commentId) {
List<Comment> replies = commentService.getRepliesByCommentId(commentId);
List<CommentResponse> responses = replies.stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
return Result.success(responses);
}
/**
* 统计帖子的评论数量
*/
@GetMapping("/post/{postId}/count")
public Result<Long> countByPostId(@PathVariable String postId) {
Long count = commentService.countByPostId(postId);
return Result.success(count);
}
/**
* 点赞评论
*/
@PutMapping("/{id}/like")
public Result<Void> likeComment(@PathVariable String id) {
boolean updated = commentService.updateLikes(id, 1);
if (!updated) {
return Result.error("点赞失败");
@GetMapping(value = "/detail")
public Result<CommentResponse> getCommentById(@RequestParam String id) {
CommentResponse response = commentService.getById(id);
if (response == null) {
return Result.notFound("评论不存在");
}
return Result.success();
return Result.success(response);
}
/**
* 取消点赞评论
*/
@PutMapping("/{id}/unlike")
public Result<Void> unlikeComment(@PathVariable String id) {
boolean updated = commentService.updateLikes(id, -1);
if (!updated) {
return Result.error("取消点赞失败");
}
return Result.success();
}
/**
* 转换为响应对象
*/
private CommentResponse convertToResponse(Comment comment) {
CommentResponse response = new CommentResponse();
BeanUtils.copyProperties(comment, response);
response.setId(comment.getId());
if (comment.getCreateTime() != null) {
response.setCreateTime(comment.getCreateTime().format(DATE_TIME_FORMATTER));
}
if (comment.getUpdateTime() != null) {
response.setUpdateTime(comment.getUpdateTime().format(DATE_TIME_FORMATTER));
}
return response;
}
/**
* 评论创建请求
*/
@lombok.Data
public static class CommentCreateRequest {
@NotBlank(message = "帖子ID不能为空")
private String postId;
@NotBlank(message = "用户ID不能为空")
private String userId;
@NotBlank(message = "评论内容不能为空")
private String content;
private String replyToId;
}
/**
* 评论响应类
*/
@lombok.Data
@lombok.EqualsAndHashCode(callSuper = true)
public static class CommentResponse extends BaseResponse {
private String postId;
private String userId;
private String content;
private String replyToId;
private Integer likes;
}
}
}
@@ -1,22 +1,16 @@
package com.emotion.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.emotion.common.PageResult;
import com.emotion.common.Result;
import com.emotion.dto.request.PageRequest;
import com.emotion.dto.response.BaseResponse;
import com.emotion.entity.CommunityPost;
import com.emotion.dto.request.community.CommunityPostCreateRequest;
import com.emotion.dto.request.community.CommunityPostUpdateRequest;
import com.emotion.dto.request.community.CommunityPostPageRequest;
import com.emotion.dto.response.community.CommunityPostResponse;
import com.emotion.service.CommunityPostService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.constraints.NotBlank;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.stream.Collectors;
/**
* 社区帖子控制器
*
@@ -24,266 +18,60 @@ import java.util.stream.Collectors;
* @date 2025-07-23
*/
@RestController
@RequestMapping("/community-post")
@RequestMapping("/communityPost")
public class CommunityPostController {
@Autowired
private CommunityPostService communityPostService;
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
/**
* 分页查询帖子
*/
@GetMapping("/page")
public Result<PageResult<CommunityPostResponse>> getPage(@Validated PageRequest request) {
IPage<CommunityPost> page = communityPostService.getPage(request);
List<CommunityPostResponse> responses = page.getRecords().stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
PageResult<CommunityPostResponse> 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);
}
/**
* 分页查询公开帖子
*/
@GetMapping("/public/page")
public Result<PageResult<CommunityPostResponse>> getPublicPostsPage(@Validated PageRequest request) {
IPage<CommunityPost> page = communityPostService.getPublicPostsPage(request);
List<CommunityPostResponse> responses = page.getRecords().stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
PageResult<CommunityPostResponse> 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);
}
/**
* 根据用户ID分页查询帖子
*/
@GetMapping("/user/{userId}/page")
public Result<PageResult<CommunityPostResponse>> getPageByUserId(@PathVariable String userId, @Validated PageRequest request) {
IPage<CommunityPost> page = communityPostService.getPageByUserId(request, userId);
List<CommunityPostResponse> responses = page.getRecords().stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
PageResult<CommunityPostResponse> pageResult = new PageResult<>();
pageResult.setCurrent(page.getCurrent());
pageResult.setSize(page.getSize());
pageResult.setTotal(page.getTotal());
pageResult.setPages(page.getPages());
pageResult.setRecords(responses);
@GetMapping(value = "/page")
public Result<PageResult<CommunityPostResponse>> getPage(@Validated CommunityPostPageRequest request) {
PageResult<CommunityPostResponse> pageResult = communityPostService.getPage(request);
return Result.success(pageResult);
}
/**
* 根据ID获取帖子
*/
@GetMapping("/{id}")
public Result<CommunityPostResponse> getById(@PathVariable String id) {
CommunityPost post = communityPostService.getById(id);
if (post == null) {
@GetMapping(value = "/detail")
public Result<CommunityPostResponse> getById(@RequestParam String id) {
CommunityPostResponse response = communityPostService.getById(id);
if (response == null) {
return Result.notFound("帖子不存在");
}
// 增加浏览数
communityPostService.incrementViewCount(id);
return Result.success(convertToResponse(post));
return Result.success(response);
}
/**
* 创建帖子
*/
@PostMapping
@PostMapping(value = "/create")
public Result<CommunityPostResponse> create(@RequestBody @Validated CommunityPostCreateRequest request) {
CommunityPost post = communityPostService.createPost(
request.getUserId(),
request.getTitle(),
request.getContent(),
request.getType(),
request.getLocationId(),
request.getTags(),
request.getIsPrivate()
);
return Result.success(convertToResponse(post));
CommunityPostResponse response = communityPostService.create(request);
return Result.success(response);
}
/**
* 更新帖子
*/
@PutMapping("/{id}")
public Result<CommunityPostResponse> update(@PathVariable String id, @RequestBody CommunityPost post) {
post.setId(id);
boolean updated = communityPostService.updateById(post);
if (!updated) {
return Result.error("更新失败");
}
CommunityPost updatedPost = communityPostService.getById(id);
return Result.success(convertToResponse(updatedPost));
@PutMapping(value = "/update")
public Result<CommunityPostResponse> update(@RequestBody @Validated CommunityPostUpdateRequest request) {
CommunityPostResponse response = communityPostService.update(request);
return Result.success(response);
}
/**
* 删除帖子
*/
@DeleteMapping("/{id}")
public Result<Void> delete(@PathVariable String id) {
boolean deleted = communityPostService.removeById(id);
@DeleteMapping(value = "/delete")
public Result<Void> delete(@RequestParam String id) {
boolean deleted = communityPostService.delete(id);
if (!deleted) {
return Result.error("删除失败");
}
return Result.success();
}
/**
* 根据类型查询帖子
*/
@GetMapping("/type/{type}")
public Result<List<CommunityPostResponse>> getByType(@PathVariable String type) {
List<CommunityPost> posts = communityPostService.getByType(type);
List<CommunityPostResponse> responses = posts.stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
return Result.success(responses);
}
/**
* 根据地点ID查询帖子
*/
@GetMapping("/location/{locationId}")
public Result<List<CommunityPostResponse>> getByLocationId(@PathVariable String locationId) {
List<CommunityPost> posts = communityPostService.getByLocationId(locationId);
List<CommunityPostResponse> responses = posts.stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
return Result.success(responses);
}
/**
* 查询最受欢迎的帖子
*/
@GetMapping("/popular")
public Result<List<CommunityPostResponse>> getMostLikedPosts(@RequestParam(defaultValue = "10") Integer limit) {
List<CommunityPost> posts = communityPostService.getMostLikedPosts(limit);
List<CommunityPostResponse> responses = posts.stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
return Result.success(responses);
}
/**
* 查询最新的帖子
*/
@GetMapping("/latest")
public Result<List<CommunityPostResponse>> getLatestPosts(@RequestParam(defaultValue = "10") Integer limit) {
List<CommunityPost> posts = communityPostService.getLatestPosts(limit);
List<CommunityPostResponse> responses = posts.stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
return Result.success(responses);
}
/**
* 点赞帖子
*/
@PutMapping("/{id}/like")
public Result<Void> likePost(@PathVariable String id) {
boolean updated = communityPostService.updateLikes(id, 1);
if (!updated) {
return Result.error("点赞失败");
}
return Result.success();
}
/**
* 取消点赞帖子
*/
@PutMapping("/{id}/unlike")
public Result<Void> unlikePost(@PathVariable String id) {
boolean updated = communityPostService.updateLikes(id, -1);
if (!updated) {
return Result.error("取消点赞失败");
}
return Result.success();
}
/**
* 根据标签搜索帖子
*/
@GetMapping("/search/tag")
public Result<List<CommunityPostResponse>> getByTag(@RequestParam String tag) {
List<CommunityPost> posts = communityPostService.getByTag(tag);
List<CommunityPostResponse> responses = posts.stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
return Result.success(responses);
}
/**
* 转换为响应对象
*/
private CommunityPostResponse convertToResponse(CommunityPost post) {
CommunityPostResponse response = new CommunityPostResponse();
BeanUtils.copyProperties(post, response);
response.setId(post.getId());
if (post.getCreateTime() != null) {
response.setCreateTime(post.getCreateTime().format(DATE_TIME_FORMATTER));
}
if (post.getUpdateTime() != null) {
response.setUpdateTime(post.getUpdateTime().format(DATE_TIME_FORMATTER));
}
return response;
}
/**
* 帖子创建请求
*/
@lombok.Data
public static class CommunityPostCreateRequest {
@NotBlank(message = "用户ID不能为空")
private String userId;
@NotBlank(message = "标题不能为空")
private String title;
@NotBlank(message = "内容不能为空")
private String content;
private String type;
private String locationId;
private String tags;
private Integer isPrivate;
}
/**
* 帖子响应类
*/
@lombok.Data
@lombok.EqualsAndHashCode(callSuper = true)
public static class CommunityPostResponse extends BaseResponse {
private String userId;
private String title;
private String content;
private String type;
private String locationId;
private String tags;
private Integer likes;
private Integer viewCount;
private Integer commentCount;
private Integer isPrivate;
}
}
}
@@ -1,22 +1,17 @@
package com.emotion.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.emotion.common.PageResult;
import com.emotion.common.Result;
import com.emotion.dto.request.PageRequest;
import com.emotion.dto.request.ConversationCreateRequest;
import com.emotion.dto.request.ConversationPageRequest;
import com.emotion.dto.response.ConversationResponse;
import com.emotion.entity.Conversation;
import com.emotion.service.ConversationService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.stream.Collectors;
/**
* 对话控制器
@@ -31,95 +26,51 @@ public class ConversationController {
@Autowired
private ConversationService conversationService;
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
/**
* 分页查询对话
*/
@GetMapping("/page")
public Result<PageResult<ConversationResponse>> getPage(@Valid PageRequest request) {
IPage<Conversation> page = conversationService.getPage(request);
List<ConversationResponse> responses = page.getRecords().stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
PageResult<ConversationResponse> 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);
}
/**
* 根据用户ID分页查询对话
*/
@GetMapping("/user/{userId}/page")
public Result<PageResult<ConversationResponse>> getPageByUserId(@PathVariable String userId,
@Valid PageRequest request) {
IPage<Conversation> page = conversationService.getPageByUserId(request, userId);
List<ConversationResponse> responses = page.getRecords().stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
PageResult<ConversationResponse> pageResult = new PageResult<>();
pageResult.setCurrent(page.getCurrent());
pageResult.setSize(page.getSize());
pageResult.setTotal(page.getTotal());
pageResult.setPages(page.getPages());
pageResult.setRecords(responses);
@GetMapping(value = "/page")
public Result<PageResult<ConversationResponse>> getPage(@Valid ConversationPageRequest request) {
PageResult<ConversationResponse> pageResult = conversationService.getPageWithResponse(request);
return Result.success(pageResult);
}
/**
* 根据ID获取对话
*/
@GetMapping("/{id}")
public Result<ConversationResponse> getById(@PathVariable String id) {
Conversation conversation = conversationService.getById(id);
if (conversation == null) {
@GetMapping(value = "/detail")
public Result<ConversationResponse> getById(@RequestParam String id) {
ConversationResponse response = conversationService.getConversationResponseById(id);
if (response == null) {
return Result.notFound("对话不存在");
}
return Result.success(convertToResponse(conversation));
return Result.success(response);
}
/**
* 创建对话
*/
@PostMapping
@PostMapping(value = "/create")
public Result<ConversationResponse> create(@Valid @RequestBody ConversationCreateRequest request,
HttpServletRequest httpRequest) {
String clientIp = getClientIp(httpRequest);
Conversation conversation = conversationService.createConversation(
request.getUserId(),
request.getTitle(),
null // cozeConversationId
);
return Result.success(convertToResponse(conversation));
ConversationResponse response = conversationService.createConversationWithResponse(request, httpRequest);
return Result.success(response);
}
/**
* 更新对话
*/
@PutMapping("/{id}")
public Result<ConversationResponse> update(@PathVariable String id, @RequestBody Conversation conversation) {
conversation.setId(id);
boolean updated = conversationService.updateById(conversation);
if (!updated) {
return Result.error("更新失败");
}
Conversation updatedConversation = conversationService.getById(id);
return Result.success(convertToResponse(updatedConversation));
@PutMapping(value = "/update")
public Result<ConversationResponse> update(@RequestBody ConversationCreateRequest request) {
ConversationResponse response = conversationService.updateConversationWithResponse(request);
return Result.success(response);
}
/**
* 删除对话
*/
@DeleteMapping("/{id}")
public Result<Void> delete(@PathVariable String id) {
@DeleteMapping(value = "/delete")
public Result<Void> delete(@RequestParam String id) {
boolean deleted = conversationService.removeById(id);
if (!deleted) {
return Result.error("删除失败");
@@ -127,93 +78,35 @@ public class ConversationController {
return Result.success();
}
/**
* 根据用户ID查询对话
*/
@GetMapping("/user/{userId}")
public Result<List<ConversationResponse>> getByUserId(@PathVariable String userId) {
List<Conversation> conversations = conversationService.getByUserId(userId);
List<ConversationResponse> responses = conversations.stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
return Result.success(responses);
}
/**
* 获取活跃对话
*/
@GetMapping("/active")
@GetMapping(value = "/active")
public Result<List<ConversationResponse>> getActiveConversations() {
// 暂时返回空列表,因为接口中没有这个方法
return Result.success();
List<ConversationResponse> responses = conversationService.getActiveConversationsWithResponse();
return Result.success(responses);
}
/**
* 获取归档对话
*/
@GetMapping("/archived")
@GetMapping(value = "/archived")
public Result<List<ConversationResponse>> getArchivedConversations() {
// 暂时返回空列表,因为接口中没有这个方法
List<ConversationResponse> responses = conversationService.getArchivedConversationsWithResponse();
return Result.success(responses);
}
/**
* 更新对话状态
*/
@PutMapping(value = "/status")
public Result<Void> updateConversationStatus(
@RequestParam String id,
@RequestParam String status) {
boolean updated = conversationService.updateConversationStatus(id, status);
if (!updated) {
return Result.error("更新状态失败");
}
return Result.success();
}
/**
* 归档对话
*/
@PutMapping("/{id}/archive")
public Result<Void> archiveConversation(@PathVariable String id) {
// 暂时返回成功,因为接口中没有这个方法
return Result.success();
}
/**
* 激活对话
*/
@PutMapping("/{id}/activate")
public Result<Void> activateConversation(@PathVariable String id) {
// 暂时返回成功,因为接口中没有这个方法
return Result.success();
}
/**
* 统计用户对话数量
*/
@GetMapping("/user/{userId}/count")
public Result<Long> countByUserId(@PathVariable String userId) {
Long count = conversationService.countByUserId(userId);
return Result.success(count);
}
/**
* 获取客户端IP
*/
private String getClientIp(HttpServletRequest request) {
String xForwardedFor = request.getHeader("X-Forwarded-For");
if (xForwardedFor != null && !xForwardedFor.isEmpty() && !"unknown".equalsIgnoreCase(xForwardedFor)) {
return xForwardedFor.split(",")[0];
}
String xRealIp = request.getHeader("X-Real-IP");
if (xRealIp != null && !xRealIp.isEmpty() && !"unknown".equalsIgnoreCase(xRealIp)) {
return xRealIp;
}
return request.getRemoteAddr();
}
/**
* 转换为响应对象
*/
private ConversationResponse convertToResponse(Conversation conversation) {
ConversationResponse response = new ConversationResponse();
BeanUtils.copyProperties(conversation, response);
response.setId(conversation.getId());
if (conversation.getCreateTime() != null) {
response.setCreateTime(conversation.getCreateTime().format(DATE_TIME_FORMATTER));
}
if (conversation.getUpdateTime() != null) {
response.setUpdateTime(conversation.getUpdateTime().format(DATE_TIME_FORMATTER));
}
return response;
}
}
}
@@ -1,20 +1,17 @@
package com.emotion.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.emotion.common.PageResult;
import com.emotion.common.Result;
import com.emotion.dto.request.PageRequest;
import com.emotion.dto.response.BaseResponse;
import com.emotion.entity.CozeApiCall;
import com.emotion.dto.request.coze.CozeApiCallPageRequest;
import com.emotion.dto.request.coze.CozeApiCallCreateRequest;
import com.emotion.dto.request.coze.CozeApiCallUpdateRequest;
import com.emotion.dto.response.coze.CozeApiCallResponse;
import com.emotion.service.CozeApiCallService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.stream.Collectors;
import java.math.BigDecimal;
/**
* Coze API调用记录控制器
@@ -29,229 +26,54 @@ public class CozeApiCallController {
@Autowired
private CozeApiCallService cozeApiCallService;
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
/**
* 分页查询API调用记录
*/
@GetMapping("/page")
public Result<PageResult<CozeApiCallResponse>> getPage(@Validated PageRequest request) {
IPage<CozeApiCall> page = cozeApiCallService.getPage(request);
List<CozeApiCallResponse> responses = page.getRecords().stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
PageResult<CozeApiCallResponse> 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);
}
/**
* 根据会话ID分页查询API调用记录
*/
@GetMapping("/conversation/{conversationId}/page")
public Result<PageResult<CozeApiCallResponse>> getPageByConversationId(@PathVariable String conversationId, @Validated PageRequest request) {
IPage<CozeApiCall> page = cozeApiCallService.getPageByConversationId(request, conversationId);
List<CozeApiCallResponse> responses = page.getRecords().stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
PageResult<CozeApiCallResponse> 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);
}
/**
* 根据用户ID分页查询API调用记录
*/
@GetMapping("/user/{userId}/page")
public Result<PageResult<CozeApiCallResponse>> getPageByUserId(@PathVariable String userId, @Validated PageRequest request) {
IPage<CozeApiCall> page = cozeApiCallService.getPageByUserId(request, userId);
List<CozeApiCallResponse> responses = page.getRecords().stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
PageResult<CozeApiCallResponse> pageResult = new PageResult<>();
pageResult.setCurrent(page.getCurrent());
pageResult.setSize(page.getSize());
pageResult.setTotal(page.getTotal());
pageResult.setPages(page.getPages());
pageResult.setRecords(responses);
@GetMapping(value = "/page")
public Result<PageResult<CozeApiCallResponse>> getCozeApiCallPage(@Validated CozeApiCallPageRequest request) {
PageResult<CozeApiCallResponse> pageResult = cozeApiCallService.getPage(request);
return Result.success(pageResult);
}
/**
* 根据ID获取API调用记录
*/
@GetMapping("/{id}")
public Result<CozeApiCallResponse> getById(@PathVariable String id) {
CozeApiCall apiCall = cozeApiCallService.getById(id);
if (apiCall == null) {
@GetMapping(value = "/detail")
public Result<CozeApiCallResponse> getCozeApiCallById(@RequestParam String id) {
CozeApiCallResponse response = cozeApiCallService.getById(id);
if (response == null) {
return Result.notFound("API调用记录不存在");
}
return Result.success(convertToResponse(apiCall));
}
/**
* 根据Bot ID查询API调用记录
*/
@GetMapping("/bot/{botId}")
public Result<List<CozeApiCallResponse>> getByBotId(@PathVariable String botId) {
List<CozeApiCall> apiCalls = cozeApiCallService.getByBotId(botId);
List<CozeApiCallResponse> responses = apiCalls.stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
return Result.success(responses);
}
/**
* 根据状态查询API调用记录
*/
@GetMapping("/status/{status}")
public Result<List<CozeApiCallResponse>> getByStatus(@PathVariable String status) {
List<CozeApiCall> apiCalls = cozeApiCallService.getByStatus(status);
List<CozeApiCallResponse> responses = apiCalls.stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
return Result.success(responses);
}
/**
* 根据请求类型查询API调用记录
*/
@GetMapping("/request-type/{requestType}")
public Result<List<CozeApiCallResponse>> getByRequestType(@PathVariable String requestType) {
List<CozeApiCall> apiCalls = cozeApiCallService.getByRequestType(requestType);
List<CozeApiCallResponse> responses = apiCalls.stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
return Result.success(responses);
}
/**
* 统计用户的API调用次数
*/
@GetMapping("/user/{userId}/count")
public Result<Long> countByUserId(@PathVariable String userId) {
Long count = cozeApiCallService.countByUserId(userId);
return Result.success(count);
}
/**
* 统计Bot的API调用次数
*/
@GetMapping("/bot/{botId}/count")
public Result<Long> countByBotId(@PathVariable String botId) {
Long count = cozeApiCallService.countByBotId(botId);
return Result.success(count);
}
/**
* 统计指定状态的API调用次数
*/
@GetMapping("/status/{status}/count")
public Result<Long> countByStatus(@PathVariable String status) {
Long count = cozeApiCallService.countByStatus(status);
return Result.success(count);
}
/**
* 统计用户的Token使用量
*/
@GetMapping("/user/{userId}/tokens")
public Result<Long> sumTokensByUserId(@PathVariable String userId) {
Long totalTokens = cozeApiCallService.sumTokensByUserId(userId);
return Result.success(totalTokens);
}
/**
* 统计用户的API调用费用
*/
@GetMapping("/user/{userId}/cost")
public Result<java.math.BigDecimal> sumCostByUserId(@PathVariable String userId) {
java.math.BigDecimal totalCost = cozeApiCallService.sumCostByUserId(userId);
return Result.success(totalCost);
}
/**
* 查询失败的API调用记录
*/
@GetMapping("/failed")
public Result<List<CozeApiCallResponse>> getFailedCalls() {
List<CozeApiCall> apiCalls = cozeApiCallService.getFailedCalls();
List<CozeApiCallResponse> responses = apiCalls.stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
return Result.success(responses);
}
/**
* 查询超时的API调用记录
*/
@GetMapping("/timeout")
public Result<List<CozeApiCallResponse>> getTimeoutCalls() {
List<CozeApiCall> apiCalls = cozeApiCallService.getTimeoutCalls();
List<CozeApiCallResponse> responses = apiCalls.stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
return Result.success(responses);
}
/**
* 根据追踪ID查询API调用记录
*/
@GetMapping("/trace/{traceId}")
public Result<CozeApiCallResponse> getByTraceId(@PathVariable String traceId) {
CozeApiCall apiCall = cozeApiCallService.getByTraceId(traceId);
if (apiCall == null) {
return Result.notFound("API调用记录不存在");
}
return Result.success(convertToResponse(apiCall));
return Result.success(response);
}
/**
* 创建API调用记录
*/
@PostMapping
public Result<CozeApiCallResponse> create(@RequestBody CozeApiCall apiCall) {
boolean saved = cozeApiCallService.save(apiCall);
if (!saved) {
return Result.error("创建失败");
}
return Result.success(convertToResponse(apiCall));
@PostMapping(value = "/create")
public Result<CozeApiCallResponse> createCozeApiCall(@RequestBody @Validated CozeApiCallCreateRequest request) {
CozeApiCallResponse response = cozeApiCallService.create(request);
return Result.success(response);
}
/**
* 更新API调用记录
*/
@PutMapping("/{id}")
public Result<CozeApiCallResponse> update(@PathVariable String id, @RequestBody CozeApiCall apiCall) {
apiCall.setId(id);
boolean updated = cozeApiCallService.updateById(apiCall);
if (!updated) {
return Result.error("更新失败");
@PutMapping(value = "/update")
public Result<CozeApiCallResponse> updateCozeApiCall(@RequestBody @Validated CozeApiCallUpdateRequest request) {
CozeApiCallResponse response = cozeApiCallService.update(request);
if (response == null) {
return Result.error("更新失败,记录不存在");
}
CozeApiCall updatedApiCall = cozeApiCallService.getById(id);
return Result.success(convertToResponse(updatedApiCall));
return Result.success(response);
}
/**
* 删除API调用记录
*/
@DeleteMapping("/{id}")
public Result<Void> delete(@PathVariable String id) {
boolean deleted = cozeApiCallService.removeById(id);
@DeleteMapping(value = "/delete")
public Result<Void> deleteCozeApiCall(@RequestParam String id) {
boolean deleted = cozeApiCallService.delete(id);
if (!deleted) {
return Result.error("删除失败");
}
@@ -259,49 +81,47 @@ public class CozeApiCallController {
}
/**
* 转换为响应对象
* 统计用户的API调用次数
*/
private CozeApiCallResponse convertToResponse(CozeApiCall apiCall) {
CozeApiCallResponse response = new CozeApiCallResponse();
BeanUtils.copyProperties(apiCall, response);
response.setId(apiCall.getId());
if (apiCall.getCreateTime() != null) {
response.setCreateTime(apiCall.getCreateTime().format(DATE_TIME_FORMATTER));
}
if (apiCall.getUpdateTime() != null) {
response.setUpdateTime(apiCall.getUpdateTime().format(DATE_TIME_FORMATTER));
}
if (apiCall.getStartTime() != null) {
response.setStartTime(apiCall.getStartTime().format(DATE_TIME_FORMATTER));
}
if (apiCall.getEndTime() != null) {
response.setEndTime(apiCall.getEndTime().format(DATE_TIME_FORMATTER));
}
return response;
@GetMapping(value = "/countByUser")
public Result<Long> countByUserId(@RequestParam String userId) {
Long count = cozeApiCallService.countByUserId(userId);
return Result.success(count);
}
/**
* API调用记录响应类
* 统计Bot的API调用次数
*/
@lombok.Data
@lombok.EqualsAndHashCode(callSuper = true)
public static class CozeApiCallResponse extends BaseResponse {
private String conversationId;
private String messageId;
private String userId;
private String botId;
private String requestType;
private String requestUrl;
private String requestBody;
private Integer responseStatus;
private String responseBody;
private String aiReply;
private Integer totalTokens;
private java.math.BigDecimal cost;
private String status;
private String finalStatus;
private String startTime;
private String endTime;
private String traceId;
@GetMapping(value = "/countByBot")
public Result<Long> countByBotId(@RequestParam String botId) {
Long count = cozeApiCallService.countByBotId(botId);
return Result.success(count);
}
}
/**
* 统计指定状态的API调用次数
*/
@GetMapping(value = "/countByStatus")
public Result<Long> countByStatus(@RequestParam String status) {
Long count = cozeApiCallService.countByStatus(status);
return Result.success(count);
}
/**
* 统计用户的Token使用量
*/
@GetMapping(value = "/tokensByUser")
public Result<Long> sumTokensByUserId(@RequestParam String userId) {
Long totalTokens = cozeApiCallService.sumTokensByUserId(userId);
return Result.success(totalTokens);
}
/**
* 统计用户的API调用费用
*/
@GetMapping(value = "/costByUser")
public Result<BigDecimal> sumCostByUserId(@RequestParam String userId) {
BigDecimal totalCost = cozeApiCallService.sumCostByUserId(userId);
return Result.success(totalCost);
}
}
@@ -1,27 +1,19 @@
package com.emotion.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.emotion.common.BasePageRequest;
import com.emotion.common.PageResult;
import com.emotion.common.Result;
import com.emotion.dto.request.DiaryCommentCreateRequest;
import com.emotion.dto.request.DiaryCommentPageRequest;
import com.emotion.dto.response.DiaryCommentResponse;
import com.emotion.entity.DiaryComment;
import com.emotion.service.DiaryCommentService;
import com.emotion.service.DiaryPostService;
import com.emotion.service.UserService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.BeanUtils;
import com.emotion.util.UserContextUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.stream.Collectors;
/**
* 日记评论控制器
@@ -30,7 +22,7 @@ import java.util.stream.Collectors;
* @date 2025-07-23
*/
@RestController
@RequestMapping("/diary-comment")
@RequestMapping("/diaryComment")
public class DiaryCommentController {
@Autowired
@@ -39,152 +31,79 @@ public class DiaryCommentController {
@Autowired
private DiaryPostService diaryPostService;
@Autowired
private UserService userService;
@Autowired
private ObjectMapper objectMapper;
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
/**
* 分页查询评论
*/
@GetMapping("/page")
public Result<PageResult<DiaryCommentResponse>> getPage(@Validated BasePageRequest request) {
IPage<DiaryComment> page = diaryCommentService.getPage(request);
List<DiaryCommentResponse> responses = page.getRecords().stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
PageResult<DiaryCommentResponse> 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);
}
/**
* 根据日记ID分页查询评论
*/
@GetMapping("/diary/{diaryId}/page")
public Result<PageResult<DiaryCommentResponse>> getPageByDiaryId(@PathVariable String diaryId,
@Validated BasePageRequest request) {
IPage<DiaryComment> page = diaryCommentService.getPageByDiaryId(diaryId, request);
List<DiaryCommentResponse> responses = page.getRecords().stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
PageResult<DiaryCommentResponse> 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);
}
/**
* 根据用户ID分页查询评论
*/
@GetMapping("/user/{userId}/page")
public Result<PageResult<DiaryCommentResponse>> getPageByUserId(@PathVariable String userId,
@Validated BasePageRequest request) {
IPage<DiaryComment> page = diaryCommentService.getPageByUserId(userId, request);
List<DiaryCommentResponse> responses = page.getRecords().stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
PageResult<DiaryCommentResponse> pageResult = new PageResult<>();
pageResult.setCurrent(page.getCurrent());
pageResult.setSize(page.getSize());
pageResult.setTotal(page.getTotal());
pageResult.setPages(page.getPages());
pageResult.setRecords(responses);
@GetMapping(value = "/page")
public Result<PageResult<DiaryCommentResponse>> getPage(@Validated DiaryCommentPageRequest request) {
PageResult<DiaryCommentResponse> pageResult = diaryCommentService.getPageWithResponse(request);
return Result.success(pageResult);
}
/**
* 获取评论树结构
*/
@GetMapping("/diary/{diaryId}/tree")
public Result<List<DiaryCommentResponse>> getCommentTree(@PathVariable String diaryId) {
List<DiaryComment> comments = diaryCommentService.getCommentTree(diaryId);
List<DiaryCommentResponse> responses = comments.stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
@GetMapping(value = "/commentTree")
public Result<List<DiaryCommentResponse>> getCommentTree(@RequestParam String diaryId) {
List<DiaryCommentResponse> responses = diaryCommentService.getCommentTreeWithResponse(diaryId);
return Result.success(responses);
}
/**
* 根据ID获取评论详情
*/
@GetMapping("/{id}")
public Result<DiaryCommentResponse> getById(@PathVariable String id) {
DiaryComment comment = diaryCommentService.getById(id);
if (comment == null) {
@GetMapping(value = "/detail")
public Result<DiaryCommentResponse> getById(@RequestParam String id) {
DiaryCommentResponse response = diaryCommentService.getCommentResponseById(id);
if (response == null) {
return Result.notFound("评论不存在");
}
return Result.success(convertToResponse(comment));
return Result.success(response);
}
/**
* 创建评论
*/
@PostMapping
@PostMapping(value = "/create")
public Result<DiaryCommentResponse> create(@Valid @RequestBody DiaryCommentCreateRequest request) {
DiaryComment comment = diaryCommentService.createComment(
request.getDiaryId(),
request.getUserId(),
request.getContent(),
request.getImages(),
request.getParentCommentId(),
request.getIsAnonymous()
);
// 从上下文中获取当前用户ID,而不是直接使用请求中的用户ID
String currentUserId = UserContextUtils.getCurrentUserId();
if (currentUserId != null) {
request.setUserId(currentUserId);
}
DiaryCommentResponse response = diaryCommentService.createCommentWithResponse(request);
// 更新日记的评论数
diaryPostService.incrementCommentCount(request.getDiaryId());
diaryPostService.updateLastCommentTime(request.getDiaryId());
return Result.success(convertToResponse(comment));
return Result.success(response);
}
/**
* 更新评论
*/
@PutMapping("/{id}")
public Result<DiaryCommentResponse> update(@PathVariable String id, @Valid @RequestBody DiaryCommentCreateRequest request) {
boolean updated = diaryCommentService.updateComment(id, request.getContent(), request.getImages());
if (!updated) {
return Result.error("更新失败");
}
DiaryComment updatedComment = diaryCommentService.getById(id);
return Result.success(convertToResponse(updatedComment));
@PutMapping(value = "/update")
public Result<DiaryCommentResponse> update(@Valid @RequestBody DiaryCommentCreateRequest request) {
DiaryCommentResponse response = diaryCommentService.updateCommentWithResponse(request);
return Result.success(response);
}
/**
* 删除评论
*/
@DeleteMapping("/{id}")
public Result<Void> delete(@PathVariable String id) {
DiaryComment comment = diaryCommentService.getById(id);
if (comment == null) {
return Result.error("评论不存在");
}
@DeleteMapping(value = "/delete")
public Result<Void> delete(@RequestParam String id) {
boolean deleted = diaryCommentService.deleteComment(id);
if (!deleted) {
return Result.error("删除失败");
}
// 更新日记的评论数
diaryPostService.decrementCommentCount(comment.getDiaryId());
DiaryCommentResponse response = diaryCommentService.getCommentResponseById(id);
if (response != null) {
diaryPostService.decrementCommentCount(response.getDiaryId());
}
return Result.success();
}
@@ -192,20 +111,18 @@ public class DiaryCommentController {
/**
* 软删除评论
*/
@DeleteMapping("/{id}/soft")
public Result<Void> softDelete(@PathVariable String id) {
DiaryComment comment = diaryCommentService.getById(id);
if (comment == null) {
return Result.error("评论不存在");
}
@DeleteMapping(value = "/softDelete")
public Result<Void> softDelete(@RequestParam String id) {
boolean deleted = diaryCommentService.softDeleteComment(id);
if (!deleted) {
return Result.error("删除失败");
}
// 更新日记的评论数
diaryPostService.decrementCommentCount(comment.getDiaryId());
DiaryCommentResponse response = diaryCommentService.getCommentResponseById(id);
if (response != null) {
diaryPostService.decrementCommentCount(response.getDiaryId());
}
return Result.success();
}
@@ -213,20 +130,18 @@ public class DiaryCommentController {
/**
* 恢复评论
*/
@PutMapping("/{id}/restore")
public Result<Void> restore(@PathVariable String id) {
DiaryComment comment = diaryCommentService.getById(id);
if (comment == null) {
return Result.error("评论不存在");
}
@PutMapping(value = "/restore")
public Result<Void> restore(@RequestParam String id) {
boolean restored = diaryCommentService.restoreComment(id);
if (!restored) {
return Result.error("恢复失败");
}
// 更新日记的评论数
diaryPostService.incrementCommentCount(comment.getDiaryId());
DiaryCommentResponse response = diaryCommentService.getCommentResponseById(id);
if (response != null) {
diaryPostService.incrementCommentCount(response.getDiaryId());
}
return Result.success();
}
@@ -234,8 +149,8 @@ public class DiaryCommentController {
/**
* 点赞评论
*/
@PostMapping("/{id}/like")
public Result<Void> like(@PathVariable String id) {
@PostMapping(value = "/like")
public Result<Void> like(@RequestParam String id) {
boolean liked = diaryCommentService.incrementLikeCount(id);
if (!liked) {
return Result.error("点赞失败");
@@ -246,8 +161,8 @@ public class DiaryCommentController {
/**
* 取消点赞评论
*/
@DeleteMapping("/{id}/like")
public Result<Void> unlike(@PathVariable String id) {
@DeleteMapping(value = "/unlike")
public Result<Void> unlike(@RequestParam String id) {
boolean unliked = diaryCommentService.decrementLikeCount(id);
if (!unliked) {
return Result.error("取消点赞失败");
@@ -258,75 +173,12 @@ public class DiaryCommentController {
/**
* 设置置顶状态
*/
@PutMapping("/{id}/top/{isTop}")
public Result<Void> setTop(@PathVariable String id, @PathVariable Integer isTop) {
@PutMapping(value = "/setTop")
public Result<Void> setTop(@RequestParam String id, @RequestParam Integer isTop) {
boolean set = diaryCommentService.setTop(id, isTop);
if (!set) {
return Result.error("设置置顶状态失败");
}
return Result.success();
}
/**
* 统计日记评论数量
*/
@GetMapping("/diary/{diaryId}/count")
public Result<Long> countByDiaryId(@PathVariable String diaryId) {
Long count = diaryCommentService.countByDiaryId(diaryId);
return Result.success(count);
}
/**
* 统计用户评论数量
*/
@GetMapping("/user/{userId}/count")
public Result<Long> countByUserId(@PathVariable String userId) {
Long count = diaryCommentService.countByUserId(userId);
return Result.success(count);
}
/**
* 统计回复数量
*/
@GetMapping("/parent/{parentCommentId}/replies/count")
public Result<Long> countReplies(@PathVariable String parentCommentId) {
Long count = diaryCommentService.countReplies(parentCommentId);
return Result.success(count);
}
/**
* 转换实体为响应DTO
*/
private DiaryCommentResponse convertToResponse(DiaryComment comment) {
DiaryCommentResponse response = new DiaryCommentResponse();
BeanUtils.copyProperties(comment, response);
// 转换时间格式
if (comment.getPublishTime() != null) {
response.setPublishTime(comment.getPublishTime().format(DATE_TIME_FORMATTER));
}
if (comment.getLastReplyTime() != null) {
response.setLastReplyTime(comment.getLastReplyTime().format(DATE_TIME_FORMATTER));
}
if (comment.getCreateTime() != null) {
response.setCreateTime(comment.getCreateTime().format(DATE_TIME_FORMATTER));
}
if (comment.getUpdateTime() != null) {
response.setUpdateTime(comment.getUpdateTime().format(DATE_TIME_FORMATTER));
}
// 转换JSON字段
try {
if (comment.getImages() != null) {
response.setImages(objectMapper.readValue(comment.getImages(), new TypeReference<List<String>>() {}));
}
if (comment.getMetadata() != null) {
response.setMetadata(objectMapper.readValue(comment.getMetadata(), Object.class));
}
} catch (JsonProcessingException e) {
// 忽略JSON解析错误
}
return response;
}
}
}
@@ -1,30 +1,18 @@
package com.emotion.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.emotion.common.BasePageRequest;
import com.emotion.common.PageResult;
import com.emotion.common.Result;
import com.emotion.dto.request.DiaryPostCreateRequest;
import com.emotion.dto.request.DiaryPostPageRequest;
import com.emotion.dto.request.DiaryPostUpdateRequest;
import com.emotion.dto.response.DiaryPostResponse;
import com.emotion.entity.DiaryPost;
import com.emotion.service.DiaryPostService;
import com.emotion.service.DiaryCommentService;
import com.emotion.service.UserService;
import com.emotion.service.AiChatService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.BeanUtils;
import com.emotion.util.UserContextUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.stream.Collectors;
import com.emotion.entity.DiaryComment;
/**
* 用户日记控制器
@@ -33,161 +21,75 @@ import com.emotion.entity.DiaryComment;
* @date 2025-07-23
*/
@RestController
@RequestMapping("/diary-post")
@RequestMapping("/diaryPost")
public class DiaryPostController {
@Autowired
private DiaryPostService diaryPostService;
@Autowired
private DiaryCommentService diaryCommentService;
@Autowired
private UserService userService;
@Autowired
private AiChatService aiChatService;
@Autowired
private ObjectMapper objectMapper;
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
/**
* 分页查询日记
*/
@GetMapping("/page")
public Result<PageResult<DiaryPostResponse>> getPage(@Validated BasePageRequest request) {
IPage<DiaryPost> page = diaryPostService.getPage(request);
List<DiaryPostResponse> responses = page.getRecords().stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
PageResult<DiaryPostResponse> 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);
}
/**
* 根据用户ID分页查询日记
*/
@GetMapping("/user/{userId}/page")
public Result<PageResult<DiaryPostResponse>> getPageByUserId(@PathVariable String userId,
@Validated BasePageRequest request) {
IPage<DiaryPost> page = diaryPostService.getPageByUserId(userId, request);
List<DiaryPostResponse> responses = page.getRecords().stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
PageResult<DiaryPostResponse> 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);
}
/**
* 根据用户ID查询公开日记
*/
@GetMapping("/user/{userId}/public/page")
public Result<PageResult<DiaryPostResponse>> getPublicPageByUserId(@PathVariable String userId,
@Validated BasePageRequest request) {
IPage<DiaryPost> page = diaryPostService.getPublicPageByUserId(userId, request);
List<DiaryPostResponse> responses = page.getRecords().stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
PageResult<DiaryPostResponse> 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);
}
/**
* 查询精选日记
*/
@GetMapping("/featured/page")
public Result<PageResult<DiaryPostResponse>> getFeaturedPage(@Validated BasePageRequest request) {
IPage<DiaryPost> page = diaryPostService.getFeaturedPage(request);
List<DiaryPostResponse> responses = page.getRecords().stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
PageResult<DiaryPostResponse> 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);
@GetMapping(value = "/page")
public Result<PageResult<DiaryPostResponse>> getPage(@Validated DiaryPostPageRequest request) {
return Result.success(diaryPostService.getPageWithResponse(request));
}
/**
* 根据ID获取日记详情
*/
@GetMapping("/{id}")
public Result<DiaryPostResponse> getById(@PathVariable String id) {
DiaryPost diaryPost = diaryPostService.getById(id);
if (diaryPost == null) {
@GetMapping(value = "/detail")
public Result<DiaryPostResponse> getById(@RequestParam String id) {
DiaryPostResponse response = diaryPostService.getDiaryPostResponseById(id);
if (response == null) {
return Result.notFound("日记不存在");
}
// 增加浏览数
diaryPostService.incrementViewCount(id);
return Result.success(convertToResponse(diaryPost));
return Result.success(response);
}
/**
* 创建日记
*/
@PostMapping
@PostMapping(value = "/create")
public Result<DiaryPostResponse> create(@Valid @RequestBody DiaryPostCreateRequest request) {
DiaryPost diaryPost = diaryPostService.createDiaryPost(request);
return Result.success(convertToResponse(diaryPost));
// 从上下文中获取当前用户ID,而不是直接使用请求中的用户ID
String currentUserId = UserContextUtils.getCurrentUserId();
if (currentUserId != null) {
request.setUserId(currentUserId);
}
return Result.success(diaryPostService.createDiaryPostWithResponse(request));
}
/**
* 发表日记并生成AI评论
*/
@PostMapping("/publish")
@PostMapping(value = "/publish")
public Result<DiaryPostResponse> publish(@Valid @RequestBody DiaryPostCreateRequest request) {
// 从上下文中获取当前用户ID,而不是直接使用请求中的用户ID
String currentUserId = UserContextUtils.getCurrentUserId();
if (currentUserId != null) {
request.setUserId(currentUserId);
}
return Result.success(diaryPostService.publishDiaryWithAiComment(request));
}
/**
* 更新日记
*/
@PutMapping("/{id}")
public Result<DiaryPostResponse> update(@PathVariable String id, @Valid @RequestBody DiaryPostUpdateRequest request) {
boolean updated = diaryPostService.updateDiaryPost(id, request);
if (!updated) {
@PutMapping(value = "/update")
public Result<DiaryPostResponse> update(@Valid @RequestBody DiaryPostUpdateRequest request) {
DiaryPostResponse response = diaryPostService.updateDiaryPostWithResponse(request);
if (response == null) {
return Result.error("更新失败");
}
DiaryPost updatedDiaryPost = diaryPostService.getById(id);
return Result.success(convertToResponse(updatedDiaryPost));
return Result.success(response);
}
/**
* 删除日记
*/
@DeleteMapping("/{id}")
public Result<Void> delete(@PathVariable String id) {
@DeleteMapping(value = "/delete")
public Result<Void> delete(@RequestParam String id) {
boolean deleted = diaryPostService.deleteDiaryPost(id);
if (!deleted) {
return Result.error("删除失败");
@@ -198,8 +100,8 @@ public class DiaryPostController {
/**
* 软删除日记
*/
@DeleteMapping("/{id}/soft")
public Result<Void> softDelete(@PathVariable String id) {
@DeleteMapping(value = "/softDelete")
public Result<Void> softDelete(@RequestParam String id) {
boolean deleted = diaryPostService.softDeleteDiaryPost(id);
if (!deleted) {
return Result.error("删除失败");
@@ -210,8 +112,8 @@ public class DiaryPostController {
/**
* 恢复日记
*/
@PutMapping("/{id}/restore")
public Result<Void> restore(@PathVariable String id) {
@PutMapping(value = "/restore")
public Result<Void> restore(@RequestParam String id) {
boolean restored = diaryPostService.restoreDiaryPost(id);
if (!restored) {
return Result.error("恢复失败");
@@ -222,8 +124,8 @@ public class DiaryPostController {
/**
* 点赞日记
*/
@PostMapping("/{id}/like")
public Result<Void> like(@PathVariable String id) {
@PostMapping(value = "/like")
public Result<Void> like(@RequestParam String id) {
boolean liked = diaryPostService.incrementLikeCount(id);
if (!liked) {
return Result.error("点赞失败");
@@ -234,8 +136,8 @@ public class DiaryPostController {
/**
* 取消点赞日记
*/
@DeleteMapping("/{id}/like")
public Result<Void> unlike(@PathVariable String id) {
@DeleteMapping(value = "/unlike")
public Result<Void> unlike(@RequestParam String id) {
boolean unliked = diaryPostService.decrementLikeCount(id);
if (!unliked) {
return Result.error("取消点赞失败");
@@ -246,8 +148,8 @@ public class DiaryPostController {
/**
* 分享日记
*/
@PostMapping("/{id}/share")
public Result<Void> share(@PathVariable String id) {
@PostMapping(value = "/share")
public Result<Void> share(@RequestParam String id) {
boolean shared = diaryPostService.incrementShareCount(id);
if (!shared) {
return Result.error("分享失败");
@@ -258,8 +160,8 @@ public class DiaryPostController {
/**
* 设置精选状态
*/
@PutMapping("/{id}/featured/{featured}")
public Result<Void> setFeatured(@PathVariable String id, @PathVariable Integer featured) {
@PutMapping(value = "/setFeatured")
public Result<Void> setFeatured(@RequestParam String id, @RequestParam Integer featured) {
boolean set = diaryPostService.setFeatured(id, featured);
if (!set) {
return Result.error("设置精选状态失败");
@@ -270,90 +172,12 @@ public class DiaryPostController {
/**
* 设置置顶优先级
*/
@PutMapping("/{id}/priority/{priority}")
public Result<Void> setPriority(@PathVariable String id, @PathVariable Integer priority) {
@PutMapping(value = "/setPriority")
public Result<Void> setPriority(@RequestParam String id, @RequestParam Integer priority) {
boolean set = diaryPostService.setPriority(id, priority);
if (!set) {
return Result.error("设置优先级失败");
}
return Result.success();
}
/**
* 统计用户日记数量
*/
@GetMapping("/user/{userId}/count")
public Result<Long> countByUserId(@PathVariable String userId) {
Long count = diaryPostService.countByUserId(userId);
return Result.success(count);
}
/**
* 统计用户公开日记数量
*/
@GetMapping("/user/{userId}/public/count")
public Result<Long> countPublicByUserId(@PathVariable String userId) {
Long count = diaryPostService.countPublicByUserId(userId);
return Result.success(count);
}
/**
* 统计精选日记数量
*/
@GetMapping("/featured/count")
public Result<Long> countFeatured() {
Long count = diaryPostService.countFeatured();
return Result.success(count);
}
/**
* 转换实体为响应DTO
*/
private DiaryPostResponse convertToResponse(DiaryPost diaryPost) {
DiaryPostResponse response = new DiaryPostResponse();
BeanUtils.copyProperties(diaryPost, response);
// 转换时间格式
if (diaryPost.getPublishTime() != null) {
response.setPublishTime(diaryPost.getPublishTime().format(DATE_TIME_FORMATTER));
}
if (diaryPost.getLastCommentTime() != null) {
response.setLastCommentTime(diaryPost.getLastCommentTime().format(DATE_TIME_FORMATTER));
}
if (diaryPost.getAiCommentTime() != null) {
response.setAiCommentTime(diaryPost.getAiCommentTime().format(DATE_TIME_FORMATTER));
}
if (diaryPost.getCreateTime() != null) {
response.setCreateTime(diaryPost.getCreateTime().format(DATE_TIME_FORMATTER));
}
if (diaryPost.getUpdateTime() != null) {
response.setUpdateTime(diaryPost.getUpdateTime().format(DATE_TIME_FORMATTER));
}
// 转换JSON字段
try {
if (diaryPost.getImages() != null) {
response.setImages(objectMapper.readValue(diaryPost.getImages(), new TypeReference<List<String>>() {}));
}
if (diaryPost.getVideos() != null) {
response.setVideos(objectMapper.readValue(diaryPost.getVideos(), new TypeReference<List<String>>() {}));
}
if (diaryPost.getTags() != null) {
response.setTags(objectMapper.readValue(diaryPost.getTags(), new TypeReference<List<String>>() {}));
}
if (diaryPost.getAiKeywords() != null) {
response.setAiKeywords(objectMapper.readValue(diaryPost.getAiKeywords(), new TypeReference<List<String>>() {}));
}
if (diaryPost.getAiEmotionAnalysis() != null) {
response.setAiEmotionAnalysis(objectMapper.readValue(diaryPost.getAiEmotionAnalysis(), Object.class));
}
if (diaryPost.getMetadata() != null) {
response.setMetadata(objectMapper.readValue(diaryPost.getMetadata(), Object.class));
}
} catch (JsonProcessingException e) {
// 忽略JSON解析错误
}
return response;
}
}
}
@@ -1,24 +1,22 @@
package com.emotion.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.emotion.common.PageResult;
import com.emotion.common.Result;
import com.emotion.dto.request.PageRequest;
import com.emotion.dto.response.BaseResponse;
import com.emotion.entity.EmotionAnalysis;
import com.emotion.dto.request.EmotionAnalysisCreateRequest;
import com.emotion.dto.request.EmotionAnalysisPageRequest;
import com.emotion.dto.request.EmotionAnalysisUpdateRequest;
import com.emotion.dto.response.EmotionAnalysisResponse;
import com.emotion.service.EmotionAnalysisService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.stream.Collectors;
/**
* 情绪分析控制器
@@ -33,108 +31,74 @@ public class EmotionAnalysisController {
@Autowired
private EmotionAnalysisService emotionAnalysisService;
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
/**
* 分页查询情绪分析记录
*/
@GetMapping("/page")
public Result<PageResult<EmotionAnalysisResponse>> getPage(@Validated PageRequest request) {
IPage<EmotionAnalysis> page = emotionAnalysisService.getPage(request);
List<EmotionAnalysisResponse> responses = page.getRecords().stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
PageResult<EmotionAnalysisResponse> 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);
public Result<PageResult<EmotionAnalysisResponse>> getPage(@Validated EmotionAnalysisPageRequest request) {
return Result.success(emotionAnalysisService.getPageWithResponse(request));
}
/**
* 根据用户ID分页查询情绪分析记录
*/
@GetMapping("/user/{userId}/page")
public Result<PageResult<EmotionAnalysisResponse>> getPageByUserId(@PathVariable String userId, @Validated PageRequest request) {
IPage<EmotionAnalysis> page = emotionAnalysisService.getPageByUserId(request, userId);
List<EmotionAnalysisResponse> responses = page.getRecords().stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
PageResult<EmotionAnalysisResponse> 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);
@GetMapping("/user/page")
public Result<PageResult<EmotionAnalysisResponse>> getPageByUserId(
@RequestParam String userId,
@Validated EmotionAnalysisPageRequest request) {
return Result.success(emotionAnalysisService.getPageByUserIdWithResponse(userId, request));
}
/**
* 根据ID获取情绪分析记录
*/
@GetMapping("/{id}")
public Result<EmotionAnalysisResponse> getById(@PathVariable String id) {
EmotionAnalysis analysis = emotionAnalysisService.getById(id);
if (analysis == null) {
@GetMapping
public Result<EmotionAnalysisResponse> getById(@RequestParam String id) {
EmotionAnalysisResponse response = emotionAnalysisService.getEmotionAnalysisResponseById(id);
if (response == null) {
return Result.notFound("情绪分析记录不存在");
}
return Result.success(convertToResponse(analysis));
return Result.success(response);
}
/**
* 根据消息ID获取情绪分析记录
*/
@GetMapping("/message/{messageId}")
public Result<EmotionAnalysisResponse> getByMessageId(@PathVariable String messageId) {
EmotionAnalysis analysis = emotionAnalysisService.getByMessageId(messageId);
if (analysis == null) {
@GetMapping("/message")
public Result<EmotionAnalysisResponse> getByMessageId(@RequestParam String messageId) {
EmotionAnalysisResponse response = emotionAnalysisService.getEmotionAnalysisResponseByMessageId(messageId);
if (response == null) {
return Result.notFound("情绪分析记录不存在");
}
return Result.success(convertToResponse(analysis));
return Result.success(response);
}
/**
* 创建情绪分析记录
*/
@PostMapping
public Result<EmotionAnalysisResponse> create(@RequestBody @Validated EmotionAnalysisCreateRequest request) {
EmotionAnalysis analysis = emotionAnalysisService.createEmotionAnalysis(
request.getMessageId(),
request.getUserId(),
request.getPrimaryEmotion(),
request.getPolarity(),
request.getIntensity(),
request.getConfidence()
);
return Result.success(convertToResponse(analysis));
public Result<EmotionAnalysisResponse> create(@RequestBody @Valid EmotionAnalysisCreateRequest request) {
return Result.success(emotionAnalysisService.createEmotionAnalysisWithResponse(request));
}
/**
* 更新情绪分析记录
*/
@PutMapping("/{id}")
public Result<EmotionAnalysisResponse> update(@PathVariable String id, @RequestBody EmotionAnalysis analysis) {
analysis.setId(id);
boolean updated = emotionAnalysisService.updateById(analysis);
if (!updated) {
@PutMapping
public Result<EmotionAnalysisResponse> update(@RequestBody @Valid EmotionAnalysisUpdateRequest request) {
EmotionAnalysisResponse response = emotionAnalysisService.updateEmotionAnalysisWithResponse(request);
if (response == null) {
return Result.error("更新失败");
}
EmotionAnalysis updatedAnalysis = emotionAnalysisService.getById(id);
return Result.success(convertToResponse(updatedAnalysis));
return Result.success(response);
}
/**
* 删除情绪分析记录
*/
@DeleteMapping("/{id}")
public Result<Void> delete(@PathVariable String id) {
boolean deleted = emotionAnalysisService.removeById(id);
@DeleteMapping
public Result<Void> delete(@RequestParam String id) {
boolean deleted = emotionAnalysisService.deleteEmotionAnalysis(id);
if (!deleted) {
return Result.error("删除失败");
}
@@ -144,59 +108,53 @@ public class EmotionAnalysisController {
/**
* 根据主要情绪查询分析记录
*/
@GetMapping("/emotion/{primaryEmotion}")
public Result<List<EmotionAnalysisResponse>> getByPrimaryEmotion(@PathVariable String primaryEmotion) {
List<EmotionAnalysis> analyses = emotionAnalysisService.getByPrimaryEmotion(primaryEmotion);
List<EmotionAnalysisResponse> responses = analyses.stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
@GetMapping("/emotion")
public Result<List<EmotionAnalysisResponse>> getByPrimaryEmotion(@RequestParam String primaryEmotion) {
List<EmotionAnalysisResponse> responses = emotionAnalysisService
.getEmotionAnalysisResponsesByPrimaryEmotion(primaryEmotion);
return Result.success(responses);
}
/**
* 根据情绪极性查询分析记录
*/
@GetMapping("/polarity/{polarity}")
public Result<List<EmotionAnalysisResponse>> getByPolarity(@PathVariable String polarity) {
List<EmotionAnalysis> analyses = emotionAnalysisService.getByPolarity(polarity);
List<EmotionAnalysisResponse> responses = analyses.stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
@GetMapping("/polarity")
public Result<List<EmotionAnalysisResponse>> getByPolarity(@RequestParam String polarity) {
List<EmotionAnalysisResponse> responses = emotionAnalysisService
.getEmotionAnalysisResponsesByPolarity(polarity);
return Result.success(responses);
}
/**
* 根据用户ID和情绪类型查询分析记录
*/
@GetMapping("/user/{userId}/emotion/{primaryEmotion}")
public Result<List<EmotionAnalysisResponse>> getByUserIdAndEmotion(@PathVariable String userId, @PathVariable String primaryEmotion) {
List<EmotionAnalysis> analyses = emotionAnalysisService.getByUserIdAndEmotion(userId, primaryEmotion);
List<EmotionAnalysisResponse> responses = analyses.stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
@GetMapping("/user/emotion")
public Result<List<EmotionAnalysisResponse>> getByUserIdAndEmotion(
@RequestParam String userId,
@RequestParam String primaryEmotion) {
List<EmotionAnalysisResponse> responses = emotionAnalysisService
.getEmotionAnalysisResponsesByUserIdAndEmotion(userId, primaryEmotion);
return Result.success(responses);
}
/**
* 根据时间范围查询用户情绪分析记录
*/
@GetMapping("/user/{userId}/time-range")
@GetMapping("/user/time-range")
public Result<List<EmotionAnalysisResponse>> getByUserIdAndTimeRange(
@PathVariable String userId,
@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime startTime,
@RequestParam String userId,
@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime startTime,
@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime endTime) {
List<EmotionAnalysis> analyses = emotionAnalysisService.getByUserIdAndTimeRange(userId, startTime, endTime);
List<EmotionAnalysisResponse> responses = analyses.stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
List<EmotionAnalysisResponse> responses = emotionAnalysisService
.getEmotionAnalysisResponsesByUserIdAndTimeRange(userId, startTime, endTime);
return Result.success(responses);
}
/**
* 统计用户的情绪分析记录数量
*/
@GetMapping("/user/{userId}/count")
public Result<Long> countByUserId(@PathVariable String userId) {
@GetMapping("/user/count")
public Result<Long> countByUserId(@RequestParam String userId) {
Long count = emotionAnalysisService.countByUserId(userId);
return Result.success(count);
}
@@ -204,20 +162,20 @@ public class EmotionAnalysisController {
/**
* 查询用户最近的情绪分析记录
*/
@GetMapping("/user/{userId}/recent")
public Result<List<EmotionAnalysisResponse>> getRecentByUserId(@PathVariable String userId, @RequestParam(defaultValue = "10") Integer limit) {
List<EmotionAnalysis> analyses = emotionAnalysisService.getRecentByUserId(userId, limit);
List<EmotionAnalysisResponse> responses = analyses.stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
@GetMapping("/user/recent")
public Result<List<EmotionAnalysisResponse>> getRecentByUserId(
@RequestParam String userId,
@RequestParam(defaultValue = "10") Integer limit) {
List<EmotionAnalysisResponse> responses = emotionAnalysisService
.getEmotionAnalysisResponsesRecentByUserId(userId, limit);
return Result.success(responses);
}
/**
* 查询用户的平均情绪强度
*/
@GetMapping("/user/{userId}/avg-intensity")
public Result<Double> getAvgIntensityByUserId(@PathVariable String userId) {
@GetMapping("/user/avg-intensity")
public Result<Double> getAvgIntensityByUserId(@RequestParam String userId) {
Double avgIntensity = emotionAnalysisService.getAvgIntensityByUserId(userId);
return Result.success(avgIntensity);
}
@@ -225,63 +183,9 @@ public class EmotionAnalysisController {
/**
* 查询用户最常见的情绪类型
*/
@GetMapping("/user/{userId}/most-frequent-emotion")
public Result<String> getMostFrequentEmotionByUserId(@PathVariable String userId) {
@GetMapping("/user/most-frequent-emotion")
public Result<String> getMostFrequentEmotionByUserId(@RequestParam String userId) {
String emotion = emotionAnalysisService.getMostFrequentEmotionByUserId(userId);
return Result.success(emotion);
}
/**
* 转换为响应对象
*/
private EmotionAnalysisResponse convertToResponse(EmotionAnalysis analysis) {
EmotionAnalysisResponse response = new EmotionAnalysisResponse();
BeanUtils.copyProperties(analysis, response);
response.setId(analysis.getId());
if (analysis.getCreateTime() != null) {
response.setCreateTime(analysis.getCreateTime().format(DATE_TIME_FORMATTER));
}
if (analysis.getUpdateTime() != null) {
response.setUpdateTime(analysis.getUpdateTime().format(DATE_TIME_FORMATTER));
}
return response;
}
/**
* 情绪分析创建请求
*/
@lombok.Data
public static class EmotionAnalysisCreateRequest {
@NotBlank(message = "消息ID不能为空")
private String messageId;
@NotBlank(message = "用户ID不能为空")
private String userId;
@NotBlank(message = "主要情绪不能为空")
private String primaryEmotion;
private String polarity;
@NotNull(message = "情绪强度不能为空")
private Double intensity;
@NotNull(message = "置信度不能为空")
private Double confidence;
}
/**
* 情绪分析响应类
*/
@lombok.Data
@lombok.EqualsAndHashCode(callSuper = true)
public static class EmotionAnalysisResponse extends BaseResponse {
private String messageId;
private String userId;
private String primaryEmotion;
private String polarity;
private Double intensity;
private Double confidence;
private String emotionDetails;
}
}
}
@@ -1,8 +1,11 @@
package com.emotion.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.emotion.common.PageResult;
import com.emotion.common.Result;
import com.emotion.entity.EmotionRecord;
import com.emotion.dto.request.EmotionRecordCreateRequest;
import com.emotion.dto.request.EmotionRecordPageRequest;
import com.emotion.dto.request.EmotionRecordUpdateRequest;
import com.emotion.dto.response.EmotionRecordResponse;
import com.emotion.service.EmotionRecordService;
import com.emotion.util.CurrentUserUtil;
import io.swagger.v3.oas.annotations.Operation;
@@ -11,10 +14,14 @@ import io.swagger.v3.oas.annotations.tags.Tag;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.util.List;
import java.util.Map;
/**
* 情绪记录控制器
@@ -36,29 +43,16 @@ public class EmotionRecordController {
* 创建情绪记录
*/
@PostMapping
public Result<Map<String, Object>> createRecord(@RequestBody Map<String, Object> request) {
log.info("创建情绪记录: {}", request);
@Operation(summary = "创建情绪记录", description = "创建新的情绪记录")
public Result<EmotionRecordResponse> createRecord(@RequestBody @Valid EmotionRecordCreateRequest request) {
log.info("创建情绪记录: userId={}", request.getUserId());
try {
Map<String, Object> record = new HashMap<>();
record.put("id", "record-" + System.currentTimeMillis());
record.put("userId", request.get("userId"));
record.put("recordDate", request.get("recordDate"));
record.put("emotionType", request.get("emotionType"));
record.put("intensity", request.get("intensity"));
record.put("triggers", request.get("triggers"));
record.put("description", request.get("description"));
record.put("tags", request.get("tags"));
record.put("weather", request.get("weather"));
record.put("location", request.get("location"));
record.put("activity", request.get("activity"));
record.put("createTime", LocalDateTime.now());
return Result.success("创建成功", record);
} catch (Exception e) {
log.error("创建情绪记录失败: {}", e.getMessage());
return Result.error("创建失败");
}
// 从上下文中获取当前用户ID
String userId = CurrentUserUtil.requireCurrentUserId();
request.setUserId(userId);
EmotionRecordResponse response = emotionRecordService.createEmotionRecordWithResponse(request);
return Result.success("创建成功", response);
}
/**
@@ -66,30 +60,20 @@ public class EmotionRecordController {
*/
@Operation(summary = "获取用户情绪记录列表", description = "分页获取当前用户的情绪记录,按创建时间倒序")
@GetMapping("/user")
public Result<IPage<EmotionRecord>> getRecordList(
@Parameter(description = "页码,从1开始") @RequestParam(defaultValue = "1") Integer current,
@Parameter(description = "每页大小") @RequestParam(defaultValue = "10") Integer size) {
public Result<PageResult<EmotionRecordResponse>> getRecordList(
@Validated EmotionRecordPageRequest request) {
try {
// 从上下文中获取当前用户ID
String userId = CurrentUserUtil.requireCurrentUserId();
// 从上下文中获取当前用户ID
String userId = CurrentUserUtil.requireCurrentUserId();
log.info("获取用户情绪记录列表: userId={}, current={}, size={}", userId, current, size);
log.info("获取用户情绪记录列表: userId={}, current={}, size={}", userId, request.getCurrent(), request.getSize());
IPage<EmotionRecord> page = emotionRecordService.getByUserIdWithPage(userId, current, size);
PageResult<EmotionRecordResponse> page = emotionRecordService.getPageByUserIdWithResponse(userId, request);
log.info("获取用户情绪记录成功: userId={}, total={}, records={}",
userId, page.getTotal(), page.getRecords().size());
log.info("获取用户情绪记录成功: userId={}, total={}, records={}",
userId, page.getTotal(), page.getRecords().size());
return Result.success(page);
} catch (IllegalStateException e) {
log.warn("用户认证失败: {}", e.getMessage());
return Result.error(e.getMessage());
} catch (Exception e) {
log.error("获取用户情绪记录失败", e);
return Result.error("获取情绪记录失败: " + e.getMessage());
}
return Result.success(page);
}
/**
@@ -97,138 +81,82 @@ public class EmotionRecordController {
*/
@Operation(summary = "获取用户最近情绪记录", description = "获取当前用户最近的情绪记录列表")
@GetMapping("/user/recent")
public Result<List<EmotionRecord>> getRecentRecords(
public Result<List<EmotionRecordResponse>> getRecentRecords(
@Parameter(description = "限制数量") @RequestParam(defaultValue = "5") Integer limit) {
try {
// 从上下文中获取当前用户ID
String userId = CurrentUserUtil.requireCurrentUserId();
// 从上下文中获取当前用户ID
String userId = CurrentUserUtil.requireCurrentUserId();
log.info("获取用户最近情绪记录: userId={}, limit={}", userId, limit);
log.info("获取用户最近情绪记录: userId={}, limit={}", userId, limit);
List<EmotionRecord> records = emotionRecordService.getRecentByUserId(userId, limit);
List<EmotionRecordResponse> records = emotionRecordService.getRecentByUserIdWithResponse(userId, limit);
log.info("获取用户最近情绪记录成功: userId={}, records={}", userId, records.size());
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());
}
return Result.success(records);
}
/**
* 获取情绪记录详情
*/
@GetMapping("/{recordId}")
public Result<Map<String, Object>> getRecord(@PathVariable String recordId) {
log.info("获取情绪记录详情: {}", recordId);
@Operation(summary = "获取情绪记录详情", description = "根据ID获取情绪记录详情")
@GetMapping
public Result<EmotionRecordResponse> getRecord(@RequestParam String id) {
log.info("获取情绪记录详情: {}", id);
try {
Map<String, Object> record = new HashMap<>();
record.put("id", recordId);
record.put("userId", "user123");
record.put("recordDate", LocalDate.now());
record.put("emotionType", "joy");
record.put("intensity", 0.8);
record.put("triggers", "完成了重要项目");
record.put("description", "今天心情很好,完成了一个重要的项目");
record.put("tags", Arrays.asList("工作", "成就感"));
record.put("weather", "晴天");
record.put("location", "办公室");
record.put("activity", "工作");
record.put("createTime", LocalDateTime.now());
return Result.success(record);
} catch (Exception e) {
log.error("获取情绪记录详情失败: {}", e.getMessage());
return Result.error("获取详情失败");
EmotionRecordResponse response = emotionRecordService.getEmotionRecordResponseById(id);
if (response == null) {
return Result.notFound("情绪记录不存在");
}
return Result.success(response);
}
/**
* 更新情绪记录
*/
@PutMapping("/{recordId}")
public Result<Map<String, Object>> updateRecord(@PathVariable String recordId,
@RequestBody Map<String, Object> request) {
log.info("更新情绪记录: {}", recordId);
@Operation(summary = "更新情绪记录", description = "更新指定的情绪记录")
@PutMapping
public Result<EmotionRecordResponse> updateRecord(@RequestBody @Valid EmotionRecordUpdateRequest request) {
log.info("更新情绪记录: {}", request.getId());
try {
Map<String, Object> record = new HashMap<>();
record.put("id", recordId);
record.put("emotionType", request.get("emotionType"));
record.put("intensity", request.get("intensity"));
record.put("triggers", request.get("triggers"));
record.put("description", request.get("description"));
record.put("tags", request.get("tags"));
record.put("updateTime", LocalDateTime.now());
return Result.success("更新成功", record);
} catch (Exception e) {
log.error("更新情绪记录失败: {}", e.getMessage());
return Result.error("更新失败");
EmotionRecordResponse response = emotionRecordService.updateEmotionRecordWithResponse(request);
if (response == null) {
return Result.notFound("情绪记录不存在");
}
return Result.success("更新成功", response);
}
/**
* 删除情绪记录
*/
@DeleteMapping("/{recordId}")
public Result<String> deleteRecord(@PathVariable String recordId) {
log.info("删除情绪记录: {}", recordId);
@Operation(summary = "删除情绪记录", description = "删除指定的情绪记录")
@DeleteMapping
public Result<String> deleteRecord(@RequestParam String id) {
log.info("删除情绪记录: {}", id);
try {
return Result.success("删除成功");
} catch (Exception e) {
log.error("删除情绪记录失败: {}", e.getMessage());
return Result.error("删除失败");
boolean deleted = emotionRecordService.deleteEmotionRecord(id);
if (!deleted) {
return Result.notFound("情绪记录不存在");
}
return Result.success("删除成功");
}
/**
* 获取情绪统计
*/
@GetMapping("/stats/{userId}")
public Result<Map<String, Object>> getEmotionStats(@PathVariable String userId,
@RequestParam(required = false) String startDate,
@RequestParam(required = false) String endDate) {
@Operation(summary = "获取情绪统计", description = "获取当前用户的情绪统计信息")
@GetMapping("/stats")
public Result<Map<String, Object>> getEmotionStats(
@RequestParam(required = false) String startDate,
@RequestParam(required = false) String endDate) {
// 从上下文中获取当前用户ID
String userId = CurrentUserUtil.requireCurrentUserId();
log.info("获取情绪统计: userId={}, startDate={}, endDate={}", userId, startDate, endDate);
try {
Map<String, Object> stats = new HashMap<>();
// 情绪类型分布
Map<String, Integer> emotionDistribution = new HashMap<>();
emotionDistribution.put("joy", 15);
emotionDistribution.put("sadness", 5);
emotionDistribution.put("anger", 3);
emotionDistribution.put("fear", 2);
emotionDistribution.put("surprise", 8);
emotionDistribution.put("neutral", 12);
stats.put("emotionDistribution", emotionDistribution);
stats.put("totalRecords", 45);
stats.put("averageIntensity", 0.72);
stats.put("mostFrequentEmotion", "joy");
stats.put("emotionTrend", "improving");
return Result.success(stats);
} catch (Exception e) {
log.error("获取情绪统计失败: {}", e.getMessage());
return Result.error("获取统计失败");
}
Map<String, Object> stats = emotionRecordService.getEmotionStats(userId, startDate, endDate);
return Result.success(stats);
}
/**
* 获取随机情绪类型
*/
private String getRandomEmotion() {
String[] emotions = {"joy", "sadness", "anger", "fear", "surprise", "neutral"};
return emotions[new Random().nextInt(emotions.length)];
}
}
}
@@ -22,4 +22,4 @@ public class ChatStatsRequest extends BaseRequest {
* 会话ID
*/
private String conversationId;
}
}
@@ -15,6 +15,11 @@ import javax.validation.constraints.NotBlank;
@EqualsAndHashCode(callSuper = true)
public class ConversationCreateRequest extends BaseRequest {
/**
* 对话ID(更新时使用)
*/
private String id;
/**
* 用户ID
*/
@@ -0,0 +1,30 @@
package com.emotion.dto.request;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 对话分页请求类
*
* @author emotion-museum
* @date 2025-09-08
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class ConversationPageRequest extends PageRequest {
/**
* 用户ID(可选)
*/
private String userId;
/**
* 对话状态(可选)
*/
private String status;
/**
* 对话类型(可选)
*/
private String type;
}
@@ -4,7 +4,6 @@ import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.util.List;
/**
@@ -16,6 +15,11 @@ import java.util.List;
@Data
public class DiaryCommentCreateRequest {
/**
* 评论ID (用于更新操作)
*/
private String id;
/**
* 日记ID
*/
@@ -49,4 +53,4 @@ public class DiaryCommentCreateRequest {
*/
@NotNull(message = "是否匿名不能为空")
private Integer isAnonymous;
}
}
@@ -0,0 +1,38 @@
package com.emotion.dto.request;
import com.emotion.common.BasePageRequest;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.Pattern;
/**
* 日记评论分页请求类
*
* @author emotion-museum
* @date 2025-09-08
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class DiaryCommentPageRequest extends BasePageRequest {
/**
* 日记ID(可选)
*/
private String diaryId;
/**
* 用户ID(可选)
*/
private String userId;
/**
* 评论类型(可选)
*/
private String commentType;
/**
* 是否只查询顶级评论(可选)
*/
private Boolean topLevelOnly;
}
@@ -0,0 +1,41 @@
package com.emotion.dto.request;
import com.emotion.common.BasePageRequest;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 日记分页请求类
*
* @author emotion-museum
* @date 2025-09-08
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class DiaryPostPageRequest extends BasePageRequest {
/**
* 用户ID(可选)
*/
private String userId;
/**
* 是否只查询公开日记(可选)
*/
private Boolean publicOnly;
/**
* 是否只查询精选日记(可选)
*/
private Boolean featuredOnly;
/**
* 心情状态(可选)
*/
private String mood;
/**
* 标签(可选)
*/
private String tag;
}
@@ -2,6 +2,7 @@ package com.emotion.dto.request;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
import java.math.BigDecimal;
import java.util.List;
@@ -15,6 +16,12 @@ import java.util.List;
@Data
public class DiaryPostUpdateRequest {
/**
* 日记ID
*/
@NotBlank(message = "日记ID不能为空")
private String id;
/**
* 日记标题
*/
@@ -88,4 +95,4 @@ public class DiaryPostUpdateRequest {
* 状态: draft-草稿, published-已发布, hidden-隐藏, deleted-已删除
*/
private String status;
}
}
@@ -0,0 +1,51 @@
package com.emotion.dto.request;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
/**
* 情绪分析创建请求类
*
* @author emotion-museum
* @date 2025-09-08
*/
@Data
public class EmotionAnalysisCreateRequest {
/**
* 消息ID
*/
@NotBlank(message = "消息ID不能为空")
private String messageId;
/**
* 用户ID
*/
@NotBlank(message = "用户ID不能为空")
private String userId;
/**
* 主要情绪
*/
@NotBlank(message = "主要情绪不能为空")
private String primaryEmotion;
/**
* 情绪极性
*/
private String polarity;
/**
* 情绪强度
*/
@NotNull(message = "情绪强度不能为空")
private Double intensity;
/**
* 置信度
*/
@NotNull(message = "置信度不能为空")
private Double confidence;
}
@@ -0,0 +1,36 @@
package com.emotion.dto.request;
import com.emotion.common.BasePageRequest;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 情绪分析分页请求类
*
* @author emotion-museum
* @date 2025-09-08
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class EmotionAnalysisPageRequest extends BasePageRequest {
/**
* 用户ID(可选)
*/
private String userId;
/**
* 消息ID(可选)
*/
private String messageId;
/**
* 主要情绪(可选)
*/
private String primaryEmotion;
/**
* 情绪极性(可选)
*/
private String polarity;
}
@@ -0,0 +1,51 @@
package com.emotion.dto.request;
import lombok.Data;
import javax.validation.constraints.NotBlank;
/**
* 情绪分析更新请求类
*
* @author emotion-museum
* @date 2025-09-08
*/
@Data
public class EmotionAnalysisUpdateRequest {
/**
* 情绪分析ID
*/
@NotBlank(message = "情绪分析ID不能为空")
private String id;
/**
* 消息ID
*/
private String messageId;
/**
* 用户ID
*/
private String userId;
/**
* 主要情绪
*/
private String primaryEmotion;
/**
* 情绪极性
*/
private String polarity;
/**
* 情绪强度
*/
private Double intensity;
/**
* 置信度
*/
private Double confidence;
}
@@ -0,0 +1,83 @@
package com.emotion.dto.request;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.List;
/**
* 情绪记录创建请求类
*
* @author emotion-museum
* @date 2025-09-08
*/
@Data
public class EmotionRecordCreateRequest {
/**
* 用户ID
*/
@NotBlank(message = "用户ID不能为空")
private String userId;
/**
* 记录日期
*/
@NotNull(message = "记录日期不能为空")
private LocalDate recordDate;
/**
* 情绪类型
*/
@NotBlank(message = "情绪类型不能为空")
private String emotionType;
/**
* 情绪强度
*/
@NotNull(message = "情绪强度不能为空")
private BigDecimal intensity;
/**
* 触发因素
*/
private String triggers;
/**
* 描述
*/
private String description;
/**
* 标签
*/
private List<String> tags;
/**
* 天气
*/
private String weather;
/**
* 地点
*/
private String location;
/**
* 活动
*/
private String activity;
/**
* 相关人物
*/
private String people;
/**
* 备注
*/
private String notes;
}
@@ -0,0 +1,31 @@
package com.emotion.dto.request;
import com.emotion.common.BasePageRequest;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 情绪记录分页请求类
*
* @author emotion-museum
* @date 2025-09-08
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class EmotionRecordPageRequest extends BasePageRequest {
/**
* 用户ID(可选)
*/
private String userId;
/**
* 情绪类型(可选)
*/
private String emotionType;
/**
* 地点(可选)
*/
private String location;
}
@@ -0,0 +1,79 @@
package com.emotion.dto.request;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.List;
/**
* 情绪记录更新请求类
*
* @author emotion-museum
* @date 2025-09-08
*/
@Data
public class EmotionRecordUpdateRequest {
/**
* 情绪记录ID
*/
@NotBlank(message = "情绪记录ID不能为空")
private String id;
/**
* 记录日期
*/
private LocalDate recordDate;
/**
* 情绪类型
*/
private String emotionType;
/**
* 情绪强度
*/
private BigDecimal intensity;
/**
* 触发因素
*/
private String triggers;
/**
* 描述
*/
private String description;
/**
* 标签
*/
private List<String> tags;
/**
* 天气
*/
private String weather;
/**
* 地点
*/
private String location;
/**
* 活动
*/
private String activity;
/**
* 相关人物
*/
private String people;
/**
* 备注
*/
private String notes;
}
@@ -0,0 +1,20 @@
package com.emotion.dto.request;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 访客用户信息请求类
*
* @author emotion-museum
* @date 2025-07-24
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class GuestUserInfoRequest extends BaseRequest {
/**
* 客户端IP
*/
private String clientIp;
}
@@ -0,0 +1,40 @@
package com.emotion.dto.request;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* WebSocket请求对象
*
* @author emotion-museum
* @date 2025-09-08
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class WebSocketRequest extends BaseRequest {
/**
* 消息内容
*/
private String content;
/**
* 发送者ID
*/
private String senderId;
/**
* 发送者类型
*/
private String senderType;
/**
* 消息类型
*/
private String messageType;
/**
* 会话ID(可选)
*/
private String conversationId;
}
@@ -0,0 +1,65 @@
package com.emotion.dto.request.achievement;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
/**
* 创建成就请求
*
* @author emotion-museum
* @date 2025-09-08
*/
@Data
public class AchievementCreateRequest {
/**
* 成就标题
*/
@NotBlank(message = "成就标题不能为空")
private String title;
/**
* 描述
*/
private String description;
/**
* 分类
*/
@NotBlank(message = "分类不能为空")
private String category;
/**
* 图标
*/
private String icon;
/**
* 稀有度
*/
@NotBlank(message = "稀有度不能为空")
private String rarity;
/**
* 条件类型
*/
private String conditionType;
/**
* 条件值
*/
private String conditionValue;
/**
* 奖励
*/
private String rewards;
/**
* 是否隐藏
*/
private Integer isHidden;
}
@@ -0,0 +1,40 @@
package com.emotion.dto.request.achievement;
import com.emotion.common.BasePageRequest;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
/**
* 成就分页查询请求
*
* @author emotion-museum
* @date 2025-09-08
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class AchievementPageRequest extends BasePageRequest {
/**
* 成就分类
*/
private String category;
/**
* 成就稀有度
*/
private String rarity;
/**
* 是否已解锁
*/
private Boolean unlocked;
/**
* 是否隐藏
*/
private Boolean hidden;
}
@@ -0,0 +1,28 @@
package com.emotion.dto.request.achievement;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
/**
* 更新成就进度请求
*
* @author emotion-museum
* @date 2025-09-08
*/
@Data
public class AchievementProgressUpdateRequest {
/**
* 成就ID
*/
@NotBlank(message = "成就ID不能为空")
private String id;
/**
* 进度值
*/
@NotNull(message = "进度值不能为空")
private Double progress;
}
@@ -0,0 +1,21 @@
package com.emotion.dto.request.achievement;
import lombok.Data;
import javax.validation.constraints.NotBlank;
/**
* 解锁成就请求
*
* @author emotion-museum
* @date 2025-09-08
*/
@Data
public class AchievementUnlockRequest {
/**
* 成就ID
*/
@NotBlank(message = "成就ID不能为空")
private String id;
}
@@ -0,0 +1,68 @@
package com.emotion.dto.request.achievement;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
/**
* 更新成就请求
*
* @author emotion-museum
* @date 2025-09-08
*/
@Data
public class AchievementUpdateRequest {
/**
* 成就ID
*/
@NotBlank(message = "成就ID不能为空")
private String id;
/**
* 成就标题
*/
private String title;
/**
* 描述
*/
private String description;
/**
* 分类
*/
private String category;
/**
* 图标
*/
private String icon;
/**
* 稀有度
*/
private String rarity;
/**
* 条件类型
*/
private String conditionType;
/**
* 条件值
*/
private String conditionValue;
/**
* 奖励
*/
private String rewards;
/**
* 是否隐藏
*/
private Integer isHidden;
}
@@ -0,0 +1,40 @@
package com.emotion.dto.request.comment;
import com.emotion.dto.request.BaseRequest;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.NotBlank;
/**
* 评论创建请求
*
* @author emotion-museum
* @date 2025-09-08
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class CommentCreateRequest extends BaseRequest {
/**
* 帖子ID
*/
@NotBlank(message = "帖子ID不能为空")
private String postId;
/**
* 用户ID
*/
private String userId; // 不再使用@NotBlank注解,因为将从上下文中获取
/**
* 评论内容
*/
@NotBlank(message = "评论内容不能为空")
private String content;
/**
* 回复的评论ID
*/
private String replyToId;
}
@@ -0,0 +1,30 @@
package com.emotion.dto.request.comment;
import com.emotion.dto.request.PageRequest;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.Pattern;
/**
* 评论分页请求
*
* @author emotion-museum
* @date 2025-09-08
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class CommentPageRequest extends PageRequest {
/**
* 帖子ID(可选)
*/
@Pattern(regexp = "^[a-zA-Z0-9_\\-]{1,50}$", message = "帖子ID格式不正确")
private String postId;
/**
* 用户ID(可选)
*/
@Pattern(regexp = "^[a-zA-Z0-9_\\-]{1,50}$", message = "用户ID格式不正确")
private String userId;
}
@@ -0,0 +1,41 @@
package com.emotion.dto.request.comment;
import com.emotion.dto.request.BaseRequest;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 评论查询请求
*
* @author emotion-museum
* @date 2025-09-08
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class CommentQueryRequest extends BaseRequest {
/**
* 帖子ID
*/
private String postId;
/**
* 用户ID
*/
private String userId;
/**
* 回复的评论ID
*/
private String replyToId;
/**
* 最小点赞数
*/
private Integer minLikes;
/**
* 最大点赞数
*/
private Integer maxLikes;
}
@@ -0,0 +1,30 @@
package com.emotion.dto.request.comment;
import com.emotion.dto.request.BaseRequest;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.NotBlank;
/**
* 评论更新请求
*
* @author emotion-museum
* @date 2025-09-08
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class CommentUpdateRequest extends BaseRequest {
/**
* 评论ID
*/
@NotBlank(message = "评论ID不能为空")
private String id;
/**
* 评论内容
*/
@NotBlank(message = "评论内容不能为空")
private String content;
}
@@ -0,0 +1,56 @@
package com.emotion.dto.request.community;
import com.emotion.dto.request.BaseRequest;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.NotBlank;
/**
* 社区帖子创建请求
*
* @author emotion-museum
* @date 2025-09-08
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class CommunityPostCreateRequest extends BaseRequest {
/**
* 用户ID
*/
@NotBlank(message = "用户ID不能为空")
private String userId;
/**
* 标题
*/
@NotBlank(message = "标题不能为空")
private String title;
/**
* 内容
*/
@NotBlank(message = "内容不能为空")
private String content;
/**
* 帖子类型
*/
private String type;
/**
* 地点ID
*/
private String locationId;
/**
* 标签
*/
private String tags;
/**
* 是否私密
*/
private Integer isPrivate;
}
@@ -0,0 +1,41 @@
package com.emotion.dto.request.community;
import com.emotion.dto.request.PageRequest;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.Pattern;
/**
* 社区帖子分页请求
*
* @author emotion-museum
* @date 2025-09-08
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class CommunityPostPageRequest extends PageRequest {
/**
* 用户ID(可选)
*/
@Pattern(regexp = "^[a-zA-Z0-9_\\-]{1,50}$", message = "用户ID格式不正确")
private String userId;
/**
* 帖子类型(可选)
*/
@Pattern(regexp = "^[a-zA-Z0-9_\\u4e00-\\u9fa5]{1,20}$", message = "帖子类型格式不正确")
private String type;
/**
* 地点ID(可选)
*/
@Pattern(regexp = "^[a-zA-Z0-9_\\-]{1,50}$", message = "地点ID格式不正确")
private String locationId;
/**
* 是否只查询公开帖子
*/
private Boolean publicOnly;
}
@@ -0,0 +1,56 @@
package com.emotion.dto.request.community;
import com.emotion.dto.request.BaseRequest;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.NotBlank;
/**
* 社区帖子更新请求
*
* @author emotion-museum
* @date 2025-09-08
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class CommunityPostUpdateRequest extends BaseRequest {
/**
* 帖子ID
*/
@NotBlank(message = "帖子ID不能为空")
private String id;
/**
* 标题
*/
@NotBlank(message = "标题不能为空")
private String title;
/**
* 内容
*/
@NotBlank(message = "内容不能为空")
private String content;
/**
* 帖子类型
*/
private String type;
/**
* 地点ID
*/
private String locationId;
/**
* 标签
*/
private String tags;
/**
* 是否私密
*/
private Integer isPrivate;
}
@@ -0,0 +1,215 @@
package com.emotion.dto.request.coze;
import com.emotion.dto.request.BaseRequest;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.NotBlank;
import java.math.BigDecimal;
/**
* Coze API调用记录创建请求
*
* @author emotion-museum
* @date 2025-09-08
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class CozeApiCallCreateRequest extends BaseRequest {
/**
* 对话ID
*/
@NotBlank(message = "对话ID不能为空")
private String conversationId;
/**
* 消息ID
*/
private String messageId;
/**
* Coze聊天ID
*/
private String cozeChatId;
/**
* Coze对话ID
*/
private String cozeConversationId;
/**
* Bot ID
*/
private String botId;
/**
* Workflow ID
*/
private String workflowId;
/**
* 用户ID
*/
private String userId;
/**
* 请求类型: chat/stream/retrieve/messages
*/
private String requestType;
/**
* 请求URL
*/
private String requestUrl;
/**
* 请求体
*/
private String requestBody;
/**
* 请求头
*/
private String requestHeaders;
/**
* 用户输入的消息内容
*/
private String userMessage;
/**
* 用户消息类型: text/image/file
*/
private String userMessageType;
/**
* AI回复的消息内容
*/
private String aiReply;
/**
* AI回复类型: text/image/file
*/
private String aiReplyType;
/**
* HTTP状态码
*/
private Integer responseStatus;
/**
* 响应体
*/
private String responseBody;
/**
* 响应头
*/
private String responseHeaders;
/**
* 轮询次数
*/
private Integer pollCount;
/**
* 轮询开始时间
*/
private String pollStartTime;
/**
* 轮询结束时间
*/
private String pollEndTime;
/**
* 最终状态: completed/failed/timeout
*/
private String finalStatus;
/**
* 调用状态: pending/success/failed/timeout
*/
private String status;
/**
* 开始时间
*/
private String startTime;
/**
* 结束时间
*/
private String endTime;
/**
* 耗时(毫秒)
*/
private Integer durationMs;
/**
* 输入Token数
*/
private Integer promptTokens;
/**
* 输出Token数
*/
private Integer completionTokens;
/**
* 总Token数
*/
private Integer totalTokens;
/**
* 费用
*/
private BigDecimal cost;
/**
* 函数调用记录
*/
private String functionCalls;
/**
* 函数调用结果
*/
private String functionResults;
/**
* 错误代码
*/
private String errorCode;
/**
* 错误信息
*/
private String errorMessage;
/**
* 客户端IP
*/
private String clientIp;
/**
* 用户代理
*/
private String userAgent;
/**
* 会话ID
*/
private String sessionId;
/**
* 追踪ID
*/
private String traceId;
/**
* 扩展元数据
*/
private String metadata;
}
@@ -0,0 +1,54 @@
package com.emotion.dto.request.coze;
import com.emotion.dto.request.PageRequest;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.Pattern;
/**
* Coze API调用记录分页请求
*
* @author emotion-museum
* @date 2025-09-08
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class CozeApiCallPageRequest extends PageRequest {
/**
* 会话ID(可选)
*/
@Pattern(regexp = "^[a-zA-Z0-9_\\-]{1,50}$", message = "会话ID格式不正确")
private String conversationId;
/**
* 用户ID(可选)
*/
@Pattern(regexp = "^[a-zA-Z0-9_\\-]{1,50}$", message = "用户ID格式不正确")
private String userId;
/**
* Bot ID(可选)
*/
@Pattern(regexp = "^[a-zA-Z0-9_\\-]{1,50}$", message = "Bot ID格式不正确")
private String botId;
/**
* 状态(可选)
*/
@Pattern(regexp = "^[a-zA-Z0-9_\\u4e00-\\u9fa5]{1,20}$", message = "状态格式不正确")
private String status;
/**
* 请求类型(可选)
*/
@Pattern(regexp = "^[a-zA-Z0-9_\\-]{1,20}$", message = "请求类型格式不正确")
private String requestType;
/**
* 追踪ID(可选)
*/
@Pattern(regexp = "^[a-zA-Z0-9_\\-]{1,50}$", message = "追踪ID格式不正确")
private String traceId;
}
@@ -0,0 +1,220 @@
package com.emotion.dto.request.coze;
import com.emotion.dto.request.BaseRequest;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.NotBlank;
import java.math.BigDecimal;
/**
* Coze API调用记录更新请求
*
* @author emotion-museum
* @date 2025-09-08
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class CozeApiCallUpdateRequest extends BaseRequest {
/**
* ID
*/
@NotBlank(message = "ID不能为空")
private String id;
/**
* 对话ID
*/
private String conversationId;
/**
* 消息ID
*/
private String messageId;
/**
* Coze聊天ID
*/
private String cozeChatId;
/**
* Coze对话ID
*/
private String cozeConversationId;
/**
* Bot ID
*/
private String botId;
/**
* Workflow ID
*/
private String workflowId;
/**
* 用户ID
*/
private String userId;
/**
* 请求类型: chat/stream/retrieve/messages
*/
private String requestType;
/**
* 请求URL
*/
private String requestUrl;
/**
* 请求体
*/
private String requestBody;
/**
* 请求头
*/
private String requestHeaders;
/**
* 用户输入的消息内容
*/
private String userMessage;
/**
* 用户消息类型: text/image/file
*/
private String userMessageType;
/**
* AI回复的消息内容
*/
private String aiReply;
/**
* AI回复类型: text/image/file
*/
private String aiReplyType;
/**
* HTTP状态码
*/
private Integer responseStatus;
/**
* 响应体
*/
private String responseBody;
/**
* 响应头
*/
private String responseHeaders;
/**
* 轮询次数
*/
private Integer pollCount;
/**
* 轮询开始时间
*/
private String pollStartTime;
/**
* 轮询结束时间
*/
private String pollEndTime;
/**
* 最终状态: completed/failed/timeout
*/
private String finalStatus;
/**
* 调用状态: pending/success/failed/timeout
*/
private String status;
/**
* 开始时间
*/
private String startTime;
/**
* 结束时间
*/
private String endTime;
/**
* 耗时(毫秒)
*/
private Integer durationMs;
/**
* 输入Token数
*/
private Integer promptTokens;
/**
* 输出Token数
*/
private Integer completionTokens;
/**
* 总Token数
*/
private Integer totalTokens;
/**
* 费用
*/
private BigDecimal cost;
/**
* 函数调用记录
*/
private String functionCalls;
/**
* 函数调用结果
*/
private String functionResults;
/**
* 错误代码
*/
private String errorCode;
/**
* 错误信息
*/
private String errorMessage;
/**
* 客户端IP
*/
private String clientIp;
/**
* 用户代理
*/
private String userAgent;
/**
* 会话ID
*/
private String sessionId;
/**
* 追踪ID
*/
private String traceId;
/**
* 扩展元数据
*/
private String metadata;
}
@@ -0,0 +1,50 @@
package com.emotion.dto.response;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 情绪分析响应类
*
* @author emotion-museum
* @date 2025-09-08
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class EmotionAnalysisResponse extends BaseResponse {
/**
* 消息ID
*/
private String messageId;
/**
* 用户ID
*/
private String userId;
/**
* 主要情绪
*/
private String primaryEmotion;
/**
* 情绪极性
*/
private String polarity;
/**
* 情绪强度
*/
private Double intensity;
/**
* 置信度
*/
private Double confidence;
/**
* 情绪详情
*/
private String emotionDetails;
}
@@ -0,0 +1,80 @@
package com.emotion.dto.response;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
/**
* 情绪记录响应类
*
* @author emotion-museum
* @date 2025-09-08
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class EmotionRecordResponse extends BaseResponse {
/**
* 用户ID
*/
private String userId;
/**
* 记录日期
*/
private LocalDate recordDate;
/**
* 情绪类型
*/
private String emotionType;
/**
* 情绪强度
*/
private BigDecimal intensity;
/**
* 触发因素
*/
private String triggers;
/**
* 描述
*/
private String description;
/**
* 标签
*/
private List<String> tags;
/**
* 天气
*/
private String weather;
/**
* 地点
*/
private String location;
/**
* 活动
*/
private String activity;
/**
* 相关人物
*/
private String people;
/**
* 备注
*/
private String notes;
}
@@ -0,0 +1,62 @@
package com.emotion.dto.response;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDateTime;
/**
* WebSocket响应对象
*
* @author emotion-museum
* @date 2025-09-08
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class WebSocketResponse extends BaseResponse {
/**
* 消息ID
*/
private String messageId;
/**
* 会话ID
*/
private String conversationId;
/**
* 消息类型
*/
private String type;
/**
* 消息内容
*/
private String content;
/**
* 发送者ID
*/
private String senderId;
/**
* 发送者类型
*/
private String senderType;
/**
* 消息状态
*/
private String status;
/**
* 创建时间
*/
private LocalDateTime createTime;
/**
* 扩展数据
*/
private Object data;
}
@@ -0,0 +1,73 @@
package com.emotion.dto.response.achievement;
import com.emotion.dto.response.BaseResponse;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.math.BigDecimal;
/**
* 成就响应类
*
* @author emotion-museum
* @date 2025-09-08
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class AchievementResponse extends BaseResponse {
/**
* 成就标题
*/
private String title;
/**
* 描述
*/
private String description;
/**
* 分类
*/
private String category;
/**
* 图标
*/
private String icon;
/**
* 稀有度
*/
private String rarity;
/**
* 条件类型
*/
private String conditionType;
/**
* 条件值
*/
private String conditionValue;
/**
* 奖励
*/
private String rewards;
/**
* 解锁时间
*/
private String unlockedTime;
/**
* 进度
*/
private Double progress;
/**
* 是否隐藏
*/
private Integer isHidden;
}
@@ -0,0 +1,41 @@
package com.emotion.dto.response.comment;
import com.emotion.dto.response.BaseResponse;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 评论响应
*
* @author emotion-museum
* @date 2025-09-08
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class CommentResponse extends BaseResponse {
/**
* 帖子ID
*/
private String postId;
/**
* 用户ID
*/
private String userId;
/**
* 评论内容
*/
private String content;
/**
* 回复的评论ID
*/
private String replyToId;
/**
* 点赞数
*/
private Integer likes;
}
@@ -0,0 +1,71 @@
package com.emotion.dto.response.community;
import com.emotion.dto.response.BaseResponse;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 社区帖子响应
*
* @author emotion-museum
* @date 2025-09-08
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class CommunityPostResponse extends BaseResponse {
/**
* 用户ID
*/
private String userId;
/**
* 地点ID
*/
private String locationId;
/**
* 标题
*/
private String title;
/**
* 内容
*/
private String content;
/**
* 帖子类型
*/
private String type;
/**
* 图片列表
*/
private String images;
/**
* 标签
*/
private String tags;
/**
* 点赞数
*/
private Integer likes;
/**
* 浏览数
*/
private Integer viewCount;
/**
* 评论数
*/
private Integer commentCount;
/**
* 是否私密
*/
private Integer isPrivate;
}
@@ -0,0 +1,212 @@
package com.emotion.dto.response.coze;
import com.emotion.dto.response.BaseResponse;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.math.BigDecimal;
/**
* Coze API调用记录响应
*
* @author emotion-museum
* @date 2025-09-08
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class CozeApiCallResponse extends BaseResponse {
/**
* 对话ID
*/
private String conversationId;
/**
* 消息ID
*/
private String messageId;
/**
* Coze聊天ID
*/
private String cozeChatId;
/**
* Coze对话ID
*/
private String cozeConversationId;
/**
* Bot ID
*/
private String botId;
/**
* Workflow ID
*/
private String workflowId;
/**
* 用户ID
*/
private String userId;
/**
* 请求类型: chat/stream/retrieve/messages
*/
private String requestType;
/**
* 请求URL
*/
private String requestUrl;
/**
* 请求体
*/
private String requestBody;
/**
* 请求头
*/
private String requestHeaders;
/**
* 用户输入的消息内容
*/
private String userMessage;
/**
* 用户消息类型: text/image/file
*/
private String userMessageType;
/**
* AI回复的消息内容
*/
private String aiReply;
/**
* AI回复类型: text/image/file
*/
private String aiReplyType;
/**
* HTTP状态码
*/
private Integer responseStatus;
/**
* 响应体
*/
private String responseBody;
/**
* 响应头
*/
private String responseHeaders;
/**
* 轮询次数
*/
private Integer pollCount;
/**
* 轮询开始时间
*/
private String pollStartTime;
/**
* 轮询结束时间
*/
private String pollEndTime;
/**
* 最终状态: completed/failed/timeout
*/
private String finalStatus;
/**
* 调用状态: pending/success/failed/timeout
*/
private String status;
/**
* 开始时间
*/
private String startTime;
/**
* 结束时间
*/
private String endTime;
/**
* 耗时(毫秒)
*/
private Integer durationMs;
/**
* 输入Token数
*/
private Integer promptTokens;
/**
* 输出Token数
*/
private Integer completionTokens;
/**
* 总Token数
*/
private Integer totalTokens;
/**
* 费用
*/
private BigDecimal cost;
/**
* 函数调用记录
*/
private String functionCalls;
/**
* 函数调用结果
*/
private String functionResults;
/**
* 错误代码
*/
private String errorCode;
/**
* 错误信息
*/
private String errorMessage;
/**
* 客户端IP
*/
private String clientIp;
/**
* 用户代理
*/
private String userAgent;
/**
* 会话ID
*/
private String sessionId;
/**
* 追踪ID
*/
private String traceId;
/**
* 扩展元数据
*/
private String metadata;
}
@@ -35,14 +35,14 @@ public class ChatRequest {
/**
* 发送者类型
*/
@NotNull(message = "发送者类型不能为空")
private SenderType senderType;
@NotBlank(message = "发送者类型不能为空")
private String senderType;
/**
* 消息类型
*/
@NotNull(message = "消息类型不能为空")
private MessageType messageType;
@NotBlank(message = "消息类型不能为空")
private String messageType;
/**
* 会话ID(可选)
@@ -53,45 +53,4 @@ public class ChatRequest {
* 发送时间戳
*/
private Long timestamp;
/**
* 发送者类型枚举
*/
public enum SenderType {
USER("用户"),
GUEST("访客"),
AI("AI助手"),
SYSTEM("系统");
private final String description;
SenderType(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
}
/**
* 消息类型枚举
*/
public enum MessageType {
TEXT("文本消息"),
IMAGE("图片消息"),
FILE("文件消息"),
SYSTEM("系统消息"),
HEARTBEAT("心跳消息");
private final String description;
MessageType(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
}
}
@@ -32,7 +32,7 @@ public class WebSocketMessage {
/**
* 消息类型
*/
private MessageType type;
private String type;
/**
* 消息内容
@@ -47,12 +47,12 @@ public class WebSocketMessage {
/**
* 发送者类型
*/
private SenderType senderType;
private String senderType;
/**
* 消息状态
*/
private MessageStatus status;
private String status;
/**
* 创建时间
@@ -63,68 +63,4 @@ public class WebSocketMessage {
* 扩展数据
*/
private Object data;
/**
* 消息类型枚举
*/
public enum MessageType {
TEXT("文本消息"),
TYPING("正在输入"),
SYSTEM("系统消息"),
ERROR("错误消息"),
HEARTBEAT("心跳消息"),
CONNECTION("连接消息"),
AI_THINKING("AI思考中");
private final String description;
MessageType(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
}
/**
* 发送者类型枚举
*/
public enum SenderType {
USER("用户"),
GUEST("访客"),
AI("AI助手"),
SYSTEM("系统");
private final String description;
SenderType(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
}
/**
* 消息状态枚举
*/
public enum MessageStatus {
SENDING("发送中"),
SENT("已发送"),
DELIVERED("已送达"),
READ("已读"),
FAILED("发送失败");
private final String description;
MessageStatus(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
}
}
@@ -2,7 +2,9 @@ package com.emotion.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
import com.emotion.common.BasePageRequest;
import com.emotion.common.PageResult;
import com.emotion.dto.request.achievement.*;
import com.emotion.dto.response.achievement.AchievementResponse;
import com.emotion.entity.Achievement;
import java.time.LocalDateTime;
@@ -12,14 +14,14 @@ import java.util.List;
* 成就服务接口
*
* @author emotion-museum
* @date 2025-07-23
* @date 2025-09-08
*/
public interface AchievementService extends IService<Achievement> {
/**
* 分页查询成就
*/
IPage<Achievement> getPage(BasePageRequest request);
IPage<Achievement> getPage(AchievementPageRequest request);
/**
* 根据分类查询成就
@@ -87,32 +89,32 @@ public interface AchievementService extends IService<Achievement> {
Long countByRarity(String rarity);
/**
* 查询平均进度
* 获取平均进度
*/
Double getAvgProgress();
/**
* 查询指定分类的平均进度
* 根据分类获取平均进度
*/
Double getAvgProgressByCategory(String category);
/**
* 查询最近解锁的成就
* 获取最近解锁的成就
*/
List<Achievement> getRecentlyUnlocked(Integer limit);
/**
* 查询即将完成的成就(进度>80%
* 获取接近完成的成就
*/
List<Achievement> getNearCompletion();
/**
* 查询稀有成就(稀有度为legendary或epic
* 获取稀有成就
*/
List<Achievement> getRareAchievements();
/**
* 更新成就解锁状态
* 解锁成就
*/
boolean unlockAchievement(String id, LocalDateTime unlockedTime);
@@ -122,12 +124,59 @@ public interface AchievementService extends IService<Achievement> {
boolean updateProgress(String id, Double progress);
/**
* 更新成就隐藏状态
* 更新隐藏状态
*/
boolean updateHiddenStatus(String id, Integer isHidden);
/**
* 查询推荐成就(基于分类和稀有度)
* 获取推荐成就
*/
List<Achievement> getRecommendedAchievements(String category, String rarity, Integer limit);
}
// 新增的Response相关方法
/**
* 分页查询成就响应
*/
PageResult<AchievementResponse> getPageWithResponse(AchievementPageRequest request);
/**
* 根据ID获取成就响应
*/
AchievementResponse getAchievementResponseById(String id);
/**
* 创建成就并返回响应
*/
AchievementResponse createAchievementWithResponse(AchievementCreateRequest request);
/**
* 更新成就并返回响应
*/
AchievementResponse updateAchievementWithResponse(AchievementUpdateRequest request);
/**
* 根据分类查询成就响应
*/
List<AchievementResponse> getByCategoryWithResponse(String category);
/**
* 根据稀有度查询成就响应
*/
List<AchievementResponse> getByRarityWithResponse(String rarity);
/**
* 查询已解锁的成就响应
*/
List<AchievementResponse> getUnlockedAchievementsWithResponse();
/**
* 查询未解锁的成就响应
*/
List<AchievementResponse> getLockedAchievementsWithResponse();
/**
* 查询最近解锁的成就响应
*/
List<AchievementResponse> getRecentlyUnlockedWithResponse(Integer limit);
}
@@ -1,5 +1,7 @@
package com.emotion.service;
import com.emotion.dto.request.*;
import com.emotion.dto.response.*;
/**
* AI聊天服务接口
@@ -10,85 +12,93 @@ package com.emotion.service;
public interface AiChatService {
/**
* 发送聊天消息(保存用户消息和AI回复)
* @param conversationId 会话ID
* @param message 用户消息内容
* @param userId 用户ID
* @return AI回复内容
* 发送聊天消息
*
* @param request AI聊天请求
* @return AI聊天响应
*/
String sendChatMessage(String conversationId, String message, String userId);
AiChatResponse sendChatMessage(AiChatRequest request);
/**
* 生成对话总结
*
* @param request AI总结请求
* @return AI总结响应
*/
AiSummaryResponse generateConversationSummary(AiSummaryRequest request);
/**
* 获取AI服务状态
*
* @return AI状态响应
*/
AiStatusResponse getServiceStatus();
/**
* 获取聊天统计
*
* @param request 聊天统计请求
* @return 聊天统计响应
*/
ChatStatsResponse getChatStats(ChatStatsRequest request);
/**
* 访客聊天
*
* @param request 访客聊天请求
* @param clientIp 客户端IP
* @return 访客聊天响应
*/
GuestChatResponse guestChat(GuestChatRequest request, String clientIp);
/**
* 获取访客用户信息
*
* @param clientIp 客户端IP
* @return 访客用户信息响应
*/
GuestUserInfoResponse getGuestUserInfo(String clientIp);
/**
* 创建对话
*
* @param request 对话创建请求
* @param clientIp 客户端IP
* @return 对话响应
*/
ConversationResponse createConversation(ConversationCreateRequest request, String clientIp);
/**
* WebSocket方式发送聊天消息(只保存AI回复)
*
* @param conversationId 会话ID
* @param message 用户消息内容
* @param userId 用户ID
* @param message 用户消息内容
* @param userId 用户ID
* @return AI回复内容
*/
String sendChatMessageForWebSocket(String conversationId, String message, String userId);
/**
* WebSocket方式发送聊天消息(只保存AI回复,带messageId
*
* @param conversationId 会话ID
* @param messageId 用户消息ID
* @param message 用户消息内容
* @param userId 用户ID
* @param messageId 用户消息ID
* @param message 用户消息内容
* @param userId 用户ID
* @return AI回复内容
*/
String sendChatMessageForWebSocket(String conversationId, String messageId, String message, String userId);
/**
* 生成对话总结
* @param conversationId 会话ID
* @param userId 用户ID
* @return 总结内容
*/
String generateConversationSummary(String conversationId, String userId);
/**
* 检查AI服务是否可用
* @return 可用返回true,否则false
*/
boolean isServiceAvailable();
/**
* 获取AI服务状态
* @return "available" 或 "unavailable"
*/
String getServiceStatus();
/**
* 发送消息到Coze AI(不保存消息,仅AI交互)
*
* @param conversationId 会话ID
* @param userMessage 用户消息内容
* @param userId 用户ID
* @param userMessage 用户消息内容
* @param userId 用户ID
* @return AI回复内容
*/
String sendMessage(String conversationId, String userMessage, String userId);
/**
* 访客聊天(不登录情况下)
* @param message 用户消息内容
* @param clientIp 客户端IP
* @return 包含AI回复等信息的Map
*/
java.util.Map<String, Object> guestChat(String message, String clientIp);
/**
* 创建新对话
* @param userId 用户ID
* @param title 对话标题
* @return 包含对话信息的Map
*/
java.util.Map<String, Object> createConversation(String userId, String title);
/**
* 获取访客用户信息
* @param clientIp 客户端IP
* @return 包含访客信息的Map
*/
java.util.Map<String, Object> getGuestUserInfo(String clientIp);
/**
* 流式聊天(暂时降级为普通聊天)
* @param conversationId 会话ID
@@ -126,4 +136,4 @@ public interface AiChatService {
* @return AI评论内容
*/
String sendSummaryMessage(String conversationId, String userMessage, String userId);
}
}
@@ -6,6 +6,8 @@ import com.emotion.dto.response.AuthResponse;
import com.emotion.dto.response.CaptchaResponse;
import com.emotion.dto.response.UserInfoResponse;
import javax.servlet.http.HttpServletRequest;
/**
* 认证服务接口
*
@@ -64,12 +66,12 @@ public interface AuthService {
boolean logout(String userId, String token);
/**
* 用户登出(通过令牌
* 用户登出(通过请求
*
* @param token 访问令牌
* @param request HTTP请求
* @return 是否登出成功
*/
boolean logoutByToken(String token);
boolean logoutByToken(HttpServletRequest request);
/**
* 刷新访问令牌
@@ -79,6 +81,14 @@ public interface AuthService {
*/
AuthResponse refreshToken(String refreshToken);
/**
* 验证访问令牌
*
* @param request HTTP请求
* @return 是否有效
*/
boolean validateToken(HttpServletRequest request);
/**
* 验证访问令牌
*
@@ -126,4 +136,4 @@ public interface AuthService {
* @return 是否存在
*/
boolean existsByPhone(String phone);
}
}
@@ -1,13 +1,13 @@
package com.emotion.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
import com.emotion.common.BasePageRequest;
import com.emotion.common.PageResult;
import com.emotion.dto.request.comment.CommentCreateRequest;
import com.emotion.dto.request.comment.CommentUpdateRequest;
import com.emotion.dto.request.comment.CommentPageRequest;
import com.emotion.dto.response.comment.CommentResponse;
import com.emotion.entity.Comment;
import java.time.LocalDateTime;
import java.util.List;
/**
* 评论服务接口
*
@@ -19,110 +19,25 @@ public interface CommentService extends IService<Comment> {
/**
* 分页查询评论
*/
IPage<Comment> getPage(BasePageRequest request);
PageResult<CommentResponse> getPage(CommentPageRequest request);
/**
* 根据帖子ID分页查询评论
* 根据ID获取评论响应
*/
IPage<Comment> getPageByPostId(BasePageRequest request, String postId);
/**
* 根据用户ID分页查询评论
*/
IPage<Comment> getPageByUserId(BasePageRequest request, String userId);
/**
* 根据帖子ID查询所有评论
*/
List<Comment> getByPostId(String postId);
/**
* 根据用户ID查询所有评论
*/
List<Comment> getByUserId(String userId);
/**
* 根据回复的评论ID查询回复
*/
List<Comment> getRepliesByCommentId(String replyToId);
/**
* 查询顶级评论(非回复的评论)
*/
List<Comment> getTopLevelCommentsByPostId(String postId);
/**
* 根据点赞数范围查询评论
*/
List<Comment> getByLikesRange(Integer minLikes, Integer maxLikes);
/**
* 根据时间范围查询评论
*/
List<Comment> getByTimeRange(LocalDateTime startTime, LocalDateTime endTime);
/**
* 统计帖子的评论数量
*/
Long countByPostId(String postId);
/**
* 统计用户的评论数量
*/
Long countByUserId(String userId);
/**
* 统计评论的回复数量
*/
Long countRepliesByCommentId(String commentId);
/**
* 统计帖子的顶级评论数量
*/
Long countTopLevelCommentsByPostId(String postId);
/**
* 查询最受欢迎的评论(按点赞数排序)
*/
List<Comment> getMostLikedCommentsByPostId(String postId, Integer limit);
/**
* 查询最新的评论
*/
List<Comment> getLatestCommentsByPostId(String postId, Integer limit);
/**
* 查询用户最近的评论
*/
List<Comment> getRecentByUserId(String userId, Integer limit);
/**
* 查询热门评论(按点赞数和回复数综合排序)
*/
List<Comment> getPopularCommentsByPostId(String postId, Integer limit);
/**
* 根据关键词搜索评论内容
*/
List<Comment> searchByKeyword(String keyword);
/**
* 根据帖子ID和关键词搜索评论
*/
List<Comment> searchByPostIdAndKeyword(String postId, String keyword);
/**
* 查询用户在指定帖子下的评论
*/
List<Comment> getByPostIdAndUserId(String postId, String userId);
/**
* 更新评论点赞数
*/
boolean updateLikes(String id, Integer increment);
CommentResponse getById(String id);
/**
* 创建评论
*/
Comment createComment(String postId, String userId, String content, String replyToId);
}
CommentResponse create(CommentCreateRequest request);
/**
* 更新评论
*/
CommentResponse update(CommentUpdateRequest request);
/**
* 删除评论
*/
boolean delete(String id);
}
@@ -1,13 +1,13 @@
package com.emotion.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
import com.emotion.common.BasePageRequest;
import com.emotion.common.PageResult;
import com.emotion.dto.request.community.CommunityPostCreateRequest;
import com.emotion.dto.request.community.CommunityPostUpdateRequest;
import com.emotion.dto.request.community.CommunityPostPageRequest;
import com.emotion.dto.response.community.CommunityPostResponse;
import com.emotion.entity.CommunityPost;
import java.time.LocalDateTime;
import java.util.List;
/**
* 社区帖子服务接口
*
@@ -19,141 +19,25 @@ public interface CommunityPostService extends IService<CommunityPost> {
/**
* 分页查询帖子
*/
IPage<CommunityPost> getPage(BasePageRequest request);
PageResult<CommunityPostResponse> getPage(CommunityPostPageRequest request);
/**
* 分页查询公开帖子
* 根据ID获取帖子响应
*/
IPage<CommunityPost> getPublicPostsPage(BasePageRequest request);
/**
* 根据用户ID分页查询帖子
*/
IPage<CommunityPost> getPageByUserId(BasePageRequest request, String userId);
/**
* 根据地点ID查询帖子
*/
List<CommunityPost> getByLocationId(String locationId);
/**
* 根据帖子类型查询帖子
*/
List<CommunityPost> getByType(String type);
/**
* 查询用户的私密帖子
*/
List<CommunityPost> getPrivatePostsByUserId(String userId);
/**
* 根据点赞数范围查询帖子
*/
List<CommunityPost> getByLikesRange(Integer minLikes, Integer maxLikes);
/**
* 根据浏览数范围查询帖子
*/
List<CommunityPost> getByViewRange(Integer minViews, Integer maxViews);
/**
* 根据评论数范围查询帖子
*/
List<CommunityPost> getByCommentRange(Integer minComments, Integer maxComments);
/**
* 根据时间范围查询帖子
*/
List<CommunityPost> getByTimeRange(LocalDateTime startTime, LocalDateTime endTime);
/**
* 统计用户的帖子数量
*/
Long countByUserId(String userId);
/**
* 统计用户的公开帖子数量
*/
Long countPublicPostsByUserId(String userId);
/**
* 统计用户的私密帖子数量
*/
Long countPrivatePostsByUserId(String userId);
/**
* 统计指定类型的帖子数量
*/
Long countByType(String type);
/**
* 统计地点的帖子数量
*/
Long countByLocationId(String locationId);
/**
* 查询最受欢迎的帖子(按点赞数排序)
*/
List<CommunityPost> getMostLikedPosts(Integer limit);
/**
* 查询最热门的帖子(按浏览数排序)
*/
List<CommunityPost> getMostViewedPosts(Integer limit);
/**
* 查询最新的帖子
*/
List<CommunityPost> getLatestPosts(Integer limit);
/**
* 查询热门帖子(综合点赞、浏览、评论)
*/
List<CommunityPost> getPopularPosts(Integer limit);
/**
* 根据标签搜索帖子
*/
List<CommunityPost> getByTag(String tag);
/**
* 根据关键词搜索帖子
*/
List<CommunityPost> searchByKeyword(String keyword);
/**
* 查询用户最近的帖子
*/
List<CommunityPost> getRecentByUserId(String userId, Integer limit);
/**
* 更新帖子点赞数
*/
boolean updateLikes(String id, Integer increment);
/**
* 更新帖子浏览数
*/
boolean incrementViewCount(String id);
/**
* 更新帖子评论数
*/
boolean updateCommentCount(String id, Integer increment);
/**
* 更新帖子隐私状态
*/
boolean updatePrivacyStatus(String id, Integer isPrivate);
/**
* 查询推荐帖子(基于类型和地点)
*/
List<CommunityPost> getRecommendedPosts(String type, String locationId, Integer limit);
CommunityPostResponse getById(String id);
/**
* 创建帖子
*/
CommunityPost createPost(String userId, String title, String content, String type,
String locationId, String tags, Integer isPrivate);
}
CommunityPostResponse create(CommunityPostCreateRequest request);
/**
* 更新帖子
*/
CommunityPostResponse update(CommunityPostUpdateRequest request);
/**
* 删除帖子
*/
boolean delete(String id);
}
@@ -2,9 +2,13 @@ package com.emotion.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
import com.emotion.common.BasePageRequest;
import com.emotion.common.PageResult;
import com.emotion.dto.request.ConversationCreateRequest;
import com.emotion.dto.request.ConversationPageRequest;
import com.emotion.dto.response.ConversationResponse;
import com.emotion.entity.Conversation;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.List;
@@ -19,12 +23,12 @@ public interface ConversationService extends IService<Conversation> {
/**
* 分页查询会话
*/
IPage<Conversation> getPage(BasePageRequest request);
IPage<Conversation> getPage(ConversationPageRequest request);
/**
* 根据用户ID分页查询会话
*/
IPage<Conversation> getPageByUserId(BasePageRequest request, String userId);
IPage<Conversation> getPageByUserId(ConversationPageRequest request);
/**
* 根据用户ID查询会话列表
@@ -85,4 +89,60 @@ public interface ConversationService extends IService<Conversation> {
* 结束会话
*/
boolean endConversation(String conversationId);
/**
* 分页查询会话响应
*/
PageResult<ConversationResponse> getPageWithResponse(ConversationPageRequest request);
/**
* 根据用户ID分页查询会话响应
*/
PageResult<ConversationResponse> getPageByUserIdWithResponse(ConversationPageRequest request);
/**
* 根据ID获取会话响应
*/
ConversationResponse getConversationResponseById(String id);
/**
* 根据用户ID查询会话响应列表
*/
List<ConversationResponse> getByUserIdWithResponse(String userId);
/**
* 获取活跃会话响应列表
*/
List<ConversationResponse> getActiveConversationsWithResponse();
/**
* 获取归档会话响应列表
*/
List<ConversationResponse> getArchivedConversationsWithResponse();
/**
* 归档对话
*/
boolean archiveConversation(String id);
/**
* 激活对话
*/
boolean activateConversation(String id);
/**
* 创建会话并返回响应对象
*/
ConversationResponse createConversationWithResponse(ConversationCreateRequest request,
HttpServletRequest httpRequest);
/**
* 更新会话并返回响应对象
*/
ConversationResponse updateConversationWithResponse(ConversationCreateRequest request);
/**
* 更新会话状态
*/
boolean updateConversationStatus(String id, String status);
}
@@ -1,8 +1,11 @@
package com.emotion.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
import com.emotion.common.BasePageRequest;
import com.emotion.common.PageResult;
import com.emotion.dto.request.coze.CozeApiCallPageRequest;
import com.emotion.dto.request.coze.CozeApiCallCreateRequest;
import com.emotion.dto.request.coze.CozeApiCallUpdateRequest;
import com.emotion.dto.response.coze.CozeApiCallResponse;
import com.emotion.entity.CozeApiCall;
import java.math.BigDecimal;
@@ -20,37 +23,27 @@ public interface CozeApiCallService extends IService<CozeApiCall> {
/**
* 分页查询API调用记录
*/
IPage<CozeApiCall> getPage(BasePageRequest request);
PageResult<CozeApiCallResponse> getPage(CozeApiCallPageRequest request);
/**
* 根据会话ID分页查询API调用记录
* 根据ID获取API调用记录
*/
IPage<CozeApiCall> getPageByConversationId(BasePageRequest request, String conversationId);
CozeApiCallResponse getById(String id);
/**
* 根据用户ID分页查询API调用记录
* 创建API调用记录
*/
IPage<CozeApiCall> getPageByUserId(BasePageRequest request, String userId);
CozeApiCallResponse create(CozeApiCallCreateRequest request);
/**
* 根据Bot ID查询API调用记录
* 更新API调用记录
*/
List<CozeApiCall> getByBotId(String botId);
CozeApiCallResponse update(CozeApiCallUpdateRequest request);
/**
* 根据状态查询API调用记录
* 删除API调用记录
*/
List<CozeApiCall> getByStatus(String status);
/**
* 根据请求类型查询API调用记录
*/
List<CozeApiCall> getByRequestType(String requestType);
/**
* 根据时间范围查询API调用记录
*/
List<CozeApiCall> getByTimeRange(LocalDateTime startTime, LocalDateTime endTime);
boolean delete(String id);
/**
* 统计用户的API调用次数
@@ -76,42 +69,4 @@ public interface CozeApiCallService extends IService<CozeApiCall> {
* 统计用户的API调用费用
*/
BigDecimal sumCostByUserId(String userId);
/**
* 查询失败的API调用记录
*/
List<CozeApiCall> getFailedCalls();
/**
* 查询超时的API调用记录
*/
List<CozeApiCall> getTimeoutCalls();
/**
* 根据追踪ID查询API调用记录
*/
CozeApiCall getByTraceId(String traceId);
/**
* 根据会话ID和请求类型查询API调用记录
*/
List<CozeApiCall> getByConversationIdAndRequestType(String conversationId, String requestType);
/**
* 更新API调用状态
*/
boolean updateStatus(String id, String status, String finalStatus, LocalDateTime endTime);
/**
* 更新API调用结果
*/
boolean updateResult(String id, Integer responseStatus, String responseBody, String aiReply,
Integer totalTokens, BigDecimal cost, String status, String finalStatus,
LocalDateTime endTime);
/**
* 创建API调用记录
*/
CozeApiCall createApiCall(String conversationId, String messageId, String userId,
String requestType, String requestUrl, String requestBody);
}
}
@@ -2,10 +2,12 @@ package com.emotion.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
import com.emotion.common.BasePageRequest;
import com.emotion.common.PageResult;
import com.emotion.dto.request.DiaryCommentCreateRequest;
import com.emotion.dto.request.DiaryCommentPageRequest;
import com.emotion.dto.response.DiaryCommentResponse;
import com.emotion.entity.DiaryComment;
import java.math.BigDecimal;
import java.util.List;
/**
@@ -19,100 +21,28 @@ public interface DiaryCommentService extends IService<DiaryComment> {
/**
* 分页查询评论
*/
IPage<DiaryComment> getPage(BasePageRequest request);
PageResult<DiaryCommentResponse> getPageWithResponse(DiaryCommentPageRequest request);
/**
* 根据日记ID分页查询评论
* 根据ID获取评论响应
*/
IPage<DiaryComment> getPageByDiaryId(String diaryId, BasePageRequest request);
DiaryCommentResponse getCommentResponseById(String id);
/**
* 根据用户ID分页查询评论
* 获取评论树结构响应
*/
IPage<DiaryComment> getPageByUserId(String userId, BasePageRequest request);
List<DiaryCommentResponse> getCommentTreeWithResponse(String diaryId);
/**
* 根据父评论ID查询回复列表
* 创建评论并返回响应对象
*/
List<DiaryComment> getRepliesByParentId(String parentCommentId);
DiaryCommentResponse createCommentWithResponse(DiaryCommentCreateRequest request);
/**
* 根据评论类型查询评论列表
* 更新评论并返回响应对象
*/
List<DiaryComment> getByCommentType(String commentType);
/**
* 根据日记ID和评论类型查询评论列表
*/
List<DiaryComment> getByDiaryIdAndCommentType(String diaryId, String commentType);
/**
* 增加点赞数
*/
boolean incrementLikeCount(String commentId);
/**
* 减少点赞数
*/
boolean decrementLikeCount(String commentId);
/**
* 增加回复数
*/
boolean incrementReplyCount(String commentId);
/**
* 减少回复数
*/
boolean decrementReplyCount(String commentId);
/**
* 更新最后回复时间
*/
boolean updateLastReplyTime(String commentId);
/**
* 设置置顶状态
*/
boolean setTop(String commentId, Integer isTop);
/**
* 统计日记评论数量
*/
Long countByDiaryId(String diaryId);
/**
* 统计用户评论数量
*/
Long countByUserId(String userId);
/**
* 统计指定类型的评论数量
*/
Long countByCommentType(String commentType);
/**
* 统计回复数量
*/
Long countReplies(String parentCommentId);
/**
* 创建评论
*/
DiaryComment createComment(String diaryId, String userId, String content, List<String> images,
String parentCommentId, Integer isAnonymous);
/**
* 创建AI评论
*/
DiaryComment createAiComment(String diaryId, String content, String aiCommentSource,
BigDecimal emotionScore, BigDecimal sentimentScore);
/**
* 更新评论
*/
boolean updateComment(String commentId, String content, List<String> images);
DiaryCommentResponse updateCommentWithResponse(DiaryCommentCreateRequest request);
/**
* 删除评论
*/
@@ -129,7 +59,17 @@ public interface DiaryCommentService extends IService<DiaryComment> {
boolean restoreComment(String commentId);
/**
* 获取评论树结构
* 增加点赞数
*/
List<DiaryComment> getCommentTree(String diaryId);
}
boolean incrementLikeCount(String commentId);
/**
* 减少点赞数
*/
boolean decrementLikeCount(String commentId);
/**
* 设置置顶状态
*/
boolean setTop(String commentId, Integer isTop);
}
@@ -1,8 +1,11 @@
package com.emotion.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
import com.emotion.common.BasePageRequest;
import com.emotion.common.PageResult;
import com.emotion.dto.request.DiaryPostCreateRequest;
import com.emotion.dto.request.DiaryPostPageRequest;
import com.emotion.dto.request.DiaryPostUpdateRequest;
import com.emotion.dto.response.DiaryPostResponse;
import com.emotion.entity.DiaryPost;
import java.math.BigDecimal;
@@ -19,47 +22,37 @@ public interface DiaryPostService extends IService<DiaryPost> {
/**
* 分页查询日记
*/
IPage<DiaryPost> getPage(BasePageRequest request);
PageResult<DiaryPostResponse> getPageWithResponse(DiaryPostPageRequest request);
/**
* 根据ID获取日记响应
*/
DiaryPostResponse getDiaryPostResponseById(String id);
/**
* 创建日记并返回响应对象
*/
DiaryPostResponse createDiaryPostWithResponse(DiaryPostCreateRequest request);
/**
* 更新日记并返回响应对象
*/
DiaryPostResponse updateDiaryPostWithResponse(DiaryPostUpdateRequest request);
/**
* 删除日记
*/
boolean deleteDiaryPost(String diaryId);
/**
* 根据用户ID分页查询日记
* 软删除日记
*/
IPage<DiaryPost> getPageByUserId(String userId, BasePageRequest request);
boolean softDeleteDiaryPost(String diaryId);
/**
* 根据用户ID查询公开日记
* 恢复日记
*/
IPage<DiaryPost> getPublicPageByUserId(String userId, BasePageRequest request);
/**
* 查询精选日记
*/
IPage<DiaryPost> getFeaturedPage(BasePageRequest request);
/**
* 根据状态查询日记列表
*/
List<DiaryPost> getByStatus(String status);
/**
* 根据用户ID和状态查询日记列表
*/
List<DiaryPost> getByUserIdAndStatus(String userId, String status);
/**
* 根据心情状态查询日记列表
*/
List<DiaryPost> getByMood(String mood);
/**
* 根据标签查询日记列表
*/
List<DiaryPost> getByTags(List<String> tags);
/**
* 根据地点查询日记列表
*/
List<DiaryPost> getByLocation(String location);
boolean restoreDiaryPost(String diaryId);
/**
* 增加浏览数
@@ -120,47 +113,9 @@ public interface DiaryPostService extends IService<DiaryPost> {
* 统计精选日记数量
*/
Long countFeatured();
/**
* 统计指定状态的日记数量
*/
Long countByStatus(String status);
/**
* 创建日记
*/
DiaryPost createDiaryPost(com.emotion.dto.request.DiaryPostCreateRequest request);
/**
* 更新日记
*/
boolean updateDiaryPost(String diaryId, com.emotion.dto.request.DiaryPostUpdateRequest request);
/**
* 删除日记
*/
boolean deleteDiaryPost(String diaryId);
/**
* 软删除日记
*/
boolean softDeleteDiaryPost(String diaryId);
/**
* 恢复日记
*/
boolean restoreDiaryPost(String diaryId);
/**
* 添加AI评论
*/
boolean addAiComment(String diaryId, String aiComment, Object aiEmotionAnalysis,
BigDecimal aiSentimentScore, List<String> aiKeywords, String aiSuggestions);
/**
* 发表日记并生成AI评论
* @param request 日记创建请求
* @return 日记响应对象,包含AI评论
*/
com.emotion.dto.response.DiaryPostResponse publishDiaryWithAiComment(com.emotion.dto.request.DiaryPostCreateRequest request);
}
DiaryPostResponse publishDiaryWithAiComment(DiaryPostCreateRequest request);
}
@@ -3,6 +3,11 @@ package com.emotion.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
import com.emotion.common.BasePageRequest;
import com.emotion.common.PageResult;
import com.emotion.dto.request.EmotionAnalysisCreateRequest;
import com.emotion.dto.request.EmotionAnalysisPageRequest;
import com.emotion.dto.request.EmotionAnalysisUpdateRequest;
import com.emotion.dto.response.EmotionAnalysisResponse;
import com.emotion.entity.EmotionAnalysis;
import java.time.LocalDateTime;
@@ -96,4 +101,67 @@ public interface EmotionAnalysisService extends IService<EmotionAnalysis> {
*/
EmotionAnalysis createEmotionAnalysis(String messageId, String userId, String primaryEmotion,
String polarity, Double intensity, Double confidence);
}
// 新增的方法
/**
* 分页查询情绪分析记录响应
*/
PageResult<EmotionAnalysisResponse> getPageWithResponse(EmotionAnalysisPageRequest request);
/**
* 根据用户ID分页查询情绪分析记录响应
*/
PageResult<EmotionAnalysisResponse> getPageByUserIdWithResponse(String userId, EmotionAnalysisPageRequest request);
/**
* 根据ID获取情绪分析记录响应
*/
EmotionAnalysisResponse getEmotionAnalysisResponseById(String id);
/**
* 根据消息ID获取情绪分析记录响应
*/
EmotionAnalysisResponse getEmotionAnalysisResponseByMessageId(String messageId);
/**
* 创建情绪分析记录并返回响应
*/
EmotionAnalysisResponse createEmotionAnalysisWithResponse(EmotionAnalysisCreateRequest request);
/**
* 更新情绪分析记录并返回响应
*/
EmotionAnalysisResponse updateEmotionAnalysisWithResponse(EmotionAnalysisUpdateRequest request);
/**
* 删除情绪分析记录
*/
boolean deleteEmotionAnalysis(String id);
/**
* 根据主要情绪查询分析记录响应
*/
List<EmotionAnalysisResponse> getEmotionAnalysisResponsesByPrimaryEmotion(String primaryEmotion);
/**
* 根据情绪极性查询分析记录响应
*/
List<EmotionAnalysisResponse> getEmotionAnalysisResponsesByPolarity(String polarity);
/**
* 根据用户ID和情绪类型查询分析记录响应
*/
List<EmotionAnalysisResponse> getEmotionAnalysisResponsesByUserIdAndEmotion(String userId, String primaryEmotion);
/**
* 根据时间范围查询用户情绪分析记录响应
*/
List<EmotionAnalysisResponse> getEmotionAnalysisResponsesByUserIdAndTimeRange(String userId,
LocalDateTime startTime, LocalDateTime endTime);
/**
* 查询用户最近的情绪分析记录响应
*/
List<EmotionAnalysisResponse> getEmotionAnalysisResponsesRecentByUserId(String userId, Integer limit);
}
@@ -3,10 +3,16 @@ package com.emotion.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
import com.emotion.common.BasePageRequest;
import com.emotion.common.PageResult;
import com.emotion.dto.request.EmotionRecordCreateRequest;
import com.emotion.dto.request.EmotionRecordPageRequest;
import com.emotion.dto.request.EmotionRecordUpdateRequest;
import com.emotion.dto.response.EmotionRecordResponse;
import com.emotion.entity.EmotionRecord;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
/**
* 情绪记录服务接口
@@ -111,4 +117,46 @@ public interface EmotionRecordService extends IService<EmotionRecord> {
*/
EmotionRecord createEmotionRecord(String userId, String emotionType, Double intensity,
String trigger, String location, String notes);
}
// 新增的方法
/**
* 分页查询情绪记录响应
*/
PageResult<EmotionRecordResponse> getPageWithResponse(EmotionRecordPageRequest request);
/**
* 根据用户ID分页查询情绪记录响应
*/
PageResult<EmotionRecordResponse> getPageByUserIdWithResponse(String userId, EmotionRecordPageRequest request);
/**
* 根据ID获取情绪记录响应
*/
EmotionRecordResponse getEmotionRecordResponseById(String id);
/**
* 创建情绪记录并返回响应
*/
EmotionRecordResponse createEmotionRecordWithResponse(EmotionRecordCreateRequest request);
/**
* 更新情绪记录并返回响应
*/
EmotionRecordResponse updateEmotionRecordWithResponse(EmotionRecordUpdateRequest request);
/**
* 删除情绪记录
*/
boolean deleteEmotionRecord(String id);
/**
* 获取用户情绪统计
*/
Map<String, Object> getEmotionStats(String userId, String startDate, String endDate);
/**
* 查询用户最近的情绪记录并返回响应
*/
List<EmotionRecordResponse> getRecentByUserIdWithResponse(String userId, Integer limit);
}
@@ -2,6 +2,8 @@ package com.emotion.service;
import com.emotion.dto.response.UserInfoResponse;
import javax.servlet.http.HttpServletRequest;
/**
* 令牌服务接口
*
@@ -13,24 +15,24 @@ public interface TokenService {
/**
* 从请求中提取并验证令牌,获取用户信息
*
* @param token 访问令牌
* @param request HTTP请求
* @return 用户信息响应
*/
UserInfoResponse getUserInfoByToken(String token);
UserInfoResponse getUserInfoByToken(HttpServletRequest request);
/**
* 从请求中提取并验证令牌,获取用户名
*
* @param token 访问令牌
* @param request HTTP请求
* @return 用户名
*/
String getUsernameByToken(String token);
String getUsernameByToken(HttpServletRequest request);
/**
* 验证令牌并返回用户ID
*
* @param token 访问令牌
* @param request HTTP请求
* @return 用户ID
*/
String validateTokenAndGetUserId(String token);
}
String validateTokenAndGetUserId(HttpServletRequest request);
}
@@ -1,6 +1,6 @@
package com.emotion.service;
import com.emotion.dto.websocket.ChatRequest;
import com.emotion.dto.request.WebSocketRequest;
import com.emotion.dto.websocket.ConnectRequest;
import java.security.Principal;
@@ -9,32 +9,48 @@ import java.security.Principal;
* WebSocket服务接口
*
* @author emotion-museum
* @date 2025-07-25
* @date 2025-09-08
*/
public interface WebSocketService {
/**
* 处理聊天消息
*
* @param request WebSocket请求对象
* @param sessionId 会话ID
* @param principal 用户主体
*/
void handleChatMessage(ChatRequest request, String sessionId, Principal principal);
void handleChatMessage(WebSocketRequest request, String sessionId, Principal principal);
/**
* 处理用户连接
*
* @param request 连接请求对象
* @param sessionId 会话ID
* @param principal 用户主体
*/
void handleUserConnect(ConnectRequest request, String sessionId, Principal principal);
/**
* 处理用户断开连接
*
* @param sessionId 会话ID
* @param principal 用户主体
*/
void handleUserDisconnect(String sessionId, Principal principal);
/**
* 处理心跳消息
*
* @param sessionId 会话ID
* @param principal 用户主体
*/
void handleHeartbeat(String sessionId, Principal principal);
/**
* 获取在线用户数量
*
* @return 在线用户数量
*/
int getOnlineUserCount();
}
}
@@ -5,27 +5,41 @@ import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.emotion.common.BasePageRequest;
import com.emotion.common.PageResult;
import com.emotion.dto.request.achievement.AchievementCreateRequest;
import com.emotion.dto.request.achievement.AchievementPageRequest;
import com.emotion.dto.request.achievement.AchievementUpdateRequest;
import com.emotion.dto.response.achievement.AchievementResponse;
import com.emotion.entity.Achievement;
import com.emotion.mapper.AchievementMapper;
import com.emotion.service.AchievementService;
import com.emotion.util.SnowflakeIdGenerator;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.stream.Collectors;
/**
* 成就服务实现类
*
* @author emotion-museum
* @date 2025-07-23
* @date 2025-09-08
*/
@Service
public class AchievementServiceImpl extends ServiceImpl<AchievementMapper, Achievement> implements AchievementService {
@Autowired
private SnowflakeIdGenerator snowflakeIdGenerator;
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
@Override
public IPage<Achievement> getPage(BasePageRequest request) {
public IPage<Achievement> getPage(AchievementPageRequest request) {
Page<Achievement> page = new Page<>(request.getCurrent(), request.getSize());
LambdaQueryWrapper<Achievement> wrapper = new LambdaQueryWrapper<>();
@@ -35,6 +49,30 @@ public class AchievementServiceImpl extends ServiceImpl<AchievementMapper, Achie
.or().like(Achievement::getDescription, request.getKeyword()));
}
// 分类筛选
if (StringUtils.hasText(request.getCategory())) {
wrapper.eq(Achievement::getCategory, request.getCategory());
}
// 稀有度筛选
if (StringUtils.hasText(request.getRarity())) {
wrapper.eq(Achievement::getRarity, request.getRarity());
}
// 解锁状态筛选
if (request.getUnlocked() != null) {
if (request.getUnlocked()) {
wrapper.isNotNull(Achievement::getUnlockedTime);
} else {
wrapper.isNull(Achievement::getUnlockedTime);
}
}
// 隐藏状态筛选
if (request.getHidden() != null) {
wrapper.eq(Achievement::getIsHidden, request.getHidden() ? 1 : 0);
}
wrapper.eq(Achievement::getIsDeleted, 0);
// 排序
@@ -256,4 +294,147 @@ public class AchievementServiceImpl extends ServiceImpl<AchievementMapper, Achie
.last("LIMIT " + limit);
return this.list(wrapper);
}
}
// 新增的Response相关方法实现
@Override
public PageResult<AchievementResponse> getPageWithResponse(AchievementPageRequest request) {
IPage<Achievement> page = getPage(request);
List<AchievementResponse> responses = page.getRecords().stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
PageResult<AchievementResponse> pageResult = new PageResult<>();
pageResult.setCurrent(page.getCurrent());
pageResult.setSize(page.getSize());
pageResult.setTotal(page.getTotal());
pageResult.setPages(page.getPages());
pageResult.setRecords(responses);
return pageResult;
}
@Override
public AchievementResponse getAchievementResponseById(String id) {
Achievement achievement = this.getById(id);
if (achievement == null) {
return null;
}
return convertToResponse(achievement);
}
@Override
public AchievementResponse createAchievementWithResponse(AchievementCreateRequest request) {
Achievement achievement = new Achievement();
org.springframework.beans.BeanUtils.copyProperties(request, achievement);
achievement.setId(snowflakeIdGenerator.nextIdAsString());
boolean saved = this.save(achievement);
if (!saved) {
return null;
}
return convertToResponse(achievement);
}
@Override
public AchievementResponse updateAchievementWithResponse(AchievementUpdateRequest request) {
Achievement achievement = this.getById(request.getId());
if (achievement == null) {
return null;
}
// 更新非空字段
if (request.getTitle() != null) {
achievement.setTitle(request.getTitle());
}
if (request.getDescription() != null) {
achievement.setDescription(request.getDescription());
}
if (request.getCategory() != null) {
achievement.setCategory(request.getCategory());
}
if (request.getIcon() != null) {
achievement.setIcon(request.getIcon());
}
if (request.getRarity() != null) {
achievement.setRarity(request.getRarity());
}
if (request.getConditionType() != null) {
achievement.setConditionType(request.getConditionType());
}
if (request.getConditionValue() != null) {
achievement.setConditionValue(request.getConditionValue());
}
if (request.getRewards() != null) {
achievement.setRewards(request.getRewards());
}
if (request.getIsHidden() != null) {
achievement.setIsHidden(request.getIsHidden());
}
achievement.setUpdateTime(LocalDateTime.now());
boolean updated = this.updateById(achievement);
if (!updated) {
return null;
}
return convertToResponse(achievement);
}
@Override
public List<AchievementResponse> getByCategoryWithResponse(String category) {
List<Achievement> achievements = getByCategory(category);
return achievements.stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
}
@Override
public List<AchievementResponse> getByRarityWithResponse(String rarity) {
List<Achievement> achievements = getByRarity(rarity);
return achievements.stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
}
@Override
public List<AchievementResponse> getUnlockedAchievementsWithResponse() {
List<Achievement> achievements = getUnlockedAchievements();
return achievements.stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
}
@Override
public List<AchievementResponse> getLockedAchievementsWithResponse() {
List<Achievement> achievements = getLockedAchievements();
return achievements.stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
}
@Override
public List<AchievementResponse> getRecentlyUnlockedWithResponse(Integer limit) {
List<Achievement> achievements = getRecentlyUnlocked(limit);
return achievements.stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
}
/**
* 转换为响应对象
*/
private AchievementResponse convertToResponse(Achievement achievement) {
AchievementResponse response = new AchievementResponse();
org.springframework.beans.BeanUtils.copyProperties(achievement, response);
response.setId(achievement.getId());
if (achievement.getCreateTime() != null) {
response.setCreateTime(achievement.getCreateTime().format(DATE_TIME_FORMATTER));
}
if (achievement.getUpdateTime() != null) {
response.setUpdateTime(achievement.getUpdateTime().format(DATE_TIME_FORMATTER));
}
if (achievement.getUnlockedTime() != null) {
response.setUnlockedTime(achievement.getUnlockedTime().format(DATE_TIME_FORMATTER));
}
return response;
}
}
File diff suppressed because it is too large Load Diff
@@ -13,6 +13,7 @@ import com.emotion.exception.TokenException;
import com.emotion.service.AuthService;
import com.emotion.service.UserService;
import com.emotion.util.JwtUtil;
import com.emotion.util.TokenUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
@@ -22,6 +23,7 @@ import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
@@ -55,6 +57,9 @@ public class AuthServiceImpl implements AuthService {
@Autowired
private JwtUtil jwtUtil;
@Autowired
private TokenUtil tokenUtil;
private static final String CAPTCHA_PREFIX = "captcha:";
private static final String TOKEN_PREFIX = "token:";
private static final String REFRESH_TOKEN_PREFIX = "refresh_token:";
@@ -239,7 +244,8 @@ public class AuthServiceImpl implements AuthService {
}
@Override
public boolean logoutByToken(String token) {
public boolean logoutByToken(HttpServletRequest request) {
String token = tokenUtil.extractToken(request);
String userId = validateTokenAndGetUserId(token);
return logout(userId, token);
}
@@ -248,7 +254,7 @@ public class AuthServiceImpl implements AuthService {
* 验证令牌并获取用户ID
*/
private String validateTokenAndGetUserId(String token) {
if (!StringUtils.hasText(token)) {
if (!tokenUtil.isValidToken(token)) {
throw new TokenException("未提供访问令牌");
}
@@ -293,9 +299,15 @@ public class AuthServiceImpl implements AuthService {
return response;
}
@Override
public boolean validateToken(HttpServletRequest request) {
String token = tokenUtil.extractToken(request);
return validateToken(token);
}
@Override
public boolean validateToken(String token) {
if (!StringUtils.hasText(token)) {
if (!tokenUtil.isValidToken(token)) {
return false;
}
@@ -314,7 +326,7 @@ public class AuthServiceImpl implements AuthService {
@Override
public String getUserIdFromToken(String token) {
if (!StringUtils.hasText(token)) {
if (!tokenUtil.isValidToken(token)) {
return null;
}
@@ -336,7 +348,7 @@ public class AuthServiceImpl implements AuthService {
@Override
public String getUsernameFromToken(String token) {
if (!StringUtils.hasText(token)) {
if (!tokenUtil.isValidToken(token)) {
return null;
}
@@ -498,4 +510,4 @@ public class AuthServiceImpl implements AuthService {
User user = userService.getByPhone(phone);
return user != null;
}
}
}
@@ -1,18 +1,27 @@
package com.emotion.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.emotion.common.BasePageRequest;
import com.emotion.common.PageResult;
import com.emotion.dto.request.comment.CommentCreateRequest;
import com.emotion.dto.request.comment.CommentUpdateRequest;
import com.emotion.dto.request.comment.CommentPageRequest;
import com.emotion.dto.response.comment.CommentResponse;
import com.emotion.entity.Comment;
import com.emotion.mapper.CommentMapper;
import com.emotion.service.CommentService;
import com.emotion.util.SnowflakeIdGenerator;
import com.emotion.util.UserContextUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.stream.Collectors;
/**
* 评论服务实现类
@@ -23,219 +32,125 @@ import java.util.List;
@Service
public class CommentServiceImpl extends ServiceImpl<CommentMapper, Comment> implements CommentService {
@Autowired
private SnowflakeIdGenerator snowflakeIdGenerator;
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
@Override
public IPage<Comment> getPage(BasePageRequest request) {
public PageResult<CommentResponse> getPage(CommentPageRequest request) {
Page<Comment> page = new Page<>(request.getCurrent(), request.getSize());
LambdaQueryWrapper<Comment> wrapper = new LambdaQueryWrapper<>();
// 根据请求参数构建查询条件
if (StringUtils.hasText(request.getPostId())) {
wrapper.eq(Comment::getPostId, request.getPostId());
}
if (StringUtils.hasText(request.getUserId())) {
wrapper.eq(Comment::getUserId, request.getUserId());
}
if (StringUtils.hasText(request.getKeyword())) {
wrapper.like(Comment::getContent, request.getKeyword());
}
wrapper.eq(Comment::getIsDeleted, 0).orderByDesc(Comment::getCreateTime);
return this.page(page, wrapper);
page = this.page(page, wrapper);
return convertPageToPageResult(page);
}
@Override
public IPage<Comment> getPageByPostId(BasePageRequest request, String postId) {
Page<Comment> page = new Page<>(request.getCurrent(), request.getSize());
LambdaQueryWrapper<Comment> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(Comment::getPostId, postId)
.eq(Comment::getIsDeleted, 0)
.orderByDesc(Comment::getCreateTime);
return this.page(page, wrapper);
public CommentResponse getById(String id) {
Comment comment = this.getBaseMapper().selectById(id);
if (comment == null) {
return null;
}
return convertToResponse(comment);
}
@Override
public IPage<Comment> getPageByUserId(BasePageRequest request, String userId) {
Page<Comment> page = new Page<>(request.getCurrent(), request.getSize());
LambdaQueryWrapper<Comment> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(Comment::getUserId, userId)
.eq(Comment::getIsDeleted, 0)
.orderByDesc(Comment::getCreateTime);
return this.page(page, wrapper);
public CommentResponse create(CommentCreateRequest request) {
Comment comment = new Comment();
comment.setId(snowflakeIdGenerator.nextIdAsString()); // 使用雪花算法生成ID
comment.setPostId(request.getPostId());
// 从上下文中获取当前用户ID
String currentUserId = UserContextUtils.getCurrentUserId();
if (currentUserId != null) {
comment.setUserId(currentUserId);
} else if (request.getUserId() != null) {
// 如果上下文中没有用户ID,则使用请求中的用户ID(向后兼容)
comment.setUserId(request.getUserId());
} else {
throw new IllegalArgumentException("用户ID不能为空");
}
comment.setContent(request.getContent());
comment.setReplyToId(request.getReplyToId());
comment.setLikes(0);
this.save(comment);
return convertToResponse(comment);
}
@Override
public List<Comment> getByPostId(String postId) {
LambdaQueryWrapper<Comment> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(Comment::getPostId, postId)
.eq(Comment::getIsDeleted, 0)
.orderByAsc(Comment::getCreateTime);
return this.list(wrapper);
public CommentResponse update(CommentUpdateRequest request) {
Comment comment = this.getBaseMapper().selectById(request.getId());
if (comment == null) {
return null;
}
// 只更新非空字段
if (StringUtils.hasText(request.getContent())) {
comment.setContent(request.getContent());
}
this.updateById(comment);
return convertToResponse(comment);
}
@Override
public List<Comment> getByUserId(String userId) {
LambdaQueryWrapper<Comment> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(Comment::getUserId, userId)
.eq(Comment::getIsDeleted, 0)
.orderByDesc(Comment::getCreateTime);
return this.list(wrapper);
}
@Override
public List<Comment> getRepliesByCommentId(String replyToId) {
LambdaQueryWrapper<Comment> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(Comment::getReplyToId, replyToId)
.eq(Comment::getIsDeleted, 0)
.orderByAsc(Comment::getCreateTime);
return this.list(wrapper);
}
@Override
public List<Comment> getTopLevelCommentsByPostId(String postId) {
LambdaQueryWrapper<Comment> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(Comment::getPostId, postId)
.isNull(Comment::getReplyToId)
.eq(Comment::getIsDeleted, 0)
.orderByAsc(Comment::getCreateTime);
return this.list(wrapper);
}
@Override
public List<Comment> getByLikesRange(Integer minLikes, Integer maxLikes) {
LambdaQueryWrapper<Comment> wrapper = new LambdaQueryWrapper<>();
wrapper.between(Comment::getLikes, minLikes, maxLikes)
.eq(Comment::getIsDeleted, 0)
.orderByDesc(Comment::getLikes);
return this.list(wrapper);
}
@Override
public List<Comment> getByTimeRange(LocalDateTime startTime, LocalDateTime endTime) {
LambdaQueryWrapper<Comment> wrapper = new LambdaQueryWrapper<>();
wrapper.between(Comment::getCreateTime, startTime, endTime)
.eq(Comment::getIsDeleted, 0)
.orderByDesc(Comment::getCreateTime);
return this.list(wrapper);
}
@Override
public Long countByPostId(String postId) {
LambdaQueryWrapper<Comment> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(Comment::getPostId, postId)
.eq(Comment::getIsDeleted, 0);
return this.count(wrapper);
}
@Override
public Long countByUserId(String userId) {
LambdaQueryWrapper<Comment> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(Comment::getUserId, userId)
.eq(Comment::getIsDeleted, 0);
return this.count(wrapper);
}
@Override
public Long countRepliesByCommentId(String commentId) {
LambdaQueryWrapper<Comment> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(Comment::getReplyToId, commentId)
.eq(Comment::getIsDeleted, 0);
return this.count(wrapper);
}
@Override
public Long countTopLevelCommentsByPostId(String postId) {
LambdaQueryWrapper<Comment> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(Comment::getPostId, postId)
.isNull(Comment::getReplyToId)
.eq(Comment::getIsDeleted, 0);
return this.count(wrapper);
}
@Override
public List<Comment> getMostLikedCommentsByPostId(String postId, Integer limit) {
LambdaQueryWrapper<Comment> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(Comment::getPostId, postId)
.eq(Comment::getIsDeleted, 0)
.orderByDesc(Comment::getLikes)
.last("LIMIT " + limit);
return this.list(wrapper);
}
@Override
public List<Comment> getLatestCommentsByPostId(String postId, Integer limit) {
LambdaQueryWrapper<Comment> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(Comment::getPostId, postId)
.eq(Comment::getIsDeleted, 0)
.orderByDesc(Comment::getCreateTime)
.last("LIMIT " + limit);
return this.list(wrapper);
}
@Override
public List<Comment> getRecentByUserId(String userId, Integer limit) {
LambdaQueryWrapper<Comment> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(Comment::getUserId, userId)
.eq(Comment::getIsDeleted, 0)
.orderByDesc(Comment::getCreateTime)
.last("LIMIT " + limit);
return this.list(wrapper);
}
@Override
public List<Comment> getPopularCommentsByPostId(String postId, Integer limit) {
// 这里需要自定义SQL查询,暂时返回最新评论
LambdaQueryWrapper<Comment> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(Comment::getPostId, postId)
.eq(Comment::getIsDeleted, 0)
.orderByDesc(Comment::getCreateTime)
.last("LIMIT " + limit);
return this.list(wrapper);
}
@Override
public List<Comment> searchByKeyword(String keyword) {
LambdaQueryWrapper<Comment> wrapper = new LambdaQueryWrapper<>();
wrapper.like(Comment::getContent, keyword)
.eq(Comment::getIsDeleted, 0)
.orderByDesc(Comment::getCreateTime);
return this.list(wrapper);
}
@Override
public List<Comment> searchByPostIdAndKeyword(String postId, String keyword) {
LambdaQueryWrapper<Comment> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(Comment::getPostId, postId)
.like(Comment::getContent, keyword)
.eq(Comment::getIsDeleted, 0)
.orderByDesc(Comment::getCreateTime);
return this.list(wrapper);
}
@Override
public List<Comment> getByPostIdAndUserId(String postId, String userId) {
LambdaQueryWrapper<Comment> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(Comment::getPostId, postId)
.eq(Comment::getUserId, userId)
.eq(Comment::getIsDeleted, 0)
.orderByDesc(Comment::getCreateTime);
return this.list(wrapper);
}
@Override
public boolean updateLikes(String id, Integer increment) {
Comment comment = this.getById(id);
public boolean delete(String id) {
Comment comment = this.getBaseMapper().selectById(id);
if (comment == null) {
return false;
}
Integer newLikes = comment.getLikes() + increment;
comment.setLikes(newLikes);
// 逻辑删除
comment.setIsDeleted(1);
return this.updateById(comment);
}
@Override
public Comment createComment(String postId, String userId, String content, String replyToId) {
Comment comment = new Comment();
comment.setPostId(postId);
comment.setUserId(userId);
comment.setContent(content);
comment.setReplyToId(replyToId);
comment.setLikes(0);
/**
* 转换为响应对象
*/
private CommentResponse convertToResponse(Comment comment) {
CommentResponse response = new CommentResponse();
BeanUtils.copyProperties(comment, response);
response.setId(comment.getId());
if (comment.getCreateTime() != null) {
response.setCreateTime(comment.getCreateTime().format(DATE_TIME_FORMATTER));
}
if (comment.getUpdateTime() != null) {
response.setUpdateTime(comment.getUpdateTime().format(DATE_TIME_FORMATTER));
}
return response;
}
this.save(comment);
return comment;
/**
* 转换分页对象为PageResult对象
*/
private PageResult<CommentResponse> convertPageToPageResult(Page<Comment> page) {
PageResult<CommentResponse> pageResult = new PageResult<>();
pageResult.setCurrent(page.getCurrent());
pageResult.setSize(page.getSize());
pageResult.setTotal(page.getTotal());
pageResult.setPages(page.getPages());
pageResult.setRecords(page.getRecords().stream()
.map(this::convertToResponse)
.collect(Collectors.toList()));
return pageResult;
}
}
@@ -1,18 +1,26 @@
package com.emotion.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.emotion.common.BasePageRequest;
import com.emotion.common.PageResult;
import com.emotion.dto.request.community.CommunityPostCreateRequest;
import com.emotion.dto.request.community.CommunityPostUpdateRequest;
import com.emotion.dto.request.community.CommunityPostPageRequest;
import com.emotion.dto.response.community.CommunityPostResponse;
import com.emotion.entity.CommunityPost;
import com.emotion.mapper.CommunityPostMapper;
import com.emotion.service.CommunityPostService;
import com.emotion.util.SnowflakeIdGenerator;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.stream.Collectors;
/**
* 社区帖子服务实现类
@@ -23,296 +31,149 @@ import java.util.List;
@Service
public class CommunityPostServiceImpl extends ServiceImpl<CommunityPostMapper, CommunityPost> implements CommunityPostService {
@Autowired
private SnowflakeIdGenerator snowflakeIdGenerator;
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
@Override
public IPage<CommunityPost> getPage(BasePageRequest request) {
public PageResult<CommunityPostResponse> getPage(CommunityPostPageRequest request) {
Page<CommunityPost> page = new Page<>(request.getCurrent(), request.getSize());
LambdaQueryWrapper<CommunityPost> wrapper = new LambdaQueryWrapper<>();
// 根据请求参数构建查询条件
if (StringUtils.hasText(request.getUserId())) {
wrapper.eq(CommunityPost::getUserId, request.getUserId());
}
if (StringUtils.hasText(request.getType())) {
wrapper.eq(CommunityPost::getType, request.getType());
}
if (StringUtils.hasText(request.getLocationId())) {
wrapper.eq(CommunityPost::getLocationId, request.getLocationId());
}
if (request.getPublicOnly() != null && request.getPublicOnly()) {
wrapper.eq(CommunityPost::getIsPrivate, 0);
}
if (StringUtils.hasText(request.getKeyword())) {
wrapper.and(w -> w.like(CommunityPost::getTitle, request.getKeyword())
.or().like(CommunityPost::getContent, request.getKeyword()));
}
wrapper.eq(CommunityPost::getIsDeleted, 0).orderByDesc(CommunityPost::getCreateTime);
return this.page(page, wrapper);
page = this.page(page, wrapper);
return convertPageToResponse(page);
}
@Override
public IPage<CommunityPost> getPublicPostsPage(BasePageRequest request) {
Page<CommunityPost> page = new Page<>(request.getCurrent(), request.getSize());
LambdaQueryWrapper<CommunityPost> wrapper = new LambdaQueryWrapper<>();
if (StringUtils.hasText(request.getKeyword())) {
wrapper.and(w -> w.like(CommunityPost::getTitle, request.getKeyword())
.or().like(CommunityPost::getContent, request.getKeyword()));
}
wrapper.eq(CommunityPost::getIsPrivate, 0)
.eq(CommunityPost::getIsDeleted, 0)
.orderByDesc(CommunityPost::getCreateTime);
return this.page(page, wrapper);
}
@Override
public IPage<CommunityPost> getPageByUserId(BasePageRequest request, String userId) {
Page<CommunityPost> page = new Page<>(request.getCurrent(), request.getSize());
LambdaQueryWrapper<CommunityPost> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(CommunityPost::getUserId, userId)
.eq(CommunityPost::getIsDeleted, 0)
.orderByDesc(CommunityPost::getCreateTime);
return this.page(page, wrapper);
}
@Override
public List<CommunityPost> getByLocationId(String locationId) {
LambdaQueryWrapper<CommunityPost> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(CommunityPost::getLocationId, locationId)
.eq(CommunityPost::getIsDeleted, 0)
.orderByDesc(CommunityPost::getCreateTime);
return this.list(wrapper);
}
@Override
public List<CommunityPost> getByType(String type) {
LambdaQueryWrapper<CommunityPost> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(CommunityPost::getType, type)
.eq(CommunityPost::getIsDeleted, 0)
.orderByDesc(CommunityPost::getCreateTime);
return this.list(wrapper);
}
@Override
public List<CommunityPost> getPrivatePostsByUserId(String userId) {
LambdaQueryWrapper<CommunityPost> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(CommunityPost::getUserId, userId)
.eq(CommunityPost::getIsPrivate, 1)
.eq(CommunityPost::getIsDeleted, 0)
.orderByDesc(CommunityPost::getCreateTime);
return this.list(wrapper);
}
@Override
public List<CommunityPost> getByLikesRange(Integer minLikes, Integer maxLikes) {
LambdaQueryWrapper<CommunityPost> wrapper = new LambdaQueryWrapper<>();
wrapper.between(CommunityPost::getLikes, minLikes, maxLikes)
.eq(CommunityPost::getIsDeleted, 0)
.orderByDesc(CommunityPost::getLikes);
return this.list(wrapper);
}
@Override
public List<CommunityPost> getByViewRange(Integer minViews, Integer maxViews) {
LambdaQueryWrapper<CommunityPost> wrapper = new LambdaQueryWrapper<>();
wrapper.between(CommunityPost::getViewCount, minViews, maxViews)
.eq(CommunityPost::getIsDeleted, 0)
.orderByDesc(CommunityPost::getViewCount);
return this.list(wrapper);
}
@Override
public List<CommunityPost> getByCommentRange(Integer minComments, Integer maxComments) {
LambdaQueryWrapper<CommunityPost> wrapper = new LambdaQueryWrapper<>();
wrapper.between(CommunityPost::getCommentCount, minComments, maxComments)
.eq(CommunityPost::getIsDeleted, 0)
.orderByDesc(CommunityPost::getCommentCount);
return this.list(wrapper);
}
@Override
public List<CommunityPost> getByTimeRange(LocalDateTime startTime, LocalDateTime endTime) {
LambdaQueryWrapper<CommunityPost> wrapper = new LambdaQueryWrapper<>();
wrapper.between(CommunityPost::getCreateTime, startTime, endTime)
.eq(CommunityPost::getIsDeleted, 0)
.orderByDesc(CommunityPost::getCreateTime);
return this.list(wrapper);
}
@Override
public Long countByUserId(String userId) {
LambdaQueryWrapper<CommunityPost> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(CommunityPost::getUserId, userId)
.eq(CommunityPost::getIsDeleted, 0);
return this.count(wrapper);
}
@Override
public Long countPublicPostsByUserId(String userId) {
LambdaQueryWrapper<CommunityPost> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(CommunityPost::getUserId, userId)
.eq(CommunityPost::getIsPrivate, 0)
.eq(CommunityPost::getIsDeleted, 0);
return this.count(wrapper);
}
@Override
public Long countPrivatePostsByUserId(String userId) {
LambdaQueryWrapper<CommunityPost> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(CommunityPost::getUserId, userId)
.eq(CommunityPost::getIsPrivate, 1)
.eq(CommunityPost::getIsDeleted, 0);
return this.count(wrapper);
}
@Override
public Long countByType(String type) {
LambdaQueryWrapper<CommunityPost> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(CommunityPost::getType, type)
.eq(CommunityPost::getIsDeleted, 0);
return this.count(wrapper);
}
@Override
public Long countByLocationId(String locationId) {
LambdaQueryWrapper<CommunityPost> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(CommunityPost::getLocationId, locationId)
.eq(CommunityPost::getIsDeleted, 0);
return this.count(wrapper);
}
@Override
public List<CommunityPost> getMostLikedPosts(Integer limit) {
LambdaQueryWrapper<CommunityPost> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(CommunityPost::getIsDeleted, 0)
.orderByDesc(CommunityPost::getLikes)
.last("LIMIT " + limit);
return this.list(wrapper);
}
@Override
public List<CommunityPost> getMostViewedPosts(Integer limit) {
LambdaQueryWrapper<CommunityPost> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(CommunityPost::getIsDeleted, 0)
.orderByDesc(CommunityPost::getViewCount)
.last("LIMIT " + limit);
return this.list(wrapper);
}
@Override
public List<CommunityPost> getLatestPosts(Integer limit) {
LambdaQueryWrapper<CommunityPost> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(CommunityPost::getIsDeleted, 0)
.orderByDesc(CommunityPost::getCreateTime)
.last("LIMIT " + limit);
return this.list(wrapper);
}
@Override
public List<CommunityPost> getPopularPosts(Integer limit) {
// 这里需要自定义SQL查询,暂时返回最新帖子
LambdaQueryWrapper<CommunityPost> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(CommunityPost::getIsDeleted, 0)
.orderByDesc(CommunityPost::getCreateTime)
.last("LIMIT " + limit);
return this.list(wrapper);
}
@Override
public List<CommunityPost> getByTag(String tag) {
LambdaQueryWrapper<CommunityPost> wrapper = new LambdaQueryWrapper<>();
wrapper.like(CommunityPost::getTags, tag)
.eq(CommunityPost::getIsDeleted, 0)
.orderByDesc(CommunityPost::getCreateTime);
return this.list(wrapper);
}
@Override
public List<CommunityPost> searchByKeyword(String keyword) {
LambdaQueryWrapper<CommunityPost> wrapper = new LambdaQueryWrapper<>();
wrapper.and(w -> w.like(CommunityPost::getTitle, keyword)
.or().like(CommunityPost::getContent, keyword))
.eq(CommunityPost::getIsDeleted, 0)
.orderByDesc(CommunityPost::getCreateTime);
return this.list(wrapper);
}
@Override
public List<CommunityPost> getRecentByUserId(String userId, Integer limit) {
LambdaQueryWrapper<CommunityPost> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(CommunityPost::getUserId, userId)
.eq(CommunityPost::getIsDeleted, 0)
.orderByDesc(CommunityPost::getCreateTime)
.last("LIMIT " + limit);
return this.list(wrapper);
}
@Override
public boolean updateLikes(String id, Integer increment) {
CommunityPost post = this.getById(id);
public CommunityPostResponse getById(String id) {
CommunityPost post = this.getBaseMapper().selectById(id);
if (post == null) {
return false;
return null;
}
// 增加浏览数
Integer viewCount = post.getViewCount() == null ? 1 : post.getViewCount() + 1;
post.setViewCount(viewCount);
this.updateById(post);
Integer newLikes = post.getLikes() + increment;
post.setLikes(newLikes);
return this.updateById(post);
return convertToResponse(post);
}
@Override
public boolean incrementViewCount(String id) {
CommunityPost post = this.getById(id);
if (post == null) {
return false;
}
Integer newViewCount = post.getViewCount() + 1;
post.setViewCount(newViewCount);
return this.updateById(post);
}
@Override
public boolean updateCommentCount(String id, Integer increment) {
CommunityPost post = this.getById(id);
if (post == null) {
return false;
}
Integer newCommentCount = post.getCommentCount() + increment;
post.setCommentCount(newCommentCount);
return this.updateById(post);
}
@Override
public boolean updatePrivacyStatus(String id, Integer isPrivate) {
public CommunityPostResponse create(CommunityPostCreateRequest request) {
CommunityPost post = new CommunityPost();
post.setId(id);
post.setIsPrivate(isPrivate);
return this.updateById(post);
}
@Override
public List<CommunityPost> getRecommendedPosts(String type, String locationId, Integer limit) {
LambdaQueryWrapper<CommunityPost> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(CommunityPost::getIsDeleted, 0);
if (StringUtils.hasText(type)) {
wrapper.eq(CommunityPost::getType, type);
}
if (StringUtils.hasText(locationId)) {
wrapper.eq(CommunityPost::getLocationId, locationId);
}
wrapper.orderByDesc(CommunityPost::getCreateTime)
.last("LIMIT " + limit);
return this.list(wrapper);
}
@Override
public CommunityPost createPost(String userId, String title, String content, String type,
String locationId, String tags, Integer isPrivate) {
CommunityPost post = new CommunityPost();
post.setUserId(userId);
post.setTitle(title);
post.setContent(content);
post.setType(type);
post.setLocationId(locationId);
post.setTags(tags);
post.setIsPrivate(isPrivate);
post.setId(snowflakeIdGenerator.nextIdAsString()); // 使用雪花算法生成ID
post.setUserId(request.getUserId());
post.setTitle(request.getTitle());
post.setContent(request.getContent());
post.setType(request.getType());
post.setLocationId(request.getLocationId());
post.setTags(request.getTags());
post.setIsPrivate(request.getIsPrivate() != null ? request.getIsPrivate() : 0); // 默认公开
post.setLikes(0);
post.setViewCount(0);
post.setCommentCount(0);
this.save(post);
return post;
return convertToResponse(post);
}
@Override
public CommunityPostResponse update(CommunityPostUpdateRequest request) {
CommunityPost post = this.getBaseMapper().selectById(request.getId());
if (post == null) {
return null;
}
// 只更新非空字段
if (request.getTitle() != null) {
post.setTitle(request.getTitle());
}
if (request.getContent() != null) {
post.setContent(request.getContent());
}
if (request.getType() != null) {
post.setType(request.getType());
}
if (request.getLocationId() != null) {
post.setLocationId(request.getLocationId());
}
if (request.getTags() != null) {
post.setTags(request.getTags());
}
if (request.getIsPrivate() != null) {
post.setIsPrivate(request.getIsPrivate());
}
this.updateById(post);
return convertToResponse(post);
}
@Override
public boolean delete(String id) {
CommunityPost post = this.getBaseMapper().selectById(id);
if (post == null) {
return false;
}
// 逻辑删除
post.setIsDeleted(1);
return this.updateById(post);
}
/**
* 转换为响应对象
*/
private CommunityPostResponse convertToResponse(CommunityPost post) {
CommunityPostResponse response = new CommunityPostResponse();
BeanUtils.copyProperties(post, response);
response.setId(post.getId());
if (post.getCreateTime() != null) {
response.setCreateTime(post.getCreateTime().format(DATE_TIME_FORMATTER));
}
if (post.getUpdateTime() != null) {
response.setUpdateTime(post.getUpdateTime().format(DATE_TIME_FORMATTER));
}
return response;
}
/**
* 转换分页对象为响应对象
*/
private PageResult<CommunityPostResponse> convertPageToResponse(Page<CommunityPost> page) {
PageResult<CommunityPostResponse> responsePage = new PageResult<>();
responsePage.setCurrent(page.getCurrent());
responsePage.setSize(page.getSize());
responsePage.setTotal(page.getTotal());
responsePage.setPages(page.getPages());
responsePage.setRecords(
page.getRecords().stream()
.map(this::convertToResponse)
.collect(Collectors.toList()));
return responsePage;
}
}
@@ -4,15 +4,25 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.emotion.common.BasePageRequest;
import com.emotion.common.PageResult;
import com.emotion.dto.request.ConversationCreateRequest;
import com.emotion.dto.request.ConversationPageRequest;
import com.emotion.dto.response.ConversationResponse;
import com.emotion.entity.Conversation;
import com.emotion.mapper.ConversationMapper;
import com.emotion.service.ConversationService;
import com.emotion.util.SnowflakeIdGenerator;
import com.emotion.util.UserContextUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.stream.Collectors;
/**
* 会话服务实现类
@@ -23,11 +33,29 @@ import java.util.List;
@Service
public class ConversationServiceImpl extends ServiceImpl<ConversationMapper, Conversation> implements ConversationService {
@Autowired
private SnowflakeIdGenerator snowflakeIdGenerator;
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
@Override
public IPage<Conversation> getPage(BasePageRequest request) {
public IPage<Conversation> getPage(ConversationPageRequest request) {
Page<Conversation> page = new Page<>(request.getCurrent(), request.getSize());
LambdaQueryWrapper<Conversation> wrapper = new LambdaQueryWrapper<>();
// 根据请求参数构建查询条件
if (StringUtils.hasText(request.getUserId())) {
wrapper.eq(Conversation::getUserId, request.getUserId());
}
if (StringUtils.hasText(request.getStatus())) {
wrapper.eq(Conversation::getConversationStatus, request.getStatus());
}
if (StringUtils.hasText(request.getType())) {
wrapper.eq(Conversation::getType, request.getType());
}
if (StringUtils.hasText(request.getKeyword())) {
wrapper.and(w -> w.like(Conversation::getTitle, request.getKeyword())
.or().like(Conversation::getSummary, request.getKeyword()));
@@ -38,10 +66,16 @@ public class ConversationServiceImpl extends ServiceImpl<ConversationMapper, Con
}
@Override
public IPage<Conversation> getPageByUserId(BasePageRequest request, String userId) {
public IPage<Conversation> getPageByUserId(ConversationPageRequest request) {
Page<Conversation> page = new Page<>(request.getCurrent(), request.getSize());
LambdaQueryWrapper<Conversation> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(Conversation::getUserId, userId)
// 必须提供userId参数
if (!StringUtils.hasText(request.getUserId())) {
throw new IllegalArgumentException("userId不能为空");
}
wrapper.eq(Conversation::getUserId, request.getUserId())
.eq(Conversation::getIsDeleted, 0)
.orderByDesc(Conversation::getCreateTime);
return this.page(page, wrapper);
@@ -148,6 +182,7 @@ public class ConversationServiceImpl extends ServiceImpl<ConversationMapper, Con
@Override
public Conversation createConversation(String userId, String title, String cozeConversationId) {
Conversation conversation = new Conversation();
conversation.setId(snowflakeIdGenerator.nextIdAsString()); // 使用雪花算法生成ID
conversation.setUserId(userId);
conversation.setTitle(title);
conversation.setCozeConversationId(cozeConversationId);
@@ -172,4 +207,157 @@ public class ConversationServiceImpl extends ServiceImpl<ConversationMapper, Con
conversation.setEndTime(LocalDateTime.now());
return this.updateById(conversation);
}
@Override
public PageResult<ConversationResponse> getPageWithResponse(ConversationPageRequest request) {
IPage<Conversation> page = this.getPage(request);
return convertPageToResponse(page);
}
@Override
public PageResult<ConversationResponse> getPageByUserIdWithResponse(ConversationPageRequest request) {
IPage<Conversation> page = this.getPageByUserId(request);
return convertPageToResponse(page);
}
@Override
public ConversationResponse getConversationResponseById(String id) {
Conversation conversation = this.getById(id);
if (conversation == null) {
return null;
}
return convertToResponse(conversation);
}
@Override
public List<ConversationResponse> getByUserIdWithResponse(String userId) {
List<Conversation> conversations = this.getByUserId(userId);
return conversations.stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
}
@Override
public List<ConversationResponse> getActiveConversationsWithResponse() {
// 实现获取活跃对话的逻辑
LambdaQueryWrapper<Conversation> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(Conversation::getConversationStatus, "active")
.eq(Conversation::getIsDeleted, 0)
.orderByDesc(Conversation::getLastActiveTime);
List<Conversation> conversations = this.list(wrapper);
return conversations.stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
}
@Override
public List<ConversationResponse> getArchivedConversationsWithResponse() {
// 实现获取归档对话的逻辑
LambdaQueryWrapper<Conversation> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(Conversation::getConversationStatus, "archived")
.eq(Conversation::getIsDeleted, 0)
.orderByDesc(Conversation::getCreateTime);
List<Conversation> conversations = this.list(wrapper);
return conversations.stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
}
@Override
public boolean archiveConversation(String id) {
Conversation conversation = new Conversation();
conversation.setId(id);
conversation.setConversationStatus("archived");
return this.updateById(conversation);
}
@Override
public boolean activateConversation(String id) {
Conversation conversation = new Conversation();
conversation.setId(id);
conversation.setConversationStatus("active");
return this.updateById(conversation);
}
@Override
public ConversationResponse createConversationWithResponse(ConversationCreateRequest request,
HttpServletRequest httpRequest) {
// 获取客户端IP地址
String clientIp = UserContextUtils.getClientIpAddress(httpRequest);
Conversation conversation = this.createConversation(
request.getUserId(),
request.getTitle(),
null // cozeConversationId
);
// 设置客户端IP
conversation.setClientIp(clientIp);
this.updateById(conversation);
return convertToResponse(conversation);
}
@Override
public ConversationResponse updateConversationWithResponse(ConversationCreateRequest request) {
Conversation conversation = this.getById(request.getId()); // 修复:使用ID而不是userId
if (conversation == null) {
return null;
}
// 更新字段
if (request.getTitle() != null) {
conversation.setTitle(request.getTitle());
}
if (request.getType() != null) {
conversation.setType(request.getType());
}
this.updateById(conversation);
return convertToResponse(conversation);
}
@Override
public boolean updateConversationStatus(String id, String status) {
Conversation conversation = new Conversation();
conversation.setId(id);
conversation.setConversationStatus(status);
return this.updateById(conversation);
}
/**
* 转换为响应对象
*/
private ConversationResponse convertToResponse(Conversation conversation) {
ConversationResponse response = new ConversationResponse();
BeanUtils.copyProperties(conversation, response);
response.setId(conversation.getId());
response.setStatus(conversation.getConversationStatus());
if (conversation.getCreateTime() != null) {
response.setCreateTime(conversation.getCreateTime().format(DATE_TIME_FORMATTER));
}
if (conversation.getUpdateTime() != null) {
response.setUpdateTime(conversation.getUpdateTime().format(DATE_TIME_FORMATTER));
}
if (conversation.getLastActiveTime() != null) {
response.setLastMessageTime(conversation.getLastActiveTime().format(DATE_TIME_FORMATTER));
}
return response;
}
/**
* 转换分页对象为响应对象
*/
private PageResult<ConversationResponse> convertPageToResponse(IPage<Conversation> page) {
PageResult<ConversationResponse> responsePage = new PageResult<>();
responsePage.setCurrent(page.getCurrent());
responsePage.setSize(page.getSize());
responsePage.setTotal(page.getTotal());
responsePage.setPages(page.getPages());
responsePage.setRecords(
page.getRecords().stream()
.map(this::convertToResponse)
.collect(Collectors.toList()));
return responsePage;
}
}
@@ -1,20 +1,27 @@
package com.emotion.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.emotion.common.BasePageRequest;
import com.emotion.common.PageResult;
import com.emotion.dto.request.coze.CozeApiCallPageRequest;
import com.emotion.dto.request.coze.CozeApiCallCreateRequest;
import com.emotion.dto.request.coze.CozeApiCallUpdateRequest;
import com.emotion.dto.response.coze.CozeApiCallResponse;
import com.emotion.entity.CozeApiCall;
import com.emotion.mapper.CozeApiCallMapper;
import com.emotion.service.CozeApiCallService;
import com.emotion.util.SnowflakeIdGenerator;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.stream.Collectors;
/**
* Coze API调用记录服务实现类
@@ -25,15 +32,46 @@ import java.util.List;
@Service
public class CozeApiCallServiceImpl extends ServiceImpl<CozeApiCallMapper, CozeApiCall> implements CozeApiCallService {
@Autowired
private SnowflakeIdGenerator snowflakeIdGenerator;
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
@Override
public IPage<CozeApiCall> getPage(BasePageRequest request) {
public PageResult<CozeApiCallResponse> getPage(CozeApiCallPageRequest request) {
Page<CozeApiCall> page = new Page<>(request.getCurrent(), request.getSize());
LambdaQueryWrapper<CozeApiCall> wrapper = new LambdaQueryWrapper<>();
// 根据请求参数构建查询条件
if (StringUtils.hasText(request.getConversationId())) {
wrapper.eq(CozeApiCall::getConversationId, request.getConversationId());
}
if (StringUtils.hasText(request.getUserId())) {
wrapper.eq(CozeApiCall::getUserId, request.getUserId());
}
if (StringUtils.hasText(request.getBotId())) {
wrapper.eq(CozeApiCall::getBotId, request.getBotId());
}
if (StringUtils.hasText(request.getStatus())) {
wrapper.eq(CozeApiCall::getStatus, request.getStatus());
}
if (StringUtils.hasText(request.getRequestType())) {
wrapper.eq(CozeApiCall::getRequestType, request.getRequestType());
}
if (StringUtils.hasText(request.getTraceId())) {
wrapper.eq(CozeApiCall::getTraceId, request.getTraceId());
}
// 关键词搜索
if (StringUtils.hasText(request.getKeyword())) {
wrapper.like(CozeApiCall::getRequestUrl, request.getKeyword())
.or().like(CozeApiCall::getAiReply, request.getKeyword());
wrapper.and(w -> w.like(CozeApiCall::getRequestUrl, request.getKeyword())
.or().like(CozeApiCall::getAiReply, request.getKeyword())
.or().like(CozeApiCall::getUserMessage, request.getKeyword()));
}
wrapper.eq(CozeApiCall::getIsDeleted, 0);
@@ -49,77 +87,193 @@ public class CozeApiCallServiceImpl extends ServiceImpl<CozeApiCallMapper, CozeA
wrapper.orderByDesc(CozeApiCall::getStartTime);
}
return this.page(page, wrapper);
page = this.page(page, wrapper);
return convertPageToPageResult(page);
}
@Override
public IPage<CozeApiCall> getPageByConversationId(BasePageRequest request, String conversationId) {
Page<CozeApiCall> page = new Page<>(request.getCurrent(), request.getSize());
LambdaQueryWrapper<CozeApiCall> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(CozeApiCall::getConversationId, conversationId)
.eq(CozeApiCall::getIsDeleted, 0);
public CozeApiCallResponse getById(String id) {
CozeApiCall apiCall = this.getBaseMapper().selectById(id);
if (apiCall == null || apiCall.getIsDeleted() == 1) {
return null;
}
return convertToResponse(apiCall);
}
// 关键词搜索
if (StringUtils.hasText(request.getKeyword())) {
wrapper.like(CozeApiCall::getRequestUrl, request.getKeyword())
.or().like(CozeApiCall::getAiReply, request.getKeyword());
@Override
public CozeApiCallResponse create(CozeApiCallCreateRequest request) {
CozeApiCall apiCall = new CozeApiCall();
apiCall.setId(snowflakeIdGenerator.nextIdAsString()); // 使用雪花算法生成ID
// 复制属性
apiCall.setConversationId(request.getConversationId());
apiCall.setMessageId(request.getMessageId());
apiCall.setCozeChatId(request.getCozeChatId());
apiCall.setCozeConversationId(request.getCozeConversationId());
apiCall.setBotId(request.getBotId());
apiCall.setWorkflowId(request.getWorkflowId());
apiCall.setUserId(request.getUserId());
apiCall.setRequestType(request.getRequestType());
apiCall.setRequestUrl(request.getRequestUrl());
apiCall.setRequestBody(request.getRequestBody());
apiCall.setRequestHeaders(request.getRequestHeaders());
apiCall.setUserMessage(request.getUserMessage());
apiCall.setUserMessageType(request.getUserMessageType());
apiCall.setAiReply(request.getAiReply());
apiCall.setAiReplyType(request.getAiReplyType());
apiCall.setResponseStatus(request.getResponseStatus());
apiCall.setResponseBody(request.getResponseBody());
apiCall.setResponseHeaders(request.getResponseHeaders());
apiCall.setPollCount(request.getPollCount());
apiCall.setFinalStatus(request.getFinalStatus());
apiCall.setStatus(request.getStatus());
apiCall.setDurationMs(request.getDurationMs());
apiCall.setPromptTokens(request.getPromptTokens());
apiCall.setCompletionTokens(request.getCompletionTokens());
apiCall.setTotalTokens(request.getTotalTokens());
apiCall.setCost(request.getCost());
apiCall.setFunctionCalls(request.getFunctionCalls());
apiCall.setFunctionResults(request.getFunctionResults());
apiCall.setErrorCode(request.getErrorCode());
apiCall.setErrorMessage(request.getErrorMessage());
apiCall.setClientIp(request.getClientIp());
apiCall.setUserAgent(request.getUserAgent());
apiCall.setSessionId(request.getSessionId());
apiCall.setTraceId(request.getTraceId());
apiCall.setMetadata(request.getMetadata());
this.save(apiCall);
return convertToResponse(apiCall);
}
@Override
public CozeApiCallResponse update(CozeApiCallUpdateRequest request) {
CozeApiCall apiCall = this.getBaseMapper().selectById(request.getId());
if (apiCall == null || apiCall.getIsDeleted() == 1) {
return null;
}
wrapper.orderByDesc(CozeApiCall::getStartTime);
return this.page(page, wrapper);
}
@Override
public IPage<CozeApiCall> getPageByUserId(BasePageRequest request, String userId) {
Page<CozeApiCall> page = new Page<>(request.getCurrent(), request.getSize());
LambdaQueryWrapper<CozeApiCall> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(CozeApiCall::getUserId, userId)
.eq(CozeApiCall::getIsDeleted, 0);
// 关键词搜索
if (StringUtils.hasText(request.getKeyword())) {
wrapper.like(CozeApiCall::getRequestUrl, request.getKeyword())
.or().like(CozeApiCall::getAiReply, request.getKeyword());
// 只更新非空字段
if (request.getConversationId() != null) {
apiCall.setConversationId(request.getConversationId());
}
if (request.getMessageId() != null) {
apiCall.setMessageId(request.getMessageId());
}
if (request.getCozeChatId() != null) {
apiCall.setCozeChatId(request.getCozeChatId());
}
if (request.getCozeConversationId() != null) {
apiCall.setCozeConversationId(request.getCozeConversationId());
}
if (request.getBotId() != null) {
apiCall.setBotId(request.getBotId());
}
if (request.getWorkflowId() != null) {
apiCall.setWorkflowId(request.getWorkflowId());
}
if (request.getUserId() != null) {
apiCall.setUserId(request.getUserId());
}
if (request.getRequestType() != null) {
apiCall.setRequestType(request.getRequestType());
}
if (request.getRequestUrl() != null) {
apiCall.setRequestUrl(request.getRequestUrl());
}
if (request.getRequestBody() != null) {
apiCall.setRequestBody(request.getRequestBody());
}
if (request.getRequestHeaders() != null) {
apiCall.setRequestHeaders(request.getRequestHeaders());
}
if (request.getUserMessage() != null) {
apiCall.setUserMessage(request.getUserMessage());
}
if (request.getUserMessageType() != null) {
apiCall.setUserMessageType(request.getUserMessageType());
}
if (request.getAiReply() != null) {
apiCall.setAiReply(request.getAiReply());
}
if (request.getAiReplyType() != null) {
apiCall.setAiReplyType(request.getAiReplyType());
}
if (request.getResponseStatus() != null) {
apiCall.setResponseStatus(request.getResponseStatus());
}
if (request.getResponseBody() != null) {
apiCall.setResponseBody(request.getResponseBody());
}
if (request.getResponseHeaders() != null) {
apiCall.setResponseHeaders(request.getResponseHeaders());
}
if (request.getPollCount() != null) {
apiCall.setPollCount(request.getPollCount());
}
if (request.getFinalStatus() != null) {
apiCall.setFinalStatus(request.getFinalStatus());
}
if (request.getStatus() != null) {
apiCall.setStatus(request.getStatus());
}
if (request.getDurationMs() != null) {
apiCall.setDurationMs(request.getDurationMs());
}
if (request.getPromptTokens() != null) {
apiCall.setPromptTokens(request.getPromptTokens());
}
if (request.getCompletionTokens() != null) {
apiCall.setCompletionTokens(request.getCompletionTokens());
}
if (request.getTotalTokens() != null) {
apiCall.setTotalTokens(request.getTotalTokens());
}
if (request.getCost() != null) {
apiCall.setCost(request.getCost());
}
if (request.getFunctionCalls() != null) {
apiCall.setFunctionCalls(request.getFunctionCalls());
}
if (request.getFunctionResults() != null) {
apiCall.setFunctionResults(request.getFunctionResults());
}
if (request.getErrorCode() != null) {
apiCall.setErrorCode(request.getErrorCode());
}
if (request.getErrorMessage() != null) {
apiCall.setErrorMessage(request.getErrorMessage());
}
if (request.getClientIp() != null) {
apiCall.setClientIp(request.getClientIp());
}
if (request.getUserAgent() != null) {
apiCall.setUserAgent(request.getUserAgent());
}
if (request.getSessionId() != null) {
apiCall.setSessionId(request.getSessionId());
}
if (request.getTraceId() != null) {
apiCall.setTraceId(request.getTraceId());
}
if (request.getMetadata() != null) {
apiCall.setMetadata(request.getMetadata());
}
wrapper.orderByDesc(CozeApiCall::getStartTime);
return this.page(page, wrapper);
this.updateById(apiCall);
return convertToResponse(apiCall);
}
@Override
public List<CozeApiCall> getByBotId(String botId) {
LambdaQueryWrapper<CozeApiCall> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(CozeApiCall::getBotId, botId)
.eq(CozeApiCall::getIsDeleted, 0)
.orderByDesc(CozeApiCall::getStartTime);
return this.list(wrapper);
}
public boolean delete(String id) {
CozeApiCall apiCall = this.getBaseMapper().selectById(id);
if (apiCall == null) {
return false;
}
@Override
public List<CozeApiCall> getByStatus(String status) {
LambdaQueryWrapper<CozeApiCall> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(CozeApiCall::getStatus, status)
.eq(CozeApiCall::getIsDeleted, 0)
.orderByDesc(CozeApiCall::getStartTime);
return this.list(wrapper);
}
@Override
public List<CozeApiCall> getByRequestType(String requestType) {
LambdaQueryWrapper<CozeApiCall> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(CozeApiCall::getRequestType, requestType)
.eq(CozeApiCall::getIsDeleted, 0)
.orderByDesc(CozeApiCall::getStartTime);
return this.list(wrapper);
}
@Override
public List<CozeApiCall> getByTimeRange(LocalDateTime startTime, LocalDateTime endTime) {
LambdaQueryWrapper<CozeApiCall> wrapper = new LambdaQueryWrapper<>();
wrapper.between(CozeApiCall::getStartTime, startTime, endTime)
.eq(CozeApiCall::getIsDeleted, 0)
.orderByDesc(CozeApiCall::getStartTime);
return this.list(wrapper);
// 逻辑删除
apiCall.setIsDeleted(1);
return this.updateById(apiCall);
}
@Override
@@ -167,87 +321,46 @@ public class CozeApiCallServiceImpl extends ServiceImpl<CozeApiCallMapper, CozeA
.reduce(BigDecimal.ZERO, BigDecimal::add);
}
@Override
public List<CozeApiCall> getFailedCalls() {
LambdaQueryWrapper<CozeApiCall> wrapper = new LambdaQueryWrapper<>();
wrapper.and(w -> w.eq(CozeApiCall::getStatus, "failed").or().eq(CozeApiCall::getFinalStatus, "failed"))
.eq(CozeApiCall::getIsDeleted, 0)
.orderByDesc(CozeApiCall::getStartTime);
return this.list(wrapper);
/**
* 转换为响应对象
*/
private CozeApiCallResponse convertToResponse(CozeApiCall apiCall) {
CozeApiCallResponse response = new CozeApiCallResponse();
BeanUtils.copyProperties(apiCall, response);
response.setId(apiCall.getId());
if (apiCall.getCreateTime() != null) {
response.setCreateTime(apiCall.getCreateTime().format(DATE_TIME_FORMATTER));
}
if (apiCall.getUpdateTime() != null) {
response.setUpdateTime(apiCall.getUpdateTime().format(DATE_TIME_FORMATTER));
}
if (apiCall.getStartTime() != null) {
response.setStartTime(apiCall.getStartTime().format(DATE_TIME_FORMATTER));
}
if (apiCall.getEndTime() != null) {
response.setEndTime(apiCall.getEndTime().format(DATE_TIME_FORMATTER));
}
if (apiCall.getPollStartTime() != null) {
response.setPollStartTime(apiCall.getPollStartTime().format(DATE_TIME_FORMATTER));
}
if (apiCall.getPollEndTime() != null) {
response.setPollEndTime(apiCall.getPollEndTime().format(DATE_TIME_FORMATTER));
}
return response;
}
@Override
public List<CozeApiCall> getTimeoutCalls() {
LambdaQueryWrapper<CozeApiCall> wrapper = new LambdaQueryWrapper<>();
wrapper.and(w -> w.eq(CozeApiCall::getStatus, "timeout").or().eq(CozeApiCall::getFinalStatus, "timeout"))
.eq(CozeApiCall::getIsDeleted, 0)
.orderByDesc(CozeApiCall::getStartTime);
return this.list(wrapper);
/**
* 转换分页对象为PageResult对象
*/
private PageResult<CozeApiCallResponse> convertPageToPageResult(Page<CozeApiCall> page) {
PageResult<CozeApiCallResponse> pageResult = new PageResult<>();
pageResult.setCurrent(page.getCurrent());
pageResult.setSize(page.getSize());
pageResult.setTotal(page.getTotal());
pageResult.setPages(page.getPages());
pageResult.setRecords(page.getRecords().stream()
.map(this::convertToResponse)
.collect(Collectors.toList()));
return pageResult;
}
@Override
public CozeApiCall getByTraceId(String traceId) {
LambdaQueryWrapper<CozeApiCall> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(CozeApiCall::getTraceId, traceId)
.eq(CozeApiCall::getIsDeleted, 0);
return this.getOne(wrapper);
}
@Override
public List<CozeApiCall> getByConversationIdAndRequestType(String conversationId, String requestType) {
LambdaQueryWrapper<CozeApiCall> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(CozeApiCall::getConversationId, conversationId)
.eq(CozeApiCall::getRequestType, requestType)
.eq(CozeApiCall::getIsDeleted, 0)
.orderByDesc(CozeApiCall::getStartTime);
return this.list(wrapper);
}
@Override
public boolean updateStatus(String id, String status, String finalStatus, LocalDateTime endTime) {
LambdaUpdateWrapper<CozeApiCall> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(CozeApiCall::getId, id)
.set(CozeApiCall::getStatus, status)
.set(CozeApiCall::getFinalStatus, finalStatus)
.set(CozeApiCall::getEndTime, endTime)
.set(CozeApiCall::getUpdateTime, LocalDateTime.now());
return this.update(wrapper);
}
@Override
public boolean updateResult(String id, Integer responseStatus, String responseBody, String aiReply,
Integer totalTokens, BigDecimal cost, String status, String finalStatus,
LocalDateTime endTime) {
LambdaUpdateWrapper<CozeApiCall> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(CozeApiCall::getId, id)
.set(CozeApiCall::getResponseStatus, responseStatus)
.set(CozeApiCall::getResponseBody, responseBody)
.set(CozeApiCall::getAiReply, aiReply)
.set(CozeApiCall::getTotalTokens, totalTokens)
.set(CozeApiCall::getCost, cost)
.set(CozeApiCall::getStatus, status)
.set(CozeApiCall::getFinalStatus, finalStatus)
.set(CozeApiCall::getEndTime, endTime)
.set(CozeApiCall::getUpdateTime, LocalDateTime.now());
return this.update(wrapper);
}
@Override
public CozeApiCall createApiCall(String conversationId, String messageId, String userId,
String requestType, String requestUrl, String requestBody) {
CozeApiCall apiCall = CozeApiCall.builder()
.conversationId(conversationId)
.messageId(messageId)
.userId(userId)
.requestType(requestType)
.requestUrl(requestUrl)
.requestBody(requestBody)
.status("pending")
.startTime(LocalDateTime.now())
.createBy(userId) // 设置创建人为当前用户
.updateBy(userId) // 设置更新人为当前用户
.build();
this.save(apiCall);
return apiCall;
}
}
}
@@ -5,18 +5,24 @@ import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.emotion.common.BasePageRequest;
import com.emotion.common.PageResult;
import com.emotion.dto.request.DiaryCommentCreateRequest;
import com.emotion.dto.request.DiaryCommentPageRequest;
import com.emotion.dto.response.DiaryCommentResponse;
import com.emotion.entity.DiaryComment;
import com.emotion.mapper.DiaryCommentMapper;
import com.emotion.service.DiaryCommentService;
import com.emotion.util.SnowflakeIdGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.stream.Collectors;
@@ -29,207 +35,105 @@ import java.util.stream.Collectors;
@Service
public class DiaryCommentServiceImpl extends ServiceImpl<DiaryCommentMapper, DiaryComment> implements DiaryCommentService {
@Autowired
private SnowflakeIdGenerator snowflakeIdGenerator;
@Autowired
private ObjectMapper objectMapper;
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
@Override
public IPage<DiaryComment> getPage(BasePageRequest request) {
public PageResult<DiaryCommentResponse> getPageWithResponse(DiaryCommentPageRequest request) {
Page<DiaryComment> page = new Page<>(request.getCurrent(), request.getSize());
LambdaQueryWrapper<DiaryComment> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(DiaryComment::getIsDeleted, 0)
.orderByDesc(DiaryComment::getIsTop)
// 基础查询条件
wrapper.eq(DiaryComment::getIsDeleted, 0);
// 根据请求参数添加查询条件
if (StringUtils.hasText(request.getDiaryId())) {
wrapper.eq(DiaryComment::getDiaryId, request.getDiaryId());
}
if (StringUtils.hasText(request.getUserId())) {
wrapper.eq(DiaryComment::getUserId, request.getUserId());
}
if (StringUtils.hasText(request.getCommentType())) {
wrapper.eq(DiaryComment::getCommentType, request.getCommentType());
}
if (request.getTopLevelOnly() != null && request.getTopLevelOnly()) {
wrapper.isNull(DiaryComment::getParentCommentId);
}
// 排序
wrapper.orderByDesc(DiaryComment::getIsTop)
.orderByDesc(DiaryComment::getPublishTime);
return this.page(page, wrapper);
IPage<DiaryComment> resultPage = this.page(page, wrapper);
return convertPageToResponse(resultPage);
}
@Override
public IPage<DiaryComment> getPageByDiaryId(String diaryId, BasePageRequest request) {
Page<DiaryComment> page = new Page<>(request.getCurrent(), request.getSize());
LambdaQueryWrapper<DiaryComment> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(DiaryComment::getDiaryId, diaryId)
.eq(DiaryComment::getIsDeleted, 0)
.orderByDesc(DiaryComment::getIsTop)
.orderByDesc(DiaryComment::getPublishTime);
return this.page(page, wrapper);
public DiaryCommentResponse getCommentResponseById(String id) {
DiaryComment comment = this.getById(id);
if (comment == null) {
return null;
}
return convertToResponse(comment);
}
@Override
public IPage<DiaryComment> getPageByUserId(String userId, BasePageRequest request) {
Page<DiaryComment> page = new Page<>(request.getCurrent(), request.getSize());
LambdaQueryWrapper<DiaryComment> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(DiaryComment::getUserId, userId)
.eq(DiaryComment::getIsDeleted, 0)
.orderByDesc(DiaryComment::getPublishTime);
return this.page(page, wrapper);
public List<DiaryCommentResponse> getCommentTreeWithResponse(String diaryId) {
List<DiaryComment> comments = this.getCommentTree(diaryId);
return comments.stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
}
@Override
public List<DiaryComment> getRepliesByParentId(String parentCommentId) {
LambdaQueryWrapper<DiaryComment> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(DiaryComment::getParentCommentId, parentCommentId)
.eq(DiaryComment::getIsDeleted, 0)
.orderByAsc(DiaryComment::getPublishTime);
return this.list(wrapper);
}
@Override
public List<DiaryComment> getByCommentType(String commentType) {
LambdaQueryWrapper<DiaryComment> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(DiaryComment::getCommentType, commentType)
.eq(DiaryComment::getIsDeleted, 0)
.orderByDesc(DiaryComment::getPublishTime);
return this.list(wrapper);
}
@Override
public List<DiaryComment> getByDiaryIdAndCommentType(String diaryId, String commentType) {
LambdaQueryWrapper<DiaryComment> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(DiaryComment::getDiaryId, diaryId)
.eq(DiaryComment::getCommentType, commentType)
.eq(DiaryComment::getIsDeleted, 0)
.orderByDesc(DiaryComment::getPublishTime);
return this.list(wrapper);
}
@Override
public boolean incrementLikeCount(String commentId) {
LambdaUpdateWrapper<DiaryComment> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(DiaryComment::getId, commentId)
.setSql("like_count = like_count + 1");
return this.update(wrapper);
}
@Override
public boolean decrementLikeCount(String commentId) {
LambdaUpdateWrapper<DiaryComment> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(DiaryComment::getId, commentId)
.setSql("like_count = GREATEST(like_count - 1, 0)");
return this.update(wrapper);
}
@Override
public boolean incrementReplyCount(String commentId) {
LambdaUpdateWrapper<DiaryComment> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(DiaryComment::getId, commentId)
.setSql("reply_count = reply_count + 1");
return this.update(wrapper);
}
@Override
public boolean decrementReplyCount(String commentId) {
LambdaUpdateWrapper<DiaryComment> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(DiaryComment::getId, commentId)
.setSql("reply_count = GREATEST(reply_count - 1, 0)");
return this.update(wrapper);
}
@Override
public boolean updateLastReplyTime(String commentId) {
LambdaUpdateWrapper<DiaryComment> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(DiaryComment::getId, commentId)
.set(DiaryComment::getLastReplyTime, LocalDateTime.now());
return this.update(wrapper);
}
@Override
public boolean setTop(String commentId, Integer isTop) {
LambdaUpdateWrapper<DiaryComment> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(DiaryComment::getId, commentId)
.set(DiaryComment::getIsTop, isTop);
return this.update(wrapper);
}
@Override
public Long countByDiaryId(String diaryId) {
LambdaQueryWrapper<DiaryComment> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(DiaryComment::getDiaryId, diaryId)
.eq(DiaryComment::getIsDeleted, 0);
return this.count(wrapper);
}
@Override
public Long countByUserId(String userId) {
LambdaQueryWrapper<DiaryComment> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(DiaryComment::getUserId, userId)
.eq(DiaryComment::getIsDeleted, 0);
return this.count(wrapper);
}
@Override
public Long countByCommentType(String commentType) {
LambdaQueryWrapper<DiaryComment> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(DiaryComment::getCommentType, commentType)
.eq(DiaryComment::getIsDeleted, 0);
return this.count(wrapper);
}
@Override
public Long countReplies(String parentCommentId) {
LambdaQueryWrapper<DiaryComment> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(DiaryComment::getParentCommentId, parentCommentId)
.eq(DiaryComment::getIsDeleted, 0);
return this.count(wrapper);
}
@Override
public DiaryComment createComment(String diaryId, String userId, String content, List<String> images,
String parentCommentId, Integer isAnonymous) {
public DiaryCommentResponse createCommentWithResponse(DiaryCommentCreateRequest request) {
DiaryComment comment = DiaryComment.builder()
.diaryId(diaryId)
.userId(userId)
.content(content)
.images(convertListToJson(images))
.parentCommentId(parentCommentId)
.id(snowflakeIdGenerator.nextIdAsString()) // 使用雪花算法生成ID
.diaryId(request.getDiaryId())
.userId(request.getUserId())
.content(request.getContent())
.images(convertListToJson(request.getImages()))
.parentCommentId(request.getParentCommentId())
.commentType("user")
.likeCount(0)
.replyCount(0)
.isAnonymous(isAnonymous)
.isAnonymous(request.getIsAnonymous())
.isTop(0)
.status("published")
.publishTime(LocalDateTime.now())
.build();
this.save(comment);
// 如果有父评论,更新父评论的回复数
if (StringUtils.hasText(parentCommentId)) {
this.incrementReplyCount(parentCommentId);
this.updateLastReplyTime(parentCommentId);
if (StringUtils.hasText(request.getParentCommentId())) {
this.incrementReplyCount(request.getParentCommentId());
this.updateLastReplyTime(request.getParentCommentId());
}
return comment;
return convertToResponse(comment);
}
@Override
public DiaryComment createAiComment(String diaryId, String content, String aiCommentSource,
BigDecimal emotionScore, BigDecimal sentimentScore) {
DiaryComment comment = DiaryComment.builder()
.diaryId(diaryId)
.userId("system") // AI评论使用system用户ID
.content(content)
.commentType("ai")
.aiCommentSource(aiCommentSource)
.likeCount(0)
.replyCount(0)
.isAnonymous(0)
.isTop(0)
.status("published")
.publishTime(LocalDateTime.now())
.emotionScore(emotionScore)
.sentimentScore(sentimentScore)
.build();
this.save(comment);
return comment;
}
@Override
public boolean updateComment(String commentId, String content, List<String> images) {
public DiaryCommentResponse updateCommentWithResponse(DiaryCommentCreateRequest request) {
LambdaUpdateWrapper<DiaryComment> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(DiaryComment::getId, commentId)
.set(StringUtils.hasText(content), DiaryComment::getContent, content)
.set(images != null, DiaryComment::getImages, convertListToJson(images));
return this.update(wrapper);
wrapper.eq(DiaryComment::getId, request.getId())
.set(StringUtils.hasText(request.getContent()), DiaryComment::getContent, request.getContent())
.set(request.getImages() != null, DiaryComment::getImages, convertListToJson(request.getImages()))
.set(DiaryComment::getUpdateTime, LocalDateTime.now());
boolean updated = this.update(wrapper);
if (!updated) {
return null;
}
DiaryComment updatedComment = this.getById(request.getId());
return updatedComment != null ? convertToResponse(updatedComment) : null;
}
@Override
@@ -241,7 +145,8 @@ public class DiaryCommentServiceImpl extends ServiceImpl<DiaryCommentMapper, Dia
public boolean softDeleteComment(String commentId) {
LambdaUpdateWrapper<DiaryComment> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(DiaryComment::getId, commentId)
.set(DiaryComment::getIsDeleted, 1);
.set(DiaryComment::getIsDeleted, 1)
.set(DiaryComment::getUpdateTime, LocalDateTime.now());
return this.update(wrapper);
}
@@ -249,22 +154,52 @@ public class DiaryCommentServiceImpl extends ServiceImpl<DiaryCommentMapper, Dia
public boolean restoreComment(String commentId) {
LambdaUpdateWrapper<DiaryComment> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(DiaryComment::getId, commentId)
.set(DiaryComment::getIsDeleted, 0);
.set(DiaryComment::getIsDeleted, 0)
.set(DiaryComment::getUpdateTime, LocalDateTime.now());
return this.update(wrapper);
}
@Override
public List<DiaryComment> getCommentTree(String diaryId) {
public boolean incrementLikeCount(String commentId) {
LambdaUpdateWrapper<DiaryComment> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(DiaryComment::getId, commentId)
.setSql("like_count = like_count + 1")
.set(DiaryComment::getUpdateTime, LocalDateTime.now());
return this.update(wrapper);
}
@Override
public boolean decrementLikeCount(String commentId) {
LambdaUpdateWrapper<DiaryComment> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(DiaryComment::getId, commentId)
.setSql("like_count = GREATEST(like_count - 1, 0)")
.set(DiaryComment::getUpdateTime, LocalDateTime.now());
return this.update(wrapper);
}
@Override
public boolean setTop(String commentId, Integer isTop) {
LambdaUpdateWrapper<DiaryComment> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(DiaryComment::getId, commentId)
.set(DiaryComment::getIsTop, isTop)
.set(DiaryComment::getUpdateTime, LocalDateTime.now());
return this.update(wrapper);
}
/**
* 获取评论树结构
*/
private List<DiaryComment> getCommentTree(String diaryId) {
// 获取所有顶级评论(没有父评论的评论)
LambdaQueryWrapper<DiaryComment> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(DiaryComment::getDiaryId, diaryId)
.isNull(DiaryComment::getParentCommentId)
.eq(DiaryComment::getIsDeleted, 0)
.orderByDesc(DiaryComment::getIsTop)
.orderByDesc(DiaryComment::getPublishTime);
.isNull(DiaryComment::getParentCommentId)
.eq(DiaryComment::getIsDeleted, 0)
.orderByDesc(DiaryComment::getIsTop)
.orderByDesc(DiaryComment::getPublishTime);
List<DiaryComment> topComments = this.list(wrapper);
// 为每个顶级评论加载回复
return topComments.stream()
.peek(comment -> {
@@ -274,6 +209,103 @@ public class DiaryCommentServiceImpl extends ServiceImpl<DiaryCommentMapper, Dia
.collect(Collectors.toList());
}
/**
* 根据父评论ID查询回复列表
*/
private List<DiaryComment> getRepliesByParentId(String parentCommentId) {
LambdaQueryWrapper<DiaryComment> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(DiaryComment::getParentCommentId, parentCommentId)
.eq(DiaryComment::getIsDeleted, 0)
.orderByAsc(DiaryComment::getPublishTime);
return this.list(wrapper);
}
/**
* 增加回复数
*/
private boolean incrementReplyCount(String commentId) {
LambdaUpdateWrapper<DiaryComment> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(DiaryComment::getId, commentId)
.setSql("reply_count = reply_count + 1")
.set(DiaryComment::getUpdateTime, LocalDateTime.now());
return this.update(wrapper);
}
/**
* 减少回复数
*/
private boolean decrementReplyCount(String commentId) {
LambdaUpdateWrapper<DiaryComment> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(DiaryComment::getId, commentId)
.setSql("reply_count = GREATEST(reply_count - 1, 0)")
.set(DiaryComment::getUpdateTime, LocalDateTime.now());
return this.update(wrapper);
}
/**
* 更新最后回复时间
*/
private boolean updateLastReplyTime(String commentId) {
LambdaUpdateWrapper<DiaryComment> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(DiaryComment::getId, commentId)
.set(DiaryComment::getLastReplyTime, LocalDateTime.now())
.set(DiaryComment::getUpdateTime, LocalDateTime.now());
return this.update(wrapper);
}
/**
* 转换为响应对象
*/
private DiaryCommentResponse convertToResponse(DiaryComment comment) {
DiaryCommentResponse response = new DiaryCommentResponse();
BeanUtils.copyProperties(comment, response);
// 转换时间格式
if (comment.getPublishTime() != null) {
response.setPublishTime(comment.getPublishTime().format(DATE_TIME_FORMATTER));
}
if (comment.getLastReplyTime() != null) {
response.setLastReplyTime(comment.getLastReplyTime().format(DATE_TIME_FORMATTER));
}
if (comment.getCreateTime() != null) {
response.setCreateTime(comment.getCreateTime().format(DATE_TIME_FORMATTER));
}
if (comment.getUpdateTime() != null) {
response.setUpdateTime(comment.getUpdateTime().format(DATE_TIME_FORMATTER));
}
// 转换JSON字段
try {
if (comment.getImages() != null) {
response.setImages(objectMapper.readValue(comment.getImages(), new TypeReference<List<String>>() {
}));
}
if (comment.getMetadata() != null) {
response.setMetadata(objectMapper.readValue(comment.getMetadata(), Object.class));
}
} catch (JsonProcessingException e) {
// 忽略JSON解析错误
}
return response;
}
/**
* 转换分页对象为响应对象
*/
private PageResult<DiaryCommentResponse> convertPageToResponse(IPage<DiaryComment> page) {
PageResult<DiaryCommentResponse> responsePage = new PageResult<>();
responsePage.setCurrent(page.getCurrent());
responsePage.setSize(page.getSize());
responsePage.setTotal(page.getTotal());
responsePage.setPages(page.getPages());
responsePage.setRecords(
page.getRecords().stream()
.map(this::convertToResponse)
.collect(Collectors.toList()));
return responsePage;
}
/**
* 将List转换为JSON字符串
*/
@@ -287,4 +319,4 @@ public class DiaryCommentServiceImpl extends ServiceImpl<DiaryCommentMapper, Dia
return null;
}
}
}
}
@@ -5,24 +5,30 @@ import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.emotion.common.BasePageRequest;
import com.emotion.common.PageResult;
import com.emotion.dto.request.DiaryPostCreateRequest;
import com.emotion.dto.request.DiaryPostPageRequest;
import com.emotion.dto.request.DiaryPostUpdateRequest;
import com.emotion.dto.response.DiaryPostResponse;
import com.emotion.entity.DiaryPost;
import com.emotion.mapper.DiaryPostMapper;
import com.emotion.service.DiaryPostService;
import com.emotion.service.DiaryCommentService;
import com.emotion.service.AiChatService;
import com.emotion.dto.request.DiaryPostCreateRequest;
import com.emotion.dto.response.DiaryPostResponse;
import com.emotion.util.SnowflakeIdGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.stream.Collectors;
/**
* 用户日记服务实现类
@@ -39,112 +45,153 @@ public class DiaryPostServiceImpl extends ServiceImpl<DiaryPostMapper, DiaryPost
private DiaryCommentService diaryCommentService;
@Autowired
private ObjectMapper objectMapper;
@Autowired
private SnowflakeIdGenerator snowflakeIdGenerator;
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
@Override
public IPage<DiaryPost> getPage(BasePageRequest request) {
public PageResult<DiaryPostResponse> getPageWithResponse(DiaryPostPageRequest request) {
Page<DiaryPost> page = new Page<>(request.getCurrent(), request.getSize());
LambdaQueryWrapper<DiaryPost> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(DiaryPost::getIsDeleted, 0)
.orderByDesc(DiaryPost::getPriority)
// 基础查询条件
wrapper.eq(DiaryPost::getIsDeleted, 0);
// 根据请求参数添加查询条件
if (StringUtils.hasText(request.getUserId())) {
wrapper.eq(DiaryPost::getUserId, request.getUserId());
}
if (Boolean.TRUE.equals(request.getPublicOnly())) {
wrapper.eq(DiaryPost::getIsPublic, 1)
.eq(DiaryPost::getStatus, "published");
}
if (Boolean.TRUE.equals(request.getFeaturedOnly())) {
wrapper.eq(DiaryPost::getFeatured, 1)
.eq(DiaryPost::getIsPublic, 1)
.eq(DiaryPost::getStatus, "published");
}
if (StringUtils.hasText(request.getMood())) {
wrapper.eq(DiaryPost::getMood, request.getMood());
}
if (StringUtils.hasText(request.getTag())) {
// 标签查询可能需要特殊处理,因为tags字段是JSON格式
wrapper.like(DiaryPost::getTags, request.getTag());
}
// 排序
wrapper.orderByDesc(DiaryPost::getPriority)
.orderByDesc(DiaryPost::getPublishTime);
return this.page(page, wrapper);
IPage<DiaryPost> resultPage = this.page(page, wrapper);
List<DiaryPostResponse> responses = resultPage.getRecords().stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
return createPageResult(resultPage, responses);
}
@Override
public IPage<DiaryPost> getPageByUserId(String userId, BasePageRequest request) {
Page<DiaryPost> page = new Page<>(request.getCurrent(), request.getSize());
LambdaQueryWrapper<DiaryPost> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(DiaryPost::getUserId, userId)
.eq(DiaryPost::getIsDeleted, 0)
.orderByDesc(DiaryPost::getPriority)
.orderByDesc(DiaryPost::getPublishTime);
return this.page(page, wrapper);
public DiaryPostResponse getDiaryPostResponseById(String id) {
DiaryPost diaryPost = this.getById(id);
if (diaryPost == null) {
return null;
}
// 增加浏览数
incrementViewCount(id);
return convertToResponse(diaryPost);
}
@Override
public IPage<DiaryPost> getPublicPageByUserId(String userId, BasePageRequest request) {
Page<DiaryPost> page = new Page<>(request.getCurrent(), request.getSize());
LambdaQueryWrapper<DiaryPost> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(DiaryPost::getUserId, userId)
.eq(DiaryPost::getIsPublic, 1)
.eq(DiaryPost::getStatus, "published")
.eq(DiaryPost::getIsDeleted, 0)
.orderByDesc(DiaryPost::getPriority)
.orderByDesc(DiaryPost::getPublishTime);
return this.page(page, wrapper);
public DiaryPostResponse createDiaryPostWithResponse(DiaryPostCreateRequest request) {
// 处理标题:如果为空,则设置为null或生成默认标题
String title = request.getTitle();
if (title == null || title.trim().isEmpty()) {
// 可以选择设置为null,或者生成一个默认标题
// 这里我们设置为null,让数据库使用默认值
title = null;
}
DiaryPost diaryPost = DiaryPost.builder()
.id(snowflakeIdGenerator.nextIdAsString()) // 使用雪花算法生成ID
.userId(request.getUserId())
.title(title)
.content(request.getContent())
.images(convertListToJson(request.getImages()))
.videos(convertListToJson(request.getVideos()))
.location(request.getLocation())
.weather(request.getWeather())
.mood(request.getMood())
.tags(convertListToJson(request.getTags()))
.isPublic(request.getIsPublic())
.isAnonymous(request.getIsAnonymous())
.viewCount(0)
.likeCount(0)
.commentCount(0)
.shareCount(0)
.publishTime(LocalDateTime.now())
.status("published")
.priority(0)
.featured(0)
.build();
this.save(diaryPost);
return convertToResponse(diaryPost);
}
@Override
public IPage<DiaryPost> getFeaturedPage(BasePageRequest request) {
Page<DiaryPost> page = new Page<>(request.getCurrent(), request.getSize());
LambdaQueryWrapper<DiaryPost> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(DiaryPost::getFeatured, 1)
.eq(DiaryPost::getIsPublic, 1)
.eq(DiaryPost::getStatus, "published")
.eq(DiaryPost::getIsDeleted, 0)
.orderByDesc(DiaryPost::getPriority)
.orderByDesc(DiaryPost::getPublishTime);
return this.page(page, wrapper);
public DiaryPostResponse updateDiaryPostWithResponse(DiaryPostUpdateRequest request) {
LambdaUpdateWrapper<DiaryPost> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(DiaryPost::getId, request.getId())
.set(StringUtils.hasText(request.getTitle()), DiaryPost::getTitle, request.getTitle())
.set(StringUtils.hasText(request.getContent()), DiaryPost::getContent, request.getContent())
.set(request.getImages() != null, DiaryPost::getImages, convertListToJson(request.getImages()))
.set(request.getVideos() != null, DiaryPost::getVideos, convertListToJson(request.getVideos()))
.set(StringUtils.hasText(request.getLocation()), DiaryPost::getLocation, request.getLocation())
.set(StringUtils.hasText(request.getWeather()), DiaryPost::getWeather, request.getWeather())
.set(StringUtils.hasText(request.getMood()), DiaryPost::getMood, request.getMood())
.set(request.getTags() != null, DiaryPost::getTags, convertListToJson(request.getTags()))
.set(request.getIsPublic() != null, DiaryPost::getIsPublic, request.getIsPublic())
.set(request.getIsAnonymous() != null, DiaryPost::getIsAnonymous, request.getIsAnonymous())
.set(StringUtils.hasText(request.getStatus()), DiaryPost::getStatus, request.getStatus())
.set(DiaryPost::getUpdateTime, LocalDateTime.now());
boolean updated = this.update(wrapper);
if (!updated) {
return null;
}
DiaryPost updatedDiaryPost = this.getById(request.getId());
return convertToResponse(updatedDiaryPost);
}
@Override
public List<DiaryPost> getByStatus(String status) {
LambdaQueryWrapper<DiaryPost> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(DiaryPost::getStatus, status)
.eq(DiaryPost::getIsDeleted, 0)
.orderByDesc(DiaryPost::getPublishTime);
return this.list(wrapper);
public boolean deleteDiaryPost(String diaryId) {
return this.removeById(diaryId);
}
@Override
public List<DiaryPost> getByUserIdAndStatus(String userId, String status) {
LambdaQueryWrapper<DiaryPost> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(DiaryPost::getUserId, userId)
.eq(DiaryPost::getStatus, status)
.eq(DiaryPost::getIsDeleted, 0)
.orderByDesc(DiaryPost::getPublishTime);
return this.list(wrapper);
public boolean softDeleteDiaryPost(String diaryId) {
LambdaUpdateWrapper<DiaryPost> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(DiaryPost::getId, diaryId)
.set(DiaryPost::getIsDeleted, 1)
.set(DiaryPost::getUpdateTime, LocalDateTime.now());
return this.update(wrapper);
}
@Override
public List<DiaryPost> getByMood(String mood) {
LambdaQueryWrapper<DiaryPost> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(DiaryPost::getMood, mood)
.eq(DiaryPost::getIsPublic, 1)
.eq(DiaryPost::getStatus, "published")
.eq(DiaryPost::getIsDeleted, 0)
.orderByDesc(DiaryPost::getPublishTime);
return this.list(wrapper);
}
@Override
public List<DiaryPost> getByTags(List<String> tags) {
// 这里需要根据实际需求实现标签查询逻辑
// 由于tags字段是JSON格式,可能需要使用数据库的JSON查询功能
LambdaQueryWrapper<DiaryPost> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(DiaryPost::getIsPublic, 1)
.eq(DiaryPost::getStatus, "published")
.eq(DiaryPost::getIsDeleted, 0)
.orderByDesc(DiaryPost::getPublishTime);
return this.list(wrapper);
}
@Override
public List<DiaryPost> getByLocation(String location) {
LambdaQueryWrapper<DiaryPost> wrapper = new LambdaQueryWrapper<>();
wrapper.like(DiaryPost::getLocation, location)
.eq(DiaryPost::getIsPublic, 1)
.eq(DiaryPost::getStatus, "published")
.eq(DiaryPost::getIsDeleted, 0)
.orderByDesc(DiaryPost::getPublishTime);
return this.list(wrapper);
public boolean restoreDiaryPost(String diaryId) {
LambdaUpdateWrapper<DiaryPost> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(DiaryPost::getId, diaryId)
.set(DiaryPost::getIsDeleted, 0)
.set(DiaryPost::getUpdateTime, LocalDateTime.now());
return this.update(wrapper);
}
@Override
public boolean incrementViewCount(String diaryId) {
LambdaUpdateWrapper<DiaryPost> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(DiaryPost::getId, diaryId)
.setSql("view_count = view_count + 1");
.setSql("view_count = view_count + 1")
.set(DiaryPost::getUpdateTime, LocalDateTime.now());
return this.update(wrapper);
}
@@ -152,7 +199,8 @@ public class DiaryPostServiceImpl extends ServiceImpl<DiaryPostMapper, DiaryPost
public boolean incrementLikeCount(String diaryId) {
LambdaUpdateWrapper<DiaryPost> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(DiaryPost::getId, diaryId)
.setSql("like_count = like_count + 1");
.setSql("like_count = like_count + 1")
.set(DiaryPost::getUpdateTime, LocalDateTime.now());
return this.update(wrapper);
}
@@ -160,7 +208,8 @@ public class DiaryPostServiceImpl extends ServiceImpl<DiaryPostMapper, DiaryPost
public boolean decrementLikeCount(String diaryId) {
LambdaUpdateWrapper<DiaryPost> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(DiaryPost::getId, diaryId)
.setSql("like_count = GREATEST(like_count - 1, 0)");
.setSql("like_count = GREATEST(like_count - 1, 0)")
.set(DiaryPost::getUpdateTime, LocalDateTime.now());
return this.update(wrapper);
}
@@ -168,7 +217,8 @@ public class DiaryPostServiceImpl extends ServiceImpl<DiaryPostMapper, DiaryPost
public boolean incrementCommentCount(String diaryId) {
LambdaUpdateWrapper<DiaryPost> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(DiaryPost::getId, diaryId)
.setSql("comment_count = comment_count + 1");
.setSql("comment_count = comment_count + 1")
.set(DiaryPost::getUpdateTime, LocalDateTime.now());
return this.update(wrapper);
}
@@ -176,7 +226,8 @@ public class DiaryPostServiceImpl extends ServiceImpl<DiaryPostMapper, DiaryPost
public boolean decrementCommentCount(String diaryId) {
LambdaUpdateWrapper<DiaryPost> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(DiaryPost::getId, diaryId)
.setSql("comment_count = GREATEST(comment_count - 1, 0)");
.setSql("comment_count = GREATEST(comment_count - 1, 0)")
.set(DiaryPost::getUpdateTime, LocalDateTime.now());
return this.update(wrapper);
}
@@ -184,7 +235,8 @@ public class DiaryPostServiceImpl extends ServiceImpl<DiaryPostMapper, DiaryPost
public boolean incrementShareCount(String diaryId) {
LambdaUpdateWrapper<DiaryPost> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(DiaryPost::getId, diaryId)
.setSql("share_count = share_count + 1");
.setSql("share_count = share_count + 1")
.set(DiaryPost::getUpdateTime, LocalDateTime.now());
return this.update(wrapper);
}
@@ -192,7 +244,8 @@ public class DiaryPostServiceImpl extends ServiceImpl<DiaryPostMapper, DiaryPost
public boolean updateLastCommentTime(String diaryId) {
LambdaUpdateWrapper<DiaryPost> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(DiaryPost::getId, diaryId)
.set(DiaryPost::getLastCommentTime, LocalDateTime.now());
.set(DiaryPost::getLastCommentTime, LocalDateTime.now())
.set(DiaryPost::getUpdateTime, LocalDateTime.now());
return this.update(wrapper);
}
@@ -200,7 +253,8 @@ public class DiaryPostServiceImpl extends ServiceImpl<DiaryPostMapper, DiaryPost
public boolean setFeatured(String diaryId, Integer featured) {
LambdaUpdateWrapper<DiaryPost> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(DiaryPost::getId, diaryId)
.set(DiaryPost::getFeatured, featured);
.set(DiaryPost::getFeatured, featured)
.set(DiaryPost::getUpdateTime, LocalDateTime.now());
return this.update(wrapper);
}
@@ -208,7 +262,8 @@ public class DiaryPostServiceImpl extends ServiceImpl<DiaryPostMapper, DiaryPost
public boolean setPriority(String diaryId, Integer priority) {
LambdaUpdateWrapper<DiaryPost> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(DiaryPost::getId, diaryId)
.set(DiaryPost::getPriority, priority);
.set(DiaryPost::getPriority, priority)
.set(DiaryPost::getUpdateTime, LocalDateTime.now());
return this.update(wrapper);
}
@@ -239,26 +294,12 @@ public class DiaryPostServiceImpl extends ServiceImpl<DiaryPostMapper, DiaryPost
}
@Override
public Long countByStatus(String status) {
LambdaQueryWrapper<DiaryPost> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(DiaryPost::getStatus, status)
.eq(DiaryPost::getIsDeleted, 0);
return this.count(wrapper);
}
@Override
public DiaryPost createDiaryPost(com.emotion.dto.request.DiaryPostCreateRequest request) {
// 处理标题:如果为空,则设置为null或生成默认标题
String title = request.getTitle();
if (title == null || title.trim().isEmpty()) {
// 可以选择设置为null,或者生成一个默认标题
// 这里我们设置为null,让数据库使用默认值
title = null;
}
public DiaryPostResponse publishDiaryWithAiComment(DiaryPostCreateRequest request) {
// 1. 保存日记
DiaryPost diaryPost = DiaryPost.builder()
.id(snowflakeIdGenerator.nextIdAsString()) // 使用雪花算法生成ID
.userId(request.getUserId())
.title(title)
.title(request.getTitle())
.content(request.getContent())
.images(convertListToJson(request.getImages()))
.videos(convertListToJson(request.getVideos()))
@@ -277,69 +318,8 @@ public class DiaryPostServiceImpl extends ServiceImpl<DiaryPostMapper, DiaryPost
.priority(0)
.featured(0)
.build();
this.save(diaryPost);
return diaryPost;
}
@Override
public boolean updateDiaryPost(String diaryId, com.emotion.dto.request.DiaryPostUpdateRequest request) {
LambdaUpdateWrapper<DiaryPost> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(DiaryPost::getId, diaryId)
.set(StringUtils.hasText(request.getTitle()), DiaryPost::getTitle, request.getTitle())
.set(StringUtils.hasText(request.getContent()), DiaryPost::getContent, request.getContent())
.set(request.getImages() != null, DiaryPost::getImages, convertListToJson(request.getImages()))
.set(request.getVideos() != null, DiaryPost::getVideos, convertListToJson(request.getVideos()))
.set(StringUtils.hasText(request.getLocation()), DiaryPost::getLocation, request.getLocation())
.set(StringUtils.hasText(request.getWeather()), DiaryPost::getWeather, request.getWeather())
.set(StringUtils.hasText(request.getMood()), DiaryPost::getMood, request.getMood())
.set(request.getTags() != null, DiaryPost::getTags, convertListToJson(request.getTags()))
.set(request.getIsPublic() != null, DiaryPost::getIsPublic, request.getIsPublic())
.set(request.getIsAnonymous() != null, DiaryPost::getIsAnonymous, request.getIsAnonymous())
.set(StringUtils.hasText(request.getStatus()), DiaryPost::getStatus, request.getStatus());
return this.update(wrapper);
}
@Override
public boolean deleteDiaryPost(String diaryId) {
return this.removeById(diaryId);
}
@Override
public boolean softDeleteDiaryPost(String diaryId) {
LambdaUpdateWrapper<DiaryPost> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(DiaryPost::getId, diaryId)
.set(DiaryPost::getIsDeleted, 1);
return this.update(wrapper);
}
@Override
public boolean restoreDiaryPost(String diaryId) {
LambdaUpdateWrapper<DiaryPost> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(DiaryPost::getId, diaryId)
.set(DiaryPost::getIsDeleted, 0);
return this.update(wrapper);
}
@Override
public boolean addAiComment(String diaryId, String aiComment, Object aiEmotionAnalysis,
BigDecimal aiSentimentScore, List<String> aiKeywords, String aiSuggestions) {
LambdaUpdateWrapper<DiaryPost> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(DiaryPost::getId, diaryId)
.set(DiaryPost::getAiComment, aiComment)
.set(DiaryPost::getAiCommentTime, LocalDateTime.now())
.set(DiaryPost::getAiEmotionAnalysis, convertObjectToJson(aiEmotionAnalysis))
.set(DiaryPost::getAiSentimentScore, aiSentimentScore)
.set(DiaryPost::getAiKeywords, convertListToJson(aiKeywords))
.set(DiaryPost::getAiSuggestions, aiSuggestions);
return this.update(wrapper);
}
@Override
public com.emotion.dto.response.DiaryPostResponse publishDiaryWithAiComment(com.emotion.dto.request.DiaryPostCreateRequest request) {
// 1. 保存日记
DiaryPost diaryPost = createDiaryPost(request);
// 2. 生成AI评论
String aiComment = null;
try {
@@ -350,13 +330,14 @@ public class DiaryPostServiceImpl extends ServiceImpl<DiaryPostMapper, DiaryPost
}
// 3. 写入AI评论到diary_comment表
if (aiComment != null) {
diaryCommentService.createAiComment(
diaryPost.getId(),
aiComment,
"diary_ai_summary",
null,
null
);
// 使用createCommentWithResponse方法创建AI评论
com.emotion.dto.request.DiaryCommentCreateRequest commentRequest = new com.emotion.dto.request.DiaryCommentCreateRequest();
commentRequest.setDiaryId(diaryPost.getId());
commentRequest.setUserId("system"); // AI评论使用system用户ID
commentRequest.setContent(aiComment);
commentRequest.setIsAnonymous(0);
diaryCommentService.createCommentWithResponse(commentRequest);
addAiComment(
diaryPost.getId(),
aiComment,
@@ -367,53 +348,37 @@ public class DiaryPostServiceImpl extends ServiceImpl<DiaryPostMapper, DiaryPost
);
}
// 4. 返回日记详情(含AI评论)
com.emotion.dto.response.DiaryPostResponse response = new com.emotion.dto.response.DiaryPostResponse();
org.springframework.beans.BeanUtils.copyProperties(diaryPost, response);
// 查询AI评论
List<com.emotion.entity.DiaryComment> aiComments = diaryCommentService.getByDiaryIdAndCommentType(diaryPost.getId(), "ai");
if (!aiComments.isEmpty()) {
response.setAiComment(aiComments.get(0).getContent());
}
// 转换时间格式
if (diaryPost.getPublishTime() != null) {
response.setPublishTime(diaryPost.getPublishTime().format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
}
if (diaryPost.getLastCommentTime() != null) {
response.setLastCommentTime(diaryPost.getLastCommentTime().format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
}
if (diaryPost.getAiCommentTime() != null) {
response.setAiCommentTime(diaryPost.getAiCommentTime().format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
}
if (diaryPost.getCreateTime() != null) {
response.setCreateTime(diaryPost.getCreateTime().format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
}
if (diaryPost.getUpdateTime() != null) {
response.setUpdateTime(diaryPost.getUpdateTime().format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
}
// 转换JSON字段
try {
if (diaryPost.getImages() != null) {
response.setImages(objectMapper.readValue(diaryPost.getImages(), new com.fasterxml.jackson.core.type.TypeReference<List<String>>() {}));
}
if (diaryPost.getVideos() != null) {
response.setVideos(objectMapper.readValue(diaryPost.getVideos(), new com.fasterxml.jackson.core.type.TypeReference<List<String>>() {}));
}
if (diaryPost.getTags() != null) {
response.setTags(objectMapper.readValue(diaryPost.getTags(), new com.fasterxml.jackson.core.type.TypeReference<List<String>>() {}));
}
if (diaryPost.getAiKeywords() != null) {
response.setAiKeywords(objectMapper.readValue(diaryPost.getAiKeywords(), new com.fasterxml.jackson.core.type.TypeReference<List<String>>() {}));
}
if (diaryPost.getAiEmotionAnalysis() != null) {
response.setAiEmotionAnalysis(objectMapper.readValue(diaryPost.getAiEmotionAnalysis(), Object.class));
}
if (diaryPost.getMetadata() != null) {
response.setMetadata(objectMapper.readValue(diaryPost.getMetadata(), Object.class));
}
} catch (com.fasterxml.jackson.core.JsonProcessingException e) {
// 忽略JSON解析错误
}
return response;
return convertToResponse(diaryPost);
}
/**
* 添加AI评论
*/
private boolean addAiComment(String diaryId, String aiComment, Object aiEmotionAnalysis,
BigDecimal aiSentimentScore, List<String> aiKeywords, String aiSuggestions) {
LambdaUpdateWrapper<DiaryPost> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(DiaryPost::getId, diaryId)
.set(DiaryPost::getAiComment, aiComment)
.set(DiaryPost::getAiCommentTime, LocalDateTime.now())
.set(DiaryPost::getAiEmotionAnalysis, convertObjectToJson(aiEmotionAnalysis))
.set(DiaryPost::getAiSentimentScore, aiSentimentScore)
.set(DiaryPost::getAiKeywords, convertListToJson(aiKeywords))
.set(DiaryPost::getAiSuggestions, aiSuggestions)
.set(DiaryPost::getUpdateTime, LocalDateTime.now());
return this.update(wrapper);
}
/**
* 创建PageResult对象
*/
private <T> PageResult<T> createPageResult(IPage<?> page, List<T> records) {
PageResult<T> pageResult = new PageResult<>();
pageResult.setCurrent(page.getCurrent());
pageResult.setSize(page.getSize());
pageResult.setTotal(page.getTotal());
pageResult.setPages(page.getPages());
pageResult.setRecords(records);
return pageResult;
}
/**
@@ -443,4 +408,60 @@ public class DiaryPostServiceImpl extends ServiceImpl<DiaryPostMapper, DiaryPost
return null;
}
}
}
/**
* 转换实体为响应DTO
*/
private DiaryPostResponse convertToResponse(DiaryPost diaryPost) {
DiaryPostResponse response = new DiaryPostResponse();
BeanUtils.copyProperties(diaryPost, response);
// 转换时间格式
if (diaryPost.getPublishTime() != null) {
response.setPublishTime(diaryPost.getPublishTime().format(DATE_TIME_FORMATTER));
}
if (diaryPost.getLastCommentTime() != null) {
response.setLastCommentTime(diaryPost.getLastCommentTime().format(DATE_TIME_FORMATTER));
}
if (diaryPost.getAiCommentTime() != null) {
response.setAiCommentTime(diaryPost.getAiCommentTime().format(DATE_TIME_FORMATTER));
}
if (diaryPost.getCreateTime() != null) {
response.setCreateTime(diaryPost.getCreateTime().format(DATE_TIME_FORMATTER));
}
if (diaryPost.getUpdateTime() != null) {
response.setUpdateTime(diaryPost.getUpdateTime().format(DATE_TIME_FORMATTER));
}
// 转换JSON字段
try {
if (diaryPost.getImages() != null) {
response.setImages(objectMapper.readValue(diaryPost.getImages(), new TypeReference<List<String>>() {
}));
}
if (diaryPost.getVideos() != null) {
response.setVideos(objectMapper.readValue(diaryPost.getVideos(), new TypeReference<List<String>>() {
}));
}
if (diaryPost.getTags() != null) {
response.setTags(objectMapper.readValue(diaryPost.getTags(), new TypeReference<List<String>>() {
}));
}
if (diaryPost.getAiKeywords() != null) {
response.setAiKeywords(
objectMapper.readValue(diaryPost.getAiKeywords(), new TypeReference<List<String>>() {
}));
}
if (diaryPost.getAiEmotionAnalysis() != null) {
response.setAiEmotionAnalysis(objectMapper.readValue(diaryPost.getAiEmotionAnalysis(), Object.class));
}
if (diaryPost.getMetadata() != null) {
response.setMetadata(objectMapper.readValue(diaryPost.getMetadata(), Object.class));
}
} catch (JsonProcessingException e) {
// 忽略JSON解析错误
}
return response;
}
}
@@ -5,15 +5,25 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.emotion.common.BasePageRequest;
import com.emotion.common.PageResult;
import com.emotion.dto.request.EmotionAnalysisCreateRequest;
import com.emotion.dto.request.EmotionAnalysisPageRequest;
import com.emotion.dto.request.EmotionAnalysisUpdateRequest;
import com.emotion.dto.response.EmotionAnalysisResponse;
import com.emotion.entity.EmotionAnalysis;
import com.emotion.mapper.EmotionAnalysisMapper;
import com.emotion.service.EmotionAnalysisService;
import com.emotion.util.SnowflakeIdGenerator;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.stream.Collectors;
/**
* 情绪分析服务实现类
@@ -24,6 +34,11 @@ import java.util.List;
@Service
public class EmotionAnalysisServiceImpl extends ServiceImpl<EmotionAnalysisMapper, EmotionAnalysis> implements EmotionAnalysisService {
@Autowired
private SnowflakeIdGenerator snowflakeIdGenerator;
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
@Override
public IPage<EmotionAnalysis> getPage(BasePageRequest request) {
Page<EmotionAnalysis> page = new Page<>(request.getCurrent(), request.getSize());
@@ -173,6 +188,7 @@ public class EmotionAnalysisServiceImpl extends ServiceImpl<EmotionAnalysisMappe
public EmotionAnalysis createEmotionAnalysis(String messageId, String userId, String primaryEmotion,
String polarity, Double intensity, Double confidence) {
EmotionAnalysis analysis = new EmotionAnalysis();
analysis.setId(snowflakeIdGenerator.nextIdAsString()); // 使用雪花算法生成ID
analysis.setMessageId(messageId);
analysis.setCreateBy(userId);
analysis.setPrimaryEmotion(primaryEmotion);
@@ -183,4 +199,162 @@ public class EmotionAnalysisServiceImpl extends ServiceImpl<EmotionAnalysisMappe
this.save(analysis);
return analysis;
}
// 新增的方法实现
@Override
public PageResult<EmotionAnalysisResponse> getPageWithResponse(EmotionAnalysisPageRequest request) {
IPage<EmotionAnalysis> page = getPage(request);
List<EmotionAnalysisResponse> responses = page.getRecords().stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
return createPageResult(page, responses);
}
@Override
public PageResult<EmotionAnalysisResponse> getPageByUserIdWithResponse(String userId,
EmotionAnalysisPageRequest request) {
IPage<EmotionAnalysis> page = getPageByUserId(request, userId);
List<EmotionAnalysisResponse> responses = page.getRecords().stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
return createPageResult(page, responses);
}
@Override
public EmotionAnalysisResponse getEmotionAnalysisResponseById(String id) {
EmotionAnalysis analysis = this.getById(id);
if (analysis == null) {
return null;
}
return convertToResponse(analysis);
}
@Override
public EmotionAnalysisResponse getEmotionAnalysisResponseByMessageId(String messageId) {
EmotionAnalysis analysis = getByMessageId(messageId);
if (analysis == null) {
return null;
}
return convertToResponse(analysis);
}
@Override
public EmotionAnalysisResponse createEmotionAnalysisWithResponse(EmotionAnalysisCreateRequest request) {
EmotionAnalysis analysis = createEmotionAnalysis(
request.getMessageId(),
request.getUserId(),
request.getPrimaryEmotion(),
request.getPolarity(),
request.getIntensity(),
request.getConfidence());
return convertToResponse(analysis);
}
@Override
public EmotionAnalysisResponse updateEmotionAnalysisWithResponse(EmotionAnalysisUpdateRequest request) {
EmotionAnalysis analysis = this.getById(request.getId());
if (analysis == null) {
return null;
}
// 更新字段
if (request.getMessageId() != null) {
analysis.setMessageId(request.getMessageId());
}
if (request.getUserId() != null) {
analysis.setCreateBy(request.getUserId());
}
if (request.getPrimaryEmotion() != null) {
analysis.setPrimaryEmotion(request.getPrimaryEmotion());
}
if (request.getPolarity() != null) {
analysis.setPolarity(request.getPolarity());
}
if (request.getIntensity() != null) {
analysis.setIntensity(BigDecimal.valueOf(request.getIntensity()));
}
if (request.getConfidence() != null) {
analysis.setConfidence(BigDecimal.valueOf(request.getConfidence()));
}
this.updateById(analysis);
return convertToResponse(analysis);
}
@Override
public boolean deleteEmotionAnalysis(String id) {
return this.removeById(id);
}
@Override
public List<EmotionAnalysisResponse> getEmotionAnalysisResponsesByPrimaryEmotion(String primaryEmotion) {
List<EmotionAnalysis> analyses = getByPrimaryEmotion(primaryEmotion);
return analyses.stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
}
@Override
public List<EmotionAnalysisResponse> getEmotionAnalysisResponsesByPolarity(String polarity) {
List<EmotionAnalysis> analyses = getByPolarity(polarity);
return analyses.stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
}
@Override
public List<EmotionAnalysisResponse> getEmotionAnalysisResponsesByUserIdAndEmotion(String userId,
String primaryEmotion) {
List<EmotionAnalysis> analyses = getByUserIdAndEmotion(userId, primaryEmotion);
return analyses.stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
}
@Override
public List<EmotionAnalysisResponse> getEmotionAnalysisResponsesByUserIdAndTimeRange(String userId,
LocalDateTime startTime, LocalDateTime endTime) {
List<EmotionAnalysis> analyses = getByUserIdAndTimeRange(userId, startTime, endTime);
return analyses.stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
}
@Override
public List<EmotionAnalysisResponse> getEmotionAnalysisResponsesRecentByUserId(String userId, Integer limit) {
List<EmotionAnalysis> analyses = getRecentByUserId(userId, limit);
return analyses.stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
}
/**
* 创建PageResult对象
*/
private <T> PageResult<T> createPageResult(IPage<?> page, List<T> records) {
PageResult<T> pageResult = new PageResult<>();
pageResult.setCurrent(page.getCurrent());
pageResult.setSize(page.getSize());
pageResult.setTotal(page.getTotal());
pageResult.setPages(page.getPages());
pageResult.setRecords(records);
return pageResult;
}
/**
* 转换为响应对象
*/
private EmotionAnalysisResponse convertToResponse(EmotionAnalysis analysis) {
EmotionAnalysisResponse response = new EmotionAnalysisResponse();
BeanUtils.copyProperties(analysis, response);
response.setId(analysis.getId());
if (analysis.getCreateTime() != null) {
response.setCreateTime(analysis.getCreateTime().format(DATE_TIME_FORMATTER));
}
if (analysis.getUpdateTime() != null) {
response.setUpdateTime(analysis.getUpdateTime().format(DATE_TIME_FORMATTER));
}
return response;
}
}
@@ -5,17 +5,29 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.emotion.common.BasePageRequest;
import com.emotion.common.PageResult;
import com.emotion.dto.request.EmotionRecordCreateRequest;
import com.emotion.dto.request.EmotionRecordPageRequest;
import com.emotion.dto.request.EmotionRecordUpdateRequest;
import com.emotion.dto.response.EmotionRecordResponse;
import com.emotion.entity.EmotionRecord;
import com.emotion.mapper.EmotionRecordMapper;
import com.emotion.service.EmotionRecordService;
import com.emotion.util.SnowflakeIdGenerator;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import java.util.Collections;
import java.util.stream.Collectors;
/**
* 情绪记录服务实现类
@@ -26,6 +38,11 @@ import java.util.Collections;
@Service
public class EmotionRecordServiceImpl extends ServiceImpl<EmotionRecordMapper, EmotionRecord> implements EmotionRecordService {
@Autowired
private SnowflakeIdGenerator snowflakeIdGenerator;
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
@Override
public IPage<EmotionRecord> getPage(BasePageRequest request) {
Page<EmotionRecord> page = new Page<>(request.getCurrent(), request.getSize());
@@ -134,20 +151,59 @@ public class EmotionRecordServiceImpl extends ServiceImpl<EmotionRecordMapper, E
@Override
public Double getAvgIntensityByUserId(String userId) {
// 这里需要自定义SQL查询平均值,暂时返回0
return 0.0;
LambdaQueryWrapper<EmotionRecord> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(EmotionRecord::getUserId, userId)
.eq(EmotionRecord::getIsDeleted, 0);
List<EmotionRecord> records = this.list(wrapper);
if (records.isEmpty()) {
return 0.0;
}
double sum = records.stream()
.mapToDouble(record -> record.getIntensity().doubleValue())
.sum();
return sum / records.size();
}
@Override
public Double getAvgIntensityByUserIdAndTimeRange(String userId, LocalDateTime startTime, LocalDateTime endTime) {
// 这里需要自定义SQL查询平均值,暂时返回0
return 0.0;
LambdaQueryWrapper<EmotionRecord> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(EmotionRecord::getUserId, userId)
.between(EmotionRecord::getCreateTime, startTime, endTime)
.eq(EmotionRecord::getIsDeleted, 0);
List<EmotionRecord> records = this.list(wrapper);
if (records.isEmpty()) {
return 0.0;
}
double sum = records.stream()
.mapToDouble(record -> record.getIntensity().doubleValue())
.sum();
return sum / records.size();
}
@Override
public String getMostFrequentEmotionByUserId(String userId) {
// 这里需要自定义SQL查询最常见的情绪类型,暂时返回null
return null;
LambdaQueryWrapper<EmotionRecord> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(EmotionRecord::getUserId, userId)
.eq(EmotionRecord::getIsDeleted, 0);
List<EmotionRecord> records = this.list(wrapper);
if (records.isEmpty()) {
return null;
}
return records.stream()
.collect(Collectors.groupingBy(EmotionRecord::getEmotionType, Collectors.counting()))
.entrySet()
.stream()
.max(Map.Entry.comparingByValue())
.map(Map.Entry::getKey)
.orElse(null);
}
@Override
@@ -181,6 +237,7 @@ public class EmotionRecordServiceImpl extends ServiceImpl<EmotionRecordMapper, E
public EmotionRecord createEmotionRecord(String userId, String emotionType, Double intensity,
String trigger, String location, String notes) {
EmotionRecord record = new EmotionRecord();
record.setId(snowflakeIdGenerator.nextIdAsString()); // 使用雪花算法生成ID
record.setUserId(userId);
record.setEmotionType(emotionType);
record.setIntensity(BigDecimal.valueOf(intensity));
@@ -204,4 +261,223 @@ public class EmotionRecordServiceImpl extends ServiceImpl<EmotionRecordMapper, E
return this.page(page, wrapper);
}
// 新增的方法实现
@Override
public PageResult<EmotionRecordResponse> getPageWithResponse(EmotionRecordPageRequest request) {
IPage<EmotionRecord> page = getPage(request);
List<EmotionRecordResponse> responses = page.getRecords().stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
PageResult<EmotionRecordResponse> pageResult = new PageResult<>();
pageResult.setCurrent(page.getCurrent());
pageResult.setSize(page.getSize());
pageResult.setTotal(page.getTotal());
pageResult.setPages(page.getPages());
pageResult.setRecords(responses);
return pageResult;
}
@Override
public PageResult<EmotionRecordResponse> getPageByUserIdWithResponse(String userId, EmotionRecordPageRequest request) {
IPage<EmotionRecord> page = getPageByUserId(request, userId);
List<EmotionRecordResponse> responses = page.getRecords().stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
PageResult<EmotionRecordResponse> pageResult = new PageResult<>();
pageResult.setCurrent(page.getCurrent());
pageResult.setSize(page.getSize());
pageResult.setTotal(page.getTotal());
pageResult.setPages(page.getPages());
pageResult.setRecords(responses);
return pageResult;
}
@Override
public EmotionRecordResponse getEmotionRecordResponseById(String id) {
EmotionRecord record = this.getById(id);
if (record == null) {
return null;
}
return convertToResponse(record);
}
@Override
public EmotionRecordResponse createEmotionRecordWithResponse(EmotionRecordCreateRequest request) {
EmotionRecord record = new EmotionRecord();
record.setId(snowflakeIdGenerator.nextIdAsString()); // 使用雪花算法生成ID
record.setUserId(request.getUserId());
record.setRecordDate(request.getRecordDate());
record.setEmotionType(request.getEmotionType());
record.setIntensity(request.getIntensity());
record.setTriggers(request.getTriggers());
record.setDescription(request.getDescription());
// 注意:tags字段在实体类中是String类型,需要转换
record.setTags(request.getTags() != null ? request.getTags().toString() : null);
record.setWeather(request.getWeather());
record.setLocation(request.getLocation());
record.setActivity(request.getActivity());
record.setPeople(request.getPeople());
record.setNotes(request.getNotes());
this.save(record);
return convertToResponse(record);
}
@Override
public EmotionRecordResponse updateEmotionRecordWithResponse(EmotionRecordUpdateRequest request) {
EmotionRecord record = this.getById(request.getId());
if (record == null) {
return null;
}
// 更新字段
if (request.getRecordDate() != null) {
record.setRecordDate(request.getRecordDate());
}
if (request.getEmotionType() != null) {
record.setEmotionType(request.getEmotionType());
}
if (request.getIntensity() != null) {
record.setIntensity(request.getIntensity());
}
if (request.getTriggers() != null) {
record.setTriggers(request.getTriggers());
}
if (request.getDescription() != null) {
record.setDescription(request.getDescription());
}
// 注意:tags字段在实体类中是String类型,需要转换
if (request.getTags() != null) {
record.setTags(request.getTags().toString());
}
if (request.getWeather() != null) {
record.setWeather(request.getWeather());
}
if (request.getLocation() != null) {
record.setLocation(request.getLocation());
}
if (request.getActivity() != null) {
record.setActivity(request.getActivity());
}
if (request.getPeople() != null) {
record.setPeople(request.getPeople());
}
if (request.getNotes() != null) {
record.setNotes(request.getNotes());
}
this.updateById(record);
return convertToResponse(record);
}
@Override
public boolean deleteEmotionRecord(String id) {
return this.removeById(id);
}
@Override
public Map<String, Object> getEmotionStats(String userId, String startDate, String endDate) {
Map<String, Object> stats = new HashMap<>();
// 构建查询条件
LambdaQueryWrapper<EmotionRecord> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(EmotionRecord::getUserId, userId)
.eq(EmotionRecord::getIsDeleted, 0);
// 如果提供了日期范围,则添加时间条件
if (StringUtils.hasText(startDate) && StringUtils.hasText(endDate)) {
try {
LocalDateTime start = LocalDate.parse(startDate).atStartOfDay();
LocalDateTime end = LocalDate.parse(endDate).atTime(23, 59, 59);
wrapper.between(EmotionRecord::getCreateTime, start, end);
} catch (Exception e) {
// 如果日期解析失败,忽略日期条件
}
}
// 查询用户的所有情绪记录
List<EmotionRecord> records = this.list(wrapper);
if (records.isEmpty()) {
stats.put("emotionDistribution", new HashMap<>());
stats.put("totalRecords", 0);
stats.put("averageIntensity", 0.0);
stats.put("mostFrequentEmotion", null);
stats.put("emotionTrend", "no_data");
return stats;
}
// 情绪类型分布
Map<String, Long> emotionDistribution = records.stream()
.collect(Collectors.groupingBy(EmotionRecord::getEmotionType, Collectors.counting()));
stats.put("emotionDistribution", emotionDistribution);
stats.put("totalRecords", records.size());
// 平均情绪强度
double avgIntensity = records.stream()
.mapToDouble(record -> record.getIntensity().doubleValue())
.average()
.orElse(0.0);
stats.put("averageIntensity", avgIntensity);
// 最常见的情绪类型
String mostFrequentEmotion = emotionDistribution.entrySet().stream()
.max(Map.Entry.comparingByValue())
.map(Map.Entry::getKey)
.orElse(null);
stats.put("mostFrequentEmotion", mostFrequentEmotion);
// 情绪趋势(简单实现:比较前半段和后半段的平均强度)
if (records.size() > 1) {
int mid = records.size() / 2;
double firstHalfAvg = records.subList(0, mid).stream()
.mapToDouble(record -> record.getIntensity().doubleValue())
.average()
.orElse(0.0);
double secondHalfAvg = records.subList(mid, records.size()).stream()
.mapToDouble(record -> record.getIntensity().doubleValue())
.average()
.orElse(0.0);
if (secondHalfAvg > firstHalfAvg) {
stats.put("emotionTrend", "improving");
} else if (secondHalfAvg < firstHalfAvg) {
stats.put("emotionTrend", "declining");
} else {
stats.put("emotionTrend", "stable");
}
} else {
stats.put("emotionTrend", "insufficient_data");
}
return stats;
}
@Override
public List<EmotionRecordResponse> getRecentByUserIdWithResponse(String userId, Integer limit) {
List<EmotionRecord> records = getRecentByUserId(userId, limit);
return records.stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
}
/**
* 转换为响应对象
*/
private EmotionRecordResponse convertToResponse(EmotionRecord record) {
EmotionRecordResponse response = new EmotionRecordResponse();
BeanUtils.copyProperties(record, response);
response.setId(record.getId());
if (record.getCreateTime() != null) {
response.setCreateTime(record.getCreateTime().format(DATE_TIME_FORMATTER));
}
if (record.getUpdateTime() != null) {
response.setUpdateTime(record.getUpdateTime().format(DATE_TIME_FORMATTER));
}
return response;
}
}
@@ -4,10 +4,13 @@ import com.emotion.dto.response.UserInfoResponse;
import com.emotion.exception.TokenException;
import com.emotion.service.AuthService;
import com.emotion.service.TokenService;
import com.emotion.util.TokenUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import javax.servlet.http.HttpServletRequest;
/**
* 令牌服务实现类
*
@@ -20,15 +23,19 @@ public class TokenServiceImpl implements TokenService {
@Autowired
private AuthService authService;
@Autowired
private TokenUtil tokenUtil;
@Override
public UserInfoResponse getUserInfoByToken(String token) {
String userId = validateTokenAndGetUserId(token);
public UserInfoResponse getUserInfoByToken(HttpServletRequest request) {
String userId = validateTokenAndGetUserId(request);
return authService.getCurrentUserInfo(userId);
}
@Override
public String getUsernameByToken(String token) {
if (!StringUtils.hasText(token)) {
public String getUsernameByToken(HttpServletRequest request) {
String token = tokenUtil.extractToken(request);
if (!tokenUtil.isValidToken(token)) {
throw new TokenException("未提供访问令牌");
}
@@ -45,8 +52,9 @@ public class TokenServiceImpl implements TokenService {
}
@Override
public String validateTokenAndGetUserId(String token) {
if (!StringUtils.hasText(token)) {
public String validateTokenAndGetUserId(HttpServletRequest request) {
String token = tokenUtil.extractToken(request);
if (!tokenUtil.isValidToken(token)) {
throw new TokenException("未提供访问令牌");
}
@@ -61,4 +69,4 @@ public class TokenServiceImpl implements TokenService {
return userId;
}
}
}
@@ -1,5 +1,6 @@
package com.emotion.service.impl;
import com.emotion.dto.request.WebSocketRequest;
import com.emotion.dto.websocket.ChatRequest;
import com.emotion.dto.websocket.ConnectRequest;
import com.emotion.dto.websocket.WebSocketMessage;
@@ -13,6 +14,7 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.security.Principal;
import java.time.LocalDateTime;
@@ -48,64 +50,54 @@ public class WebSocketServiceImpl implements WebSocketService {
* 处理聊天消息
*/
@Override
public void handleChatMessage(ChatRequest request, String sessionId, Principal principal) {
public void handleChatMessage(WebSocketRequest webSocketRequest, String sessionId, Principal principal) {
try {
log.info("处理聊天消息: request={}, sessionId={}, principal={}", request, sessionId, principal);
log.info("处理聊天消息: request={}, sessionId={}, principal={}", webSocketRequest, sessionId, principal);
// 验证请求参数
if (request.getContent() == null || request.getContent().trim().isEmpty()) {
sendErrorMessage(request.getSenderId(), "消息内容不能为空");
if (webSocketRequest.getContent() == null || webSocketRequest.getContent().trim().isEmpty()) {
sendErrorMessage(getUserId(principal, sessionId), "消息内容不能为空");
return;
}
// 确定用户身份和类型
String userId = request.getSenderId();
WebSocketMessage.SenderType senderType = WebSocketMessage.SenderType.GUEST;
// 设置默认值
setWebSocketRequestDefaults(webSocketRequest, principal, sessionId);
if (principal != null) {
userId = principal.getName();
// 如果用户ID不是以guest_开头,说明是认证用户
if (!userId.startsWith("guest_")) {
senderType = WebSocketMessage.SenderType.USER;
}
}
log.info("确定用户身份: userId={}, senderType={}", webSocketRequest.getSenderId(),
webSocketRequest.getSenderType());
// 更新请求中的用户信息
request.setSenderId(userId);
request.setSenderType(senderType == WebSocketMessage.SenderType.USER ? ChatRequest.SenderType.USER
: ChatRequest.SenderType.GUEST);
log.info("确定用户身份: userId={}, senderType={}", userId, senderType);
// 转换请求对象
ChatRequest chatRequest = convertToChatRequest(webSocketRequest);
// 构建用户消息
WebSocketMessage userMessage = WebSocketMessage.builder()
.messageId(UUID.randomUUID().toString())
.conversationId(request.getConversationId())
.type(WebSocketMessage.MessageType.TEXT)
.content(request.getContent())
.senderId(userId)
.senderType(senderType)
.status(WebSocketMessage.MessageStatus.SENT)
.conversationId(chatRequest.getConversationId())
.type("TEXT")
.content(chatRequest.getContent())
.senderId(chatRequest.getSenderId())
.senderType(getSenderType(chatRequest.getSenderType()))
.status("SENT")
.createTime(LocalDateTime.now())
.build();
// 发送用户消息到会话
if (request.getConversationId() != null) {
messagingTemplate.convertAndSend("/topic/conversation/" + request.getConversationId(), userMessage);
if (chatRequest.getConversationId() != null) {
messagingTemplate.convertAndSend("/topic/conversation/" + chatRequest.getConversationId(), userMessage);
}
// 发送给用户私有队列
messagingTemplate.convertAndSendToUser(request.getSenderId(), "/queue/messages", userMessage);
messagingTemplate.convertAndSendToUser(chatRequest.getSenderId(), "/queue/messages", userMessage);
// 发送AI思考状态
sendAiThinkingMessage(request.getSenderId(), request.getConversationId());
sendAiThinkingMessage(chatRequest.getSenderId(), chatRequest.getConversationId());
// 异步调用AI服务
processAiResponse(request);
processAiResponse(chatRequest);
} catch (Exception e) {
log.error("处理聊天消息失败", e);
sendErrorMessage(request.getSenderId(), "消息处理失败,请稍后重试");
sendErrorMessage(getUserId(principal, sessionId), "消息处理失败,请稍后重试");
}
}
@@ -115,39 +107,27 @@ public class WebSocketServiceImpl implements WebSocketService {
@Override
public void handleUserConnect(ConnectRequest request, String sessionId, Principal principal) {
try {
String userId = request.getUserId();
boolean isAuthenticated = false;
// 优先从Principal获取认证用户信息
if (principal != null) {
userId = principal.getName();
// 检查是否是认证用户(不是访客)
isAuthenticated = !userId.startsWith("guest_");
}
// 如果还没有userId,生成访客ID
if (userId == null) {
userId = "guest_" + sessionId;
}
// 设置默认值
setConnectRequestDefaults(request, principal, sessionId);
log.info("用户连接WebSocket: userId={}, sessionId={}, authenticated={}",
userId, sessionId, isAuthenticated);
request.getUserId(), sessionId, isUserAuthenticated(request.getUserId()));
// 记录在线用户
onlineUsers.put(sessionId, userId);
onlineUsers.put(sessionId, request.getUserId());
// 发送连接成功消息
WebSocketMessage connectMessage = WebSocketMessage.builder()
.messageId(UUID.randomUUID().toString())
.type(WebSocketMessage.MessageType.CONNECTION)
.type("CONNECTION")
.content("连接成功")
.senderId("system")
.senderType(WebSocketMessage.SenderType.SYSTEM)
.status(WebSocketMessage.MessageStatus.SENT)
.senderType("SYSTEM")
.status("SENT")
.createTime(LocalDateTime.now())
.build();
messagingTemplate.convertAndSendToUser(userId, "/queue/messages", connectMessage);
messagingTemplate.convertAndSendToUser(request.getUserId(), "/queue/messages", connectMessage);
} catch (Exception e) {
log.error("处理用户连接失败", e);
@@ -182,11 +162,11 @@ public class WebSocketServiceImpl implements WebSocketService {
// 发送心跳响应
WebSocketMessage heartbeatMessage = WebSocketMessage.builder()
.messageId(UUID.randomUUID().toString())
.type(WebSocketMessage.MessageType.HEARTBEAT)
.type("HEARTBEAT")
.content("pong")
.senderId("system")
.senderType(WebSocketMessage.SenderType.SYSTEM)
.status(WebSocketMessage.MessageStatus.SENT)
.senderType("SYSTEM")
.status("SENT")
.createTime(LocalDateTime.now())
.build();
@@ -206,11 +186,11 @@ public class WebSocketServiceImpl implements WebSocketService {
WebSocketMessage thinkingMessage = WebSocketMessage.builder()
.messageId(UUID.randomUUID().toString())
.conversationId(conversationId)
.type(WebSocketMessage.MessageType.AI_THINKING)
.type("AI_THINKING")
.content("AI正在思考中...")
.senderId("ai")
.senderType(WebSocketMessage.SenderType.AI)
.status(WebSocketMessage.MessageStatus.SENT)
.senderType("AI")
.status("SENT")
.createTime(LocalDateTime.now())
.build();
@@ -246,7 +226,7 @@ public class WebSocketServiceImpl implements WebSocketService {
userMessage.setUserId(userId);
userMessage.setCreateBy(userId); // 设置创建人为当前用户
userMessage
.setUserType(request.getSenderType() == ChatRequest.SenderType.USER ? "registered" : "guest");
.setUserType("USER".equals(request.getSenderType()) ? "registered" : "guest");
userMessage.setContent(request.getContent());
userMessage.setType("text");
userMessage.setSender("user");
@@ -281,11 +261,11 @@ public class WebSocketServiceImpl implements WebSocketService {
private void sendErrorMessage(String userId, String errorContent) {
WebSocketMessage errorMessage = WebSocketMessage.builder()
.messageId(UUID.randomUUID().toString())
.type(WebSocketMessage.MessageType.ERROR)
.type("ERROR")
.content(errorContent)
.senderId("system")
.senderType(WebSocketMessage.SenderType.SYSTEM)
.status(WebSocketMessage.MessageStatus.SENT)
.senderType("SYSTEM")
.status("SENT")
.createTime(LocalDateTime.now())
.build();
@@ -309,7 +289,7 @@ public class WebSocketServiceImpl implements WebSocketService {
Conversation conversation = Conversation.builder()
.userId(userId)
.userType(request.getSenderType() == ChatRequest.SenderType.USER ? "registered" : "guest")
.userType("USER".equals(request.getSenderType()) ? "registered" : "guest")
.title("新对话")
.type("chat")
.conversationStatus("active")
@@ -341,7 +321,7 @@ public class WebSocketServiceImpl implements WebSocketService {
// 如果会话不存在,创建一个
conversation = Conversation.builder()
.userId(userId)
.userType(request.getSenderType() == ChatRequest.SenderType.USER ? "registered" : "guest")
.userType("USER".equals(request.getSenderType()) ? "registered" : "guest")
.title("对话")
.type("chat")
.conversationStatus("active")
@@ -453,11 +433,11 @@ public class WebSocketServiceImpl implements WebSocketService {
WebSocketMessage fallbackMessage = WebSocketMessage.builder()
.messageId(UUID.randomUUID().toString())
.conversationId(conversationId)
.type(WebSocketMessage.MessageType.TEXT)
.type("TEXT")
.content(aiReply)
.senderId("ai")
.senderType(WebSocketMessage.SenderType.AI)
.status(WebSocketMessage.MessageStatus.SENT)
.senderType("AI")
.status("SENT")
.createTime(LocalDateTime.now())
.build();
@@ -488,11 +468,11 @@ public class WebSocketServiceImpl implements WebSocketService {
WebSocketMessage aiMessage = WebSocketMessage.builder()
.messageId(UUID.randomUUID().toString())
.conversationId(conversationId)
.type(WebSocketMessage.MessageType.TEXT)
.type("TEXT")
.content(content)
.senderId("ai")
.senderType(WebSocketMessage.SenderType.AI)
.status(WebSocketMessage.MessageStatus.SENT)
.senderType("AI")
.status("SENT")
.createTime(LocalDateTime.now())
.build();
@@ -534,4 +514,95 @@ public class WebSocketServiceImpl implements WebSocketService {
log.debug("没有换行符,返回原始内容");
return new String[]{aiReply};
}
}
/**
* 设置WebSocket请求的默认值
*/
private void setWebSocketRequestDefaults(WebSocketRequest request, Principal principal, String sessionId) {
// 如果请求中没有发送者ID,尝试从Principal获取
if (request.getSenderId() == null && principal != null) {
request.setSenderId(principal.getName());
}
// 如果还是没有发送者ID,使用会话ID作为访客ID
if (request.getSenderId() == null) {
request.setSenderId("guest_" + sessionId);
request.setSenderType("GUEST");
}
// 设置时间戳
if (request.getTimestamp() == null) {
request.setTimestamp(System.currentTimeMillis());
}
}
/**
* 设置连接请求的默认值
*/
private void setConnectRequestDefaults(ConnectRequest request, Principal principal, String sessionId) {
// 优先从Principal获取认证用户信息
if (principal != null) {
request.setUserId(principal.getName());
}
// 如果还没有userId,生成访客ID
if (request.getUserId() == null) {
request.setUserId("guest_" + sessionId);
}
// 设置连接时间戳
if (request.getTimestamp() == null) {
request.setTimestamp(System.currentTimeMillis());
}
}
/**
* 获取用户ID
*/
private String getUserId(Principal principal, String sessionId) {
if (principal != null) {
return principal.getName();
}
return "guest_" + sessionId;
}
/**
* 判断用户是否已认证
*/
private boolean isUserAuthenticated(String userId) {
return userId != null && !userId.startsWith("guest_");
}
/**
* 获取发送者类型
*/
private String getSenderType(String senderType) {
if ("USER".equals(senderType)) {
return "USER";
} else if ("GUEST".equals(senderType)) {
return "GUEST";
} else if ("AI".equals(senderType)) {
return "AI";
} else if ("SYSTEM".equals(senderType)) {
return "SYSTEM";
}
return "USER";
}
/**
* 转换WebSocketRequest到ChatRequest
*
* @param webSocketRequest WebSocket请求对象
* @return ChatRequest对象
*/
private ChatRequest convertToChatRequest(WebSocketRequest webSocketRequest) {
return ChatRequest.builder()
.content(webSocketRequest.getContent())
.senderId(webSocketRequest.getSenderId())
.senderType(webSocketRequest.getSenderType())
.messageType(webSocketRequest.getMessageType())
.conversationId(webSocketRequest.getConversationId())
.timestamp(webSocketRequest.getTimestamp())
.build();
}
}
@@ -0,0 +1,47 @@
package com.emotion.util;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import javax.servlet.http.HttpServletRequest;
/**
* 令牌工具类
*
* @author emotion-museum
* @date 2025-09-08
*/
@Component
public class TokenUtil {
/**
* 从请求中提取访问令牌
*
* @param request HTTP请求
* @return 访问令牌
*/
public String extractToken(HttpServletRequest request) {
String authHeader = request.getHeader("Authorization");
if (authHeader != null && authHeader.startsWith("Bearer ")) {
return authHeader.substring(7);
}
// 也可以从请求参数中获取
String tokenParam = request.getParameter("token");
if (tokenParam != null && !tokenParam.trim().isEmpty()) {
return tokenParam.trim();
}
return null;
}
/**
* 验证令牌是否有效
*
* @param token 令牌
* @return 是否有效
*/
public boolean isValidToken(String token) {
return StringUtils.hasText(token);
}
}
@@ -1,57 +1,94 @@
package com.emotion.util;
import com.emotion.util.UserContextHolder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
import javax.servlet.http.HttpServletRequest;
/**
* 用户上下文工具类
* 提供便捷的用户上下文操作方法
* 提供便捷的方法操作用户上下文信息
*
* @author emotion-museum
* @date 2025-07-23
* @date 2025-07-25
*/
@Slf4j
public class UserContextUtils {
/**
* 获取当前用户ID,如果为空则返回默认值
* 获取当前用户ID
*
* @param defaultValue 默认值
* @return 用户ID
* @return 当前用户ID,如果未登录则返回null
*/
public static String getCurrentUserIdOrDefault(String defaultValue) {
String userId = UserContextHolder.getCurrentUserId();
return userId != null ? userId : defaultValue;
public static String getCurrentUserId() {
return UserContextHolder.getCurrentUserId();
}
/**
* 获取当前用户ID,如果为空则返回"system"
* 获取当前用户
*
* @return 用户ID
* @return 当前用户名,如果未登录则返回null
*/
public static String getCurrentUserIdOrSystem() {
return getCurrentUserIdOrDefault("system");
public static String getCurrentUsername() {
return UserContextHolder.getCurrentUsername();
}
/**
* 获取当前用户名,如果为空则返回默认值
* 获取当前用户类型
*
* @param defaultValue 默认值
* @return 用户名
* @return 当前用户类型,如果未登录则返回null
*/
public static String getCurrentUsernameOrDefault(String defaultValue) {
String username = UserContextHolder.getCurrentUsername();
return username != null ? username : defaultValue;
public static String getCurrentUserType() {
return UserContextHolder.getCurrentUserType();
}
/**
* 获取当前用户名,如果为空则返回"guest"
* 获取客户端IP
*
* @return 用户名
* @return 客户端IP
*/
public static String getCurrentUsernameOrGuest() {
return getCurrentUsernameOrDefault("guest");
public static String getClientIp() {
return UserContextHolder.getClientIp();
}
/**
* 获取请求ID
*
* @return 请求ID
*/
public static String getRequestId() {
return UserContextHolder.getRequestId();
}
/**
* 获取当前用户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;
}
/**
* 获取当前用户类型,如果为空则返回默认值
*
@@ -62,7 +99,7 @@ public class UserContextUtils {
String userType = UserContextHolder.getCurrentUserType();
return userType != null ? userType : defaultValue;
}
/**
* 获取当前用户类型,如果为空则返回"GUEST"
*
@@ -71,7 +108,7 @@ public class UserContextUtils {
public static String getCurrentUserTypeOrGuest() {
return getCurrentUserTypeOrDefault("GUEST");
}
/**
* 获取客户端IP,如果为空则返回默认值
*
@@ -82,7 +119,7 @@ public class UserContextUtils {
String clientIp = UserContextHolder.getClientIp();
return clientIp != null ? clientIp : defaultValue;
}
/**
* 获取客户端IP,如果为空则返回"unknown"
*
@@ -91,7 +128,7 @@ public class UserContextUtils {
public static String getClientIpOrUnknown() {
return getClientIpOrDefault("unknown");
}
/**
* 获取请求ID,如果为空则返回默认值
*
@@ -102,87 +139,27 @@ public class UserContextUtils {
String requestId = UserContextHolder.getRequestId();
return requestId != null ? requestId : defaultValue;
}
/**
* 获取请求ID,如果为空则返回"unknown"
* 获取请求ID,如果为空则返回随机UUID
*
* @return 请求ID
*/
public static String getRequestIdOrUnknown() {
return getRequestIdOrDefault("unknown");
}
/**
* 检查当前用户是否为访客
*
* @return 是否为访客
*/
public static boolean isGuest() {
String userId = UserContextHolder.getCurrentUserId();
String userType = UserContextHolder.getCurrentUserType();
return userId == null ||
userId.startsWith("guest_") ||
"GUEST".equalsIgnoreCase(userType);
}
/**
* 检查当前用户是否为系统用户
*
* @return 是否为系统用户
*/
public static boolean isSystem() {
String userId = UserContextHolder.getCurrentUserId();
String userType = UserContextHolder.getCurrentUserType();
return "system".equals(userId) ||
"SYSTEM".equalsIgnoreCase(userType);
}
/**
* 检查当前用户是否为注册用户
*
* @return 是否为注册用户
*/
public static boolean isRegisteredUser() {
return !isGuest() && !isSystem();
}
/**
* 安全地执行需要用户上下文的操作
* 如果没有用户上下文,会设置默认的系统上下文
*
* @param operation 操作
*/
public static void executeWithContext(Runnable operation) {
boolean hasContext = UserContextHolder.hasUserContext();
try {
// 如果没有用户上下文,设置默认的系统上下文
if (!hasContext) {
UserContextHolder.setUserContext("system", "system", "SYSTEM", "127.0.0.1", "system");
log.debug("设置默认系统用户上下文");
}
// 执行操作
operation.run();
} finally {
// 如果是我们设置的默认上下文,执行后清理
if (!hasContext) {
UserContextHolder.clear();
log.debug("清理默认系统用户上下文");
}
public static String getRequestIdOrRandom() {
String requestId = UserContextHolder.getRequestId();
if (requestId != null) {
return requestId;
}
return java.util.UUID.randomUUID().toString().replace("-", "");
}
/**
* 临时设置用户上下文执行操作
* 执行带有临时上下文操作
*
* @param userId 用户ID
* @param username 用户名
* @param userType 用户类型
* @param operation 操作
* @param userId 用户ID
* @param username 用户名
* @param userType 用户类型
* @param operation 要执行的操作
*/
public static void executeWithTempContext(String userId, String username, String userType, Runnable operation) {
// 保存当前上下文
@@ -211,4 +188,59 @@ public class UserContextUtils {
}
}
}
}
/**
* 从HTTP请求中获取客户端真实IP地址
*
* @param request HTTP请求
* @return 客户端IP地址
*/
public static String getClientIpAddress(HttpServletRequest request) {
String ip = null;
// 1. 从X-Forwarded-For获取(经过代理的情况)
ip = request.getHeader("X-Forwarded-For");
if (StringUtils.hasText(ip) && !"unknown".equalsIgnoreCase(ip)) {
// 多个IP的情况,取第一个
int index = ip.indexOf(',');
if (index != -1) {
ip = ip.substring(0, index);
}
return ip.trim();
}
// 2. 从X-Real-IP获取
ip = request.getHeader("X-Real-IP");
if (StringUtils.hasText(ip) && !"unknown".equalsIgnoreCase(ip)) {
return ip.trim();
}
// 3. 从Proxy-Client-IP获取
ip = request.getHeader("Proxy-Client-IP");
if (StringUtils.hasText(ip) && !"unknown".equalsIgnoreCase(ip)) {
return ip.trim();
}
// 4. 从WL-Proxy-Client-IP获取
ip = request.getHeader("WL-Proxy-Client-IP");
if (StringUtils.hasText(ip) && !"unknown".equalsIgnoreCase(ip)) {
return ip.trim();
}
// 5. 从HTTP_CLIENT_IP获取
ip = request.getHeader("HTTP_CLIENT_IP");
if (StringUtils.hasText(ip) && !"unknown".equalsIgnoreCase(ip)) {
return ip.trim();
}
// 6. 从HTTP_X_FORWARDED_FOR获取
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
if (StringUtils.hasText(ip) && !"unknown".equalsIgnoreCase(ip)) {
return ip.trim();
}
// 7. 最后从getRemoteAddr获取
ip = request.getRemoteAddr();
return StringUtils.hasText(ip) ? ip.trim() : "unknown";
}
}