feat: 完善后端架构 - 标准化Controller层和Service层

 新功能:
- 创建了完整的Service层架构,包含所有业务实体的Service接口和实现类
- 新增8个标准化的Controller类,支持完整的CRUD操作
- 实现了统一的Request/Response模式和分页查询功能
- 创建了认证服务(AuthService)和令牌服务(TokenService)
- 添加了Redis配置和认证拦截器

🏗️ 架构优化:
- 移除Controller层所有try-catch块,使用全局异常处理机制
- 创建了专门的异常类(AuthException, TokenException, CaptchaException)
- 统一了API返回格式,完善了Result类的方法
- 实现了标准的分页查询和参数校验

📦 新增文件:
- 8个Controller类: Achievement, Comment, CommunityPost, Conversation, CozeApiCall, EmotionAnalysis, Reward, UserStats
- 12个Service接口和对应的实现类
- 标准化的DTO类(Request/Response)
- 异常处理类和拦截器
- 测试用例

🔧 重构优化:
- 重写了AuthController,移除所有业务逻辑到Service层
- 优化了MessageController,使用标准的Request/Response格式
- 更新了全局异常处理器,支持多种异常类型
- 完善了WebConfig配置,添加认证拦截器

📊 代码统计:
- 新增文件: 60+个
- 新增代码行数: 8000+行
- 重构代码行数: 1000+行
- 移除过时接口: 4个
This commit is contained in:
2025-07-24 07:38:40 +08:00
parent 880e0e3c88
commit 873b8e55da
67 changed files with 8619 additions and 850 deletions
@@ -0,0 +1,227 @@
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.service.AchievementService;
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;
/**
* 成就控制器
*
* @author emotion-museum
* @date 2025-07-23
*/
@RestController
@RequestMapping("/achievement")
public class AchievementController {
@Autowired
private AchievementService achievementService;
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
/**
* 分页查询成就
*/
@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);
return Result.success(pageResult);
}
/**
* 根据ID获取成就
*/
@GetMapping("/{id}")
public Result<AchievementResponse> getById(@PathVariable String id) {
Achievement achievement = achievementService.getById(id);
if (achievement == null) {
return Result.notFound("成就不存在");
}
return Result.success(convertToResponse(achievement));
}
/**
* 创建成就
*/
@PostMapping
public Result<AchievementResponse> create(@RequestBody Achievement achievement) {
boolean saved = achievementService.save(achievement);
if (!saved) {
return Result.error("创建失败");
}
return Result.success(convertToResponse(achievement));
}
/**
* 更新成就
*/
@PutMapping("/{id}")
public Result<AchievementResponse> update(@PathVariable String id, @RequestBody Achievement achievement) {
achievement.setId(id);
boolean updated = achievementService.updateById(achievement);
if (!updated) {
return Result.error("更新失败");
}
Achievement updatedAchievement = achievementService.getById(id);
return Result.success(convertToResponse(updatedAchievement));
}
/**
* 删除成就
*/
@DeleteMapping("/{id}")
public Result<Void> delete(@PathVariable String id) {
boolean deleted = achievementService.removeById(id);
if (!deleted) {
return Result.error("删除失败");
}
return Result.success();
}
/**
* 根据分类查询成就
*/
@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());
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());
return Result.success(responses);
}
/**
* 查询已解锁的成就
*/
@GetMapping("/unlocked")
public Result<List<AchievementResponse>> getUnlockedAchievements() {
List<Achievement> achievements = achievementService.getUnlockedAchievements();
List<AchievementResponse> responses = achievements.stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
return Result.success(responses);
}
/**
* 查询未解锁的成就
*/
@GetMapping("/locked")
public Result<List<AchievementResponse>> getLockedAchievements() {
List<Achievement> achievements = achievementService.getLockedAchievements();
List<AchievementResponse> responses = achievements.stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
return Result.success(responses);
}
/**
* 统计已解锁成就数量
*/
@GetMapping("/count/unlocked")
public Result<Long> countUnlockedAchievements() {
Long count = achievementService.countUnlockedAchievements();
return Result.success(count);
}
/**
* 统计未解锁成就数量
*/
@GetMapping("/count/locked")
public Result<Long> countLockedAchievements() {
Long count = achievementService.countLockedAchievements();
return Result.success(count);
}
/**
* 解锁成就
*/
@PutMapping("/{id}/unlock")
public Result<Void> unlockAchievement(@PathVariable String id) {
boolean unlocked = achievementService.unlockAchievement(id, java.time.LocalDateTime.now());
if (!unlocked) {
return Result.error("解锁失败");
}
return Result.success();
}
/**
* 更新成就进度
*/
@PutMapping("/{id}/progress")
public Result<Void> updateProgress(@PathVariable String id, @RequestParam Double progress) {
boolean updated = achievementService.updateProgress(id, progress);
if (!updated) {
return Result.error("更新进度失败");
}
return Result.success();
}
/**
* 转换为响应对象
*/
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;
}
/**
* 成就响应类
*/
@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;
}
}
@@ -1,9 +1,9 @@
package com.emotion.controller;
import com.emotion.common.Result;
import com.emotion.service.IAiService;
import com.emotion.service.IMessageService;
import com.emotion.service.IConversationService;
import com.emotion.service.AiService;
import com.emotion.service.MessageService;
import com.emotion.service.ConversationService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@@ -23,13 +23,13 @@ import java.util.Map;
public class AiChatController {
@Autowired
private IAiService aiService;
private AiService aiService;
@Autowired
private IMessageService messageService;
private MessageService messageService;
@Autowired
private IConversationService conversationService;
private ConversationService conversationService;
/**
* 发送聊天消息
@@ -1,293 +1,130 @@
package com.emotion.controller;
import com.emotion.common.Result;
import com.emotion.entity.User;
import com.emotion.service.UserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.emotion.dto.request.LoginRequest;
import com.emotion.dto.request.RegisterRequest;
import com.emotion.dto.response.AuthResponse;
import com.emotion.dto.response.CaptchaResponse;
import com.emotion.dto.response.UserInfoResponse;
import com.emotion.service.AuthService;
import com.emotion.service.TokenService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import com.wf.captcha.SpecCaptcha;
import com.wf.captcha.base.Captcha;
import com.emotion.util.JwtUtil;
import javax.servlet.http.HttpServletRequest;
/**
* 认证控制器
*
*
* @author emotion-museum
* @date 2025-07-22
* @date 2025-07-23
*/
@RestController
@RequestMapping("/auth")
public class AuthController {
private static final Logger log = LoggerFactory.getLogger(AuthController.class);
// 验证码存储(生产环境应使用Redis)
private static final Map<String, String> captchaStore = new ConcurrentHashMap<>();
@Autowired
private AuthService authService;
@Autowired
private UserService userService;
@Autowired
private JwtUtil jwtUtil;
private TokenService tokenService;
/**
* 用户登录
*/
@PostMapping("/login")
public Result<Map<String, Object>> login(@RequestBody Map<String, String> request) {
log.info("用户登录请求: {}", request.get("account"));
try {
String account = request.get("account");
String password = request.get("password");
String captcha = request.get("captcha");
String captchaKey = request.get("captchaKey");
if (account == null || password == null) {
return Result.error("账号和密码不能为空");
}
// 验证验证码
if (captcha == null || captchaKey == null) {
return Result.error("验证码不能为空");
}
String storedCaptcha = captchaStore.get(captchaKey);
if (storedCaptcha == null) {
return Result.error("验证码已过期");
}
if (!storedCaptcha.equals(captcha.toLowerCase())) {
captchaStore.remove(captchaKey); // 验证失败后移除验证码
return Result.error("验证码错误");
}
// 验证成功后移除验证码
captchaStore.remove(captchaKey);
// 查找用户
User user = userService.findByAccount(account);
if (user == null) {
return Result.error("用户不存在");
}
// 验证密码
if (!userService.validatePassword(password, user.getPassword())) {
return Result.error("密码错误");
}
// 更新最后活跃时间
userService.updateLastActiveTime(user.getId());
// 生成JWT token
String accessToken = jwtUtil.generateToken(user.getId(), user.getUsername());
String refreshToken = jwtUtil.generateRefreshToken(user.getId(), user.getUsername());
// 构建响应
Map<String, Object> response = new HashMap<>();
response.put("accessToken", accessToken);
response.put("refreshToken", refreshToken);
response.put("expiresIn", 86400L);
Map<String, Object> userInfo = new HashMap<>();
userInfo.put("id", user.getId());
userInfo.put("username", user.getUsername());
userInfo.put("account", user.getAccount());
userInfo.put("nickname", user.getNickname());
userInfo.put("avatar", user.getAvatar());
userInfo.put("status", user.getStatus());
response.put("userInfo", userInfo);
response.put("loginTime", LocalDateTime.now());
return Result.success("登录成功", response);
} catch (Exception e) {
log.error("用户登录失败: {}", e.getMessage());
return Result.error("登录失败: " + e.getMessage());
}
public Result<AuthResponse> login(@RequestBody @Validated LoginRequest request) {
AuthResponse response = authService.login(request);
return Result.success("登录成功", response);
}
/**
* 用户注册
*/
@PostMapping("/register")
public Result<Map<String, Object>> register(@RequestBody Map<String, String> request) {
log.info("用户注册请求: {}", request.get("account"));
try {
String account = request.get("account");
String password = request.get("password");
String username = request.get("username");
String email = request.get("email");
String phone = request.get("phone");
String nickname = request.get("nickname");
String captcha = request.get("captcha");
String captchaKey = request.get("captchaKey");
if (account == null || password == null) {
return Result.error("账号和密码不能为空");
}
// 验证验证码
if (captcha == null || captchaKey == null) {
return Result.error("验证码不能为空");
}
String storedCaptcha = captchaStore.get(captchaKey);
if (storedCaptcha == null) {
return Result.error("验证码已过期");
}
if (!storedCaptcha.equals(captcha.toLowerCase())) {
captchaStore.remove(captchaKey); // 验证失败后移除验证码
return Result.error("验证码错误");
}
// 验证成功后移除验证码
captchaStore.remove(captchaKey);
// 检查账号是否已存在
if (userService.accountExists(account)) {
return Result.error("账号已存在");
}
// 创建用户
User user = new User();
user.setAccount(account);
user.setPassword(password);
user.setUsername(username != null ? username : account);
user.setEmail(email);
user.setPhone(phone);
user.setNickname(nickname != null ? nickname : username != null ? username : account);
User createdUser = userService.createUser(user);
// 生成JWT token(注册成功后自动登录)
String accessToken = jwtUtil.generateToken(createdUser.getId(), createdUser.getUsername());
String refreshToken = jwtUtil.generateRefreshToken(createdUser.getId(), createdUser.getUsername());
// 构建用户信息
Map<String, Object> userInfo = new HashMap<>();
userInfo.put("id", createdUser.getId());
userInfo.put("username", createdUser.getUsername());
userInfo.put("account", createdUser.getAccount());
userInfo.put("nickname", createdUser.getNickname());
userInfo.put("avatar", createdUser.getAvatar());
userInfo.put("status", createdUser.getStatus());
userInfo.put("createTime", createdUser.getCreateTime());
// 构建完整响应(包含token信息)
Map<String, Object> response = new HashMap<>();
response.put("accessToken", accessToken);
response.put("refreshToken", refreshToken);
response.put("expiresIn", 86400L); // 24小时
response.put("userInfo", userInfo);
response.put("loginTime", LocalDateTime.now());
log.info("用户注册并自动登录成功: {}", createdUser.getAccount());
return Result.success("注册成功,已自动登录", response);
} catch (Exception e) {
log.error("用户注册失败: {}", e.getMessage());
return Result.error("注册失败: " + e.getMessage());
}
public Result<AuthResponse> register(@RequestBody @Validated RegisterRequest request) {
AuthResponse response = authService.register(request);
return Result.success("注册成功", response);
}
/**
* 获取当前用户信息
*/
@GetMapping("/user-info")
public Result<Map<String, Object>> getCurrentUserInfo(HttpServletRequest request) {
try {
// 从请求属性中获取用户信息(由JWT拦截器设置)
String userId = (String) request.getAttribute("userId");
String username = (String) request.getAttribute("username");
if (userId == null) {
return Result.error("用户未登录");
}
// 根据用户ID获取完整用户信息
User user = userService.findById(userId);
if (user == null) {
return Result.error("用户不存在");
}
// 构建用户信息响应
Map<String, Object> userInfo = new HashMap<>();
userInfo.put("id", user.getId());
userInfo.put("username", user.getUsername());
userInfo.put("account", user.getAccount());
userInfo.put("nickname", user.getNickname());
userInfo.put("avatar", user.getAvatar());
userInfo.put("email", user.getEmail());
userInfo.put("phone", user.getPhone());
userInfo.put("status", user.getStatus());
userInfo.put("createTime", user.getCreateTime());
return Result.success("获取用户信息成功", userInfo);
} catch (Exception e) {
log.error("获取用户信息失败: {}", e.getMessage());
return Result.error("获取用户信息失败");
}
@GetMapping("/user/info")
public Result<UserInfoResponse> getCurrentUserInfo(HttpServletRequest request) {
String token = extractToken(request);
UserInfoResponse userInfo = tokenService.getUserInfoByToken(token);
return Result.success(userInfo);
}
/**
* 获取验证码
* 生成验证码
*/
@GetMapping("/captcha")
public Result<Map<String, Object>> getCaptcha() {
log.info("获取验证码请求");
try {
// 生成验证码
SpecCaptcha captcha = new SpecCaptcha(130, 48, 4);
captcha.setCharType(Captcha.TYPE_DEFAULT);
// 生成验证码key
String captchaKey = "captcha_" + System.currentTimeMillis();
String captchaText = captcha.text().toLowerCase();
// 存储验证码(5分钟过期)
captchaStore.put(captchaKey, captchaText);
// 5分钟后清理验证码
new Thread(() -> {
try {
Thread.sleep(300000); // 5分钟
captchaStore.remove(captchaKey);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
Map<String, Object> response = new HashMap<>();
response.put("key", captchaKey);
response.put("image", captcha.toBase64().replace("data:image/png;base64,", ""));
response.put("expireTime", 300);
log.info("生成验证码成功,key: {}, text: {}", captchaKey, captchaText);
return Result.success("获取验证码成功", response);
} catch (Exception e) {
log.error("获取验证码失败: {}", e.getMessage());
return Result.error("获取验证码失败");
}
public Result<CaptchaResponse> generateCaptcha() {
CaptchaResponse response = authService.generateCaptcha();
return Result.success(response);
}
/**
* 用户登出
*/
@PostMapping("/logout")
public Result<String> logout(@RequestBody Map<String, String> request) {
log.info("用户登出请求: {}", request.get("userId"));
return Result.success("登出成功");
public Result<Void> logout(HttpServletRequest request) {
String token = extractToken(request);
authService.logoutByToken(token);
return Result.success();
}
}
/**
* 刷新访问令牌
*/
@PostMapping("/refresh")
public Result<AuthResponse> refreshToken(@RequestParam String refreshToken) {
AuthResponse response = authService.refreshToken(refreshToken);
return Result.success("令牌刷新成功", response);
}
/**
* 验证访问令牌
*/
@GetMapping("/validate")
public Result<Boolean> validateToken(HttpServletRequest request) {
String token = extractToken(request);
if (token == null) {
return Result.success(false);
}
boolean isValid = authService.validateToken(token);
return Result.success(isValid);
}
/**
* 获取用户名(通过令牌)
*/
@GetMapping("/username")
public Result<String> getUsernameFromToken(HttpServletRequest request) {
String token = extractToken(request);
String username = tokenService.getUsernameByToken(token);
return Result.success(username);
}
/**
* 从请求中提取访问令牌
*/
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;
}
}
@@ -0,0 +1,249 @@
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.service.CommentService;
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;
/**
* 评论控制器
*
* @author emotion-museum
* @date 2025-07-23
*/
@RestController
@RequestMapping("/comment")
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);
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));
}
/**
* 更新评论
*/
@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));
}
/**
* 删除评论
*/
@DeleteMapping("/{id}")
public Result<Void> delete(@PathVariable String id) {
boolean deleted = commentService.removeById(id);
if (!deleted) {
return Result.error("删除失败");
}
return Result.success();
}
/**
* 根据帖子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("点赞失败");
}
return Result.success();
}
/**
* 取消点赞评论
*/
@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;
}
}
@@ -0,0 +1,289 @@
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.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;
/**
* 社区帖子控制器
*
* @author emotion-museum
* @date 2025-07-23
*/
@RestController
@RequestMapping("/community-post")
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);
return Result.success(pageResult);
}
/**
* 根据ID获取帖子
*/
@GetMapping("/{id}")
public Result<CommunityPostResponse> getById(@PathVariable String id) {
CommunityPost post = communityPostService.getById(id);
if (post == null) {
return Result.notFound("帖子不存在");
}
// 增加浏览数
communityPostService.incrementViewCount(id);
return Result.success(convertToResponse(post));
}
/**
* 创建帖子
*/
@PostMapping
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));
}
/**
* 更新帖子
*/
@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));
}
/**
* 删除帖子
*/
@DeleteMapping("/{id}")
public Result<Void> delete(@PathVariable String id) {
boolean deleted = communityPostService.removeById(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;
}
}
@@ -0,0 +1,283 @@
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.Conversation;
import com.emotion.service.ConversationService;
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.servlet.http.HttpServletRequest;
import javax.validation.constraints.NotBlank;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.stream.Collectors;
/**
* 对话控制器
*
* @author emotion-museum
* @date 2025-07-23
*/
@RestController
@RequestMapping("/conversation")
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(@Validated 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, @Validated 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);
return Result.success(pageResult);
}
/**
* 根据ID获取对话
*/
@GetMapping("/{id}")
public Result<ConversationResponse> getById(@PathVariable String id) {
Conversation conversation = conversationService.getById(id);
if (conversation == null) {
return Result.notFound("对话不存在");
}
return Result.success(convertToResponse(conversation));
}
/**
* 创建对话
*/
@PostMapping
public Result<ConversationResponse> create(@RequestBody @Validated ConversationCreateRequest request, HttpServletRequest httpRequest) {
String clientIp = getClientIp(httpRequest);
Conversation conversation = conversationService.createConversation(
request.getUserId(),
request.getTitle(),
request.getType(),
clientIp
);
return Result.success(convertToResponse(conversation));
}
/**
* 更新对话
*/
@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));
}
/**
* 删除对话
*/
@DeleteMapping("/{id}")
public Result<Void> delete(@PathVariable String id) {
boolean deleted = conversationService.removeById(id);
if (!deleted) {
return Result.error("删除失败");
}
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("/type/{type}")
public Result<List<ConversationResponse>> getByType(@PathVariable String type) {
List<Conversation> conversations = conversationService.getByType(type);
List<ConversationResponse> responses = conversations.stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
return Result.success(responses);
}
/**
* 根据状态查询对话
*/
@GetMapping("/status/{status}")
public Result<List<ConversationResponse>> getByStatus(@PathVariable String status) {
List<Conversation> conversations = conversationService.getByStatus(status);
List<ConversationResponse> responses = conversations.stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
return Result.success(responses);
}
/**
* 查询活跃对话
*/
@GetMapping("/active")
public Result<List<ConversationResponse>> getActiveConversations() {
List<Conversation> conversations = conversationService.getActiveConversations();
List<ConversationResponse> responses = conversations.stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
return Result.success(responses);
}
/**
* 查询已归档对话
*/
@GetMapping("/archived")
public Result<List<ConversationResponse>> getArchivedConversations() {
List<Conversation> conversations = conversationService.getArchivedConversations();
List<ConversationResponse> responses = conversations.stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
return Result.success(responses);
}
/**
* 归档对话
*/
@PutMapping("/{id}/archive")
public Result<Void> archiveConversation(@PathVariable String id) {
boolean archived = conversationService.archiveConversation(id);
if (!archived) {
return Result.error("归档失败");
}
return Result.success();
}
/**
* 激活对话
*/
@PutMapping("/{id}/activate")
public Result<Void> activateConversation(@PathVariable String id) {
boolean activated = conversationService.activateConversation(id);
if (!activated) {
return Result.error("激活失败");
}
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));
}
if (conversation.getLastMessageTime() != null) {
response.setLastMessageTime(conversation.getLastMessageTime().format(DATE_TIME_FORMATTER));
}
return response;
}
/**
* 对话创建请求
*/
@lombok.Data
public static class ConversationCreateRequest {
@NotBlank(message = "用户ID不能为空")
private String userId;
private String title;
private String type;
}
/**
* 对话响应类
*/
@lombok.Data
@lombok.EqualsAndHashCode(callSuper = true)
public static class ConversationResponse extends BaseResponse {
private String userId;
private String cozeConversationId;
private String title;
private String type;
private String status;
private Integer messageCount;
private String lastMessageTime;
private String clientIp;
}
}
@@ -0,0 +1,307 @@
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.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;
/**
* Coze API调用记录控制器
*
* @author emotion-museum
* @date 2025-07-23
*/
@RestController
@RequestMapping("/coze-api-call")
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);
return Result.success(pageResult);
}
/**
* 根据ID获取API调用记录
*/
@GetMapping("/{id}")
public Result<CozeApiCallResponse> getById(@PathVariable String id) {
CozeApiCall apiCall = cozeApiCallService.getById(id);
if (apiCall == 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));
}
/**
* 创建API调用记录
*/
@PostMapping
public Result<CozeApiCallResponse> create(@RequestBody CozeApiCall apiCall) {
boolean saved = cozeApiCallService.save(apiCall);
if (!saved) {
return Result.error("创建失败");
}
return Result.success(convertToResponse(apiCall));
}
/**
* 更新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("更新失败");
}
CozeApiCall updatedApiCall = cozeApiCallService.getById(id);
return Result.success(convertToResponse(updatedApiCall));
}
/**
* 删除API调用记录
*/
@DeleteMapping("/{id}")
public Result<Void> delete(@PathVariable String id) {
boolean deleted = cozeApiCallService.removeById(id);
if (!deleted) {
return Result.error("删除失败");
}
return Result.success();
}
/**
* 转换为响应对象
*/
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;
}
/**
* 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;
}
}
@@ -0,0 +1,287 @@
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.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.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;
/**
* 情绪分析控制器
*
* @author emotion-museum
* @date 2025-07-23
*/
@RestController
@RequestMapping("/emotion-analysis")
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);
}
/**
* 根据用户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);
}
/**
* 根据ID获取情绪分析记录
*/
@GetMapping("/{id}")
public Result<EmotionAnalysisResponse> getById(@PathVariable String id) {
EmotionAnalysis analysis = emotionAnalysisService.getById(id);
if (analysis == null) {
return Result.notFound("情绪分析记录不存在");
}
return Result.success(convertToResponse(analysis));
}
/**
* 根据消息ID获取情绪分析记录
*/
@GetMapping("/message/{messageId}")
public Result<EmotionAnalysisResponse> getByMessageId(@PathVariable String messageId) {
EmotionAnalysis analysis = emotionAnalysisService.getByMessageId(messageId);
if (analysis == null) {
return Result.notFound("情绪分析记录不存在");
}
return Result.success(convertToResponse(analysis));
}
/**
* 创建情绪分析记录
*/
@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));
}
/**
* 更新情绪分析记录
*/
@PutMapping("/{id}")
public Result<EmotionAnalysisResponse> update(@PathVariable String id, @RequestBody EmotionAnalysis analysis) {
analysis.setId(id);
boolean updated = emotionAnalysisService.updateById(analysis);
if (!updated) {
return Result.error("更新失败");
}
EmotionAnalysis updatedAnalysis = emotionAnalysisService.getById(id);
return Result.success(convertToResponse(updatedAnalysis));
}
/**
* 删除情绪分析记录
*/
@DeleteMapping("/{id}")
public Result<Void> delete(@PathVariable String id) {
boolean deleted = emotionAnalysisService.removeById(id);
if (!deleted) {
return Result.error("删除失败");
}
return Result.success();
}
/**
* 根据主要情绪查询分析记录
*/
@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());
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());
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());
return Result.success(responses);
}
/**
* 根据时间范围查询用户情绪分析记录
*/
@GetMapping("/user/{userId}/time-range")
public Result<List<EmotionAnalysisResponse>> getByUserIdAndTimeRange(
@PathVariable 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());
return Result.success(responses);
}
/**
* 统计用户的情绪分析记录数量
*/
@GetMapping("/user/{userId}/count")
public Result<Long> countByUserId(@PathVariable String userId) {
Long count = emotionAnalysisService.countByUserId(userId);
return Result.success(count);
}
/**
* 查询用户最近的情绪分析记录
*/
@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());
return Result.success(responses);
}
/**
* 查询用户的平均情绪强度
*/
@GetMapping("/user/{userId}/avg-intensity")
public Result<Double> getAvgIntensityByUserId(@PathVariable String userId) {
Double avgIntensity = emotionAnalysisService.getAvgIntensityByUserId(userId);
return Result.success(avgIntensity);
}
/**
* 查询用户最常见的情绪类型
*/
@GetMapping("/user/{userId}/most-frequent-emotion")
public Result<String> getMostFrequentEmotionByUserId(@PathVariable 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,220 +1,173 @@
package com.emotion.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
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.Message;
import com.emotion.service.IMessageService;
import lombok.extern.slf4j.Slf4j;
import com.emotion.service.MessageService;
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.LocalDateTime;
import java.util.HashMap;
import javax.validation.constraints.NotBlank;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* 消息控制器
*
*
* @author emotion-museum
* @date 2025-07-23
*/
@Slf4j
@RestController
@RequestMapping("/api/message")
@RequestMapping("/message")
public class MessageController {
@Autowired
private IMessageService messageService;
private MessageService messageService;
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
/**
* 保存消息
* 分页查询消息
*/
@PostMapping("/save")
public Result<Message> saveMessage(@RequestBody Map<String, String> request) {
try {
String conversationId = request.get("conversationId");
String content = request.get("content");
String type = request.get("type");
String sender = request.get("sender");
if (content == null || content.trim().isEmpty()) {
return Result.error("消息内容不能为空");
}
if (sender == null || sender.trim().isEmpty()) {
return Result.error("发送者不能为空");
}
Message message = messageService.saveMessage(conversationId, content, type, sender);
if (message != null) {
return Result.success(message);
} else {
return Result.error("保存消息失败");
}
} catch (Exception e) {
log.error("保存消息失败", e);
return Result.error("保存消息失败:" + e.getMessage());
}
@GetMapping("/page")
public Result<PageResult<MessageResponse>> getPage(@Validated PageRequest request) {
IPage<Message> page = messageService.getPage(request);
List<MessageResponse> responses = page.getRecords().stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
PageResult<MessageResponse> pageResult = new PageResult<>();
pageResult.setCurrent(page.getCurrent());
pageResult.setSize(page.getSize());
pageResult.setTotal(page.getTotal());
pageResult.setPages(page.getPages());
pageResult.setRecords(responses);
return Result.success(pageResult);
}
/**
* 根据会话ID分页查询消息
*/
@GetMapping("/conversation/{conversationId}/page")
public Result<PageResult<MessageResponse>> getPageByConversationId(@PathVariable String conversationId, @Validated PageRequest request) {
IPage<Message> page = messageService.getPageByConversationId(request, conversationId);
List<MessageResponse> responses = page.getRecords().stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
PageResult<MessageResponse> pageResult = new PageResult<>();
pageResult.setCurrent(page.getCurrent());
pageResult.setSize(page.getSize());
pageResult.setTotal(page.getTotal());
pageResult.setPages(page.getPages());
pageResult.setRecords(responses);
return Result.success(pageResult);
}
/**
* 根据ID获取消息
*/
@GetMapping("/{id}")
public Result<MessageResponse> getById(@PathVariable String id) {
Message message = messageService.getById(id);
if (message == null) {
return Result.notFound("消息不存在");
}
return Result.success(convertToResponse(message));
}
/**
* 创建消息
*/
@PostMapping
public Result<MessageResponse> create(@RequestBody @Validated MessageCreateRequest request) {
Message message = messageService.createMessage(
request.getConversationId(),
request.getUserId(),
request.getContent(),
request.getContentType(),
request.getSenderType(),
request.getSenderId()
);
return Result.success(convertToResponse(message));
}
/**
* 根据会话ID查询消息
*/
@GetMapping("/conversation/{conversationId}")
public Result<IPage<Message>> getMessagesByConversationId(
@PathVariable String conversationId,
@RequestParam(defaultValue = "1") Integer current,
@RequestParam(defaultValue = "20") Integer size) {
try {
Page<Message> page = new Page<>(current, size);
IPage<Message> result = messageService.getByConversationId(page, conversationId);
return Result.success(result);
} catch (Exception e) {
log.error("查询会话消息失败", e);
return Result.error("查询会话消息失败:" + e.getMessage());
}
public Result<List<MessageResponse>> getByConversationId(@PathVariable String conversationId) {
List<Message> messages = messageService.getByConversationId(conversationId);
List<MessageResponse> responses = messages.stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
return Result.success(responses);
}
/**
* 根据发送者分页查询消息
*/
@GetMapping("/sender/{sender}")
public Result<IPage<Message>> getMessagesBySender(
@PathVariable String sender,
@RequestParam(defaultValue = "1") Integer current,
@RequestParam(defaultValue = "20") Integer size) {
try {
Page<Message> page = new Page<>(current, size);
IPage<Message> result = messageService.getBySender(page, sender);
return Result.success(result);
} catch (Exception e) {
log.error("查询发送者消息失败", e);
return Result.error("查询发送者消息失败:" + e.getMessage());
}
}
/**
* 查询会话的最后一条消息
*/
@GetMapping("/last/{conversationId}")
public Result<Message> getLastMessage(@PathVariable String conversationId) {
try {
Message message = messageService.getLastMessageByConversationId(conversationId);
return Result.success(message);
} catch (Exception e) {
log.error("查询最后一条消息失败", e);
return Result.error("查询最后一条消息失败:" + e.getMessage());
}
}
/**
* 统计会话消息数量
*/
@GetMapping("/count/{conversationId}")
public Result<Long> countMessages(@PathVariable String conversationId) {
try {
Long count = messageService.countByConversationId(conversationId);
return Result.success(count);
} catch (Exception e) {
log.error("统计消息数量失败", e);
return Result.error("统计消息数量失败:" + e.getMessage());
}
@GetMapping("/conversation/{conversationId}/count")
public Result<Long> countByConversationId(@PathVariable String conversationId) {
Long count = messageService.countByConversationId(conversationId);
return Result.success(count);
}
/**
* 更新消息状态
* 转换为响应对象
*/
@PutMapping("/{messageId}/status")
public Result<Boolean> updateStatus(@PathVariable String messageId,
@RequestBody Map<String, String> request) {
try {
String status = request.get("status");
if (status == null || status.trim().isEmpty()) {
return Result.error("状态不能为空");
}
boolean success = messageService.updateStatus(messageId, status);
return Result.success(success);
} catch (Exception e) {
log.error("更新消息状态失败", e);
return Result.error("更新消息状态失败:" + e.getMessage());
private MessageResponse convertToResponse(Message message) {
MessageResponse response = new MessageResponse();
BeanUtils.copyProperties(message, response);
response.setId(message.getId());
if (message.getCreateTime() != null) {
response.setCreateTime(message.getCreateTime().format(DATE_TIME_FORMATTER));
}
if (message.getUpdateTime() != null) {
response.setUpdateTime(message.getUpdateTime().format(DATE_TIME_FORMATTER));
}
return response;
}
/**
* 标记消息为已读
* 消息创建请求
*/
@PutMapping("/{messageId}/read")
public Result<Boolean> markAsRead(@PathVariable String messageId) {
try {
boolean success = messageService.updateReadStatus(messageId, 1);
return Result.success(success);
} catch (Exception e) {
log.error("标记消息已读失败", e);
return Result.error("标记消息已读失败:" + e.getMessage());
}
@lombok.Data
public static class MessageCreateRequest {
@NotBlank(message = "会话ID不能为空")
private String conversationId;
@NotBlank(message = "用户ID不能为空")
private String userId;
@NotBlank(message = "消息内容不能为空")
private String content;
private String contentType;
private String senderType;
private String senderId;
}
/**
* 批量标记会话消息为已读
* 消息响应类
*/
@PutMapping("/conversation/{conversationId}/read")
public Result<Boolean> markConversationAsRead(@PathVariable String conversationId) {
try {
boolean success = messageService.markConversationMessagesAsRead(conversationId);
return Result.success(success);
} catch (Exception e) {
log.error("批量标记消息已读失败", e);
return Result.error("批量标记消息已读失败:" + e.getMessage());
}
@lombok.Data
@lombok.EqualsAndHashCode(callSuper = true)
public static class MessageResponse extends BaseResponse {
private String conversationId;
private String content;
private String type;
private String sender;
private Integer isRead;
private String aiReply;
private String emotionAnalysis;
}
/**
* 获取消息统计信息
*/
@GetMapping("/stats")
public Result<Map<String, Object>> getMessageStats(
@RequestParam(required = false) String conversationId,
@RequestParam(required = false) String sender) {
try {
Map<String, Object> stats = new HashMap<>();
if (conversationId != null && !conversationId.trim().isEmpty()) {
Long conversationCount = messageService.countByConversationId(conversationId);
stats.put("conversationMessageCount", conversationCount);
Message lastMessage = messageService.getLastMessageByConversationId(conversationId);
stats.put("lastMessage", lastMessage);
}
if (sender != null && !sender.trim().isEmpty()) {
Long senderCount = messageService.countBySender(sender);
stats.put("senderMessageCount", senderCount);
}
stats.put("timestamp", System.currentTimeMillis());
return Result.success(stats);
} catch (Exception e) {
log.error("获取消息统计失败", e);
return Result.error("获取消息统计失败:" + e.getMessage());
}
}
/**
* 删除会话的所有消息
*/
@DeleteMapping("/conversation/{conversationId}")
public Result<Boolean> deleteConversationMessages(@PathVariable String conversationId) {
try {
boolean success = messageService.deleteByConversationId(conversationId);
return Result.success(success);
} catch (Exception e) {
log.error("删除会话消息失败", e);
return Result.error("删除会话消息失败:" + e.getMessage());
}
}
}
}
@@ -0,0 +1,332 @@
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.Reward;
import com.emotion.service.RewardService;
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.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;
/**
* 奖励控制器
*
* @author emotion-museum
* @date 2025-07-23
*/
@RestController
@RequestMapping("/reward")
public class RewardController {
@Autowired
private RewardService rewardService;
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
/**
* 分页查询奖励
*/
@GetMapping("/page")
public Result<PageResult<RewardResponse>> getPage(@Validated PageRequest request) {
IPage<Reward> page = rewardService.getPage(request);
List<RewardResponse> responses = page.getRecords().stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
PageResult<RewardResponse> 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<RewardResponse>> getPageByUserId(@PathVariable String userId, @Validated PageRequest request) {
IPage<Reward> page = rewardService.getPageByUserId(request, userId);
List<RewardResponse> responses = page.getRecords().stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
PageResult<RewardResponse> 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<RewardResponse> getById(@PathVariable String id) {
Reward reward = rewardService.getById(id);
if (reward == null) {
return Result.notFound("奖励不存在");
}
return Result.success(convertToResponse(reward));
}
/**
* 创建奖励
*/
@PostMapping
public Result<RewardResponse> create(@RequestBody @Validated RewardCreateRequest request) {
Reward reward = rewardService.createReward(
request.getUserId(),
request.getRewardType(),
request.getPoints(),
request.getSource(),
request.getDescription(),
request.getExpiredTime()
);
return Result.success(convertToResponse(reward));
}
/**
* 更新奖励
*/
@PutMapping("/{id}")
public Result<RewardResponse> update(@PathVariable String id, @RequestBody Reward reward) {
reward.setId(id);
boolean updated = rewardService.updateById(reward);
if (!updated) {
return Result.error("更新失败");
}
Reward updatedReward = rewardService.getById(id);
return Result.success(convertToResponse(updatedReward));
}
/**
* 删除奖励
*/
@DeleteMapping("/{id}")
public Result<Void> delete(@PathVariable String id) {
boolean deleted = rewardService.removeById(id);
if (!deleted) {
return Result.error("删除失败");
}
return Result.success();
}
/**
* 根据用户ID查询奖励
*/
@GetMapping("/user/{userId}")
public Result<List<RewardResponse>> getByUserId(@PathVariable String userId) {
List<Reward> rewards = rewardService.getByUserId(userId);
List<RewardResponse> responses = rewards.stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
return Result.success(responses);
}
/**
* 根据奖励类型查询奖励
*/
@GetMapping("/type/{rewardType}")
public Result<List<RewardResponse>> getByRewardType(@PathVariable String rewardType) {
List<Reward> rewards = rewardService.getByRewardType(rewardType);
List<RewardResponse> responses = rewards.stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
return Result.success(responses);
}
/**
* 根据状态查询奖励
*/
@GetMapping("/status/{status}")
public Result<List<RewardResponse>> getByStatus(@PathVariable String status) {
List<Reward> rewards = rewardService.getByStatus(status);
List<RewardResponse> responses = rewards.stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
return Result.success(responses);
}
/**
* 查询用户待领取的奖励
*/
@GetMapping("/user/{userId}/pending")
public Result<List<RewardResponse>> getPendingRewardsByUserId(@PathVariable String userId) {
List<Reward> rewards = rewardService.getPendingRewardsByUserId(userId);
List<RewardResponse> responses = rewards.stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
return Result.success(responses);
}
/**
* 查询用户已领取的奖励
*/
@GetMapping("/user/{userId}/claimed")
public Result<List<RewardResponse>> getClaimedRewardsByUserId(@PathVariable String userId) {
List<Reward> rewards = rewardService.getClaimedRewardsByUserId(userId);
List<RewardResponse> responses = rewards.stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
return Result.success(responses);
}
/**
* 统计用户的奖励数量
*/
@GetMapping("/user/{userId}/count")
public Result<Long> countByUserId(@PathVariable String userId) {
Long count = rewardService.countByUserId(userId);
return Result.success(count);
}
/**
* 统计用户的总积分
*/
@GetMapping("/user/{userId}/total-points")
public Result<Integer> sumPointsByUserId(@PathVariable String userId) {
Integer totalPoints = rewardService.sumPointsByUserId(userId);
return Result.success(totalPoints);
}
/**
* 查询用户最近获得的奖励
*/
@GetMapping("/user/{userId}/recent")
public Result<List<RewardResponse>> getRecentByUserId(@PathVariable String userId, @RequestParam(defaultValue = "10") Integer limit) {
List<Reward> rewards = rewardService.getRecentByUserId(userId, limit);
List<RewardResponse> responses = rewards.stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
return Result.success(responses);
}
/**
* 领取奖励
*/
@PutMapping("/{id}/claim")
public Result<Void> claimReward(@PathVariable String id) {
boolean claimed = rewardService.updateStatus(id, "claimed", LocalDateTime.now());
if (!claimed) {
return Result.error("领取失败");
}
return Result.success();
}
/**
* 查询高积分奖励
*/
@GetMapping("/high-points")
public Result<List<RewardResponse>> getHighPointsRewards(@RequestParam(defaultValue = "100") Integer minPoints) {
List<Reward> rewards = rewardService.getHighPointsRewards(minPoints);
List<RewardResponse> responses = rewards.stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
return Result.success(responses);
}
/**
* 查询已过期的奖励
*/
@GetMapping("/expired")
public Result<List<RewardResponse>> getExpiredRewards() {
List<Reward> rewards = rewardService.getExpiredRewards();
List<RewardResponse> responses = rewards.stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
return Result.success(responses);
}
/**
* 批量更新过期奖励状态
*/
@PutMapping("/update-expired")
public Result<Void> updateExpiredRewards() {
boolean updated = rewardService.updateExpiredRewards();
if (!updated) {
return Result.error("更新失败");
}
return Result.success();
}
/**
* 转换为响应对象
*/
private RewardResponse convertToResponse(Reward reward) {
RewardResponse response = new RewardResponse();
BeanUtils.copyProperties(reward, response);
response.setId(reward.getId());
if (reward.getCreateTime() != null) {
response.setCreateTime(reward.getCreateTime().format(DATE_TIME_FORMATTER));
}
if (reward.getUpdateTime() != null) {
response.setUpdateTime(reward.getUpdateTime().format(DATE_TIME_FORMATTER));
}
if (reward.getEarnedTime() != null) {
response.setEarnedTime(reward.getEarnedTime().format(DATE_TIME_FORMATTER));
}
if (reward.getClaimedTime() != null) {
response.setClaimedTime(reward.getClaimedTime().format(DATE_TIME_FORMATTER));
}
if (reward.getExpiredTime() != null) {
response.setExpiredTime(reward.getExpiredTime().format(DATE_TIME_FORMATTER));
}
return response;
}
/**
* 奖励创建请求
*/
@lombok.Data
public static class RewardCreateRequest {
@NotBlank(message = "用户ID不能为空")
private String userId;
@NotBlank(message = "奖励类型不能为空")
private String rewardType;
@NotNull(message = "积分不能为空")
private Integer points;
private String source;
private String description;
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime expiredTime;
}
/**
* 奖励响应类
*/
@lombok.Data
@lombok.EqualsAndHashCode(callSuper = true)
public static class RewardResponse extends BaseResponse {
private String userId;
private String rewardType;
private Integer points;
private String source;
private String description;
private String status;
private String earnedTime;
private String claimedTime;
private String expiredTime;
}
}
@@ -1,18 +1,25 @@
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.UserCreateRequest;
import com.emotion.dto.response.UserResponse;
import com.emotion.entity.User;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.emotion.service.UserService;
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.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.stream.Collectors;
/**
* 用户控制器
*
*
* @author emotion-museum
* @date 2025-07-22
*/
@@ -20,100 +27,121 @@ import java.util.Map;
@RequestMapping("/user")
public class UserController {
private static final Logger log = LoggerFactory.getLogger(UserController.class);
@Autowired
private UserService userService;
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
/**
* 获取用户信息
* 分页查询用户
*/
@GetMapping("/info/{userId}")
public Result<Map<String, Object>> getUserInfo(@PathVariable String userId) {
log.info("获取用户信息: {}", userId);
try {
// 模拟用户信息
Map<String, Object> userInfo = new HashMap<>();
userInfo.put("id", userId);
userInfo.put("username", "user" + userId);
userInfo.put("account", "user" + userId);
userInfo.put("nickname", "用户" + userId);
userInfo.put("avatar", "https://example.com/avatar/" + userId + ".jpg");
userInfo.put("status", 1);
userInfo.put("memberLevel", "free");
userInfo.put("totalDays", 30);
userInfo.put("createTime", LocalDateTime.now().minusDays(30));
userInfo.put("lastActiveTime", LocalDateTime.now());
return Result.success(userInfo);
} catch (Exception e) {
log.error("获取用户信息失败: {}", e.getMessage());
return Result.error("获取用户信息失败");
}
@GetMapping("/page")
public Result<PageResult<UserResponse>> getPage(@Validated BasePageRequest request) {
IPage<User> page = userService.getPage(request);
List<UserResponse> userResponses = page.getRecords().stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
PageResult<UserResponse> pageResult = new PageResult<>();
pageResult.setCurrent(page.getCurrent());
pageResult.setSize(page.getSize());
pageResult.setTotal(page.getTotal());
pageResult.setPages(page.getPages());
pageResult.setRecords(userResponses);
return Result.success(pageResult);
}
/**
* 更新用户信息
* 根据ID获取用户信息
*/
@PutMapping("/info/{userId}")
public Result<Map<String, Object>> updateUserInfo(@PathVariable String userId,
@RequestBody Map<String, Object> request) {
log.info("更新用户信息: {}", userId);
try {
// 模拟更新用户信息
Map<String, Object> userInfo = new HashMap<>();
userInfo.put("id", userId);
userInfo.put("nickname", request.get("nickname"));
userInfo.put("avatar", request.get("avatar"));
userInfo.put("bio", request.get("bio"));
userInfo.put("gender", request.get("gender"));
userInfo.put("updateTime", LocalDateTime.now());
return Result.success("更新成功", userInfo);
} catch (Exception e) {
log.error("更新用户信息失败: {}", e.getMessage());
return Result.error("更新用户信息失败");
@GetMapping("/{id}")
public Result<UserResponse> getById(@PathVariable String id) {
User user = userService.getById(id);
if (user == null) {
return Result.notFound("用户不存在");
}
return Result.success(convertToResponse(user));
}
/**
* 更新最后活跃时间
* 创建用户
*/
@PostMapping("/active/{userId}")
public Result<String> updateLastActiveTime(@PathVariable String userId) {
log.info("更新最后活跃时间: {}", userId);
try {
// 模拟更新活跃时间
return Result.success("更新成功");
} catch (Exception e) {
log.error("更新最后活跃时间失败: {}", e.getMessage());
@PostMapping
public Result<UserResponse> create(@Validated @RequestBody UserCreateRequest request) {
User user = userService.createUser(
request.getAccount(),
request.getUsername(),
request.getPassword(),
request.getEmail(),
request.getPhone()
);
return Result.success(convertToResponse(user));
}
/**
* 更新用户
*/
@PutMapping("/{id}")
public Result<UserResponse> update(@PathVariable String id, @RequestBody User user) {
user.setId(id);
boolean updated = userService.updateById(user);
if (!updated) {
return Result.error("更新失败");
}
User updatedUser = userService.getById(id);
return Result.success(convertToResponse(updatedUser));
}
/**
* 获取用户统计信息
* 删除用户
*/
@GetMapping("/stats/{userId}")
public Result<Map<String, Object>> getUserStats(@PathVariable String userId) {
log.info("获取用户统计信息: {}", userId);
try {
Map<String, Object> stats = new HashMap<>();
stats.put("totalDays", 30);
stats.put("totalRecords", 45);
stats.put("totalConversations", 12);
stats.put("totalMessages", 156);
stats.put("selfAwareness", 75.5);
stats.put("emotionalResilience", 68.2);
stats.put("actionPower", 82.1);
stats.put("empathy", 79.3);
stats.put("lifeEnthusiasm", 85.7);
return Result.success(stats);
} catch (Exception e) {
log.error("获取用户统计信息失败: {}", e.getMessage());
return Result.error("获取统计信息失败");
@DeleteMapping("/{id}")
public Result<Void> delete(@PathVariable String id) {
boolean deleted = userService.removeById(id);
if (!deleted) {
return Result.error("删除失败");
}
return Result.success();
}
}
/**
* 根据账号查询用户
*/
@GetMapping("/account/{account}")
public Result<UserResponse> getByAccount(@PathVariable String account) {
User user = userService.getByAccount(account);
if (user == null) {
return Result.notFound("用户不存在");
}
return Result.success(convertToResponse(user));
}
/**
* 统计用户数量
*/
@GetMapping("/count/status/{status}")
public Result<Long> countByStatus(@PathVariable Integer status) {
Long count = userService.countByStatus(status);
return Result.success(count);
}
/**
* 转换为响应对象
*/
private UserResponse convertToResponse(User user) {
UserResponse response = new UserResponse();
BeanUtils.copyProperties(user, response);
response.setId(user.getId());
if (user.getCreateTime() != null) {
response.setCreateTime(user.getCreateTime().format(DATE_TIME_FORMATTER));
}
if (user.getUpdateTime() != null) {
response.setUpdateTime(user.getUpdateTime().format(DATE_TIME_FORMATTER));
}
if (user.getLastActiveTime() != null) {
response.setLastActiveTime(user.getLastActiveTime().format(DATE_TIME_FORMATTER));
}
return response;
}
}
@@ -0,0 +1,270 @@
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.UserStats;
import com.emotion.service.UserStatsService;
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 javax.validation.constraints.NotNull;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.stream.Collectors;
/**
* 用户统计控制器
*
* @author emotion-museum
* @date 2025-07-23
*/
@RestController
@RequestMapping("/user-stats")
public class UserStatsController {
@Autowired
private UserStatsService userStatsService;
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
/**
* 分页查询用户统计
*/
@GetMapping("/page")
public Result<PageResult<UserStatsResponse>> getPage(@Validated PageRequest request) {
IPage<UserStats> page = userStatsService.getPage(request);
List<UserStatsResponse> responses = page.getRecords().stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
PageResult<UserStatsResponse> 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 Result<UserStatsResponse> getByUserId(@PathVariable String userId) {
UserStats stats = userStatsService.getByUserId(userId);
if (stats == null) {
return Result.notFound("用户统计不存在");
}
return Result.success(convertToResponse(stats));
}
/**
* 根据用户ID和统计类型获取统计信息
*/
@GetMapping("/user/{userId}/type/{statsType}")
public Result<UserStatsResponse> getByUserIdAndStatsType(@PathVariable String userId, @PathVariable String statsType) {
UserStats stats = userStatsService.getByUserIdAndStatsType(userId, statsType);
if (stats == null) {
return Result.notFound("用户统计不存在");
}
return Result.success(convertToResponse(stats));
}
/**
* 根据统计类型查询统计信息
*/
@GetMapping("/type/{statsType}")
public Result<List<UserStatsResponse>> getByStatsType(@PathVariable String statsType) {
List<UserStats> statsList = userStatsService.getByStatsType(statsType);
List<UserStatsResponse> responses = statsList.stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
return Result.success(responses);
}
/**
* 查询用户的所有统计类型
*/
@GetMapping("/user/{userId}/all")
public Result<List<UserStatsResponse>> getAllStatsByUserId(@PathVariable String userId) {
List<UserStats> statsList = userStatsService.getAllStatsByUserId(userId);
List<UserStatsResponse> responses = statsList.stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
return Result.success(responses);
}
/**
* 查询排名前N的用户统计
*/
@GetMapping("/type/{statsType}/top")
public Result<List<UserStatsResponse>> getTopUsersByStatsType(@PathVariable String statsType, @RequestParam(defaultValue = "10") Integer limit) {
List<UserStats> statsList = userStatsService.getTopUsersByStatsType(statsType, limit);
List<UserStatsResponse> responses = statsList.stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
return Result.success(responses);
}
/**
* 查询用户在指定统计类型中的排名
*/
@GetMapping("/user/{userId}/type/{statsType}/rank")
public Result<Long> getUserRankByStatsType(@PathVariable String userId, @PathVariable String statsType) {
Long rank = userStatsService.getUserRankByStatsType(userId, statsType);
return Result.success(rank);
}
/**
* 创建或更新用户统计
*/
@PostMapping
public Result<UserStatsResponse> createOrUpdate(@RequestBody @Validated UserStatsCreateRequest request) {
UserStats stats = userStatsService.createOrUpdateUserStats(
request.getUserId(),
request.getStatsType(),
request.getValue(),
request.getPeriod()
);
return Result.success(convertToResponse(stats));
}
/**
* 更新用户统计值
*/
@PutMapping("/user/{userId}/type/{statsType}")
public Result<Void> updateStatsValue(@PathVariable String userId, @PathVariable String statsType, @RequestParam Double value) {
boolean updated = userStatsService.updateStatsValue(userId, statsType, value);
if (!updated) {
return Result.error("更新失败");
}
return Result.success();
}
/**
* 增加用户统计值
*/
@PutMapping("/user/{userId}/type/{statsType}/increment")
public Result<Void> incrementStatsValue(@PathVariable String userId, @PathVariable String statsType, @RequestParam Double increment) {
boolean updated = userStatsService.incrementStatsValue(userId, statsType, increment);
if (!updated) {
return Result.error("增加失败");
}
return Result.success();
}
/**
* 重新计算用户统计
*/
@PutMapping("/user/{userId}/recalculate")
public Result<Void> recalculateUserStats(@PathVariable String userId) {
boolean recalculated = userStatsService.recalculateUserStats(userId);
if (!recalculated) {
return Result.error("重新计算失败");
}
return Result.success();
}
/**
* 重新计算所有用户统计
*/
@PutMapping("/recalculate-all")
public Result<Void> recalculateAllUserStats() {
boolean recalculated = userStatsService.recalculateAllUserStats();
if (!recalculated) {
return Result.error("重新计算失败");
}
return Result.success();
}
/**
* 查询平均统计值
*/
@GetMapping("/type/{statsType}/avg")
public Result<Double> getAvgValueByStatsType(@PathVariable String statsType) {
Double avgValue = userStatsService.getAvgValueByStatsType(statsType);
return Result.success(avgValue);
}
/**
* 查询最大统计值
*/
@GetMapping("/type/{statsType}/max")
public Result<Double> getMaxValueByStatsType(@PathVariable String statsType) {
Double maxValue = userStatsService.getMaxValueByStatsType(statsType);
return Result.success(maxValue);
}
/**
* 查询最小统计值
*/
@GetMapping("/type/{statsType}/min")
public Result<Double> getMinValueByStatsType(@PathVariable String statsType) {
Double minValue = userStatsService.getMinValueByStatsType(statsType);
return Result.success(minValue);
}
/**
* 删除过期的统计数据
*/
@DeleteMapping("/expired")
public Result<Void> deleteExpiredStats(@RequestParam(defaultValue = "30") Integer days) {
boolean deleted = userStatsService.deleteExpiredStats(days);
if (!deleted) {
return Result.error("删除失败");
}
return Result.success();
}
/**
* 转换为响应对象
*/
private UserStatsResponse convertToResponse(UserStats stats) {
UserStatsResponse response = new UserStatsResponse();
BeanUtils.copyProperties(stats, response);
response.setId(stats.getId());
if (stats.getCreateTime() != null) {
response.setCreateTime(stats.getCreateTime().format(DATE_TIME_FORMATTER));
}
if (stats.getUpdateTime() != null) {
response.setUpdateTime(stats.getUpdateTime().format(DATE_TIME_FORMATTER));
}
return response;
}
/**
* 用户统计创建请求
*/
@lombok.Data
public static class UserStatsCreateRequest {
@NotBlank(message = "用户ID不能为空")
private String userId;
@NotBlank(message = "统计类型不能为空")
private String statsType;
@NotNull(message = "统计值不能为空")
private Double value;
private String period;
}
/**
* 用户统计响应类
*/
@lombok.Data
@lombok.EqualsAndHashCode(callSuper = true)
public static class UserStatsResponse extends BaseResponse {
private String userId;
private String statsType;
private Double value;
private String period;
}
}