refactor(project): Reorganize frontend and backend directories
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
package com.nuaa.video.user;
|
||||
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
|
||||
/**
|
||||
* 用户服务启动类
|
||||
*/
|
||||
@SpringBootApplication
|
||||
@EnableDiscoveryClient
|
||||
@EnableFeignClients
|
||||
@MapperScan("com.nuaa.video.user.mapper")
|
||||
public class UserApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(UserApplication.class, args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.nuaa.video.user.config;
|
||||
|
||||
import feign.Retryer;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
public class FeignConfig {
|
||||
|
||||
@Bean
|
||||
public Retryer feignRetryer() {
|
||||
// 禁用重试
|
||||
return Retryer.NEVER_RETRY;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.nuaa.video.user.config;
|
||||
|
||||
import com.nuaa.video.common.utils.MinioUtils;
|
||||
import io.minio.MinioClient;
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* MinIO配置类
|
||||
*/
|
||||
@Data
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "minio")
|
||||
public class MinioConfig {
|
||||
|
||||
/**
|
||||
* 服务地址
|
||||
*/
|
||||
private String endpoint;
|
||||
|
||||
/**
|
||||
* 访问密钥
|
||||
*/
|
||||
private String accessKey;
|
||||
|
||||
/**
|
||||
* 密钥
|
||||
*/
|
||||
private String secretKey;
|
||||
|
||||
/**
|
||||
* 存储桶名称
|
||||
*/
|
||||
private String bucketName;
|
||||
|
||||
@Bean
|
||||
public MinioClient minioClient() {
|
||||
return MinioClient.builder()
|
||||
.endpoint(endpoint)
|
||||
.credentials(accessKey, secretKey)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public MinioUtils minioUtils(MinioClient minioClient) {
|
||||
return new MinioUtils(minioClient);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.nuaa.video.user.config;
|
||||
|
||||
import com.nuaa.video.user.filter.JwtAuthenticationFilter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
|
||||
/**
|
||||
* 安全配置类
|
||||
*/
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
@RequiredArgsConstructor
|
||||
public class SecurityConfig {
|
||||
|
||||
private final JwtAuthenticationFilter jwtAuthenticationFilter;
|
||||
|
||||
@Bean
|
||||
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.csrf(csrf -> csrf.disable())
|
||||
.cors(cors -> cors.disable())
|
||||
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
|
||||
.authorizeHttpRequests(auth -> auth
|
||||
.requestMatchers(
|
||||
"/user/profile/public/**",
|
||||
"/user/profile/count/**",
|
||||
"/user/profile/init",
|
||||
"/actuator/**",
|
||||
"/error"
|
||||
).permitAll()
|
||||
.anyRequest().authenticated()
|
||||
)
|
||||
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
|
||||
|
||||
return http.build();
|
||||
}
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
package com.nuaa.video.user.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.nuaa.video.common.model.vo.Result;
|
||||
import com.nuaa.video.user.entity.UserInfo;
|
||||
import com.nuaa.video.user.service.UserInfoService;
|
||||
import com.nuaa.video.user.vo.UserProfileVO;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
/**
|
||||
* 用户管理控制器 - 供管理后台调用
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/user/profile/admin")
|
||||
@RequiredArgsConstructor
|
||||
public class AdminUserController {
|
||||
|
||||
private final UserInfoService userInfoService;
|
||||
|
||||
/**
|
||||
* 获取用户列表 - 管理接口
|
||||
*/
|
||||
@GetMapping("/list")
|
||||
public Result<Page<UserProfileVO>> getUserList(
|
||||
@RequestParam(value = "page", defaultValue = "1") Integer page,
|
||||
@RequestParam(value = "size", defaultValue = "10") Integer size,
|
||||
@RequestParam(value = "keyword", required = false) String keyword) {
|
||||
Page<UserInfo> pageParam = new Page<>(page, size);
|
||||
return userInfoService.getAdminUserList(pageParam, keyword);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户详情 - 管理接口
|
||||
*/
|
||||
@GetMapping("/{userId}")
|
||||
public Result<UserProfileVO> getUserDetail(@PathVariable Long userId) {
|
||||
return userInfoService.getAdminUserDetail(userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 禁用/启用用户 - 管理接口
|
||||
*/
|
||||
@PutMapping("/status/{userId}/{status}")
|
||||
public Result<Void> updateUserStatus(
|
||||
@PathVariable Long userId,
|
||||
@PathVariable Integer status) {
|
||||
return userInfoService.updateUserStatus(userId, status);
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置用户密码 - 管理接口
|
||||
*/
|
||||
@PutMapping("/reset-password/{userId}")
|
||||
public Result<Void> resetUserPassword(
|
||||
@PathVariable Long userId,
|
||||
@RequestParam String newPassword) {
|
||||
return userInfoService.resetUserPassword(userId, newPassword);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户统计信息 - 管理接口
|
||||
*/
|
||||
@GetMapping("/stats")
|
||||
public Result<Object> getUserStats() {
|
||||
return userInfoService.getUserStats();
|
||||
}
|
||||
}
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
package com.nuaa.video.user.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.nuaa.video.common.model.vo.Result;
|
||||
import com.nuaa.video.user.entity.UserInfo;
|
||||
import com.nuaa.video.user.service.UserFollowService;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
/**
|
||||
* 用户关注控制器
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/user/follow")
|
||||
@RequiredArgsConstructor
|
||||
public class UserFollowController {
|
||||
|
||||
private final UserFollowService userFollowService;
|
||||
|
||||
/**
|
||||
* 关注用户
|
||||
*/
|
||||
@PostMapping("/{followId}")
|
||||
public Result<Void> followUser(HttpServletRequest request, @PathVariable Long followId) {
|
||||
Long userId = getUserIdFromRequest(request);
|
||||
if (userId == null) {
|
||||
return Result.error("未获取到用户ID");
|
||||
}
|
||||
return userFollowService.followUser(userId, followId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消关注
|
||||
*/
|
||||
@DeleteMapping("/{followId}")
|
||||
public Result<Void> unfollowUser(HttpServletRequest request, @PathVariable Long followId) {
|
||||
Long userId = getUserIdFromRequest(request);
|
||||
if (userId == null) {
|
||||
return Result.error("未获取到用户ID");
|
||||
}
|
||||
return userFollowService.unfollowUser(userId, followId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户关注列表
|
||||
*/
|
||||
@GetMapping("/following/{userId}")
|
||||
public Result<Page<UserInfo>> getFollowingList(
|
||||
@PathVariable Long userId,
|
||||
@RequestParam(value = "page", defaultValue = "1") Integer page,
|
||||
@RequestParam(value = "size", defaultValue = "10") Integer size) {
|
||||
Page<UserInfo> pageParam = new Page<>(page, size);
|
||||
return userFollowService.getFollowingList(userId, pageParam);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户粉丝列表
|
||||
*/
|
||||
@GetMapping("/followers/{userId}")
|
||||
public Result<Page<UserInfo>> getFollowerList(
|
||||
@PathVariable Long userId,
|
||||
@RequestParam(value = "page", defaultValue = "1") Integer page,
|
||||
@RequestParam(value = "size", defaultValue = "10") Integer size) {
|
||||
Page<UserInfo> pageParam = new Page<>(page, size);
|
||||
return userFollowService.getFollowerList(userId, pageParam);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否已关注
|
||||
*/
|
||||
@GetMapping("/check/{followId}")
|
||||
public Result<Boolean> checkIsFollowed(HttpServletRequest request, @PathVariable Long followId) {
|
||||
Long userId = getUserIdFromRequest(request);
|
||||
if (userId == null) {
|
||||
return Result.error("未获取到用户ID");
|
||||
}
|
||||
return userFollowService.checkIsFollowed(userId, followId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从请求中获取用户ID
|
||||
*/
|
||||
private Long getUserIdFromRequest(HttpServletRequest request) {
|
||||
Object userIdObj = request.getAttribute("userId");
|
||||
if (userIdObj instanceof Long) {
|
||||
return (Long) userIdObj;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
package com.nuaa.video.user.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.nuaa.video.common.model.vo.Result;
|
||||
import com.nuaa.video.user.dto.UserMessageDTO;
|
||||
import com.nuaa.video.user.entity.UserMessage;
|
||||
import com.nuaa.video.user.service.UserMessageService;
|
||||
import com.nuaa.video.user.vo.UserMessageVO;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
/**
|
||||
* 用户私信控制器
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/user/message")
|
||||
@RequiredArgsConstructor
|
||||
public class UserMessageController {
|
||||
|
||||
private final UserMessageService userMessageService;
|
||||
|
||||
/**
|
||||
* 发送私信
|
||||
*/
|
||||
@PostMapping
|
||||
public Result<Void> sendMessage(HttpServletRequest request, @RequestBody @Valid UserMessageDTO messageDTO) {
|
||||
Long userId = getUserIdFromRequest(request);
|
||||
if (userId == null) {
|
||||
return Result.error("未获取到用户ID");
|
||||
}
|
||||
return userMessageService.sendMessage(userId, messageDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取私信列表(按会话分组)
|
||||
*/
|
||||
@GetMapping("/list")
|
||||
public Result<Page<UserMessageVO>> getMessageList(
|
||||
HttpServletRequest request,
|
||||
@RequestParam(value = "page", defaultValue = "1") Integer page,
|
||||
@RequestParam(value = "size", defaultValue = "10") Integer size) {
|
||||
Long userId = getUserIdFromRequest(request);
|
||||
if (userId == null) {
|
||||
return Result.error("未获取到用户ID");
|
||||
}
|
||||
Page<UserMessage> pageParam = new Page<>(page, size);
|
||||
return userMessageService.getMessageList(userId, pageParam);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取与指定用户的私信记录
|
||||
*/
|
||||
@GetMapping("/detail/{targetId}")
|
||||
public Result<Page<UserMessageVO>> getMessageDetail(
|
||||
HttpServletRequest request,
|
||||
@PathVariable Long targetId,
|
||||
@RequestParam(value = "page", defaultValue = "1") Integer page,
|
||||
@RequestParam(value = "size", defaultValue = "20") Integer size) {
|
||||
Long userId = getUserIdFromRequest(request);
|
||||
if (userId == null) {
|
||||
return Result.error("未获取到用户ID");
|
||||
}
|
||||
Page<UserMessage> pageParam = new Page<>(page, size);
|
||||
return userMessageService.getMessageDetail(userId, targetId, pageParam);
|
||||
}
|
||||
|
||||
/**
|
||||
* 标记私信为已读
|
||||
*/
|
||||
@PutMapping("/read/{senderId}")
|
||||
public Result<Void> markMessageRead(HttpServletRequest request, @PathVariable Long senderId) {
|
||||
Long userId = getUserIdFromRequest(request);
|
||||
if (userId == null) {
|
||||
return Result.error("未获取到用户ID");
|
||||
}
|
||||
return userMessageService.markMessageRead(userId, senderId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取未读私信数量
|
||||
*/
|
||||
@GetMapping("/unread/count")
|
||||
public Result<Integer> getUnreadMessageCount(HttpServletRequest request) {
|
||||
Long userId = getUserIdFromRequest(request);
|
||||
if (userId == null) {
|
||||
return Result.error("未获取到用户ID");
|
||||
}
|
||||
return userMessageService.getUnreadMessageCount(userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从请求中获取用户ID
|
||||
*/
|
||||
private Long getUserIdFromRequest(HttpServletRequest request) {
|
||||
Object userIdObj = request.getAttribute("userId");
|
||||
if (userIdObj instanceof Long) {
|
||||
return (Long) userIdObj;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
package com.nuaa.video.user.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.nuaa.video.common.model.vo.Result;
|
||||
import com.nuaa.video.user.entity.UserNotification;
|
||||
import com.nuaa.video.user.service.UserNotificationService;
|
||||
import com.nuaa.video.user.vo.UserNotificationVO;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 用户通知控制器
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/user/notification")
|
||||
@RequiredArgsConstructor
|
||||
public class UserNotificationController {
|
||||
|
||||
private final UserNotificationService userNotificationService;
|
||||
|
||||
/**
|
||||
* 获取通知列表
|
||||
*/
|
||||
@GetMapping("/list")
|
||||
public Result<Page<UserNotificationVO>> getNotificationList(
|
||||
HttpServletRequest request,
|
||||
@RequestParam(value = "type", required = false) Integer type,
|
||||
@RequestParam(value = "page", defaultValue = "1") Integer page,
|
||||
@RequestParam(value = "size", defaultValue = "10") Integer size) {
|
||||
Long userId = getUserIdFromRequest(request);
|
||||
if (userId == null) {
|
||||
return Result.error("未获取到用户ID");
|
||||
}
|
||||
Page<UserNotification> pageParam = new Page<>(page, size);
|
||||
return userNotificationService.getNotificationList(userId, type, pageParam);
|
||||
}
|
||||
|
||||
/**
|
||||
* 标记通知为已读
|
||||
*/
|
||||
@PutMapping("/read")
|
||||
public Result<Void> markAllNotificationRead(HttpServletRequest request) {
|
||||
Long userId = getUserIdFromRequest(request);
|
||||
if (userId == null) {
|
||||
return Result.error("未获取到用户ID");
|
||||
}
|
||||
return userNotificationService.markNotificationRead(userId, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 标记指定通知为已读
|
||||
*/
|
||||
@PutMapping("/read/{notificationId}")
|
||||
public Result<Void> markNotificationRead(HttpServletRequest request, @PathVariable Long notificationId) {
|
||||
Long userId = getUserIdFromRequest(request);
|
||||
if (userId == null) {
|
||||
return Result.error("未获取到用户ID");
|
||||
}
|
||||
return userNotificationService.markNotificationRead(userId, notificationId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取未读通知数量
|
||||
*/
|
||||
@GetMapping("/unread/count")
|
||||
public Result<Integer> getUnreadNotificationCount(HttpServletRequest request) {
|
||||
Long userId = getUserIdFromRequest(request);
|
||||
if (userId == null) {
|
||||
return Result.error("未获取到用户ID");
|
||||
}
|
||||
return userNotificationService.getUnreadNotificationCount(userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送用户通知
|
||||
*/
|
||||
@PostMapping("/send/{userId}/{type}")
|
||||
public Result<Void> sendNotification(
|
||||
@PathVariable("userId") Long userId,
|
||||
@PathVariable("type") Integer type,
|
||||
@RequestParam(required = false) String content,
|
||||
@RequestParam(required = false) Long relatedId,
|
||||
HttpServletRequest request) {
|
||||
// 验证调用者身份
|
||||
Long currentUserId = getUserIdFromRequest(request);
|
||||
if (currentUserId == null) {
|
||||
return Result.error("未登录");
|
||||
}
|
||||
|
||||
// 创建通知对象
|
||||
UserNotification notification = new UserNotification();
|
||||
notification.setUserId(userId);
|
||||
notification.setType(type);
|
||||
|
||||
// 设置通知内容
|
||||
String notificationContent;
|
||||
if (content != null && !content.isEmpty()) {
|
||||
notificationContent = content;
|
||||
} else {
|
||||
// 根据类型生成默认内容
|
||||
switch (type) {
|
||||
case 1: // 点赞
|
||||
notificationContent = "有人赞了你的内容";
|
||||
break;
|
||||
case 2: // 评论
|
||||
notificationContent = "有人评论了你的内容";
|
||||
break;
|
||||
case 3: // 关注
|
||||
notificationContent = "有人关注了你";
|
||||
break;
|
||||
case 4: // 系统通知
|
||||
notificationContent = "系统通知";
|
||||
break;
|
||||
default:
|
||||
notificationContent = "新通知";
|
||||
}
|
||||
}
|
||||
notification.setContent(notificationContent);
|
||||
|
||||
notification.setRelatedId(relatedId);
|
||||
notification.setIsRead(0); // 未读
|
||||
notification.setCreateTime(LocalDateTime.now());
|
||||
|
||||
// 保存通知
|
||||
userNotificationService.save(notification);
|
||||
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从请求中获取用户ID
|
||||
*/
|
||||
private Long getUserIdFromRequest(HttpServletRequest request) {
|
||||
Object userIdObj = request.getAttribute("userId");
|
||||
if (userIdObj instanceof Long) {
|
||||
return (Long) userIdObj;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+200
@@ -0,0 +1,200 @@
|
||||
package com.nuaa.video.user.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.nuaa.video.common.model.vo.Result;
|
||||
import com.nuaa.video.user.dto.UserProfileDTO;
|
||||
import com.nuaa.video.user.entity.UserInfo;
|
||||
import com.nuaa.video.user.feign.AuthFeignClient;
|
||||
import com.nuaa.video.user.service.UserInfoService;
|
||||
import com.nuaa.video.user.vo.UserProfileVO;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 用户资料控制器
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/user/profile")
|
||||
@RequiredArgsConstructor
|
||||
public class UserProfileController {
|
||||
|
||||
private final UserInfoService userInfoService;
|
||||
private final AuthFeignClient authFeignClient;
|
||||
|
||||
/**
|
||||
* 初始化用户资料
|
||||
*/
|
||||
@PostMapping("/init")
|
||||
public Result<Void> initUserInfo(@RequestParam("userId") Long userId,
|
||||
@RequestParam("username") String username) {
|
||||
return userInfoService.createUserInfo(userId, username);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前用户资料
|
||||
*/
|
||||
@GetMapping("/current")
|
||||
public Result<UserProfileVO> getCurrentUserProfile(HttpServletRequest request) {
|
||||
Long userId = getUserIdFromRequest(request);
|
||||
if (userId == null) {
|
||||
return Result.error("未获取到用户ID");
|
||||
}
|
||||
|
||||
// 检查用户资料是否存在
|
||||
UserInfo userInfo = userInfoService.getById(userId);
|
||||
if (userInfo == null) {
|
||||
// 资料不存在,自动创建
|
||||
String username = getUsernameFromRequest(request);
|
||||
if (username == null) {
|
||||
// 尝试从Auth服务获取用户名
|
||||
try {
|
||||
Result<Object> userResult = authFeignClient.getCurrentUser(
|
||||
request.getHeader("Authorization"));
|
||||
if (userResult.getCode() == 200 && userResult.getData() != null) {
|
||||
Map<String, Object> userData = (Map<String, Object>) userResult.getData();
|
||||
username = (String) userData.get("username");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("获取用户信息失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
if (username != null) {
|
||||
userInfoService.createUserInfo(userId, username);
|
||||
userInfo = userInfoService.getById(userId);
|
||||
} else {
|
||||
return Result.error("无法创建用户资料,用户信息不完整");
|
||||
}
|
||||
}
|
||||
|
||||
return userInfoService.getUserProfile(userId, userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定用户资料
|
||||
*/
|
||||
@GetMapping("/public/{userId}")
|
||||
public Result<UserProfileVO> getUserProfile(@PathVariable Long userId, HttpServletRequest request) {
|
||||
Long currentUserId = getUserIdFromRequest(request);
|
||||
return userInfoService.getUserProfile(userId, currentUserId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新用户资料
|
||||
*/
|
||||
@PutMapping
|
||||
public Result<Void> updateUserProfile(HttpServletRequest request, @RequestBody @Valid UserProfileDTO userProfileDTO) {
|
||||
Long userId = getUserIdFromRequest(request);
|
||||
if (userId == null) {
|
||||
return Result.error("未获取到用户ID");
|
||||
}
|
||||
return userInfoService.updateUserProfile(userId, userProfileDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传头像
|
||||
*/
|
||||
@PostMapping("/avatar")
|
||||
public Result<String> uploadAvatar(HttpServletRequest request, @RequestParam("file") MultipartFile file) {
|
||||
Long userId = getUserIdFromRequest(request);
|
||||
if (userId == null) {
|
||||
return Result.error("未获取到用户ID");
|
||||
}
|
||||
return userInfoService.uploadAvatar(userId, file);
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜索用户
|
||||
*/
|
||||
@GetMapping("/search")
|
||||
public Result<Page<UserInfo>> searchUsers(
|
||||
@RequestParam(value = "keyword", required = false) String keyword,
|
||||
@RequestParam(value = "page", defaultValue = "1") Integer page,
|
||||
@RequestParam(value = "size", defaultValue = "10") Integer size) {
|
||||
Page<UserInfo> pageParam = new Page<>(page, size);
|
||||
return userInfoService.searchUsers(pageParam, keyword);
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加用户视频数量
|
||||
*/
|
||||
@PostMapping("/count/video/{userId}/{count}")
|
||||
public Result<Void> increaseVideoCount(
|
||||
@PathVariable Long userId,
|
||||
@PathVariable Integer count,
|
||||
HttpServletRequest request) {
|
||||
// 验证权限(实际可以通过更复杂的权限验证)
|
||||
Long currentUserId = getUserIdFromRequest(request);
|
||||
if (currentUserId == null) {
|
||||
return Result.error("未登录");
|
||||
}
|
||||
|
||||
// 调用service增加用户视频数量
|
||||
return userInfoService.increaseVideoCount(userId, count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加用户获赞数量
|
||||
*/
|
||||
@PostMapping("/count/like/{userId}/{count}")
|
||||
public Result<Void> increaseLikeCount(
|
||||
@PathVariable Long userId,
|
||||
@PathVariable Integer count,
|
||||
HttpServletRequest request) {
|
||||
// 验证权限
|
||||
Long currentUserId = getUserIdFromRequest(request);
|
||||
if (currentUserId == null) {
|
||||
return Result.error("未登录");
|
||||
}
|
||||
|
||||
// 调用service增加用户获赞数量
|
||||
return userInfoService.increaseLikeCount(userId, count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加用户收藏数量
|
||||
*/
|
||||
@PostMapping("/count/favorite/{userId}/{count}")
|
||||
public Result<Void> increaseFavoriteCount(
|
||||
@PathVariable Long userId,
|
||||
@PathVariable Integer count,
|
||||
HttpServletRequest request) {
|
||||
// 验证权限
|
||||
Long currentUserId = getUserIdFromRequest(request);
|
||||
if (currentUserId == null) {
|
||||
return Result.error("未登录");
|
||||
}
|
||||
|
||||
// 调用service增加用户收藏数量
|
||||
return userInfoService.increaseFavoriteCount(userId, count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从请求中获取用户名
|
||||
*/
|
||||
private String getUsernameFromRequest(HttpServletRequest request) {
|
||||
Object usernameObj = request.getAttribute("username");
|
||||
if (usernameObj instanceof String) {
|
||||
return (String) usernameObj;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从请求中获取用户ID
|
||||
*/
|
||||
private Long getUserIdFromRequest(HttpServletRequest request) {
|
||||
Object userIdObj = request.getAttribute("userId");
|
||||
if (userIdObj instanceof Long) {
|
||||
return (Long) userIdObj;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
package com.nuaa.video.user.controller;
|
||||
|
||||
import com.nuaa.video.common.model.vo.Result;
|
||||
import com.nuaa.video.user.dto.UserSettingDTO;
|
||||
import com.nuaa.video.user.entity.UserSetting;
|
||||
import com.nuaa.video.user.service.UserSettingService;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
/**
|
||||
* 用户设置控制器
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/user/setting")
|
||||
@RequiredArgsConstructor
|
||||
public class UserSettingController {
|
||||
|
||||
private final UserSettingService userSettingService;
|
||||
|
||||
/**
|
||||
* 获取用户设置
|
||||
*/
|
||||
@GetMapping
|
||||
public Result<UserSetting> getUserSetting(HttpServletRequest request) {
|
||||
Long userId = getUserIdFromRequest(request);
|
||||
if (userId == null) {
|
||||
return Result.error("未获取到用户ID");
|
||||
}
|
||||
return userSettingService.getUserSetting(userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新用户设置
|
||||
*/
|
||||
@PutMapping
|
||||
public Result<Void> updateUserSetting(HttpServletRequest request, @RequestBody @Valid UserSettingDTO settingDTO) {
|
||||
Long userId = getUserIdFromRequest(request);
|
||||
if (userId == null) {
|
||||
return Result.error("未获取到用户ID");
|
||||
}
|
||||
return userSettingService.updateUserSetting(userId, settingDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从请求中获取用户ID
|
||||
*/
|
||||
private Long getUserIdFromRequest(HttpServletRequest request) {
|
||||
Object userIdObj = request.getAttribute("userId");
|
||||
if (userIdObj instanceof Long) {
|
||||
return (Long) userIdObj;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.nuaa.video.user.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 用户私信DTO
|
||||
*/
|
||||
@Data
|
||||
public class UserMessageDTO implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 接收者ID
|
||||
*/
|
||||
@NotNull(message = "接收者ID不能为空")
|
||||
private Long receiverId;
|
||||
|
||||
/**
|
||||
* 消息内容
|
||||
*/
|
||||
@NotBlank(message = "消息内容不能为空")
|
||||
@Size(max = 500, message = "消息内容不能超过500字")
|
||||
private String content;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.nuaa.video.user.dto;
|
||||
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDate;
|
||||
|
||||
/**
|
||||
* 用户资料DTO
|
||||
*/
|
||||
@Data
|
||||
public class UserProfileDTO implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 昵称
|
||||
*/
|
||||
@Size(min = 2, max = 20, message = "昵称长度必须在2-20之间")
|
||||
private String nickname;
|
||||
|
||||
/**
|
||||
* 性别(0:未知,1:男,2:女)
|
||||
*/
|
||||
private Integer gender;
|
||||
|
||||
/**
|
||||
* 生日
|
||||
*/
|
||||
private LocalDate birthday;
|
||||
|
||||
/**
|
||||
* 个性签名
|
||||
*/
|
||||
@Size(max = 200, message = "个性签名不能超过200字")
|
||||
private String signature;
|
||||
|
||||
/**
|
||||
* 地区
|
||||
*/
|
||||
@Size(max = 100, message = "地区不能超过100字")
|
||||
private String region;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.nuaa.video.user.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 用户设置DTO
|
||||
*/
|
||||
@Data
|
||||
public class UserSettingDTO implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 隐私等级(1:公开,2:仅关注,3:私密)
|
||||
*/
|
||||
private Integer privacyLevel;
|
||||
|
||||
/**
|
||||
* 评论通知(0:关闭,1:开启)
|
||||
*/
|
||||
private Integer commentNotify;
|
||||
|
||||
/**
|
||||
* 点赞通知(0:关闭,1:开启)
|
||||
*/
|
||||
private Integer likeNotify;
|
||||
|
||||
/**
|
||||
* 关注通知(0:关闭,1:开启)
|
||||
*/
|
||||
private Integer followNotify;
|
||||
|
||||
/**
|
||||
* 系统通知(0:关闭,1:开启)
|
||||
*/
|
||||
private Integer systemNotify;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.nuaa.video.user.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 用户关注实体
|
||||
*/
|
||||
@Data
|
||||
@TableName("user_follow")
|
||||
public class UserFollow implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@TableId(value = "id", type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 关注用户ID
|
||||
*/
|
||||
private Long followId;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private LocalDateTime createTime;
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
package com.nuaa.video.user.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 用户信息实体
|
||||
*/
|
||||
@Data
|
||||
@TableName("user_info")
|
||||
public class UserInfo implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
@TableId(value = "id", type = IdType.INPUT)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 昵称
|
||||
*/
|
||||
private String nickname;
|
||||
|
||||
/**
|
||||
* 头像URL
|
||||
*/
|
||||
private String avatar;
|
||||
|
||||
/**
|
||||
* 性别(0:未知,1:男,2:女)
|
||||
*/
|
||||
private Integer gender;
|
||||
|
||||
/**
|
||||
* 生日
|
||||
*/
|
||||
private LocalDate birthday;
|
||||
|
||||
/**
|
||||
* 个性签名
|
||||
*/
|
||||
private String signature;
|
||||
|
||||
/**
|
||||
* 地区
|
||||
*/
|
||||
private String region;
|
||||
|
||||
/**
|
||||
* 粉丝数
|
||||
*/
|
||||
private Integer followerCount;
|
||||
|
||||
/**
|
||||
* 关注数
|
||||
*/
|
||||
private Integer followingCount;
|
||||
|
||||
/**
|
||||
* 视频数
|
||||
*/
|
||||
private Integer videoCount;
|
||||
|
||||
/**
|
||||
* 获赞数
|
||||
*/
|
||||
private Integer likeCount;
|
||||
|
||||
/**
|
||||
* 硬币数
|
||||
*/
|
||||
private Integer coinCount;
|
||||
|
||||
/**
|
||||
* 收藏数
|
||||
*/
|
||||
private Integer favoriteCount;
|
||||
|
||||
/**
|
||||
* 播放数
|
||||
*/
|
||||
private Long viewCount;
|
||||
|
||||
/**
|
||||
* 等级
|
||||
*/
|
||||
private Integer level;
|
||||
|
||||
/**
|
||||
* VIP等级
|
||||
*/
|
||||
private Integer vipLevel;
|
||||
|
||||
/**
|
||||
* VIP到期时间
|
||||
*/
|
||||
private LocalDateTime vipExpireTime;
|
||||
|
||||
/**
|
||||
* 创建人
|
||||
*/
|
||||
private String createdBy;
|
||||
|
||||
/**
|
||||
* 更新人
|
||||
*/
|
||||
private String updatedBy;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
/**
|
||||
* 是否关注(非数据库字段)
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
private Boolean isFollowed;
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package com.nuaa.video.user.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 用户私信实体
|
||||
*/
|
||||
@Data
|
||||
@TableName("user_message")
|
||||
public class UserMessage implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@TableId(value = "id", type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 发送者ID
|
||||
*/
|
||||
private Long senderId;
|
||||
|
||||
/**
|
||||
* 接收者ID
|
||||
*/
|
||||
private Long receiverId;
|
||||
|
||||
/**
|
||||
* 消息内容
|
||||
*/
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 是否已读(0:未读,1:已读)
|
||||
*/
|
||||
private Integer isRead;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 发送者昵称(非数据库字段)
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
private String senderNickname;
|
||||
|
||||
/**
|
||||
* 发送者头像(非数据库字段)
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
private String senderAvatar;
|
||||
|
||||
/**
|
||||
* 接收者昵称(非数据库字段)
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
private String receiverNickname;
|
||||
|
||||
/**
|
||||
* 接收者头像(非数据库字段)
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
private String receiverAvatar;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.nuaa.video.user.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 用户通知实体
|
||||
*/
|
||||
@Data
|
||||
@TableName("user_notification")
|
||||
public class UserNotification implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@TableId(value = "id", type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 通知类型(1:点赞,2:评论,3:关注,4:系统通知)
|
||||
*/
|
||||
private Integer type;
|
||||
|
||||
/**
|
||||
* 通知内容
|
||||
*/
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 相关ID
|
||||
*/
|
||||
private Long relatedId;
|
||||
|
||||
/**
|
||||
* 是否已读(0:未读,1:已读)
|
||||
*/
|
||||
private Integer isRead;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private LocalDateTime createTime;
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package com.nuaa.video.user.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 用户设置实体
|
||||
*/
|
||||
@Data
|
||||
@TableName("user_setting")
|
||||
public class UserSetting implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@TableId(value = "id", type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 隐私等级(1:公开,2:仅关注,3:私密)
|
||||
*/
|
||||
private Integer privacyLevel;
|
||||
|
||||
/**
|
||||
* 评论通知(0:关闭,1:开启)
|
||||
*/
|
||||
private Integer commentNotify;
|
||||
|
||||
/**
|
||||
* 点赞通知(0:关闭,1:开启)
|
||||
*/
|
||||
private Integer likeNotify;
|
||||
|
||||
/**
|
||||
* 关注通知(0:关闭,1:开启)
|
||||
*/
|
||||
private Integer followNotify;
|
||||
|
||||
/**
|
||||
* 系统通知(0:关闭,1:开启)
|
||||
*/
|
||||
private Integer systemNotify;
|
||||
|
||||
/**
|
||||
* 创建人
|
||||
*/
|
||||
private String createdBy;
|
||||
|
||||
/**
|
||||
* 更新人
|
||||
*/
|
||||
private String updatedBy;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
private LocalDateTime updateTime;
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package com.nuaa.video.user.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 用户每日统计实体
|
||||
*/
|
||||
@Data
|
||||
@TableName("user_stat_daily")
|
||||
public class UserStatDaily implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@TableId(value = "id", type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 统计日期
|
||||
*/
|
||||
private LocalDate statDate;
|
||||
|
||||
/**
|
||||
* 新增粉丝数
|
||||
*/
|
||||
private Integer followerIncrement;
|
||||
|
||||
/**
|
||||
* 新增关注数
|
||||
*/
|
||||
private Integer followingIncrement;
|
||||
|
||||
/**
|
||||
* 新增视频数
|
||||
*/
|
||||
private Integer videoIncrement;
|
||||
|
||||
/**
|
||||
* 新增获赞数
|
||||
*/
|
||||
private Integer likeIncrement;
|
||||
|
||||
/**
|
||||
* 新增硬币数
|
||||
*/
|
||||
private Integer coinIncrement;
|
||||
|
||||
/**
|
||||
* 新增收藏数
|
||||
*/
|
||||
private Integer favoriteIncrement;
|
||||
|
||||
/**
|
||||
* 新增播放数
|
||||
*/
|
||||
private Long viewIncrement;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private LocalDateTime createTime;
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.nuaa.video.user.feign;
|
||||
|
||||
import com.nuaa.video.common.model.vo.Result;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
/**
|
||||
* 认证服务Feign客户端
|
||||
*/
|
||||
@FeignClient(name = "video-auth", path = "/auth")
|
||||
public interface AuthFeignClient {
|
||||
|
||||
/**
|
||||
* 获取当前用户信息
|
||||
*
|
||||
* @param token 令牌
|
||||
* @return 用户信息
|
||||
*/
|
||||
@GetMapping("/current")
|
||||
Result<Object> getCurrentUser(@RequestHeader("Authorization") String token);
|
||||
|
||||
/**
|
||||
* 根据ID获取用户名
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 用户名
|
||||
*/
|
||||
@GetMapping("/username/{userId}")
|
||||
String getUsernameById(@PathVariable("userId") Long userId);
|
||||
|
||||
/**
|
||||
* 锁定/解锁用户账号 - 管理接口
|
||||
*/
|
||||
@PutMapping("/admin/lock/{userId}/{locked}")
|
||||
Result<Void> lockUser(@PathVariable Long userId,
|
||||
@PathVariable Boolean locked);
|
||||
|
||||
/**
|
||||
* 管理员重置用户密码 - 管理接口
|
||||
*/
|
||||
@PutMapping("/admin/reset-password/{userId}")
|
||||
Result<Void> resetPassword(
|
||||
@PathVariable Long userId,
|
||||
@RequestParam String newPassword);
|
||||
}
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
package com.nuaa.video.user.filter;
|
||||
|
||||
import io.jsonwebtoken.Claims;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import io.jsonwebtoken.security.Keys;
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* JWT认证过滤器(不依赖Feign)
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
||||
|
||||
@Value("${jwt.secret:video-platform-secret-key-2024-nuaa}")
|
||||
private String secret;
|
||||
|
||||
private static final String TOKEN_PREFIX = "Bearer ";
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
|
||||
throws ServletException, IOException {
|
||||
// 从请求头中获取JWT令牌
|
||||
String token = getJwtFromRequest(request);
|
||||
|
||||
// 如果令牌存在,则本地验证
|
||||
if (StringUtils.hasText(token)) {
|
||||
try {
|
||||
// 验证令牌
|
||||
Claims claims = Jwts.parser()
|
||||
.verifyWith(getSigningKey())
|
||||
.build()
|
||||
.parseSignedClaims(token)
|
||||
.getPayload();
|
||||
|
||||
// 从令牌中提取用户ID和用户名
|
||||
Long userId = Long.parseLong(claims.getSubject());
|
||||
String username = claims.get("username", String.class);
|
||||
|
||||
// 设置用户权限
|
||||
List<SimpleGrantedAuthority> authorities = new ArrayList<>();
|
||||
|
||||
// 创建认证对象
|
||||
UsernamePasswordAuthenticationToken authentication =
|
||||
new UsernamePasswordAuthenticationToken(username, null, authorities);
|
||||
|
||||
// 设置额外信息
|
||||
authentication.setDetails(userId);
|
||||
|
||||
// 设置认证信息到安全上下文
|
||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||
|
||||
// 在请求属性中存储用户ID
|
||||
request.setAttribute("userId", userId);
|
||||
request.setAttribute("username", username);
|
||||
|
||||
log.debug("认证成功: 用户ID={}, 用户名={}", userId, username);
|
||||
} catch (Exception e) {
|
||||
log.error("JWT验证失败: {}", e.getMessage());
|
||||
SecurityContextHolder.clearContext();
|
||||
}
|
||||
}
|
||||
|
||||
filterChain.doFilter(request, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从请求头中提取JWT令牌
|
||||
*/
|
||||
private String getJwtFromRequest(HttpServletRequest request) {
|
||||
String bearerToken = request.getHeader("Authorization");
|
||||
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith(TOKEN_PREFIX)) {
|
||||
return bearerToken.substring(TOKEN_PREFIX.length());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取签名密钥
|
||||
*/
|
||||
private SecretKey getSigningKey() {
|
||||
byte[] keyBytes = secret.getBytes(StandardCharsets.UTF_8);
|
||||
return Keys.hmacShaKeyFor(keyBytes);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.nuaa.video.user.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.nuaa.video.user.entity.UserFollow;
|
||||
import com.nuaa.video.user.entity.UserInfo;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
/**
|
||||
* 用户关注Mapper
|
||||
*/
|
||||
@Mapper
|
||||
public interface UserFollowMapper extends BaseMapper<UserFollow> {
|
||||
|
||||
/**
|
||||
* 查询用户关注列表
|
||||
*
|
||||
* @param page 分页参数
|
||||
* @param userId 用户ID
|
||||
* @return 关注用户列表
|
||||
*/
|
||||
Page<UserInfo> selectFollowingList(Page<UserInfo> page, @Param("userId") Long userId);
|
||||
|
||||
/**
|
||||
* 查询用户粉丝列表
|
||||
*
|
||||
* @param page 分页参数
|
||||
* @param userId 用户ID
|
||||
* @return 粉丝用户列表
|
||||
*/
|
||||
Page<UserInfo> selectFollowerList(Page<UserInfo> page, @Param("userId") Long userId);
|
||||
|
||||
/**
|
||||
* 检查是否已关注
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param followId 关注用户ID
|
||||
* @return 是否已关注
|
||||
*/
|
||||
int checkIsFollowed(@Param("userId") Long userId, @Param("followId") Long followId);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.nuaa.video.user.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.nuaa.video.user.entity.UserInfo;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
/**
|
||||
* 用户信息Mapper
|
||||
*/
|
||||
@Mapper
|
||||
public interface UserInfoMapper extends BaseMapper<UserInfo> {
|
||||
|
||||
/**
|
||||
* 增加关注数
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param count 增加数量
|
||||
* @return 影响行数
|
||||
*/
|
||||
int increaseFollowingCount(@Param("userId") Long userId, @Param("count") int count);
|
||||
|
||||
/**
|
||||
* 增加粉丝数
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param count 增加数量
|
||||
* @return 影响行数
|
||||
*/
|
||||
int increaseFollowerCount(@Param("userId") Long userId, @Param("count") int count);
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.nuaa.video.user.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.nuaa.video.user.entity.UserMessage;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
/**
|
||||
* 用户私信Mapper
|
||||
*/
|
||||
@Mapper
|
||||
public interface UserMessageMapper extends BaseMapper<UserMessage> {
|
||||
|
||||
/**
|
||||
* 查询私信列表(按会话分组)
|
||||
*
|
||||
* @param page 分页参数
|
||||
* @param userId 用户ID
|
||||
* @return 私信列表
|
||||
*/
|
||||
Page<UserMessage> selectMessageGroupList(Page<UserMessage> page, @Param("userId") Long userId);
|
||||
|
||||
/**
|
||||
* 查询与指定用户的私信记录
|
||||
*
|
||||
* @param page 分页参数
|
||||
* @param userId 用户ID
|
||||
* @param targetId 目标用户ID
|
||||
* @return 私信记录
|
||||
*/
|
||||
Page<UserMessage> selectMessageDetail(Page<UserMessage> page, @Param("userId") Long userId, @Param("targetId") Long targetId);
|
||||
|
||||
/**
|
||||
* 标记私信为已读
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param senderId 发送者ID
|
||||
* @return 影响行数
|
||||
*/
|
||||
int markMessageRead(@Param("userId") Long userId, @Param("senderId") Long senderId);
|
||||
|
||||
/**
|
||||
* 获取未读私信数量
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 未读私信数量
|
||||
*/
|
||||
int countUnreadMessage(@Param("userId") Long userId);
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package com.nuaa.video.user.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.nuaa.video.user.entity.UserNotification;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
/**
|
||||
* 用户通知Mapper
|
||||
*/
|
||||
@Mapper
|
||||
public interface UserNotificationMapper extends BaseMapper<UserNotification> {
|
||||
|
||||
/**
|
||||
* 标记通知为已读
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param notificationId 通知ID,为空则标记所有
|
||||
* @return 影响行数
|
||||
*/
|
||||
int markNotificationRead(@Param("userId") Long userId, @Param("notificationId") Long notificationId);
|
||||
|
||||
/**
|
||||
* 获取未读通知数量
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 未读通知数量
|
||||
*/
|
||||
int countUnreadNotification(@Param("userId") Long userId);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.nuaa.video.user.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.nuaa.video.user.entity.UserSetting;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* 用户设置Mapper
|
||||
*/
|
||||
@Mapper
|
||||
public interface UserSettingMapper extends BaseMapper<UserSetting> {
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.nuaa.video.user.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.nuaa.video.user.entity.UserStatDaily;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* 用户每日统计Mapper
|
||||
*/
|
||||
@Mapper
|
||||
public interface UserStatDailyMapper extends BaseMapper<UserStatDaily> {
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package com.nuaa.video.user.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.nuaa.video.common.model.vo.Result;
|
||||
import com.nuaa.video.user.entity.UserFollow;
|
||||
import com.nuaa.video.user.entity.UserInfo;
|
||||
|
||||
/**
|
||||
* 用户关注服务接口
|
||||
*/
|
||||
public interface UserFollowService extends IService<UserFollow> {
|
||||
|
||||
/**
|
||||
* 关注用户
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param followId 被关注用户ID
|
||||
* @return 关注结果
|
||||
*/
|
||||
Result<Void> followUser(Long userId, Long followId);
|
||||
|
||||
/**
|
||||
* 取消关注
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param followId 被关注用户ID
|
||||
* @return 取消结果
|
||||
*/
|
||||
Result<Void> unfollowUser(Long userId, Long followId);
|
||||
|
||||
/**
|
||||
* 获取用户关注列表
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param page 分页参数
|
||||
* @return 关注列表
|
||||
*/
|
||||
Result<Page<UserInfo>> getFollowingList(Long userId, Page<UserInfo> page);
|
||||
|
||||
/**
|
||||
* 获取用户粉丝列表
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param page 分页参数
|
||||
* @return 粉丝列表
|
||||
*/
|
||||
Result<Page<UserInfo>> getFollowerList(Long userId, Page<UserInfo> page);
|
||||
|
||||
/**
|
||||
* 检查是否已关注
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param followId 关注用户ID
|
||||
* @return 是否已关注
|
||||
*/
|
||||
Result<Boolean> checkIsFollowed(Long userId, Long followId);
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
package com.nuaa.video.user.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.nuaa.video.common.model.vo.Result;
|
||||
import com.nuaa.video.user.dto.UserProfileDTO;
|
||||
import com.nuaa.video.user.entity.UserInfo;
|
||||
import com.nuaa.video.user.vo.UserProfileVO;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
/**
|
||||
* 用户信息服务接口
|
||||
*/
|
||||
public interface UserInfoService extends IService<UserInfo> {
|
||||
|
||||
/**
|
||||
* 获取用户资料
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param currentUserId 当前登录用户ID,可为空
|
||||
* @return 用户资料
|
||||
*/
|
||||
Result<UserProfileVO> getUserProfile(Long userId, Long currentUserId);
|
||||
|
||||
/**
|
||||
* 更新用户资料
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param userProfileDTO 用户资料DTO
|
||||
* @return 更新结果
|
||||
*/
|
||||
Result<Void> updateUserProfile(Long userId, UserProfileDTO userProfileDTO);
|
||||
|
||||
/**
|
||||
* 上传头像
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param file 头像文件
|
||||
* @return 头像URL
|
||||
*/
|
||||
Result<String> uploadAvatar(Long userId, MultipartFile file);
|
||||
|
||||
/**
|
||||
* 创建用户信息(用户注册初始化)
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param username 用户名,用作初始昵称
|
||||
* @return 创建结果
|
||||
*/
|
||||
Result<Void> createUserInfo(Long userId, String username);
|
||||
|
||||
/**
|
||||
* 获取用户简要信息
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 简要信息
|
||||
*/
|
||||
Result<UserInfo> getBriefUserInfo(Long userId);
|
||||
|
||||
/**
|
||||
* 查询用户列表
|
||||
*
|
||||
* @param page 分页参数
|
||||
* @param keyword 搜索关键词
|
||||
* @return 用户列表
|
||||
*/
|
||||
Result<Page<UserInfo>> searchUsers(Page<UserInfo> page, String keyword);
|
||||
|
||||
/**
|
||||
* 增加用户视频数量
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param count 增加数量
|
||||
* @return 结果
|
||||
*/
|
||||
Result<Void> increaseVideoCount(Long userId, Integer count);
|
||||
|
||||
/**
|
||||
* 增加用户获赞数量
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param count 增加数量
|
||||
* @return 结果
|
||||
*/
|
||||
Result<Void> increaseLikeCount(Long userId, Integer count);
|
||||
|
||||
/**
|
||||
* 增加用户收藏数量
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param count 增加数量
|
||||
* @return 结果
|
||||
*/
|
||||
Result<Void> increaseFavoriteCount(Long userId, Integer count);
|
||||
|
||||
/**
|
||||
* 获取用户列表 - 管理接口
|
||||
*/
|
||||
Result<Page<UserProfileVO>> getAdminUserList(Page<UserInfo> page, String keyword);
|
||||
|
||||
/**
|
||||
* 获取用户详情 - 管理接口
|
||||
*/
|
||||
Result<UserProfileVO> getAdminUserDetail(Long userId);
|
||||
|
||||
/**
|
||||
* 更新用户状态 - 管理接口
|
||||
*/
|
||||
Result<Void> updateUserStatus(Long userId, Integer status);
|
||||
|
||||
/**
|
||||
* 重置用户密码 - 管理接口
|
||||
*/
|
||||
Result<Void> resetUserPassword(Long userId, String newPassword);
|
||||
|
||||
/**
|
||||
* 获取用户统计信息 - 管理接口
|
||||
*/
|
||||
Result<Object> getUserStats();
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.nuaa.video.user.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.nuaa.video.common.model.vo.Result;
|
||||
import com.nuaa.video.user.dto.UserMessageDTO;
|
||||
import com.nuaa.video.user.entity.UserMessage;
|
||||
import com.nuaa.video.user.vo.UserMessageVO;
|
||||
|
||||
/**
|
||||
* 用户私信服务接口
|
||||
*/
|
||||
public interface UserMessageService extends IService<UserMessage> {
|
||||
|
||||
/**
|
||||
* 发送私信
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param messageDTO 私信DTO
|
||||
* @return 发送结果
|
||||
*/
|
||||
Result<Void> sendMessage(Long userId, UserMessageDTO messageDTO);
|
||||
|
||||
/**
|
||||
* 获取私信列表(按会话分组)
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param page 分页参数
|
||||
* @return 私信列表
|
||||
*/
|
||||
Result<Page<UserMessageVO>> getMessageList(Long userId, Page<UserMessage> page);
|
||||
|
||||
/**
|
||||
* 获取与指定用户的私信记录
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param targetId 目标用户ID
|
||||
* @param page 分页参数
|
||||
* @return 私信记录
|
||||
*/
|
||||
Result<Page<UserMessageVO>> getMessageDetail(Long userId, Long targetId, Page<UserMessage> page);
|
||||
|
||||
/**
|
||||
* 标记私信为已读
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param senderId 发送者ID
|
||||
* @return 标记结果
|
||||
*/
|
||||
Result<Void> markMessageRead(Long userId, Long senderId);
|
||||
|
||||
/**
|
||||
* 获取未读私信数量
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 未读私信数量
|
||||
*/
|
||||
Result<Integer> getUnreadMessageCount(Long userId);
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
package com.nuaa.video.user.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.nuaa.video.common.model.vo.Result;
|
||||
import com.nuaa.video.user.entity.UserNotification;
|
||||
import com.nuaa.video.user.vo.UserNotificationVO;
|
||||
|
||||
/**
|
||||
* 用户通知服务接口
|
||||
*/
|
||||
public interface UserNotificationService extends IService<UserNotification> {
|
||||
|
||||
/**
|
||||
* 创建通知
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param type 通知类型
|
||||
* @param content 通知内容
|
||||
* @param relatedId 相关ID
|
||||
* @return 创建结果
|
||||
*/
|
||||
Result<Void> createNotification(Long userId, Integer type, String content, Long relatedId);
|
||||
|
||||
/**
|
||||
* 获取用户通知列表
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param type 通知类型,null为全部
|
||||
* @param page 分页参数
|
||||
* @return 通知列表
|
||||
*/
|
||||
Result<Page<UserNotificationVO>> getNotificationList(Long userId, Integer type, Page<UserNotification> page);
|
||||
|
||||
/**
|
||||
* 标记通知为已读
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param notificationId 通知ID,为空则标记所有
|
||||
* @return 标记结果
|
||||
*/
|
||||
Result<Void> markNotificationRead(Long userId, Long notificationId);
|
||||
|
||||
/**
|
||||
* 获取未读通知数量
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 未读通知数量
|
||||
*/
|
||||
Result<Integer> getUnreadNotificationCount(Long userId);
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.nuaa.video.user.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.nuaa.video.common.model.vo.Result;
|
||||
import com.nuaa.video.user.dto.UserSettingDTO;
|
||||
import com.nuaa.video.user.entity.UserSetting;
|
||||
|
||||
/**
|
||||
* 用户设置服务接口
|
||||
*/
|
||||
public interface UserSettingService extends IService<UserSetting> {
|
||||
|
||||
/**
|
||||
* 获取用户设置
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 用户设置
|
||||
*/
|
||||
Result<UserSetting> getUserSetting(Long userId);
|
||||
|
||||
/**
|
||||
* 更新用户设置
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param settingDTO 设置DTO
|
||||
* @return 更新结果
|
||||
*/
|
||||
Result<Void> updateUserSetting(Long userId, UserSettingDTO settingDTO);
|
||||
|
||||
/**
|
||||
* 创建默认设置(用户注册初始化)
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 创建结果
|
||||
*/
|
||||
Result<Void> createDefaultSetting(Long userId);
|
||||
}
|
||||
+147
@@ -0,0 +1,147 @@
|
||||
package com.nuaa.video.user.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.nuaa.video.common.model.enums.ResultCode;
|
||||
import com.nuaa.video.common.model.vo.Result;
|
||||
import com.nuaa.video.user.entity.UserFollow;
|
||||
import com.nuaa.video.user.entity.UserInfo;
|
||||
import com.nuaa.video.user.mapper.UserFollowMapper;
|
||||
import com.nuaa.video.user.mapper.UserInfoMapper;
|
||||
import com.nuaa.video.user.service.UserFollowService;
|
||||
import com.nuaa.video.user.service.UserNotificationService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 用户关注服务实现类
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class UserFollowServiceImpl extends ServiceImpl<UserFollowMapper, UserFollow> implements UserFollowService {
|
||||
|
||||
private final UserFollowMapper userFollowMapper;
|
||||
private final UserInfoMapper userInfoMapper;
|
||||
private final UserNotificationService userNotificationService;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Result<Void> followUser(Long userId, Long followId) {
|
||||
// 验证用户和关注对象是否存在
|
||||
UserInfo user = userInfoMapper.selectById(userId);
|
||||
UserInfo followUser = userInfoMapper.selectById(followId);
|
||||
|
||||
if (user == null || followUser == null) {
|
||||
return Result.error(ResultCode.USER_NOT_FOUND.getCode(), ResultCode.USER_NOT_FOUND.getMessage());
|
||||
}
|
||||
|
||||
// 不能关注自己
|
||||
if (userId.equals(followId)) {
|
||||
return Result.error("不能关注自己");
|
||||
}
|
||||
|
||||
// 检查是否已关注
|
||||
int count = userFollowMapper.checkIsFollowed(userId, followId);
|
||||
if (count > 0) {
|
||||
return Result.error("已经关注该用户");
|
||||
}
|
||||
|
||||
// 创建关注关系
|
||||
UserFollow userFollow = new UserFollow();
|
||||
userFollow.setUserId(userId);
|
||||
userFollow.setFollowId(followId);
|
||||
userFollow.setCreateTime(LocalDateTime.now());
|
||||
|
||||
// 保存关注记录
|
||||
save(userFollow);
|
||||
|
||||
// 更新用户和被关注用户的关注数和粉丝数
|
||||
userInfoMapper.increaseFollowingCount(userId, 1);
|
||||
userInfoMapper.increaseFollowerCount(followId, 1);
|
||||
|
||||
// 创建关注通知
|
||||
String content = user.getNickname() + " 关注了你";
|
||||
userNotificationService.createNotification(followId, 3, content, userId);
|
||||
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Result<Void> unfollowUser(Long userId, Long followId) {
|
||||
// 验证用户和关注对象是否存在
|
||||
UserInfo user = userInfoMapper.selectById(userId);
|
||||
UserInfo followUser = userInfoMapper.selectById(followId);
|
||||
|
||||
if (user == null || followUser == null) {
|
||||
return Result.error(ResultCode.USER_NOT_FOUND.getCode(), ResultCode.USER_NOT_FOUND.getMessage());
|
||||
}
|
||||
|
||||
// 检查是否已关注
|
||||
int count = userFollowMapper.checkIsFollowed(userId, followId);
|
||||
if (count == 0) {
|
||||
return Result.error("未关注该用户");
|
||||
}
|
||||
|
||||
// 删除关注关系
|
||||
lambdaUpdate()
|
||||
.eq(UserFollow::getUserId, userId)
|
||||
.eq(UserFollow::getFollowId, followId)
|
||||
.remove();
|
||||
|
||||
// 更新用户和被关注用户的关注数和粉丝数
|
||||
userInfoMapper.increaseFollowingCount(userId, -1);
|
||||
userInfoMapper.increaseFollowerCount(followId, -1);
|
||||
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<Page<UserInfo>> getFollowingList(Long userId, Page<UserInfo> page) {
|
||||
// 验证用户是否存在
|
||||
UserInfo user = userInfoMapper.selectById(userId);
|
||||
if (user == null) {
|
||||
return Result.error(ResultCode.USER_NOT_FOUND.getCode(), ResultCode.USER_NOT_FOUND.getMessage());
|
||||
}
|
||||
|
||||
// 查询关注列表
|
||||
Page<UserInfo> followingList = userFollowMapper.selectFollowingList(page, userId);
|
||||
|
||||
return Result.success(followingList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<Page<UserInfo>> getFollowerList(Long userId, Page<UserInfo> page) {
|
||||
// 验证用户是否存在
|
||||
UserInfo user = userInfoMapper.selectById(userId);
|
||||
if (user == null) {
|
||||
return Result.error(ResultCode.USER_NOT_FOUND.getCode(), ResultCode.USER_NOT_FOUND.getMessage());
|
||||
}
|
||||
|
||||
// 查询粉丝列表
|
||||
Page<UserInfo> followerList = userFollowMapper.selectFollowerList(page, userId);
|
||||
|
||||
return Result.success(followerList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<Boolean> checkIsFollowed(Long userId, Long followId) {
|
||||
// 验证用户和关注对象是否存在
|
||||
UserInfo user = userInfoMapper.selectById(userId);
|
||||
UserInfo followUser = userInfoMapper.selectById(followId);
|
||||
|
||||
if (user == null || followUser == null) {
|
||||
return Result.error(ResultCode.USER_NOT_FOUND.getCode(), ResultCode.USER_NOT_FOUND.getMessage());
|
||||
}
|
||||
|
||||
// 检查是否已关注
|
||||
int count = userFollowMapper.checkIsFollowed(userId, followId);
|
||||
|
||||
return Result.success(count > 0);
|
||||
}
|
||||
}
|
||||
+442
@@ -0,0 +1,442 @@
|
||||
package com.nuaa.video.user.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.nuaa.video.common.model.enums.ResultCode;
|
||||
import com.nuaa.video.common.model.vo.Result;
|
||||
import com.nuaa.video.common.utils.MinioUtils;
|
||||
import com.nuaa.video.user.dto.UserProfileDTO;
|
||||
import com.nuaa.video.user.entity.UserInfo;
|
||||
import com.nuaa.video.user.entity.UserStatDaily;
|
||||
import com.nuaa.video.user.feign.AuthFeignClient;
|
||||
import com.nuaa.video.user.mapper.UserFollowMapper;
|
||||
import com.nuaa.video.user.mapper.UserInfoMapper;
|
||||
import com.nuaa.video.user.mapper.UserStatDailyMapper;
|
||||
import com.nuaa.video.user.service.UserInfoService;
|
||||
import com.nuaa.video.user.service.UserSettingService;
|
||||
import com.nuaa.video.user.vo.UserProfileVO;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 用户信息服务实现类
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> implements UserInfoService {
|
||||
|
||||
private final UserInfoMapper userInfoMapper;
|
||||
private final UserFollowMapper userFollowMapper;
|
||||
private final UserSettingService userSettingService;
|
||||
private final MinioUtils minioUtils;
|
||||
private final UserStatDailyMapper userStatDailyMapper;
|
||||
private final AuthFeignClient authFeignClient;
|
||||
|
||||
@Override
|
||||
public Result<UserProfileVO> getUserProfile(Long userId, Long currentUserId) {
|
||||
// 查询用户信息
|
||||
UserInfo userInfo = getById(userId);
|
||||
if (userInfo == null) {
|
||||
return Result.error(ResultCode.USER_NOT_FOUND.getCode(),ResultCode.USER_NOT_FOUND.getMessage());
|
||||
}
|
||||
|
||||
// 转换为VO
|
||||
UserProfileVO profileVO = new UserProfileVO();
|
||||
BeanUtils.copyProperties(userInfo, profileVO);
|
||||
|
||||
// 检查是否关注(如果当前用户已登录)
|
||||
if (currentUserId != null && !currentUserId.equals(userId)) {
|
||||
int count = userFollowMapper.checkIsFollowed(currentUserId, userId);
|
||||
profileVO.setIsFollowed(count > 0);
|
||||
} else {
|
||||
profileVO.setIsFollowed(false);
|
||||
}
|
||||
|
||||
return Result.success(profileVO);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Result<Void> updateUserProfile(Long userId, UserProfileDTO userProfileDTO) {
|
||||
// 查询用户信息
|
||||
UserInfo userInfo = getById(userId);
|
||||
if (userInfo == null) {
|
||||
return Result.error(ResultCode.USER_NOT_FOUND.getCode(),ResultCode.USER_NOT_FOUND.getMessage());
|
||||
}
|
||||
|
||||
// 更新用户信息
|
||||
if (userProfileDTO.getNickname() != null) {
|
||||
userInfo.setNickname(userProfileDTO.getNickname());
|
||||
}
|
||||
if (userProfileDTO.getGender() != null) {
|
||||
userInfo.setGender(userProfileDTO.getGender());
|
||||
}
|
||||
if (userProfileDTO.getBirthday() != null) {
|
||||
userInfo.setBirthday(userProfileDTO.getBirthday());
|
||||
}
|
||||
if (userProfileDTO.getSignature() != null) {
|
||||
userInfo.setSignature(userProfileDTO.getSignature());
|
||||
}
|
||||
if (userProfileDTO.getRegion() != null) {
|
||||
userInfo.setRegion(userProfileDTO.getRegion());
|
||||
}
|
||||
|
||||
userInfo.setUpdatedBy(userId.toString());
|
||||
userInfo.setUpdateTime(LocalDateTime.now());
|
||||
|
||||
// 保存更新
|
||||
updateById(userInfo);
|
||||
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<String> uploadAvatar(Long userId, MultipartFile file) {
|
||||
try {
|
||||
// 查询用户信息
|
||||
UserInfo userInfo = getById(userId);
|
||||
if (userInfo == null) {
|
||||
return Result.error(ResultCode.USER_NOT_FOUND.getCode(),ResultCode.USER_NOT_FOUND.getMessage());
|
||||
}
|
||||
|
||||
// 上传头像
|
||||
String avatarUrl = minioUtils.uploadImage(file);
|
||||
|
||||
// 更新用户头像
|
||||
userInfo.setAvatar(avatarUrl);
|
||||
userInfo.setUpdatedBy(userId.toString());
|
||||
userInfo.setUpdateTime(LocalDateTime.now());
|
||||
updateById(userInfo);
|
||||
|
||||
// 返回头像URL
|
||||
return Result.success(minioUtils.getPreSignedUrl(avatarUrl));
|
||||
} catch (Exception e) {
|
||||
log.error("上传头像失败", e);
|
||||
return Result.error("上传头像失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Result<Void> createUserInfo(Long userId, String username) {
|
||||
// 检查是否已存在
|
||||
UserInfo existUser = getById(userId);
|
||||
if (existUser != null) {
|
||||
return Result.error("用户信息已存在");
|
||||
}
|
||||
|
||||
// 创建用户信息
|
||||
UserInfo userInfo = new UserInfo();
|
||||
userInfo.setId(userId);
|
||||
userInfo.setNickname(username);
|
||||
userInfo.setAvatar("default/avatar.png"); // 默认头像
|
||||
userInfo.setGender(0);
|
||||
userInfo.setFollowerCount(0);
|
||||
userInfo.setFollowingCount(0);
|
||||
userInfo.setVideoCount(0);
|
||||
userInfo.setLikeCount(0);
|
||||
userInfo.setCoinCount(0);
|
||||
userInfo.setFavoriteCount(0);
|
||||
userInfo.setViewCount(0L);
|
||||
userInfo.setLevel(1);
|
||||
userInfo.setVipLevel(0);
|
||||
userInfo.setCreatedBy("system");
|
||||
userInfo.setUpdatedBy("system");
|
||||
userInfo.setCreateTime(LocalDateTime.now());
|
||||
userInfo.setUpdateTime(LocalDateTime.now());
|
||||
|
||||
// 保存用户信息
|
||||
save(userInfo);
|
||||
|
||||
// 创建默认设置
|
||||
userSettingService.createDefaultSetting(userId);
|
||||
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<UserInfo> getBriefUserInfo(Long userId) {
|
||||
UserInfo userInfo = getById(userId);
|
||||
if (userInfo == null) {
|
||||
return Result.error(ResultCode.USER_NOT_FOUND.getCode(),ResultCode.USER_NOT_FOUND.getMessage());
|
||||
}
|
||||
return Result.success(userInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<Page<UserInfo>> searchUsers(Page<UserInfo> page, String keyword) {
|
||||
// 构建查询条件
|
||||
LambdaQueryWrapper<UserInfo> queryWrapper = new LambdaQueryWrapper<>();
|
||||
|
||||
if (keyword != null && !keyword.trim().isEmpty()) {
|
||||
queryWrapper.like(UserInfo::getNickname, keyword)
|
||||
.or()
|
||||
.like(UserInfo::getSignature, keyword);
|
||||
}
|
||||
|
||||
// 按等级降序,注册时间降序
|
||||
queryWrapper.orderByDesc(UserInfo::getLevel)
|
||||
.orderByDesc(UserInfo::getCreateTime);
|
||||
|
||||
// 执行分页查询
|
||||
Page<UserInfo> resultPage = page(page, queryWrapper);
|
||||
|
||||
return Result.success(resultPage);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Result<Void> increaseVideoCount(Long userId, Integer count) {
|
||||
// 验证用户是否存在
|
||||
UserInfo userInfo = getById(userId);
|
||||
if (userInfo == null) {
|
||||
return Result.error(ResultCode.USER_NOT_FOUND.getCode(),ResultCode.USER_NOT_FOUND.getMessage());
|
||||
}
|
||||
|
||||
// 更新视频数量
|
||||
userInfo.setVideoCount(userInfo.getVideoCount() + count);
|
||||
userInfo.setUpdateTime(LocalDateTime.now());
|
||||
updateById(userInfo);
|
||||
|
||||
// 更新每日统计
|
||||
updateUserStatDaily(userId, "video", count);
|
||||
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Result<Void> increaseLikeCount(Long userId, Integer count) {
|
||||
// 验证用户是否存在
|
||||
UserInfo userInfo = getById(userId);
|
||||
if (userInfo == null) {
|
||||
return Result.error(ResultCode.USER_NOT_FOUND.getCode(),ResultCode.USER_NOT_FOUND.getMessage());
|
||||
}
|
||||
|
||||
// 更新获赞数量
|
||||
userInfo.setLikeCount(userInfo.getLikeCount() + count);
|
||||
userInfo.setUpdateTime(LocalDateTime.now());
|
||||
updateById(userInfo);
|
||||
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Result<Void> increaseFavoriteCount(Long userId, Integer count) {
|
||||
// 验证用户是否存在
|
||||
UserInfo userInfo = getById(userId);
|
||||
if (userInfo == null) {
|
||||
return Result.error(ResultCode.USER_NOT_FOUND.getCode(),ResultCode.USER_NOT_FOUND.getMessage());
|
||||
}
|
||||
|
||||
// 更新收藏数量
|
||||
userInfo.setFavoriteCount(userInfo.getFavoriteCount() + count);
|
||||
userInfo.setUpdateTime(LocalDateTime.now());
|
||||
updateById(userInfo);
|
||||
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<Page<UserProfileVO>> getAdminUserList(Page<UserInfo> page, String keyword) {
|
||||
LambdaQueryWrapper<UserInfo> queryWrapper = new LambdaQueryWrapper<>();
|
||||
|
||||
// 添加关键词搜索条件
|
||||
if (StringUtils.hasText(keyword)) {
|
||||
queryWrapper.like(UserInfo::getNickname, keyword)
|
||||
.or()
|
||||
.like(UserInfo::getSignature, keyword);
|
||||
}
|
||||
|
||||
// 按创建时间降序排序
|
||||
queryWrapper.orderByDesc(UserInfo::getCreateTime);
|
||||
|
||||
// 分页查询
|
||||
Page<UserInfo> userPage = page(page, queryWrapper);
|
||||
|
||||
// 转换为VO
|
||||
Page<UserProfileVO> voPage = new Page<>();
|
||||
BeanUtils.copyProperties(userPage, voPage, "records");
|
||||
|
||||
List<UserProfileVO> voList = userPage.getRecords().stream()
|
||||
.map(user -> {
|
||||
UserProfileVO vo = new UserProfileVO();
|
||||
BeanUtils.copyProperties(user, vo);
|
||||
return vo;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
|
||||
voPage.setRecords(voList);
|
||||
|
||||
return Result.success(voPage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<UserProfileVO> getAdminUserDetail(Long userId) {
|
||||
// 查询用户信息
|
||||
UserInfo userInfo = getById(userId);
|
||||
if (userInfo == null) {
|
||||
return Result.error("用户不存在");
|
||||
}
|
||||
|
||||
// 转换为VO
|
||||
UserProfileVO vo = new UserProfileVO();
|
||||
BeanUtils.copyProperties(userInfo, vo);
|
||||
|
||||
// 查询用户统计信息
|
||||
// 例如:历史发布视频、历史评论等
|
||||
|
||||
return Result.success(vo);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Result<Void> updateUserStatus(Long userId, Integer status) {
|
||||
// 查询用户是否存在
|
||||
UserInfo userInfo = getById(userId);
|
||||
if (userInfo == null) {
|
||||
return Result.error("用户不存在");
|
||||
}
|
||||
|
||||
// 更新用户状态
|
||||
UserInfo updateUser = new UserInfo();
|
||||
updateUser.setId(userId);
|
||||
updateUser.setUpdateTime(LocalDateTime.now());
|
||||
updateUser.setUpdatedBy("admin");
|
||||
updateById(updateUser);
|
||||
|
||||
// 当禁用用户,需要清除用户相关的登录态等
|
||||
if (status == 0) {
|
||||
// 发送事件通知auth服务禁用该用户账号
|
||||
//TODO 可以优化为消息队列
|
||||
authFeignClient.lockUser(userId, true);
|
||||
}
|
||||
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Result<Void> resetUserPassword(Long userId, String newPassword) {
|
||||
// 查询用户是否存在
|
||||
UserInfo userInfo = getById(userId);
|
||||
if (userInfo == null) {
|
||||
return Result.error("用户不存在");
|
||||
}
|
||||
|
||||
// 调用auth服务重置密码(实际项目中通过Feign或消息队列)
|
||||
authFeignClient.resetPassword(userId, newPassword);
|
||||
|
||||
log.info("重置用户密码: userId={}, newPassword=***", userId);
|
||||
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<Object> getUserStats() {
|
||||
// 收集用户统计数据
|
||||
Map<String, Object> stats = new HashMap<>();
|
||||
|
||||
// 总用户数
|
||||
long totalUsers = count();
|
||||
stats.put("totalUsers", totalUsers);
|
||||
|
||||
// 今日新增用户
|
||||
LocalDate today = LocalDate.now();
|
||||
long todayNewUsers = count(new LambdaQueryWrapper<UserInfo>()
|
||||
.ge(UserInfo::getCreateTime, today.atStartOfDay()));
|
||||
stats.put("todayNewUsers", todayNewUsers);
|
||||
|
||||
// 活跃用户数(最近7天有登录记录的用户)
|
||||
// 此处需要结合用户登录记录表查询,简化处理
|
||||
stats.put("activeUsers", totalUsers * 0.3); // 示例数据
|
||||
|
||||
// 用户等级分布
|
||||
// 假设level字段代表用户等级
|
||||
Map<String, Object> levelDistribution = new HashMap<>();
|
||||
for (int i = 1; i <= 6; i++) {
|
||||
int level = i;
|
||||
long count = count(new LambdaQueryWrapper<UserInfo>().eq(UserInfo::getLevel, level));
|
||||
levelDistribution.put("level" + i, count);
|
||||
}
|
||||
stats.put("levelDistribution", levelDistribution);
|
||||
|
||||
return Result.success(stats);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新用户每日统计数据
|
||||
*/
|
||||
private void updateUserStatDaily(Long userId, String statType, int count) {
|
||||
LocalDate today = LocalDate.now();
|
||||
|
||||
// 查询今日统计记录
|
||||
UserStatDaily statDaily = userStatDailyMapper.selectOne(
|
||||
new LambdaQueryWrapper<UserStatDaily>()
|
||||
.eq(UserStatDaily::getUserId, userId)
|
||||
.eq(UserStatDaily::getStatDate, today)
|
||||
);
|
||||
|
||||
// 如果不存在,创建新记录
|
||||
if (statDaily == null) {
|
||||
statDaily = new UserStatDaily();
|
||||
statDaily.setUserId(userId);
|
||||
statDaily.setStatDate(today);
|
||||
statDaily.setFollowerIncrement(0);
|
||||
statDaily.setFollowingIncrement(0);
|
||||
statDaily.setVideoIncrement(0);
|
||||
statDaily.setLikeIncrement(0);
|
||||
statDaily.setCoinIncrement(0);
|
||||
statDaily.setFavoriteIncrement(0);
|
||||
statDaily.setViewIncrement(0L);
|
||||
statDaily.setCreateTime(LocalDateTime.now());
|
||||
}
|
||||
|
||||
// 根据统计类型更新数据
|
||||
switch (statType) {
|
||||
case "video":
|
||||
statDaily.setVideoIncrement(statDaily.getVideoIncrement() + count);
|
||||
break;
|
||||
case "like":
|
||||
statDaily.setLikeIncrement(statDaily.getLikeIncrement() + count);
|
||||
break;
|
||||
case "follower":
|
||||
statDaily.setFollowerIncrement(statDaily.getFollowerIncrement() + count);
|
||||
break;
|
||||
case "following":
|
||||
statDaily.setFollowingIncrement(statDaily.getFollowingIncrement() + count);
|
||||
break;
|
||||
case "favorite":
|
||||
statDaily.setFavoriteIncrement(statDaily.getFavoriteIncrement() + count);
|
||||
break;
|
||||
case "coin":
|
||||
statDaily.setCoinIncrement(statDaily.getCoinIncrement() + count);
|
||||
break;
|
||||
case "view":
|
||||
statDaily.setViewIncrement(statDaily.getViewIncrement() + count);
|
||||
break;
|
||||
}
|
||||
|
||||
// 保存更新
|
||||
if (statDaily.getId() == null) {
|
||||
userStatDailyMapper.insert(statDaily);
|
||||
} else {
|
||||
userStatDailyMapper.updateById(statDaily);
|
||||
}
|
||||
}
|
||||
}
|
||||
+148
@@ -0,0 +1,148 @@
|
||||
package com.nuaa.video.user.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.nuaa.video.common.model.enums.ResultCode;
|
||||
import com.nuaa.video.common.model.vo.Result;
|
||||
import com.nuaa.video.user.dto.UserMessageDTO;
|
||||
import com.nuaa.video.user.entity.UserInfo;
|
||||
import com.nuaa.video.user.entity.UserMessage;
|
||||
import com.nuaa.video.user.mapper.UserInfoMapper;
|
||||
import com.nuaa.video.user.mapper.UserMessageMapper;
|
||||
import com.nuaa.video.user.service.UserMessageService;
|
||||
import com.nuaa.video.user.vo.UserMessageVO;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 用户私信服务实现类
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class UserMessageServiceImpl extends ServiceImpl<UserMessageMapper, UserMessage> implements UserMessageService {
|
||||
|
||||
private final UserMessageMapper userMessageMapper;
|
||||
private final UserInfoMapper userInfoMapper;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Result<Void> sendMessage(Long userId, UserMessageDTO messageDTO) {
|
||||
// 验证发送者和接收者是否存在
|
||||
UserInfo sender = userInfoMapper.selectById(userId);
|
||||
UserInfo receiver = userInfoMapper.selectById(messageDTO.getReceiverId());
|
||||
|
||||
if (sender == null || receiver == null) {
|
||||
return Result.error(ResultCode.USER_NOT_FOUND.getCode(), ResultCode.USER_NOT_FOUND.getMessage());
|
||||
}
|
||||
|
||||
// 不能给自己发私信
|
||||
if (userId.equals(messageDTO.getReceiverId())) {
|
||||
return Result.error("不能给自己发私信");
|
||||
}
|
||||
|
||||
// 创建私信记录
|
||||
UserMessage message = new UserMessage();
|
||||
message.setSenderId(userId);
|
||||
message.setReceiverId(messageDTO.getReceiverId());
|
||||
message.setContent(messageDTO.getContent());
|
||||
message.setIsRead(0);
|
||||
message.setCreateTime(LocalDateTime.now());
|
||||
|
||||
// 保存私信
|
||||
save(message);
|
||||
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<Page<UserMessageVO>> getMessageList(Long userId, Page<UserMessage> page) {
|
||||
// 验证用户是否存在
|
||||
UserInfo user = userInfoMapper.selectById(userId);
|
||||
if (user == null) {
|
||||
return Result.error(ResultCode.USER_NOT_FOUND.getCode(), ResultCode.USER_NOT_FOUND.getMessage());
|
||||
}
|
||||
|
||||
// 查询私信列表
|
||||
Page<UserMessage> messagePage = userMessageMapper.selectMessageGroupList(page, userId);
|
||||
|
||||
// 转换为VO对象
|
||||
Page<UserMessageVO> resultPage = new Page<>();
|
||||
BeanUtils.copyProperties(messagePage, resultPage, "records");
|
||||
|
||||
// 转换记录
|
||||
resultPage.setRecords(messagePage.getRecords().stream().map(this::convertToVO).collect(Collectors.toList()));
|
||||
|
||||
return Result.success(resultPage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<Page<UserMessageVO>> getMessageDetail(Long userId, Long targetId, Page<UserMessage> page) {
|
||||
// 验证用户是否存在
|
||||
UserInfo user = userInfoMapper.selectById(userId);
|
||||
UserInfo target = userInfoMapper.selectById(targetId);
|
||||
|
||||
if (user == null || target == null) {
|
||||
return Result.error(ResultCode.USER_NOT_FOUND.getCode(), ResultCode.USER_NOT_FOUND.getMessage());
|
||||
}
|
||||
|
||||
// 查询私信记录
|
||||
Page<UserMessage> messagePage = userMessageMapper.selectMessageDetail(page, userId, targetId);
|
||||
|
||||
// 标记私信为已读
|
||||
markMessageRead(userId, targetId);
|
||||
|
||||
// 转换为VO对象
|
||||
Page<UserMessageVO> resultPage = new Page<>();
|
||||
BeanUtils.copyProperties(messagePage, resultPage, "records");
|
||||
|
||||
// 转换记录
|
||||
resultPage.setRecords(messagePage.getRecords().stream().map(this::convertToVO).collect(Collectors.toList()));
|
||||
|
||||
return Result.success(resultPage);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Result<Void> markMessageRead(Long userId, Long senderId) {
|
||||
// 验证用户是否存在
|
||||
UserInfo user = userInfoMapper.selectById(userId);
|
||||
if (user == null) {
|
||||
return Result.error(ResultCode.USER_NOT_FOUND.getCode(), ResultCode.USER_NOT_FOUND.getMessage());
|
||||
}
|
||||
|
||||
// 标记私信为已读
|
||||
userMessageMapper.markMessageRead(userId, senderId);
|
||||
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<Integer> getUnreadMessageCount(Long userId) {
|
||||
// 验证用户是否存在
|
||||
UserInfo user = userInfoMapper.selectById(userId);
|
||||
if (user == null) {
|
||||
return Result.error(ResultCode.USER_NOT_FOUND.getCode(), ResultCode.USER_NOT_FOUND.getMessage());
|
||||
}
|
||||
|
||||
// 获取未读私信数量
|
||||
int count = userMessageMapper.countUnreadMessage(userId);
|
||||
|
||||
return Result.success(count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将实体对象转换为VO对象
|
||||
*/
|
||||
private UserMessageVO convertToVO(UserMessage message) {
|
||||
UserMessageVO vo = new UserMessageVO();
|
||||
BeanUtils.copyProperties(message, vo);
|
||||
return vo;
|
||||
}
|
||||
}
|
||||
+156
@@ -0,0 +1,156 @@
|
||||
package com.nuaa.video.user.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.nuaa.video.common.model.enums.ResultCode;
|
||||
import com.nuaa.video.common.model.vo.Result;
|
||||
import com.nuaa.video.user.entity.UserInfo;
|
||||
import com.nuaa.video.user.entity.UserNotification;
|
||||
import com.nuaa.video.user.entity.UserSetting;
|
||||
import com.nuaa.video.user.mapper.UserInfoMapper;
|
||||
import com.nuaa.video.user.mapper.UserNotificationMapper;
|
||||
import com.nuaa.video.user.mapper.UserSettingMapper;
|
||||
import com.nuaa.video.user.service.UserNotificationService;
|
||||
import com.nuaa.video.user.vo.UserNotificationVO;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 用户通知服务实现类
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class UserNotificationServiceImpl extends ServiceImpl<UserNotificationMapper, UserNotification> implements UserNotificationService {
|
||||
|
||||
private final UserNotificationMapper userNotificationMapper;
|
||||
private final UserInfoMapper userInfoMapper;
|
||||
private final UserSettingMapper userSettingMapper;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Result<Void> createNotification(Long userId, Integer type, String content, Long relatedId) {
|
||||
// 验证用户是否存在
|
||||
UserInfo user = userInfoMapper.selectById(userId);
|
||||
if (user == null) {
|
||||
return Result.error(ResultCode.USER_NOT_FOUND.getCode(), ResultCode.USER_NOT_FOUND.getMessage());
|
||||
}
|
||||
|
||||
// 检查用户通知设置
|
||||
UserSetting setting = userSettingMapper.selectOne(
|
||||
new LambdaQueryWrapper<UserSetting>().eq(UserSetting::getUserId, userId)
|
||||
);
|
||||
|
||||
// 根据设置决定是否发送通知
|
||||
if (setting != null) {
|
||||
boolean shouldNotify = false;
|
||||
|
||||
switch (type) {
|
||||
case 1: // 点赞通知
|
||||
shouldNotify = setting.getLikeNotify() == 1;
|
||||
break;
|
||||
case 2: // 评论通知
|
||||
shouldNotify = setting.getCommentNotify() == 1;
|
||||
break;
|
||||
case 3: // 关注通知
|
||||
shouldNotify = setting.getFollowNotify() == 1;
|
||||
break;
|
||||
case 4: // 系统通知
|
||||
shouldNotify = setting.getSystemNotify() == 1;
|
||||
break;
|
||||
default:
|
||||
shouldNotify = true;
|
||||
}
|
||||
|
||||
if (!shouldNotify) {
|
||||
return Result.success(); // 用户已关闭该类型通知
|
||||
}
|
||||
}
|
||||
|
||||
// 创建通知
|
||||
UserNotification notification = new UserNotification();
|
||||
notification.setUserId(userId);
|
||||
notification.setType(type);
|
||||
notification.setContent(content);
|
||||
notification.setRelatedId(relatedId);
|
||||
notification.setIsRead(0);
|
||||
notification.setCreateTime(LocalDateTime.now());
|
||||
|
||||
// 保存通知
|
||||
save(notification);
|
||||
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<Page<UserNotificationVO>> getNotificationList(Long userId, Integer type, Page<UserNotification> page) {
|
||||
// 验证用户是否存在
|
||||
UserInfo user = userInfoMapper.selectById(userId);
|
||||
if (user == null) {
|
||||
return Result.error(ResultCode.USER_NOT_FOUND.getCode(), ResultCode.USER_NOT_FOUND.getMessage());
|
||||
}
|
||||
|
||||
// 构建查询条件
|
||||
LambdaQueryWrapper<UserNotification> queryWrapper = new LambdaQueryWrapper<UserNotification>()
|
||||
.eq(UserNotification::getUserId, userId)
|
||||
.eq(type != null, UserNotification::getType, type)
|
||||
.orderByDesc(UserNotification::getCreateTime);
|
||||
|
||||
// 分页查询
|
||||
Page<UserNotification> notificationPage = page(page, queryWrapper);
|
||||
|
||||
// 转换为VO对象
|
||||
Page<UserNotificationVO> resultPage = new Page<>();
|
||||
BeanUtils.copyProperties(notificationPage, resultPage, "records");
|
||||
|
||||
// 转换记录
|
||||
resultPage.setRecords(notificationPage.getRecords().stream().map(this::convertToVO).collect(Collectors.toList()));
|
||||
|
||||
return Result.success(resultPage);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Result<Void> markNotificationRead(Long userId, Long notificationId) {
|
||||
// 验证用户是否存在
|
||||
UserInfo user = userInfoMapper.selectById(userId);
|
||||
if (user == null) {
|
||||
return Result.error(ResultCode.USER_NOT_FOUND.getCode(), ResultCode.USER_NOT_FOUND.getMessage());
|
||||
}
|
||||
|
||||
// 标记通知为已读
|
||||
userNotificationMapper.markNotificationRead(userId, notificationId);
|
||||
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<Integer> getUnreadNotificationCount(Long userId) {
|
||||
// 验证用户是否存在
|
||||
UserInfo user = userInfoMapper.selectById(userId);
|
||||
if (user == null) {
|
||||
return Result.error(ResultCode.USER_NOT_FOUND.getCode(), ResultCode.USER_NOT_FOUND.getMessage());
|
||||
}
|
||||
|
||||
// 获取未读通知数量
|
||||
int count = userNotificationMapper.countUnreadNotification(userId);
|
||||
|
||||
return Result.success(count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将实体对象转换为VO对象
|
||||
*/
|
||||
private UserNotificationVO convertToVO(UserNotification notification) {
|
||||
UserNotificationVO vo = new UserNotificationVO();
|
||||
BeanUtils.copyProperties(notification, vo);
|
||||
return vo;
|
||||
}
|
||||
}
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
package com.nuaa.video.user.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.nuaa.video.common.model.enums.ResultCode;
|
||||
import com.nuaa.video.common.model.vo.Result;
|
||||
import com.nuaa.video.user.dto.UserSettingDTO;
|
||||
import com.nuaa.video.user.entity.UserInfo;
|
||||
import com.nuaa.video.user.entity.UserSetting;
|
||||
import com.nuaa.video.user.mapper.UserInfoMapper;
|
||||
import com.nuaa.video.user.mapper.UserSettingMapper;
|
||||
import com.nuaa.video.user.service.UserSettingService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 用户设置服务实现类
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class UserSettingServiceImpl extends ServiceImpl<UserSettingMapper, UserSetting> implements UserSettingService {
|
||||
|
||||
private final UserSettingMapper userSettingMapper;
|
||||
private final UserInfoMapper userInfoMapper;
|
||||
|
||||
@Override
|
||||
public Result<UserSetting> getUserSetting(Long userId) {
|
||||
// 验证用户是否存在
|
||||
UserInfo user = userInfoMapper.selectById(userId);
|
||||
if (user == null) {
|
||||
return Result.error(ResultCode.USER_NOT_FOUND.getCode(), ResultCode.USER_NOT_FOUND.getMessage());
|
||||
}
|
||||
|
||||
// 查询用户设置
|
||||
UserSetting setting = getOne(new LambdaQueryWrapper<UserSetting>().eq(UserSetting::getUserId, userId));
|
||||
|
||||
// 如果设置不存在,创建默认设置
|
||||
if (setting == null) {
|
||||
createDefaultSetting(userId);
|
||||
setting = getOne(new LambdaQueryWrapper<UserSetting>().eq(UserSetting::getUserId, userId));
|
||||
}
|
||||
|
||||
return Result.success(setting);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Result<Void> updateUserSetting(Long userId, UserSettingDTO settingDTO) {
|
||||
// 验证用户是否存在
|
||||
UserInfo user = userInfoMapper.selectById(userId);
|
||||
if (user == null) {
|
||||
return Result.error(ResultCode.USER_NOT_FOUND.getCode(), ResultCode.USER_NOT_FOUND.getMessage());
|
||||
}
|
||||
|
||||
// 查询用户设置
|
||||
UserSetting setting = getOne(new LambdaQueryWrapper<UserSetting>().eq(UserSetting::getUserId, userId));
|
||||
|
||||
// 如果设置不存在,创建默认设置
|
||||
if (setting == null) {
|
||||
setting = new UserSetting();
|
||||
setting.setUserId(userId);
|
||||
setting.setCreatedBy(userId.toString());
|
||||
setting.setCreateTime(LocalDateTime.now());
|
||||
}
|
||||
|
||||
// 更新设置
|
||||
if (settingDTO.getPrivacyLevel() != null) {
|
||||
setting.setPrivacyLevel(settingDTO.getPrivacyLevel());
|
||||
}
|
||||
if (settingDTO.getCommentNotify() != null) {
|
||||
setting.setCommentNotify(settingDTO.getCommentNotify());
|
||||
}
|
||||
if (settingDTO.getLikeNotify() != null) {
|
||||
setting.setLikeNotify(settingDTO.getLikeNotify());
|
||||
}
|
||||
if (settingDTO.getFollowNotify() != null) {
|
||||
setting.setFollowNotify(settingDTO.getFollowNotify());
|
||||
}
|
||||
if (settingDTO.getSystemNotify() != null) {
|
||||
setting.setSystemNotify(settingDTO.getSystemNotify());
|
||||
}
|
||||
|
||||
setting.setUpdatedBy(userId.toString());
|
||||
setting.setUpdateTime(LocalDateTime.now());
|
||||
|
||||
// 保存设置
|
||||
saveOrUpdate(setting);
|
||||
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Result<Void> createDefaultSetting(Long userId) {
|
||||
// 验证用户是否存在
|
||||
UserInfo user = userInfoMapper.selectById(userId);
|
||||
if (user == null) {
|
||||
return Result.error(ResultCode.USER_NOT_FOUND.getCode(), ResultCode.USER_NOT_FOUND.getMessage());
|
||||
}
|
||||
|
||||
// 检查是否已存在设置
|
||||
Long count = count(new LambdaQueryWrapper<UserSetting>().eq(UserSetting::getUserId, userId));
|
||||
if (count > 0) {
|
||||
return Result.success(); // 已存在设置,不需要创建
|
||||
}
|
||||
|
||||
// 创建默认设置
|
||||
UserSetting setting = new UserSetting();
|
||||
setting.setUserId(userId);
|
||||
setting.setPrivacyLevel(1); // 公开
|
||||
setting.setCommentNotify(1); // 开启
|
||||
setting.setLikeNotify(1); // 开启
|
||||
setting.setFollowNotify(1); // 开启
|
||||
setting.setSystemNotify(1); // 开启
|
||||
setting.setCreatedBy("system");
|
||||
setting.setUpdatedBy("system");
|
||||
setting.setCreateTime(LocalDateTime.now());
|
||||
setting.setUpdateTime(LocalDateTime.now());
|
||||
|
||||
// 保存设置
|
||||
save(setting);
|
||||
|
||||
return Result.success();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.nuaa.video.user.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 用户私信VO
|
||||
*/
|
||||
@Data
|
||||
public class UserMessageVO implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 消息ID
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 发送者ID
|
||||
*/
|
||||
private Long senderId;
|
||||
|
||||
/**
|
||||
* 发送者昵称
|
||||
*/
|
||||
private String senderNickname;
|
||||
|
||||
/**
|
||||
* 发送者头像
|
||||
*/
|
||||
private String senderAvatar;
|
||||
|
||||
/**
|
||||
* 接收者ID
|
||||
*/
|
||||
private Long receiverId;
|
||||
|
||||
/**
|
||||
* 接收者昵称
|
||||
*/
|
||||
private String receiverNickname;
|
||||
|
||||
/**
|
||||
* 接收者头像
|
||||
*/
|
||||
private String receiverAvatar;
|
||||
|
||||
/**
|
||||
* 消息内容
|
||||
*/
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 是否已读(0:未读,1:已读)
|
||||
*/
|
||||
private Integer isRead;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private LocalDateTime createTime;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.nuaa.video.user.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 用户通知VO
|
||||
*/
|
||||
@Data
|
||||
public class UserNotificationVO implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 通知ID
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 通知类型(1:点赞,2:评论,3:关注,4:系统通知)
|
||||
*/
|
||||
private Integer type;
|
||||
|
||||
/**
|
||||
* 通知内容
|
||||
*/
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 相关ID
|
||||
*/
|
||||
private Long relatedId;
|
||||
|
||||
/**
|
||||
* 是否已读(0:未读,1:已读)
|
||||
*/
|
||||
private Integer isRead;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private LocalDateTime createTime;
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package com.nuaa.video.user.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 用户资料VO
|
||||
*/
|
||||
@Data
|
||||
public class UserProfileVO implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 昵称
|
||||
*/
|
||||
private String nickname;
|
||||
|
||||
/**
|
||||
* 头像URL
|
||||
*/
|
||||
private String avatar;
|
||||
|
||||
/**
|
||||
* 性别(0:未知,1:男,2:女)
|
||||
*/
|
||||
private Integer gender;
|
||||
|
||||
/**
|
||||
* 生日
|
||||
*/
|
||||
private LocalDate birthday;
|
||||
|
||||
/**
|
||||
* 个性签名
|
||||
*/
|
||||
private String signature;
|
||||
|
||||
/**
|
||||
* 地区
|
||||
*/
|
||||
private String region;
|
||||
|
||||
/**
|
||||
* 粉丝数
|
||||
*/
|
||||
private Integer followerCount;
|
||||
|
||||
/**
|
||||
* 关注数
|
||||
*/
|
||||
private Integer followingCount;
|
||||
|
||||
/**
|
||||
* 视频数
|
||||
*/
|
||||
private Integer videoCount;
|
||||
|
||||
/**
|
||||
* 获赞数
|
||||
*/
|
||||
private Integer likeCount;
|
||||
|
||||
/**
|
||||
* 等级
|
||||
*/
|
||||
private Integer level;
|
||||
|
||||
/**
|
||||
* VIP等级
|
||||
*/
|
||||
private Integer vipLevel;
|
||||
|
||||
/**
|
||||
* VIP到期时间
|
||||
*/
|
||||
private LocalDateTime vipExpireTime;
|
||||
|
||||
/**
|
||||
* 是否已关注
|
||||
*/
|
||||
private Boolean isFollowed;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private LocalDateTime createTime;
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
server:
|
||||
port: 9002
|
||||
|
||||
spring:
|
||||
application:
|
||||
name: video-user
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
server-addr: ${NACOS_HOST:nacos}:${NACOS_PORT:8848}
|
||||
namespace: public
|
||||
group: DEFAULT_GROUP
|
||||
sentinel:
|
||||
transport:
|
||||
dashboard: ${SENTINEL_HOST:sentinel}:${SENTINEL_PORT:8080}
|
||||
port: 8722
|
||||
eager: true
|
||||
datasource:
|
||||
url: jdbc:mysql://${MYSQL_HOST:mysql}:${MYSQL_PORT:3306}/video_user?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
|
||||
username: root
|
||||
password: root
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
data:
|
||||
redis:
|
||||
host: ${REDIS_HOST:redis}
|
||||
port: ${REDIS_PORT:6379}
|
||||
password: redis_EsJ8FX
|
||||
database: 1
|
||||
timeout: 10000
|
||||
lettuce:
|
||||
pool:
|
||||
max-active: 200
|
||||
max-wait: -1
|
||||
max-idle: 10
|
||||
min-idle: 0
|
||||
|
||||
mybatis-plus:
|
||||
mapper-locations: classpath:mapper/*.xml
|
||||
type-aliases-package: com.nuaa.video.user.entity
|
||||
configuration:
|
||||
map-underscore-to-camel-case: true
|
||||
cache-enabled: false
|
||||
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
||||
global-config:
|
||||
db-config:
|
||||
id-type: auto
|
||||
logic-delete-field: is_deleted
|
||||
logic-delete-value: 1
|
||||
logic-not-delete-value: 0
|
||||
@@ -0,0 +1,38 @@
|
||||
server:
|
||||
port: 9002
|
||||
|
||||
spring:
|
||||
application:
|
||||
name: video-user
|
||||
profiles:
|
||||
active: ${SPRING_PROFILES_ACTIVE:dev}
|
||||
config:
|
||||
import:
|
||||
- optional:nacos:common.yaml?refreshEnabled=true
|
||||
- optional:nacos:common-redis-${spring.profiles.active}.yaml?refreshEnabled=true
|
||||
- optional:nacos:${spring.application.name}.yaml?refreshEnabled=true
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
server-addr: ${NACOS_SERVER_ADDR:${NACOS_HOST:127.0.0.1}:${NACOS_PORT:8848}}
|
||||
namespace: ${NACOS_NAMESPACE:}
|
||||
group: ${NACOS_GROUP:DEFAULT_GROUP}
|
||||
username: ${NACOS_USERNAME:nacos}
|
||||
password: ${NACOS_PASSWORD:nacos}
|
||||
ip: ${SPRING_CLOUD_NACOS_DISCOVERY_IP:}
|
||||
config:
|
||||
server-addr: ${spring.cloud.nacos.discovery.server-addr}
|
||||
namespace: ${spring.cloud.nacos.discovery.namespace}
|
||||
group: ${spring.cloud.nacos.discovery.group}
|
||||
file-extension: yaml
|
||||
username: ${NACOS_USERNAME:nacos}
|
||||
password: ${NACOS_PASSWORD:nacos}
|
||||
|
||||
management:
|
||||
endpoints:
|
||||
web:
|
||||
exposure:
|
||||
include: '*'
|
||||
endpoint:
|
||||
health:
|
||||
show-details: always
|
||||
@@ -0,0 +1,23 @@
|
||||
spring:
|
||||
application:
|
||||
name: video-user
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
server-addr: ${NACOS_HOST:nacos}:${NACOS_PORT:8848}
|
||||
namespace: public
|
||||
group: DEFAULT_GROUP
|
||||
username: ${NACOS_USERNAME:nacos}
|
||||
password: ${NACOS_PASSWORD:nacos}
|
||||
ephemeral: true
|
||||
ip: ${USER_IP:}
|
||||
port: ${server.port:9002}
|
||||
fail-fast: true
|
||||
register-enabled: true
|
||||
config:
|
||||
server-addr: ${NACOS_HOST:nacos}:${NACOS_PORT:8848}
|
||||
namespace: public
|
||||
group: DEFAULT_GROUP
|
||||
username: ${NACOS_USERNAME:nacos}
|
||||
password: ${NACOS_PASSWORD:nacos}
|
||||
file-extension: yaml
|
||||
@@ -0,0 +1,30 @@
|
||||
<?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.nuaa.video.user.mapper.UserFollowMapper">
|
||||
|
||||
<!-- 查询用户关注列表 -->
|
||||
<select id="selectFollowingList" resultType="com.nuaa.video.user.entity.UserInfo">
|
||||
SELECT u.*
|
||||
FROM user_follow f
|
||||
JOIN user_info u ON f.follow_id = u.id
|
||||
WHERE f.user_id = #{userId}
|
||||
ORDER BY f.create_time DESC
|
||||
</select>
|
||||
|
||||
<!-- 查询用户粉丝列表 -->
|
||||
<select id="selectFollowerList" resultType="com.nuaa.video.user.entity.UserInfo">
|
||||
SELECT u.*
|
||||
FROM user_follow f
|
||||
JOIN user_info u ON f.user_id = u.id
|
||||
WHERE f.follow_id = #{userId}
|
||||
ORDER BY f.create_time DESC
|
||||
</select>
|
||||
|
||||
<!-- 检查是否已关注 -->
|
||||
<select id="checkIsFollowed" resultType="int">
|
||||
SELECT COUNT(1)
|
||||
FROM user_follow
|
||||
WHERE user_id = #{userId} AND follow_id = #{followId}
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
@@ -0,0 +1,19 @@
|
||||
<?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.nuaa.video.user.mapper.UserInfoMapper">
|
||||
|
||||
<!-- 增加关注数 -->
|
||||
<update id="increaseFollowingCount">
|
||||
UPDATE user_info
|
||||
SET following_count = following_count + #{count}
|
||||
WHERE id = #{userId}
|
||||
</update>
|
||||
|
||||
<!-- 增加粉丝数 -->
|
||||
<update id="increaseFollowerCount">
|
||||
UPDATE user_info
|
||||
SET follower_count = follower_count + #{count}
|
||||
WHERE id = #{userId}
|
||||
</update>
|
||||
|
||||
</mapper>
|
||||
@@ -0,0 +1,67 @@
|
||||
<?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.nuaa.video.user.mapper.UserMessageMapper">
|
||||
|
||||
<!-- 查询私信列表(按会话分组) -->
|
||||
<select id="selectMessageGroupList" resultType="com.nuaa.video.user.entity.UserMessage">
|
||||
SELECT
|
||||
m.*,
|
||||
u1.nickname as sender_nickname,
|
||||
u1.avatar as sender_avatar,
|
||||
u2.nickname as receiver_nickname,
|
||||
u2.avatar as receiver_avatar
|
||||
FROM (
|
||||
SELECT
|
||||
MAX(id) as id,
|
||||
IF(sender_id = #{userId}, receiver_id, sender_id) as chat_user_id,
|
||||
MAX(create_time) as latest_time
|
||||
FROM
|
||||
user_message
|
||||
WHERE
|
||||
sender_id = #{userId} OR receiver_id = #{userId}
|
||||
GROUP BY
|
||||
chat_user_id
|
||||
ORDER BY
|
||||
latest_time DESC
|
||||
) as t
|
||||
JOIN user_message m ON t.id = m.id
|
||||
JOIN user_info u1 ON m.sender_id = u1.id
|
||||
JOIN user_info u2 ON m.receiver_id = u2.id
|
||||
ORDER BY m.create_time DESC
|
||||
</select>
|
||||
|
||||
<!-- 查询与指定用户的私信记录 -->
|
||||
<select id="selectMessageDetail" resultType="com.nuaa.video.user.entity.UserMessage">
|
||||
SELECT
|
||||
m.*,
|
||||
u1.nickname as sender_nickname,
|
||||
u1.avatar as sender_avatar,
|
||||
u2.nickname as receiver_nickname,
|
||||
u2.avatar as receiver_avatar
|
||||
FROM
|
||||
user_message m
|
||||
JOIN user_info u1 ON m.sender_id = u1.id
|
||||
JOIN user_info u2 ON m.receiver_id = u2.id
|
||||
WHERE
|
||||
(m.sender_id = #{userId} AND m.receiver_id = #{targetId})
|
||||
OR
|
||||
(m.sender_id = #{targetId} AND m.receiver_id = #{userId})
|
||||
ORDER BY
|
||||
m.create_time ASC
|
||||
</select>
|
||||
|
||||
<!-- 标记私信为已读 -->
|
||||
<update id="markMessageRead">
|
||||
UPDATE user_message
|
||||
SET is_read = 1
|
||||
WHERE receiver_id = #{userId} AND sender_id = #{senderId} AND is_read = 0
|
||||
</update>
|
||||
|
||||
<!-- 获取未读私信数量 -->
|
||||
<select id="countUnreadMessage" resultType="int">
|
||||
SELECT COUNT(1)
|
||||
FROM user_message
|
||||
WHERE receiver_id = #{userId} AND is_read = 0
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
@@ -0,0 +1,23 @@
|
||||
<?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.nuaa.video.user.mapper.UserNotificationMapper">
|
||||
|
||||
<!-- 标记通知为已读 -->
|
||||
<update id="markNotificationRead">
|
||||
UPDATE user_notification
|
||||
SET is_read = 1
|
||||
WHERE user_id = #{userId}
|
||||
<if test="notificationId != null">
|
||||
AND id = #{notificationId}
|
||||
</if>
|
||||
AND is_read = 0
|
||||
</update>
|
||||
|
||||
<!-- 获取未读通知数量 -->
|
||||
<select id="countUnreadNotification" resultType="int">
|
||||
SELECT COUNT(1)
|
||||
FROM user_notification
|
||||
WHERE user_id = #{userId} AND is_read = 0
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
+203
@@ -0,0 +1,203 @@
|
||||
package com.nuaa.video.user.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.nuaa.video.common.model.vo.Result;
|
||||
import com.nuaa.video.user.entity.UserFollow;
|
||||
import com.nuaa.video.user.entity.UserInfo;
|
||||
import com.nuaa.video.user.mapper.UserFollowMapper;
|
||||
import com.nuaa.video.user.mapper.UserInfoMapper;
|
||||
import com.nuaa.video.user.service.impl.UserFollowServiceImpl;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
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 java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* UserFollowService 单元测试
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@DisplayName("用户关注服务测试")
|
||||
class UserFollowServiceTest {
|
||||
|
||||
@Mock
|
||||
private UserFollowMapper userFollowMapper;
|
||||
|
||||
@Mock
|
||||
private UserInfoMapper userInfoMapper;
|
||||
|
||||
@InjectMocks
|
||||
private UserFollowServiceImpl userFollowService;
|
||||
|
||||
private UserInfo testUser;
|
||||
private UserInfo followUser;
|
||||
private UserFollow userFollow;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
// 初始化测试用户
|
||||
testUser = new UserInfo();
|
||||
testUser.setId(1L);
|
||||
testUser.setNickname("测试用户");
|
||||
testUser.setFollowerCount(100);
|
||||
testUser.setFollowingCount(50);
|
||||
|
||||
// 初始化被关注用户
|
||||
followUser = new UserInfo();
|
||||
followUser.setId(2L);
|
||||
followUser.setNickname("被关注用户");
|
||||
followUser.setFollowerCount(10);
|
||||
followUser.setFollowingCount(5);
|
||||
|
||||
// 初始化关注关系
|
||||
userFollow = new UserFollow();
|
||||
userFollow.setId(1L);
|
||||
userFollow.setUserId(1L);
|
||||
userFollow.setFollowId(2L);
|
||||
userFollow.setCreateTime(LocalDateTime.now());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("关注用户 - 成功")
|
||||
void testFollowUserSuccess() {
|
||||
// Given
|
||||
Long userId = 1L;
|
||||
Long followId = 2L;
|
||||
|
||||
when(userInfoMapper.selectById(userId)).thenReturn(testUser);
|
||||
when(userInfoMapper.selectById(followId)).thenReturn(followUser);
|
||||
when(userFollowMapper.selectOne(any())).thenReturn(null); // 未关注过
|
||||
when(userFollowMapper.insert(any(UserFollow.class))).thenReturn(1);
|
||||
when(userInfoMapper.updateById(any(UserInfo.class))).thenReturn(1);
|
||||
|
||||
// Then
|
||||
assertNotNull(testUser);
|
||||
assertNotNull(followUser);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("关注用户 - 不能关注自己")
|
||||
void testFollowUserCannotFollowSelf() {
|
||||
// Given
|
||||
Long userId = 1L;
|
||||
Long followId = 1L;
|
||||
|
||||
// Then
|
||||
assertEquals(userId, followId); // 不能关注自己
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("关注用户 - 已经关注过")
|
||||
void testFollowUserAlreadyFollowed() {
|
||||
// Given
|
||||
Long userId = 1L;
|
||||
Long followId = 2L;
|
||||
|
||||
when(userFollowMapper.selectOne(any())).thenReturn(userFollow); // 已关注
|
||||
|
||||
// Then
|
||||
assertNotNull(userFollow);
|
||||
assertEquals(userId, userFollow.getUserId());
|
||||
assertEquals(followId, userFollow.getFollowId());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("取消关注 - 成功")
|
||||
void testUnfollowUserSuccess() {
|
||||
// Given
|
||||
Long userId = 1L;
|
||||
Long followId = 2L;
|
||||
|
||||
when(userFollowMapper.selectOne(any())).thenReturn(userFollow);
|
||||
when(userFollowMapper.deleteById(any())).thenReturn(1);
|
||||
when(userInfoMapper.selectById(userId)).thenReturn(testUser);
|
||||
when(userInfoMapper.selectById(followId)).thenReturn(followUser);
|
||||
when(userInfoMapper.updateById(any(UserInfo.class))).thenReturn(1);
|
||||
|
||||
// Then
|
||||
assertNotNull(userFollow);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("取消关注 - 未关注")
|
||||
void testUnfollowUserNotFollowed() {
|
||||
// Given
|
||||
Long userId = 1L;
|
||||
Long followId = 2L;
|
||||
|
||||
when(userFollowMapper.selectOne(any())).thenReturn(null); // 未关注
|
||||
|
||||
// Then
|
||||
// 取消关注应该返回"未关注"错误
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("获取关注列表 - 成功")
|
||||
void testGetFollowingListSuccess() {
|
||||
// Given
|
||||
Long userId = 1L;
|
||||
Page<UserInfo> page = new Page<>(1, 10);
|
||||
|
||||
List<UserInfo> followingList = new ArrayList<>();
|
||||
followingList.add(followUser);
|
||||
|
||||
// Then
|
||||
assertNotNull(page);
|
||||
assertEquals(1, page.getCurrent());
|
||||
assertEquals(10, page.getSize());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("获取粉丝列表 - 成功")
|
||||
void testGetFollowerListSuccess() {
|
||||
// Given
|
||||
Long userId = 1L;
|
||||
Page<UserInfo> page = new Page<>(1, 10);
|
||||
|
||||
List<UserInfo> followerList = new ArrayList<>();
|
||||
followerList.add(testUser);
|
||||
|
||||
// Then
|
||||
assertNotNull(page);
|
||||
assertEquals(1, page.getCurrent());
|
||||
assertEquals(10, page.getSize());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("检查是否已关注 - 已关注")
|
||||
void testCheckIsFollowedTrue() {
|
||||
// Given
|
||||
Long userId = 1L;
|
||||
Long followId = 2L;
|
||||
|
||||
when(userFollowMapper.selectOne(any())).thenReturn(userFollow);
|
||||
|
||||
// Then
|
||||
assertNotNull(userFollow);
|
||||
assertEquals(userId, userFollow.getUserId());
|
||||
assertEquals(followId, userFollow.getFollowId());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("检查是否已关注 - 未关注")
|
||||
void testCheckIsFollowedFalse() {
|
||||
// Given
|
||||
Long userId = 1L;
|
||||
Long followId = 2L;
|
||||
|
||||
when(userFollowMapper.selectOne(any())).thenReturn(null);
|
||||
|
||||
// Then
|
||||
// 应该返回false
|
||||
}
|
||||
}
|
||||
+219
@@ -0,0 +1,219 @@
|
||||
package com.nuaa.video.user.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.nuaa.video.common.model.vo.Result;
|
||||
import com.nuaa.video.user.dto.UserProfileDTO;
|
||||
import com.nuaa.video.user.entity.UserInfo;
|
||||
import com.nuaa.video.user.mapper.UserInfoMapper;
|
||||
import com.nuaa.video.user.service.impl.UserInfoServiceImpl;
|
||||
import com.nuaa.video.user.vo.UserProfileVO;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
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.web.multipart.MultipartFile;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* UserInfoService 单元测试
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@DisplayName("用户信息服务测试")
|
||||
class UserInfoServiceTest {
|
||||
|
||||
@Mock
|
||||
private UserInfoMapper userInfoMapper;
|
||||
|
||||
@InjectMocks
|
||||
private UserInfoServiceImpl userInfoService;
|
||||
|
||||
private UserInfo testUserInfo;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
testUserInfo = new UserInfo();
|
||||
testUserInfo.setId(1L);
|
||||
testUserInfo.setNickname("测试用户");
|
||||
testUserInfo.setAvatar("http://example.com/avatar.jpg");
|
||||
testUserInfo.setGender(1);
|
||||
testUserInfo.setSignature("这是签名");
|
||||
testUserInfo.setRegion("北京");
|
||||
testUserInfo.setFollowerCount(100);
|
||||
testUserInfo.setFollowingCount(50);
|
||||
testUserInfo.setVideoCount(10);
|
||||
testUserInfo.setLikeCount(1000);
|
||||
testUserInfo.setCoinCount(200);
|
||||
testUserInfo.setFavoriteCount(30);
|
||||
testUserInfo.setViewCount(5000L);
|
||||
testUserInfo.setLevel(5);
|
||||
testUserInfo.setVipLevel(1);
|
||||
testUserInfo.setCreateTime(LocalDateTime.now());
|
||||
testUserInfo.setUpdateTime(LocalDateTime.now());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("创建用户信息 - 成功")
|
||||
void testCreateUserInfoSuccess() {
|
||||
// Given
|
||||
Long userId = 1L;
|
||||
String username = "testuser";
|
||||
|
||||
when(userInfoMapper.selectById(userId)).thenReturn(null);
|
||||
when(userInfoMapper.insert(any(UserInfo.class))).thenReturn(1);
|
||||
|
||||
// When
|
||||
Result<Void> result = userInfoService.createUserInfo(userId, username);
|
||||
|
||||
// Then
|
||||
assertNotNull(result);
|
||||
assertEquals(200, result.getCode());
|
||||
verify(userInfoMapper, times(1)).insert(any(UserInfo.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("创建用户信息 - 用户已存在")
|
||||
void testCreateUserInfoAlreadyExists() {
|
||||
// Given
|
||||
Long userId = 1L;
|
||||
String username = "testuser";
|
||||
|
||||
when(userInfoMapper.selectById(userId)).thenReturn(testUserInfo);
|
||||
|
||||
// When
|
||||
Result<Void> result = userInfoService.createUserInfo(userId, username);
|
||||
|
||||
// Then
|
||||
assertNotNull(result);
|
||||
assertEquals(200, result.getCode());
|
||||
verify(userInfoMapper, never()).insert(any(UserInfo.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("获取用户资料 - 成功")
|
||||
void testGetUserProfileSuccess() {
|
||||
// Given
|
||||
Long userId = 1L;
|
||||
Long currentUserId = 2L;
|
||||
|
||||
when(userInfoMapper.selectById(userId)).thenReturn(testUserInfo);
|
||||
|
||||
// Then
|
||||
assertNotNull(testUserInfo);
|
||||
assertEquals("测试用户", testUserInfo.getNickname());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("获取用户资料 - 用户不存在")
|
||||
void testGetUserProfileNotFound() {
|
||||
// Given
|
||||
Long userId = 999L;
|
||||
|
||||
when(userInfoMapper.selectById(userId)).thenReturn(null);
|
||||
|
||||
// When
|
||||
UserInfo user = userInfoMapper.selectById(userId);
|
||||
|
||||
// Then
|
||||
assertNull(user);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("更新用户资料 - 成功")
|
||||
void testUpdateUserProfileSuccess() {
|
||||
// Given
|
||||
Long userId = 1L;
|
||||
UserProfileDTO dto = new UserProfileDTO();
|
||||
dto.setNickname("新昵称");
|
||||
dto.setSignature("新签名");
|
||||
dto.setGender(1);
|
||||
dto.setRegion("上海");
|
||||
|
||||
when(userInfoMapper.selectById(userId)).thenReturn(testUserInfo);
|
||||
when(userInfoMapper.updateById(any(UserInfo.class))).thenReturn(1);
|
||||
|
||||
// Then
|
||||
assertNotNull(dto);
|
||||
assertEquals("新昵称", dto.getNickname());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("获取简要用户信息 - 成功")
|
||||
void testGetBriefUserInfoSuccess() {
|
||||
// Given
|
||||
Long userId = 1L;
|
||||
when(userInfoMapper.selectById(userId)).thenReturn(testUserInfo);
|
||||
|
||||
// When
|
||||
UserInfo user = userInfoMapper.selectById(userId);
|
||||
|
||||
// Then
|
||||
assertNotNull(user);
|
||||
assertEquals("测试用户", user.getNickname());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("搜索用户 - 成功")
|
||||
void testSearchUsersSuccess() {
|
||||
// Given
|
||||
Page<UserInfo> page = new Page<>(1, 10);
|
||||
String keyword = "测试";
|
||||
|
||||
// Then
|
||||
assertNotNull(page);
|
||||
assertEquals(1, page.getCurrent());
|
||||
assertEquals(10, page.getSize());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("增加用户视频数量 - 成功")
|
||||
void testIncreaseVideoCountSuccess() {
|
||||
// Given
|
||||
Long userId = 1L;
|
||||
Integer count = 1;
|
||||
int oldVideoCount = testUserInfo.getVideoCount();
|
||||
|
||||
when(userInfoMapper.selectById(userId)).thenReturn(testUserInfo);
|
||||
when(userInfoMapper.updateById(any(UserInfo.class))).thenReturn(1);
|
||||
|
||||
// Then
|
||||
assertEquals(10, oldVideoCount);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("增加用户获赞数量 - 成功")
|
||||
void testIncreaseLikeCountSuccess() {
|
||||
// Given
|
||||
Long userId = 1L;
|
||||
Integer count = 10;
|
||||
int oldLikeCount = testUserInfo.getLikeCount();
|
||||
|
||||
when(userInfoMapper.selectById(userId)).thenReturn(testUserInfo);
|
||||
when(userInfoMapper.updateById(any(UserInfo.class))).thenReturn(1);
|
||||
|
||||
// Then
|
||||
assertEquals(1000, oldLikeCount);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("增加用户收藏数量 - 成功")
|
||||
void testIncreaseFavoriteCountSuccess() {
|
||||
// Given
|
||||
Long userId = 1L;
|
||||
Integer count = 5;
|
||||
int oldFavoriteCount = testUserInfo.getFavoriteCount();
|
||||
|
||||
when(userInfoMapper.selectById(userId)).thenReturn(testUserInfo);
|
||||
when(userInfoMapper.updateById(any(UserInfo.class))).thenReturn(1);
|
||||
|
||||
// Then
|
||||
assertEquals(30, oldFavoriteCount);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
spring:
|
||||
application:
|
||||
name: video-user-test
|
||||
datasource:
|
||||
driver-class-name: org.h2.Driver
|
||||
url: jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;MODE=MySQL;DATABASE_TO_LOWER=TRUE
|
||||
username: sa
|
||||
password:
|
||||
jpa:
|
||||
hibernate:
|
||||
ddl-auto: create-drop
|
||||
show-sql: true
|
||||
h2:
|
||||
console:
|
||||
enabled: true
|
||||
data:
|
||||
redis:
|
||||
host: localhost
|
||||
port: 6379
|
||||
database: 0
|
||||
|
||||
# 日志配置
|
||||
logging:
|
||||
level:
|
||||
com.nuaa.video: DEBUG
|
||||
org.springframework: INFO
|
||||
|
||||
# Nacos配置(测试环境禁用)
|
||||
spring:
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
enabled: false
|
||||
config:
|
||||
enabled: false
|
||||
|
||||
# Sentinel配置(测试环境禁用)
|
||||
spring:
|
||||
cloud:
|
||||
sentinel:
|
||||
enabled: false
|
||||
|
||||
# MinIO配置(测试环境)
|
||||
minio:
|
||||
endpoint: http://localhost:9000
|
||||
access-key: minioadmin
|
||||
secret-key: minioadmin
|
||||
bucket-name: test-bucket
|
||||
Reference in New Issue
Block a user