main
parent
d236217c22
commit
b796dab689
@ -0,0 +1,28 @@
|
|||||||
|
# 环境变量配置示例
|
||||||
|
# 复制此文件为 .env 并填入真实配置
|
||||||
|
|
||||||
|
# 阿里云OSS配置
|
||||||
|
ALIYUN_OSS_ENDPOINT=your-endpoint
|
||||||
|
ALIYUN_OSS_ACCESS_KEY_ID=your-access-key-id
|
||||||
|
ALIYUN_OSS_ACCESS_KEY_SECRET=your-access-key-secret
|
||||||
|
ALIYUN_OSS_BUCKET_NAME=your-bucket-name
|
||||||
|
ALIYUN_OSS_URL_PREFIX=https://your-bucket-name.oss-region.aliyuncs.com/
|
||||||
|
|
||||||
|
# 数据库配置
|
||||||
|
DB_URL=jdbc:mysql://localhost:3306/UniLife?allowPublicKeyRetrieval=true&useSSL=false&serverTimezone=UTC&characterEncoding=UTF-8
|
||||||
|
DB_USERNAME=root
|
||||||
|
DB_PASSWORD=123456
|
||||||
|
|
||||||
|
# Redis配置
|
||||||
|
REDIS_HOST=127.0.0.1
|
||||||
|
REDIS_PORT=6379
|
||||||
|
|
||||||
|
# JWT配置
|
||||||
|
JWT_SECRET=qwertyuiopasdfghjklzxcvbnm
|
||||||
|
JWT_EXPIRATION=86400
|
||||||
|
|
||||||
|
# 邮箱配置
|
||||||
|
MAIL_HOST=smtp.163.com
|
||||||
|
MAIL_PORT=465
|
||||||
|
MAIL_USERNAME=your-email@163.com
|
||||||
|
MAIL_PASSWORD=your-auth-code
|
@ -0,0 +1,39 @@
|
|||||||
|
package com.unilife.config;
|
||||||
|
|
||||||
|
import com.aliyun.oss.OSS;
|
||||||
|
import com.aliyun.oss.OSSClientBuilder;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class OssConfig {
|
||||||
|
|
||||||
|
@Value("${aliyun.oss.endpoint}")
|
||||||
|
private String endpoint;
|
||||||
|
|
||||||
|
@Value("${aliyun.oss.accessKeyId}")
|
||||||
|
private String accessKeyId;
|
||||||
|
|
||||||
|
@Value("${aliyun.oss.accessKeySecret}")
|
||||||
|
private String accessKeySecret;
|
||||||
|
|
||||||
|
@Value("${aliyun.oss.bucketName}")
|
||||||
|
private String bucketName;
|
||||||
|
|
||||||
|
@Value("${aliyun.oss.urlPrefix}")
|
||||||
|
private String urlPrefix;
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public OSS ossClient() {
|
||||||
|
return new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBucketName() {
|
||||||
|
return bucketName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUrlPrefix() {
|
||||||
|
return urlPrefix;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,146 @@
|
|||||||
|
package com.unilife.controller;
|
||||||
|
|
||||||
|
import com.unilife.common.result.Result;
|
||||||
|
import com.unilife.service.AdminService;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/admin")
|
||||||
|
@Tag(name = "管理员接口", description = "后台管理相关接口")
|
||||||
|
public class AdminController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private AdminService adminService;
|
||||||
|
|
||||||
|
@Operation(summary = "获取系统统计数据")
|
||||||
|
@GetMapping("/stats")
|
||||||
|
public Result getSystemStats() {
|
||||||
|
return adminService.getSystemStats();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取用户列表")
|
||||||
|
@GetMapping("/users")
|
||||||
|
public Result getUserList(
|
||||||
|
@RequestParam(defaultValue = "1") Integer page,
|
||||||
|
@RequestParam(defaultValue = "10") Integer size,
|
||||||
|
@RequestParam(required = false) String keyword,
|
||||||
|
@RequestParam(required = false) Integer role,
|
||||||
|
@RequestParam(required = false) Integer status) {
|
||||||
|
return adminService.getUserList(page, size, keyword, role, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "更新用户状态")
|
||||||
|
@PutMapping("/users/{userId}/status")
|
||||||
|
public Result updateUserStatus(@PathVariable Long userId, @RequestBody Map<String, Integer> request) {
|
||||||
|
Integer status = request.get("status");
|
||||||
|
return adminService.updateUserStatus(userId, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "更新用户角色")
|
||||||
|
@PutMapping("/users/{userId}/role")
|
||||||
|
public Result updateUserRole(@PathVariable Long userId, @RequestBody Map<String, Integer> request) {
|
||||||
|
Integer role = request.get("role");
|
||||||
|
return adminService.updateUserRole(userId, role);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "删除用户")
|
||||||
|
@DeleteMapping("/users/{userId}")
|
||||||
|
public Result deleteUser(@PathVariable Long userId) {
|
||||||
|
return adminService.deleteUser(userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取帖子列表")
|
||||||
|
@GetMapping("/posts")
|
||||||
|
public Result getPostList(
|
||||||
|
@RequestParam(defaultValue = "1") Integer page,
|
||||||
|
@RequestParam(defaultValue = "10") Integer size,
|
||||||
|
@RequestParam(required = false) String keyword,
|
||||||
|
@RequestParam(required = false) Long categoryId,
|
||||||
|
@RequestParam(required = false) Integer status) {
|
||||||
|
return adminService.getPostList(page, size, keyword, categoryId, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "更新帖子状态")
|
||||||
|
@PutMapping("/posts/{postId}/status")
|
||||||
|
public Result updatePostStatus(@PathVariable Long postId, @RequestBody Map<String, Integer> request) {
|
||||||
|
Integer status = request.get("status");
|
||||||
|
return adminService.updatePostStatus(postId, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "删除帖子")
|
||||||
|
@DeleteMapping("/posts/{postId}")
|
||||||
|
public Result deletePost(@PathVariable Long postId) {
|
||||||
|
return adminService.deletePost(postId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "永久删除帖子")
|
||||||
|
@DeleteMapping("/posts/{postId}/permanent")
|
||||||
|
public Result permanentDeletePost(@PathVariable Long postId) {
|
||||||
|
return adminService.permanentDeletePost(postId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取评论列表")
|
||||||
|
@GetMapping("/comments")
|
||||||
|
public Result getCommentList(
|
||||||
|
@RequestParam(defaultValue = "1") Integer page,
|
||||||
|
@RequestParam(defaultValue = "10") Integer size,
|
||||||
|
@RequestParam(required = false) String keyword,
|
||||||
|
@RequestParam(required = false) Long postId,
|
||||||
|
@RequestParam(required = false) Integer status) {
|
||||||
|
return adminService.getCommentList(page, size, keyword, postId, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "删除评论")
|
||||||
|
@DeleteMapping("/comments/{commentId}")
|
||||||
|
public Result deleteComment(@PathVariable Long commentId) {
|
||||||
|
return adminService.deleteComment(commentId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取分类列表")
|
||||||
|
@GetMapping("/categories")
|
||||||
|
public Result getCategoryList() {
|
||||||
|
return adminService.getCategoryList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "创建分类")
|
||||||
|
@PostMapping("/categories")
|
||||||
|
public Result createCategory(@RequestBody Map<String, Object> request) {
|
||||||
|
return adminService.createCategory(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "更新分类")
|
||||||
|
@PutMapping("/categories/{categoryId}")
|
||||||
|
public Result updateCategory(@PathVariable Long categoryId, @RequestBody Map<String, Object> request) {
|
||||||
|
return adminService.updateCategory(categoryId, request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "删除分类")
|
||||||
|
@DeleteMapping("/categories/{categoryId}")
|
||||||
|
public Result deleteCategory(@PathVariable Long categoryId) {
|
||||||
|
return adminService.deleteCategory(categoryId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取资源列表")
|
||||||
|
@GetMapping("/resources")
|
||||||
|
public Result getResourceList(
|
||||||
|
@RequestParam(defaultValue = "1") Integer page,
|
||||||
|
@RequestParam(defaultValue = "10") Integer size,
|
||||||
|
@RequestParam(required = false) String keyword,
|
||||||
|
@RequestParam(required = false) Long categoryId,
|
||||||
|
@RequestParam(required = false) Integer status) {
|
||||||
|
return adminService.getResourceList(page, size, keyword, categoryId, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "删除资源")
|
||||||
|
@DeleteMapping("/resources/{resourceId}")
|
||||||
|
public Result deleteResource(@PathVariable Long resourceId) {
|
||||||
|
return adminService.deleteResource(resourceId);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
package com.unilife.mapper;
|
||||||
|
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 资源点赞数据访问层
|
||||||
|
*/
|
||||||
|
@Mapper
|
||||||
|
public interface ResourceLikeMapper {
|
||||||
|
/**
|
||||||
|
* 检查用户是否已点赞资源
|
||||||
|
* @param resourceId 资源ID
|
||||||
|
* @param userId 用户ID
|
||||||
|
* @return 是否已点赞
|
||||||
|
*/
|
||||||
|
boolean isLiked(@Param("resourceId") Long resourceId, @Param("userId") Long userId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加点赞记录
|
||||||
|
* @param resourceId 资源ID
|
||||||
|
* @param userId 用户ID
|
||||||
|
*/
|
||||||
|
void insert(@Param("resourceId") Long resourceId, @Param("userId") Long userId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除点赞记录
|
||||||
|
* @param resourceId 资源ID
|
||||||
|
* @param userId 用户ID
|
||||||
|
*/
|
||||||
|
void delete(@Param("resourceId") Long resourceId, @Param("userId") Long userId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取资源的点赞用户数量
|
||||||
|
* @param resourceId 资源ID
|
||||||
|
* @return 点赞数量
|
||||||
|
*/
|
||||||
|
int getLikeCount(@Param("resourceId") Long resourceId);
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
package com.unilife.model.dto;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class AiUpdateSessionDTO {
|
||||||
|
/**
|
||||||
|
* 更新后的会话标题
|
||||||
|
*/
|
||||||
|
private String title;
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
package com.unilife.model.vo;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class AiCreateSessionVO {
|
||||||
|
/**
|
||||||
|
* 会话ID
|
||||||
|
*/
|
||||||
|
private String sessionId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 会话标题
|
||||||
|
*/
|
||||||
|
private String title;
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
package com.unilife.model.vo;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class AiMessageHistoryVO {
|
||||||
|
/**
|
||||||
|
* 消息列表
|
||||||
|
*/
|
||||||
|
private List<AiMessageVO> messages;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 总数量
|
||||||
|
*/
|
||||||
|
private Long total;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 会话信息
|
||||||
|
*/
|
||||||
|
private AiSessionVO sessionInfo;
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
package com.unilife.model.vo;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class AiMessageVO {
|
||||||
|
/**
|
||||||
|
* 消息ID
|
||||||
|
*/
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 角色
|
||||||
|
*/
|
||||||
|
private String role;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 消息内容
|
||||||
|
*/
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 时间戳
|
||||||
|
*/
|
||||||
|
private String timestamp;
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
package com.unilife.model.vo;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class AiSessionListVO {
|
||||||
|
/**
|
||||||
|
* 会话列表
|
||||||
|
*/
|
||||||
|
private List<AiSessionVO> sessions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 总数量
|
||||||
|
*/
|
||||||
|
private Long total;
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
package com.unilife.model.vo;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class AiSessionVO {
|
||||||
|
/**
|
||||||
|
* 会话ID
|
||||||
|
*/
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 会话标题
|
||||||
|
*/
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
private String createdAt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
private String updatedAt;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,93 @@
|
|||||||
|
package com.unilife.service;
|
||||||
|
|
||||||
|
import com.unilife.common.result.Result;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public interface AdminService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取系统统计数据
|
||||||
|
*/
|
||||||
|
Result getSystemStats();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用户列表
|
||||||
|
*/
|
||||||
|
Result getUserList(Integer page, Integer size, String keyword, Integer role, Integer status);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新用户状态
|
||||||
|
*/
|
||||||
|
Result updateUserStatus(Long userId, Integer status);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新用户角色
|
||||||
|
*/
|
||||||
|
Result updateUserRole(Long userId, Integer role);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除用户
|
||||||
|
*/
|
||||||
|
Result deleteUser(Long userId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取帖子列表
|
||||||
|
*/
|
||||||
|
Result getPostList(Integer page, Integer size, String keyword, Long categoryId, Integer status);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新帖子状态
|
||||||
|
*/
|
||||||
|
Result updatePostStatus(Long postId, Integer status);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除帖子
|
||||||
|
*/
|
||||||
|
Result deletePost(Long postId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 永久删除帖子
|
||||||
|
*/
|
||||||
|
Result permanentDeletePost(Long postId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取评论列表
|
||||||
|
*/
|
||||||
|
Result getCommentList(Integer page, Integer size, String keyword, Long postId, Integer status);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除评论
|
||||||
|
*/
|
||||||
|
Result deleteComment(Long commentId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取分类列表
|
||||||
|
*/
|
||||||
|
Result getCategoryList();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建分类
|
||||||
|
*/
|
||||||
|
Result createCategory(Map<String, Object> request);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新分类
|
||||||
|
*/
|
||||||
|
Result updateCategory(Long categoryId, Map<String, Object> request);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除分类
|
||||||
|
*/
|
||||||
|
Result deleteCategory(Long categoryId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取资源列表
|
||||||
|
*/
|
||||||
|
Result getResourceList(Integer page, Integer size, String keyword, Long categoryId, Integer status);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除资源
|
||||||
|
*/
|
||||||
|
Result deleteResource(Long resourceId);
|
||||||
|
}
|
@ -0,0 +1,356 @@
|
|||||||
|
package com.unilife.service.impl;
|
||||||
|
|
||||||
|
import com.unilife.common.result.Result;
|
||||||
|
import com.unilife.mapper.*;
|
||||||
|
import com.unilife.model.entity.*;
|
||||||
|
import com.unilife.service.AdminService;
|
||||||
|
import com.unilife.service.UserService;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
public class AdminServiceImpl implements AdminService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private UserMapper userMapper;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private PostMapper postMapper;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private CommentMapper commentMapper;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private CategoryMapper categoryMapper;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ResourceMapper resourceMapper;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private UserService userService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Result getSystemStats() {
|
||||||
|
try {
|
||||||
|
Map<String, Object> stats = new HashMap<>();
|
||||||
|
|
||||||
|
// 用户统计
|
||||||
|
stats.put("totalUsers", userMapper.getTotalCount());
|
||||||
|
stats.put("activeUsers", userMapper.getActiveUserCount());
|
||||||
|
stats.put("newUsersToday", userMapper.getNewUserCountToday());
|
||||||
|
|
||||||
|
// 帖子统计
|
||||||
|
stats.put("totalPosts", postMapper.getTotalCount());
|
||||||
|
stats.put("newPostsToday", postMapper.getNewPostCountToday());
|
||||||
|
|
||||||
|
// 评论统计
|
||||||
|
stats.put("totalComments", commentMapper.getTotalCount());
|
||||||
|
stats.put("newCommentsToday", commentMapper.getNewCommentCountToday());
|
||||||
|
|
||||||
|
// 资源统计
|
||||||
|
stats.put("totalResources", resourceMapper.getTotalCount());
|
||||||
|
stats.put("newResourcesToday", resourceMapper.getNewResourceCountToday());
|
||||||
|
|
||||||
|
// 分类统计
|
||||||
|
stats.put("totalCategories", categoryMapper.getTotalCount());
|
||||||
|
|
||||||
|
return Result.success(stats);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("获取系统统计数据失败", e);
|
||||||
|
return Result.error(500, "获取系统统计数据失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Result getUserList(Integer page, Integer size, String keyword, Integer role, Integer status) {
|
||||||
|
try {
|
||||||
|
int offset = (page - 1) * size;
|
||||||
|
List<User> users = userMapper.getAdminUserList(offset, size, keyword, role, status);
|
||||||
|
int total = userMapper.getAdminUserCount(keyword, role, status);
|
||||||
|
|
||||||
|
Map<String, Object> result = new HashMap<>();
|
||||||
|
result.put("list", users);
|
||||||
|
result.put("total", total);
|
||||||
|
result.put("pages", (total + size - 1) / size);
|
||||||
|
|
||||||
|
return Result.success(result);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("获取用户列表失败", e);
|
||||||
|
return Result.error(500, "获取用户列表失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Result updateUserStatus(Long userId, Integer status) {
|
||||||
|
try {
|
||||||
|
User user = userMapper.getUserById(userId);
|
||||||
|
if (user == null) {
|
||||||
|
return Result.error(404, "用户不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
userMapper.updateUserStatus(userId, status);
|
||||||
|
return Result.success(null, "用户状态更新成功");
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("更新用户状态失败", e);
|
||||||
|
return Result.error(500, "更新用户状态失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Result updateUserRole(Long userId, Integer role) {
|
||||||
|
try {
|
||||||
|
User user = userMapper.getUserById(userId);
|
||||||
|
if (user == null) {
|
||||||
|
return Result.error(404, "用户不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
userMapper.updateUserRole(userId, role);
|
||||||
|
return Result.success(null, "用户角色更新成功");
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("更新用户角色失败", e);
|
||||||
|
return Result.error(500, "更新用户角色失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Result deleteUser(Long userId) {
|
||||||
|
try {
|
||||||
|
User user = userMapper.getUserById(userId);
|
||||||
|
if (user == null) {
|
||||||
|
return Result.error(404, "用户不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否为管理员
|
||||||
|
if (user.getRole() == 2) {
|
||||||
|
return Result.error(400, "不能删除管理员账号");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调用UserService的完整删除逻辑
|
||||||
|
return userService.deleteUser(userId);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("删除用户失败", e);
|
||||||
|
return Result.error(500, "删除用户失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Result getPostList(Integer page, Integer size, String keyword, Long categoryId, Integer status) {
|
||||||
|
try {
|
||||||
|
int offset = (page - 1) * size;
|
||||||
|
List<Post> posts = postMapper.getAdminPostList(offset, size, keyword, categoryId, status);
|
||||||
|
int total = postMapper.getAdminPostCount(keyword, categoryId, status);
|
||||||
|
|
||||||
|
Map<String, Object> result = new HashMap<>();
|
||||||
|
result.put("list", posts);
|
||||||
|
result.put("total", total);
|
||||||
|
result.put("pages", (total + size - 1) / size);
|
||||||
|
|
||||||
|
return Result.success(result);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("获取帖子列表失败", e);
|
||||||
|
return Result.error(500, "获取帖子列表失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Result updatePostStatus(Long postId, Integer status) {
|
||||||
|
try {
|
||||||
|
Post post = postMapper.getPostById(postId);
|
||||||
|
if (post == null) {
|
||||||
|
return Result.error(404, "帖子不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
postMapper.updatePostStatus(postId, status);
|
||||||
|
return Result.success(null, "帖子状态更新成功");
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("更新帖子状态失败", e);
|
||||||
|
return Result.error(500, "更新帖子状态失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Result deletePost(Long postId) {
|
||||||
|
try {
|
||||||
|
Post post = postMapper.getPostById(postId);
|
||||||
|
if (post == null) {
|
||||||
|
return Result.error(404, "帖子不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
postMapper.deletePost(postId);
|
||||||
|
return Result.success(null, "帖子删除成功");
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("删除帖子失败", e);
|
||||||
|
return Result.error(500, "删除帖子失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Result permanentDeletePost(Long postId) {
|
||||||
|
try {
|
||||||
|
Post post = postMapper.getPostById(postId);
|
||||||
|
if (post == null) {
|
||||||
|
return Result.error(404, "帖子不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 永久删除帖子(物理删除)
|
||||||
|
postMapper.permanentDeletePost(postId);
|
||||||
|
return Result.success(null, "帖子永久删除成功");
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("永久删除帖子失败", e);
|
||||||
|
return Result.error(500, "永久删除帖子失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Result getCommentList(Integer page, Integer size, String keyword, Long postId, Integer status) {
|
||||||
|
try {
|
||||||
|
int offset = (page - 1) * size;
|
||||||
|
List<Comment> comments = commentMapper.getAdminCommentList(offset, size, keyword, postId, status);
|
||||||
|
int total = commentMapper.getAdminCommentCount(keyword, postId, status);
|
||||||
|
|
||||||
|
Map<String, Object> result = new HashMap<>();
|
||||||
|
result.put("list", comments);
|
||||||
|
result.put("total", total);
|
||||||
|
result.put("pages", (total + size - 1) / size);
|
||||||
|
|
||||||
|
return Result.success(result);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("获取评论列表失败", e);
|
||||||
|
return Result.error(500, "获取评论列表失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Result deleteComment(Long commentId) {
|
||||||
|
try {
|
||||||
|
Comment comment = commentMapper.getCommentById(commentId);
|
||||||
|
if (comment == null) {
|
||||||
|
return Result.error(404, "评论不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
commentMapper.deleteComment(commentId);
|
||||||
|
return Result.success(null, "评论删除成功");
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("删除评论失败", e);
|
||||||
|
return Result.error(500, "删除评论失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Result getCategoryList() {
|
||||||
|
try {
|
||||||
|
List<Category> categories = categoryMapper.getAllCategories();
|
||||||
|
return Result.success(categories);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("获取分类列表失败", e);
|
||||||
|
return Result.error(500, "获取分类列表失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Result createCategory(Map<String, Object> request) {
|
||||||
|
try {
|
||||||
|
Category category = new Category();
|
||||||
|
category.setName((String) request.get("name"));
|
||||||
|
category.setDescription((String) request.get("description"));
|
||||||
|
category.setIcon((String) request.get("icon"));
|
||||||
|
category.setSort((Integer) request.get("sort"));
|
||||||
|
Integer statusInt = (Integer) request.get("status");
|
||||||
|
category.setStatus(statusInt != null ? statusInt.byteValue() : (byte) 1);
|
||||||
|
|
||||||
|
categoryMapper.insertCategory(category);
|
||||||
|
return Result.success(null, "分类创建成功");
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("创建分类失败", e);
|
||||||
|
return Result.error(500, "创建分类失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Result updateCategory(Long categoryId, Map<String, Object> request) {
|
||||||
|
try {
|
||||||
|
Category category = categoryMapper.getCategoryById(categoryId);
|
||||||
|
if (category == null) {
|
||||||
|
return Result.error(404, "分类不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
category.setName((String) request.get("name"));
|
||||||
|
category.setDescription((String) request.get("description"));
|
||||||
|
category.setIcon((String) request.get("icon"));
|
||||||
|
category.setSort((Integer) request.get("sort"));
|
||||||
|
Integer statusInt = (Integer) request.get("status");
|
||||||
|
category.setStatus(statusInt != null ? statusInt.byteValue() : (byte) 1);
|
||||||
|
|
||||||
|
categoryMapper.updateCategory(category);
|
||||||
|
return Result.success(null, "分类更新成功");
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("更新分类失败", e);
|
||||||
|
return Result.error(500, "更新分类失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Result deleteCategory(Long categoryId) {
|
||||||
|
try {
|
||||||
|
Category category = categoryMapper.getCategoryById(categoryId);
|
||||||
|
if (category == null) {
|
||||||
|
return Result.error(404, "分类不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否有帖子或资源使用该分类
|
||||||
|
int postCount = postMapper.getCountByCategoryId(categoryId);
|
||||||
|
int resourceCount = resourceMapper.getCountByCategoryId(categoryId);
|
||||||
|
|
||||||
|
if (postCount > 0 || resourceCount > 0) {
|
||||||
|
return Result.error(400, "该分类下还有帖子或资源,无法删除");
|
||||||
|
}
|
||||||
|
|
||||||
|
categoryMapper.deleteCategory(categoryId);
|
||||||
|
return Result.success(null, "分类删除成功");
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("删除分类失败", e);
|
||||||
|
return Result.error(500, "删除分类失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Result getResourceList(Integer page, Integer size, String keyword, Long categoryId, Integer status) {
|
||||||
|
try {
|
||||||
|
int offset = (page - 1) * size;
|
||||||
|
List<Resource> resources = resourceMapper.getAdminResourceList(offset, size, keyword, categoryId, status);
|
||||||
|
int total = resourceMapper.getAdminResourceCount(keyword, categoryId, status);
|
||||||
|
|
||||||
|
Map<String, Object> result = new HashMap<>();
|
||||||
|
result.put("list", resources);
|
||||||
|
result.put("total", total);
|
||||||
|
result.put("pages", (total + size - 1) / size);
|
||||||
|
|
||||||
|
return Result.success(result);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("获取资源列表失败", e);
|
||||||
|
return Result.error(500, "获取资源列表失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Result deleteResource(Long resourceId) {
|
||||||
|
try {
|
||||||
|
Resource resource = resourceMapper.getResourceById(resourceId);
|
||||||
|
if (resource == null) {
|
||||||
|
return Result.error(404, "资源不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
resourceMapper.deleteResource(resourceId);
|
||||||
|
return Result.success(null, "资源删除成功");
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("删除资源失败", e);
|
||||||
|
return Result.error(500, "删除资源失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,87 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
<mapper namespace="com.unilife.mapper.AiChatSessionMapper">
|
||||||
|
|
||||||
|
<!-- 结果映射 -->
|
||||||
|
<resultMap id="AiChatSessionResultMap" type="com.unilife.model.entity.AiChatSession">
|
||||||
|
<id column="id" property="id"/>
|
||||||
|
<result column="user_id" property="userId"/>
|
||||||
|
<result column="title" property="title"/>
|
||||||
|
<result column="created_at" property="createdAt"/>
|
||||||
|
<result column="updated_at" property="updatedAt"/>
|
||||||
|
</resultMap>
|
||||||
|
|
||||||
|
<!-- 插入会话 -->
|
||||||
|
<insert id="insert" parameterType="com.unilife.model.entity.AiChatSession">
|
||||||
|
INSERT INTO ai_chat_sessions (
|
||||||
|
id, user_id, title, created_at, updated_at
|
||||||
|
) VALUES (
|
||||||
|
#{id}, #{userId}, #{title}, #{createdAt}, #{updatedAt}
|
||||||
|
)
|
||||||
|
</insert>
|
||||||
|
|
||||||
|
<!-- 根据ID查询会话 -->
|
||||||
|
<select id="selectById" resultMap="AiChatSessionResultMap">
|
||||||
|
SELECT id, user_id, title, created_at, updated_at
|
||||||
|
FROM ai_chat_sessions
|
||||||
|
WHERE id = #{id}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- 根据用户ID分页查询会话列表 -->
|
||||||
|
<select id="selectByUserId" resultMap="AiChatSessionResultMap">
|
||||||
|
SELECT id, user_id, title, created_at, updated_at
|
||||||
|
FROM ai_chat_sessions
|
||||||
|
WHERE user_id = #{userId}
|
||||||
|
ORDER BY updated_at DESC
|
||||||
|
LIMIT #{offset}, #{limit}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- 查询匿名会话列表 -->
|
||||||
|
<select id="selectAnonymousSessions" resultMap="AiChatSessionResultMap">
|
||||||
|
SELECT id, user_id, title, created_at, updated_at
|
||||||
|
FROM ai_chat_sessions
|
||||||
|
WHERE user_id IS NULL
|
||||||
|
ORDER BY updated_at DESC
|
||||||
|
LIMIT #{offset}, #{limit}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- 统计用户会话总数 -->
|
||||||
|
<select id="countByUserId" resultType="long">
|
||||||
|
SELECT COUNT(*)
|
||||||
|
FROM ai_chat_sessions
|
||||||
|
WHERE user_id = #{userId}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- 统计匿名会话总数 -->
|
||||||
|
<select id="countAnonymousSessions" resultType="long">
|
||||||
|
SELECT COUNT(*)
|
||||||
|
FROM ai_chat_sessions
|
||||||
|
WHERE user_id IS NULL
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- 更新会话标题 -->
|
||||||
|
<update id="updateTitle">
|
||||||
|
UPDATE ai_chat_sessions
|
||||||
|
SET title = #{title}, updated_at = CURRENT_TIMESTAMP
|
||||||
|
WHERE id = #{id}
|
||||||
|
</update>
|
||||||
|
|
||||||
|
<!-- 更新会话的最后消息时间和消息数量(简化方案中保留方法但不使用) -->
|
||||||
|
<update id="updateMessageInfo">
|
||||||
|
UPDATE ai_chat_sessions
|
||||||
|
SET updated_at = #{lastMessageTime}
|
||||||
|
WHERE id = #{id}
|
||||||
|
</update>
|
||||||
|
|
||||||
|
<!-- 删除会话 -->
|
||||||
|
<delete id="deleteById">
|
||||||
|
DELETE FROM ai_chat_sessions WHERE id = #{id}
|
||||||
|
</delete>
|
||||||
|
|
||||||
|
<!-- 批量删除过期会话 -->
|
||||||
|
<delete id="deleteExpiredSessions">
|
||||||
|
DELETE FROM ai_chat_sessions
|
||||||
|
WHERE updated_at < #{expireTime}
|
||||||
|
</delete>
|
||||||
|
|
||||||
|
</mapper>
|
@ -0,0 +1,25 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
|
||||||
|
<mapper namespace="com.unilife.mapper.ResourceLikeMapper">
|
||||||
|
<select id="isLiked" resultType="boolean">
|
||||||
|
SELECT COUNT(*) > 0
|
||||||
|
FROM resource_likes
|
||||||
|
WHERE resource_id = #{resourceId} AND user_id = #{userId}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<insert id="insert">
|
||||||
|
INSERT INTO resource_likes (resource_id, user_id, created_at)
|
||||||
|
VALUES (#{resourceId}, #{userId}, NOW())
|
||||||
|
</insert>
|
||||||
|
|
||||||
|
<delete id="delete">
|
||||||
|
DELETE FROM resource_likes
|
||||||
|
WHERE resource_id = #{resourceId} AND user_id = #{userId}
|
||||||
|
</delete>
|
||||||
|
|
||||||
|
<select id="getLikeCount" resultType="int">
|
||||||
|
SELECT COUNT(*)
|
||||||
|
FROM resource_likes
|
||||||
|
WHERE resource_id = #{resourceId}
|
||||||
|
</select>
|
||||||
|
</mapper>
|
@ -0,0 +1,80 @@
|
|||||||
|
package com.unilife.config;
|
||||||
|
|
||||||
|
import org.springframework.boot.test.context.TestConfiguration;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Primary;
|
||||||
|
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||||
|
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
|
||||||
|
import org.springframework.data.redis.core.RedisTemplate;
|
||||||
|
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||||
|
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||||
|
import org.springframework.mail.javamail.JavaMailSender;
|
||||||
|
import org.springframework.mail.javamail.JavaMailSenderImpl;
|
||||||
|
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 测试配置类
|
||||||
|
* 为测试环境提供特定的Bean配置
|
||||||
|
*/
|
||||||
|
@TestConfiguration
|
||||||
|
public class TestConfig {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 测试用的Redis连接工厂
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
@Primary
|
||||||
|
public RedisConnectionFactory testRedisConnectionFactory() {
|
||||||
|
LettuceConnectionFactory factory = new LettuceConnectionFactory("localhost", 6379);
|
||||||
|
factory.setDatabase(1); // 使用数据库1进行测试
|
||||||
|
return factory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 测试用的RedisTemplate
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
@Primary
|
||||||
|
public RedisTemplate<String, Object> testRedisTemplate(RedisConnectionFactory connectionFactory) {
|
||||||
|
RedisTemplate<String, Object> template = new RedisTemplate<>();
|
||||||
|
template.setConnectionFactory(connectionFactory);
|
||||||
|
template.setKeySerializer(new StringRedisSerializer());
|
||||||
|
template.setValueSerializer(new StringRedisSerializer());
|
||||||
|
template.setHashKeySerializer(new StringRedisSerializer());
|
||||||
|
template.setHashValueSerializer(new StringRedisSerializer());
|
||||||
|
return template;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 测试用的StringRedisTemplate
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
@Primary
|
||||||
|
public StringRedisTemplate testStringRedisTemplate(RedisConnectionFactory connectionFactory) {
|
||||||
|
StringRedisTemplate template = new StringRedisTemplate();
|
||||||
|
template.setConnectionFactory(connectionFactory);
|
||||||
|
return template;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 测试用的邮件发送器
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
@Primary
|
||||||
|
public JavaMailSender testJavaMailSender() {
|
||||||
|
JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
|
||||||
|
mailSender.setHost("smtp.example.com");
|
||||||
|
mailSender.setPort(587);
|
||||||
|
mailSender.setUsername("test@example.com");
|
||||||
|
mailSender.setPassword("testpassword");
|
||||||
|
|
||||||
|
Properties props = mailSender.getJavaMailProperties();
|
||||||
|
props.put("mail.transport.protocol", "smtp");
|
||||||
|
props.put("mail.smtp.auth", "true");
|
||||||
|
props.put("mail.smtp.starttls.enable", "true");
|
||||||
|
props.put("mail.debug", "false"); // 测试时关闭debug
|
||||||
|
|
||||||
|
return mailSender;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,169 @@
|
|||||||
|
package com.unilife.controller;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.unilife.common.result.Result;
|
||||||
|
import com.unilife.model.dto.CreatePostDTO;
|
||||||
|
import com.unilife.model.dto.UpdatePostDTO;
|
||||||
|
import com.unilife.service.PostService;
|
||||||
|
import com.unilife.utils.BaseContext;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
|
||||||
|
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
|
|
||||||
|
import static org.mockito.ArgumentMatchers.*;
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
|
||||||
|
|
||||||
|
@WebMvcTest(PostController.class)
|
||||||
|
class PostControllerTest {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private MockMvc mockMvc;
|
||||||
|
|
||||||
|
@MockBean
|
||||||
|
private PostService postService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ObjectMapper objectMapper;
|
||||||
|
|
||||||
|
private CreatePostDTO createPostDTO;
|
||||||
|
private UpdatePostDTO updatePostDTO;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
createPostDTO = new CreatePostDTO();
|
||||||
|
createPostDTO.setTitle("测试帖子");
|
||||||
|
createPostDTO.setContent("测试内容");
|
||||||
|
createPostDTO.setCategoryId(1L);
|
||||||
|
|
||||||
|
updatePostDTO = new UpdatePostDTO();
|
||||||
|
updatePostDTO.setTitle("更新标题");
|
||||||
|
updatePostDTO.setContent("更新内容");
|
||||||
|
updatePostDTO.setCategoryId(1L);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCreatePost_Success() throws Exception {
|
||||||
|
// Mock用户已登录
|
||||||
|
try (var mockedStatic = mockStatic(BaseContext.class)) {
|
||||||
|
mockedStatic.when(BaseContext::getId).thenReturn(1L);
|
||||||
|
|
||||||
|
when(postService.createPost(eq(1L), any(CreatePostDTO.class)))
|
||||||
|
.thenReturn(Result.success("帖子发布成功"));
|
||||||
|
|
||||||
|
mockMvc.perform(post("/posts")
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content(objectMapper.writeValueAsString(createPostDTO)))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.success").value(true))
|
||||||
|
.andExpect(jsonPath("$.message").value("帖子发布成功"));
|
||||||
|
|
||||||
|
verify(postService).createPost(eq(1L), any(CreatePostDTO.class));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCreatePost_Unauthorized() throws Exception {
|
||||||
|
// Mock用户未登录
|
||||||
|
try (var mockedStatic = mockStatic(BaseContext.class)) {
|
||||||
|
mockedStatic.when(BaseContext::getId).thenReturn(null);
|
||||||
|
|
||||||
|
mockMvc.perform(post("/posts")
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content(objectMapper.writeValueAsString(createPostDTO)))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpected(jsonPath("$.success").value(false))
|
||||||
|
.andExpected(jsonPath("$.code").value(401))
|
||||||
|
.andExpected(jsonPath("$.message").value("未登录"));
|
||||||
|
|
||||||
|
verify(postService, never()).createPost(anyLong(), any(CreatePostDTO.class));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testGetPostDetail_Success() throws Exception {
|
||||||
|
when(postService.getPostDetail(eq(1L), any()))
|
||||||
|
.thenReturn(Result.success("帖子详情"));
|
||||||
|
|
||||||
|
mockMvc.perform(get("/posts/1"))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpected(jsonPath("$.success").value(true));
|
||||||
|
|
||||||
|
verify(postService).getPostDetail(eq(1L), any());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testGetPostList_Success() throws Exception {
|
||||||
|
when(postService.getPostList(any(), any(), anyInt(), anyInt(), any(), any()))
|
||||||
|
.thenReturn(Result.success("帖子列表"));
|
||||||
|
|
||||||
|
mockMvc.perform(get("/posts")
|
||||||
|
.param("categoryId", "1")
|
||||||
|
.param("keyword", "测试")
|
||||||
|
.param("page", "1")
|
||||||
|
.param("size", "10")
|
||||||
|
.param("sort", "latest"))
|
||||||
|
.andExpected(status().isOk())
|
||||||
|
.andExpected(jsonPath("$.success").value(true));
|
||||||
|
|
||||||
|
verify(postService).getPostList(eq(1L), eq("测试"), eq(1), eq(10), eq("latest"), any());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testUpdatePost_Success() throws Exception {
|
||||||
|
try (var mockedStatic = mockStatic(BaseContext.class)) {
|
||||||
|
mockedStatic.when(BaseContext::getId).thenReturn(1L);
|
||||||
|
|
||||||
|
when(postService.updatePost(eq(1L), eq(1L), any(UpdatePostDTO.class)))
|
||||||
|
.thenReturn(Result.success("帖子更新成功"));
|
||||||
|
|
||||||
|
mockMvc.perform(put("/posts/1")
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content(objectMapper.writeValueAsString(updatePostDTO)))
|
||||||
|
.andExpected(status().isOk())
|
||||||
|
.andExpected(jsonPath("$.success").value(true))
|
||||||
|
.andExpected(jsonPath("$.message").value("帖子更新成功"));
|
||||||
|
|
||||||
|
verify(postService).updatePost(eq(1L), eq(1L), any(UpdatePostDTO.class));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testDeletePost_Success() throws Exception {
|
||||||
|
try (var mockedStatic = mockStatic(BaseContext.class)) {
|
||||||
|
mockedStatic.when(BaseContext::getId).thenReturn(1L);
|
||||||
|
|
||||||
|
when(postService.deletePost(eq(1L), eq(1L)))
|
||||||
|
.thenReturn(Result.success("帖子删除成功"));
|
||||||
|
|
||||||
|
mockMvc.perform(delete("/posts/1"))
|
||||||
|
.andExpected(status().isOk())
|
||||||
|
.andExpected(jsonPath("$.success").value(true))
|
||||||
|
.andExpected(jsonPath("$.message").value("帖子删除成功"));
|
||||||
|
|
||||||
|
verify(postService).deletePost(eq(1L), eq(1L));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testLikePost_Success() throws Exception {
|
||||||
|
try (var mockedStatic = mockStatic(BaseContext.class)) {
|
||||||
|
mockedStatic.when(BaseContext::getId).thenReturn(1L);
|
||||||
|
|
||||||
|
when(postService.likePost(eq(1L), eq(1L)))
|
||||||
|
.thenReturn(Result.success("点赞成功"));
|
||||||
|
|
||||||
|
mockMvc.perform(post("/posts/1/like"))
|
||||||
|
.andExpected(status().isOk())
|
||||||
|
.andExpected(jsonPath("$.success").value(true))
|
||||||
|
.andExpected(jsonPath("$.message").value("点赞成功"));
|
||||||
|
|
||||||
|
verify(postService).likePost(eq(1L), eq(1L));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,287 @@
|
|||||||
|
package com.unilife.service;
|
||||||
|
|
||||||
|
import com.unilife.common.result.Result;
|
||||||
|
import com.unilife.mapper.PostMapper;
|
||||||
|
import com.unilife.mapper.UserMapper;
|
||||||
|
import com.unilife.mapper.CategoryMapper;
|
||||||
|
import com.unilife.model.dto.CreatePostDTO;
|
||||||
|
import com.unilife.model.dto.UpdatePostDTO;
|
||||||
|
import com.unilife.model.entity.Post;
|
||||||
|
import com.unilife.model.entity.User;
|
||||||
|
import com.unilife.model.entity.Category;
|
||||||
|
import com.unilife.service.impl.PostServiceImpl;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
import static org.mockito.ArgumentMatchers.*;
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
@SpringBootTest
|
||||||
|
class PostServiceTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private PostMapper postMapper;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private UserMapper userMapper;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private CategoryMapper categoryMapper;
|
||||||
|
|
||||||
|
@InjectMocks
|
||||||
|
private PostServiceImpl postService;
|
||||||
|
|
||||||
|
private User testUser;
|
||||||
|
private Category testCategory;
|
||||||
|
private Post testPost;
|
||||||
|
private CreatePostDTO createPostDTO;
|
||||||
|
private UpdatePostDTO updatePostDTO;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
// 初始化测试数据
|
||||||
|
testUser = new User();
|
||||||
|
testUser.setId(1L);
|
||||||
|
testUser.setNickname("测试用户");
|
||||||
|
testUser.setAvatar("avatar.jpg");
|
||||||
|
|
||||||
|
testCategory = new Category();
|
||||||
|
testCategory.setId(1L);
|
||||||
|
testCategory.setName("学习讨论");
|
||||||
|
testCategory.setStatus(1);
|
||||||
|
|
||||||
|
testPost = new Post();
|
||||||
|
testPost.setId(1L);
|
||||||
|
testPost.setTitle("测试帖子");
|
||||||
|
testPost.setContent("这是一个测试帖子的内容");
|
||||||
|
testPost.setUserId(1L);
|
||||||
|
testPost.setCategoryId(1L);
|
||||||
|
testPost.setLikeCount(0);
|
||||||
|
testPost.setViewCount(0);
|
||||||
|
testPost.setCommentCount(0);
|
||||||
|
testPost.setCreatedAt(LocalDateTime.now());
|
||||||
|
testPost.setUpdatedAt(LocalDateTime.now());
|
||||||
|
|
||||||
|
createPostDTO = new CreatePostDTO();
|
||||||
|
createPostDTO.setTitle("新帖子标题");
|
||||||
|
createPostDTO.setContent("新帖子内容");
|
||||||
|
createPostDTO.setCategoryId(1L);
|
||||||
|
|
||||||
|
updatePostDTO = new UpdatePostDTO();
|
||||||
|
updatePostDTO.setTitle("更新后的标题");
|
||||||
|
updatePostDTO.setContent("更新后的内容");
|
||||||
|
updatePostDTO.setCategoryId(1L);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCreatePost_Success() {
|
||||||
|
// Mock 依赖方法
|
||||||
|
when(userMapper.findById(1L)).thenReturn(testUser);
|
||||||
|
when(categoryMapper.findById(1L)).thenReturn(testCategory);
|
||||||
|
when(postMapper.insert(any(Post.class))).thenReturn(1);
|
||||||
|
|
||||||
|
// 执行测试
|
||||||
|
Result<?> result = postService.createPost(1L, createPostDTO);
|
||||||
|
|
||||||
|
// 验证结果
|
||||||
|
assertTrue(result.isSuccess());
|
||||||
|
assertEquals("帖子发布成功", result.getMessage());
|
||||||
|
|
||||||
|
// 验证方法调用
|
||||||
|
verify(userMapper).findById(1L);
|
||||||
|
verify(categoryMapper).findById(1L);
|
||||||
|
verify(postMapper).insert(any(Post.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCreatePost_UserNotFound() {
|
||||||
|
// Mock 用户不存在
|
||||||
|
when(userMapper.findById(1L)).thenReturn(null);
|
||||||
|
|
||||||
|
// 执行测试
|
||||||
|
Result<?> result = postService.createPost(1L, createPostDTO);
|
||||||
|
|
||||||
|
// 验证结果
|
||||||
|
assertFalse(result.isSuccess());
|
||||||
|
assertEquals(404, result.getCode());
|
||||||
|
assertEquals("用户不存在", result.getMessage());
|
||||||
|
|
||||||
|
// 验证不会尝试创建帖子
|
||||||
|
verify(postMapper, never()).insert(any(Post.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCreatePost_CategoryNotFound() {
|
||||||
|
// Mock 用户存在但分类不存在
|
||||||
|
when(userMapper.findById(1L)).thenReturn(testUser);
|
||||||
|
when(categoryMapper.findById(1L)).thenReturn(null);
|
||||||
|
|
||||||
|
// 执行测试
|
||||||
|
Result<?> result = postService.createPost(1L, createPostDTO);
|
||||||
|
|
||||||
|
// 验证结果
|
||||||
|
assertFalse(result.isSuccess());
|
||||||
|
assertEquals(404, result.getCode());
|
||||||
|
assertEquals("分类不存在", result.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCreatePost_InvalidTitle() {
|
||||||
|
// 测试空标题
|
||||||
|
createPostDTO.setTitle("");
|
||||||
|
|
||||||
|
// 执行测试
|
||||||
|
Result<?> result = postService.createPost(1L, createPostDTO);
|
||||||
|
|
||||||
|
// 验证结果
|
||||||
|
assertFalse(result.isSuccess());
|
||||||
|
assertEquals(400, result.getCode());
|
||||||
|
assertTrue(result.getMessage().contains("标题不能为空"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testGetPostDetail_Success() {
|
||||||
|
// Mock 依赖方法
|
||||||
|
when(postMapper.findById(1L)).thenReturn(testPost);
|
||||||
|
when(userMapper.findById(1L)).thenReturn(testUser);
|
||||||
|
when(categoryMapper.findById(1L)).thenReturn(testCategory);
|
||||||
|
|
||||||
|
// 执行测试
|
||||||
|
Result<?> result = postService.getPostDetail(1L, 1L);
|
||||||
|
|
||||||
|
// 验证结果
|
||||||
|
assertTrue(result.isSuccess());
|
||||||
|
assertNotNull(result.getData());
|
||||||
|
|
||||||
|
// 验证浏览量增加
|
||||||
|
verify(postMapper).updateViewCount(1L);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testGetPostDetail_PostNotFound() {
|
||||||
|
// Mock 帖子不存在
|
||||||
|
when(postMapper.findById(1L)).thenReturn(null);
|
||||||
|
|
||||||
|
// 执行测试
|
||||||
|
Result<?> result = postService.getPostDetail(1L, 1L);
|
||||||
|
|
||||||
|
// 验证结果
|
||||||
|
assertFalse(result.isSuccess());
|
||||||
|
assertEquals(404, result.getCode());
|
||||||
|
assertEquals("帖子不存在", result.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testGetPostList_Success() {
|
||||||
|
// Mock 帖子列表
|
||||||
|
List<Post> posts = Arrays.asList(testPost);
|
||||||
|
when(postMapper.findByConditions(any(), any(), anyInt(), anyInt(), any())).thenReturn(posts);
|
||||||
|
when(postMapper.countByConditions(any(), any())).thenReturn(1);
|
||||||
|
|
||||||
|
// 执行测试
|
||||||
|
Result<?> result = postService.getPostList(1L, "测试", 1, 10, "latest", 1L);
|
||||||
|
|
||||||
|
// 验证结果
|
||||||
|
assertTrue(result.isSuccess());
|
||||||
|
assertNotNull(result.getData());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testUpdatePost_Success() {
|
||||||
|
// Mock 依赖方法
|
||||||
|
when(postMapper.findById(1L)).thenReturn(testPost);
|
||||||
|
when(categoryMapper.findById(1L)).thenReturn(testCategory);
|
||||||
|
when(postMapper.update(any(Post.class))).thenReturn(1);
|
||||||
|
|
||||||
|
// 执行测试
|
||||||
|
Result<?> result = postService.updatePost(1L, 1L, updatePostDTO);
|
||||||
|
|
||||||
|
// 验证结果
|
||||||
|
assertTrue(result.isSuccess());
|
||||||
|
assertEquals("帖子更新成功", result.getMessage());
|
||||||
|
|
||||||
|
// 验证方法调用
|
||||||
|
verify(postMapper).update(any(Post.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testUpdatePost_Unauthorized() {
|
||||||
|
// Mock 其他用户的帖子
|
||||||
|
testPost.setUserId(2L);
|
||||||
|
when(postMapper.findById(1L)).thenReturn(testPost);
|
||||||
|
|
||||||
|
// 执行测试
|
||||||
|
Result<?> result = postService.updatePost(1L, 1L, updatePostDTO);
|
||||||
|
|
||||||
|
// 验证结果
|
||||||
|
assertFalse(result.isSuccess());
|
||||||
|
assertEquals(403, result.getCode());
|
||||||
|
assertEquals("无权限修改此帖子", result.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testDeletePost_Success() {
|
||||||
|
// Mock 依赖方法
|
||||||
|
when(postMapper.findById(1L)).thenReturn(testPost);
|
||||||
|
when(postMapper.delete(1L)).thenReturn(1);
|
||||||
|
|
||||||
|
// 执行测试
|
||||||
|
Result<?> result = postService.deletePost(1L, 1L);
|
||||||
|
|
||||||
|
// 验证结果
|
||||||
|
assertTrue(result.isSuccess());
|
||||||
|
assertEquals("帖子删除成功", result.getMessage());
|
||||||
|
|
||||||
|
// 验证方法调用
|
||||||
|
verify(postMapper).delete(1L);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testLikePost_Success() {
|
||||||
|
// Mock 依赖方法
|
||||||
|
when(postMapper.findById(1L)).thenReturn(testPost);
|
||||||
|
when(postMapper.isLikedByUser(1L, 1L)).thenReturn(false);
|
||||||
|
when(postMapper.insertLike(1L, 1L)).thenReturn(1);
|
||||||
|
|
||||||
|
// 执行测试
|
||||||
|
Result<?> result = postService.likePost(1L, 1L);
|
||||||
|
|
||||||
|
// 验证结果
|
||||||
|
assertTrue(result.isSuccess());
|
||||||
|
assertEquals("点赞成功", result.getMessage());
|
||||||
|
|
||||||
|
// 验证方法调用
|
||||||
|
verify(postMapper).insertLike(1L, 1L);
|
||||||
|
verify(postMapper).updateLikeCount(1L, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testUnlikePost_Success() {
|
||||||
|
// Mock 已点赞状态
|
||||||
|
when(postMapper.findById(1L)).thenReturn(testPost);
|
||||||
|
when(postMapper.isLikedByUser(1L, 1L)).thenReturn(true);
|
||||||
|
when(postMapper.deleteLike(1L, 1L)).thenReturn(1);
|
||||||
|
|
||||||
|
// 执行测试
|
||||||
|
Result<?> result = postService.likePost(1L, 1L);
|
||||||
|
|
||||||
|
// 验证结果
|
||||||
|
assertTrue(result.isSuccess());
|
||||||
|
assertEquals("取消点赞成功", result.getMessage());
|
||||||
|
|
||||||
|
// 验证方法调用
|
||||||
|
verify(postMapper).deleteLike(1L, 1L);
|
||||||
|
verify(postMapper).updateLikeCount(1L, -1);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,348 @@
|
|||||||
|
package com.unilife.service;
|
||||||
|
|
||||||
|
import com.unilife.common.result.Result;
|
||||||
|
import com.unilife.mapper.ResourceMapper;
|
||||||
|
import com.unilife.mapper.UserMapper;
|
||||||
|
import com.unilife.mapper.CategoryMapper;
|
||||||
|
import com.unilife.model.dto.CreateResourceDTO;
|
||||||
|
import com.unilife.model.entity.Resource;
|
||||||
|
import com.unilife.model.entity.User;
|
||||||
|
import com.unilife.model.entity.Category;
|
||||||
|
import com.unilife.service.impl.ResourceServiceImpl;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.mock.web.MockMultipartFile;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
import static org.mockito.ArgumentMatchers.*;
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
@SpringBootTest
|
||||||
|
class ResourceServiceTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private ResourceMapper resourceMapper;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private UserMapper userMapper;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private CategoryMapper categoryMapper;
|
||||||
|
|
||||||
|
@InjectMocks
|
||||||
|
private ResourceServiceImpl resourceService;
|
||||||
|
|
||||||
|
private User testUser;
|
||||||
|
private Category testCategory;
|
||||||
|
private Resource testResource;
|
||||||
|
private CreateResourceDTO createResourceDTO;
|
||||||
|
private MockMultipartFile mockFile;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
// 初始化测试数据
|
||||||
|
testUser = new User();
|
||||||
|
testUser.setId(1L);
|
||||||
|
testUser.setNickname("测试用户");
|
||||||
|
testUser.setAvatar("avatar.jpg");
|
||||||
|
|
||||||
|
testCategory = new Category();
|
||||||
|
testCategory.setId(1L);
|
||||||
|
testCategory.setName("学习资料");
|
||||||
|
testCategory.setStatus(1);
|
||||||
|
|
||||||
|
testResource = new Resource();
|
||||||
|
testResource.setId(1L);
|
||||||
|
testResource.setTitle("测试资源");
|
||||||
|
testResource.setDescription("测试资源描述");
|
||||||
|
testResource.setFileName("test.pdf");
|
||||||
|
testResource.setFileUrl("http://example.com/test.pdf");
|
||||||
|
testResource.setFileSize(1024L);
|
||||||
|
testResource.setFileType("pdf");
|
||||||
|
testResource.setUserId(1L);
|
||||||
|
testResource.setCategoryId(1L);
|
||||||
|
testResource.setDownloadCount(0);
|
||||||
|
testResource.setLikeCount(0);
|
||||||
|
testResource.setCreatedAt(LocalDateTime.now());
|
||||||
|
testResource.setUpdatedAt(LocalDateTime.now());
|
||||||
|
|
||||||
|
createResourceDTO = new CreateResourceDTO();
|
||||||
|
createResourceDTO.setTitle("新资源标题");
|
||||||
|
createResourceDTO.setDescription("新资源描述");
|
||||||
|
createResourceDTO.setCategoryId(1L);
|
||||||
|
|
||||||
|
mockFile = new MockMultipartFile(
|
||||||
|
"file",
|
||||||
|
"test.pdf",
|
||||||
|
"application/pdf",
|
||||||
|
"test content".getBytes()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testUploadResource_Success() {
|
||||||
|
// Mock 依赖方法
|
||||||
|
when(userMapper.findById(1L)).thenReturn(testUser);
|
||||||
|
when(categoryMapper.findById(1L)).thenReturn(testCategory);
|
||||||
|
when(resourceMapper.insert(any(Resource.class))).thenReturn(1);
|
||||||
|
|
||||||
|
// 执行测试
|
||||||
|
Result<?> result = resourceService.uploadResource(1L, createResourceDTO, mockFile);
|
||||||
|
|
||||||
|
// 验证结果
|
||||||
|
assertTrue(result.isSuccess());
|
||||||
|
assertEquals("资源上传成功", result.getMessage());
|
||||||
|
|
||||||
|
// 验证方法调用
|
||||||
|
verify(userMapper).findById(1L);
|
||||||
|
verify(categoryMapper).findById(1L);
|
||||||
|
verify(resourceMapper).insert(any(Resource.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testUploadResource_UserNotFound() {
|
||||||
|
// Mock 用户不存在
|
||||||
|
when(userMapper.findById(1L)).thenReturn(null);
|
||||||
|
|
||||||
|
// 执行测试
|
||||||
|
Result<?> result = resourceService.uploadResource(1L, createResourceDTO, mockFile);
|
||||||
|
|
||||||
|
// 验证结果
|
||||||
|
assertFalse(result.isSuccess());
|
||||||
|
assertEquals(404, result.getCode());
|
||||||
|
assertEquals("用户不存在", result.getMessage());
|
||||||
|
|
||||||
|
// 验证不会尝试上传资源
|
||||||
|
verify(resourceMapper, never()).insert(any(Resource.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testUploadResource_CategoryNotFound() {
|
||||||
|
// Mock 用户存在但分类不存在
|
||||||
|
when(userMapper.findById(1L)).thenReturn(testUser);
|
||||||
|
when(categoryMapper.findById(1L)).thenReturn(null);
|
||||||
|
|
||||||
|
// 执行测试
|
||||||
|
Result<?> result = resourceService.uploadResource(1L, createResourceDTO, mockFile);
|
||||||
|
|
||||||
|
// 验证结果
|
||||||
|
assertFalse(result.isSuccess());
|
||||||
|
assertEquals(404, result.getCode());
|
||||||
|
assertEquals("分类不存在", result.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testUploadResource_EmptyFile() {
|
||||||
|
// 测试空文件
|
||||||
|
MockMultipartFile emptyFile = new MockMultipartFile(
|
||||||
|
"file",
|
||||||
|
"empty.pdf",
|
||||||
|
"application/pdf",
|
||||||
|
new byte[0]
|
||||||
|
);
|
||||||
|
|
||||||
|
// 执行测试
|
||||||
|
Result<?> result = resourceService.uploadResource(1L, createResourceDTO, emptyFile);
|
||||||
|
|
||||||
|
// 验证结果
|
||||||
|
assertFalse(result.isSuccess());
|
||||||
|
assertEquals(400, result.getCode());
|
||||||
|
assertEquals("文件不能为空", result.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testUploadResource_InvalidFileType() {
|
||||||
|
// 测试不支持的文件类型
|
||||||
|
MockMultipartFile invalidFile = new MockMultipartFile(
|
||||||
|
"file",
|
||||||
|
"test.exe",
|
||||||
|
"application/octet-stream",
|
||||||
|
"test content".getBytes()
|
||||||
|
);
|
||||||
|
|
||||||
|
// 执行测试
|
||||||
|
Result<?> result = resourceService.uploadResource(1L, createResourceDTO, invalidFile);
|
||||||
|
|
||||||
|
// 验证结果
|
||||||
|
assertFalse(result.isSuccess());
|
||||||
|
assertEquals(400, result.getCode());
|
||||||
|
assertTrue(result.getMessage().contains("不支持的文件类型"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testGetResourceDetail_Success() {
|
||||||
|
// Mock 依赖方法
|
||||||
|
when(resourceMapper.findById(1L)).thenReturn(testResource);
|
||||||
|
when(userMapper.findById(1L)).thenReturn(testUser);
|
||||||
|
when(categoryMapper.findById(1L)).thenReturn(testCategory);
|
||||||
|
|
||||||
|
// 执行测试
|
||||||
|
Result<?> result = resourceService.getResourceDetail(1L, 1L);
|
||||||
|
|
||||||
|
// 验证结果
|
||||||
|
assertTrue(result.isSuccess());
|
||||||
|
assertNotNull(result.getData());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testGetResourceDetail_ResourceNotFound() {
|
||||||
|
// Mock 资源不存在
|
||||||
|
when(resourceMapper.findById(1L)).thenReturn(null);
|
||||||
|
|
||||||
|
// 执行测试
|
||||||
|
Result<?> result = resourceService.getResourceDetail(1L, 1L);
|
||||||
|
|
||||||
|
// 验证结果
|
||||||
|
assertFalse(result.isSuccess());
|
||||||
|
assertEquals(404, result.getCode());
|
||||||
|
assertEquals("资源不存在", result.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testGetResourceList_Success() {
|
||||||
|
// Mock 资源列表
|
||||||
|
List<Resource> resources = Arrays.asList(testResource);
|
||||||
|
when(resourceMapper.findByConditions(any(), any(), any(), anyInt(), anyInt())).thenReturn(resources);
|
||||||
|
when(resourceMapper.countByConditions(any(), any(), any())).thenReturn(1);
|
||||||
|
|
||||||
|
// 执行测试
|
||||||
|
Result<?> result = resourceService.getResourceList(1L, 1L, "测试", 1, 10, 1L);
|
||||||
|
|
||||||
|
// 验证结果
|
||||||
|
assertTrue(result.isSuccess());
|
||||||
|
assertNotNull(result.getData());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testUpdateResource_Success() {
|
||||||
|
// Mock 依赖方法
|
||||||
|
when(resourceMapper.findById(1L)).thenReturn(testResource);
|
||||||
|
when(categoryMapper.findById(1L)).thenReturn(testCategory);
|
||||||
|
when(resourceMapper.update(any(Resource.class))).thenReturn(1);
|
||||||
|
|
||||||
|
// 执行测试
|
||||||
|
Result<?> result = resourceService.updateResource(1L, 1L, createResourceDTO);
|
||||||
|
|
||||||
|
// 验证结果
|
||||||
|
assertTrue(result.isSuccess());
|
||||||
|
assertEquals("资源更新成功", result.getMessage());
|
||||||
|
|
||||||
|
// 验证方法调用
|
||||||
|
verify(resourceMapper).update(any(Resource.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testUpdateResource_Unauthorized() {
|
||||||
|
// Mock 其他用户的资源
|
||||||
|
testResource.setUserId(2L);
|
||||||
|
when(resourceMapper.findById(1L)).thenReturn(testResource);
|
||||||
|
|
||||||
|
// 执行测试
|
||||||
|
Result<?> result = resourceService.updateResource(1L, 1L, createResourceDTO);
|
||||||
|
|
||||||
|
// 验证结果
|
||||||
|
assertFalse(result.isSuccess());
|
||||||
|
assertEquals(403, result.getCode());
|
||||||
|
assertEquals("无权限修改此资源", result.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testDeleteResource_Success() {
|
||||||
|
// Mock 依赖方法
|
||||||
|
when(resourceMapper.findById(1L)).thenReturn(testResource);
|
||||||
|
when(resourceMapper.delete(1L)).thenReturn(1);
|
||||||
|
|
||||||
|
// 执行测试
|
||||||
|
Result<?> result = resourceService.deleteResource(1L, 1L);
|
||||||
|
|
||||||
|
// 验证结果
|
||||||
|
assertTrue(result.isSuccess());
|
||||||
|
assertEquals("资源删除成功", result.getMessage());
|
||||||
|
|
||||||
|
// 验证方法调用
|
||||||
|
verify(resourceMapper).delete(1L);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testDownloadResource_Success() {
|
||||||
|
// Mock 依赖方法
|
||||||
|
when(resourceMapper.findById(1L)).thenReturn(testResource);
|
||||||
|
|
||||||
|
// 执行测试
|
||||||
|
Result<?> result = resourceService.downloadResource(1L, 1L);
|
||||||
|
|
||||||
|
// 验证结果
|
||||||
|
assertTrue(result.isSuccess());
|
||||||
|
assertNotNull(result.getData());
|
||||||
|
|
||||||
|
// 验证下载量增加
|
||||||
|
verify(resourceMapper).updateDownloadCount(1L);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testLikeResource_Success() {
|
||||||
|
// Mock 依赖方法
|
||||||
|
when(resourceMapper.findById(1L)).thenReturn(testResource);
|
||||||
|
when(resourceMapper.isLikedByUser(1L, 1L)).thenReturn(false);
|
||||||
|
when(resourceMapper.insertLike(1L, 1L)).thenReturn(1);
|
||||||
|
|
||||||
|
// 执行测试
|
||||||
|
Result<?> result = resourceService.likeResource(1L, 1L);
|
||||||
|
|
||||||
|
// 验证结果
|
||||||
|
assertTrue(result.isSuccess());
|
||||||
|
assertEquals("点赞成功", result.getMessage());
|
||||||
|
|
||||||
|
// 验证方法调用
|
||||||
|
verify(resourceMapper).insertLike(1L, 1L);
|
||||||
|
verify(resourceMapper).updateLikeCount(1L, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testUnlikeResource_Success() {
|
||||||
|
// Mock 已点赞状态
|
||||||
|
when(resourceMapper.findById(1L)).thenReturn(testResource);
|
||||||
|
when(resourceMapper.isLikedByUser(1L, 1L)).thenReturn(true);
|
||||||
|
when(resourceMapper.deleteLike(1L, 1L)).thenReturn(1);
|
||||||
|
|
||||||
|
// 执行测试
|
||||||
|
Result<?> result = resourceService.likeResource(1L, 1L);
|
||||||
|
|
||||||
|
// 验证结果
|
||||||
|
assertTrue(result.isSuccess());
|
||||||
|
assertEquals("取消点赞成功", result.getMessage());
|
||||||
|
|
||||||
|
// 验证方法调用
|
||||||
|
verify(resourceMapper).deleteLike(1L, 1L);
|
||||||
|
verify(resourceMapper).updateLikeCount(1L, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testGetUserResources_Success() {
|
||||||
|
// Mock 用户资源列表
|
||||||
|
List<Resource> userResources = Arrays.asList(testResource);
|
||||||
|
when(resourceMapper.findByUserId(eq(1L), anyInt(), anyInt())).thenReturn(userResources);
|
||||||
|
when(resourceMapper.countByUserId(1L)).thenReturn(1);
|
||||||
|
|
||||||
|
// 执行测试
|
||||||
|
Result<?> result = resourceService.getUserResources(1L, 1, 10);
|
||||||
|
|
||||||
|
// 验证结果
|
||||||
|
assertTrue(result.isSuccess());
|
||||||
|
assertNotNull(result.getData());
|
||||||
|
|
||||||
|
// 验证方法调用
|
||||||
|
verify(resourceMapper).findByUserId(eq(1L), anyInt(), anyInt());
|
||||||
|
verify(resourceMapper).countByUserId(1L);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,370 @@
|
|||||||
|
package com.unilife.service;
|
||||||
|
|
||||||
|
import com.unilife.common.result.Result;
|
||||||
|
import com.unilife.mapper.ScheduleMapper;
|
||||||
|
import com.unilife.mapper.UserMapper;
|
||||||
|
import com.unilife.model.dto.CreateScheduleDTO;
|
||||||
|
import com.unilife.model.entity.Schedule;
|
||||||
|
import com.unilife.model.entity.User;
|
||||||
|
import com.unilife.service.impl.ScheduleServiceImpl;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
import static org.mockito.ArgumentMatchers.*;
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
@SpringBootTest
|
||||||
|
class ScheduleServiceTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private ScheduleMapper scheduleMapper;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private UserMapper userMapper;
|
||||||
|
|
||||||
|
@InjectMocks
|
||||||
|
private ScheduleServiceImpl scheduleService;
|
||||||
|
|
||||||
|
private User testUser;
|
||||||
|
private Schedule testSchedule;
|
||||||
|
private CreateScheduleDTO createScheduleDTO;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
// 初始化测试数据
|
||||||
|
testUser = new User();
|
||||||
|
testUser.setId(1L);
|
||||||
|
testUser.setNickname("测试用户");
|
||||||
|
testUser.setAvatar("avatar.jpg");
|
||||||
|
|
||||||
|
testSchedule = new Schedule();
|
||||||
|
testSchedule.setId(1L);
|
||||||
|
testSchedule.setTitle("测试课程");
|
||||||
|
testSchedule.setDescription("测试课程描述");
|
||||||
|
testSchedule.setStartTime(LocalDateTime.of(2024, 1, 15, 9, 0));
|
||||||
|
testSchedule.setEndTime(LocalDateTime.of(2024, 1, 15, 10, 30));
|
||||||
|
testSchedule.setLocation("教学楼A101");
|
||||||
|
testSchedule.setType("COURSE");
|
||||||
|
testSchedule.setRepeatType("WEEKLY");
|
||||||
|
testSchedule.setRepeatEnd(LocalDateTime.of(2024, 6, 15, 10, 30));
|
||||||
|
testSchedule.setUserId(1L);
|
||||||
|
testSchedule.setCreatedAt(LocalDateTime.now());
|
||||||
|
testSchedule.setUpdatedAt(LocalDateTime.now());
|
||||||
|
|
||||||
|
createScheduleDTO = new CreateScheduleDTO();
|
||||||
|
createScheduleDTO.setTitle("新课程");
|
||||||
|
createScheduleDTO.setDescription("新课程描述");
|
||||||
|
createScheduleDTO.setStartTime(LocalDateTime.of(2024, 1, 16, 14, 0));
|
||||||
|
createScheduleDTO.setEndTime(LocalDateTime.of(2024, 1, 16, 15, 30));
|
||||||
|
createScheduleDTO.setLocation("教学楼B201");
|
||||||
|
createScheduleDTO.setType("COURSE");
|
||||||
|
createScheduleDTO.setRepeatType("WEEKLY");
|
||||||
|
createScheduleDTO.setRepeatEnd(LocalDateTime.of(2024, 6, 16, 15, 30));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCreateSchedule_Success() {
|
||||||
|
// Mock 依赖方法
|
||||||
|
when(userMapper.findById(1L)).thenReturn(testUser);
|
||||||
|
when(scheduleMapper.findConflictingSchedules(eq(1L), any(), any(), any())).thenReturn(Arrays.asList());
|
||||||
|
when(scheduleMapper.insert(any(Schedule.class))).thenReturn(1);
|
||||||
|
|
||||||
|
// 执行测试
|
||||||
|
Result<?> result = scheduleService.createSchedule(1L, createScheduleDTO);
|
||||||
|
|
||||||
|
// 验证结果
|
||||||
|
assertTrue(result.isSuccess());
|
||||||
|
assertEquals("日程创建成功", result.getMessage());
|
||||||
|
|
||||||
|
// 验证方法调用
|
||||||
|
verify(userMapper).findById(1L);
|
||||||
|
verify(scheduleMapper).findConflictingSchedules(eq(1L), any(), any(), any());
|
||||||
|
verify(scheduleMapper).insert(any(Schedule.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCreateSchedule_UserNotFound() {
|
||||||
|
// Mock 用户不存在
|
||||||
|
when(userMapper.findById(1L)).thenReturn(null);
|
||||||
|
|
||||||
|
// 执行测试
|
||||||
|
Result<?> result = scheduleService.createSchedule(1L, createScheduleDTO);
|
||||||
|
|
||||||
|
// 验证结果
|
||||||
|
assertFalse(result.isSuccess());
|
||||||
|
assertEquals(404, result.getCode());
|
||||||
|
assertEquals("用户不存在", result.getMessage());
|
||||||
|
|
||||||
|
// 验证不会尝试创建日程
|
||||||
|
verify(scheduleMapper, never()).insert(any(Schedule.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCreateSchedule_TimeConflict() {
|
||||||
|
// Mock 时间冲突
|
||||||
|
Schedule conflictingSchedule = new Schedule();
|
||||||
|
conflictingSchedule.setId(2L);
|
||||||
|
conflictingSchedule.setTitle("冲突课程");
|
||||||
|
conflictingSchedule.setStartTime(LocalDateTime.of(2024, 1, 16, 14, 30));
|
||||||
|
conflictingSchedule.setEndTime(LocalDateTime.of(2024, 1, 16, 16, 0));
|
||||||
|
|
||||||
|
when(userMapper.findById(1L)).thenReturn(testUser);
|
||||||
|
when(scheduleMapper.findConflictingSchedules(eq(1L), any(), any(), any()))
|
||||||
|
.thenReturn(Arrays.asList(conflictingSchedule));
|
||||||
|
|
||||||
|
// 执行测试
|
||||||
|
Result<?> result = scheduleService.createSchedule(1L, createScheduleDTO);
|
||||||
|
|
||||||
|
// 验证结果
|
||||||
|
assertFalse(result.isSuccess());
|
||||||
|
assertEquals(400, result.getCode());
|
||||||
|
assertTrue(result.getMessage().contains("时间冲突"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCreateSchedule_InvalidTimeRange() {
|
||||||
|
// 测试结束时间早于开始时间
|
||||||
|
createScheduleDTO.setStartTime(LocalDateTime.of(2024, 1, 16, 16, 0));
|
||||||
|
createScheduleDTO.setEndTime(LocalDateTime.of(2024, 1, 16, 14, 0));
|
||||||
|
|
||||||
|
// 执行测试
|
||||||
|
Result<?> result = scheduleService.createSchedule(1L, createScheduleDTO);
|
||||||
|
|
||||||
|
// 验证结果
|
||||||
|
assertFalse(result.isSuccess());
|
||||||
|
assertEquals(400, result.getCode());
|
||||||
|
assertEquals("结束时间不能早于开始时间", result.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testGetScheduleDetail_Success() {
|
||||||
|
// Mock 依赖方法
|
||||||
|
when(scheduleMapper.findById(1L)).thenReturn(testSchedule);
|
||||||
|
|
||||||
|
// 执行测试
|
||||||
|
Result<?> result = scheduleService.getScheduleDetail(1L, 1L);
|
||||||
|
|
||||||
|
// 验证结果
|
||||||
|
assertTrue(result.isSuccess());
|
||||||
|
assertNotNull(result.getData());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testGetScheduleDetail_NotFound() {
|
||||||
|
// Mock 日程不存在
|
||||||
|
when(scheduleMapper.findById(1L)).thenReturn(null);
|
||||||
|
|
||||||
|
// 执行测试
|
||||||
|
Result<?> result = scheduleService.getScheduleDetail(1L, 1L);
|
||||||
|
|
||||||
|
// 验证结果
|
||||||
|
assertFalse(result.isSuccess());
|
||||||
|
assertEquals(404, result.getCode());
|
||||||
|
assertEquals("日程不存在", result.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testGetScheduleDetail_Unauthorized() {
|
||||||
|
// Mock 其他用户的日程
|
||||||
|
testSchedule.setUserId(2L);
|
||||||
|
when(scheduleMapper.findById(1L)).thenReturn(testSchedule);
|
||||||
|
|
||||||
|
// 执行测试
|
||||||
|
Result<?> result = scheduleService.getScheduleDetail(1L, 1L);
|
||||||
|
|
||||||
|
// 验证结果
|
||||||
|
assertFalse(result.isSuccess());
|
||||||
|
assertEquals(403, result.getCode());
|
||||||
|
assertEquals("无权限查看此日程", result.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testGetScheduleList_Success() {
|
||||||
|
// Mock 日程列表
|
||||||
|
List<Schedule> schedules = Arrays.asList(testSchedule);
|
||||||
|
when(scheduleMapper.findByUserId(1L)).thenReturn(schedules);
|
||||||
|
|
||||||
|
// 执行测试
|
||||||
|
Result<?> result = scheduleService.getScheduleList(1L);
|
||||||
|
|
||||||
|
// 验证结果
|
||||||
|
assertTrue(result.isSuccess());
|
||||||
|
assertNotNull(result.getData());
|
||||||
|
|
||||||
|
// 验证方法调用
|
||||||
|
verify(scheduleMapper).findByUserId(1L);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testGetScheduleListByTimeRange_Success() {
|
||||||
|
LocalDateTime startTime = LocalDateTime.of(2024, 1, 1, 0, 0);
|
||||||
|
LocalDateTime endTime = LocalDateTime.of(2024, 1, 31, 23, 59);
|
||||||
|
|
||||||
|
// Mock 时间范围内的日程列表
|
||||||
|
List<Schedule> schedules = Arrays.asList(testSchedule);
|
||||||
|
when(scheduleMapper.findByUserIdAndTimeRange(1L, startTime, endTime)).thenReturn(schedules);
|
||||||
|
|
||||||
|
// 执行测试
|
||||||
|
Result<?> result = scheduleService.getScheduleListByTimeRange(1L, startTime, endTime);
|
||||||
|
|
||||||
|
// 验证结果
|
||||||
|
assertTrue(result.isSuccess());
|
||||||
|
assertNotNull(result.getData());
|
||||||
|
|
||||||
|
// 验证方法调用
|
||||||
|
verify(scheduleMapper).findByUserIdAndTimeRange(1L, startTime, endTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testUpdateSchedule_Success() {
|
||||||
|
// Mock 依赖方法
|
||||||
|
when(scheduleMapper.findById(1L)).thenReturn(testSchedule);
|
||||||
|
when(scheduleMapper.findConflictingSchedules(eq(1L), any(), any(), eq(1L))).thenReturn(Arrays.asList());
|
||||||
|
when(scheduleMapper.update(any(Schedule.class))).thenReturn(1);
|
||||||
|
|
||||||
|
// 执行测试
|
||||||
|
Result<?> result = scheduleService.updateSchedule(1L, 1L, createScheduleDTO);
|
||||||
|
|
||||||
|
// 验证结果
|
||||||
|
assertTrue(result.isSuccess());
|
||||||
|
assertEquals("日程更新成功", result.getMessage());
|
||||||
|
|
||||||
|
// 验证方法调用
|
||||||
|
verify(scheduleMapper).update(any(Schedule.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testUpdateSchedule_Unauthorized() {
|
||||||
|
// Mock 其他用户的日程
|
||||||
|
testSchedule.setUserId(2L);
|
||||||
|
when(scheduleMapper.findById(1L)).thenReturn(testSchedule);
|
||||||
|
|
||||||
|
// 执行测试
|
||||||
|
Result<?> result = scheduleService.updateSchedule(1L, 1L, createScheduleDTO);
|
||||||
|
|
||||||
|
// 验证结果
|
||||||
|
assertFalse(result.isSuccess());
|
||||||
|
assertEquals(403, result.getCode());
|
||||||
|
assertEquals("无权限修改此日程", result.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testDeleteSchedule_Success() {
|
||||||
|
// Mock 依赖方法
|
||||||
|
when(scheduleMapper.findById(1L)).thenReturn(testSchedule);
|
||||||
|
when(scheduleMapper.delete(1L)).thenReturn(1);
|
||||||
|
|
||||||
|
// 执行测试
|
||||||
|
Result<?> result = scheduleService.deleteSchedule(1L, 1L);
|
||||||
|
|
||||||
|
// 验证结果
|
||||||
|
assertTrue(result.isSuccess());
|
||||||
|
assertEquals("日程删除成功", result.getMessage());
|
||||||
|
|
||||||
|
// 验证方法调用
|
||||||
|
verify(scheduleMapper).delete(1L);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCheckScheduleConflict_NoConflict() {
|
||||||
|
LocalDateTime startTime = LocalDateTime.of(2024, 1, 16, 14, 0);
|
||||||
|
LocalDateTime endTime = LocalDateTime.of(2024, 1, 16, 15, 30);
|
||||||
|
|
||||||
|
// Mock 无冲突
|
||||||
|
when(scheduleMapper.findConflictingSchedules(eq(1L), eq(startTime), eq(endTime), any()))
|
||||||
|
.thenReturn(Arrays.asList());
|
||||||
|
|
||||||
|
// 执行测试
|
||||||
|
Result<?> result = scheduleService.checkScheduleConflict(1L, startTime, endTime, null);
|
||||||
|
|
||||||
|
// 验证结果
|
||||||
|
assertTrue(result.isSuccess());
|
||||||
|
assertEquals("无时间冲突", result.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCheckScheduleConflict_HasConflict() {
|
||||||
|
LocalDateTime startTime = LocalDateTime.of(2024, 1, 16, 14, 0);
|
||||||
|
LocalDateTime endTime = LocalDateTime.of(2024, 1, 16, 15, 30);
|
||||||
|
|
||||||
|
// Mock 有冲突
|
||||||
|
when(scheduleMapper.findConflictingSchedules(eq(1L), eq(startTime), eq(endTime), any()))
|
||||||
|
.thenReturn(Arrays.asList(testSchedule));
|
||||||
|
|
||||||
|
// 执行测试
|
||||||
|
Result<?> result = scheduleService.checkScheduleConflict(1L, startTime, endTime, null);
|
||||||
|
|
||||||
|
// 验证结果
|
||||||
|
assertFalse(result.isSuccess());
|
||||||
|
assertEquals(400, result.getCode());
|
||||||
|
assertTrue(result.getMessage().contains("时间冲突"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testProcessScheduleReminders_Success() {
|
||||||
|
// Mock 需要提醒的日程
|
||||||
|
List<Schedule> upcomingSchedules = Arrays.asList(testSchedule);
|
||||||
|
when(scheduleMapper.findUpcomingSchedules(any())).thenReturn(upcomingSchedules);
|
||||||
|
|
||||||
|
// 执行测试
|
||||||
|
Result<?> result = scheduleService.processScheduleReminders();
|
||||||
|
|
||||||
|
// 验证结果
|
||||||
|
assertTrue(result.isSuccess());
|
||||||
|
assertEquals("提醒处理完成", result.getMessage());
|
||||||
|
|
||||||
|
// 验证方法调用
|
||||||
|
verify(scheduleMapper).findUpcomingSchedules(any());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCreateSchedule_WeeklyRepeat() {
|
||||||
|
// 测试周重复日程
|
||||||
|
createScheduleDTO.setRepeatType("WEEKLY");
|
||||||
|
createScheduleDTO.setRepeatEnd(LocalDateTime.of(2024, 3, 16, 15, 30));
|
||||||
|
|
||||||
|
when(userMapper.findById(1L)).thenReturn(testUser);
|
||||||
|
when(scheduleMapper.findConflictingSchedules(eq(1L), any(), any(), any())).thenReturn(Arrays.asList());
|
||||||
|
when(scheduleMapper.insert(any(Schedule.class))).thenReturn(1);
|
||||||
|
|
||||||
|
// 执行测试
|
||||||
|
Result<?> result = scheduleService.createSchedule(1L, createScheduleDTO);
|
||||||
|
|
||||||
|
// 验证结果
|
||||||
|
assertTrue(result.isSuccess());
|
||||||
|
|
||||||
|
// 验证会创建多个重复的日程实例
|
||||||
|
verify(scheduleMapper, atLeast(1)).insert(any(Schedule.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCreateSchedule_DailyRepeat() {
|
||||||
|
// 测试日重复日程
|
||||||
|
createScheduleDTO.setRepeatType("DAILY");
|
||||||
|
createScheduleDTO.setRepeatEnd(LocalDateTime.of(2024, 1, 20, 15, 30));
|
||||||
|
|
||||||
|
when(userMapper.findById(1L)).thenReturn(testUser);
|
||||||
|
when(scheduleMapper.findConflictingSchedules(eq(1L), any(), any(), any())).thenReturn(Arrays.asList());
|
||||||
|
when(scheduleMapper.insert(any(Schedule.class))).thenReturn(1);
|
||||||
|
|
||||||
|
// 执行测试
|
||||||
|
Result<?> result = scheduleService.createSchedule(1L, createScheduleDTO);
|
||||||
|
|
||||||
|
// 验证结果
|
||||||
|
assertTrue(result.isSuccess());
|
||||||
|
|
||||||
|
// 验证会创建多个重复的日程实例
|
||||||
|
verify(scheduleMapper, atLeast(1)).insert(any(Schedule.class));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,438 @@
|
|||||||
|
package com.unilife.service;
|
||||||
|
|
||||||
|
import com.unilife.common.result.Result;
|
||||||
|
import com.unilife.mapper.UserMapper;
|
||||||
|
import com.unilife.model.dto.CreateUserDTO;
|
||||||
|
import com.unilife.model.dto.UpdateUserDTO;
|
||||||
|
import com.unilife.model.dto.LoginDTO;
|
||||||
|
import com.unilife.model.entity.User;
|
||||||
|
import com.unilife.service.impl.UserServiceImpl;
|
||||||
|
import com.unilife.utils.JwtUtil;
|
||||||
|
import com.unilife.utils.PasswordUtil;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.MockedStatic;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||||
|
import org.springframework.mail.SimpleMailMessage;
|
||||||
|
import org.springframework.mail.javamail.JavaMailSender;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
import static org.mockito.ArgumentMatchers.*;
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
@SpringBootTest
|
||||||
|
class UserServiceTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private UserMapper userMapper;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private StringRedisTemplate redisTemplate;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private JavaMailSender mailSender;
|
||||||
|
|
||||||
|
@InjectMocks
|
||||||
|
private UserServiceImpl userService;
|
||||||
|
|
||||||
|
private User testUser;
|
||||||
|
private CreateUserDTO createUserDTO;
|
||||||
|
private UpdateUserDTO updateUserDTO;
|
||||||
|
private LoginDTO loginDTO;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
// 初始化测试数据
|
||||||
|
testUser = new User();
|
||||||
|
testUser.setId(1L);
|
||||||
|
testUser.setUsername("testuser");
|
||||||
|
testUser.setEmail("test@example.com");
|
||||||
|
testUser.setNickname("测试用户");
|
||||||
|
testUser.setPassword("$2a$10$encrypted_password"); // 模拟加密后的密码
|
||||||
|
testUser.setAvatar("avatar.jpg");
|
||||||
|
testUser.setStatus(1);
|
||||||
|
testUser.setCreatedAt(LocalDateTime.now());
|
||||||
|
testUser.setUpdatedAt(LocalDateTime.now());
|
||||||
|
|
||||||
|
createUserDTO = new CreateUserDTO();
|
||||||
|
createUserDTO.setUsername("newuser");
|
||||||
|
createUserDTO.setEmail("newuser@example.com");
|
||||||
|
createUserDTO.setNickname("新用户");
|
||||||
|
createUserDTO.setPassword("password123");
|
||||||
|
|
||||||
|
updateUserDTO = new UpdateUserDTO();
|
||||||
|
updateUserDTO.setNickname("更新后的昵称");
|
||||||
|
updateUserDTO.setAvatar("new_avatar.jpg");
|
||||||
|
|
||||||
|
loginDTO = new LoginDTO();
|
||||||
|
loginDTO.setUsername("testuser");
|
||||||
|
loginDTO.setPassword("password123");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testRegister_Success() {
|
||||||
|
// Mock 依赖方法
|
||||||
|
when(userMapper.findByUsername("newuser")).thenReturn(null);
|
||||||
|
when(userMapper.findByEmail("newuser@example.com")).thenReturn(null);
|
||||||
|
when(userMapper.insert(any(User.class))).thenReturn(1);
|
||||||
|
|
||||||
|
try (MockedStatic<PasswordUtil> passwordUtil = mockStatic(PasswordUtil.class)) {
|
||||||
|
passwordUtil.when(() -> PasswordUtil.encode("password123"))
|
||||||
|
.thenReturn("$2a$10$encrypted_password");
|
||||||
|
|
||||||
|
// 执行测试
|
||||||
|
Result<?> result = userService.register(createUserDTO);
|
||||||
|
|
||||||
|
// 验证结果
|
||||||
|
assertTrue(result.isSuccess());
|
||||||
|
assertEquals("注册成功", result.getMessage());
|
||||||
|
|
||||||
|
// 验证方法调用
|
||||||
|
verify(userMapper).findByUsername("newuser");
|
||||||
|
verify(userMapper).findByEmail("newuser@example.com");
|
||||||
|
verify(userMapper).insert(any(User.class));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testRegister_UsernameExists() {
|
||||||
|
// Mock 用户名已存在
|
||||||
|
when(userMapper.findByUsername("newuser")).thenReturn(testUser);
|
||||||
|
|
||||||
|
// 执行测试
|
||||||
|
Result<?> result = userService.register(createUserDTO);
|
||||||
|
|
||||||
|
// 验证结果
|
||||||
|
assertFalse(result.isSuccess());
|
||||||
|
assertEquals(400, result.getCode());
|
||||||
|
assertEquals("用户名已存在", result.getMessage());
|
||||||
|
|
||||||
|
// 验证不会尝试插入用户
|
||||||
|
verify(userMapper, never()).insert(any(User.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testRegister_EmailExists() {
|
||||||
|
// Mock 邮箱已存在
|
||||||
|
when(userMapper.findByUsername("newuser")).thenReturn(null);
|
||||||
|
when(userMapper.findByEmail("newuser@example.com")).thenReturn(testUser);
|
||||||
|
|
||||||
|
// 执行测试
|
||||||
|
Result<?> result = userService.register(createUserDTO);
|
||||||
|
|
||||||
|
// 验证结果
|
||||||
|
assertFalse(result.isSuccess());
|
||||||
|
assertEquals(400, result.getCode());
|
||||||
|
assertEquals("邮箱已存在", result.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testLogin_Success() {
|
||||||
|
// Mock 依赖方法
|
||||||
|
when(userMapper.findByUsername("testuser")).thenReturn(testUser);
|
||||||
|
|
||||||
|
try (MockedStatic<PasswordUtil> passwordUtil = mockStatic(PasswordUtil.class);
|
||||||
|
MockedStatic<JwtUtil> jwtUtil = mockStatic(JwtUtil.class)) {
|
||||||
|
|
||||||
|
passwordUtil.when(() -> PasswordUtil.matches("password123", "$2a$10$encrypted_password"))
|
||||||
|
.thenReturn(true);
|
||||||
|
jwtUtil.when(() -> JwtUtil.generateToken(1L))
|
||||||
|
.thenReturn("mock_jwt_token");
|
||||||
|
|
||||||
|
// 执行测试
|
||||||
|
Result<?> result = userService.login(loginDTO);
|
||||||
|
|
||||||
|
// 验证结果
|
||||||
|
assertTrue(result.isSuccess());
|
||||||
|
assertEquals("登录成功", result.getMessage());
|
||||||
|
assertNotNull(result.getData());
|
||||||
|
|
||||||
|
// 验证方法调用
|
||||||
|
verify(userMapper).findByUsername("testuser");
|
||||||
|
verify(userMapper).updateLastLoginTime(1L);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testLogin_UserNotFound() {
|
||||||
|
// Mock 用户不存在
|
||||||
|
when(userMapper.findByUsername("testuser")).thenReturn(null);
|
||||||
|
|
||||||
|
// 执行测试
|
||||||
|
Result<?> result = userService.login(loginDTO);
|
||||||
|
|
||||||
|
// 验证结果
|
||||||
|
assertFalse(result.isSuccess());
|
||||||
|
assertEquals(401, result.getCode());
|
||||||
|
assertEquals("用户名或密码错误", result.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testLogin_PasswordIncorrect() {
|
||||||
|
// Mock 密码错误
|
||||||
|
when(userMapper.findByUsername("testuser")).thenReturn(testUser);
|
||||||
|
|
||||||
|
try (MockedStatic<PasswordUtil> passwordUtil = mockStatic(PasswordUtil.class)) {
|
||||||
|
passwordUtil.when(() -> PasswordUtil.matches("password123", "$2a$10$encrypted_password"))
|
||||||
|
.thenReturn(false);
|
||||||
|
|
||||||
|
// 执行测试
|
||||||
|
Result<?> result = userService.login(loginDTO);
|
||||||
|
|
||||||
|
// 验证结果
|
||||||
|
assertFalse(result.isSuccess());
|
||||||
|
assertEquals(401, result.getCode());
|
||||||
|
assertEquals("用户名或密码错误", result.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testLogin_UserDisabled() {
|
||||||
|
// Mock 用户被禁用
|
||||||
|
testUser.setStatus(0);
|
||||||
|
when(userMapper.findByUsername("testuser")).thenReturn(testUser);
|
||||||
|
|
||||||
|
try (MockedStatic<PasswordUtil> passwordUtil = mockStatic(PasswordUtil.class)) {
|
||||||
|
passwordUtil.when(() -> PasswordUtil.matches("password123", "$2a$10$encrypted_password"))
|
||||||
|
.thenReturn(true);
|
||||||
|
|
||||||
|
// 执行测试
|
||||||
|
Result<?> result = userService.login(loginDTO);
|
||||||
|
|
||||||
|
// 验证结果
|
||||||
|
assertFalse(result.isSuccess());
|
||||||
|
assertEquals(403, result.getCode());
|
||||||
|
assertEquals("账户已被禁用", result.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testGetUserInfo_Success() {
|
||||||
|
// Mock 依赖方法
|
||||||
|
when(userMapper.findById(1L)).thenReturn(testUser);
|
||||||
|
|
||||||
|
// 执行测试
|
||||||
|
Result<?> result = userService.getUserInfo(1L);
|
||||||
|
|
||||||
|
// 验证结果
|
||||||
|
assertTrue(result.isSuccess());
|
||||||
|
assertNotNull(result.getData());
|
||||||
|
|
||||||
|
// 验证方法调用
|
||||||
|
verify(userMapper).findById(1L);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testGetUserInfo_UserNotFound() {
|
||||||
|
// Mock 用户不存在
|
||||||
|
when(userMapper.findById(1L)).thenReturn(null);
|
||||||
|
|
||||||
|
// 执行测试
|
||||||
|
Result<?> result = userService.getUserInfo(1L);
|
||||||
|
|
||||||
|
// 验证结果
|
||||||
|
assertFalse(result.isSuccess());
|
||||||
|
assertEquals(404, result.getCode());
|
||||||
|
assertEquals("用户不存在", result.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testUpdateUserInfo_Success() {
|
||||||
|
// Mock 依赖方法
|
||||||
|
when(userMapper.findById(1L)).thenReturn(testUser);
|
||||||
|
when(userMapper.update(any(User.class))).thenReturn(1);
|
||||||
|
|
||||||
|
// 执行测试
|
||||||
|
Result<?> result = userService.updateUserInfo(1L, updateUserDTO);
|
||||||
|
|
||||||
|
// 验证结果
|
||||||
|
assertTrue(result.isSuccess());
|
||||||
|
assertEquals("用户信息更新成功", result.getMessage());
|
||||||
|
|
||||||
|
// 验证方法调用
|
||||||
|
verify(userMapper).update(any(User.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSendEmailVerificationCode_Success() {
|
||||||
|
String email = "test@example.com";
|
||||||
|
String verificationCode = "123456";
|
||||||
|
|
||||||
|
// Mock Redis操作
|
||||||
|
when(redisTemplate.opsForValue()).thenReturn(mock(org.springframework.data.redis.core.ValueOperations.class));
|
||||||
|
|
||||||
|
// 执行测试
|
||||||
|
Result<?> result = userService.sendEmailVerificationCode(email);
|
||||||
|
|
||||||
|
// 验证结果
|
||||||
|
assertTrue(result.isSuccess());
|
||||||
|
assertEquals("验证码发送成功", result.getMessage());
|
||||||
|
|
||||||
|
// 验证邮件发送
|
||||||
|
verify(mailSender).send(any(SimpleMailMessage.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testVerifyEmailCode_Success() {
|
||||||
|
String email = "test@example.com";
|
||||||
|
String code = "123456";
|
||||||
|
|
||||||
|
// Mock Redis操作
|
||||||
|
when(redisTemplate.opsForValue()).thenReturn(mock(org.springframework.data.redis.core.ValueOperations.class));
|
||||||
|
when(redisTemplate.opsForValue().get("email_code:" + email)).thenReturn(code);
|
||||||
|
|
||||||
|
// 执行测试
|
||||||
|
Result<?> result = userService.verifyEmailCode(email, code);
|
||||||
|
|
||||||
|
// 验证结果
|
||||||
|
assertTrue(result.isSuccess());
|
||||||
|
assertEquals("验证码验证成功", result.getMessage());
|
||||||
|
|
||||||
|
// 验证删除验证码
|
||||||
|
verify(redisTemplate).delete("email_code:" + email);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testVerifyEmailCode_CodeExpired() {
|
||||||
|
String email = "test@example.com";
|
||||||
|
String code = "123456";
|
||||||
|
|
||||||
|
// Mock 验证码不存在(已过期)
|
||||||
|
when(redisTemplate.opsForValue()).thenReturn(mock(org.springframework.data.redis.core.ValueOperations.class));
|
||||||
|
when(redisTemplate.opsForValue().get("email_code:" + email)).thenReturn(null);
|
||||||
|
|
||||||
|
// 执行测试
|
||||||
|
Result<?> result = userService.verifyEmailCode(email, code);
|
||||||
|
|
||||||
|
// 验证结果
|
||||||
|
assertFalse(result.isSuccess());
|
||||||
|
assertEquals(400, result.getCode());
|
||||||
|
assertEquals("验证码已过期", result.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testVerifyEmailCode_CodeIncorrect() {
|
||||||
|
String email = "test@example.com";
|
||||||
|
String code = "123456";
|
||||||
|
String wrongCode = "654321";
|
||||||
|
|
||||||
|
// Mock 验证码错误
|
||||||
|
when(redisTemplate.opsForValue()).thenReturn(mock(org.springframework.data.redis.core.ValueOperations.class));
|
||||||
|
when(redisTemplate.opsForValue().get("email_code:" + email)).thenReturn(wrongCode);
|
||||||
|
|
||||||
|
// 执行测试
|
||||||
|
Result<?> result = userService.verifyEmailCode(email, code);
|
||||||
|
|
||||||
|
// 验证结果
|
||||||
|
assertFalse(result.isSuccess());
|
||||||
|
assertEquals(400, result.getCode());
|
||||||
|
assertEquals("验证码错误", result.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testResetPassword_Success() {
|
||||||
|
String email = "test@example.com";
|
||||||
|
String newPassword = "newpassword123";
|
||||||
|
|
||||||
|
// Mock 依赖方法
|
||||||
|
when(userMapper.findByEmail(email)).thenReturn(testUser);
|
||||||
|
when(userMapper.updatePassword(eq(1L), anyString())).thenReturn(1);
|
||||||
|
|
||||||
|
try (MockedStatic<PasswordUtil> passwordUtil = mockStatic(PasswordUtil.class)) {
|
||||||
|
passwordUtil.when(() -> PasswordUtil.encode(newPassword))
|
||||||
|
.thenReturn("$2a$10$new_encrypted_password");
|
||||||
|
|
||||||
|
// 执行测试
|
||||||
|
Result<?> result = userService.resetPassword(email, newPassword);
|
||||||
|
|
||||||
|
// 验证结果
|
||||||
|
assertTrue(result.isSuccess());
|
||||||
|
assertEquals("密码重置成功", result.getMessage());
|
||||||
|
|
||||||
|
// 验证方法调用
|
||||||
|
verify(userMapper).updatePassword(eq(1L), eq("$2a$10$new_encrypted_password"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testGetUserList_Success() {
|
||||||
|
// Mock 用户列表
|
||||||
|
List<User> users = Arrays.asList(testUser);
|
||||||
|
when(userMapper.findByConditions(any(), any(), anyInt(), anyInt())).thenReturn(users);
|
||||||
|
when(userMapper.countByConditions(any(), any())).thenReturn(1);
|
||||||
|
|
||||||
|
// 执行测试
|
||||||
|
Result<?> result = userService.getUserList("测试", 1, 1, 10);
|
||||||
|
|
||||||
|
// 验证结果
|
||||||
|
assertTrue(result.isSuccess());
|
||||||
|
assertNotNull(result.getData());
|
||||||
|
|
||||||
|
// 验证方法调用
|
||||||
|
verify(userMapper).findByConditions(any(), any(), anyInt(), anyInt());
|
||||||
|
verify(userMapper).countByConditions(any(), any());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testChangePassword_Success() {
|
||||||
|
String oldPassword = "oldpassword";
|
||||||
|
String newPassword = "newpassword123";
|
||||||
|
|
||||||
|
// Mock 依赖方法
|
||||||
|
when(userMapper.findById(1L)).thenReturn(testUser);
|
||||||
|
when(userMapper.updatePassword(eq(1L), anyString())).thenReturn(1);
|
||||||
|
|
||||||
|
try (MockedStatic<PasswordUtil> passwordUtil = mockStatic(PasswordUtil.class)) {
|
||||||
|
passwordUtil.when(() -> PasswordUtil.matches(oldPassword, "$2a$10$encrypted_password"))
|
||||||
|
.thenReturn(true);
|
||||||
|
passwordUtil.when(() -> PasswordUtil.encode(newPassword))
|
||||||
|
.thenReturn("$2a$10$new_encrypted_password");
|
||||||
|
|
||||||
|
// 执行测试
|
||||||
|
Result<?> result = userService.changePassword(1L, oldPassword, newPassword);
|
||||||
|
|
||||||
|
// 验证结果
|
||||||
|
assertTrue(result.isSuccess());
|
||||||
|
assertEquals("密码修改成功", result.getMessage());
|
||||||
|
|
||||||
|
// 验证方法调用
|
||||||
|
verify(userMapper).updatePassword(eq(1L), eq("$2a$10$new_encrypted_password"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testChangePassword_OldPasswordIncorrect() {
|
||||||
|
String oldPassword = "wrongpassword";
|
||||||
|
String newPassword = "newpassword123";
|
||||||
|
|
||||||
|
// Mock 依赖方法
|
||||||
|
when(userMapper.findById(1L)).thenReturn(testUser);
|
||||||
|
|
||||||
|
try (MockedStatic<PasswordUtil> passwordUtil = mockStatic(PasswordUtil.class)) {
|
||||||
|
passwordUtil.when(() -> PasswordUtil.matches(oldPassword, "$2a$10$encrypted_password"))
|
||||||
|
.thenReturn(false);
|
||||||
|
|
||||||
|
// 执行测试
|
||||||
|
Result<?> result = userService.changePassword(1L, oldPassword, newPassword);
|
||||||
|
|
||||||
|
// 验证结果
|
||||||
|
assertFalse(result.isSuccess());
|
||||||
|
assertEquals(400, result.getCode());
|
||||||
|
assertEquals("原密码错误", result.getMessage());
|
||||||
|
|
||||||
|
// 验证不会更新密码
|
||||||
|
verify(userMapper, never()).updatePassword(anyLong(), anyString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,117 @@
|
|||||||
|
package com.unilife.utils;
|
||||||
|
|
||||||
|
import com.unilife.model.dto.*;
|
||||||
|
import com.unilife.model.entity.*;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 测试数据构建工具类
|
||||||
|
* 提供各种实体和DTO的测试数据构建方法
|
||||||
|
*/
|
||||||
|
public class TestDataBuilder {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建测试用户
|
||||||
|
*/
|
||||||
|
public static User buildTestUser() {
|
||||||
|
User user = new User();
|
||||||
|
user.setId(1L);
|
||||||
|
user.setUsername("testuser");
|
||||||
|
user.setEmail("test@example.com");
|
||||||
|
user.setNickname("测试用户");
|
||||||
|
user.setPassword("$2a$10$encrypted_password");
|
||||||
|
user.setAvatar("avatar.jpg");
|
||||||
|
user.setStatus(1);
|
||||||
|
user.setCreatedAt(LocalDateTime.now());
|
||||||
|
user.setUpdatedAt(LocalDateTime.now());
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建测试分类
|
||||||
|
*/
|
||||||
|
public static Category buildTestCategory() {
|
||||||
|
Category category = new Category();
|
||||||
|
category.setId(1L);
|
||||||
|
category.setName("测试分类");
|
||||||
|
category.setDescription("测试分类描述");
|
||||||
|
category.setIcon("test-icon");
|
||||||
|
category.setSort(1);
|
||||||
|
category.setStatus(1);
|
||||||
|
category.setCreatedAt(LocalDateTime.now());
|
||||||
|
category.setUpdatedAt(LocalDateTime.now());
|
||||||
|
return category;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建测试帖子
|
||||||
|
*/
|
||||||
|
public static Post buildTestPost() {
|
||||||
|
Post post = new Post();
|
||||||
|
post.setId(1L);
|
||||||
|
post.setTitle("测试帖子");
|
||||||
|
post.setContent("测试帖子内容");
|
||||||
|
post.setUserId(1L);
|
||||||
|
post.setCategoryId(1L);
|
||||||
|
post.setLikeCount(0);
|
||||||
|
post.setViewCount(0);
|
||||||
|
post.setCommentCount(0);
|
||||||
|
post.setCreatedAt(LocalDateTime.now());
|
||||||
|
post.setUpdatedAt(LocalDateTime.now());
|
||||||
|
return post;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建测试资源
|
||||||
|
*/
|
||||||
|
public static Resource buildTestResource() {
|
||||||
|
Resource resource = new Resource();
|
||||||
|
resource.setId(1L);
|
||||||
|
resource.setTitle("测试资源");
|
||||||
|
resource.setDescription("测试资源描述");
|
||||||
|
resource.setFileName("test.pdf");
|
||||||
|
resource.setFileUrl("http://example.com/test.pdf");
|
||||||
|
resource.setFileSize(1024L);
|
||||||
|
resource.setFileType("pdf");
|
||||||
|
resource.setUserId(1L);
|
||||||
|
resource.setCategoryId(1L);
|
||||||
|
resource.setDownloadCount(0);
|
||||||
|
resource.setLikeCount(0);
|
||||||
|
resource.setCreatedAt(LocalDateTime.now());
|
||||||
|
resource.setUpdatedAt(LocalDateTime.now());
|
||||||
|
return resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建创建帖子DTO
|
||||||
|
*/
|
||||||
|
public static CreatePostDTO buildCreatePostDTO() {
|
||||||
|
CreatePostDTO dto = new CreatePostDTO();
|
||||||
|
dto.setTitle("新帖子标题");
|
||||||
|
dto.setContent("新帖子内容");
|
||||||
|
dto.setCategoryId(1L);
|
||||||
|
return dto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建创建用户DTO
|
||||||
|
*/
|
||||||
|
public static CreateUserDTO buildCreateUserDTO() {
|
||||||
|
CreateUserDTO dto = new CreateUserDTO();
|
||||||
|
dto.setUsername("newuser");
|
||||||
|
dto.setEmail("newuser@example.com");
|
||||||
|
dto.setNickname("新用户");
|
||||||
|
dto.setPassword("password123");
|
||||||
|
return dto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建带有指定ID的用户
|
||||||
|
*/
|
||||||
|
public static User buildTestUser(Long id) {
|
||||||
|
User user = buildTestUser();
|
||||||
|
user.setId(id);
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,66 @@
|
|||||||
|
spring:
|
||||||
|
datasource:
|
||||||
|
driver-class-name: org.h2.Driver
|
||||||
|
url: jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
|
||||||
|
username: sa
|
||||||
|
password:
|
||||||
|
|
||||||
|
h2:
|
||||||
|
console:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
jpa:
|
||||||
|
database-platform: org.hibernate.dialect.H2Dialect
|
||||||
|
hibernate:
|
||||||
|
ddl-auto: create-drop
|
||||||
|
show-sql: true
|
||||||
|
|
||||||
|
redis:
|
||||||
|
host: localhost
|
||||||
|
port: 6379
|
||||||
|
database: 1
|
||||||
|
timeout: 2000ms
|
||||||
|
|
||||||
|
mail:
|
||||||
|
host: smtp.example.com
|
||||||
|
port: 587
|
||||||
|
username: test@example.com
|
||||||
|
password: testpassword
|
||||||
|
properties:
|
||||||
|
mail:
|
||||||
|
smtp:
|
||||||
|
auth: true
|
||||||
|
starttls:
|
||||||
|
enable: true
|
||||||
|
|
||||||
|
mybatis:
|
||||||
|
mapper-locations: classpath:mapper/*.xml
|
||||||
|
type-aliases-package: com.unilife.model.entity
|
||||||
|
configuration:
|
||||||
|
map-underscore-to-camel-case: true
|
||||||
|
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
||||||
|
|
||||||
|
logging:
|
||||||
|
level:
|
||||||
|
com.unilife: DEBUG
|
||||||
|
org.springframework.web: DEBUG
|
||||||
|
pattern:
|
||||||
|
console: "%d{yyyy-MM-dd HH:mm:ss} - %msg%n"
|
||||||
|
|
||||||
|
# JWT配置
|
||||||
|
jwt:
|
||||||
|
secret: test-secret-key-for-unit-testing-purposes-only
|
||||||
|
expiration: 3600000
|
||||||
|
|
||||||
|
# 文件上传配置
|
||||||
|
file:
|
||||||
|
upload:
|
||||||
|
path: /tmp/unilife-test/uploads/
|
||||||
|
max-size: 10MB
|
||||||
|
|
||||||
|
# 测试特定配置
|
||||||
|
test:
|
||||||
|
mock:
|
||||||
|
enabled: true
|
||||||
|
database:
|
||||||
|
cleanup: true
|
Loading…
Reference in new issue