feat: 私信和关注、粉丝功能
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
package com.ivmiku.tutorial.config;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.DbType;
|
||||
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
@MapperScan("com.ivmiku.tutorial.mapper")
|
||||
public class MybatisPlusConfig {
|
||||
|
||||
/**
|
||||
* 添加分页插件
|
||||
*/
|
||||
@Bean
|
||||
public MybatisPlusInterceptor mybatisPlusInterceptor() {
|
||||
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
|
||||
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
|
||||
//interceptor.addInnerInterceptor(new PaginationInnerInterceptor()); 如果有多数据源可以不配具体类型 否则都建议配上具体的DbType
|
||||
return interceptor;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.ivmiku.tutorial.config;
|
||||
|
||||
import org.springframework.amqp.core.Binding;
|
||||
import org.springframework.amqp.core.BindingBuilder;
|
||||
import org.springframework.amqp.core.FanoutExchange;
|
||||
import org.springframework.amqp.core.Queue;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
public class RabbitMqConfig {
|
||||
@Bean
|
||||
public Queue queue1() {
|
||||
return new Queue("queue1",true);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public FanoutExchange exchange1() {
|
||||
return new FanoutExchange("exchange1",true, false);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Binding binding1() {
|
||||
return BindingBuilder.bind(queue1()).to(exchange1());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.ivmiku.tutorial.config;
|
||||
|
||||
import cn.dev33.satoken.interceptor.SaInterceptor;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
@Configuration
|
||||
public class SaTokenConfigure implements WebMvcConfigurer {
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
// 注册 Sa-Token 拦截器,打开注解式鉴权功能
|
||||
registry.addInterceptor(new SaInterceptor()).addPathPatterns("/**");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.ivmiku.tutorial.config;
|
||||
|
||||
import jakarta.servlet.ServletContext;
|
||||
import jakarta.servlet.ServletException;
|
||||
import org.springframework.boot.web.servlet.ServletContextInitializer;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
|
||||
import org.springframework.web.socket.server.standard.ServletServerContainerFactoryBean;
|
||||
import org.springframework.web.util.WebAppRootListener;
|
||||
|
||||
@Configuration
|
||||
public class WebSocketConfig implements ServletContextInitializer {
|
||||
@Bean
|
||||
public ServerEndpointExporter serverEndpointExporter (){
|
||||
return new ServerEndpointExporter();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartup(ServletContext servletContext) throws ServletException {
|
||||
servletContext.addListener(WebAppRootListener.class);
|
||||
servletContext.setInitParameter("org.apache.tomcat.websocket.textBufferSize","102400000");
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ServletServerContainerFactoryBean createWebSocketContainer() {
|
||||
ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
|
||||
// 在此处设置bufferSize
|
||||
container.setMaxTextMessageBufferSize(50*1024*1024);
|
||||
container.setMaxBinaryMessageBufferSize(50*1024*1024);
|
||||
container.setMaxSessionIdleTimeout(15 * 60000L);
|
||||
return container;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.ivmiku.tutorial.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckLogin;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.ivmiku.tutorial.entity.HistoryQuery;
|
||||
import com.ivmiku.tutorial.response.Result;
|
||||
import com.ivmiku.tutorial.service.MessageService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.text.ParseException;
|
||||
|
||||
@SaCheckLogin
|
||||
@RestController
|
||||
@RequestMapping("/api/message")
|
||||
public class MessageController {
|
||||
@Autowired
|
||||
private MessageService messageService;
|
||||
|
||||
@PostMapping("/history")
|
||||
public Object getChatHistory(@RequestBody HistoryQuery input) throws ParseException {
|
||||
Result result = Result.ok();
|
||||
if (input.getStartDate() != null && input.getEndDate() != null) {
|
||||
result.setData(messageService.getChatHistoryByDate(input.getUser1Id(), input.getUser2Id(), input.getStartDate(), input.getEndDate(), input.getPage(), input.getSize()));
|
||||
} else {
|
||||
if (input.getPage() <= 0 || input.getSize() <=0) {
|
||||
return Result.error("请输入合法分页参数");
|
||||
} else {
|
||||
result.setData(messageService.getChatHistory(input.getUser1Id(), input.getUser2Id(), input.getPage(), input.getSize()));
|
||||
}
|
||||
}
|
||||
return JSON.toJSON(result);
|
||||
}
|
||||
|
||||
@GetMapping("/list")
|
||||
public Object getChatList(@RequestParam String user_id, @RequestParam int page, @RequestParam int size) {
|
||||
return Result.ok(messageService.getChatList(user_id, page, size));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.ivmiku.tutorial.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckLogin;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import com.ivmiku.tutorial.entity.IgnoreUser;
|
||||
import com.ivmiku.tutorial.response.Result;
|
||||
import com.ivmiku.tutorial.service.RelationService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@SaCheckLogin
|
||||
@RestController
|
||||
@RequestMapping("/api/relation")
|
||||
public class RelationController {
|
||||
@Autowired
|
||||
private RelationService relationService;
|
||||
|
||||
@PostMapping("/ignore")
|
||||
public Object ignoreUser(@RequestBody IgnoreUser input) {
|
||||
relationService.IgnoreUser(input.getUserId(), input.getToIgnore());
|
||||
return Result.ok();
|
||||
}
|
||||
|
||||
@GetMapping("subscribe")
|
||||
public Object subscribe(@RequestParam String subId) {
|
||||
String userId = (String) StpUtil.getLoginId();
|
||||
relationService.subscribe(userId, subId);
|
||||
return Result.ok();
|
||||
}
|
||||
|
||||
@GetMapping("fan")
|
||||
public Object getFanList(@RequestParam int page, @RequestParam int size) {
|
||||
String userId = (String) StpUtil.getLoginId();
|
||||
return Result.ok(relationService.getFanList(userId, page, size));
|
||||
}
|
||||
|
||||
@GetMapping("follow")
|
||||
public Object getFollowList(@RequestParam int page, @RequestParam int size) {
|
||||
String userId = (String) StpUtil.getLoginId();
|
||||
return Result.ok(relationService.getSubList(userId, page, size));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
package com.ivmiku.tutorial.controller;
|
||||
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.ivmiku.tutorial.entity.Message;
|
||||
import com.ivmiku.tutorial.service.MessageService;
|
||||
import com.ivmiku.tutorial.service.RelationService;
|
||||
import com.ivmiku.tutorial.utils.DateUtil;
|
||||
import com.ivmiku.tutorial.utils.MessageUtil;
|
||||
import jakarta.websocket.*;
|
||||
import jakarta.websocket.server.PathParam;
|
||||
import jakarta.websocket.server.ServerEndpoint;
|
||||
import org.springframework.amqp.core.ExchangeTypes;
|
||||
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.RabbitHandler;
|
||||
import org.springframework.amqp.rabbit.annotation.RabbitListener;
|
||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||
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.Controller;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.text.ParseException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import static jakarta.websocket.CloseReason.CloseCodes.CLOSED_ABNORMALLY;
|
||||
|
||||
@Controller
|
||||
@ServerEndpoint(value = "/chat/{satoken}")
|
||||
public class WebSocketServer implements ApplicationContextAware {
|
||||
public static Map<String, Session> sessionMap = new ConcurrentHashMap<>();
|
||||
|
||||
public static Map<String, Integer> controlMap = new HashMap<>();
|
||||
|
||||
private static ApplicationContext applicationContext;
|
||||
|
||||
@Autowired
|
||||
private MessageService messageService;
|
||||
|
||||
private RabbitTemplate rabbitTemplate;
|
||||
|
||||
private RelationService relationService;
|
||||
|
||||
@OnMessage
|
||||
public void onMessage(String message, Session session) throws IOException {
|
||||
Message msg = JSON.parseObject(message, Message.class);
|
||||
msg.setDate(DateUtil.getCurrentTime());
|
||||
if (MessageUtil.checkMessage(msg.getMessage())) {
|
||||
session.getBasicRemote().sendText("发送的信息含有敏感词,请进行调整");
|
||||
if (!controlMap.containsKey(msg.getFromId())){
|
||||
controlMap.put(msg.getFromId(), 0);
|
||||
}
|
||||
if (controlMap.get(msg.getFromId()) == 4){
|
||||
session.getBasicRemote().sendText("由于多次违反社区规则,您已被封禁1小时");
|
||||
session.close(new CloseReason(CLOSED_ABNORMALLY, "账号被封禁"));
|
||||
StpUtil.kickout(msg.getFromId());
|
||||
StpUtil.disable(msg.getFromId(), 3600);
|
||||
controlMap.put(msg.getFromId(), 0);
|
||||
}
|
||||
controlMap.put(msg.getFromId(), controlMap.get(msg.getFromId())+1);
|
||||
} else
|
||||
if (relationService.ifIgnored(msg.getToId(), msg.getFromId())) {
|
||||
session.getBasicRemote().sendText("您已被对方屏蔽");
|
||||
} else {
|
||||
rabbitTemplate.convertAndSend("exchange1", "", JSON.toJSONString(msg));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@OnOpen
|
||||
public void onOpen(Session session, EndpointConfig endpointConfig, @PathParam("satoken") String satoken) throws IOException {
|
||||
String userId = (String) StpUtil.getLoginIdByToken(satoken);
|
||||
if (userId == null) {
|
||||
session.getBasicRemote().sendText("Invalid Token");
|
||||
session.close();
|
||||
}
|
||||
this.messageService = WebSocketServer.applicationContext.getBean(MessageService.class);
|
||||
this.rabbitTemplate = WebSocketServer.applicationContext.getBean(RabbitTemplate.class);
|
||||
this.relationService = WebSocketServer.applicationContext.getBean(RelationService.class);
|
||||
sessionMap.put(userId, session);
|
||||
List<Message> unreadList = messageService.getUnreadMsg(userId);
|
||||
for(Message msg : unreadList) {
|
||||
session.getBasicRemote().sendText(JSON.toJSONString(msg));
|
||||
}
|
||||
}
|
||||
|
||||
@OnClose
|
||||
public void onClose(CloseReason closeReason, Session session){
|
||||
sessionMap.remove(session.getId());
|
||||
}
|
||||
|
||||
@OnError
|
||||
public void onError(Throwable throwable) throws IOException {
|
||||
throwable.printStackTrace();
|
||||
}
|
||||
|
||||
public void sendToUser(Message msg) throws IOException, ParseException {
|
||||
if (sessionMap.containsKey(msg.getToId())){
|
||||
sessionMap.get(msg.getToId()).getBasicRemote().sendText(JSON.toJSONString(msg));
|
||||
}
|
||||
else {
|
||||
messageService.insertUnreadMsg(msg.getToId(), msg);
|
||||
}
|
||||
msg.setChatId(messageService.getChatId(msg.getFromId(), msg.getToId()));
|
||||
messageService.insertToMysql(msg);
|
||||
messageService.insertToRedis(msg);
|
||||
}
|
||||
|
||||
public void sendToPublic(Message msg) throws IOException, ParseException {
|
||||
for (Session session : sessionMap.values()) {
|
||||
session.getBasicRemote().sendText(JSON.toJSONString(msg));
|
||||
}
|
||||
msg.setChatId(messageService.getChatId(msg.getFromId(), msg.getToId()));
|
||||
messageService.insertToMysql(msg);
|
||||
messageService.insertToRedis(msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
WebSocketServer.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
@RabbitHandler
|
||||
@RabbitListener(bindings = @QueueBinding(
|
||||
value = @Queue(),
|
||||
exchange = @Exchange(value = "exchange1",type = ExchangeTypes.FANOUT)
|
||||
))
|
||||
public void sendMsg(String message) throws IOException, ParseException {
|
||||
Message msg = JSON.parseObject(message, Message.class);
|
||||
if (sessionMap.containsKey(msg.getToId())) {
|
||||
sessionMap.get(msg.getToId()).getBasicRemote().sendText(JSON.toJSONString(msg));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.ivmiku.tutorial.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author Aurora
|
||||
*/
|
||||
@Data
|
||||
@TableName("chatid")
|
||||
public class ChatId {
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
private String id;
|
||||
private String user1Id;
|
||||
private String user2Id;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.ivmiku.tutorial.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @author Aurora
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@TableName("friend")
|
||||
public class Friend {
|
||||
private String user1Id;
|
||||
private String user2Id;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.ivmiku.tutorial.entity;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author Aurora
|
||||
*/
|
||||
@Data
|
||||
public class FriendQuery {
|
||||
private String user1Id;
|
||||
private String user2Id;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.ivmiku.tutorial.entity;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author Aurora
|
||||
*/
|
||||
@Data
|
||||
public class HistoryQuery {
|
||||
private String user1Id;
|
||||
private String user2Id;
|
||||
private int page;
|
||||
private int size;
|
||||
private String startDate;
|
||||
private String endDate;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.ivmiku.tutorial.entity;
|
||||
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @author Aurora
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@TableName("blacklist")
|
||||
public class IgnoreUser {
|
||||
@JSONField(ordinal = 1)
|
||||
private String userId;
|
||||
@JSONField(ordinal = 2)
|
||||
private String toIgnore;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.ivmiku.tutorial.entity;
|
||||
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @author Aurora
|
||||
*/
|
||||
@Data
|
||||
@TableName("message")
|
||||
public class Message implements Serializable {
|
||||
@JSONField(ordinal = 1)
|
||||
private String chatId;
|
||||
@JSONField(ordinal = 2)
|
||||
private String fromId;
|
||||
@JSONField(ordinal = 3)
|
||||
private String toId;
|
||||
@JSONField(ordinal = 4)
|
||||
private String message;
|
||||
@JSONField(ordinal = 5)
|
||||
private String date;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.ivmiku.tutorial.entity;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class SubUser {
|
||||
private String id;
|
||||
private String username;
|
||||
private String avatarUrl;
|
||||
|
||||
public static SubUser setUser(User user) {
|
||||
SubUser subUser = new SubUser();
|
||||
subUser.setId(user.getOpenid());
|
||||
subUser.setUsername(user.getNickname());
|
||||
subUser.setAvatarUrl(user.getAvatarUrl());
|
||||
return subUser;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.ivmiku.tutorial.entity;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author Aurora
|
||||
*/
|
||||
@TableName("subscribe")
|
||||
@Data
|
||||
public class Subscribe {
|
||||
private Long id;
|
||||
private Long subId;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.ivmiku.tutorial.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.ivmiku.tutorial.entity.IgnoreUser;
|
||||
|
||||
public interface BlackListMapper extends BaseMapper<IgnoreUser> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.ivmiku.tutorial.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.ivmiku.tutorial.entity.ChatId;
|
||||
|
||||
public interface ChatIdMapper extends BaseMapper<ChatId> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.ivmiku.tutorial.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.ivmiku.tutorial.entity.Friend;
|
||||
|
||||
public interface FriendMapper extends BaseMapper<Friend> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.ivmiku.tutorial.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.ivmiku.tutorial.entity.Message;
|
||||
|
||||
public interface MessageMapper extends BaseMapper<Message> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.ivmiku.tutorial.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.ivmiku.tutorial.entity.Subscribe;
|
||||
|
||||
public interface SubscribeMapper extends BaseMapper<Subscribe> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,236 @@
|
||||
package com.ivmiku.tutorial.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.ivmiku.tutorial.entity.ChatId;
|
||||
import com.ivmiku.tutorial.entity.Message;
|
||||
import com.ivmiku.tutorial.mapper.ChatIdMapper;
|
||||
import com.ivmiku.tutorial.mapper.MessageMapper;
|
||||
import com.ivmiku.tutorial.utils.RedisUtil;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author Aurora
|
||||
*/
|
||||
@Service
|
||||
public class MessageService {
|
||||
@Autowired
|
||||
private MessageMapper messageMapper;
|
||||
|
||||
@Autowired
|
||||
private ChatIdMapper chatIdMapper;
|
||||
|
||||
@Autowired
|
||||
private RedisUtil redisUtil;
|
||||
|
||||
/**
|
||||
* 获取会话id
|
||||
* @param user1Id 用户1id
|
||||
* @param user2Id 用户2id
|
||||
* @return 查询结果
|
||||
*/
|
||||
public String getChatId(String user1Id, String user2Id) {
|
||||
if (Objects.equals(user1Id, "public") || Objects.equals(user2Id, "public")) {
|
||||
return "0";
|
||||
}
|
||||
String id1, id2;
|
||||
if (user1Id.compareTo(user2Id) < 0) {
|
||||
id1 = user1Id;
|
||||
id2 = user2Id;
|
||||
} else {
|
||||
id1 = user2Id;
|
||||
id2 = user1Id;
|
||||
}
|
||||
QueryWrapper<ChatId> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("user1_id", id1);
|
||||
queryWrapper.eq("user2_id", id2);
|
||||
ChatId chatId = chatIdMapper.selectOne(queryWrapper);
|
||||
if (chatId == null) {
|
||||
chatId = new ChatId();
|
||||
chatId.setUser1Id(id1);
|
||||
chatId.setUser2Id(id2);
|
||||
chatIdMapper.insert(chatId);
|
||||
}
|
||||
return chatIdMapper.selectOne(queryWrapper).getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* 聊天记录写redis
|
||||
* @param msg 要写入的信息
|
||||
* @throws ParseException
|
||||
*/
|
||||
@Async
|
||||
public void insertToRedis(Message msg) throws ParseException {
|
||||
if (!(msg.getMessage().length() >1000)) {
|
||||
if (redisUtil.getZsetSize("history:" + msg.getChatId()) >= 50) {
|
||||
redisUtil.zsetRightPop("history:" + msg.getChatId());
|
||||
}
|
||||
redisUtil.zsetAdd("history:" + msg.getChatId(), msg);
|
||||
redisUtil.setExpireTime("history:" + msg.getChatId());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 聊天记录写入mysql
|
||||
* @param msg 要写入的消息
|
||||
*/
|
||||
@Async
|
||||
public void insertToMysql(Message msg) {
|
||||
if (!(msg.getMessage().length() >1000)) {
|
||||
messageMapper.insert(msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 未读消息写入redis
|
||||
* @param userId 用户id
|
||||
* @param msg 未读消息
|
||||
*/
|
||||
@Async
|
||||
public void insertUnreadMsg(String userId, Message msg) {
|
||||
redisUtil.listAdd("unread:" + userId, msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取未读消息列表
|
||||
* @param userId 用户id
|
||||
* @return 查询结果
|
||||
*/
|
||||
public List<Message> getUnreadMsg(String userId) {
|
||||
List<Message> result = redisUtil.listGet("unread:" + userId, 0, -1);
|
||||
redisUtil.listClear(userId);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从数据库获取聊天记录
|
||||
* @param chatId 会话id
|
||||
* @param current 分页参数
|
||||
* @param size 分页参数
|
||||
* @return 返回的查询结果
|
||||
*/
|
||||
public List<Message> getChatHistoryFromDB(String chatId, int current, int size) {
|
||||
QueryWrapper<Message> queryWrapper = new QueryWrapper<>();
|
||||
Page<Message> page = new Page<>(current, size);
|
||||
queryWrapper.eq("chat_id", chatId);
|
||||
queryWrapper.orderByDesc("date");
|
||||
return messageMapper.selectPage(page, queryWrapper).getRecords();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从数据库获取聊天记录,查询一定范围内
|
||||
* @param chatId 会话id
|
||||
* @param current 分页参数
|
||||
* @param size 分页参数
|
||||
* @param startDate 开始日期
|
||||
* @param endDate 结束日期
|
||||
* @return 返回的查询结果
|
||||
*/
|
||||
public List<Message> getChatHistoryFromDBByDate(String chatId, int current, int size, String startDate, String endDate) {
|
||||
QueryWrapper<Message> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.between("date", startDate, endDate);
|
||||
Page<Message> page = new Page<>(current, size);
|
||||
queryWrapper.eq("chat_id", chatId);
|
||||
queryWrapper.orderByDesc("date");
|
||||
return messageMapper.selectPage(page, queryWrapper).getRecords();
|
||||
}
|
||||
|
||||
/**
|
||||
* redis获取聊天记录
|
||||
* @param chatId 会话id
|
||||
* @param s 开始
|
||||
* @param e 结束
|
||||
* @return 查询结果
|
||||
*/
|
||||
public List<Message> getChatHistoryFromRedis(String chatId, int s, int e) {
|
||||
return redisUtil.zsetGet("history:" + chatId, s, e);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询聊天记录
|
||||
* @param user1Id 用户1id
|
||||
* @param user2Id 用户2id
|
||||
* @param page 分页参数
|
||||
* @param size 分页参数
|
||||
* @return 查询结果
|
||||
* @throws ParseException
|
||||
*/
|
||||
public List<Message> getChatHistory(String user1Id, String user2Id, int page, int size) throws ParseException {
|
||||
int start = page * size - size;
|
||||
int end = page * size - 1;
|
||||
String chatId = getChatId(user1Id, user2Id);
|
||||
loadCache(chatId);
|
||||
List<Message> result = new ArrayList<>(redisUtil.zsetGet("history:" + chatId, start, end));
|
||||
if ((end -start + 1) == result.size()) {
|
||||
return result;
|
||||
}
|
||||
int redisSize = result.size();
|
||||
List<Message> dbList = getChatHistoryFromDB(chatId, ((end - result.size()) / size) + 1, size);
|
||||
result.addAll(dbList.subList(redisSize, dbList.size()));
|
||||
redisUtil.refreshExpire("history:" + chatId);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 在一定时间范围内查询聊天记录
|
||||
* @param user1Id 用户1id
|
||||
* @param user2Id 用户2id
|
||||
* @param startDate 开始日期
|
||||
* @param endDate 结束日期
|
||||
* @param page 分页参数
|
||||
* @param size 分页参数
|
||||
* @return 查询结果
|
||||
* @throws ParseException
|
||||
*/
|
||||
public List<Message> getChatHistoryByDate(String user1Id, String user2Id, String startDate, String endDate, int page, int size) throws ParseException {
|
||||
int start = page * size - size;
|
||||
int end = page * size - 1;
|
||||
String chatId = getChatId(user1Id, user2Id);
|
||||
loadCache(chatId);
|
||||
List<Message> result = new ArrayList<>(redisUtil.zsetGetByDate("history:" + chatId, startDate, endDate, start, size));
|
||||
redisUtil.refreshExpire("history:" + chatId);
|
||||
if (result.size() == (end - start + 1)) {
|
||||
return result;
|
||||
}
|
||||
int redisSize = result.size();
|
||||
List<Message> dbList = getChatHistoryFromDBByDate(chatId, ((end - result.size()) / size) + 1, size, startDate, endDate).subList(result.size(), size);
|
||||
result.addAll(dbList.subList(redisSize, dbList.size()));
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会话列表
|
||||
* @param userId 用户id
|
||||
* @param current 分页参数
|
||||
* @param size 分页参数
|
||||
* @return 查询结果
|
||||
*/
|
||||
public List<ChatId> getChatList(String userId, int current, int size) {
|
||||
QueryWrapper<ChatId> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("user1_id", userId).or().eq("user2_id", userId);
|
||||
queryWrapper.orderByDesc("id");
|
||||
Page<ChatId> page = new Page<>(current, size);
|
||||
return chatIdMapper.selectPage(page, queryWrapper).getRecords();
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载聊天记录到redis
|
||||
* @param chatId 会话id
|
||||
* @throws ParseException
|
||||
*/
|
||||
public void loadCache(String chatId) throws ParseException {
|
||||
if (!redisUtil.ifExist("history:" + chatId)) {
|
||||
List<Message> list = getChatHistoryFromDB(chatId, 1, 20);
|
||||
for (Message message : list) {
|
||||
insertToRedis(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
package com.ivmiku.tutorial.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
|
||||
import com.ivmiku.tutorial.entity.*;
|
||||
import com.ivmiku.tutorial.mapper.BlackListMapper;
|
||||
import com.ivmiku.tutorial.mapper.FriendMapper;
|
||||
import com.ivmiku.tutorial.mapper.SubscribeMapper;
|
||||
import com.ivmiku.tutorial.utils.RedisUtil;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Aurora
|
||||
*/
|
||||
@Service
|
||||
public class RelationService {
|
||||
@Autowired
|
||||
private BlackListMapper blackListMapper;
|
||||
|
||||
@Autowired
|
||||
private FriendMapper friendMapper;
|
||||
|
||||
@Autowired
|
||||
private RedisUtil redisUtil;
|
||||
|
||||
@Autowired
|
||||
private SubscribeMapper subscribeMapper;
|
||||
|
||||
@Autowired
|
||||
private UserService userService;
|
||||
|
||||
/**
|
||||
* 屏蔽用户
|
||||
* @param userId 用户id
|
||||
* @param toIgnore 要屏蔽的用户id
|
||||
*/
|
||||
public void IgnoreUser(String userId, String toIgnore) {
|
||||
blackListMapper.insert(new IgnoreUser(userId, toIgnore));
|
||||
if (redisUtil.ifExist("blacklist:" + userId)) {
|
||||
redisUtil.listAdd("blacklist:" + userId, toIgnore);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询用户是否屏蔽了该用户
|
||||
* @param userId 用户id
|
||||
* @param ignoreId 要查询的id
|
||||
* @return 查询结果
|
||||
*/
|
||||
public boolean ifIgnored(String userId, String ignoreId) {
|
||||
loadCache(userId);
|
||||
List<String> blackList = redisUtil.getStringList("blacklist:" + userId, 0, -1);
|
||||
redisUtil.refreshExpire("blacklist:" + userId);
|
||||
return blackList.contains(ignoreId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载缓存
|
||||
* @param userId 用户id
|
||||
*/
|
||||
public void loadCache(String userId) {
|
||||
if (!redisUtil.ifExist("blacklist:" + userId)) {
|
||||
QueryWrapper<IgnoreUser> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("user_id", userId);
|
||||
List<IgnoreUser> list = blackListMapper.selectList(queryWrapper);
|
||||
List<String> result = new ArrayList<>();
|
||||
if (list != null) {
|
||||
for (IgnoreUser object : list) {
|
||||
result.add(object.getToIgnore());
|
||||
}
|
||||
}
|
||||
for (String toIgnore : result) {
|
||||
redisUtil.listAdd("blacklist:" + userId, toIgnore);
|
||||
}
|
||||
redisUtil.setExpireTime("blacklist:" + userId);
|
||||
}
|
||||
}
|
||||
|
||||
public void subscribe(String id, String toId) {
|
||||
Subscribe subscribe = new Subscribe();
|
||||
subscribe.setId(Long.valueOf(id));
|
||||
subscribe.setSubId(Long.valueOf(toId));
|
||||
subscribeMapper.insert(subscribe);
|
||||
}
|
||||
|
||||
public List<SubUser> getFanList(String userId, int page, int size) {
|
||||
List<SubUser> list = new ArrayList<>();
|
||||
QueryWrapper<Subscribe> queryWrapper = new QueryWrapper<>();
|
||||
Page<Subscribe> pager = new Page<>(page, size);
|
||||
queryWrapper.eq("sub_id", userId);
|
||||
List<Subscribe> array = subscribeMapper.selectPage(pager, queryWrapper).getRecords();
|
||||
for (Subscribe subscribe : array) {
|
||||
String queryId = String.valueOf(subscribe.getId());
|
||||
User user = userService.selectUserById(queryId);
|
||||
SubUser subUser = SubUser.setUser(user);
|
||||
list.add(subUser);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public List<SubUser> getSubList(String userId, int page, int size) {
|
||||
List<SubUser> list = new ArrayList<>();
|
||||
QueryWrapper<Subscribe> queryWrapper = new QueryWrapper<>();
|
||||
Page<Subscribe> pager = new Page<>(page, size);
|
||||
queryWrapper.eq("id", userId);
|
||||
List<Subscribe> array = subscribeMapper.selectPage(pager, queryWrapper).getRecords();
|
||||
for (Subscribe subscribe : array) {
|
||||
String queryId = String.valueOf(subscribe.getSubId());
|
||||
User user = userService.selectUserById(queryId);
|
||||
SubUser subUser = SubUser.setUser(user);
|
||||
list.add(subUser);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.ivmiku.tutorial.service;
|
||||
|
||||
import com.ivmiku.tutorial.entity.User;
|
||||
import com.ivmiku.tutorial.mapper.UserMapper;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class UserService {
|
||||
@Resource
|
||||
private UserMapper userMapper;
|
||||
|
||||
public User selectUserById(String id) {
|
||||
return userMapper.selectById(id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.ivmiku.tutorial.utils;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.TimeZone;
|
||||
|
||||
/**
|
||||
* @author Aurora
|
||||
*/
|
||||
public class DateUtil {
|
||||
/**
|
||||
* 获取当前时间
|
||||
* @return 当前时间字符串
|
||||
*/
|
||||
public static String getCurrentTime() {
|
||||
Date date = new Date();
|
||||
SimpleDateFormat ft = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
return ft.format(date);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为时间戳
|
||||
* @param time 时间字符串
|
||||
* @return 时间戳
|
||||
* @throws ParseException
|
||||
*/
|
||||
public static long toTimeSig(String time) throws ParseException {
|
||||
SimpleDateFormat ft = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
Date date = ft.parse(time);
|
||||
return date.getTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当前时区GMT+8
|
||||
*/
|
||||
@PostConstruct
|
||||
public void setTimeZone() {
|
||||
TimeZone.setDefault(TimeZone.getTimeZone("Asia/Shanghai"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.ivmiku.tutorial.utils;
|
||||
|
||||
import com.ivmiku.tutorial.service.RelationService;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 敏感词检测
|
||||
* @author Aurora
|
||||
*/
|
||||
@Component
|
||||
public class MessageUtil {
|
||||
private static final String[] SENSITIVE = {"你妈", "你妈逼的"};
|
||||
|
||||
private static RelationService relationService = null;
|
||||
|
||||
public MessageUtil(RelationService relationService) {
|
||||
MessageUtil.relationService = relationService;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查看发送的信息是否含有敏感词
|
||||
* @param message 要发送的信息
|
||||
* @return 检查结果
|
||||
*/
|
||||
public static boolean checkMessage(String message) {
|
||||
if (message != null) {
|
||||
for(String keyword : SENSITIVE) {
|
||||
if (message.contains(keyword)){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,9 +1,18 @@
|
||||
package com.ivmiku.tutorial.utils;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.ivmiku.tutorial.entity.Message;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @author Aurora
|
||||
*/
|
||||
@@ -12,11 +21,97 @@ public class RedisUtil {
|
||||
@Autowired
|
||||
private RedisTemplate<String, Object> redisTemplate;
|
||||
|
||||
public void insertKey(String userId, String sessionKey) {
|
||||
redisTemplate.opsForValue().set("sessionkey:" + userId, sessionKey);
|
||||
public void listAdd(String key, Object value) {
|
||||
redisTemplate.opsForList().leftPush(key, value);
|
||||
}
|
||||
|
||||
public String getKey(String userId) {
|
||||
return (String) redisTemplate.opsForValue().get("sessionkey:" + userId);
|
||||
public List<Message> listGet(String key, int s, int e) {
|
||||
List<Object> list = redisTemplate.opsForList().range(key, s, e);
|
||||
List<Message> result = new ArrayList<>();
|
||||
if (list != null) {
|
||||
for (Object json : list) {
|
||||
result.add(JSON.parseObject(JSON.toJSONString(json), Message.class));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public void listClear(String key) {
|
||||
redisTemplate.opsForList().trim(key, 1, 0);
|
||||
}
|
||||
|
||||
public Long getListSize(String key) {
|
||||
return redisTemplate.opsForList().size(key);
|
||||
}
|
||||
|
||||
public Set<String> getKey() {
|
||||
return redisTemplate.keys("history:*");
|
||||
}
|
||||
|
||||
public Object rightPop(String key) {
|
||||
return redisTemplate.opsForList().rightPop(key);
|
||||
}
|
||||
|
||||
public void zsetAdd(String key, Message value) throws ParseException {
|
||||
redisTemplate.opsForZSet().add(key, value, DateUtil.toTimeSig(value.getDate()));
|
||||
}
|
||||
|
||||
public List<Message> zsetGet(String key, int s, int e) {
|
||||
Set<Object> list = redisTemplate.opsForZSet().reverseRange(key, s, e);
|
||||
List<Message> result = new ArrayList<>();
|
||||
if (list != null) {
|
||||
for (Object json : list) {
|
||||
result.add(JSON.parseObject(JSON.toJSONString(json), Message.class));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public Long getzsetSize(String key) {
|
||||
return redisTemplate.opsForZSet().size(key);
|
||||
}
|
||||
|
||||
public List<Message> zsetGetByDate(String key, String startDate, String endDate, int offset, int count) throws ParseException {
|
||||
Set<Object> list = redisTemplate.opsForZSet().reverseRangeByScore(key, DateUtil.toTimeSig(startDate), DateUtil.toTimeSig(endDate), offset, count);
|
||||
List<Message> result = new ArrayList<>();
|
||||
if (list != null) {
|
||||
for (Object json : list) {
|
||||
result.add(JSON.parseObject(JSON.toJSONString(json), Message.class));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public Message zsetRightPop(String key) {
|
||||
return JSON.parseObject(JSON.toJSONString(Objects.requireNonNull(redisTemplate.opsForZSet().popMin(key)).getValue()), Message.class);
|
||||
}
|
||||
|
||||
public Long getZsetSize(String key) {
|
||||
return redisTemplate.opsForZSet().size(key);
|
||||
}
|
||||
|
||||
public void setExpireTime(String key) {
|
||||
if (redisTemplate.opsForValue().getOperations().getExpire(key) > 0) {
|
||||
redisTemplate.expire(key, 3, TimeUnit.DAYS);
|
||||
}
|
||||
}
|
||||
|
||||
public void refreshExpire(String key) {
|
||||
redisTemplate.persist(key);
|
||||
redisTemplate.expire(key, 3, TimeUnit.DAYS);
|
||||
}
|
||||
|
||||
public boolean ifExist(String key) {
|
||||
return Boolean.TRUE.equals(redisTemplate.hasKey(key));
|
||||
}
|
||||
|
||||
public List<String> getStringList(String key, int s, int e) {
|
||||
List<Object> list = redisTemplate.opsForList().range(key, s, e);
|
||||
List<String> result = new ArrayList<>();
|
||||
assert list != null;
|
||||
for (Object object : list) {
|
||||
result.add((String) object);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user