Compare commits
No commits in common. 'main' and 'lzt' have entirely different histories.
File diff suppressed because it is too large
Load Diff
@ -1,22 +0,0 @@
|
||||
package com.luojia_channel.common.config;
|
||||
|
||||
import io.swagger.v3.oas.models.info.Info;
|
||||
import io.swagger.v3.oas.models.OpenAPI;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
@Configuration
|
||||
public class OpenApiConfig {
|
||||
|
||||
@Bean
|
||||
public OpenAPI customOpenAPI() {
|
||||
return new OpenAPI()
|
||||
.info(new Info()
|
||||
.title("珞珈岛API文档")
|
||||
.description("珞珈岛社交平台API接口文档")
|
||||
.version("1.0")
|
||||
);
|
||||
}
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
package com.luojia_channel.common.domain.page;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class ScrollPageRequest {
|
||||
private Long lastVal; // 上次查询的最小值(用于游标分页)
|
||||
private Integer offset = 0; // 偏移量(用于分页位置标记)
|
||||
private Long size = 10L; // 每页数量
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
package com.luojia_channel.common.domain.page;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
// 滚动分页请求
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class ScrollPageResponse<T> {
|
||||
private Long lastVal; // 上次查询的最小值(用于游标分页)
|
||||
private Integer offset = 0; // 偏移量(用于分页位置标记)
|
||||
private Long size = 10L; // 每页数量
|
||||
private List<T> records; // 数据列表
|
||||
}
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,3 @@
|
||||
artifactId=common
|
||||
groupId=com.luojia
|
||||
version=1.0.0
|
@ -0,0 +1,11 @@
|
||||
com\luojia\luojia_channel\advice\GlobalExceptionHandler.class
|
||||
com\luojia\luojia_channel\domain\UserDTO.class
|
||||
com\luojia\luojia_channel\utils\UserContext.class
|
||||
com\luojia\luojia_channel\utils\JWTUtil.class
|
||||
com\luojia\luojia_channel\config\RedisConfig.class
|
||||
com\luojia\luojia_channel\utils\RedisUtil$ZSetItem.class
|
||||
com\luojia\luojia_channel\domain\UserDTO$UserDTOBuilder.class
|
||||
com\luojia\luojia_channel\exception\BaseException.class
|
||||
com\luojia\luojia_channel\utils\RedisUtil.class
|
||||
com\luojia\luojia_channel\domain\Result.class
|
||||
com\luojia\luojia_channel\exception\UserException.class
|
@ -1,19 +1,9 @@
|
||||
D:\yys\学习资料?\大二下\软工\大作业\software_teamwork\珞珈岛-项目相关文件\luojia-island\common\src\main\java\com\luojia_channel\common\advice\GlobalExceptionHandler.java
|
||||
D:\yys\学习资料?\大二下\软工\大作业\software_teamwork\珞珈岛-项目相关文件\luojia-island\common\src\main\java\com\luojia_channel\common\config\MybatisConfig.java
|
||||
D:\yys\学习资料?\大二下\软工\大作业\software_teamwork\珞珈岛-项目相关文件\luojia-island\common\src\main\java\com\luojia_channel\common\config\OpenApiConfig.java
|
||||
D:\yys\学习资料?\大二下\软工\大作业\software_teamwork\珞珈岛-项目相关文件\luojia-island\common\src\main\java\com\luojia_channel\common\config\RedisConfig.java
|
||||
D:\yys\学习资料?\大二下\软工\大作业\software_teamwork\珞珈岛-项目相关文件\luojia-island\common\src\main\java\com\luojia_channel\common\config\WebMvcConfig.java
|
||||
D:\yys\学习资料?\大二下\软工\大作业\software_teamwork\珞珈岛-项目相关文件\luojia-island\common\src\main\java\com\luojia_channel\common\constants\RedisConstant.java
|
||||
D:\yys\学习资料?\大二下\软工\大作业\software_teamwork\珞珈岛-项目相关文件\luojia-island\common\src\main\java\com\luojia_channel\common\domain\page\PageRequest.java
|
||||
D:\yys\学习资料?\大二下\软工\大作业\software_teamwork\珞珈岛-项目相关文件\luojia-island\common\src\main\java\com\luojia_channel\common\domain\page\PageResponse.java
|
||||
D:\yys\学习资料?\大二下\软工\大作业\software_teamwork\珞珈岛-项目相关文件\luojia-island\common\src\main\java\com\luojia_channel\common\domain\Result.java
|
||||
D:\yys\学习资料?\大二下\软工\大作业\software_teamwork\珞珈岛-项目相关文件\luojia-island\common\src\main\java\com\luojia_channel\common\domain\UserDTO.java
|
||||
D:\yys\学习资料?\大二下\软工\大作业\software_teamwork\珞珈岛-项目相关文件\luojia-island\common\src\main\java\com\luojia_channel\common\exception\BaseException.java
|
||||
D:\yys\学习资料?\大二下\软工\大作业\software_teamwork\珞珈岛-项目相关文件\luojia-island\common\src\main\java\com\luojia_channel\common\exception\FileException.java
|
||||
D:\yys\学习资料?\大二下\软工\大作业\software_teamwork\珞珈岛-项目相关文件\luojia-island\common\src\main\java\com\luojia_channel\common\exception\PostException.java
|
||||
D:\yys\学习资料?\大二下\软工\大作业\software_teamwork\珞珈岛-项目相关文件\luojia-island\common\src\main\java\com\luojia_channel\common\exception\UserException.java
|
||||
D:\yys\学习资料?\大二下\软工\大作业\software_teamwork\珞珈岛-项目相关文件\luojia-island\common\src\main\java\com\luojia_channel\common\interceptor\AuthInterceptor.java
|
||||
D:\yys\学习资料?\大二下\软工\大作业\software_teamwork\珞珈岛-项目相关文件\luojia-island\common\src\main\java\com\luojia_channel\common\utils\JWTUtil.java
|
||||
D:\yys\学习资料?\大二下\软工\大作业\software_teamwork\珞珈岛-项目相关文件\luojia-island\common\src\main\java\com\luojia_channel\common\utils\PageUtil.java
|
||||
D:\yys\学习资料?\大二下\软工\大作业\software_teamwork\珞珈岛-项目相关文件\luojia-island\common\src\main\java\com\luojia_channel\common\utils\RedisUtil.java
|
||||
D:\yys\学习资料?\大二下\软工\大作业\software_teamwork\珞珈岛-项目相关文件\luojia-island\common\src\main\java\com\luojia_channel\common\utils\UserContext.java
|
||||
D:\javaCode\luojia_channel\common\src\main\java\com\luojia\luojia_channel\advice\GlobalExceptionHandler.java
|
||||
D:\javaCode\luojia_channel\common\src\main\java\com\luojia\luojia_channel\config\RedisConfig.java
|
||||
D:\javaCode\luojia_channel\common\src\main\java\com\luojia\luojia_channel\domain\Result.java
|
||||
D:\javaCode\luojia_channel\common\src\main\java\com\luojia\luojia_channel\domain\UserDTO.java
|
||||
D:\javaCode\luojia_channel\common\src\main\java\com\luojia\luojia_channel\exception\BaseException.java
|
||||
D:\javaCode\luojia_channel\common\src\main\java\com\luojia\luojia_channel\exception\UserException.java
|
||||
D:\javaCode\luojia_channel\common\src\main\java\com\luojia\luojia_channel\utils\JWTUtil.java
|
||||
D:\javaCode\luojia_channel\common\src\main\java\com\luojia\luojia_channel\utils\RedisUtil.java
|
||||
D:\javaCode\luojia_channel\common\src\main\java\com\luojia\luojia_channel\utils\UserContext.java
|
||||
|
@ -1,52 +0,0 @@
|
||||
package com.luojia_channel.modules.interact.controller;
|
||||
|
||||
import com.luojia_channel.common.domain.Result;
|
||||
import com.luojia_channel.common.domain.page.PageResponse;
|
||||
import com.luojia_channel.common.domain.page.ScrollPageResponse;
|
||||
import com.luojia_channel.modules.interact.dto.ChatItemDTO;
|
||||
import com.luojia_channel.modules.interact.dto.ChatPageQueryDTO;
|
||||
import com.luojia_channel.modules.interact.service.ChatService;
|
||||
import com.luojia_channel.modules.message.dto.MessageResponse;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponses;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/message")
|
||||
@RequiredArgsConstructor
|
||||
@Tag(name = "聊天模块", description = "好友聊天模块相关接口")
|
||||
public class ChatController {
|
||||
private final ChatService chatService;
|
||||
|
||||
@Operation(
|
||||
summary = "聊天列表",
|
||||
description = "传入分页参数,查询私信用户列表(带最新消息)",
|
||||
tags = {"聊天模块"}
|
||||
)
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "200", description = "获取成功"),
|
||||
@ApiResponse(responseCode = "500", description = "获取失败,请稍后重试")
|
||||
})
|
||||
@GetMapping("/chat-list")
|
||||
public Result<ScrollPageResponse<ChatItemDTO>> getChatList(@RequestBody ChatPageQueryDTO chatPageQueryDTO) {
|
||||
return Result.success(chatService.getChatList(chatPageQueryDTO));
|
||||
}
|
||||
|
||||
@Operation(
|
||||
summary = "历史记录",
|
||||
description = "传入分页参数,获取与特定用户的完整聊天记录",
|
||||
tags = {"聊天模块"}
|
||||
)
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "200", description = "获取成功"),
|
||||
@ApiResponse(responseCode = "500", description = "获取失败,请稍后重试")
|
||||
})
|
||||
@GetMapping("/history")
|
||||
public Result<ScrollPageResponse<MessageResponse>> getChatHistory(@RequestBody ChatPageQueryDTO chatPageQueryDTO) {
|
||||
return Result.success(chatService.getChatHistory(chatPageQueryDTO));
|
||||
}
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
package com.luojia_channel.modules.interact.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@Schema(description = "聊天列表项DTO")
|
||||
public class ChatItemDTO {
|
||||
@Schema(
|
||||
description = "聊天对象的用户ID",
|
||||
required = true,
|
||||
example = "123456"
|
||||
)
|
||||
private Long chatUserId;
|
||||
|
||||
@Schema(
|
||||
description = "聊天对象的头像URL",
|
||||
example = "https://example.com/avatar.jpg"
|
||||
)
|
||||
private String avatar;
|
||||
|
||||
@Schema(
|
||||
description = "聊天对象的用户名",
|
||||
required = true,
|
||||
example = "张三"
|
||||
)
|
||||
private String username;
|
||||
|
||||
@Schema(
|
||||
description = "最新消息内容",
|
||||
required = true,
|
||||
maxLength = 500,
|
||||
example = "今天下午开会"
|
||||
)
|
||||
private String latestMessage;
|
||||
|
||||
@Schema(
|
||||
description = "最新消息时间",
|
||||
required = true,
|
||||
example = "2023-10-15T14:30:00"
|
||||
)
|
||||
private LocalDateTime latestTime;
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
package com.luojia_channel.modules.interact.dto;
|
||||
|
||||
import com.luojia_channel.common.domain.page.ScrollPageRequest;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ChatPageQueryDTO extends ScrollPageRequest {
|
||||
private Long chatUserId;
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
package com.luojia_channel.modules.interact.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@TableName("follow")
|
||||
@Schema(description = "关注表")
|
||||
public class Follow implements Serializable {
|
||||
|
||||
|
||||
/**
|
||||
* 主键
|
||||
*/
|
||||
@TableId(value = "id", type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 用户id
|
||||
*/
|
||||
@Schema(
|
||||
description = "用户id"
|
||||
)
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 关联的用户id
|
||||
*/
|
||||
@Schema(
|
||||
description = "关联的用户id"
|
||||
)
|
||||
private Long followUserId;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@Schema(
|
||||
description = "创建时间"
|
||||
)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
package com.luojia_channel.modules.interact.mapper;
|
||||
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.luojia_channel.modules.interact.entity.Follow;
|
||||
import org.apache.ibatis.annotations.Delete;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
|
||||
@Mapper
|
||||
public interface FollowMapper extends BaseMapper<Follow> {
|
||||
@Delete("delete from follow where user_id = #{userId} and follow_user_id = #{followUserId}")
|
||||
boolean delete(Long userId, Long followUserId);
|
||||
|
||||
@Select("select count(*) from follow where user_id = #{userId} and follow_user_id = #{followUserId}")
|
||||
Integer queryCount(Long userId, Long followUserId);
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
package com.luojia_channel.modules.interact.service;
|
||||
|
||||
import com.luojia_channel.common.domain.page.PageResponse;
|
||||
import com.luojia_channel.common.domain.page.ScrollPageResponse;
|
||||
import com.luojia_channel.modules.interact.dto.ChatItemDTO;
|
||||
import com.luojia_channel.modules.interact.dto.ChatPageQueryDTO;
|
||||
import com.luojia_channel.modules.message.dto.MessageResponse;
|
||||
import com.luojia_channel.modules.message.entity.MessageDO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface ChatService {
|
||||
|
||||
ScrollPageResponse<ChatItemDTO> getChatList(ChatPageQueryDTO chatPageQueryDTO);
|
||||
|
||||
ScrollPageResponse<MessageResponse> getChatHistory(ChatPageQueryDTO chatPageQueryDTO);
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
package com.luojia_channel.modules.interact.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.luojia_channel.common.domain.UserDTO;
|
||||
import com.luojia_channel.common.domain.page.PageResponse;
|
||||
import com.luojia_channel.common.domain.page.ScrollPageResponse;
|
||||
import com.luojia_channel.modules.interact.entity.Follow;
|
||||
import com.luojia_channel.modules.post.dto.req.PostPageQueryDTO;
|
||||
import com.luojia_channel.modules.post.dto.resp.PostBasicInfoDTO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
public interface FollowService extends IService<Follow> {
|
||||
|
||||
void follow(Long followUserId, Boolean isFollow);
|
||||
|
||||
boolean isFollow(Long followUserId);
|
||||
|
||||
List<UserDTO> followCommons(Long id);
|
||||
|
||||
ScrollPageResponse<PostBasicInfoDTO> queryPostFollow(PostPageQueryDTO postPageQueryDTO);
|
||||
}
|
@ -1,128 +0,0 @@
|
||||
package com.luojia_channel.modules.interact.service.impl;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import com.luojia_channel.common.domain.page.ScrollPageResponse;
|
||||
import com.luojia_channel.common.utils.PageUtil;
|
||||
import com.luojia_channel.common.utils.RedisUtil;
|
||||
import com.luojia_channel.common.utils.UserContext;
|
||||
import com.luojia_channel.modules.interact.dto.ChatItemDTO;
|
||||
import com.luojia_channel.modules.interact.dto.ChatPageQueryDTO;
|
||||
import com.luojia_channel.modules.interact.service.ChatService;
|
||||
import com.luojia_channel.modules.message.dto.MessageResponse;
|
||||
import com.luojia_channel.modules.message.entity.MessageDO;
|
||||
import com.luojia_channel.modules.message.mapper.MessageMapper;
|
||||
import com.luojia_channel.modules.user.entity.User;
|
||||
import com.luojia_channel.modules.user.mapper.UserMapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class ChatServiceImpl implements ChatService {
|
||||
|
||||
private final MessageMapper messageMapper;
|
||||
private final UserMapper userMapper;
|
||||
private final RedisUtil redisUtil;
|
||||
|
||||
@Override
|
||||
public ScrollPageResponse<ChatItemDTO> getChatList(ChatPageQueryDTO chatPageQueryDTO) {
|
||||
/*
|
||||
Long userId = UserContext.getUserId();
|
||||
IPage<ChatItemDTO> chatPage = messageMapper.selectChatList(PageUtil.convert(chatPageQueryDTO), userId);
|
||||
return PageResponse.<ChatItemDTO>builder()
|
||||
.current(chatPage.getCurrent())
|
||||
.size(chatPage.getSize())
|
||||
.total(chatPage.getTotal())
|
||||
.records(chatPage.getRecords())
|
||||
.build();
|
||||
*/
|
||||
Long userId = UserContext.getUserId();
|
||||
String key = "chat:user_list:" + userId;
|
||||
|
||||
return redisUtil.scrollPageQuery(key, ChatItemDTO.class, chatPageQueryDTO,
|
||||
(chatUserIds) -> {
|
||||
List<ChatItemDTO> chatItems = new ArrayList<>();
|
||||
List<Long> latestMessageIds = new ArrayList<>();
|
||||
List<User> users = userMapper.selectByIdsOrderByField(chatUserIds);
|
||||
for(Long chatUserId : chatUserIds){
|
||||
String messageKey = "chat:history:" + Math.min(userId, chatUserId) + ":" +Math.max(userId, chatUserId);
|
||||
// 获取zset中最新的messageId
|
||||
Long latestMessageId = redisUtil.zRevMaxValue(messageKey);
|
||||
latestMessageIds.add(latestMessageId);
|
||||
}
|
||||
List<MessageDO> messageDOS = messageMapper.selectByIdsOrderByField(latestMessageIds);
|
||||
int i=0;
|
||||
for(User user : users){
|
||||
ChatItemDTO chatItemDTO = ChatItemDTO.builder()
|
||||
.chatUserId(user.getId())
|
||||
.avatar(user.getAvatar())
|
||||
.username(user.getUsername())
|
||||
.latestMessage(messageDOS.get(i).getContent())
|
||||
.latestTime(messageDOS.get(i).getCreateTime())
|
||||
.build();
|
||||
chatItems.add(chatItemDTO);
|
||||
i++;
|
||||
}
|
||||
|
||||
return chatItems;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScrollPageResponse<MessageResponse> getChatHistory(ChatPageQueryDTO chatPageQueryDTO) {
|
||||
/*
|
||||
Long userId = UserContext.getUserId();
|
||||
Long chatUserId = chatPageQueryDTO.getChatUserId();
|
||||
LambdaQueryWrapper<MessageDO> queryWrapper = Wrappers.lambdaQuery(MessageDO.class)
|
||||
.eq(MessageDO::getSenderId, userId)
|
||||
.eq(MessageDO::getReceiverId, chatUserId)
|
||||
.or()
|
||||
.eq(MessageDO::getReceiverId, userId)
|
||||
.eq(MessageDO::getSenderId, chatUserId)
|
||||
.orderByDesc(MessageDO::getCreateTime);
|
||||
// 查询的是私信消息
|
||||
queryWrapper.eq(MessageDO::getMessageType, 1);
|
||||
IPage<MessageDO> page = messageMapper.selectPage(PageUtil.convert(chatPageQueryDTO), queryWrapper);
|
||||
User chatUser = userMapper.selectById(chatUserId);
|
||||
return PageUtil.convert(page, (message) -> {
|
||||
MessageResponse messageResponse = BeanUtil.copyProperties(message, MessageResponse.class);
|
||||
if(messageResponse.getSenderId().equals(userId)) {
|
||||
messageResponse.setSenderAvatar(UserContext.getAvatar());
|
||||
messageResponse.setSenderName(UserContext.getUsername());
|
||||
}else{
|
||||
messageResponse.setSenderAvatar(chatUser.getAvatar());
|
||||
messageResponse.setSenderName(chatUser.getUsername());
|
||||
}
|
||||
return messageResponse;
|
||||
});
|
||||
*/
|
||||
|
||||
// 改成滚动分页查询
|
||||
Long userId = UserContext.getUserId();
|
||||
Long chatUserId = chatPageQueryDTO.getChatUserId();
|
||||
String key = "chat:history:" + Math.min(userId, chatUserId) + ":" +Math.max(userId, chatUserId);
|
||||
return redisUtil.scrollPageQuery(key, MessageResponse.class, chatPageQueryDTO,
|
||||
(messageIds) -> {
|
||||
List<MessageDO> messageDOS = messageMapper.selectByIdsOrderByField(messageIds);
|
||||
User chatUser = userMapper.selectById(chatUserId);
|
||||
List<MessageResponse> messageResponses = new ArrayList<>();
|
||||
for(MessageDO message : messageDOS){
|
||||
MessageResponse messageResponse = BeanUtil.copyProperties(message, MessageResponse.class);
|
||||
if(messageResponse.getSenderId().equals(userId)) {
|
||||
messageResponse.setSenderAvatar(UserContext.getAvatar());
|
||||
messageResponse.setSenderName(UserContext.getUsername());
|
||||
}else{
|
||||
messageResponse.setSenderAvatar(chatUser.getAvatar());
|
||||
messageResponse.setSenderName(chatUser.getUsername());
|
||||
}
|
||||
messageResponses.add(messageResponse);
|
||||
}
|
||||
return messageResponses;
|
||||
});
|
||||
}
|
||||
}
|
@ -1,107 +0,0 @@
|
||||
package com.luojia_channel.modules.interact.service.impl;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.luojia_channel.common.domain.UserDTO;
|
||||
import com.luojia_channel.common.domain.page.PageResponse;
|
||||
import com.luojia_channel.common.domain.page.ScrollPageResponse;
|
||||
import com.luojia_channel.common.utils.RedisUtil;
|
||||
import com.luojia_channel.common.utils.UserContext;
|
||||
import com.luojia_channel.modules.interact.entity.Follow;
|
||||
import com.luojia_channel.modules.interact.mapper.FollowMapper;
|
||||
import com.luojia_channel.modules.interact.service.FollowService;
|
||||
import com.luojia_channel.modules.post.dto.req.PostPageQueryDTO;
|
||||
import com.luojia_channel.modules.post.dto.resp.PostBasicInfoDTO;
|
||||
import com.luojia_channel.modules.post.entity.Post;
|
||||
import com.luojia_channel.modules.post.mapper.PostMapper;
|
||||
import com.luojia_channel.modules.user.entity.User;
|
||||
import com.luojia_channel.modules.user.mapper.UserMapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class FollowServiceImpl extends ServiceImpl<FollowMapper, Follow> implements FollowService {
|
||||
private final FollowMapper followMapper;
|
||||
private final UserMapper userMapper;
|
||||
private final RedisTemplate<String, Object> redisTemplate;
|
||||
private final RedisUtil redisUtil;
|
||||
private final PostMapper postMapper;
|
||||
|
||||
@Override
|
||||
public void follow(Long followUserId, Boolean isFollow) {
|
||||
Long userId = UserContext.getUserId();
|
||||
String key = "follows:" + userId;
|
||||
if(isFollow){
|
||||
Follow follow = new Follow();
|
||||
follow.setUserId(userId);
|
||||
follow.setFollowUserId(followUserId);
|
||||
follow.setCreateTime(LocalDateTime.now());
|
||||
boolean isSuccess = save(follow);
|
||||
if(isSuccess){
|
||||
redisTemplate.opsForSet().add(key, followUserId);
|
||||
}
|
||||
}else{
|
||||
boolean isSuccess = followMapper.delete(userId, followUserId);
|
||||
if(isSuccess){
|
||||
redisTemplate.opsForSet().remove(key, followUserId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFollow(Long followUserId) {
|
||||
Long userId = UserContext.getUserId();
|
||||
Integer count = followMapper.queryCount(userId, followUserId);
|
||||
return count > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserDTO> followCommons(Long id) {
|
||||
Long userId = UserContext.getUserId();
|
||||
String userKey = "follows:" + userId;
|
||||
String followKey = "follows:" + id;
|
||||
Set<Object> intersect = redisTemplate.opsForSet().intersect(userKey, followKey);
|
||||
if(intersect == null || intersect.isEmpty()){
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<Long> ids = intersect.stream().map(obj -> (Long)obj).collect(Collectors.toList());
|
||||
List<UserDTO> userDTOS = userMapper.selectBatchIds(ids).stream()
|
||||
.map(user -> BeanUtil.copyProperties(user, UserDTO.class))
|
||||
.toList();
|
||||
|
||||
return userDTOS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScrollPageResponse<PostBasicInfoDTO> queryPostFollow(PostPageQueryDTO postPageQueryDTO) {
|
||||
Long userId = UserContext.getUserId();
|
||||
String key = "post:follow_of:" + userId;
|
||||
return redisUtil.scrollPageQuery(key, PostBasicInfoDTO.class, postPageQueryDTO,
|
||||
(postIds) -> {
|
||||
List<Post> posts = postMapper.selectBatchIds(postIds);
|
||||
List<Long> userIds = posts.stream().map(Post::getUserId).toList();
|
||||
List<User> users = userMapper.selectBatchIds(userIds);
|
||||
Map<Long, User> userMap = users.stream()
|
||||
.collect(Collectors.toMap(User::getId, user -> user));
|
||||
List<PostBasicInfoDTO> postBasicInfoDTOS = new ArrayList<>();
|
||||
for(Post post : posts){
|
||||
User user = userMap.getOrDefault(post.getUserId(), new User());
|
||||
PostBasicInfoDTO postBasicInfoDTO = BeanUtil.copyProperties(post, PostBasicInfoDTO.class);
|
||||
postBasicInfoDTO.setUserName(user.getUsername());
|
||||
postBasicInfoDTO.setUserAvatar(user.getAvatar());
|
||||
postBasicInfoDTOS.add(postBasicInfoDTO);
|
||||
}
|
||||
// 按照发布时间倒序排序
|
||||
postBasicInfoDTOS.sort(Comparator.comparing(PostBasicInfoDTO::getCreateTime).reversed());
|
||||
return postBasicInfoDTOS;
|
||||
});
|
||||
}
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
package com.luojia_channel.modules.message.config;
|
||||
|
||||
import jakarta.servlet.ServletContext;
|
||||
import org.springframework.boot.web.servlet.ServletContextInitializer;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.socket.config.annotation.EnableWebSocket;
|
||||
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
|
||||
import org.springframework.web.util.WebAppRootListener;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSocket
|
||||
public class WebSocketConfig implements ServletContextInitializer {
|
||||
|
||||
@Bean
|
||||
public ServerEndpointExporter serverEndpointExporter() {
|
||||
return new ServerEndpointExporter();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartup(ServletContext servletContext) {
|
||||
servletContext.addListener(WebAppRootListener.class);
|
||||
servletContext.setInitParameter("org.apache.tomcat.websocket.textBufferSize", (1024 * 200) + "");
|
||||
servletContext.setInitParameter("org.apache.tomcat.websocket.binaryBufferSize", (1024 * 200) + "");
|
||||
}
|
||||
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
package com.luojia_channel.modules.message.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.Data;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 消息实体类
|
||||
*/
|
||||
@Data
|
||||
@TableName("message")
|
||||
public class MessageDO {
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
private Integer messageType; // 0-私聊, 1-系统消息
|
||||
private String content;
|
||||
private Long senderId;
|
||||
private Long receiverId;
|
||||
private LocalDateTime createTime;
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
package com.luojia_channel.modules.message.mq.config;
|
||||
|
||||
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
|
||||
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
|
||||
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
|
||||
import org.springframework.amqp.support.converter.MessageConverter;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
public class RabbitMQConfig {
|
||||
|
||||
@Bean
|
||||
public MessageConverter jsonMessageConverter() {
|
||||
return new Jackson2JsonMessageConverter();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory(ConnectionFactory connectionFactory, MessageConverter jsonMessageConverter) {
|
||||
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
|
||||
factory.setConnectionFactory(connectionFactory);
|
||||
factory.setMessageConverter(jsonMessageConverter);
|
||||
return factory;
|
||||
}
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
package com.luojia_channel.modules.message.mq.consumer;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.luojia_channel.modules.message.dto.MessageRequest;
|
||||
import com.luojia_channel.modules.message.mq.MessageWrapper;
|
||||
import com.luojia_channel.modules.message.mq.domain.NotificationMessage;
|
||||
import com.luojia_channel.modules.message.server.WebSocketServer;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.amqp.rabbit.annotation.Exchange;
|
||||
import org.springframework.amqp.rabbit.annotation.Queue;
|
||||
import org.springframework.amqp.rabbit.annotation.QueueBinding;
|
||||
import org.springframework.amqp.rabbit.annotation.RabbitListener;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class NotificationListener {
|
||||
public static final String EXCHANGE_NAME = "notify.exchange";
|
||||
public static final String QUEUE_NAME = "notify.queue";
|
||||
public static final String ROUTING_KEY = "notify.routing.key";
|
||||
|
||||
private final WebSocketServer webSocketServer;
|
||||
@RabbitListener(bindings = @QueueBinding(
|
||||
value = @Queue(name = QUEUE_NAME),
|
||||
exchange = @Exchange(name = EXCHANGE_NAME, type = "direct"),
|
||||
key = ROUTING_KEY
|
||||
))
|
||||
public void handleNotification(MessageWrapper<NotificationMessage> wrapper) {
|
||||
try {
|
||||
NotificationMessage message = wrapper.getMessage();
|
||||
MessageRequest request = BeanUtil.copyProperties(message, MessageRequest.class);
|
||||
Integer messageType = message.getMessageType();
|
||||
if (messageType != null && !messageType.equals(0)) {
|
||||
webSocketServer.sendPrivateMessage(message.getSenderId(), request);
|
||||
} else {
|
||||
webSocketServer.sendSystemNotification(request);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// 记录异常日志
|
||||
log.error("处理消息时发生异常: {}", e.getMessage(), e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
package com.luojia_channel.modules.message.mq.domain;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Builder
|
||||
public class NotificationMessage {
|
||||
private Long senderId;
|
||||
private Long receiverId;
|
||||
private String content;
|
||||
private String senderName; // 用户名
|
||||
private String senderAvatar; // 用户头像
|
||||
private Integer messageType; // 0-私信 1-系统
|
||||
}
|
@ -1,148 +0,0 @@
|
||||
package com.luojia_channel.modules.message.server;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.luojia_channel.common.utils.RedisUtil;
|
||||
import com.luojia_channel.modules.message.dto.MessageRequest;
|
||||
import com.luojia_channel.modules.message.dto.MessageResponse;
|
||||
import com.luojia_channel.modules.message.entity.MessageDO;
|
||||
import com.luojia_channel.modules.message.mapper.MessageMapper;
|
||||
import com.luojia_channel.modules.message.util.WebSocketContext;
|
||||
import jakarta.websocket.*;
|
||||
import jakarta.websocket.server.PathParam;
|
||||
import jakarta.websocket.server.ServerEndpoint;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@ServerEndpoint(value = "/connect/{userId}")
|
||||
@Slf4j
|
||||
@Component
|
||||
public class WebSocketServer {
|
||||
// 存储在线用户会话 <userId, Session>
|
||||
private final static Map<String, Session> CLIENTS = new ConcurrentHashMap<>();
|
||||
|
||||
@OnOpen
|
||||
public void onOpen(@PathParam("userId") String userId,
|
||||
Session session) {
|
||||
// 将新连接加入客户端列表
|
||||
CLIENTS.put(userId, session);
|
||||
log.info("用户 [{}] 已连接,当前在线人数:{}", userId, CLIENTS.size());
|
||||
}
|
||||
|
||||
@OnClose
|
||||
public void onClose(@PathParam("userId") String userId,
|
||||
Session session) {
|
||||
// 移除断开连接的用户
|
||||
CLIENTS.remove(userId);
|
||||
log.info("用户 [{}] 已断开,当前在线人数:{}", userId, CLIENTS.size());
|
||||
}
|
||||
|
||||
@OnError
|
||||
public void onError(@PathParam("userId") String userId,
|
||||
Session session, Throwable e) {
|
||||
log.error("用户 [{}] 发生错误: {}", userId, e.getMessage(), e);
|
||||
try {
|
||||
session.close();
|
||||
} catch (IOException ex) {
|
||||
log.error("关闭会话失败: {}", ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@OnMessage
|
||||
public void onMessage(@PathParam("userId") String senderId,
|
||||
String message, Session session) {
|
||||
try {
|
||||
// 解析客户端发送的 JSON 消息
|
||||
MessageRequest request = JSON.parseObject(message, MessageRequest.class);
|
||||
switch (request.getMessageType()) {
|
||||
case 0 -> sendSystemNotification(request);
|
||||
case 1 -> sendPrivateMessage(Long.parseLong(senderId), request);
|
||||
default -> log.warn("未知消息类型: {}", request.getMessageType());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("消息处理失败: {}", e.getMessage());
|
||||
sendErrorResponse(session, "消息处理失败,请稍后重试");
|
||||
}
|
||||
}
|
||||
|
||||
// 发送一对一私信
|
||||
public void sendPrivateMessage(Long senderId, MessageRequest request) {
|
||||
Long receiverId = request.getReceiverId();
|
||||
Session receiverSession = CLIENTS.get(receiverId.toString());
|
||||
// 构建私信响应
|
||||
MessageResponse response = MessageResponse.builder()
|
||||
.messageType(request.getMessageType())
|
||||
.content(request.getContent())
|
||||
.senderId(senderId)
|
||||
.senderName(request.getSenderName())
|
||||
.senderAvatar(request.getSenderAvatar())
|
||||
.receiverId(receiverId)
|
||||
.createTime(LocalDateTime.now())
|
||||
.build();
|
||||
|
||||
// 发送给接收方
|
||||
if (receiverSession != null && receiverSession.isOpen()) {
|
||||
sendMessage(receiverSession, JSON.toJSONString(response));
|
||||
} else {
|
||||
log.info("接收方 [{}] 不在线,消息无法即时送达", receiverId);
|
||||
}
|
||||
MessageDO message = BeanUtil.copyProperties(response, MessageDO.class);
|
||||
MessageMapper messageMapper = WebSocketContext.getBean(MessageMapper.class);
|
||||
RedisUtil redisUtil = WebSocketContext.getBean(RedisUtil.class);
|
||||
messageMapper.insert(message);
|
||||
sendMessage(CLIENTS.get(senderId.toString()), JSON.toJSONString(response));
|
||||
// 存储消息至redis
|
||||
if(request.getMessageType().equals(1)){
|
||||
String key = "chat:history:" + Math.min(senderId, receiverId) + ":" +Math.max(senderId, receiverId);
|
||||
redisUtil.zAdd(key, message.getId(), System.currentTimeMillis());
|
||||
String chatListKey = "chat:user_list:" + senderId;
|
||||
redisUtil.zAdd(chatListKey, receiverId, System.currentTimeMillis());
|
||||
chatListKey = "chat:user_list:" + receiverId;
|
||||
redisUtil.zAdd(chatListKey, senderId, System.currentTimeMillis());
|
||||
}
|
||||
}
|
||||
|
||||
// 发送系统通知
|
||||
public void sendSystemNotification(MessageRequest request) {
|
||||
MessageResponse response = MessageResponse.builder()
|
||||
.senderId(0L)
|
||||
.receiverId(0L)
|
||||
.messageType(request.getMessageType())
|
||||
.content(request.getContent())
|
||||
.createTime(LocalDateTime.now())
|
||||
.build();
|
||||
// 广播给所有在线用户
|
||||
for (Session session : CLIENTS.values()) {
|
||||
sendMessage(session, JSON.toJSONString(response));
|
||||
}
|
||||
MessageDO message = BeanUtil.copyProperties(response, MessageDO.class);
|
||||
MessageMapper messageMapper = WebSocketContext.getBean(MessageMapper.class);
|
||||
messageMapper.insert(message);
|
||||
}
|
||||
|
||||
// 安全消息
|
||||
private void sendMessage(Session session, String message) {
|
||||
try {
|
||||
if (session != null && session.isOpen()) {
|
||||
session.getBasicRemote().sendText(message);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.error("发送消息失败: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// 发送错误响应
|
||||
private void sendErrorResponse(Session session, String errorMessage) {
|
||||
MessageResponse errorResponse = MessageResponse.builder()
|
||||
.messageType(-1) // 错误消息类型
|
||||
.content(errorMessage)
|
||||
.createTime(LocalDateTime.now())
|
||||
.build();
|
||||
sendMessage(session, JSON.toJSONString(errorResponse));
|
||||
}
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
package com.luojia_channel.modules.message.util;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class WebSocketContext implements ApplicationContextAware {
|
||||
private static ApplicationContext applicationContext;
|
||||
|
||||
@Override
|
||||
@Autowired
|
||||
public void setApplicationContext(ApplicationContext inApplicationContext) throws BeansException {
|
||||
applicationContext = inApplicationContext;
|
||||
}
|
||||
|
||||
public static ApplicationContext getApplicationContext() {
|
||||
return applicationContext;
|
||||
}
|
||||
|
||||
public static Object getBean(String name) {
|
||||
return getApplicationContext().getBean(name);
|
||||
}
|
||||
|
||||
public static <T> T getBean(Class<T> clazz) {
|
||||
return getApplicationContext().getBean(clazz);
|
||||
}
|
||||
|
||||
public static <T> T getBean(String name, Class<T> clazz) {
|
||||
return getApplicationContext().getBean(name, clazz);
|
||||
}
|
||||
|
||||
|
||||
public static String getActiveProfile() {
|
||||
String[] activeProfiles = getApplicationContext().getEnvironment().getActiveProfiles();
|
||||
if (activeProfiles.length == 0) {
|
||||
return null;
|
||||
}
|
||||
return activeProfiles[0];
|
||||
}
|
||||
}
|
@ -1,20 +1,9 @@
|
||||
package com.luojia_channel.modules.post.dto.req;
|
||||
|
||||
import com.luojia_channel.common.domain.page.PageRequest;
|
||||
import com.luojia_channel.common.domain.page.ScrollPageRequest;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@Schema(title = "分页查询评论请求DTO")
|
||||
public class CommentPageQueryDTO extends ScrollPageRequest {
|
||||
@Schema(title = "帖子ID")
|
||||
private Long postId;
|
||||
|
||||
@Schema(title = "评论ID")
|
||||
private Long parentCommentId;
|
||||
|
||||
private Boolean orderByTime = true;
|
||||
|
||||
private Boolean orderByHot = false;
|
||||
public class CommentPageQueryDTO extends PageRequest {
|
||||
private Integer postId;
|
||||
}
|
||||
|
@ -1,27 +0,0 @@
|
||||
package com.luojia_channel.modules.post.mq.producer;
|
||||
|
||||
import com.luojia_channel.modules.message.mq.AbstractSendProduceTemplate;
|
||||
import com.luojia_channel.modules.message.mq.BaseSendExtendDTO;
|
||||
import com.luojia_channel.modules.message.mq.domain.NotificationMessage;
|
||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class NotificationProducer extends AbstractSendProduceTemplate<NotificationMessage> {
|
||||
|
||||
public NotificationProducer(@Autowired RabbitTemplate rabbitTemplate){
|
||||
super(rabbitTemplate);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BaseSendExtendDTO buildBaseSendParam(NotificationMessage messageSendEvent) {
|
||||
return BaseSendExtendDTO.builder()
|
||||
.eventName("NotificationMessageEvent")
|
||||
.exchange("notify.exchange")
|
||||
.routingKey("notify.routing.key")
|
||||
.keys(messageSendEvent.getSenderId().toString())
|
||||
.delay(null)
|
||||
.build();
|
||||
}
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
package com.luojia_channel.modules.search.controller;
|
||||
|
||||
public class SearchController {
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
package com.luojia_channel.modules.search.entity;
|
||||
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.elasticsearch.annotations.Document;
|
||||
import org.springframework.data.elasticsearch.annotations.Field;
|
||||
import org.springframework.data.elasticsearch.annotations.FieldType;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
|
||||
@Data
|
||||
@Document(indexName = "post_index")
|
||||
public class EsPost {
|
||||
@Id
|
||||
private Long id;
|
||||
|
||||
@Field(type = FieldType.Text, analyzer = "ik_max_word")
|
||||
private String title;
|
||||
|
||||
private String image;
|
||||
|
||||
@Field(type = FieldType.Text, analyzer = "ik_max_word")
|
||||
private String content;
|
||||
|
||||
private Integer status;
|
||||
private Integer likeCount;
|
||||
private Integer commentCount;
|
||||
private Integer favoriteCount;
|
||||
private Integer viewCount;
|
||||
private Long userId;
|
||||
private Long categoryId;
|
||||
|
||||
private LocalDateTime createTime;
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
package com.luojia_channel.modules.search.entity;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.elasticsearch.annotations.Document;
|
||||
import org.springframework.data.elasticsearch.annotations.Field;
|
||||
import org.springframework.data.elasticsearch.annotations.FieldType;
|
||||
|
||||
|
||||
@Data
|
||||
@Document(indexName = "user_index")
|
||||
public class EsUser {
|
||||
@Id
|
||||
private Long id;
|
||||
|
||||
@Field(type = FieldType.Text, analyzer = "ik_max_word")
|
||||
private String username;
|
||||
|
||||
@Field(type = FieldType.Keyword)
|
||||
private String studentId;
|
||||
|
||||
private String avatar;
|
||||
private Integer gender;
|
||||
|
||||
|
||||
private Integer integral;
|
||||
|
||||
@Field(type = FieldType.Keyword)
|
||||
private String college;
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
package com.luojia_channel.modules.search.service;
|
||||
|
||||
public class SearchService {
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
package com.luojia_channel.modules.search.task;
|
||||
|
||||
public class DataSyncTask {
|
||||
}
|
@ -1,22 +1,11 @@
|
||||
package com.luojia_channel.modules.user.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@Schema(description = "用户登录DTO")
|
||||
public class UserLoginDTO {
|
||||
// 用户标志,支持学号,手机号,邮箱
|
||||
@Schema(
|
||||
description = "用户登录用标志,支持用户名,手机号,邮箱"
|
||||
)
|
||||
private String userFlag;
|
||||
|
||||
@Schema(
|
||||
description = "用户登陆用密码",
|
||||
required = true,
|
||||
minLength = 6,
|
||||
maxLength = 16
|
||||
)
|
||||
private String password;
|
||||
}
|
||||
|
@ -1,44 +1,16 @@
|
||||
package com.luojia_channel.modules.user.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@Schema(description = "用户注册DTO")
|
||||
public class UserRegisterDTO {
|
||||
@Schema(
|
||||
description = "用户名",
|
||||
example = "ttt",
|
||||
required = true,
|
||||
minLength = 1,
|
||||
maxLength = 15
|
||||
)
|
||||
private String username;
|
||||
|
||||
@Schema(
|
||||
description = "密码",
|
||||
example = "123456",
|
||||
required = true,
|
||||
minLength = 6,
|
||||
maxLength = 16,
|
||||
format = "password"
|
||||
)
|
||||
private String password;
|
||||
|
||||
@Schema(
|
||||
description = "手机号",
|
||||
example = "12345678901",
|
||||
pattern = "^1([38][0-9]|4[579]|5[0-3,5-9]|6[6]|7[0135678]|9[89])\\d{8}$"
|
||||
)
|
||||
private String phone;
|
||||
|
||||
@Schema(
|
||||
description = "邮箱",
|
||||
example = "123456@qq.com",
|
||||
pattern = "^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)+$"
|
||||
)
|
||||
private String email;
|
||||
|
||||
@Schema(description = "学生学号,暂不需要")
|
||||
private String studentId;
|
||||
}
|
||||
|
@ -1,135 +0,0 @@
|
||||
package com.luojia_channel.modules.user.utils;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Random;
|
||||
|
||||
public class CaptchaUtils {
|
||||
/**
|
||||
* 生成验证码图片的宽度
|
||||
*/
|
||||
private int width = 180;
|
||||
|
||||
/**
|
||||
* 生成验证码图片的高度
|
||||
*/
|
||||
private int height = 30;
|
||||
|
||||
/**
|
||||
* 字符样式
|
||||
*/
|
||||
private String[] fontNames = { "宋体", "楷体", "隶书", "微软雅黑" };
|
||||
|
||||
/**
|
||||
* 定义验证码图片的背景颜色为白色
|
||||
*/
|
||||
private Color bgColor = new Color(255, 255, 255);
|
||||
|
||||
/**
|
||||
* 生成随机
|
||||
*/
|
||||
private Random random = new Random();
|
||||
|
||||
/**
|
||||
* 定义code字符
|
||||
*/
|
||||
private String codes = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
|
||||
/**
|
||||
* 记录随机字符串
|
||||
*/
|
||||
private String text;
|
||||
|
||||
/**
|
||||
* 获取一个随意颜色
|
||||
* @return
|
||||
*/
|
||||
private Color randomColor() {
|
||||
int red = random.nextInt(150);
|
||||
int green = random.nextInt(150);
|
||||
int blue = random.nextInt(150);
|
||||
return new Color(red, green, blue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取一个随机字体
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private Font randomFont() {
|
||||
String name = fontNames[random.nextInt(fontNames.length)];
|
||||
int style = random.nextInt(4);
|
||||
int size = random.nextInt(5) + 24;
|
||||
return new Font(name, style, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取一个随机字符
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private char randomChar() {
|
||||
return codes.charAt(random.nextInt(codes.length()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个空白的BufferedImage对象
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private BufferedImage createImage() {
|
||||
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
|
||||
Graphics2D g2 = (Graphics2D) image.getGraphics();
|
||||
//设置验证码图片的背景颜色
|
||||
g2.setColor(bgColor);
|
||||
g2.fillRect(0, 0, width, height);
|
||||
return image;
|
||||
}
|
||||
|
||||
public BufferedImage getImage() {
|
||||
BufferedImage image = createImage();
|
||||
Graphics2D g2 = (Graphics2D) image.getGraphics();
|
||||
StringBuffer sb = new StringBuffer();
|
||||
for (int i = 0; i < 4; i++) {
|
||||
String s = randomChar() + "";
|
||||
sb.append(s);
|
||||
g2.setColor(randomColor());
|
||||
g2.setFont(randomFont());
|
||||
float x = i * width * 1.0f / 4;
|
||||
g2.drawString(s, x, height - 8);
|
||||
}
|
||||
this.text = sb.toString();
|
||||
drawLine(image);
|
||||
return image;
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制干扰线
|
||||
*
|
||||
* @param image
|
||||
*/
|
||||
private void drawLine(BufferedImage image) {
|
||||
Graphics2D g2 = (Graphics2D) image.getGraphics();
|
||||
int num = 5;
|
||||
for (int i = 0; i < num; i++) {
|
||||
int x1 = random.nextInt(width);
|
||||
int y1 = random.nextInt(height);
|
||||
int x2 = random.nextInt(width);
|
||||
int y2 = random.nextInt(height);
|
||||
g2.setColor(randomColor());
|
||||
g2.setStroke(new BasicStroke(1.5f));
|
||||
g2.drawLine(x1, y1, x2, y2);
|
||||
}
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
public static void output(BufferedImage image, OutputStream out) throws IOException {
|
||||
ImageIO.write(image, "PNG", out);
|
||||
}
|
||||
}
|
@ -1,36 +1,36 @@
|
||||
# 本地开发环境
|
||||
# lj:
|
||||
# db:
|
||||
# host: localhost
|
||||
# password: 123456
|
||||
# redis:
|
||||
# host: localhost
|
||||
# port: 6379
|
||||
# password: 123456
|
||||
# rabbitmq:
|
||||
# host: localhost
|
||||
# port: 15672
|
||||
# username: root
|
||||
# password: 123456
|
||||
# minio:
|
||||
# endpoint: http://localhost:9000
|
||||
# accessKey: minioadmin
|
||||
# secretKey: minioadmin
|
||||
|
||||
lj:
|
||||
db:
|
||||
host: localhost
|
||||
password: 1243969857
|
||||
host: 192.168.59.129
|
||||
password: Forely123!
|
||||
redis:
|
||||
host: localhost
|
||||
host: 192.168.59.129
|
||||
port: 6379
|
||||
password: 1243969857
|
||||
password: Forely123!
|
||||
rabbitmq:
|
||||
host: localhost
|
||||
host: 192.168.59.129
|
||||
port: 5672
|
||||
username: guest
|
||||
password: guest
|
||||
username: admin
|
||||
password: Forely123!
|
||||
minio:
|
||||
endpoint: http://localhost:9000
|
||||
accessKey: minioadmin
|
||||
secretKey: minioadmin
|
||||
|
||||
#lj:
|
||||
# db:
|
||||
# host: 192.168.59.129
|
||||
# password: Forely123!
|
||||
# redis:
|
||||
# host: 192.168.59.129
|
||||
# port: 6379
|
||||
# password: Forely123!
|
||||
# rabbitmq:
|
||||
# host: 192.168.59.129
|
||||
# port: 5672
|
||||
# username: admin
|
||||
# password: Forely123!
|
||||
# minio:
|
||||
# endpoint: http://192.168.59.129:9000
|
||||
# accessKey: forely
|
||||
# secretKey: Forely123!
|
||||
endpoint: http://192.168.59.129:9000
|
||||
accessKey: forely
|
||||
secretKey: Forely123!
|
||||
|
@ -1,12 +0,0 @@
|
||||
<?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.luojia_channel.modules.post.mapper.CommentMapper">
|
||||
<select id="selectByIdsOrderByField" resultType="com.luojia_channel.modules.post.entity.Comment">
|
||||
SELECT * FROM comment
|
||||
WHERE id IN
|
||||
<foreach item="id" collection="ids" open="(" separator="," close=")">#{id}</foreach>
|
||||
ORDER BY FIELD(id,
|
||||
<foreach item="id" collection="ids" separator="," open="" close="">#{id}</foreach>)
|
||||
</select>
|
||||
</mapper>
|
@ -1,12 +0,0 @@
|
||||
<?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.luojia_channel.modules.post.mapper.PostMapper">
|
||||
<select id="selectByIdsOrderByField" resultType="com.luojia_channel.modules.post.entity.Post">
|
||||
SELECT * FROM post
|
||||
WHERE id IN
|
||||
<foreach item="id" collection="ids" open="(" separator="," close=")">#{id}</foreach>
|
||||
ORDER BY FIELD(id,
|
||||
<foreach item="id" collection="ids" separator="," open="" close="">#{id}</foreach>)
|
||||
</select>
|
||||
</mapper>
|
@ -1,36 +1,36 @@
|
||||
# 本地开发环境
|
||||
# lj:
|
||||
# db:
|
||||
# host: localhost
|
||||
# password: 123456
|
||||
# redis:
|
||||
# host: localhost
|
||||
# port: 6379
|
||||
# password: 123456
|
||||
# rabbitmq:
|
||||
# host: localhost
|
||||
# port: 15672
|
||||
# username: root
|
||||
# password: 123456
|
||||
# minio:
|
||||
# endpoint: http://localhost:9000
|
||||
# accessKey: minioadmin
|
||||
# secretKey: minioadmin
|
||||
|
||||
lj:
|
||||
db:
|
||||
host: localhost
|
||||
password: 1243969857
|
||||
host: 192.168.59.129
|
||||
password: Forely123!
|
||||
redis:
|
||||
host: localhost
|
||||
host: 192.168.59.129
|
||||
port: 6379
|
||||
password: 1243969857
|
||||
password: Forely123!
|
||||
rabbitmq:
|
||||
host: localhost
|
||||
host: 192.168.59.129
|
||||
port: 5672
|
||||
username: guest
|
||||
password: guest
|
||||
username: admin
|
||||
password: Forely123!
|
||||
minio:
|
||||
endpoint: http://localhost:9000
|
||||
accessKey: minioadmin
|
||||
secretKey: minioadmin
|
||||
|
||||
#lj:
|
||||
# db:
|
||||
# host: 192.168.59.129
|
||||
# password: Forely123!
|
||||
# redis:
|
||||
# host: 192.168.59.129
|
||||
# port: 6379
|
||||
# password: Forely123!
|
||||
# rabbitmq:
|
||||
# host: 192.168.59.129
|
||||
# port: 5672
|
||||
# username: admin
|
||||
# password: Forely123!
|
||||
# minio:
|
||||
# endpoint: http://192.168.59.129:9000
|
||||
# accessKey: forely
|
||||
# secretKey: Forely123!
|
||||
endpoint: http://192.168.59.129:9000
|
||||
accessKey: forely
|
||||
secretKey: Forely123!
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue