feat: 私信和关注、粉丝功能

This commit is contained in:
ivmiku
2024-08-24 18:19:01 +08:00
parent 7deaba288b
commit 641449be7c
27 changed files with 1062 additions and 4 deletions

View File

@@ -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;
}
}

View File

@@ -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());
}
}

View File

@@ -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("/**");
}
}

View File

@@ -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;
}
}

View File

@@ -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));
}
}

View File

@@ -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));
}
}

View File

@@ -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));
}
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -0,0 +1,12 @@
package com.ivmiku.tutorial.entity;
import lombok.Data;
/**
* @author Aurora
*/
@Data
public class FriendQuery {
private String user1Id;
private String user2Id;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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> {
}

View File

@@ -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> {
}

View File

@@ -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> {
}

View File

@@ -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> {
}

View File

@@ -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> {
}

View File

@@ -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);
}
}
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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"));
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}