├── .gitignore ├── README.md ├── chathub-common ├── pom.xml └── src │ └── main │ └── java │ └── bupt │ └── edu │ └── jhc │ └── chathub │ └── common │ ├── aspect │ └── ReqLogRecord.java │ ├── config │ ├── CorsConfig.java │ ├── MvcConfig.java │ ├── MybatisPlusConfig.java │ ├── SwaggerConfig.java │ └── ThreadPoolConfig.java │ ├── domain │ ├── constants │ │ ├── MDCKeys.java │ │ ├── MqConstants.java │ │ ├── RedisConstants.java │ │ └── SystemConstants.java │ ├── enums │ │ └── ErrorStatus.java │ └── vo │ │ ├── request │ │ └── CursorPageBaseReq.java │ │ └── resp │ │ ├── CursorPageBaseResp.java │ │ └── Response.java │ ├── interceptor │ ├── CheckTokenInterceptor.java │ └── RefreshTokenInterceptor.java │ ├── service │ └── cache │ │ ├── AbstractRedisStringCache.java │ │ └── BatchCache.java │ └── utils │ ├── CursorUtils.java │ ├── JsonUtils.java │ ├── PasswordEncoder.java │ ├── RedisUtils.java │ ├── context │ ├── RequestHolder.java │ └── RequestInfo.java │ ├── exception │ ├── BizException.java │ ├── GlobalExceptionHandler.java │ └── ThrowUtils.java │ └── regex │ ├── RegexPatterns.java │ └── RegexUtils.java ├── chathub-server ├── pom.xml └── src │ └── main │ ├── java │ └── bupt │ │ └── edu │ │ └── jhc │ │ └── chathub │ │ └── server │ │ ├── ChathubApplication.java │ │ ├── chat │ │ ├── controller │ │ │ └── MessageController.java │ │ ├── domain │ │ │ ├── dto │ │ │ │ ├── FileMsgDTO.java │ │ │ │ ├── ImgMsgDTO.java │ │ │ │ ├── MessageExtra.java │ │ │ │ ├── MsgPageReq.java │ │ │ │ ├── SendMsgDTO.java │ │ │ │ └── TextMsgDTO.java │ │ │ ├── entity │ │ │ │ ├── Message.java │ │ │ │ ├── Room.java │ │ │ │ └── UserRoom.java │ │ │ ├── enums │ │ │ │ └── MsgTypeEnum.java │ │ │ └── vo │ │ │ │ ├── RoomVO.java │ │ │ │ └── ShowMsgVO.java │ │ ├── event │ │ │ ├── MessageSendEvent.java │ │ │ └── listener │ │ │ │ └── MessageSendListener.java │ │ ├── mapper │ │ │ ├── MessageMapper.java │ │ │ ├── RoomMapper.java │ │ │ └── UserRoomMapper.java │ │ └── service │ │ │ ├── IMessageService.java │ │ │ ├── IRoomService.java │ │ │ ├── adapter │ │ │ └── MsgAdapter.java │ │ │ ├── cache │ │ │ ├── RoomLatestMsgCache.java │ │ │ └── UserRoomCache.java │ │ │ ├── impl │ │ │ ├── MessageServiceImpl.java │ │ │ └── RoomServiceImpl.java │ │ │ └── strategy │ │ │ ├── AbstractMsgHandler.java │ │ │ ├── FileMsgHandler.java │ │ │ ├── ImgMsgHandler.java │ │ │ ├── MsgHandlerFactory.java │ │ │ └── TextMsgHandler.java │ │ ├── friend │ │ ├── controller │ │ │ ├── FriendController.java │ │ │ └── NoticeController.java │ │ ├── domain │ │ │ ├── dto │ │ │ │ ├── FriendApplication.java │ │ │ │ └── FriendApplicationReply.java │ │ │ ├── entity │ │ │ │ ├── FriendNotice.java │ │ │ │ └── FriendRelation.java │ │ │ └── vo │ │ │ │ └── FriendNoticeVO.java │ │ ├── mapper │ │ │ ├── FriendNoticeMapper.java │ │ │ └── FriendRelationMapper.java │ │ └── service │ │ │ ├── IFriendNoticeService.java │ │ │ ├── IFriendService.java │ │ │ └── impl │ │ │ ├── FriendNoticeServiceImpl.java │ │ │ └── FriendServiceImpl.java │ │ ├── group │ │ ├── controller │ │ │ └── GroupController.java │ │ ├── domain │ │ │ ├── dto │ │ │ │ ├── CreateGroupDTO.java │ │ │ │ └── InvitationRespDTO.java │ │ │ ├── entity │ │ │ │ ├── Group.java │ │ │ │ ├── GroupNotice.java │ │ │ │ └── GroupRelation.java │ │ │ └── vo │ │ │ │ ├── GroupNoticeVO.java │ │ │ │ └── GroupVO.java │ │ ├── mapper │ │ │ ├── GroupMapper.java │ │ │ ├── GroupNoticeMapper.java │ │ │ └── GroupRelationMapper.java │ │ └── service │ │ │ ├── IGroupNoticeService.java │ │ │ ├── IGroupService.java │ │ │ ├── adapter │ │ │ ├── GroupAdapter.java │ │ │ └── GroupNoticeAdapter.java │ │ │ └── impl │ │ │ ├── GroupNoticeServiceImpl.java │ │ │ └── GroupServiceImpl.java │ │ ├── sse │ │ ├── controller │ │ │ └── SseController.java │ │ ├── domain │ │ │ ├── enums │ │ │ │ └── SseRespType.java │ │ │ └── vo │ │ │ │ └── SseResponse.java │ │ ├── manager │ │ │ └── SseSessionManager.java │ │ └── service │ │ │ ├── ISseService.java │ │ │ └── impl │ │ │ └── SseServiceImpl.java │ │ ├── trend │ │ ├── controller │ │ │ ├── CommentController.java │ │ │ └── TrendController.java │ │ ├── domain │ │ │ ├── dto │ │ │ │ ├── comment │ │ │ │ │ └── PostCommentDTO.java │ │ │ │ └── talk │ │ │ │ │ ├── CreateTalkDTO.java │ │ │ │ │ ├── ImgDTO.java │ │ │ │ │ ├── LikeInfoDTO.java │ │ │ │ │ ├── TalkExtra.java │ │ │ │ │ └── VideoDTO.java │ │ │ ├── entity │ │ │ │ ├── Comment.java │ │ │ │ ├── Feeds.java │ │ │ │ ├── Like.java │ │ │ │ └── Talk.java │ │ │ └── vo │ │ │ │ ├── CommentVO.java │ │ │ │ └── TalkVO.java │ │ ├── mapper │ │ │ ├── CommentMapper.java │ │ │ ├── FeedsMapper.java │ │ │ ├── LikeMapper.java │ │ │ └── TalkMapper.java │ │ ├── mq │ │ │ ├── feeds │ │ │ │ ├── FeedsInitMain.java │ │ │ │ ├── FeedsMessage.java │ │ │ │ ├── FeedsMessageConsumer.java │ │ │ │ └── FeedsMessageProducer.java │ │ │ └── like │ │ │ │ ├── LikeInitMain.java │ │ │ │ ├── LikeMessage.java │ │ │ │ ├── LikeMessageConsumer.java │ │ │ │ └── LikeMessageProducer.java │ │ └── service │ │ │ ├── ICommentService.java │ │ │ ├── ITrendService.java │ │ │ └── impl │ │ │ ├── CommentServiceImpl.java │ │ │ ├── FeedsServiceImpl.java │ │ │ └── TrendServiceImpl.java │ │ ├── user │ │ ├── controller │ │ │ ├── OSSController.java │ │ │ └── UserController.java │ │ ├── domain │ │ │ ├── dto │ │ │ │ ├── LoginFormDTO.java │ │ │ │ ├── PhoneLoginFormDTO.java │ │ │ │ ├── RegisterFormDTO.java │ │ │ │ └── UserDTO.java │ │ │ ├── entity │ │ │ │ └── User.java │ │ │ └── vo │ │ │ │ ├── ForceLogoutInfo.java │ │ │ │ └── UserVO.java │ │ ├── mapper │ │ │ └── UserMapper.java │ │ └── service │ │ │ ├── IUserService.java │ │ │ └── impl │ │ │ └── UserServiceImpl.java │ │ └── websocket │ │ ├── NettyWebSocketServer.java │ │ ├── domain │ │ ├── enums │ │ │ └── WSReqEnum.java │ │ ├── req │ │ │ └── WSReqDTO.java │ │ └── resp │ │ │ └── WSResponse.java │ │ ├── handler │ │ ├── HttpHeaderHandler.java │ │ └── NettyWebSocketServerHandler.java │ │ ├── service │ │ ├── IWebSocketService.java │ │ └── impl │ │ │ └── WebSocketServiceImpl.java │ │ └── utils │ │ └── NettyUtils.java │ └── resources │ ├── application.yaml │ └── logback.xml ├── chathub-tools ├── chathub-frequency-control │ ├── pom.xml │ └── src │ │ └── main │ │ └── java │ │ └── bupt │ │ └── edu │ │ └── jhc │ │ └── chathub │ │ └── frequency_control │ │ ├── annotation │ │ ├── FrequencyControl.java │ │ └── FrequencyControlContainer.java │ │ ├── aspect │ │ └── FrequencyControlAspect.java │ │ ├── domain │ │ └── FrequencyControlDTO.java │ │ ├── service │ │ ├── AbstractFrequencyControlService.java │ │ ├── FrequencyControlStrategyFactory.java │ │ └── strategy │ │ │ └── TotalCountWithInFixTimeFrequencyController.java │ │ └── utils │ │ ├── FrequencyControlUtils.java │ │ ├── JsonUtils.java │ │ ├── RedisUtils2.java │ │ └── SpElUtils.java ├── chathub-oss-starter │ ├── pom.xml │ └── src │ │ └── main │ │ ├── java │ │ └── bupt │ │ │ └── edu │ │ │ └── jhc │ │ │ └── chathub │ │ │ └── oss │ │ │ ├── FileUtils.java │ │ │ ├── OssProperties.java │ │ │ ├── OssType.java │ │ │ └── minio │ │ │ ├── MinioConfig.java │ │ │ └── MinioTemplate.java │ │ └── resources │ │ └── META-INF │ │ └── spring.factories └── pom.xml ├── docs ├── chathub.sql ├── deployment │ ├── java.sh │ ├── minio │ │ └── docker-compose.yml │ ├── mysql │ │ └── docker-compose.yml │ ├── rabbitmq │ │ └── docker-compose.yml │ └── redis │ │ └── docker-compose.yml └── images │ ├── 图片1.jpg │ ├── 图片2.jpg │ ├── 图片3.jpg │ ├── 图片4.jpg │ └── 系统架构图.jpg └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | !**/src/main/**/target/ 4 | !**/src/test/**/target/ 5 | 6 | ### IntelliJ IDEA ### 7 | .idea 8 | *.iws 9 | *.iml 10 | *.ipr 11 | 12 | ### Eclipse ### 13 | .apt_generated 14 | .classpath 15 | .factorypath 16 | .project 17 | .settings 18 | .springBeans 19 | .sts4-cache 20 | 21 | ### NetBeans ### 22 | /nbproject/private/ 23 | /nbbuild/ 24 | /dist/ 25 | /nbdist/ 26 | /.nb-gradle/ 27 | build/ 28 | !**/src/main/**/build/ 29 | !**/src/test/**/build/ 30 | 31 | ### VS Code ### 32 | .vscode/ 33 | 34 | ### Mac OS ### 35 | .DS_Store 36 | 37 | ### data ### 38 | data/ 39 | 40 | ### application-prod.properties ### 41 | chathub-server/src/main/resources/application-prod.properties 42 | chathub-server/src/main/resources/application-dev.properties 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # chathub - 一个简化版qq的移动端项目 2 | 3 | >聊天中心项目:仿照 qq 移动端的 UI 页面,实现了 qq 相关的核心功能! 4 | > 5 | >前后端全栈项目 By [JollyCorivuG](https://github.com/JollyCorivuG) 6 | 7 | 上线地址:[chathub.love](http://www.chathub.love/) 8 | 9 | ## 效果展示 10 | 11 | ![](https://github.com/JollyCorivuG/chathub/blob/main/docs/images/图片1.jpg) 12 | 13 | ![](https://github.com/JollyCorivuG/chathub/blob/main/docs/images/图片2.jpg) 14 | 15 | ![](https://github.com/JollyCorivuG/chathub/blob/main/docs/images/图片3.jpg) 16 | 17 | ![](https://github.com/JollyCorivuG/chathub/blob/main/docs/images/图片4.jpg) 18 | 19 | ## 功能大全 20 | 21 | ### 用户 22 | - 用户注册、登录(含账号、手机验证码两种登录方式) 23 | - 用户可以通过关键字搜索用户,并发送好友申请 24 | - 用户可以查看通知,同意或拒绝好友申请 25 | - 同一个账号只能在一个浏览器登录,否则原有账号会被强制下线 26 | - 用户可以修改头像、用户名等个人信息 27 | 28 | ### 消息 29 | - 用户可以发送消息给自己的好友(支持表情包、图片、文件等多种类型) 30 | - 用户可以实时接收消息 31 | - 消息面板自动刷新,会更新消息未读数 32 | - 用户可以删除会话信息,并在有消息时重新显示 33 | 34 | ### 群组 35 | - 用户可以创建群组,并指定群组相关信息(头像,最大人数限制等) 36 | - 用户可以邀请自己的好友加入群组 37 | - 用户可以在群组内发送消息,同属一个群组的人都能实时收到消息 38 | - 用户可以同意、拒绝入群邀请 39 | 40 | ### 动态 41 | - 用户可以发送动态,并可以选取相应的图片 42 | - 用户可以点赞动态,并看到最新点赞的人和点赞总数 43 | - 用户可以评论好友发送的动态,也可以进行评论的回复 44 | 45 | ## 技术栈 46 | ### 前端 47 | - vue3 + ts 48 | - Vant4 UI 49 | - axios 请求库 50 | - pinia 状态管理 51 | - pinia 持久化插件 52 | - Vue Router 路由管理 53 | 54 | ### 后端 55 | - Java 8 + Spring Boot 框架(spring boot 2.6.7) 56 | - Spring MVC + Mybatis Plus 框架 57 | - Knife4j + Swagger 生成接口文档 58 | - MySQL 8.x (数据存储) + Redis(缓存) 59 | - Netty + WebSocket 实现即时通讯 60 | - SSE 实现消息列表实时刷新 61 | - RabbitMQ 消息队列 62 | - MinIO 实现文件存储 63 | 64 | ## 系统架构 65 | 66 | ![](https://github.com/JollyCorivuG/chathub/blob/main/docs/images/系统架构图.jpg) 67 | 68 | -------------------------------------------------------------------------------- /chathub-common/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | bupt.edu.jhc 8 | chathub 9 | 1.0-SNAPSHOT 10 | 11 | 12 | chathub-common 13 | 14 | 15 | 8 16 | 8 17 | UTF-8 18 | 19 | 20 | 21 | 22 | org.projectlombok 23 | lombok 24 | 25 | 26 | cn.hutool 27 | hutool-all 28 | 29 | 30 | org.apache.commons 31 | commons-lang3 32 | 33 | 34 | org.springframework.boot 35 | spring-boot-starter 36 | 37 | 38 | org.springframework.boot 39 | spring-boot-starter-actuator 40 | 41 | 42 | org.springframework.boot 43 | spring-boot-starter-aop 44 | 45 | 46 | org.springframework.boot 47 | spring-boot-starter-web 48 | 49 | 50 | org.springframework.boot 51 | spring-boot-starter-data-redis 52 | 53 | 54 | com.baomidou 55 | mybatis-plus-boot-starter 56 | 57 | 58 | com.github.xiaoymin 59 | knife4j-spring-boot-starter 60 | 61 | 62 | 63 | 64 | 65 | 66 | org.springframework.boot 67 | spring-boot-maven-plugin 68 | 69 | none 70 | execute 71 | 72 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /chathub-common/src/main/java/bupt/edu/jhc/chathub/common/aspect/ReqLogRecord.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.common.aspect; 2 | 3 | import bupt.edu.jhc.chathub.common.domain.constants.MDCKeys; 4 | import cn.hutool.core.lang.UUID; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.aspectj.lang.ProceedingJoinPoint; 7 | import org.aspectj.lang.annotation.Around; 8 | import org.aspectj.lang.annotation.Aspect; 9 | import org.slf4j.MDC; 10 | import org.springframework.stereotype.Component; 11 | import org.springframework.util.StopWatch; 12 | import org.springframework.web.context.request.RequestAttributes; 13 | import org.springframework.web.context.request.RequestContextHolder; 14 | import org.springframework.web.context.request.ServletRequestAttributes; 15 | import org.apache.commons.lang3.StringUtils; 16 | 17 | import javax.servlet.http.HttpServletRequest; 18 | 19 | /** 20 | * @Description: 接口请求日志记录 21 | * @Author: JollyCorivuG 22 | * @CreateTime: 2024/1/18 23 | */ 24 | @Aspect 25 | @Component 26 | @Slf4j 27 | public class ReqLogRecord { 28 | @Around("execution(* bupt.edu.jhc.chathub.server.*.controller..*.*(..))") 29 | public Object doInterceptor(ProceedingJoinPoint point) throws Throwable { 30 | // 1.开启一个计时器 31 | StopWatch stopWatch = new StopWatch(); 32 | stopWatch.start(); 33 | 34 | // 2.输出请求日志 35 | // 2.1获取请求路径 36 | RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes(); 37 | HttpServletRequest httpServletRequest = ((ServletRequestAttributes) requestAttributes).getRequest(); 38 | // 2.2生成链路唯一 id 39 | String tId = UUID.randomUUID().toString(); 40 | MDC.put(MDCKeys.TID, tId); 41 | String url = httpServletRequest.getRequestURI(); 42 | // 2.3获取请求参数 43 | Object[] args = point.getArgs(); 44 | String reqParam = "[" + StringUtils.join(args, ", ") + "]"; 45 | // 2.4打印日志 46 | log.info("request start, path: {}, ip: {}, params: {}", url, 47 | httpServletRequest.getRemoteHost(), reqParam); 48 | 49 | // 3.执行原方法 50 | Object result = point.proceed(); 51 | MDC.remove(MDCKeys.TID); 52 | 53 | // 4.输出响应日志 54 | stopWatch.stop(); 55 | long totalTimeMillis = stopWatch.getTotalTimeMillis(); 56 | log.info("request end, cost: {} ms", totalTimeMillis); 57 | return result; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /chathub-common/src/main/java/bupt/edu/jhc/chathub/common/config/CorsConfig.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.common.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.web.cors.CorsConfiguration; 6 | import org.springframework.web.cors.UrlBasedCorsConfigurationSource; 7 | import org.springframework.web.filter.CorsFilter; 8 | 9 | /** 10 | * @Description: 跨域配置类 11 | * @Author: JollyCorivuG 12 | * @CreateTime: 2024/1/16 13 | */ 14 | @Configuration 15 | public class CorsConfig { 16 | @Bean 17 | public CorsFilter corsFilter() { 18 | // 1.创建 CORS 配置对象 19 | CorsConfiguration config = new CorsConfiguration(); 20 | // 支持域 21 | config.addAllowedOriginPattern("*"); 22 | // 是否发送 Cookie 23 | config.setAllowCredentials(true); 24 | // 支持请求方式 25 | config.addAllowedMethod("*"); 26 | // 允许的原始请求头部信息 27 | config.addAllowedHeader("*"); 28 | // 暴露的头部信息 29 | config.addExposedHeader("*"); 30 | // 2.添加地址映射 31 | UrlBasedCorsConfigurationSource corsConfigurationSource = new UrlBasedCorsConfigurationSource(); 32 | corsConfigurationSource.registerCorsConfiguration("/**", config); 33 | // 3.返回 CorsFilter 对象 34 | return new CorsFilter(corsConfigurationSource); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /chathub-common/src/main/java/bupt/edu/jhc/chathub/common/config/MvcConfig.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.common.config; 2 | 3 | import bupt.edu.jhc.chathub.common.interceptor.CheckTokenInterceptor; 4 | import bupt.edu.jhc.chathub.common.interceptor.RefreshTokenInterceptor; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.data.redis.core.StringRedisTemplate; 7 | import org.springframework.web.servlet.config.annotation.InterceptorRegistry; 8 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 9 | 10 | import javax.annotation.Resource; 11 | 12 | /** 13 | * @Description: Mvc 配置 14 | * @Author: JollyCorivuG 15 | * @CreateTime: 2024/1/16 16 | */ 17 | @Configuration 18 | public class MvcConfig implements WebMvcConfigurer { 19 | @Resource 20 | private StringRedisTemplate stringRedisTemplate; 21 | 22 | @Override 23 | public void addInterceptors(InterceptorRegistry registry) { 24 | // 刷新token的拦截器 25 | registry.addInterceptor(new RefreshTokenInterceptor(stringRedisTemplate)).addPathPatterns("/**").order(0); 26 | 27 | // 需要校验token的拦截器 28 | registry.addInterceptor(new CheckTokenInterceptor()) 29 | .addPathPatterns("/api/**") 30 | .excludePathPatterns( 31 | "/api/upload/**", 32 | "/api/users/login", 33 | "/api/users/register", 34 | "/api/users/phone_code/**", 35 | "/api/users/phone_login", 36 | "/api/sse/unsubscribe" 37 | ).order(1); 38 | } 39 | } -------------------------------------------------------------------------------- /chathub-common/src/main/java/bupt/edu/jhc/chathub/common/config/MybatisPlusConfig.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.common.config; 2 | 3 | import com.baomidou.mybatisplus.annotation.DbType; 4 | import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; 5 | import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | import org.springframework.transaction.annotation.EnableTransactionManagement; 9 | 10 | /** 11 | * @Description: Mybatis Plus 配置类 12 | * @Author: JollyCorivuG 13 | * @CreateTime: 2024/1/16 14 | */ 15 | @Configuration 16 | @EnableTransactionManagement 17 | public class MybatisPlusConfig { 18 | /** 19 | * 新增分页拦截器,并设置数据库类型为mysql 20 | * @return MybatisPlusInterceptor 21 | */ 22 | @Bean 23 | public MybatisPlusInterceptor mybatisPlusInterceptor() { 24 | MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); 25 | interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); 26 | return interceptor; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /chathub-common/src/main/java/bupt/edu/jhc/chathub/common/config/ThreadPoolConfig.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.common.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.context.annotation.Primary; 6 | import org.springframework.scheduling.annotation.AsyncConfigurer; 7 | import org.springframework.scheduling.annotation.EnableAsync; 8 | import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; 9 | 10 | import java.util.concurrent.Executor; 11 | import java.util.concurrent.ThreadPoolExecutor; 12 | 13 | /** 14 | * @Description: 线程池配置 15 | * @Author: JollyCorivuG 16 | * @CreateTime: 2024/1/16 17 | */ 18 | @Configuration 19 | @EnableAsync 20 | public class ThreadPoolConfig implements AsyncConfigurer { 21 | /** 22 | * 项目共用线程池 23 | */ 24 | public static final String CHATHUB_EXECUTOR = "chathubExecutor"; 25 | /** 26 | * websocket通信线程池 27 | */ 28 | public static final String WS_EXECUTOR = "websocketExecutor"; 29 | 30 | @Override 31 | public Executor getAsyncExecutor() { 32 | return chathubExecutor(); 33 | } 34 | 35 | @Bean(CHATHUB_EXECUTOR) 36 | @Primary 37 | public ThreadPoolTaskExecutor chathubExecutor() { 38 | ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); 39 | executor.setCorePoolSize(16); // 核心线程数 40 | executor.setMaxPoolSize(16); // 最大线程数 41 | executor.setQueueCapacity(200); // 队列大小 42 | executor.setThreadNamePrefix("chathub-executor-"); 43 | executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); // 满了调用线程执行,认为重要任务 44 | executor.initialize(); 45 | return executor; 46 | } 47 | 48 | @Bean(WS_EXECUTOR) 49 | public ThreadPoolTaskExecutor websocketExecutor() { 50 | ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); 51 | executor.setCorePoolSize(16); 52 | executor.setMaxPoolSize(16); 53 | executor.setQueueCapacity(1000); // 支持同时推送1000人 54 | executor.setThreadNamePrefix("websocket-executor-"); 55 | executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy()); //满了直接丢弃,默认为不重要消息推送 56 | executor.initialize(); 57 | return executor; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /chathub-common/src/main/java/bupt/edu/jhc/chathub/common/domain/constants/MDCKeys.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.common.domain.constants; 2 | 3 | /** 4 | * @Description: MDC 的 key 常量 5 | * @Author: JollyCorivuG 6 | * @CreateTime: 2024/1/16 7 | */ 8 | public class MDCKeys { 9 | public static final String TID = "tid"; 10 | public static final String UID = "uid"; 11 | } 12 | -------------------------------------------------------------------------------- /chathub-common/src/main/java/bupt/edu/jhc/chathub/common/domain/constants/MqConstants.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.common.domain.constants; 2 | 3 | /** 4 | * @Description: MQ 常量 5 | * @Author: JollyCorivuG 6 | * @CreateTime: 2024/1/22 7 | */ 8 | public class MqConstants { 9 | // 跟feeds流相关消息队列的常量 10 | public static final String FEEDS_EXCHANGE = "feeds_exchange"; 11 | public static final String FEEDS_QUEUE = "feeds_queue"; 12 | public static final String FEEDS_ROUTING_KEY = "feeds_routing_key"; 13 | 14 | // 跟点赞相关消息队列的常量 15 | public static final String LIKE_EXCHANGE = "like_exchange"; 16 | public static final String LIKE_QUEUE = "like_queue"; 17 | public static final String LIKE_ROUTING_KEY = "like_routing_key"; 18 | } 19 | -------------------------------------------------------------------------------- /chathub-common/src/main/java/bupt/edu/jhc/chathub/common/domain/constants/RedisConstants.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.common.domain.constants; 2 | 3 | /** 4 | * @Description: redis 常量 5 | * @Author: JollyCorivuG 6 | * @CreateTime: 2024/1/16 7 | */ 8 | public class RedisConstants { 9 | // token保存30min 10 | public static final String USER_TOKEN_KEY = "user:token:"; 11 | public static final Integer USER_TOKEN_KEY_TTL = 30; 12 | 13 | // 手机验证码保存1min 14 | public static final String PHONE_CODE_KEY = "phone:code:"; 15 | public static final Integer PHONE_CODE_KEY_TTL = 1; 16 | 17 | // 记录在线用户 18 | public static final String ID_TO_TOKEN = "token:id:"; 19 | public static final String ONLINE_USER_KEY = "online:user"; 20 | 21 | // 记录用户的好友 22 | public static final String USER_FRIEND_KEY = "user:friend:"; 23 | 24 | // 缓存查询用户 25 | public static final String CACHE_QUERY_USER_KET = "cache:users:"; 26 | public static final Integer CACHE_QUERY_USER_KEY_TTL = 2; 27 | 28 | // 记录用户对于一个房间最新的已读消息id, key为用户id:房间id 29 | public static final String USER_READ_LATEST_MESSAGE = "user:read:latest:message:"; 30 | 31 | // 记录每个房间最新的消息id, key为房间id 32 | public static final String ROOM_LATEST_MESSAGE = "room:latest:message:"; 33 | 34 | // 记录用户删除一个会话时, 当前会话的最新消息id, key为用户id:会话id 35 | public static final String USER_DELETE_LATEST_MESSAGE = "user:delete:latest:message:"; 36 | 37 | public static final String USER_ROOM_INFO = "user:room:info:"; 38 | 39 | 40 | // 记录某个说说最新点赞的用户id, key为说说id 41 | public static final String TALK_LATEST_LIKE = "talk:latest:like:"; 42 | } 43 | -------------------------------------------------------------------------------- /chathub-common/src/main/java/bupt/edu/jhc/chathub/common/domain/constants/SystemConstants.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.common.domain.constants; 2 | 3 | /** 4 | * @Description: 系统常量 5 | * @Author: JollyCorivuG 6 | * @CreateTime: 2024/1/18 7 | */ 8 | public class SystemConstants { 9 | // 响应码 0:成功 1:失败 10 | public static final Integer SUCCESS_REQUEST = 0; 11 | public static final Integer COMMON_ERROR = 1; 12 | 13 | // 默认用户前缀名 14 | public static final String DEFAULT_NICK_NAME_PREFIX = "user_"; 15 | 16 | // 默认用户头像 17 | public static final String DEV_DEFAULT_USER_AVATAR_URL = "/src/assets/images/avatar/default_user_avatar.jpg"; 18 | public static final String PROD_DEFAULT_USER_AVATAR_URL = "/assets/images/avatar/default_user_avatar.jpg"; 19 | 20 | // 查询用户的默认分页参数 21 | public static final Integer DEFAULT_PAGE_SIZE = 20; 22 | 23 | // 好友通知的一些常量 24 | public static final Integer NOTICE_TYPE_ADD_OTHER = 0; // 申请添加好友 25 | public static final Integer NOTICE_TYPE_OTHER_ADD_ME = 1; // 对方申请添加好友 26 | public static final Integer NOTICE_STATUS_WAIT = 0; // 待处理 27 | public static final Integer NOTICE_STATUS_PASS = 1; // 已通过 28 | public static final Integer NOTICE_STATUS_NOT_PASS = 2; // 未通过 29 | public static final Integer NOTICE_STATUS_REFUSE = 3; // 已拒绝 30 | public static final Integer NOTICE_STATUS_ACCEPT = 4; // 已接受 31 | public static final Integer NOTICE_STATUS_PENDING = 5; // 等待处理 32 | 33 | // 群组通知的一些常量 34 | public static final Integer GROUP_NOTICE_STATUS_PENDING = 0; // 等待处理 35 | public static final Integer GROUP_NOTICE_STATUS_AGREE = 1; // 已同意 36 | public static final Integer GROUP_NOTICE_STATUS_REFUSE = 2; // 已拒绝 37 | 38 | // 消息类型 39 | public static final Integer TEXT_MSG = 0; 40 | public static final Integer IMG_MSG = 1; 41 | public static final Integer FILE_MSG = 2; 42 | 43 | // WS响应消息类型 44 | public static final Integer WS_NORMAL_MSG = 0; // 普通消息 45 | public static final Integer WS_HEAD_SHAKE_SUCCESS_MSG = 1; // 握手成功消息 46 | public static final Integer WS_HEAD_SHAKE_FAIL_MSG = 2; // 握手失败消息 47 | 48 | // room类型 49 | public static final Integer ROOM_TYPE_SINGLE = 0; // 单聊 50 | public static final Integer ROOM_TYPE_GROUP = 1; // 群聊 51 | 52 | // 说说的额外内容的类型, 0表示图片, 1表示视频 53 | public static final Integer TALK_EXTRA_TYPE_IMG = 0; 54 | public static final Integer TALK_EXTRA_TYPE_VIDEO = 1; 55 | } 56 | -------------------------------------------------------------------------------- /chathub-common/src/main/java/bupt/edu/jhc/chathub/common/domain/enums/ErrorStatus.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.common.domain.enums; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | 6 | /** 7 | * @Description: 错误状态枚举 8 | * @Author: JollyCorivuG 9 | * @CreateTime: 2024/1/18 10 | */ 11 | @Getter 12 | @AllArgsConstructor 13 | public enum ErrorStatus { 14 | PARAMS_ERROR(40000, "请求参数错误"), 15 | NOT_LOGIN_ERROR(40100, "未登录"), 16 | NO_AUTH_ERROR(40101, "无权限"), 17 | NOT_FOUND_ERROR(40400, "请求数据不存在"), 18 | FORBIDDEN_ERROR(40300, "禁止访问"), 19 | SYSTEM_ERROR(50000, "系统内部异常"), 20 | OPERATION_ERROR(50001, "操作失败"), 21 | ; 22 | 23 | private final Integer code; 24 | private final String message; 25 | } 26 | -------------------------------------------------------------------------------- /chathub-common/src/main/java/bupt/edu/jhc/chathub/common/domain/vo/request/CursorPageBaseReq.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.common.domain.vo.request; 2 | 3 | import cn.hutool.core.util.StrUtil; 4 | import com.fasterxml.jackson.annotation.JsonIgnore; 5 | import io.swagger.annotations.ApiModel; 6 | import io.swagger.annotations.ApiModelProperty; 7 | import lombok.Data; 8 | import lombok.experimental.Accessors; 9 | 10 | /** 11 | * @Description: 游标分页基础请求 12 | * @Author: JollyCorivuG 13 | * @CreateTime: 2024/1/18 14 | */ 15 | @Data 16 | @Accessors(chain = true) 17 | @ApiModel("游标分页基础请求") 18 | public class CursorPageBaseReq { 19 | @ApiModelProperty("每页大小") 20 | private Integer pageSize = 20; // 每页大小默认 20 21 | @ApiModelProperty("游标") 22 | private String cursor; // 游标 23 | 24 | @JsonIgnore 25 | public Boolean isFirstPage() { 26 | return StrUtil.isBlank(cursor); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /chathub-common/src/main/java/bupt/edu/jhc/chathub/common/domain/vo/resp/CursorPageBaseResp.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.common.domain.vo.resp; 2 | 3 | import cn.hutool.core.collection.CollectionUtil; 4 | import com.fasterxml.jackson.annotation.JsonIgnore; 5 | import io.swagger.annotations.ApiModel; 6 | import io.swagger.annotations.ApiModelProperty; 7 | import lombok.AllArgsConstructor; 8 | import lombok.Data; 9 | import lombok.NoArgsConstructor; 10 | import lombok.experimental.Accessors; 11 | 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | 15 | /** 16 | * @Description: 游标分页响应类 17 | * @Author: JollyCorivuG 18 | * @CreateTime: 2024/1/18 19 | */ 20 | @Data 21 | @Accessors(chain = true) 22 | @ApiModel("游标分页响应") 23 | @AllArgsConstructor 24 | @NoArgsConstructor 25 | public class CursorPageBaseResp { 26 | @ApiModelProperty("是否最后一页") 27 | private Boolean isLast = Boolean.FALSE; // 是否最后一页 28 | @ApiModelProperty("游标 (下次翻页时需要带上的参数)") 29 | private String cursor; // 游标 (下次翻页时需要带上的参数) 30 | @ApiModelProperty("数据列表") 31 | private List list; // 数据列表 32 | 33 | public static CursorPageBaseResp change(CursorPageBaseResp cursorPage, List list) { 34 | CursorPageBaseResp cursorPageBaseResp = new CursorPageBaseResp<>(); 35 | cursorPageBaseResp 36 | .setIsLast(cursorPage.getIsLast()) 37 | .setList(list) 38 | .setCursor(cursorPage.getCursor()); 39 | return cursorPageBaseResp; 40 | } 41 | 42 | @JsonIgnore 43 | public Boolean isEmpty() { 44 | return CollectionUtil.isEmpty(list); 45 | } 46 | 47 | public static CursorPageBaseResp empty() { 48 | CursorPageBaseResp cursorPageBaseResp = new CursorPageBaseResp<>(); 49 | cursorPageBaseResp 50 | .setIsLast(true) 51 | .setList(new ArrayList<>()); 52 | return cursorPageBaseResp; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /chathub-common/src/main/java/bupt/edu/jhc/chathub/common/domain/vo/resp/Response.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.common.domain.vo.resp; 2 | 3 | import bupt.edu.jhc.chathub.common.domain.constants.SystemConstants; 4 | import bupt.edu.jhc.chathub.common.domain.enums.ErrorStatus; 5 | import io.swagger.annotations.ApiModel; 6 | import io.swagger.annotations.ApiModelProperty; 7 | import lombok.Data; 8 | import lombok.experimental.Accessors; 9 | 10 | /** 11 | * @Description: 通用响应类 12 | * @Author: JollyCorivuG 13 | * @CreateTime: 2024/1/18 14 | */ 15 | @Data 16 | @Accessors(chain = true) 17 | @ApiModel("基础响应体") 18 | public class Response { 19 | @ApiModelProperty("状态码 0-成功 其他值-失败") 20 | private Integer statusCode; 21 | @ApiModelProperty("状态信息") 22 | private String statusMsg; 23 | @ApiModelProperty("响应数据") 24 | private T data; 25 | public static Response success(T data) { 26 | Response response = new Response<>(); 27 | return response 28 | .setStatusCode(SystemConstants.SUCCESS_REQUEST) 29 | .setStatusMsg("成功") 30 | .setData(data); 31 | } 32 | public static Response fail(ErrorStatus errorStatus) { 33 | Response response = new Response<>(); 34 | return response 35 | .setStatusCode(errorStatus.getCode()) 36 | .setStatusMsg(errorStatus.getMessage()); 37 | } 38 | public static Response fail(Integer code, String message) { 39 | Response response = new Response<>(); 40 | return response 41 | .setStatusCode(code) 42 | .setStatusMsg(message); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /chathub-common/src/main/java/bupt/edu/jhc/chathub/common/interceptor/CheckTokenInterceptor.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.common.interceptor; 2 | 3 | import bupt.edu.jhc.chathub.common.domain.constants.MDCKeys; 4 | import bupt.edu.jhc.chathub.common.utils.context.RequestHolder; 5 | import org.slf4j.MDC; 6 | import org.springframework.lang.NonNull; 7 | import org.springframework.stereotype.Component; 8 | import org.springframework.web.servlet.HandlerInterceptor; 9 | 10 | import javax.servlet.http.HttpServletRequest; 11 | import javax.servlet.http.HttpServletResponse; 12 | import java.util.Objects; 13 | 14 | /** 15 | * @Description: 校验 token 拦截器 16 | * @Author: JollyCorivuG 17 | * @CreateTime: 2024/1/16 18 | */ 19 | @Component 20 | public class CheckTokenInterceptor implements HandlerInterceptor { 21 | @Override 22 | public boolean preHandle(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull Object handler) { 23 | if (Objects.isNull(RequestHolder.get())) { 24 | response.setStatus(401); 25 | return false; 26 | } 27 | return true; 28 | } 29 | 30 | @Override 31 | public void afterCompletion(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull Object handler, Exception ex) { 32 | MDC.remove(MDCKeys.UID); 33 | RequestHolder.remove(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /chathub-common/src/main/java/bupt/edu/jhc/chathub/common/interceptor/RefreshTokenInterceptor.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.common.interceptor; 2 | 3 | import bupt.edu.jhc.chathub.common.utils.context.RequestInfo; 4 | import bupt.edu.jhc.chathub.common.domain.constants.MDCKeys; 5 | import bupt.edu.jhc.chathub.common.domain.constants.RedisConstants; 6 | import bupt.edu.jhc.chathub.common.utils.context.RequestHolder; 7 | import cn.hutool.core.bean.BeanUtil; 8 | import cn.hutool.core.util.StrUtil; 9 | import org.slf4j.MDC; 10 | import org.springframework.data.redis.core.StringRedisTemplate; 11 | import org.springframework.lang.NonNull; 12 | import org.springframework.stereotype.Component; 13 | import org.springframework.web.servlet.HandlerInterceptor; 14 | 15 | import javax.servlet.http.HttpServletRequest; 16 | import javax.servlet.http.HttpServletResponse; 17 | import java.util.Map; 18 | import java.util.concurrent.TimeUnit; 19 | 20 | /** 21 | * @Description: 刷新 token 拦截器 22 | * @Author: JollyCorivuG 23 | * @CreateTime: 2024/1/16 24 | */ 25 | @Component 26 | public class RefreshTokenInterceptor implements HandlerInterceptor { 27 | private final StringRedisTemplate stringRedisTemplate; 28 | 29 | public RefreshTokenInterceptor(StringRedisTemplate stringRedisTemplate) { 30 | this.stringRedisTemplate = stringRedisTemplate; 31 | } 32 | 33 | private static final String TOKEN_KET = "token"; 34 | 35 | @Override 36 | public boolean preHandle(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull Object handler) { 37 | // 1.获取请求头中的 token 或者 url 中的 token 38 | String token = request.getHeader(TOKEN_KET); 39 | if (StrUtil.isBlank(token)) { 40 | token = request.getParameter(TOKEN_KET); 41 | } 42 | if (StrUtil.isBlank(token)) { 43 | return true; 44 | } 45 | 46 | // 2.基于 token 从 redis 中获取请求信息 47 | Map requestInfoMap = stringRedisTemplate.opsForHash().entries(RedisConstants.USER_TOKEN_KEY + token); 48 | if (requestInfoMap.isEmpty()) { 49 | stringRedisTemplate.opsForSet().remove(RedisConstants.ONLINE_USER_KEY, token); 50 | return true; 51 | } 52 | 53 | // 3.将 uid 记录到日志 54 | RequestInfo requestInfo = BeanUtil.fillBeanWithMap(requestInfoMap, new RequestInfo(), false); 55 | MDC.put(MDCKeys.UID, String.valueOf(requestInfo.getUid())); 56 | 57 | // 4.保存到 ThreadLocal 中 58 | RequestHolder.save(requestInfo); 59 | 60 | // 5.刷新 token 的过期时间 61 | stringRedisTemplate.expire(RedisConstants.USER_TOKEN_KEY + token, RedisConstants.USER_TOKEN_KEY_TTL, TimeUnit.MINUTES); 62 | 63 | // 6.放行 64 | return true; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /chathub-common/src/main/java/bupt/edu/jhc/chathub/common/service/cache/BatchCache.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.common.service.cache; 2 | 3 | import java.util.List; 4 | import java.util.Map; 5 | 6 | /** 7 | * @Description: 批量缓存接口 8 | * @Author: JollyCorivuG 9 | * @CreateTime: 2024/2/11 10 | */ 11 | public interface BatchCache { 12 | /** 13 | * 获取单个 14 | * @param req 15 | * @return OUT 16 | */ 17 | OUT get(IN req); 18 | 19 | /** 20 | * 获取批量 21 | * @param req 22 | * @return Map 23 | */ 24 | Map getBatch(List req); 25 | 26 | /** 27 | * 删除单个 28 | * @param req 29 | */ 30 | void del(IN req); 31 | 32 | /** 33 | * 删除多个 34 | * @param req 35 | */ 36 | void delBatch(List req); 37 | } 38 | -------------------------------------------------------------------------------- /chathub-common/src/main/java/bupt/edu/jhc/chathub/common/utils/CursorUtils.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.common.utils; 2 | 3 | import bupt.edu.jhc.chathub.common.domain.vo.request.CursorPageBaseReq; 4 | import bupt.edu.jhc.chathub.common.domain.vo.resp.CursorPageBaseResp; 5 | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; 6 | import com.baomidou.mybatisplus.core.toolkit.support.SFunction; 7 | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; 8 | import com.baomidou.mybatisplus.extension.service.IService; 9 | 10 | import java.util.Optional; 11 | import java.util.function.Consumer; 12 | 13 | /** 14 | * @Description: 游标翻页工具类 15 | * @Author: JollyCorivuG 16 | * @CreateTime: 2024/1/18 17 | */ 18 | public class CursorUtils { 19 | public static CursorPageBaseResp getCursorPageByMysql(IService mapper, CursorPageBaseReq req, 20 | Consumer> initWrapper, SFunction cursorColumn) { 21 | // 1. 初始化 wrapper 22 | LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); 23 | initWrapper.accept(wrapper); 24 | if (!req.isFirstPage()) { 25 | wrapper.lt(cursorColumn, req.getCursor()); 26 | } 27 | 28 | // 2. 排序 29 | wrapper.orderByDesc(cursorColumn); 30 | Page page = mapper.page(new Page(1, req.getPageSize()).setSearchCount(false), wrapper); 31 | if (page.getRecords().isEmpty()) { 32 | return CursorPageBaseResp.empty(); 33 | } 34 | 35 | // 3. 获取游标 36 | String cursor = Optional.ofNullable(page.getRecords().get(page.getRecords().size() - 1)) 37 | .map(cursorColumn) 38 | .map(String::valueOf) 39 | .orElse(null); 40 | Boolean isLast = page.getRecords().size() != req.getPageSize(); 41 | return new CursorPageBaseResp<>(isLast, cursor, page.getRecords()); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /chathub-common/src/main/java/bupt/edu/jhc/chathub/common/utils/JsonUtils.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.common.utils; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.fasterxml.jackson.databind.JsonNode; 5 | import com.fasterxml.jackson.databind.ObjectMapper; 6 | 7 | /** 8 | * @Description: json 工具类 9 | * @Author: JollyCorivuG 10 | * @CreateTime: 2024/2/11 11 | */ 12 | public class JsonUtils { 13 | private static final ObjectMapper jsonMapper = new ObjectMapper(); 14 | 15 | public static T toObj(String str, Class clz) { 16 | try { 17 | return jsonMapper.readValue(str, clz); 18 | } catch (JsonProcessingException e) { 19 | throw new UnsupportedOperationException(e); 20 | } 21 | } 22 | 23 | public static JsonNode toJsonNode(String str) { 24 | try { 25 | return jsonMapper.readTree(str); 26 | } catch (JsonProcessingException e) { 27 | throw new UnsupportedOperationException(e); 28 | } 29 | } 30 | 31 | public static String toStr(Object t) { 32 | try { 33 | return jsonMapper.writeValueAsString(t); 34 | } catch (Exception e) { 35 | throw new UnsupportedOperationException(e); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /chathub-common/src/main/java/bupt/edu/jhc/chathub/common/utils/PasswordEncoder.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.common.utils; 2 | 3 | import bupt.edu.jhc.chathub.common.domain.enums.ErrorStatus; 4 | import bupt.edu.jhc.chathub.common.utils.exception.ThrowUtils; 5 | import cn.hutool.core.util.RandomUtil; 6 | import org.springframework.util.DigestUtils; 7 | 8 | import java.nio.charset.StandardCharsets; 9 | import java.util.Objects; 10 | 11 | /** 12 | * @Description: 密码加密工具类 13 | * @Author: JollyCorivuG 14 | * @CreateTime: 2024/1/18 15 | */ 16 | public class PasswordEncoder { 17 | public static String encode(String password) { 18 | String salt = RandomUtil.randomString(20); 19 | return encode(password,salt); 20 | } 21 | private static String encode(String password, String salt) { 22 | return salt + "@" + DigestUtils.md5DigestAsHex((password + salt).getBytes(StandardCharsets.UTF_8)); 23 | } 24 | public static Boolean matches(String encodedPassword, String rawPassword) { 25 | if (Objects.isNull(encodedPassword) || Objects.isNull(rawPassword)) { 26 | return false; 27 | } 28 | ThrowUtils.throwIf(!encodedPassword.contains("@"), ErrorStatus.PARAMS_ERROR, "密码格式不正确!"); 29 | String[] arr = encodedPassword.split("@"); 30 | String salt = arr[0]; 31 | return encodedPassword.equals(encode(rawPassword, salt)); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /chathub-common/src/main/java/bupt/edu/jhc/chathub/common/utils/context/RequestHolder.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.common.utils.context; 2 | 3 | /** 4 | * @Description: 记录请求上下文 5 | * @Author: JollyCorivuG 6 | * @CreateTime: 2024/1/16 7 | */ 8 | public class RequestHolder { 9 | private static final ThreadLocal tl = new ThreadLocal<>(); 10 | 11 | /** 12 | * 保存请求信息 13 | * @param info 14 | */ 15 | public static void save(RequestInfo info){ 16 | tl.set(info); 17 | } 18 | 19 | /** 20 | * 获取请求信息 21 | * @return 请求信息 22 | */ 23 | public static RequestInfo get(){ 24 | return tl.get(); 25 | } 26 | 27 | /** 28 | * 移除请求信息 29 | */ 30 | public static void remove(){ 31 | tl.remove(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /chathub-common/src/main/java/bupt/edu/jhc/chathub/common/utils/context/RequestInfo.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.common.utils.context; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | /** 9 | * @Description: 上下文请求信息 10 | * @Author: JollyCorivuG 11 | * @CreateTime: 2024/1/16 12 | */ 13 | @Data 14 | @Builder 15 | @AllArgsConstructor 16 | @NoArgsConstructor 17 | public class RequestInfo { 18 | private Long uid; // 用户id 19 | } 20 | -------------------------------------------------------------------------------- /chathub-common/src/main/java/bupt/edu/jhc/chathub/common/utils/exception/BizException.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.common.utils.exception; 2 | 3 | import bupt.edu.jhc.chathub.common.domain.enums.ErrorStatus; 4 | import lombok.Getter; 5 | 6 | /** 7 | * @Description: 业务异常类 8 | * @Author: JollyCorivuG 9 | * @CreateTime: 2024/1/18 10 | */ 11 | @Getter 12 | public class BizException extends RuntimeException { 13 | private final Integer code; 14 | 15 | public BizException(ErrorStatus errorStatus) { 16 | super(errorStatus.getMessage()); 17 | this.code = errorStatus.getCode(); 18 | } 19 | 20 | public BizException(ErrorStatus errorStatus, String message) { 21 | super(message); 22 | this.code = errorStatus.getCode(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /chathub-common/src/main/java/bupt/edu/jhc/chathub/common/utils/exception/GlobalExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.common.utils.exception; 2 | 3 | import bupt.edu.jhc.chathub.common.domain.enums.ErrorStatus; 4 | import bupt.edu.jhc.chathub.common.domain.vo.resp.Response; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.springframework.web.bind.annotation.ExceptionHandler; 7 | import org.springframework.web.bind.annotation.RestControllerAdvice; 8 | 9 | 10 | /** 11 | * @Description: 全局异常处理 12 | * @Author: JollyCorivuG 13 | * @CreateTime: 2024/1/18 14 | */ 15 | @Slf4j 16 | @RestControllerAdvice 17 | public class GlobalExceptionHandler { 18 | /** 19 | * 系统异常处理 20 | * @param e 21 | * @return Response 22 | */ 23 | @ExceptionHandler(RuntimeException.class) 24 | public Response runtimeExceptionHandler(RuntimeException e) { 25 | log.error("System exception!The reason is: {}", e.getMessage()); 26 | return Response.fail(ErrorStatus.SYSTEM_ERROR); 27 | } 28 | 29 | /** 30 | * 业务异常处理 31 | * @param e 32 | * @return Response 33 | */ 34 | @ExceptionHandler(BizException.class) 35 | public Response bizExceptionHandler(BizException e) { 36 | log.error("Business exception!The reason is: {}", e.getMessage()); 37 | return Response.fail(e.getCode(), e.getMessage()); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /chathub-common/src/main/java/bupt/edu/jhc/chathub/common/utils/exception/ThrowUtils.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.common.utils.exception; 2 | 3 | import bupt.edu.jhc.chathub.common.domain.enums.ErrorStatus; 4 | 5 | /** 6 | * @Description: 抛出异常工具类 7 | * @Author: JollyCorivuG 8 | * @CreateTime: 2024/1/18 9 | */ 10 | public class ThrowUtils { 11 | /** 12 | * 条件成立则抛异常 13 | * @param condition 14 | * @param bizException 15 | */ 16 | public static void throwIf(boolean condition, BizException bizException) { 17 | if (condition) { 18 | throw bizException; 19 | } 20 | } 21 | public static void throwIf(boolean condition, ErrorStatus errorStatus) { 22 | throwIf(condition, new BizException(errorStatus)); 23 | } 24 | public static void throwIf(boolean condition, ErrorStatus errorStatus, String message) { 25 | throwIf(condition, new BizException(errorStatus, message)); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /chathub-common/src/main/java/bupt/edu/jhc/chathub/common/utils/regex/RegexPatterns.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.common.utils.regex; 2 | 3 | /** 4 | * @Description: 正则表达式 5 | * @Author: JollyCorivuG 6 | * @CreateTime: 2024/1/18 7 | */ 8 | public abstract class RegexPatterns { 9 | /** 10 | * 手机号正则 11 | */ 12 | public static final String PHONE_REGEX = "^1[3-9]\\d{9}$"; 13 | /** 14 | * 账号正则,10位数字 15 | */ 16 | public static final String ACCOUNT_REGEX = "^\\d{10}$"; 17 | } 18 | -------------------------------------------------------------------------------- /chathub-common/src/main/java/bupt/edu/jhc/chathub/common/utils/regex/RegexUtils.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.common.utils.regex; 2 | 3 | import cn.hutool.core.util.StrUtil; 4 | 5 | /** 6 | * @Description: 正则工具类 7 | * @Author: JollyCorivuG 8 | * @CreateTime: 2024/1/18 9 | */ 10 | public class RegexUtils { 11 | public static boolean isPhoneValid(String phone){ 12 | return match(phone, RegexPatterns.PHONE_REGEX); 13 | } 14 | public static boolean isAccountValid(String account){ 15 | return match(account, RegexPatterns.ACCOUNT_REGEX); 16 | } 17 | 18 | // 校验是否不符合正则格式 19 | private static boolean match(String str, String regex){ 20 | if (StrUtil.isBlank(str)) { 21 | return true; 22 | } 23 | return str.matches(regex); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /chathub-server/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | bupt.edu.jhc 8 | chathub 9 | 1.0-SNAPSHOT 10 | 11 | 12 | chathub-server 13 | 14 | 15 | 8 16 | 8 17 | UTF-8 18 | 19 | 20 | 21 | 22 | bupt.edu.jhc 23 | chathub-common 24 | 1.0-SNAPSHOT 25 | 26 | 27 | bupt.edu.jhc 28 | chathub-oss-starter 29 | 1.0-SNAPSHOT 30 | 31 | 32 | bupt.edu.jhc 33 | chathub-frequency-control 34 | 1.0-SNAPSHOT 35 | 36 | 37 | mysql 38 | mysql-connector-java 39 | 40 | 41 | io.netty 42 | netty-all 43 | 44 | 45 | org.springframework.boot 46 | spring-boot-starter-amqp 47 | 48 | 49 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/ChathubApplication.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server; 2 | 3 | import org.mybatis.spring.annotation.MapperScan; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | import org.springframework.boot.web.servlet.ServletComponentScan; 7 | import org.springframework.context.annotation.EnableAspectJAutoProxy; 8 | 9 | /** 10 | * @Description: 主启动类 11 | * @Author: JollyCorivuG 12 | * @CreateTime: 2024/1/16 13 | */ 14 | @SpringBootApplication(scanBasePackages = "bupt.edu.jhc.chathub") 15 | @EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true) 16 | @ServletComponentScan 17 | @MapperScan({"bupt.edu.jhc.chathub.server.**.mapper"}) 18 | public class ChathubApplication { 19 | public static void main(String[] args) { 20 | SpringApplication.run(ChathubApplication.class, args); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/chat/controller/MessageController.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.chat.controller; 2 | 3 | import bupt.edu.jhc.chathub.common.domain.vo.resp.CursorPageBaseResp; 4 | import bupt.edu.jhc.chathub.common.domain.vo.resp.Response; 5 | import bupt.edu.jhc.chathub.common.utils.context.RequestHolder; 6 | import bupt.edu.jhc.chathub.frequency_control.annotation.FrequencyControl; 7 | import bupt.edu.jhc.chathub.server.chat.domain.dto.MsgPageReq; 8 | import bupt.edu.jhc.chathub.server.chat.domain.dto.SendMsgDTO; 9 | import bupt.edu.jhc.chathub.server.chat.domain.vo.RoomVO; 10 | import bupt.edu.jhc.chathub.server.chat.domain.vo.ShowMsgVO; 11 | import bupt.edu.jhc.chathub.server.chat.service.IMessageService; 12 | import io.swagger.annotations.Api; 13 | import io.swagger.annotations.ApiOperation; 14 | import org.springframework.web.bind.annotation.*; 15 | 16 | import javax.annotation.Resource; 17 | import java.util.List; 18 | 19 | /** 20 | * @Description: 消息接口 21 | * @Author: JollyCorivuG 22 | * @CreateTime: 2024/1/21 23 | */ 24 | @RestController 25 | @RequestMapping("/api/messages") 26 | @Api(tags = "消息相关接口") 27 | public class MessageController { 28 | @Resource 29 | private IMessageService messageService; 30 | 31 | @PostMapping("/send") 32 | @ApiOperation("发送消息") 33 | @FrequencyControl(time = 5, count = 3, target = FrequencyControl.Target.UID) 34 | @FrequencyControl(time = 30, count = 5, target = FrequencyControl.Target.UID) 35 | @FrequencyControl(time = 60, count = 10, target = FrequencyControl.Target.UID) 36 | public Response sendMessage(@RequestBody SendMsgDTO sendMsg) { 37 | Long msgId = messageService.sendMsg(RequestHolder.get().getUid(), sendMsg); 38 | return Response.success(messageService.convertToShowMsgVO(messageService.getById(msgId))); 39 | } 40 | 41 | @GetMapping("/list") 42 | @ApiOperation("分页获取消息列表") 43 | public Response> getMessageList(MsgPageReq msgPageReq) { 44 | CursorPageBaseResp msgPage = messageService.getMsgPage(msgPageReq); 45 | return Response.success(msgPage); 46 | } 47 | 48 | @GetMapping("/room") 49 | @ApiOperation("获取房间列表") 50 | public Response> getRoomList() { 51 | Long userId = RequestHolder.get().getUid(); 52 | List roomList = messageService.getRoomList(userId); 53 | return Response.success(roomList); 54 | } 55 | 56 | @DeleteMapping("/room/{id}") 57 | @ApiOperation("删除房间") 58 | public Response deleteRoom(@PathVariable Long id) { 59 | Long userId = RequestHolder.get().getUid(); 60 | messageService.deleteRoom(userId, id); 61 | return Response.success(null); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/chat/domain/dto/FileMsgDTO.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.chat.domain.dto; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | import lombok.experimental.Accessors; 7 | 8 | /** 9 | * @Description: 文件消息 dto 10 | * @Author: JollyCorivuG 11 | * @CreateTime: 2024/1/21 12 | */ 13 | @Data 14 | @Accessors(chain = true) 15 | @ApiModel("文件消息") 16 | public class FileMsgDTO { 17 | @ApiModelProperty("文件大小") 18 | private Long size; 19 | @ApiModelProperty("文件类型") 20 | private String url; 21 | @ApiModelProperty("文件名") 22 | private String fileName; 23 | } 24 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/chat/domain/dto/ImgMsgDTO.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.chat.domain.dto; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | import lombok.experimental.Accessors; 7 | 8 | /** 9 | * @Description: 图片消息 dto 10 | * @Author: JollyCorivuG 11 | * @CreateTime: 2024/1/21 12 | */ 13 | @Data 14 | @Accessors(chain = true) 15 | @ApiModel("图片消息") 16 | public class ImgMsgDTO { 17 | @ApiModelProperty("图片大小") 18 | private Long size; 19 | @ApiModelProperty("图片宽度") 20 | private Integer width; 21 | @ApiModelProperty("图片高度") 22 | private Integer height; 23 | @ApiModelProperty("图片地址") 24 | private String url; 25 | } -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/chat/domain/dto/MessageExtra.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.chat.domain.dto; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import io.swagger.annotations.ApiModel; 5 | import io.swagger.annotations.ApiModelProperty; 6 | import lombok.Data; 7 | import lombok.experimental.Accessors; 8 | 9 | /** 10 | * @Description: 消息额外内容 11 | * @Author: JollyCorivuG 12 | * @CreateTime: 2024/1/21 13 | */ 14 | @Data 15 | @Accessors(chain = true) 16 | @JsonIgnoreProperties(ignoreUnknown = true) 17 | @ApiModel("消息额外内容") 18 | public class MessageExtra { 19 | @ApiModelProperty("图片消息") 20 | private ImgMsgDTO imgMsg; // 图片消息 21 | @ApiModelProperty("文件消息") 22 | private FileMsgDTO fileMsg; // 文件消息 23 | } 24 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/chat/domain/dto/MsgPageReq.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.chat.domain.dto; 2 | 3 | import bupt.edu.jhc.chathub.common.domain.vo.request.CursorPageBaseReq; 4 | import io.swagger.annotations.ApiModel; 5 | import io.swagger.annotations.ApiModelProperty; 6 | import lombok.Data; 7 | import lombok.EqualsAndHashCode; 8 | 9 | /** 10 | * @Description: 消息翻页请求 11 | * @Author: JollyCorivuG 12 | * @CreateTime: 2024/1/21 13 | */ 14 | @Data 15 | @EqualsAndHashCode(callSuper = true) 16 | @ApiModel("消息翻页请求") 17 | public class MsgPageReq extends CursorPageBaseReq { 18 | @ApiModelProperty("房间 id") 19 | private Long roomId; 20 | } 21 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/chat/domain/dto/SendMsgDTO.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.chat.domain.dto; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | import lombok.experimental.Accessors; 7 | 8 | /** 9 | * @Description: 发送消息请求 10 | * @Author: JollyCorivuG 11 | * @CreateTime: 2024/1/21 12 | */ 13 | @Data 14 | @Accessors(chain = true) 15 | @ApiModel("发送消息请求") 16 | public class SendMsgDTO { 17 | @ApiModelProperty("房间 id") 18 | private Long roomId; 19 | @ApiModelProperty("消息类型 0-文本消息 1-图片消息 2-文件消息") 20 | private Integer msgType; 21 | @ApiModelProperty("消息体") 22 | private Object body; // 消息的主体, 根据不同消息类型, 传不同的值 23 | } 24 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/chat/domain/dto/TextMsgDTO.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.chat.domain.dto; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | import lombok.experimental.Accessors; 7 | 8 | /** 9 | * @Description: 文本消息 dto 10 | * @Author: JollyCorivuG 11 | * @CreateTime: 2024/1/21 12 | */ 13 | @Data 14 | @Accessors(chain = true) 15 | @ApiModel("文本消息") 16 | public class TextMsgDTO { 17 | @ApiModelProperty("文本内容") 18 | private String content; 19 | } 20 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/chat/domain/entity/Message.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.chat.domain.entity; 2 | 3 | import bupt.edu.jhc.chathub.server.chat.domain.dto.MessageExtra; 4 | import com.baomidou.mybatisplus.annotation.IdType; 5 | import com.baomidou.mybatisplus.annotation.TableField; 6 | import com.baomidou.mybatisplus.annotation.TableId; 7 | import com.baomidou.mybatisplus.annotation.TableName; 8 | import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; 9 | import lombok.Data; 10 | import lombok.experimental.Accessors; 11 | 12 | import java.time.LocalDateTime; 13 | 14 | /** 15 | * @Description: 消息实体 16 | * @Author: JollyCorivuG 17 | * @CreateTime: 2024/1/21 18 | */ 19 | @Data 20 | @Accessors(chain = true) 21 | @TableName(value = "tb_message", autoResultMap = true) 22 | public class Message { 23 | @TableId(value = "id", type = IdType.AUTO) 24 | private Long id; 25 | @TableField(value = "room_id") 26 | private Long roomId; // 会话id 27 | @TableField(value = "from_user_id") 28 | private Long fromUserId; 29 | @TableField(value = "content") 30 | private String content; 31 | @TableField(value = "msg_type") 32 | private Integer msgType; // 消息类型 33 | @TableField(value = "extra", typeHandler = JacksonTypeHandler.class) 34 | private MessageExtra extra; // 消息的扩展字段,包括图片、文件、表情包 35 | @TableField(value = "create_time") 36 | private LocalDateTime createTime; 37 | @TableField(value = "update_time") 38 | private LocalDateTime updateTime; 39 | } 40 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/chat/domain/entity/Room.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.chat.domain.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableField; 5 | import com.baomidou.mybatisplus.annotation.TableId; 6 | import com.baomidou.mybatisplus.annotation.TableName; 7 | import lombok.Data; 8 | import lombok.experimental.Accessors; 9 | 10 | import java.time.LocalDateTime; 11 | 12 | /** 13 | * @Description: 房间实体 14 | * @Author: JollyCorivuG 15 | * @CreateTime: 2024/1/21 16 | */ 17 | @Data 18 | @Accessors(chain = true) 19 | @TableName(value = "tb_room") 20 | public class Room { 21 | @TableId(value = "id", type = IdType.AUTO) 22 | private Long id; 23 | @TableField(value = "room_type") 24 | private Integer roomType; 25 | @TableField(value = "latest_msg_id") 26 | private Long latestMsgId; 27 | @TableField(value = "create_time") 28 | private LocalDateTime createTime; 29 | @TableField(value = "update_time") 30 | private LocalDateTime updateTime; 31 | } 32 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/chat/domain/entity/UserRoom.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.chat.domain.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableField; 5 | import com.baomidou.mybatisplus.annotation.TableId; 6 | import com.baomidou.mybatisplus.annotation.TableName; 7 | import com.fasterxml.jackson.annotation.JsonIgnore; 8 | import lombok.Data; 9 | import lombok.experimental.Accessors; 10 | 11 | import java.time.LocalDateTime; 12 | 13 | /** 14 | * @Description: 用户与房间的关系 15 | * @Author: JollyCorivuG 16 | * @CreateTime: 2024/2/11 17 | */ 18 | @Data 19 | @Accessors(chain = true) 20 | @TableName(value = "tb_user_room") 21 | public class UserRoom { 22 | @TableId(value = "id", type = IdType.AUTO) 23 | private Long id; 24 | @TableField(value = "user_id") 25 | private Long userId; 26 | @TableField(value = "room_id") 27 | private Long roomId; 28 | @TableField(value = "latest_del_msg_id") 29 | private Long latestDelMsgId; 30 | @TableField(value = "latest_read_msg_id") 31 | private Long latestReadMsgId; 32 | 33 | @TableField(value = "create_time") 34 | @JsonIgnore 35 | private LocalDateTime createTime; 36 | @TableField(value = "update_time") 37 | @JsonIgnore 38 | private LocalDateTime updateTime; 39 | } 40 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/chat/domain/enums/MsgTypeEnum.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.chat.domain.enums; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | 6 | import java.util.Arrays; 7 | import java.util.Map; 8 | import java.util.function.Function; 9 | import java.util.stream.Collectors; 10 | 11 | /** 12 | * @Description: 消息类型枚举 13 | * @Author: JollyCorivuG 14 | * @CreateTime: 2024/1/21 15 | */ 16 | @AllArgsConstructor 17 | @Getter 18 | public enum MsgTypeEnum { 19 | TEXT(0, "文本消息"), 20 | IMG(1, "图片消息"), 21 | FILE(2, "文件消息"), 22 | ; 23 | 24 | private final Integer type; 25 | private final String description; 26 | 27 | private static final Map cache; 28 | 29 | static { 30 | cache = Arrays.stream(MsgTypeEnum.values()).collect(Collectors.toMap(MsgTypeEnum::getType, Function.identity())); 31 | } 32 | 33 | public static MsgTypeEnum of(Integer type) { 34 | return cache.get(type); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/chat/domain/vo/RoomVO.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.chat.domain.vo; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | import lombok.experimental.Accessors; 7 | 8 | /** 9 | * @Description: 房间 vo 10 | * @Author: JollyCorivuG 11 | * @CreateTime: 2024/1/21 12 | */ 13 | @Data 14 | @Accessors(chain = true) 15 | @ApiModel("房间") 16 | public class RoomVO { 17 | @ApiModelProperty("房间id") 18 | private Long id; 19 | @ApiModelProperty("房间类型") 20 | private Integer roomType; 21 | @ApiModelProperty("相关联信息") 22 | private Object connectInfo; // 如果是私聊,这里是对方用户信息; 如果是群聊,这里是群聊信息 23 | @ApiModelProperty("最新消息") 24 | private ShowMsgVO latestMsg; // 最新消息 25 | @ApiModelProperty("未读消息数") 26 | private Integer unreadCount; // 未读消息数 27 | } 28 | 29 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/chat/domain/vo/ShowMsgVO.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.chat.domain.vo; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | import lombok.experimental.Accessors; 7 | 8 | import java.time.LocalDateTime; 9 | 10 | /** 11 | * @Description: 消息 vo 12 | * @Author: JollyCorivuG 13 | * @CreateTime: 2024/1/21 14 | */ 15 | @Data 16 | @Accessors(chain = true) 17 | @ApiModel 18 | public class ShowMsgVO { 19 | @ApiModelProperty("发送者") 20 | private UserInfo fromUser; 21 | @ApiModelProperty("消息") 22 | private MessageInfo message; 23 | 24 | @Data 25 | @Accessors(chain = true) 26 | public static class UserInfo { 27 | private Long id; // 只需要返回用户id即可 28 | } 29 | 30 | @Data 31 | @Accessors(chain = true) 32 | public static class MessageInfo { 33 | @ApiModelProperty("消息id") 34 | private Long id; 35 | @ApiModelProperty("发送时间") 36 | private LocalDateTime sendTime; 37 | @ApiModelProperty("消息类型 0-文本 1-图片 2-文件") 38 | private Integer msgType; 39 | @ApiModelProperty("消息体") 40 | private Object body; 41 | } 42 | } 43 | 44 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/chat/event/MessageSendEvent.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.chat.event; 2 | 3 | import lombok.Getter; 4 | import org.springframework.context.ApplicationEvent; 5 | 6 | /** 7 | * @Description: 消息发送事件 8 | * @Author: JollyCorivuG 9 | * @CreateTime: 2024/1/21 10 | */ 11 | @Getter 12 | public class MessageSendEvent extends ApplicationEvent { 13 | private final Long msgId; 14 | 15 | public MessageSendEvent(Object source, Long msgId) { 16 | super(source); 17 | this.msgId = msgId; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/chat/event/listener/MessageSendListener.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.chat.event.listener; 2 | 3 | import bupt.edu.jhc.chathub.common.domain.constants.SystemConstants; 4 | import bupt.edu.jhc.chathub.server.chat.domain.entity.Message; 5 | import bupt.edu.jhc.chathub.server.chat.domain.vo.RoomVO; 6 | import bupt.edu.jhc.chathub.server.chat.domain.vo.ShowMsgVO; 7 | import bupt.edu.jhc.chathub.server.chat.event.MessageSendEvent; 8 | import bupt.edu.jhc.chathub.server.chat.service.IMessageService; 9 | import bupt.edu.jhc.chathub.server.chat.service.IRoomService; 10 | import bupt.edu.jhc.chathub.server.sse.domain.enums.SseRespType; 11 | import bupt.edu.jhc.chathub.server.sse.domain.vo.SseResponse; 12 | import bupt.edu.jhc.chathub.server.sse.manager.SseSessionManager; 13 | import bupt.edu.jhc.chathub.server.sse.service.ISseService; 14 | import bupt.edu.jhc.chathub.server.websocket.domain.resp.WSResponse; 15 | import bupt.edu.jhc.chathub.server.websocket.service.IWebSocketService; 16 | import lombok.extern.slf4j.Slf4j; 17 | import org.springframework.scheduling.annotation.Async; 18 | import org.springframework.stereotype.Component; 19 | import org.springframework.transaction.event.TransactionalEventListener; 20 | 21 | import javax.annotation.Resource; 22 | import java.util.List; 23 | import java.util.concurrent.ExecutorService; 24 | import java.util.concurrent.Executors; 25 | 26 | /** 27 | * @Description: 消息发送监听器 28 | * @Author: JollyCorivuG 29 | * @CreateTime: 2024/1/21 30 | */ 31 | @Component 32 | @Slf4j 33 | public class MessageSendListener { 34 | @Resource 35 | private IMessageService messageService; 36 | 37 | @Resource 38 | private IWebSocketService webSocketService; 39 | 40 | @Resource 41 | private ISseService sseService; 42 | 43 | @Resource 44 | private IRoomService roomService; 45 | 46 | // 线程池执行推送消息 47 | private static final ExecutorService SSE_SEND_MSG_TASK_EXECUTOR = Executors.newFixedThreadPool(10); 48 | 49 | 50 | @Async 51 | @TransactionalEventListener(classes = MessageSendEvent.class, fallbackExecution = true) 52 | public void notifyOtherOnline(MessageSendEvent event) { 53 | Message message = messageService.getById(event.getMsgId()); 54 | ShowMsgVO showMsg = messageService.convertToShowMsgVO(message); 55 | webSocketService.sendToAssignedRoom( 56 | message.getRoomId(), 57 | WSResponse.build(SystemConstants.WS_NORMAL_MSG, showMsg), 58 | message.getFromUserId() 59 | ); 60 | } 61 | 62 | @Async 63 | @TransactionalEventListener(classes = MessageSendEvent.class, fallbackExecution = true) 64 | public void notifyFreshMsgList(MessageSendEvent event) { 65 | // 1.查询哪些用户需要更新消息列表 66 | Message message = messageService.getById(event.getMsgId()); 67 | List userIds = roomService.listUserIdsByRoomId(message.getRoomId()); 68 | 69 | // 2.遍历用户列表,如果其存在SseEmitter,则发送消息 70 | userIds.forEach(userId -> { 71 | if (SseSessionManager.isExist(userId) && !webSocketService.isExist(userId)) { 72 | // 2.1构建信息列表的消息 73 | List roomList = messageService.getRoomList(userId); 74 | // 2.2发送消息 75 | SSE_SEND_MSG_TASK_EXECUTOR.execute(() -> sseService.send(userId, SseResponse.build(SseRespType.FRESH_ROOM_LIST.getCode(), roomList))); 76 | } 77 | }); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/chat/mapper/MessageMapper.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.chat.mapper; 2 | 3 | import bupt.edu.jhc.chathub.server.chat.domain.entity.Message; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | import org.apache.ibatis.annotations.Mapper; 6 | 7 | /** 8 | * @Description: 消息 mapper 9 | * @Author: JollyCorivuG 10 | * @CreateTime: 2024/1/21 11 | */ 12 | @Mapper 13 | public interface MessageMapper extends BaseMapper { 14 | } 15 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/chat/mapper/RoomMapper.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.chat.mapper; 2 | 3 | import bupt.edu.jhc.chathub.server.chat.domain.entity.Room; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | import org.apache.ibatis.annotations.Mapper; 6 | 7 | /** 8 | * @Description: 房间 mapper 9 | * @Author: JollyCorivuG 10 | * @CreateTime: 2024/1/21 11 | */ 12 | @Mapper 13 | public interface RoomMapper extends BaseMapper { 14 | 15 | } 16 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/chat/mapper/UserRoomMapper.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.chat.mapper; 2 | 3 | import bupt.edu.jhc.chathub.server.chat.domain.entity.UserRoom; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | import org.apache.ibatis.annotations.Mapper; 6 | 7 | /** 8 | * @Description: 用户房间 mapper 9 | * @Author: JollyCorivuG 10 | * @CreateTime: 2024/2/11 11 | */ 12 | @Mapper 13 | public interface UserRoomMapper extends BaseMapper { 14 | } 15 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/chat/service/IMessageService.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.chat.service; 2 | 3 | import bupt.edu.jhc.chathub.common.domain.vo.resp.CursorPageBaseResp; 4 | import bupt.edu.jhc.chathub.server.chat.domain.dto.MsgPageReq; 5 | import bupt.edu.jhc.chathub.server.chat.domain.dto.SendMsgDTO; 6 | import bupt.edu.jhc.chathub.server.chat.domain.entity.Message; 7 | import bupt.edu.jhc.chathub.server.chat.domain.vo.RoomVO; 8 | import bupt.edu.jhc.chathub.server.chat.domain.vo.ShowMsgVO; 9 | import com.baomidou.mybatisplus.extension.service.IService; 10 | 11 | import java.util.List; 12 | 13 | /** 14 | * @Description: 消息服务接口 15 | * @Author: JollyCorivuG 16 | * @CreateTime: 2024/1/21 17 | */ 18 | public interface IMessageService extends IService { 19 | Long sendMsg(Long userId, SendMsgDTO sendMsg); 20 | 21 | void updateUserReadLatestMsg(Long userId, Long roomId, Long msgId); 22 | 23 | ShowMsgVO convertToShowMsgVO(Message message); 24 | 25 | CursorPageBaseResp getMsgPage(MsgPageReq msgPageReq); 26 | 27 | List getRoomList(Long userId); 28 | 29 | void deleteRoom(Long userId, Long id); 30 | } 31 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/chat/service/IRoomService.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.chat.service; 2 | 3 | import bupt.edu.jhc.chathub.server.chat.domain.entity.Room; 4 | import com.baomidou.mybatisplus.extension.service.IService; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * @Description: 房间服务接口 10 | * @Author: JollyCorivuG 11 | * @CreateTime: 2024/1/21 12 | */ 13 | public interface IRoomService extends IService { 14 | List listUserIdsByRoomId(Long roomId); 15 | } -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/chat/service/adapter/MsgAdapter.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.chat.service.adapter; 2 | 3 | import bupt.edu.jhc.chathub.common.domain.constants.SystemConstants; 4 | import bupt.edu.jhc.chathub.server.chat.domain.dto.SendMsgDTO; 5 | import bupt.edu.jhc.chathub.server.chat.domain.dto.TextMsgDTO; 6 | import bupt.edu.jhc.chathub.server.chat.domain.entity.Message; 7 | import bupt.edu.jhc.chathub.server.chat.domain.vo.ShowMsgVO; 8 | import cn.hutool.core.bean.BeanUtil; 9 | 10 | import java.util.Objects; 11 | 12 | /** 13 | * @Description: 消息适配器 14 | * @Author: JollyCorivuG 15 | * @CreateTime: 2024/1/21 16 | */ 17 | public class MsgAdapter { 18 | public static Message buildMsgSave(Long userId, SendMsgDTO sendMsg) { 19 | Message message = new Message(); 20 | message.setRoomId(sendMsg.getRoomId()) 21 | .setFromUserId(userId) 22 | .setMsgType(sendMsg.getMsgType()); 23 | return message; 24 | } 25 | 26 | public static ShowMsgVO.UserInfo buildFromUser(Long fromUserId) { 27 | return new ShowMsgVO.UserInfo().setId(fromUserId); 28 | } 29 | 30 | public static ShowMsgVO buildMsgResp(Message msg) { 31 | ShowMsgVO showMsg = new ShowMsgVO(); 32 | showMsg.setFromUser(buildFromUser(msg.getFromUserId())); 33 | ShowMsgVO.MessageInfo msgInfo = new ShowMsgVO.MessageInfo(); 34 | BeanUtil.copyProperties(msg, msgInfo); 35 | msgInfo.setSendTime(msg.getCreateTime()); 36 | msgInfo.setBody(Objects.equals(msg.getMsgType(), SystemConstants.TEXT_MSG) ? new TextMsgDTO().setContent(msg.getContent()) : msg.getExtra()); 37 | showMsg.setMessage(msgInfo); 38 | return showMsg; 39 | } 40 | } -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/chat/service/cache/RoomLatestMsgCache.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.chat.service.cache; 2 | 3 | import bupt.edu.jhc.chathub.common.domain.constants.RedisConstants; 4 | import bupt.edu.jhc.chathub.common.service.cache.AbstractRedisStringCache; 5 | import bupt.edu.jhc.chathub.server.chat.domain.entity.Room; 6 | import bupt.edu.jhc.chathub.server.chat.mapper.RoomMapper; 7 | import org.springframework.stereotype.Component; 8 | 9 | import javax.annotation.Resource; 10 | import java.util.List; 11 | import java.util.Map; 12 | import java.util.stream.Collectors; 13 | 14 | /** 15 | * @Description: 房间最新信息缓存 16 | * @Author: JollyCorivuG 17 | * @CreateTime: 2024/2/11 18 | */ 19 | @Component 20 | public class RoomLatestMsgCache extends AbstractRedisStringCache { 21 | @Resource 22 | private RoomMapper roomMapper; 23 | 24 | @Override 25 | protected String getKey(Long roomId) { 26 | return RedisConstants.ROOM_LATEST_MESSAGE + roomId; 27 | } 28 | 29 | @Override 30 | protected Long getExpireSeconds() { 31 | return 5 * 60L; 32 | } 33 | 34 | @Override 35 | protected Map load(List roomIds) { 36 | List rooms = roomMapper.selectBatchIds(roomIds); 37 | return rooms.stream() 38 | .collect(Collectors.toMap(Room::getId, Room::getLatestMsgId)); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/chat/service/cache/UserRoomCache.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.chat.service.cache; 2 | 3 | import bupt.edu.jhc.chathub.common.domain.constants.RedisConstants; 4 | import bupt.edu.jhc.chathub.common.service.cache.AbstractRedisStringCache; 5 | import bupt.edu.jhc.chathub.server.chat.domain.entity.UserRoom; 6 | import bupt.edu.jhc.chathub.server.chat.mapper.UserRoomMapper; 7 | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; 8 | import kotlin.Pair; 9 | import org.springframework.stereotype.Component; 10 | 11 | import javax.annotation.Resource; 12 | import java.util.List; 13 | import java.util.Map; 14 | import java.util.Objects; 15 | import java.util.stream.Collectors; 16 | 17 | /** 18 | * @Description: 用户房间对应信息缓存 19 | * @Author: JollyCorivuG 20 | * @CreateTime: 2024/2/11 21 | */ 22 | @Component 23 | public class UserRoomCache extends AbstractRedisStringCache, UserRoom> { 24 | @Resource 25 | private UserRoomMapper userRoomMapper; 26 | 27 | @Override 28 | protected String getKey(Pair req) { 29 | return RedisConstants.USER_ROOM_INFO + req.getFirst() + ":" + req.getSecond(); 30 | } 31 | 32 | @Override 33 | protected Long getExpireSeconds() { 34 | return 5 * 60L; 35 | } 36 | 37 | @Override 38 | protected Map, UserRoom> load(List> req) { 39 | List userRooms = req.stream().map(p -> { 40 | QueryWrapper queryWrapper = new QueryWrapper<>(); 41 | queryWrapper.eq("user_id", p.getFirst()); 42 | queryWrapper.eq("room_id", p.getSecond()); 43 | return userRoomMapper.selectList(queryWrapper).isEmpty() ? null : userRoomMapper.selectList(queryWrapper).get(0); 44 | }).collect(Collectors.toList()); 45 | return userRooms.stream() 46 | .filter(Objects::nonNull) 47 | .collect(Collectors.toMap(userRoom -> new Pair<>(userRoom.getUserId(), userRoom.getRoomId()), userRoom -> userRoom)); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/chat/service/impl/RoomServiceImpl.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.chat.service.impl; 2 | 3 | import bupt.edu.jhc.chathub.server.chat.domain.entity.Room; 4 | import bupt.edu.jhc.chathub.server.chat.mapper.RoomMapper; 5 | import bupt.edu.jhc.chathub.server.chat.service.IRoomService; 6 | import bupt.edu.jhc.chathub.server.friend.domain.entity.FriendRelation; 7 | import bupt.edu.jhc.chathub.server.friend.service.IFriendService; 8 | import bupt.edu.jhc.chathub.server.group.domain.entity.Group; 9 | import bupt.edu.jhc.chathub.server.group.domain.entity.GroupRelation; 10 | import bupt.edu.jhc.chathub.server.group.mapper.GroupRelationMapper; 11 | import bupt.edu.jhc.chathub.server.group.service.IGroupService; 12 | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; 13 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 14 | import org.springframework.stereotype.Service; 15 | 16 | import javax.annotation.Resource; 17 | import java.util.ArrayList; 18 | import java.util.List; 19 | 20 | /** 21 | * @Description: 房间服务实现类 22 | * @Author: JollyCorivuG 23 | * @CreateTime: 2024/1/21 24 | */ 25 | @Service 26 | public class RoomServiceImpl extends ServiceImpl implements IRoomService { 27 | 28 | @Resource 29 | private IFriendService friendService; 30 | 31 | @Resource 32 | private IGroupService groupService; 33 | 34 | @Resource 35 | private GroupRelationMapper groupRelationMapper; 36 | 37 | @Override 38 | public List listUserIdsByRoomId(Long roomId) { 39 | List results = new ArrayList<>(); 40 | 41 | // 1.先查询好友关系中的用户 42 | List friendRelationList = friendService.query().eq("room_id", roomId).list(); 43 | friendRelationList.forEach(friendRelation -> { 44 | results.add(friendRelation.getUserId1()); 45 | results.add(friendRelation.getUserId2()); 46 | }); 47 | 48 | // 2.再查询群组中的用户 49 | List groups = groupService.query().eq("room_id", roomId).list(); 50 | groups.forEach(group -> { 51 | QueryWrapper wrapper = new QueryWrapper().eq("group_id", group.getId()); 52 | List groupRelations = groupRelationMapper.selectList(wrapper); 53 | groupRelations.forEach(groupRelation -> { 54 | results.add(groupRelation.getUserId()); 55 | }); 56 | results.add(group.getOwnerId()); 57 | }); 58 | 59 | // 3.返回结果 60 | return results; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/chat/service/strategy/AbstractMsgHandler.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.chat.service.strategy; 2 | 3 | 4 | import bupt.edu.jhc.chathub.server.chat.domain.dto.SendMsgDTO; 5 | import bupt.edu.jhc.chathub.server.chat.domain.entity.Message; 6 | import bupt.edu.jhc.chathub.server.chat.domain.enums.MsgTypeEnum; 7 | 8 | import javax.annotation.PostConstruct; 9 | 10 | /** 11 | * @Description: 消息处理器抽象类 12 | * @Author: JollyCorivuG 13 | * @CreateTime: 2023/8/23 14 | */ 15 | public abstract class AbstractMsgHandler { 16 | @PostConstruct 17 | private void init() { 18 | MsgHandlerFactory.register(getMsgTypeEnum().getType(), this); 19 | } 20 | 21 | abstract MsgTypeEnum getMsgTypeEnum(); 22 | 23 | public abstract void checkMsg(Long userId, SendMsgDTO sendMsg); 24 | 25 | public abstract Message saveMsg(Long userId, SendMsgDTO sendMsg); 26 | } 27 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/chat/service/strategy/FileMsgHandler.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.chat.service.strategy; 2 | 3 | import bupt.edu.jhc.chathub.server.chat.domain.dto.FileMsgDTO; 4 | import bupt.edu.jhc.chathub.server.chat.domain.dto.MessageExtra; 5 | import bupt.edu.jhc.chathub.server.chat.domain.dto.SendMsgDTO; 6 | import bupt.edu.jhc.chathub.server.chat.domain.entity.Message; 7 | import bupt.edu.jhc.chathub.server.chat.domain.enums.MsgTypeEnum; 8 | import bupt.edu.jhc.chathub.server.chat.service.adapter.MsgAdapter; 9 | import cn.hutool.core.bean.BeanUtil; 10 | import org.springframework.stereotype.Component; 11 | 12 | /** 13 | * @Description: 文件消息处理器 14 | * @Author: JollyCorivuG 15 | * @CreateTime: 2023/8/23 16 | */ 17 | @Component 18 | public class FileMsgHandler extends AbstractMsgHandler { 19 | /** 20 | * 获取消息类型 21 | * @return MsgTypeEnum 22 | */ 23 | @Override 24 | MsgTypeEnum getMsgTypeEnum() { 25 | return MsgTypeEnum.FILE; 26 | } 27 | 28 | /** 29 | * 文件消息校验 30 | * @param userId 31 | * @param sendMsg 32 | */ 33 | @Override 34 | public void checkMsg(Long userId, SendMsgDTO sendMsg) { 35 | // TODO 文件消息校验 36 | } 37 | 38 | /** 39 | * 保存消息 40 | * @param userId 41 | * @param sendMsg 42 | * @return Message 43 | */ 44 | @Override 45 | public Message saveMsg(Long userId, SendMsgDTO sendMsg) { 46 | Message message = MsgAdapter.buildMsgSave(userId, sendMsg); 47 | FileMsgDTO fileMsg = BeanUtil.toBean(sendMsg.getBody(), FileMsgDTO.class); 48 | message.setExtra(new MessageExtra().setFileMsg(fileMsg)); 49 | return message; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/chat/service/strategy/ImgMsgHandler.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.chat.service.strategy; 2 | 3 | import bupt.edu.jhc.chathub.server.chat.domain.dto.ImgMsgDTO; 4 | import bupt.edu.jhc.chathub.server.chat.domain.dto.MessageExtra; 5 | import bupt.edu.jhc.chathub.server.chat.domain.dto.SendMsgDTO; 6 | import bupt.edu.jhc.chathub.server.chat.domain.entity.Message; 7 | import bupt.edu.jhc.chathub.server.chat.domain.enums.MsgTypeEnum; 8 | import bupt.edu.jhc.chathub.server.chat.service.adapter.MsgAdapter; 9 | import cn.hutool.core.bean.BeanUtil; 10 | import org.springframework.stereotype.Component; 11 | 12 | /** 13 | * @Description: 图片消息处理器 14 | * @Author: JollyCorivuG 15 | * @CreateTime: 2023/8/23 16 | */ 17 | @Component 18 | public class ImgMsgHandler extends AbstractMsgHandler { 19 | /** 20 | * 获取消息类型 21 | * @return MsgTypeEnum 22 | */ 23 | @Override 24 | MsgTypeEnum getMsgTypeEnum() { 25 | return MsgTypeEnum.IMG; 26 | } 27 | 28 | /** 29 | * 图片消息校验 30 | * @param userId 31 | * @param sendMsg 32 | */ 33 | @Override 34 | public void checkMsg(Long userId, SendMsgDTO sendMsg) { 35 | // TODO 图片消息校验 36 | } 37 | 38 | /** 39 | * 保存消息 40 | * @param userId 41 | * @param sendMsg 42 | * @return Message 43 | */ 44 | @Override 45 | public Message saveMsg(Long userId, SendMsgDTO sendMsg) { 46 | Message message = MsgAdapter.buildMsgSave(userId, sendMsg); 47 | ImgMsgDTO imgMsg = BeanUtil.toBean(sendMsg.getBody(), ImgMsgDTO.class); 48 | message.setExtra(new MessageExtra().setImgMsg(imgMsg)); 49 | return message; 50 | } 51 | 52 | 53 | } 54 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/chat/service/strategy/MsgHandlerFactory.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.chat.service.strategy; 2 | 3 | 4 | import bupt.edu.jhc.chathub.common.domain.enums.ErrorStatus; 5 | import bupt.edu.jhc.chathub.common.utils.exception.ThrowUtils; 6 | import lombok.extern.slf4j.Slf4j; 7 | 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | 11 | /** 12 | * @Description: 消息处理器工厂类 13 | * @Author: JollyCorivuG 14 | * @CreateTime: 2023/8/23 15 | */ 16 | @Slf4j 17 | public class MsgHandlerFactory { 18 | private static final Map STRATEGY_MAP = new HashMap<>(); 19 | 20 | public static void register(Integer code, AbstractMsgHandler strategy) { 21 | STRATEGY_MAP.put(code, strategy); 22 | } 23 | 24 | public static AbstractMsgHandler getStrategy(Integer code) { 25 | AbstractMsgHandler strategy = STRATEGY_MAP.get(code); 26 | ThrowUtils.throwIf(strategy == null, ErrorStatus.PARAMS_ERROR, "消息类型错误"); 27 | return strategy; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/chat/service/strategy/TextMsgHandler.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.chat.service.strategy; 2 | 3 | import bupt.edu.jhc.chathub.server.chat.domain.dto.SendMsgDTO; 4 | import bupt.edu.jhc.chathub.server.chat.domain.dto.TextMsgDTO; 5 | import bupt.edu.jhc.chathub.server.chat.domain.entity.Message; 6 | import bupt.edu.jhc.chathub.server.chat.domain.enums.MsgTypeEnum; 7 | import bupt.edu.jhc.chathub.server.chat.service.adapter.MsgAdapter; 8 | import cn.hutool.core.bean.BeanUtil; 9 | import org.springframework.stereotype.Component; 10 | 11 | /** 12 | * @Description: 文本消息处理器 13 | * @Author: JollyCorivuG 14 | * @CreateTime: 2023/8/23 15 | */ 16 | @Component 17 | public class TextMsgHandler extends AbstractMsgHandler { 18 | 19 | /** 20 | * 获取消息类型 21 | * @return 22 | */ 23 | @Override 24 | MsgTypeEnum getMsgTypeEnum() { 25 | return MsgTypeEnum.TEXT; 26 | } 27 | 28 | /** 29 | * 文本消息校验, 如敏感词过滤、长度限制等 30 | * @param userId 31 | * @param sendMsg 32 | */ 33 | @Override 34 | public void checkMsg(Long userId, SendMsgDTO sendMsg) { 35 | // TODO 文本消息校验 36 | } 37 | 38 | /** 39 | * 保存消息 40 | * @param userId 41 | * @param sendMsg 42 | * @return Message 43 | */ 44 | @Override 45 | public Message saveMsg(Long userId, SendMsgDTO sendMsg) { 46 | Message message = MsgAdapter.buildMsgSave(userId, sendMsg); 47 | TextMsgDTO textMsg = BeanUtil.toBean(sendMsg.getBody(), TextMsgDTO.class); 48 | message.setContent(textMsg.getContent()); 49 | return message; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/friend/controller/FriendController.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.friend.controller; 2 | 3 | import bupt.edu.jhc.chathub.common.domain.vo.resp.Response; 4 | import bupt.edu.jhc.chathub.common.utils.context.RequestHolder; 5 | import bupt.edu.jhc.chathub.server.friend.domain.dto.FriendApplication; 6 | import bupt.edu.jhc.chathub.server.friend.domain.dto.FriendApplicationReply; 7 | import bupt.edu.jhc.chathub.server.friend.service.IFriendService; 8 | import bupt.edu.jhc.chathub.server.user.domain.vo.UserVO; 9 | import bupt.edu.jhc.chathub.server.user.mapper.UserMapper; 10 | import bupt.edu.jhc.chathub.server.user.service.IUserService; 11 | import io.swagger.annotations.Api; 12 | import io.swagger.annotations.ApiOperation; 13 | import org.springframework.web.bind.annotation.*; 14 | 15 | import javax.annotation.Resource; 16 | import java.util.Collections; 17 | import java.util.List; 18 | import java.util.stream.Collectors; 19 | 20 | /** 21 | * @Description: 好友接口 22 | * @Author: JollyCorivuG 23 | * @CreateTime: 2024/1/21 24 | */ 25 | @RestController 26 | @RequestMapping("/api/friends") 27 | @Api(tags = "好友相关接口") 28 | public class FriendController { 29 | @Resource 30 | private IFriendService friendService; 31 | 32 | @Resource 33 | private IUserService userService; 34 | 35 | @Resource 36 | private UserMapper userMapper; 37 | 38 | @PostMapping("/application") 39 | @ApiOperation("发送好友申请") 40 | public Response friendApplication(@RequestBody FriendApplication friendApplication) { 41 | return friendService.friendApplication(friendApplication); 42 | } 43 | 44 | @PostMapping("/application/reply") 45 | @ApiOperation("接受或拒绝好友申请") 46 | public Response friendApplicationReply(@RequestBody FriendApplicationReply friendApplicationReply) { 47 | return friendService.acceptFriendApplication(friendApplicationReply); 48 | } 49 | 50 | @GetMapping("/list") 51 | @ApiOperation("获取好友列表") 52 | public Response> getFriendList() { 53 | Long selfId = RequestHolder.get().getUid(); 54 | List friendsId = userService.queryFriendIds(selfId); 55 | if (friendsId.isEmpty()) { 56 | return Response.success(Collections.emptyList()); 57 | } 58 | List userVOList = userMapper.selectBatchIds(friendsId).stream() 59 | .map(user -> userService.convertUserToUserVO(selfId, user)) 60 | .sorted((o1, o2) -> { 61 | if (o1.getIsOnline() && !o2.getIsOnline()) { 62 | return -1; 63 | } else if (!o1.getIsOnline() && o2.getIsOnline()) { 64 | return 1; 65 | } else { 66 | return 0; 67 | } 68 | }) 69 | .collect(Collectors.toList()); 70 | return Response.success(userVOList); 71 | } 72 | 73 | @DeleteMapping("/{id}") 74 | @ApiOperation("删除好友") 75 | public Response deleteFriend(@PathVariable Long id) { 76 | return friendService.deleteFriend(id); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/friend/controller/NoticeController.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.friend.controller; 2 | 3 | import bupt.edu.jhc.chathub.common.domain.enums.ErrorStatus; 4 | import bupt.edu.jhc.chathub.common.domain.vo.resp.Response; 5 | import bupt.edu.jhc.chathub.common.utils.context.RequestHolder; 6 | import bupt.edu.jhc.chathub.common.utils.exception.ThrowUtils; 7 | import bupt.edu.jhc.chathub.server.friend.domain.vo.FriendNoticeVO; 8 | import bupt.edu.jhc.chathub.server.friend.mapper.FriendNoticeMapper; 9 | import bupt.edu.jhc.chathub.server.friend.service.IFriendNoticeService; 10 | import bupt.edu.jhc.chathub.server.group.domain.vo.GroupNoticeVO; 11 | import bupt.edu.jhc.chathub.server.group.service.IGroupNoticeService; 12 | import io.swagger.annotations.Api; 13 | import io.swagger.annotations.ApiOperation; 14 | import org.springframework.web.bind.annotation.*; 15 | 16 | import javax.annotation.Resource; 17 | import java.util.List; 18 | 19 | /** 20 | * @Description: 通知接口 21 | * @Author: JollyCorivuG 22 | * @CreateTime: 2024/1/21 23 | */ 24 | @RestController 25 | @RequestMapping("/api/notices") 26 | @Api(tags = "通知相关接口") 27 | public class NoticeController { 28 | @Resource 29 | private IFriendNoticeService friendNoticeService; 30 | @Resource 31 | private FriendNoticeMapper friendNoticeMapper; 32 | 33 | @Resource 34 | private IGroupNoticeService groupNoticeService; 35 | 36 | @GetMapping("/friends") 37 | @ApiOperation("查询好友通知") 38 | public Response> friendNoticeList() { 39 | return friendNoticeService.friendNoticeList(); 40 | } 41 | 42 | @DeleteMapping("/friends/{id}") 43 | @ApiOperation("删除好友通知") 44 | public Response deleteFriendNotice(@PathVariable Long id) { 45 | int isSuccess = friendNoticeMapper.deleteById(id); 46 | ThrowUtils.throwIf(isSuccess != 1, ErrorStatus.OPERATION_ERROR); 47 | return Response.success(null); 48 | } 49 | 50 | @GetMapping("/groups") 51 | @ApiOperation("查询群组通知") 52 | public Response> groupNoticeList() { 53 | Long userId = RequestHolder.get().getUid(); 54 | return Response.success(groupNoticeService.getGroupNotice(userId)); 55 | } 56 | 57 | @DeleteMapping("/groups/{id}") 58 | @ApiOperation("删除群组通知") 59 | public Response deleteGroupNotice(@PathVariable Long id) { 60 | ThrowUtils.throwIf(!groupNoticeService.removeById(id), ErrorStatus.OPERATION_ERROR); 61 | return Response.success(null); 62 | } 63 | } 64 | 65 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/friend/domain/dto/FriendApplication.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.friend.domain.dto; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | 7 | /** 8 | * @Description: 好友申请 9 | * @Author: JollyCorivuG 10 | * @CreateTime: 2024/1/21 11 | */ 12 | @Data 13 | @ApiModel("好友申请") 14 | public class FriendApplication { 15 | @ApiModelProperty("对方 id") 16 | private Long toUserId; 17 | @ApiModelProperty("描述信息") 18 | private String description; 19 | } 20 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/friend/domain/dto/FriendApplicationReply.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.friend.domain.dto; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | 7 | /** 8 | * @Description: 好友申请回复 9 | * @Author: JollyCorivuG 10 | * @CreateTime: 2024/1/21 11 | */ 12 | @Data 13 | @ApiModel("好友申请回复") 14 | public class FriendApplicationReply { 15 | @ApiModelProperty("是否接受") 16 | private Boolean isAccept; 17 | @ApiModelProperty("通知 id") 18 | private Long noticeId; 19 | @ApiModelProperty("用户 id") 20 | private Long userId; 21 | } 22 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/friend/domain/entity/FriendNotice.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.friend.domain.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableField; 5 | import com.baomidou.mybatisplus.annotation.TableId; 6 | import com.baomidou.mybatisplus.annotation.TableName; 7 | import lombok.Data; 8 | import lombok.experimental.Accessors; 9 | 10 | import java.time.LocalDateTime; 11 | 12 | /** 13 | * @Description: 好友通知实体类 14 | * @Author: JollyCorivuG 15 | * @CreateTime: 2024/1/21 16 | */ 17 | @Data 18 | @Accessors(chain = true) 19 | @TableName("tb_friend_notice") 20 | public class FriendNotice { 21 | @TableId(value = "id", type = IdType.AUTO) 22 | private Long id; 23 | @TableField("connect_user_id") 24 | private Long connectUserId; 25 | @TableField("other_user_id") 26 | private Long otherUserId; 27 | @TableField("description") 28 | private String description; 29 | @TableField("notice_type") 30 | private Integer noticeType; 31 | @TableField("status_info") 32 | private Integer statusInfo; 33 | @TableField("create_time") 34 | private LocalDateTime createTime; 35 | @TableField("update_time") 36 | private LocalDateTime updateTime; 37 | } 38 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/friend/domain/entity/FriendRelation.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.friend.domain.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableField; 5 | import com.baomidou.mybatisplus.annotation.TableId; 6 | import com.baomidou.mybatisplus.annotation.TableName; 7 | import lombok.Data; 8 | import lombok.experimental.Accessors; 9 | 10 | import java.time.LocalDateTime; 11 | 12 | /** 13 | * @Description: 好友关系实体类 14 | * @Author: JollyCorivuG 15 | * @CreateTime: 2024/1/21 16 | */ 17 | @Data 18 | @Accessors(chain = true) 19 | @TableName("tb_friend_relation") 20 | public class FriendRelation { 21 | @TableId(value = "id", type = IdType.AUTO) 22 | private Long id; 23 | @TableField("user_id1") 24 | private Long userId1; 25 | @TableField("user_id2") 26 | private Long userId2; 27 | @TableField("room_id") 28 | private Long roomId; // 两个人所属的会话id 29 | @TableField("create_time") 30 | private LocalDateTime createTime; 31 | @TableField("update_time") 32 | private LocalDateTime updateTime; 33 | } 34 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/friend/domain/vo/FriendNoticeVO.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.friend.domain.vo; 2 | 3 | import bupt.edu.jhc.chathub.server.user.domain.vo.UserVO; 4 | import io.swagger.annotations.ApiModel; 5 | import io.swagger.annotations.ApiModelProperty; 6 | import lombok.Data; 7 | import lombok.experimental.Accessors; 8 | 9 | import java.time.LocalDateTime; 10 | 11 | /** 12 | * @Description: 好友通知信息 13 | * @Author: JollyCorivuG 14 | * @CreateTime: 2024/1/21 15 | */ 16 | @Data 17 | @Accessors(chain = true) 18 | @ApiModel("好友通知信息") 19 | public class FriendNoticeVO { 20 | @ApiModelProperty("通知 id") 21 | private Long id; 22 | @ApiModelProperty("对方用户信息") 23 | private UserVO showUserInfo; 24 | @ApiModelProperty("描述信息") 25 | private String description; 26 | @ApiModelProperty("通知类型 0-申请添加好友 1-对方申请你添加好友") 27 | private Integer noticeType; 28 | @ApiModelProperty("状态信息") 29 | private Integer statusInfo; 30 | @ApiModelProperty("创建时间") 31 | private LocalDateTime createTime; 32 | } 33 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/friend/mapper/FriendNoticeMapper.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.friend.mapper; 2 | 3 | import bupt.edu.jhc.chathub.server.friend.domain.entity.FriendNotice; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | import org.apache.ibatis.annotations.Mapper; 6 | 7 | /** 8 | * @Description: 好友通知 mapper 9 | * @Author: JollyCorivuG 10 | * @CreateTime: 2024/1/21 11 | */ 12 | @Mapper 13 | public interface FriendNoticeMapper extends BaseMapper { 14 | } 15 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/friend/mapper/FriendRelationMapper.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.friend.mapper; 2 | 3 | import bupt.edu.jhc.chathub.server.friend.domain.entity.FriendRelation; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | import org.apache.ibatis.annotations.Mapper; 6 | 7 | /** 8 | * @Description: 好友关系 mapper 9 | * @Author: JollyCorivuG 10 | * @CreateTime: 2024/1/21 11 | */ 12 | @Mapper 13 | public interface FriendRelationMapper extends BaseMapper { 14 | } 15 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/friend/service/IFriendNoticeService.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.friend.service; 2 | 3 | import bupt.edu.jhc.chathub.common.domain.vo.resp.Response; 4 | import bupt.edu.jhc.chathub.server.friend.domain.entity.FriendNotice; 5 | import bupt.edu.jhc.chathub.server.friend.domain.vo.FriendNoticeVO; 6 | import com.baomidou.mybatisplus.extension.service.IService; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * @Description: 好友通知服务接口 12 | * @Author: JollyCorivuG 13 | * @CreateTime: 2024/1/21 14 | */ 15 | public interface IFriendNoticeService extends IService { 16 | Response> friendNoticeList(); 17 | } 18 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/friend/service/IFriendService.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.friend.service; 2 | 3 | import bupt.edu.jhc.chathub.common.domain.vo.resp.Response; 4 | import bupt.edu.jhc.chathub.server.friend.domain.dto.FriendApplication; 5 | import bupt.edu.jhc.chathub.server.friend.domain.dto.FriendApplicationReply; 6 | import bupt.edu.jhc.chathub.server.friend.domain.entity.FriendRelation; 7 | import com.baomidou.mybatisplus.extension.service.IService; 8 | 9 | /** 10 | * @Description: 好友服务接口 11 | * @Author: JollyCorivuG 12 | * @CreateTime: 2024/1/21 13 | */ 14 | public interface IFriendService extends IService { 15 | Response friendApplication(FriendApplication friendApplication); 16 | 17 | Response acceptFriendApplication(FriendApplicationReply friendApplicationReply); 18 | 19 | Response deleteFriend(Long id); 20 | 21 | Long getRoomId(Long selfId, Long otherId); 22 | } 23 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/friend/service/impl/FriendNoticeServiceImpl.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.friend.service.impl; 2 | 3 | import bupt.edu.jhc.chathub.common.domain.vo.resp.Response; 4 | import bupt.edu.jhc.chathub.common.utils.context.RequestHolder; 5 | import bupt.edu.jhc.chathub.server.friend.domain.entity.FriendNotice; 6 | import bupt.edu.jhc.chathub.server.friend.domain.vo.FriendNoticeVO; 7 | import bupt.edu.jhc.chathub.server.friend.mapper.FriendNoticeMapper; 8 | import bupt.edu.jhc.chathub.server.friend.service.IFriendNoticeService; 9 | import bupt.edu.jhc.chathub.server.user.domain.entity.User; 10 | import bupt.edu.jhc.chathub.server.user.domain.vo.UserVO; 11 | import bupt.edu.jhc.chathub.server.user.mapper.UserMapper; 12 | import cn.hutool.core.bean.BeanUtil; 13 | import cn.hutool.json.JSONUtil; 14 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 15 | import org.springframework.stereotype.Service; 16 | 17 | import javax.annotation.Resource; 18 | import java.util.List; 19 | import java.util.stream.Collectors; 20 | 21 | /** 22 | * @Description: 好友通知服务实现类 23 | * @Author: JollyCorivuG 24 | * @CreateTime: 2024/1/21 25 | */ 26 | @Service 27 | public class FriendNoticeServiceImpl extends ServiceImpl implements IFriendNoticeService { 28 | @Resource 29 | private UserMapper userMapper; 30 | private FriendNoticeVO convertToVo(FriendNotice friendNotice) { 31 | User user = userMapper.selectById(friendNotice.getOtherUserId()); 32 | FriendNoticeVO friendNoticeVO = new FriendNoticeVO(); 33 | BeanUtil.copyProperties(friendNotice, friendNoticeVO); 34 | friendNoticeVO.setShowUserInfo(JSONUtil.toBean(JSONUtil.toJsonStr(user), UserVO.class)); 35 | return friendNoticeVO; 36 | } 37 | @Override 38 | public Response> friendNoticeList() { 39 | // 1.先查询出来FriendNotice, 按照create_time倒序排列 40 | Long userId = RequestHolder.get().getUid(); 41 | List friendNotices = query().eq("connect_user_id", userId).orderByDesc("create_time").list(); 42 | 43 | // 2.将friendNotice转换为friendNoticeVO返回 44 | return Response.success(friendNotices.stream().map(this::convertToVo).collect(Collectors.toList())); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/group/controller/GroupController.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.group.controller; 2 | 3 | import bupt.edu.jhc.chathub.common.domain.vo.resp.Response; 4 | import bupt.edu.jhc.chathub.common.utils.context.RequestHolder; 5 | import bupt.edu.jhc.chathub.server.group.domain.dto.CreateGroupDTO; 6 | import bupt.edu.jhc.chathub.server.group.domain.dto.InvitationRespDTO; 7 | import bupt.edu.jhc.chathub.server.group.domain.vo.GroupVO; 8 | import bupt.edu.jhc.chathub.server.group.service.IGroupService; 9 | import io.swagger.annotations.Api; 10 | import io.swagger.annotations.ApiOperation; 11 | import org.springframework.web.bind.annotation.*; 12 | 13 | import javax.annotation.Resource; 14 | import java.util.List; 15 | 16 | /** 17 | * @Description: 群组接口 18 | * @Author: JollyCorivuG 19 | * @CreateTime: 2024/1/21 20 | */ 21 | @RestController 22 | @RequestMapping("/api/groups") 23 | @Api(tags = "群组相关接口") 24 | public class GroupController { 25 | @Resource 26 | private IGroupService groupService; 27 | 28 | @PostMapping("/create") 29 | @ApiOperation("创建群组") 30 | public Response createGroup(@RequestBody CreateGroupDTO createGroup) { 31 | Long userId = RequestHolder.get().getUid(); 32 | return Response.success(groupService.createGroup(userId, createGroup)); 33 | } 34 | 35 | @GetMapping("/manage") 36 | @ApiOperation("获取自己管理的群组") 37 | public Response> getManageGroup() { 38 | Long userId = RequestHolder.get().getUid(); 39 | return Response.success(groupService.getManageGroup(userId)); 40 | } 41 | 42 | @GetMapping("/join") 43 | @ApiOperation("获取自己加入的群组") 44 | public Response> getJoinGroup() { 45 | Long userId = RequestHolder.get().getUid(); 46 | return Response.success(groupService.getJoinGroup(userId)); 47 | } 48 | 49 | @GetMapping("/{id}") 50 | @ApiOperation("获取特定群组信息") 51 | public Response getGroupInfo(@PathVariable("id") Long groupId) { 52 | Long userId = RequestHolder.get().getUid(); 53 | return Response.success(groupService.getGroupInfo(userId, groupId)); 54 | } 55 | 56 | @PostMapping("/{id}/invite") 57 | @ApiOperation("邀请用户加入群组") 58 | public Response inviteUsers(@RequestBody List inviteUserIds, @PathVariable("id") Long groupId) { 59 | Long userId = RequestHolder.get().getUid(); 60 | groupService.inviteUsers(userId, inviteUserIds, groupId); 61 | return Response.success(null); 62 | } 63 | 64 | @PostMapping("/resp") 65 | @ApiOperation("响应群组邀请") 66 | public Response invitationResp(@RequestBody InvitationRespDTO invitationResp) { 67 | Long userId = RequestHolder.get().getUid(); 68 | groupService.invitationResp(userId, invitationResp); 69 | return Response.success(null); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/group/domain/dto/CreateGroupDTO.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.group.domain.dto; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | 7 | /** 8 | * @Description: 创建群组 dto 9 | * @Author: JollyCorivuG 10 | * @CreateTime: 2024/1/21 11 | */ 12 | @Data 13 | @ApiModel("创建群组请求信息") 14 | public class CreateGroupDTO { 15 | @ApiModelProperty("群组名称") 16 | private String name; 17 | @ApiModelProperty("群组头像") 18 | private String avatar; 19 | @ApiModelProperty("群组最大人数") 20 | private Integer maxPeopleNum; 21 | } 22 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/group/domain/dto/InvitationRespDTO.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.group.domain.dto; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | 7 | /** 8 | * @Description: 入群邀请响应信息 9 | * @Author: JollyCorivuG 10 | * @CreateTime: 2024/1/21 11 | */ 12 | @Data 13 | @ApiModel("入群邀请响应信息") 14 | public class InvitationRespDTO { 15 | @ApiModelProperty("群组id") 16 | private Long groupId; 17 | @ApiModelProperty("通知id") 18 | private Long noticeId; 19 | @ApiModelProperty("是否同意") 20 | private Boolean isAgree; 21 | } 22 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/group/domain/entity/Group.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.group.domain.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableField; 5 | import com.baomidou.mybatisplus.annotation.TableId; 6 | import com.baomidou.mybatisplus.annotation.TableName; 7 | import lombok.AllArgsConstructor; 8 | import lombok.Builder; 9 | import lombok.Data; 10 | import lombok.NoArgsConstructor; 11 | 12 | import java.time.LocalDateTime; 13 | 14 | /** 15 | * @Description: 群组实体类 16 | * @Author: JollyCorivuG 17 | * @CreateTime: 2024/1/21 18 | */ 19 | @Data 20 | @Builder 21 | @AllArgsConstructor 22 | @NoArgsConstructor 23 | @TableName("tb_group") 24 | public class Group { 25 | /** 26 | * 主键id 27 | */ 28 | @TableId(value = "id", type = IdType.AUTO) 29 | private Long id; 30 | 31 | /** 32 | * 群号 33 | */ 34 | @TableField("number") 35 | private String number; 36 | 37 | /** 38 | * 群组名称 39 | */ 40 | @TableField("name") 41 | private String name; 42 | 43 | /** 44 | * 群组头像 45 | */ 46 | @TableField("avatar") 47 | private String avatar; 48 | 49 | /** 50 | * 群组人数 51 | */ 52 | @TableField("people_num") 53 | private Integer peopleNum; 54 | 55 | /** 56 | * 群组最大人数 57 | */ 58 | @TableField("max_people_num") 59 | private Integer maxPeopleNum; 60 | 61 | /** 62 | * 群主id 63 | */ 64 | @TableField("owner_id") 65 | private Long ownerId; 66 | 67 | /** 68 | * 对应的房间id 69 | */ 70 | @TableField("room_id") 71 | private Long roomId; 72 | 73 | /** 74 | * 创建时间 75 | */ 76 | @TableField("create_time") 77 | private LocalDateTime createTime; 78 | 79 | /** 80 | * 更新时间 81 | */ 82 | @TableField("update_time") 83 | private LocalDateTime updateTime; 84 | } 85 | 86 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/group/domain/entity/GroupNotice.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.group.domain.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableField; 5 | import com.baomidou.mybatisplus.annotation.TableId; 6 | import com.baomidou.mybatisplus.annotation.TableName; 7 | import lombok.AllArgsConstructor; 8 | import lombok.Builder; 9 | import lombok.Data; 10 | import lombok.NoArgsConstructor; 11 | 12 | import java.time.LocalDateTime; 13 | 14 | /** 15 | * @Description: 群组通知实体类 16 | * @Author: JollyCorivuG 17 | * @CreateTime: 2024/1/21 18 | */ 19 | @Data 20 | @Builder 21 | @AllArgsConstructor 22 | @NoArgsConstructor 23 | @TableName("tb_group_notice") 24 | public class GroupNotice { 25 | @TableId(value = "id", type = IdType.AUTO) 26 | private Long id; 27 | @TableField("user_id") 28 | private Long userId; 29 | @TableField("group_id") 30 | private Long groupId; 31 | @TableField("description") 32 | private String description; 33 | @TableField("status_info") 34 | private Integer statusInfo; 35 | @TableField("create_time") 36 | private LocalDateTime createTime; 37 | @TableField("update_time") 38 | private LocalDateTime updateTime; 39 | } 40 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/group/domain/entity/GroupRelation.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.group.domain.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableField; 5 | import com.baomidou.mybatisplus.annotation.TableId; 6 | import com.baomidou.mybatisplus.annotation.TableName; 7 | import lombok.AllArgsConstructor; 8 | import lombok.Builder; 9 | import lombok.Data; 10 | import lombok.NoArgsConstructor; 11 | 12 | import java.time.LocalDateTime; 13 | 14 | /** 15 | * @Description: 群组关系实体类 16 | * @Author: JollyCorivuG 17 | * @CreateTime: 2024/1/21 18 | */ 19 | @Data 20 | @Builder 21 | @AllArgsConstructor 22 | @NoArgsConstructor 23 | @TableName("tb_group_relation") 24 | public class GroupRelation { 25 | /** 26 | * 主键id 27 | */ 28 | @TableId(value = "id", type = IdType.AUTO) 29 | private Long id; 30 | 31 | /** 32 | * 用户id 33 | */ 34 | @TableField("user_id") 35 | private Long userId; 36 | 37 | /** 38 | * 群组id 39 | */ 40 | @TableField("group_id") 41 | private Long groupId; 42 | 43 | /** 44 | * 创建时间 45 | */ 46 | @TableField("create_time") 47 | private LocalDateTime createTime; 48 | 49 | /** 50 | * 更新时间 51 | */ 52 | @TableField("update_time") 53 | private LocalDateTime updateTime; 54 | } 55 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/group/domain/vo/GroupNoticeVO.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.group.domain.vo; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Builder; 7 | import lombok.Data; 8 | import lombok.NoArgsConstructor; 9 | 10 | import java.time.LocalDateTime; 11 | 12 | /** 13 | * @Description: 群组通知 vo 14 | * @Author: JollyCorivuG 15 | * @CreateTime: 2024/1/21 16 | */ 17 | @Data 18 | @Builder 19 | @AllArgsConstructor 20 | @NoArgsConstructor 21 | @ApiModel("群组通知 vo") 22 | public class GroupNoticeVO { 23 | @ApiModelProperty("通知 id") 24 | private Long id; 25 | @ApiModelProperty("群组信息") 26 | private GroupVO showGroupInfo; 27 | @ApiModelProperty("通知描述") 28 | private String description; 29 | @ApiModelProperty("通知状态") 30 | private Integer statusInfo; 31 | @ApiModelProperty("通知创建时间") 32 | private LocalDateTime createTime; 33 | } 34 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/group/domain/vo/GroupVO.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.group.domain.vo; 2 | 3 | import bupt.edu.jhc.chathub.server.user.domain.vo.UserVO; 4 | import io.swagger.annotations.ApiModel; 5 | import io.swagger.annotations.ApiModelProperty; 6 | import lombok.AllArgsConstructor; 7 | import lombok.Builder; 8 | import lombok.Data; 9 | import lombok.NoArgsConstructor; 10 | 11 | import java.time.LocalDateTime; 12 | import java.util.List; 13 | 14 | /** 15 | * @Description: 群组 vo 16 | * @Author: JollyCorivuG 17 | * @CreateTime: 2024/1/21 18 | */ 19 | @Data 20 | @Builder 21 | @AllArgsConstructor 22 | @NoArgsConstructor 23 | @ApiModel("群组 vo") 24 | public class GroupVO { 25 | @ApiModelProperty("群组 id") 26 | private Long id; 27 | @ApiModelProperty("群组号") 28 | private String number; 29 | @ApiModelProperty("群组名称") 30 | private String name; 31 | @ApiModelProperty("群组头像") 32 | private String avatar; 33 | @ApiModelProperty("群组人数") 34 | private Integer peopleNum; 35 | @ApiModelProperty("群主") 36 | private UserVO owner; 37 | @ApiModelProperty("群成员") 38 | private List members; 39 | @ApiModelProperty("群组聊天室 id") 40 | private Long roomId; 41 | @ApiModelProperty("群组创建时间") 42 | private LocalDateTime createTime; 43 | } 44 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/group/mapper/GroupMapper.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.group.mapper; 2 | 3 | import bupt.edu.jhc.chathub.server.group.domain.entity.Group; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | import org.apache.ibatis.annotations.Mapper; 6 | 7 | /** 8 | * @Description: 群组 mapper 9 | * @Author: JollyCorivuG 10 | * @CreateTime: 2024/1/21 11 | */ 12 | @Mapper 13 | public interface GroupMapper extends BaseMapper { 14 | } 15 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/group/mapper/GroupNoticeMapper.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.group.mapper; 2 | 3 | import bupt.edu.jhc.chathub.server.group.domain.entity.GroupNotice; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | import org.apache.ibatis.annotations.Mapper; 6 | 7 | /** 8 | * @Description: 群组通知 mapper 9 | * @Author: JollyCorivuG 10 | * @CreateTime: 2024/1/21 11 | */ 12 | @Mapper 13 | public interface GroupNoticeMapper extends BaseMapper { 14 | } 15 | 16 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/group/mapper/GroupRelationMapper.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.group.mapper; 2 | 3 | import bupt.edu.jhc.chathub.server.group.domain.entity.GroupRelation; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | import org.apache.ibatis.annotations.Mapper; 6 | 7 | /** 8 | * @Description: 群组关系 mapper 9 | * @Author: JollyCorivuG 10 | * @CreateTime: 2024/1/21 11 | */ 12 | @Mapper 13 | public interface GroupRelationMapper extends BaseMapper { 14 | } 15 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/group/service/IGroupNoticeService.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.group.service; 2 | 3 | import bupt.edu.jhc.chathub.server.group.domain.entity.GroupNotice; 4 | import bupt.edu.jhc.chathub.server.group.domain.vo.GroupNoticeVO; 5 | import com.baomidou.mybatisplus.extension.service.IService; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * @Description: 群组通知服务接口 11 | * @Author: JollyCorivuG 12 | * @CreateTime: 2024/1/21 13 | */ 14 | public interface IGroupNoticeService extends IService { 15 | List getGroupNotice(Long userId); 16 | } 17 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/group/service/IGroupService.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.group.service; 2 | 3 | import bupt.edu.jhc.chathub.server.group.domain.dto.CreateGroupDTO; 4 | import bupt.edu.jhc.chathub.server.group.domain.dto.InvitationRespDTO; 5 | import bupt.edu.jhc.chathub.server.group.domain.entity.Group; 6 | import bupt.edu.jhc.chathub.server.group.domain.vo.GroupVO; 7 | import com.baomidou.mybatisplus.extension.service.IService; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * @Description: 群组服务接口 13 | * @Author: JollyCorivuG 14 | * @CreateTime: 2024/1/21 15 | */ 16 | public interface IGroupService extends IService { 17 | GroupVO createGroup(Long userId, CreateGroupDTO createGroup); 18 | 19 | List getManageGroup(Long userId); 20 | 21 | List getJoinGroup(Long userId); 22 | 23 | GroupVO getGroupInfo(Long userId, Long groupId); 24 | 25 | void inviteUsers(Long userId, List inviteUserIds, Long groupId); 26 | 27 | void invitationResp(Long userId, InvitationRespDTO invitationResp); 28 | } 29 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/group/service/adapter/GroupAdapter.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.group.service.adapter; 2 | 3 | import bupt.edu.jhc.chathub.common.domain.constants.SystemConstants; 4 | import bupt.edu.jhc.chathub.common.domain.enums.ErrorStatus; 5 | import bupt.edu.jhc.chathub.common.utils.exception.ThrowUtils; 6 | import bupt.edu.jhc.chathub.server.chat.domain.entity.Room; 7 | import bupt.edu.jhc.chathub.server.chat.mapper.RoomMapper; 8 | import bupt.edu.jhc.chathub.server.group.domain.dto.CreateGroupDTO; 9 | import bupt.edu.jhc.chathub.server.group.domain.entity.Group; 10 | import bupt.edu.jhc.chathub.server.group.domain.entity.GroupRelation; 11 | import bupt.edu.jhc.chathub.server.group.domain.vo.GroupVO; 12 | import bupt.edu.jhc.chathub.server.group.mapper.GroupRelationMapper; 13 | import bupt.edu.jhc.chathub.server.user.domain.vo.UserVO; 14 | import bupt.edu.jhc.chathub.server.user.service.IUserService; 15 | import cn.hutool.core.util.RandomUtil; 16 | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; 17 | import org.springframework.stereotype.Component; 18 | 19 | import javax.annotation.Resource; 20 | import java.util.List; 21 | import java.util.stream.Collectors; 22 | 23 | /** 24 | * @Description: 群组适配器 25 | * @Author: JollyCorivuG 26 | * @CreateTime: 2024/1/21 27 | */ 28 | @Component 29 | public class GroupAdapter { 30 | @Resource 31 | private RoomMapper roomMapper; 32 | 33 | @Resource 34 | private IUserService userService; 35 | 36 | @Resource 37 | private GroupRelationMapper groupRelationMapper; 38 | 39 | 40 | public Group buildGroupSave(Long userId, CreateGroupDTO createGroup) { 41 | ThrowUtils.throwIf(createGroup == null, ErrorStatus.PARAMS_ERROR); 42 | String groupNumber = RandomUtil.randomNumbers(6); 43 | Room room = new Room().setRoomType(SystemConstants.ROOM_TYPE_GROUP); 44 | roomMapper.insert(room); 45 | return Group.builder() 46 | .number(groupNumber) 47 | .name(createGroup.getName()) 48 | .avatar(createGroup.getAvatar()) 49 | .peopleNum(1) 50 | .maxPeopleNum(createGroup.getMaxPeopleNum()) 51 | .ownerId(userId) 52 | .roomId(room.getId()) 53 | .build(); 54 | } 55 | 56 | public GroupVO buildGroupResp(Long userId, Group group) { 57 | ThrowUtils.throwIf(group == null, ErrorStatus.NOT_FOUND_ERROR); 58 | UserVO owner = userService.convertUserToUserVO(userId, userService.getById(group.getOwnerId())); 59 | QueryWrapper queryWrapper = new QueryWrapper().eq("group_id", group.getId()); 60 | List groupRelations = groupRelationMapper.selectList(queryWrapper); 61 | List members = groupRelations.stream().map( 62 | relation -> userService.convertUserToUserVO(userId, userService.getById(relation.getUserId())) 63 | ).collect(Collectors.toList()); 64 | return GroupVO.builder() 65 | .id(group.getId()) 66 | .number(group.getNumber()) 67 | .name(group.getName()) 68 | .avatar(group.getAvatar()) 69 | .peopleNum(group.getPeopleNum()) 70 | .owner(owner) 71 | .members(members) 72 | .roomId(group.getRoomId()) 73 | .createTime(group.getCreateTime()) 74 | .build(); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/group/service/adapter/GroupNoticeAdapter.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.group.service.adapter; 2 | 3 | import bupt.edu.jhc.chathub.server.group.domain.entity.GroupNotice; 4 | import bupt.edu.jhc.chathub.server.group.domain.vo.GroupNoticeVO; 5 | import bupt.edu.jhc.chathub.server.group.domain.vo.GroupVO; 6 | import bupt.edu.jhc.chathub.server.group.mapper.GroupMapper; 7 | import cn.hutool.core.bean.BeanUtil; 8 | import org.springframework.stereotype.Component; 9 | 10 | import javax.annotation.Resource; 11 | 12 | /** 13 | * @Description: 群组通知适配器 14 | * @Author: JollyCorivuG 15 | * @CreateTime: 2024/1/21 16 | */ 17 | @Component 18 | public class GroupNoticeAdapter { 19 | @Resource 20 | private GroupMapper groupMapper; 21 | 22 | public GroupNoticeVO buildGroupNoticeResp(GroupNotice groupNotice) { 23 | GroupVO groupInfo = BeanUtil.toBean(groupMapper.selectById(groupNotice.getGroupId()), GroupVO.class); 24 | return GroupNoticeVO.builder() 25 | .id(groupNotice.getId()) 26 | .showGroupInfo(groupInfo) 27 | .description(groupNotice.getDescription()) 28 | .statusInfo(groupNotice.getStatusInfo()) 29 | .createTime(groupNotice.getCreateTime()) 30 | .build(); 31 | } 32 | } 33 | 34 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/group/service/impl/GroupNoticeServiceImpl.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.group.service.impl; 2 | 3 | import bupt.edu.jhc.chathub.server.group.domain.entity.GroupNotice; 4 | import bupt.edu.jhc.chathub.server.group.domain.vo.GroupNoticeVO; 5 | import bupt.edu.jhc.chathub.server.group.mapper.GroupNoticeMapper; 6 | import bupt.edu.jhc.chathub.server.group.service.IGroupNoticeService; 7 | import bupt.edu.jhc.chathub.server.group.service.adapter.GroupNoticeAdapter; 8 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 9 | import org.springframework.stereotype.Service; 10 | 11 | import javax.annotation.Resource; 12 | import java.util.List; 13 | import java.util.stream.Collectors; 14 | 15 | /** 16 | * @Description: 群组通知接口实现 17 | * @Author: JollyCorivuG 18 | * @CreateTime: 2024/1/21 19 | */ 20 | @Service 21 | public class GroupNoticeServiceImpl extends ServiceImpl implements IGroupNoticeService { 22 | @Resource 23 | private GroupNoticeAdapter groupNoticeAdapter; 24 | 25 | @Override 26 | public List getGroupNotice(Long userId) { 27 | // 1.根据 userId 查询 GroupNotice 28 | List groupNotices = query().eq("user_id", userId).orderByDesc("create_time").list(); 29 | 30 | // 2.将 GroupNotice 转换为 GroupNoticeVO 31 | return groupNotices.stream().map(groupNoticeAdapter::buildGroupNoticeResp).collect(Collectors.toList()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/sse/controller/SseController.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.sse.controller; 2 | 3 | import bupt.edu.jhc.chathub.common.utils.context.RequestHolder; 4 | import bupt.edu.jhc.chathub.server.sse.service.ISseService; 5 | import io.swagger.annotations.Api; 6 | import io.swagger.annotations.ApiOperation; 7 | import org.springframework.http.MediaType; 8 | import org.springframework.web.bind.annotation.*; 9 | import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; 10 | 11 | import javax.annotation.Resource; 12 | 13 | /** 14 | * @Description: sse 接口 15 | * @Author: JollyCorivuG 16 | * @CreateTime: 2024/1/21 17 | */ 18 | @RestController 19 | @RequestMapping("/api/sse") 20 | @Api(tags = "SSE相关接口") 21 | @CrossOrigin 22 | public class SseController { 23 | @Resource 24 | private ISseService sseService; 25 | 26 | @GetMapping(value = "/subscribe", produces = MediaType.TEXT_EVENT_STREAM_VALUE) 27 | @ApiOperation("订阅SSE") 28 | public SseEmitter subscribe() { 29 | Long userId = RequestHolder.get().getUid(); 30 | return sseService.connect(userId); 31 | } 32 | 33 | @GetMapping(value = "/close") 34 | @ApiOperation("关闭SSE") 35 | public void close(){ 36 | Long userId = RequestHolder.get().getUid(); 37 | sseService.close(userId); 38 | } 39 | 40 | @PostMapping(value = "/unsubscribe") 41 | @ApiOperation("取消订阅SSE") 42 | public void unsubscribe(@RequestParam("userId") Long userId) { 43 | sseService.close(userId); 44 | } 45 | } -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/sse/domain/enums/SseRespType.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.sse.domain.enums; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | 6 | /** 7 | * @Description: sse 响应类型 8 | * @Author: JollyCorivuG 9 | * @CreateTime: 2024/1/21 10 | */ 11 | @Getter 12 | @AllArgsConstructor 13 | public enum SseRespType { 14 | FRESH_ROOM_LIST(0, "刷新房间列表"), 15 | FORCE_LOGOUT(1, "登录多个账号时强制下线"), 16 | ; 17 | 18 | private final Integer code; 19 | private final String description; 20 | } 21 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/sse/domain/vo/SseResponse.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.sse.domain.vo; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | import lombok.experimental.Accessors; 7 | 8 | /** 9 | * @Description: sse 响应 10 | * @Author: JollyCorivuG 11 | * @CreateTime: 2024/1/21 12 | */ 13 | @Data 14 | @Accessors(chain = true) 15 | @ApiModel("sse 响应") 16 | public class SseResponse { 17 | @ApiModelProperty("响应类型 0-刷新房间列表 1-强制下线") 18 | Integer type; 19 | @ApiModelProperty("响应数据") 20 | T data; 21 | public static SseResponse build(Integer type, T data) { 22 | SseResponse response = new SseResponse<>(); 23 | return response.setType(type).setData(data); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/sse/manager/SseSessionManager.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.sse.manager; 2 | 3 | import bupt.edu.jhc.chathub.common.domain.enums.ErrorStatus; 4 | import bupt.edu.jhc.chathub.common.utils.exception.ThrowUtils; 5 | import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; 6 | 7 | import java.io.IOException; 8 | import java.util.concurrent.ConcurrentHashMap; 9 | 10 | /** 11 | * @Description: sse 管理 12 | * @Author: JollyCorivuG 13 | * @CreateTime: 2024/1/21 14 | */ 15 | public class SseSessionManager { 16 | // userId -> SseEmitter 17 | private static final ConcurrentHashMap SESSION_MAP = new ConcurrentHashMap<>(); 18 | 19 | // 添加SseEmitter 20 | public static void add(Long userId, SseEmitter sseEmitter) { 21 | ThrowUtils.throwIf(isExist(userId), ErrorStatus.OPERATION_ERROR, "该用户已经有一个SseEmitter了"); 22 | SESSION_MAP.put(userId, sseEmitter); 23 | } 24 | 25 | // 判断是否存在SseEmitter 26 | public static boolean isExist(Long userId) { 27 | return SESSION_MAP.get(userId) != null; 28 | } 29 | 30 | // 移除SseEmitter 31 | public static void remove(Long userId) { 32 | SseEmitter sseEmitter = SESSION_MAP.get(userId); 33 | ThrowUtils.throwIf(sseEmitter == null, ErrorStatus.OPERATION_ERROR, "该用户没有SseEmitter"); 34 | sseEmitter.complete(); 35 | SESSION_MAP.remove(userId); 36 | } 37 | 38 | // 发生错误 39 | public static void onError(Long userId, Throwable throwable) { 40 | SseEmitter sseEmitter = SESSION_MAP.get(userId); 41 | ThrowUtils.throwIf(sseEmitter == null, ErrorStatus.OPERATION_ERROR, "该用户没有SseEmitter"); 42 | sseEmitter.completeWithError(throwable); 43 | } 44 | 45 | // 发送消息 46 | public static void send(Long userId, Object data) throws IOException { 47 | SseEmitter sseEmitter = SESSION_MAP.get(userId); 48 | ThrowUtils.throwIf(sseEmitter == null, ErrorStatus.OPERATION_ERROR, "该用户没有SseEmitter"); 49 | sseEmitter.send(data); 50 | } 51 | } 52 | 53 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/sse/service/ISseService.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.sse.service; 2 | 3 | import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; 4 | 5 | /** 6 | * @Description: sse 服务接口 7 | * @Author: JollyCorivuG 8 | * @CreateTime: 2024/1/21 9 | */ 10 | public interface ISseService { 11 | SseEmitter connect(Long userId); 12 | 13 | void close(Long userId); 14 | 15 | void send(Long userId, Object data); 16 | } 17 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/sse/service/impl/SseServiceImpl.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.sse.service.impl; 2 | 3 | import bupt.edu.jhc.chathub.common.domain.enums.ErrorStatus; 4 | import bupt.edu.jhc.chathub.common.utils.exception.BizException; 5 | import bupt.edu.jhc.chathub.common.utils.exception.ThrowUtils; 6 | import bupt.edu.jhc.chathub.server.sse.domain.enums.SseRespType; 7 | import bupt.edu.jhc.chathub.server.sse.domain.vo.SseResponse; 8 | import bupt.edu.jhc.chathub.server.sse.manager.SseSessionManager; 9 | import bupt.edu.jhc.chathub.server.sse.service.ISseService; 10 | import bupt.edu.jhc.chathub.server.user.domain.vo.ForceLogoutInfo; 11 | import lombok.extern.slf4j.Slf4j; 12 | import org.springframework.stereotype.Service; 13 | import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; 14 | 15 | import java.time.LocalDateTime; 16 | 17 | /** 18 | * @Description: sse 服务实现类 19 | * @Author: JollyCorivuG 20 | * @CreateTime: 2024/1/21 21 | */ 22 | @Service 23 | @Slf4j 24 | public class SseServiceImpl implements ISseService { 25 | private SseEmitter newSseEmitter(Long userId) { 26 | SseEmitter sseEmitter = new SseEmitter(0L); 27 | sseEmitter.onError((err)-> { 28 | log.error("type: SseSession Error, msg: {} session Id : {}",err.getMessage(), userId); 29 | SseSessionManager.onError(userId, err); 30 | }); 31 | sseEmitter.onTimeout(() -> { 32 | log.info("type: SseSession Timeout, session Id : {}", userId); 33 | SseSessionManager.remove(userId); 34 | }); 35 | sseEmitter.onCompletion(() -> { 36 | log.info("type: SseSession Completion, session Id : {}", userId); 37 | SseSessionManager.remove(userId); 38 | }); 39 | return sseEmitter; 40 | } 41 | 42 | @Override 43 | public SseEmitter connect(Long userId) { 44 | // 1.先判断是否已经存在 45 | if (SseSessionManager.isExist(userId)) { 46 | // 1.1如果已经存在, 则发布一个强制下线的消息 47 | send(userId, SseResponse.build(SseRespType.FORCE_LOGOUT.getCode(), ForceLogoutInfo.builder().time(LocalDateTime.now()).build())); 48 | // 1.2然后关闭之前的SseEmitter 49 | SseSessionManager.remove(userId); 50 | } 51 | 52 | // 2.创建SseEmitter并加入到SseSessionManager中 53 | SseEmitter sseEmitter = newSseEmitter(userId); 54 | log.info("type: SseSession Add, session Id : {}", userId); 55 | SseSessionManager.add(userId, sseEmitter); 56 | 57 | // 3.返回SseEmitter 58 | return sseEmitter; 59 | } 60 | 61 | @Override 62 | public void close(Long userId) { 63 | log.info("type: SseSession Close, session Id : {}", userId); 64 | SseSessionManager.remove(userId); 65 | } 66 | 67 | @Override 68 | public void send(Long userId, Object data) { 69 | ThrowUtils.throwIf(!SseSessionManager.isExist(userId), ErrorStatus.OPERATION_ERROR, "该用户没有SseEmitter"); 70 | try { 71 | SseSessionManager.send(userId, data); 72 | } catch (Exception e) { 73 | throw new BizException(ErrorStatus.OPERATION_ERROR, "发送Sse消息失败"); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/trend/controller/CommentController.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.trend.controller; 2 | 3 | import bupt.edu.jhc.chathub.common.domain.vo.resp.Response; 4 | import bupt.edu.jhc.chathub.common.utils.context.RequestHolder; 5 | import bupt.edu.jhc.chathub.server.trend.domain.dto.comment.PostCommentDTO; 6 | import bupt.edu.jhc.chathub.server.trend.domain.vo.CommentVO; 7 | import bupt.edu.jhc.chathub.server.trend.service.ICommentService; 8 | import io.swagger.annotations.Api; 9 | import io.swagger.annotations.ApiOperation; 10 | import org.springframework.web.bind.annotation.PostMapping; 11 | import org.springframework.web.bind.annotation.RequestBody; 12 | import org.springframework.web.bind.annotation.RequestMapping; 13 | import org.springframework.web.bind.annotation.RestController; 14 | 15 | import javax.annotation.Resource; 16 | 17 | /** 18 | * @Description: 评论接口 19 | * @Author: JollyCorivuG 20 | * @CreateTime: 2024/1/22 21 | */ 22 | @RestController 23 | @RequestMapping("/api/comments") 24 | @Api(tags = "评论相关接口") 25 | public class CommentController { 26 | @Resource 27 | private ICommentService commentService; 28 | 29 | @PostMapping("/post") 30 | @ApiOperation("发布评论") 31 | public Response postComment(@RequestBody PostCommentDTO comment) { 32 | Long userId = RequestHolder.get().getUid(); 33 | return Response.success(commentService.postComment(userId, comment)); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/trend/controller/TrendController.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.trend.controller; 2 | 3 | import bupt.edu.jhc.chathub.common.domain.vo.request.CursorPageBaseReq; 4 | import bupt.edu.jhc.chathub.common.domain.vo.resp.CursorPageBaseResp; 5 | import bupt.edu.jhc.chathub.common.domain.vo.resp.Response; 6 | import bupt.edu.jhc.chathub.common.utils.context.RequestHolder; 7 | import bupt.edu.jhc.chathub.server.trend.domain.dto.talk.CreateTalkDTO; 8 | import bupt.edu.jhc.chathub.server.trend.domain.dto.talk.LikeInfoDTO; 9 | import bupt.edu.jhc.chathub.server.trend.domain.vo.TalkVO; 10 | import bupt.edu.jhc.chathub.server.trend.service.ITrendService; 11 | import io.swagger.annotations.Api; 12 | import io.swagger.annotations.ApiOperation; 13 | import org.springframework.web.bind.annotation.*; 14 | 15 | import javax.annotation.Resource; 16 | 17 | /** 18 | * @Description: 动态接口 19 | * @Author: JollyCorivuG 20 | * @CreateTime: 2024/1/22 21 | */ 22 | @RestController 23 | @RequestMapping("/api/trend") 24 | @Api(tags = "动态相关接口") 25 | public class TrendController { 26 | @Resource 27 | private ITrendService trendService; 28 | 29 | @PostMapping("/talk") 30 | @ApiOperation("发布说说") 31 | public Response createTalk(@RequestBody CreateTalkDTO talk) { 32 | Long userId = RequestHolder.get().getUid(); 33 | return Response.success(trendService.createTalk(userId, talk)); 34 | } 35 | 36 | @GetMapping("/talk/list") 37 | @ApiOperation("分页获取说说列表") 38 | public Response> getTalkList(CursorPageBaseReq req) { 39 | Long userId = RequestHolder.get().getUid(); 40 | return Response.success(trendService.getTalkPage(userId, req)); 41 | } 42 | 43 | @PostMapping("/talk/like") 44 | @ApiOperation("点赞或取消点赞") 45 | public Response likeTalk(@RequestBody LikeInfoDTO likeInfo) { 46 | Long userId = RequestHolder.get().getUid(); 47 | if (likeInfo.getIsLike()) { 48 | trendService.likeTalk(userId, likeInfo.getTalkId()); 49 | } else { 50 | trendService.cancelLikeTalk(userId, likeInfo.getTalkId()); 51 | } 52 | return Response.success(null); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/trend/domain/dto/comment/PostCommentDTO.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.trend.domain.dto.comment; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | 7 | /** 8 | * @Description: 评论请求 9 | * @Author: JollyCorivuG 10 | * @CreateTime: 2024/1/22 11 | */ 12 | @Data 13 | @ApiModel("评论请求") 14 | public class PostCommentDTO { 15 | @ApiModelProperty("说说 id") 16 | private Long talkId; 17 | @ApiModelProperty("评论内容") 18 | private String content; 19 | @ApiModelProperty("回复的用户 id") 20 | private Long replyUserId = 0L; // 回复的用户id 21 | } 22 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/trend/domain/dto/talk/CreateTalkDTO.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.trend.domain.dto.talk; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | import lombok.experimental.Accessors; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * @Description: 创建说说请求 12 | * @Author: JollyCorivuG 13 | * @CreateTime: 2024/1/22 14 | */ 15 | @Data 16 | @Accessors(chain = true) 17 | @ApiModel("创建说说请求") 18 | public class CreateTalkDTO { 19 | @ApiModelProperty("说说内容") 20 | private String content; 21 | @ApiModelProperty("说说附加内容, 支持图片") 22 | private List extra; 23 | } 24 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/trend/domain/dto/talk/ImgDTO.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.trend.domain.dto.talk; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | import lombok.experimental.Accessors; 7 | 8 | /** 9 | * @Description: 图片 dto 10 | * @Author: JollyCorivuG 11 | * @CreateTime: 2024/1/22 12 | */ 13 | @Data 14 | @Accessors(chain = true) 15 | @ApiModel("说说图片信息") 16 | public class ImgDTO { 17 | @ApiModelProperty("图片 url") 18 | private String url; 19 | } 20 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/trend/domain/dto/talk/LikeInfoDTO.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.trend.domain.dto.talk; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | import lombok.experimental.Accessors; 7 | 8 | /** 9 | * @Description: 点赞信息 10 | * @Author: JollyCorivuG 11 | * @CreateTime: 2024/1/22 12 | */ 13 | @Data 14 | @Accessors(chain = true) 15 | @ApiModel("点赞信息") 16 | public class LikeInfoDTO { 17 | @ApiModelProperty("说说 id") 18 | private Long talkId; 19 | @ApiModelProperty("点赞 or 取消点赞") 20 | private Boolean isLike; 21 | } 22 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/trend/domain/dto/talk/TalkExtra.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.trend.domain.dto.talk; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | import lombok.experimental.Accessors; 7 | 8 | /** 9 | * @Description: 说说额外内容 10 | * @Author: JollyCorivuG 11 | * @CreateTime: 2024/1/22 12 | */ 13 | @Data 14 | @Accessors(chain = true) 15 | @ApiModel("说说额外内容") 16 | public class TalkExtra { 17 | @ApiModelProperty("类型") 18 | private Integer type; 19 | @ApiModelProperty("数据") 20 | private Object data; 21 | } 22 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/trend/domain/dto/talk/VideoDTO.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.trend.domain.dto.talk; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | import lombok.experimental.Accessors; 7 | 8 | /** 9 | * @Description: 视频 dto 10 | * @Author: JollyCorivuG 11 | * @CreateTime: 2024/1/22 12 | */ 13 | @Data 14 | @Accessors(chain = true) 15 | @ApiModel("说说视频信息") 16 | public class VideoDTO { 17 | @ApiModelProperty("视频 url") 18 | private String url; 19 | @ApiModelProperty("视频封面 url") 20 | private String coverUrl; 21 | } 22 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/trend/domain/entity/Comment.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.trend.domain.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableField; 5 | import com.baomidou.mybatisplus.annotation.TableId; 6 | import com.baomidou.mybatisplus.annotation.TableName; 7 | import lombok.Data; 8 | import lombok.experimental.Accessors; 9 | 10 | import java.time.LocalDateTime; 11 | 12 | /** 13 | * @Description: 评论实体类 14 | * @Author: JollyCorivuG 15 | * @CreateTime: 2024/1/22 16 | */ 17 | @Data 18 | @Accessors(chain = true) 19 | @TableName("tb_comment") 20 | public class Comment { 21 | @TableId(value = "id", type = IdType.AUTO) 22 | private Long id; 23 | @TableField("sender_id") 24 | private Long senderId; 25 | @TableField("talk_id") 26 | private Long talkId; 27 | @TableField("content") 28 | private String content; 29 | @TableField("reply_user_id") 30 | private Long replyUserId; 31 | @TableField("create_time") 32 | private LocalDateTime createTime; 33 | @TableField("update_time") 34 | private LocalDateTime updateTime; 35 | } 36 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/trend/domain/entity/Feeds.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.trend.domain.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableField; 5 | import com.baomidou.mybatisplus.annotation.TableId; 6 | import com.baomidou.mybatisplus.annotation.TableName; 7 | import lombok.Data; 8 | import lombok.experimental.Accessors; 9 | 10 | import java.time.LocalDateTime; 11 | 12 | /** 13 | * @Description: feed 流实体类 14 | * @Author: JollyCorivuG 15 | * @CreateTime: 2024/1/22 16 | */ 17 | @Data 18 | @Accessors(chain = true) 19 | @TableName("tb_feeds") 20 | public class Feeds { 21 | @TableId(value = "id", type = IdType.AUTO) 22 | private Long id; 23 | @TableField("talk_id") 24 | private Long talkId; 25 | @TableField("connect_user_id") 26 | private Long connectUserId; 27 | @TableField("create_time") 28 | private LocalDateTime createTime; 29 | @TableField("update_time") 30 | private LocalDateTime updateTime; 31 | } 32 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/trend/domain/entity/Like.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.trend.domain.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableField; 5 | import com.baomidou.mybatisplus.annotation.TableId; 6 | import com.baomidou.mybatisplus.annotation.TableName; 7 | import lombok.Data; 8 | import lombok.experimental.Accessors; 9 | 10 | import java.time.LocalDateTime; 11 | 12 | /** 13 | * @Description: 点赞信息实体类 14 | * @Author: JollyCorivuG 15 | * @CreateTime: 2024/1/22 16 | */ 17 | @Data 18 | @Accessors(chain = true) 19 | @TableName("tb_like") 20 | public class Like { 21 | @TableId(value = "id", type = IdType.AUTO) 22 | private Long id; 23 | @TableField("talk_id") 24 | private Long talkId; 25 | @TableField("user_id") 26 | private Long userId; 27 | @TableField("create_time") 28 | private LocalDateTime createTime; 29 | @TableField("update_time") 30 | private LocalDateTime updateTime; 31 | } -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/trend/domain/entity/Talk.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.trend.domain.entity; 2 | 3 | import bupt.edu.jhc.chathub.server.trend.domain.dto.talk.TalkExtra; 4 | import com.baomidou.mybatisplus.annotation.IdType; 5 | import com.baomidou.mybatisplus.annotation.TableField; 6 | import com.baomidou.mybatisplus.annotation.TableId; 7 | import com.baomidou.mybatisplus.annotation.TableName; 8 | import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; 9 | import lombok.Data; 10 | import lombok.experimental.Accessors; 11 | 12 | import java.time.LocalDateTime; 13 | import java.util.Collections; 14 | import java.util.List; 15 | 16 | /** 17 | * @Description: 说说实体类 18 | * @Author: JollyCorivuG 19 | * @CreateTime: 2024/1/22 20 | */ 21 | @Data 22 | @Accessors(chain = true) 23 | @TableName(value = "tb_talk", autoResultMap = true) 24 | public class Talk { 25 | @TableId(value = "id", type = IdType.AUTO) 26 | private Long id; 27 | @TableField("author_id") 28 | private Long authorId; 29 | @TableField("content") 30 | private String content; 31 | @TableField(value = "extra", typeHandler = JacksonTypeHandler.class) 32 | private List extra = Collections.emptyList(); 33 | @TableField("like_count") 34 | private Integer likeCount; 35 | @TableField("create_time") 36 | private LocalDateTime createTime; 37 | @TableField("update_time") 38 | private LocalDateTime updateTime; 39 | } 40 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/trend/domain/vo/CommentVO.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.trend.domain.vo; 2 | 3 | import bupt.edu.jhc.chathub.server.user.domain.vo.UserVO; 4 | import io.swagger.annotations.ApiModel; 5 | import io.swagger.annotations.ApiModelProperty; 6 | import lombok.Data; 7 | import lombok.experimental.Accessors; 8 | 9 | import java.time.LocalDateTime; 10 | 11 | /** 12 | * @Description: 评论 vo 13 | * @Author: JollyCorivuG 14 | * @CreateTime: 2024/1/22 15 | */ 16 | @Data 17 | @Accessors(chain = true) 18 | @ApiModel("评论信息") 19 | public class CommentVO { 20 | @ApiModelProperty("评论 id") 21 | private Long id; 22 | @ApiModelProperty("说说 id") 23 | private UserVO sender; 24 | @ApiModelProperty("评论内容") 25 | private String content; 26 | @ApiModelProperty("回复的评论 id") 27 | private Long replyUserId; 28 | @ApiModelProperty("回复的用户信息") 29 | private UserVO replyUser; 30 | @ApiModelProperty("创建时间") 31 | private LocalDateTime createTime; 32 | } 33 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/trend/domain/vo/TalkVO.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.trend.domain.vo; 2 | 3 | import bupt.edu.jhc.chathub.server.trend.domain.dto.talk.TalkExtra; 4 | import bupt.edu.jhc.chathub.server.user.domain.vo.UserVO; 5 | import io.swagger.annotations.ApiModel; 6 | import io.swagger.annotations.ApiModelProperty; 7 | import lombok.Data; 8 | import lombok.experimental.Accessors; 9 | 10 | import java.time.LocalDateTime; 11 | import java.util.Collections; 12 | import java.util.List; 13 | 14 | /** 15 | * @Description: 说说 vo 16 | * @Author: JollyCorivuG 17 | * @CreateTime: 2024/1/22 18 | */ 19 | @Data 20 | @Accessors(chain = true) 21 | @ApiModel("说说信息") 22 | public class TalkVO { 23 | @ApiModelProperty("说说 id") 24 | private Long id; 25 | @ApiModelProperty("说说作者信息") 26 | private UserVO author; 27 | @ApiModelProperty("说说内容") 28 | private String content; 29 | @ApiModelProperty("说说附加内容, 支持图片") 30 | private List extra; 31 | @ApiModelProperty("点赞数") 32 | private Integer likeCount; 33 | @ApiModelProperty("是否点赞") 34 | private Boolean isLike; 35 | @ApiModelProperty("最新点赞的用户, 最多显示5个") 36 | private List latestLikeUsers; 37 | @ApiModelProperty("评论列表") 38 | private List comments = Collections.emptyList(); 39 | @ApiModelProperty("创建时间") 40 | private LocalDateTime createTime; 41 | } 42 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/trend/mapper/CommentMapper.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.trend.mapper; 2 | 3 | import bupt.edu.jhc.chathub.server.trend.domain.entity.Comment; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | import org.apache.ibatis.annotations.Mapper; 6 | 7 | /** 8 | * @Description: 评论 mapper 9 | * @Author: JollyCorivuG 10 | * @CreateTime: 2024/1/22 11 | */ 12 | @Mapper 13 | public interface CommentMapper extends BaseMapper { 14 | } 15 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/trend/mapper/FeedsMapper.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.trend.mapper; 2 | 3 | import bupt.edu.jhc.chathub.server.trend.domain.entity.Feeds; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | import org.apache.ibatis.annotations.Mapper; 6 | 7 | /** 8 | * @Description: feed 流 mapper 9 | * @Author: JollyCorivuG 10 | * @CreateTime: 2024/1/22 11 | */ 12 | @Mapper 13 | public interface FeedsMapper extends BaseMapper { 14 | } 15 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/trend/mapper/LikeMapper.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.trend.mapper; 2 | 3 | import bupt.edu.jhc.chathub.server.trend.domain.entity.Like; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | import org.apache.ibatis.annotations.Mapper; 6 | 7 | /** 8 | * @Description: 点赞 mapper 9 | * @Author: JollyCorivuG 10 | * @CreateTime: 2024/1/22 11 | */ 12 | @Mapper 13 | public interface LikeMapper extends BaseMapper { 14 | } 15 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/trend/mapper/TalkMapper.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.trend.mapper; 2 | 3 | import bupt.edu.jhc.chathub.server.trend.domain.entity.Talk; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | import org.apache.ibatis.annotations.Mapper; 6 | 7 | /** 8 | * @Description: 说说 mapper 9 | * @Author: JollyCorivuG 10 | * @CreateTime: 2024/1/22 11 | */ 12 | @Mapper 13 | public interface TalkMapper extends BaseMapper { 14 | } 15 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/trend/mq/feeds/FeedsInitMain.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.trend.mq.feeds; 2 | 3 | import bupt.edu.jhc.chathub.common.domain.constants.MqConstants; 4 | import com.rabbitmq.client.Channel; 5 | import com.rabbitmq.client.Connection; 6 | import com.rabbitmq.client.ConnectionFactory; 7 | import lombok.extern.slf4j.Slf4j; 8 | 9 | /** 10 | * @Description: feed 流消息队列初始化 11 | * @Author: JollyCorivuG 12 | * @CreateTime: 2023/8/12 13 | */ 14 | @Slf4j 15 | public class FeedsInitMain { 16 | 17 | public static void main(String[] args) { 18 | try { 19 | ConnectionFactory factory = new ConnectionFactory(); 20 | factory.setUsername("admin"); 21 | factory.setPassword("admin123"); 22 | factory.setHost("localhost"); 23 | Connection connection = factory.newConnection(); 24 | Channel channel = connection.createChannel(); 25 | channel.exchangeDeclare(MqConstants.FEEDS_EXCHANGE, "direct", true); 26 | channel.queueDeclare(MqConstants.FEEDS_QUEUE, true, false, false, null); 27 | channel.queueBind(MqConstants.FEEDS_QUEUE, MqConstants.FEEDS_EXCHANGE, MqConstants.FEEDS_ROUTING_KEY); 28 | } catch (Exception e) { 29 | log.error("初始化消息队列失败", e); 30 | } 31 | } 32 | } 33 | 34 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/trend/mq/feeds/FeedsMessage.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.trend.mq.feeds; 2 | 3 | import lombok.Data; 4 | import lombok.experimental.Accessors; 5 | 6 | /** 7 | * @Description: feed 流消息 8 | * @Author: JollyCorivuG 9 | * @CreateTime: 2023/8/12 10 | */ 11 | @Data 12 | @Accessors(chain = true) 13 | public class FeedsMessage { 14 | private Long authorId; 15 | private Long talkId; 16 | public static boolean isValid(FeedsMessage feedsMessage) { 17 | return feedsMessage != null && feedsMessage.getAuthorId() != null && feedsMessage.getTalkId() != null; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/trend/mq/feeds/FeedsMessageConsumer.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.trend.mq.feeds; 2 | 3 | import bupt.edu.jhc.chathub.common.domain.constants.MqConstants; 4 | import bupt.edu.jhc.chathub.server.trend.domain.entity.Feeds; 5 | import bupt.edu.jhc.chathub.server.trend.mapper.FeedsMapper; 6 | import bupt.edu.jhc.chathub.server.user.service.IUserService; 7 | import cn.hutool.json.JSONUtil; 8 | import com.rabbitmq.client.Channel; 9 | import lombok.extern.slf4j.Slf4j; 10 | import org.springframework.amqp.rabbit.annotation.RabbitListener; 11 | import org.springframework.amqp.support.AmqpHeaders; 12 | import org.springframework.messaging.handler.annotation.Header; 13 | import org.springframework.stereotype.Component; 14 | 15 | import javax.annotation.Resource; 16 | import java.util.List; 17 | 18 | /** 19 | * @Description: feed 流消费者 20 | * @Author: JollyCorivuG 21 | * @CreateTime: 2023/8/12 22 | */ 23 | @Component 24 | @Slf4j 25 | public class FeedsMessageConsumer { 26 | @Resource 27 | private IUserService userService; 28 | 29 | @Resource 30 | private FeedsMapper feedsMapper; 31 | 32 | 33 | @RabbitListener(queues = {MqConstants.FEEDS_QUEUE}, ackMode = "MANUAL") 34 | public void receiveMessage(String message, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag) { 35 | log.info("接收到消息:{}", message); 36 | try { 37 | // 1.将message转换为FeedsMessage对象 38 | FeedsMessage feedsMsg = JSONUtil.toBean(message, FeedsMessage.class); 39 | if (!FeedsMessage.isValid(feedsMsg)) { 40 | log.error("收到的消息格式不正确"); 41 | channel.basicAck(deliveryTag, false); 42 | return; 43 | } 44 | 45 | // 2.根据authorId获取所有好友id 46 | List friendIds = userService.queryFriendIds(feedsMsg.getAuthorId()); 47 | friendIds.forEach(id -> { 48 | // 2.1构建feeds对象, 插入到数据库 49 | Feeds feeds = new Feeds(); 50 | feeds.setConnectUserId(id).setTalkId(feedsMsg.getTalkId()); 51 | feedsMapper.insert(feeds); 52 | }); 53 | 54 | // 3.手动确认消息已经被消费 55 | channel.basicAck(deliveryTag, false); 56 | } catch (Exception e) { 57 | throw new RuntimeException(e); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/trend/mq/feeds/FeedsMessageProducer.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.trend.mq.feeds; 2 | 3 | import bupt.edu.jhc.chathub.common.domain.constants.MqConstants; 4 | import org.springframework.amqp.rabbit.core.RabbitTemplate; 5 | import org.springframework.stereotype.Component; 6 | 7 | import javax.annotation.Resource; 8 | 9 | /** 10 | * @Description: feed 流生产者 11 | * @Author: JollyCorivuG 12 | * @CreateTime: 2023/8/12 13 | */ 14 | @Component 15 | public class FeedsMessageProducer { 16 | @Resource 17 | private RabbitTemplate rabbitTemplate; 18 | 19 | // 发送消息 20 | public void sendMessage(String message) { 21 | rabbitTemplate.convertAndSend(MqConstants.FEEDS_EXCHANGE, MqConstants.FEEDS_ROUTING_KEY, message); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/trend/mq/like/LikeInitMain.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.trend.mq.like; 2 | 3 | import bupt.edu.jhc.chathub.common.domain.constants.MqConstants; 4 | import com.rabbitmq.client.Channel; 5 | import com.rabbitmq.client.Connection; 6 | import com.rabbitmq.client.ConnectionFactory; 7 | import lombok.extern.slf4j.Slf4j; 8 | 9 | /** 10 | * @Description: like 消息队列初始化 11 | * @Author: JollyCorivuG 12 | * @CreateTime: 2023/8/16 13 | */ 14 | @Slf4j 15 | public class LikeInitMain { 16 | public static void main(String[] args) { 17 | try { 18 | ConnectionFactory factory = new ConnectionFactory(); 19 | factory.setUsername("admin"); 20 | factory.setPassword("admin123"); 21 | factory.setHost("localhost"); 22 | Connection connection = factory.newConnection(); 23 | Channel channel = connection.createChannel(); 24 | channel.exchangeDeclare(MqConstants.LIKE_EXCHANGE, "direct", true); 25 | channel.queueDeclare(MqConstants.LIKE_QUEUE, true, false, false, null); 26 | channel.queueBind(MqConstants.LIKE_QUEUE, MqConstants.LIKE_EXCHANGE, MqConstants.LIKE_ROUTING_KEY); 27 | } catch (Exception e) { 28 | log.error("初始化消息队列失败", e); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/trend/mq/like/LikeMessage.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.trend.mq.like; 2 | 3 | import lombok.Data; 4 | import lombok.experimental.Accessors; 5 | 6 | import java.time.LocalDateTime; 7 | 8 | /** 9 | * @Description: like 消息 10 | * @Author: JollyCorivuG 11 | * @CreateTime: 2023/8/16 12 | */ 13 | @Data 14 | @Accessors(chain = true) 15 | public class LikeMessage { 16 | private Long userId; 17 | private Long talkId; 18 | private Boolean isLike; 19 | private LocalDateTime createTime; 20 | 21 | public static boolean isValid(LikeMessage likeMessage) { 22 | return likeMessage != null && likeMessage.getUserId() != null && likeMessage.getTalkId() != null && likeMessage.getIsLike() != null; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/trend/mq/like/LikeMessageProducer.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.trend.mq.like; 2 | 3 | import bupt.edu.jhc.chathub.common.domain.constants.MqConstants; 4 | import org.springframework.amqp.rabbit.core.RabbitTemplate; 5 | import org.springframework.stereotype.Component; 6 | 7 | import javax.annotation.Resource; 8 | 9 | /** 10 | * @Description: like 消息生产者 11 | * @Author: JollyCorivuG 12 | * @CreateTime: 2023/8/16 13 | */ 14 | @Component 15 | public class LikeMessageProducer { 16 | @Resource 17 | private RabbitTemplate rabbitTemplate; 18 | 19 | // 发送消息 20 | public void sendMessage(String message) { 21 | rabbitTemplate.convertAndSend(MqConstants.LIKE_EXCHANGE, MqConstants.LIKE_ROUTING_KEY, message); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/trend/service/ICommentService.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.trend.service; 2 | 3 | import bupt.edu.jhc.chathub.server.trend.domain.dto.comment.PostCommentDTO; 4 | import bupt.edu.jhc.chathub.server.trend.domain.entity.Comment; 5 | import bupt.edu.jhc.chathub.server.trend.domain.vo.CommentVO; 6 | import com.baomidou.mybatisplus.extension.service.IService; 7 | 8 | /** 9 | * @Description: 评论服务接口 10 | * @Author: JollyCorivuG 11 | * @CreateTime: 2024/1/22 12 | */ 13 | public interface ICommentService extends IService { 14 | CommentVO convertCommentToVO(Comment comment); 15 | 16 | CommentVO postComment(Long userId, PostCommentDTO comment); 17 | } 18 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/trend/service/ITrendService.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.trend.service; 2 | 3 | import bupt.edu.jhc.chathub.common.domain.vo.request.CursorPageBaseReq; 4 | import bupt.edu.jhc.chathub.common.domain.vo.resp.CursorPageBaseResp; 5 | import bupt.edu.jhc.chathub.server.trend.domain.dto.talk.CreateTalkDTO; 6 | import bupt.edu.jhc.chathub.server.trend.domain.entity.Talk; 7 | import bupt.edu.jhc.chathub.server.trend.domain.vo.TalkVO; 8 | import com.baomidou.mybatisplus.extension.service.IService; 9 | 10 | /** 11 | * @Description: 动态服务接口 12 | * @Author: JollyCorivuG 13 | * @CreateTime: 2024/1/22 14 | */ 15 | public interface ITrendService extends IService { 16 | TalkVO convertTalkToVO(Talk talk); 17 | 18 | TalkVO createTalk(Long userId, CreateTalkDTO talk); 19 | 20 | CursorPageBaseResp getTalkPage(Long userId, CursorPageBaseReq req); 21 | 22 | void likeTalk(Long userId, Long talkId); 23 | 24 | void cancelLikeTalk(Long userId, Long talkId); 25 | } 26 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/trend/service/impl/CommentServiceImpl.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.trend.service.impl; 2 | 3 | import bupt.edu.jhc.chathub.server.trend.domain.dto.comment.PostCommentDTO; 4 | import bupt.edu.jhc.chathub.server.trend.domain.entity.Comment; 5 | import bupt.edu.jhc.chathub.server.trend.domain.vo.CommentVO; 6 | import bupt.edu.jhc.chathub.server.trend.mapper.CommentMapper; 7 | import bupt.edu.jhc.chathub.server.trend.service.ICommentService; 8 | import bupt.edu.jhc.chathub.server.user.domain.vo.UserVO; 9 | import bupt.edu.jhc.chathub.server.user.service.IUserService; 10 | import cn.hutool.core.bean.BeanUtil; 11 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 12 | import org.springframework.stereotype.Service; 13 | 14 | import javax.annotation.Resource; 15 | 16 | /** 17 | * @Description: 评论服务实现类 18 | * @Author: JollyCorivuG 19 | * @CreateTime: 2024/1/22 20 | */ 21 | @Service 22 | public class CommentServiceImpl extends ServiceImpl implements ICommentService { 23 | @Resource 24 | private IUserService userService; 25 | 26 | @Override 27 | public CommentVO convertCommentToVO(Comment comment) { 28 | // 1.先直接利用hutool工具进行拷贝 29 | CommentVO commentVO = BeanUtil.copyProperties(comment, CommentVO.class); 30 | 31 | // 2.查询发布该评论的用户信息 32 | UserVO sender = userService.convertUserToUserVO(comment.getSenderId(), userService.getById(comment.getSenderId())); 33 | commentVO.setSender(sender); 34 | 35 | // 3.查询被这条评论回复的用户信息 36 | if (comment.getReplyUserId() != 0) { 37 | UserVO replyUser = userService.convertUserToUserVO(comment.getReplyUserId(), userService.getById(comment.getReplyUserId())); 38 | commentVO.setReplyUser(replyUser); 39 | } 40 | 41 | // 4.返回 42 | return commentVO; 43 | } 44 | 45 | @Override 46 | public CommentVO postComment(Long userId, PostCommentDTO comment) { 47 | // 1.保存评论到数据库 48 | Comment saveComment = new Comment(); 49 | saveComment.setSenderId(userId) 50 | .setTalkId(comment.getTalkId()) 51 | .setContent(comment.getContent()) 52 | .setReplyUserId(comment.getReplyUserId()); 53 | save(saveComment); 54 | 55 | // 2.返回 56 | return convertCommentToVO(getById(saveComment.getId())); 57 | } 58 | } 59 | 60 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/trend/service/impl/FeedsServiceImpl.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.trend.service.impl; 2 | 3 | import bupt.edu.jhc.chathub.server.trend.domain.entity.Feeds; 4 | import bupt.edu.jhc.chathub.server.trend.mapper.FeedsMapper; 5 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 6 | import org.springframework.stereotype.Service; 7 | 8 | /** 9 | * @Description: feed 流实现类 10 | * @Author: JollyCorivuG 11 | * @CreateTime: 2024/1/22 12 | */ 13 | @Service 14 | public class FeedsServiceImpl extends ServiceImpl { 15 | } 16 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/user/controller/OSSController.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.user.controller; 2 | 3 | import bupt.edu.jhc.chathub.common.domain.vo.resp.Response; 4 | import bupt.edu.jhc.chathub.common.utils.context.RequestHolder; 5 | import bupt.edu.jhc.chathub.oss.minio.MinioTemplate; 6 | import io.swagger.annotations.Api; 7 | import io.swagger.annotations.ApiOperation; 8 | import org.springframework.web.bind.annotation.PostMapping; 9 | import org.springframework.web.bind.annotation.RequestMapping; 10 | import org.springframework.web.bind.annotation.RequestParam; 11 | import org.springframework.web.bind.annotation.RestController; 12 | import org.springframework.web.multipart.MultipartFile; 13 | 14 | import javax.annotation.Resource; 15 | 16 | /** 17 | * @Description: OSS 接口 18 | * @Author: JollyCorivuG 19 | * @CreateTime: 2024/1/22 20 | */ 21 | @RestController 22 | @RequestMapping("/api/oss") 23 | @Api(tags = "OSS相关接口") 24 | public class OSSController { 25 | @Resource 26 | private MinioTemplate minioTemplate; 27 | 28 | 29 | @PostMapping("/upload") 30 | @ApiOperation("上传文件") 31 | public Response uploadFile(@RequestParam("file") MultipartFile file) { 32 | Long userId = RequestHolder.get().getUid(); 33 | return Response.success(minioTemplate.uploadFile(userId, file)); 34 | } 35 | } -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/user/domain/dto/LoginFormDTO.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.user.domain.dto; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | import lombok.experimental.Accessors; 7 | 8 | /** 9 | * @Description: 登录表单 10 | * @Author: JollyCorivuG 11 | * @CreateTime: 2024/1/21 12 | */ 13 | @Data 14 | @Accessors(chain = true) 15 | @ApiModel("登录表单") 16 | public class LoginFormDTO { 17 | @ApiModelProperty("账号") 18 | private String account; 19 | @ApiModelProperty("密码") 20 | private String password; 21 | } 22 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/user/domain/dto/PhoneLoginFormDTO.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.user.domain.dto; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | import lombok.experimental.Accessors; 7 | 8 | /** 9 | * @Description: 手机号登录表单 10 | * @Author: JollyCorivuG 11 | * @CreateTime: 2024/1/21 12 | */ 13 | @Data 14 | @Accessors(chain = true) 15 | @ApiModel("手机号登录表单") 16 | public class PhoneLoginFormDTO { 17 | @ApiModelProperty("手机号") 18 | private String phone; 19 | @ApiModelProperty("验证码") 20 | private String code; 21 | } 22 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/user/domain/dto/RegisterFormDTO.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.user.domain.dto; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | import lombok.experimental.Accessors; 7 | 8 | /** 9 | * @Description: 注册表单 10 | * @Author: JollyCorivuG 11 | * @CreateTime: 2024/1/21 12 | */ 13 | @Data 14 | @Accessors(chain = true) 15 | @ApiModel("注册表单") 16 | public class RegisterFormDTO { 17 | @ApiModelProperty("手机号") 18 | private String phone; 19 | @ApiModelProperty("账号") 20 | private String account; 21 | @ApiModelProperty("密码") 22 | private String password; 23 | } 24 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/user/domain/dto/UserDTO.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.user.domain.dto; 2 | 3 | import lombok.Data; 4 | import lombok.experimental.Accessors; 5 | 6 | /** 7 | * @Description: 用户 dto 8 | * @Author: JollyCorivuG 9 | * @CreateTime: 2024/1/21 10 | */ 11 | @Data 12 | @Accessors(chain = true) 13 | public class UserDTO { 14 | private Long id; 15 | private String account; 16 | private String nickName; 17 | private String avatarUrl; 18 | private Integer level; 19 | private Integer friendCount; 20 | private Integer groupCount; 21 | } 22 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/user/domain/entity/User.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.user.domain.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableField; 5 | import com.baomidou.mybatisplus.annotation.TableId; 6 | import com.baomidou.mybatisplus.annotation.TableName; 7 | import lombok.Data; 8 | import lombok.experimental.Accessors; 9 | 10 | import java.time.LocalDateTime; 11 | 12 | /** 13 | * @Description: 用户实体对象 14 | * @Author: JollyCorivuG 15 | * @CreateTime: 2024/1/21 16 | */ 17 | @Data 18 | @Accessors(chain = true) 19 | @TableName("tb_user") 20 | public class User { 21 | @TableId(value = "id", type = IdType.AUTO) 22 | private Long id; 23 | @TableField("account") 24 | private String account; 25 | @TableField("password") 26 | private String password; 27 | @TableField("phone") 28 | private String phone; 29 | @TableField("nick_name") 30 | private String nickName; 31 | @TableField("avatar_url") 32 | private String avatarUrl; 33 | @TableField("level") 34 | private Integer level; 35 | @TableField("friend_count") 36 | private Integer friendCount; 37 | @TableField("group_count") 38 | private Integer groupCount; 39 | @TableField("create_time") 40 | private LocalDateTime createTime; 41 | @TableField("update_time") 42 | private LocalDateTime updateTime; 43 | } 44 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/user/domain/vo/ForceLogoutInfo.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.user.domain.vo; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Builder; 7 | import lombok.Data; 8 | import lombok.NoArgsConstructor; 9 | 10 | import java.time.LocalDateTime; 11 | 12 | /** 13 | * @Description: 强制下线信息 14 | * @Author: JollyCorivuG 15 | * @CreateTime: 2024/1/21 16 | */ 17 | @Data 18 | @Builder 19 | @AllArgsConstructor 20 | @NoArgsConstructor 21 | @ApiModel("强制下线信息") 22 | public class ForceLogoutInfo { 23 | /** 24 | * 被强制下线的时间 25 | */ 26 | @ApiModelProperty("被强制下线的时间") 27 | LocalDateTime time; 28 | } 29 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/user/domain/vo/UserVO.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.user.domain.vo; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.Data; 6 | import lombok.experimental.Accessors; 7 | 8 | import java.time.LocalDateTime; 9 | 10 | /** 11 | * @Description: 用户 vo 12 | * @Author: JollyCorivuG 13 | * @CreateTime: 2024/1/21 14 | */ 15 | @Data 16 | @Accessors(chain = true) 17 | @ApiModel("用户信息") 18 | public class UserVO { 19 | @ApiModelProperty("用户 id") 20 | private Long id; 21 | @ApiModelProperty("手机号") 22 | private String phone; 23 | @ApiModelProperty("账号") 24 | private String account; 25 | @ApiModelProperty("昵称") 26 | private String nickName; 27 | @ApiModelProperty("头像") 28 | private String avatarUrl; 29 | @ApiModelProperty("等级") 30 | private Integer level; 31 | @ApiModelProperty("好友数") 32 | private Integer friendCount; 33 | @ApiModelProperty("群组数") 34 | private Integer groupCount; 35 | @ApiModelProperty("是否在线") 36 | private Boolean isOnline; 37 | @ApiModelProperty("是否为好友") 38 | private Boolean isFriend; 39 | @ApiModelProperty("成为好友时间") 40 | private LocalDateTime becomeFriendTime; 41 | @ApiModelProperty("属于的房间 id") 42 | private Long roomId; 43 | } 44 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/user/mapper/UserMapper.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.user.mapper; 2 | 3 | import bupt.edu.jhc.chathub.server.user.domain.entity.User; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | import org.apache.ibatis.annotations.Mapper; 6 | 7 | /** 8 | * @Description: 用户 mapper 9 | * @Author: JollyCorivuG 10 | * @CreateTime: 2024/1/21 11 | */ 12 | @Mapper 13 | public interface UserMapper extends BaseMapper { 14 | } 15 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/user/service/IUserService.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.user.service; 2 | 3 | import bupt.edu.jhc.chathub.common.domain.vo.resp.Response; 4 | import bupt.edu.jhc.chathub.server.user.domain.dto.LoginFormDTO; 5 | import bupt.edu.jhc.chathub.server.user.domain.dto.PhoneLoginFormDTO; 6 | import bupt.edu.jhc.chathub.server.user.domain.dto.RegisterFormDTO; 7 | import bupt.edu.jhc.chathub.server.user.domain.dto.UserDTO; 8 | import bupt.edu.jhc.chathub.server.user.domain.entity.User; 9 | import bupt.edu.jhc.chathub.server.user.domain.vo.UserVO; 10 | import com.baomidou.mybatisplus.extension.service.IService; 11 | 12 | import java.util.List; 13 | 14 | /** 15 | * @Description: 用户服务接口类 16 | * @Author: JollyCorivuG 17 | * @CreateTime: 2024/1/21 18 | */ 19 | public interface IUserService extends IService { 20 | Response login(LoginFormDTO loginForm); 21 | 22 | Response phoneLogin(PhoneLoginFormDTO phoneLoginForm); 23 | 24 | Response register(RegisterFormDTO registerForm); 25 | 26 | Response phoneCode(String phone); 27 | 28 | Response getUserInfo(Long selfId, Long userId); 29 | 30 | Response> queryByKeyword(String keyword, Integer page); 31 | 32 | UserVO convertUserToUserVO(Long selfId, User user); 33 | 34 | UserDTO getUserByToken(String token); 35 | 36 | List queryFriendIds(Long userId); 37 | 38 | List queryGroupIds(Long userId); 39 | 40 | Response updateUserInfo(Long selfId, UserVO userVO); 41 | } 42 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/websocket/domain/enums/WSReqEnum.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.websocket.domain.enums; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | 6 | import java.util.Arrays; 7 | import java.util.Map; 8 | import java.util.function.Function; 9 | import java.util.stream.Collectors; 10 | 11 | /** 12 | * @Description: ws 请求类型 13 | * @Author: JollyCorivuG 14 | * @CreateTime: 2024/1/21 15 | */ 16 | @AllArgsConstructor 17 | @Getter 18 | public enum WSReqEnum { 19 | HEARTBEAT(0, "心跳包"), 20 | AUTHORIZE(1, "登录认证"), 21 | ; 22 | 23 | private final Integer type; 24 | private final String desc; 25 | 26 | private static final Map cache; 27 | 28 | static { 29 | cache = Arrays.stream(WSReqEnum.values()).collect(Collectors.toMap(WSReqEnum::getType, Function.identity())); 30 | } 31 | 32 | 33 | public static WSReqEnum of(Integer type) { 34 | return cache.get(type); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/websocket/domain/req/WSReqDTO.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.websocket.domain.req; 2 | 3 | import lombok.Data; 4 | import lombok.experimental.Accessors; 5 | 6 | /** 7 | * @Description: ws 请求 8 | * @Author: JollyCorivuG 9 | * @CreateTime: 2024/1/21 10 | */ 11 | @Data 12 | @Accessors(chain = true) 13 | public class WSReqDTO { 14 | Integer type; 15 | String data; 16 | } 17 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/websocket/domain/resp/WSResponse.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.websocket.domain.resp; 2 | 3 | import lombok.Data; 4 | import lombok.experimental.Accessors; 5 | 6 | /** 7 | * @Description: ws 响应 8 | * @Author: JollyCorivuG 9 | * @CreateTime: 2024/1/21 10 | */ 11 | @Data 12 | @Accessors(chain = true) 13 | public class WSResponse { 14 | Integer type; 15 | T data; 16 | public static WSResponse build(Integer type, T data) { 17 | WSResponse response = new WSResponse<>(); 18 | return response.setType(type).setData(data); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/websocket/handler/HttpHeaderHandler.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.websocket.handler; 2 | 3 | import bupt.edu.jhc.chathub.server.websocket.utils.NettyUtils; 4 | import cn.hutool.core.net.url.UrlBuilder; 5 | import io.netty.channel.ChannelHandlerContext; 6 | import io.netty.channel.ChannelInboundHandlerAdapter; 7 | import io.netty.handler.codec.http.FullHttpRequest; 8 | 9 | import java.util.Optional; 10 | 11 | /** 12 | * @Description: Http 请求头处理器 13 | * @Author: JollyCorivuG 14 | * @CreateTime: 2024/1/21 15 | */ 16 | public class HttpHeaderHandler extends ChannelInboundHandlerAdapter { 17 | @Override 18 | public void channelRead(ChannelHandlerContext ctx, Object msg) { 19 | if (msg instanceof FullHttpRequest) { 20 | FullHttpRequest request = (FullHttpRequest) msg; 21 | UrlBuilder urlBuilder = UrlBuilder.ofHttp(request.uri()); 22 | // 获取token参数以及roomId 23 | String token = Optional.ofNullable(urlBuilder.getQuery()).map(k -> k.get("token")).map(CharSequence::toString).orElse(""); 24 | Long roomId = Optional.ofNullable(urlBuilder.getQuery()).map(k -> k.get("roomId")).map(CharSequence::toString).map(Long::parseLong).orElse(0L); 25 | NettyUtils.setAttr(ctx.channel(), NettyUtils.TOKEN, token); 26 | NettyUtils.setAttr(ctx.channel(), NettyUtils.ROOM_ID, roomId); 27 | // 设置请求路径 28 | request.setUri(urlBuilder.getPath().toString()); 29 | // 移除当前handler 30 | ctx.pipeline().remove(this); 31 | ctx.fireChannelRead(request); 32 | } else { 33 | ctx.fireChannelRead(msg); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/websocket/handler/NettyWebSocketServerHandler.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.websocket.handler; 2 | 3 | import bupt.edu.jhc.chathub.server.websocket.domain.enums.WSReqEnum; 4 | import bupt.edu.jhc.chathub.server.websocket.domain.req.WSReqDTO; 5 | import bupt.edu.jhc.chathub.server.websocket.service.IWebSocketService; 6 | import cn.hutool.extra.spring.SpringUtil; 7 | import cn.hutool.json.JSONUtil; 8 | import io.netty.channel.Channel; 9 | import io.netty.channel.ChannelHandlerContext; 10 | import io.netty.channel.SimpleChannelInboundHandler; 11 | import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; 12 | import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler; 13 | import io.netty.handler.timeout.IdleStateEvent; 14 | import lombok.extern.slf4j.Slf4j; 15 | 16 | /** 17 | * @Description: ws 服务处理器 18 | * @Author: JollyCorivuG 19 | * @CreateTime: 2024/1/21 20 | */ 21 | @Slf4j 22 | public class NettyWebSocketServerHandler extends SimpleChannelInboundHandler { 23 | private final IWebSocketService webSocketService = SpringUtil.getBean(IWebSocketService.class); 24 | 25 | @Override 26 | public void handlerAdded(ChannelHandlerContext ctx) { 27 | log.info("客户端尝试连接"); 28 | } 29 | 30 | @Override 31 | public void handlerRemoved(ChannelHandlerContext ctx) { 32 | webSocketService.remove(ctx.channel()); 33 | webSocketService.showOnlineUser(); 34 | } 35 | 36 | @Override 37 | public void channelInactive(ChannelHandlerContext ctx) { 38 | log.warn("触发 channelInactive 掉线![{}]", ctx.channel().id()); 39 | } 40 | 41 | @Override 42 | public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { 43 | if (evt instanceof IdleStateEvent) { 44 | IdleStateEvent idleStateEvent = (IdleStateEvent)evt; 45 | // 如果处于读空闲状态,则关闭连接 46 | if (idleStateEvent.state() == IdleStateEvent.READER_IDLE_STATE_EVENT.state()) { 47 | webSocketService.remove(ctx.channel()); 48 | ctx.channel().close(); 49 | webSocketService.showOnlineUser(); 50 | } 51 | } else if (evt instanceof WebSocketServerProtocolHandler.HandshakeComplete) { 52 | // 握手成功后, 需要进行认证 53 | webSocketService.authorize(ctx.channel()); 54 | webSocketService.showOnlineUser(); 55 | } 56 | super.userEventTriggered(ctx, evt); 57 | } 58 | 59 | @Override 60 | protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) { 61 | WSReqDTO wsReq = JSONUtil.toBean(msg.text(), WSReqDTO.class); 62 | WSReqEnum wsReqEnum = WSReqEnum.of(wsReq.getType()); 63 | switch (wsReqEnum) { 64 | case HEARTBEAT: { 65 | break; 66 | } 67 | case AUTHORIZE: { 68 | log.info("收到认证包"); 69 | break; 70 | } 71 | default: { 72 | log.info("收到未知类型的包"); 73 | break; 74 | } 75 | } 76 | } 77 | 78 | @Override 79 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 80 | log.error("NettyWebSocketServerHandler Exception!", cause); 81 | Channel channel = ctx.channel(); 82 | if (channel.isActive()) ctx.close(); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/websocket/service/IWebSocketService.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.websocket.service; 2 | 3 | import bupt.edu.jhc.chathub.server.websocket.domain.resp.WSResponse; 4 | import io.netty.channel.Channel; 5 | 6 | /** 7 | * @Description: ws 服务接口 8 | * @Author: JollyCorivuG 9 | * @CreateTime: 2024/1/21 10 | */ 11 | public interface IWebSocketService { 12 | void authorize(Channel channel); 13 | void connect(Channel channel); 14 | void remove(Channel channel); 15 | void sendToAssignedRoom(Long roomId, WSResponse wsResponse, Long skipUid); 16 | void showOnlineUser(); 17 | 18 | boolean isExist(Long userId); 19 | } 20 | -------------------------------------------------------------------------------- /chathub-server/src/main/java/bupt/edu/jhc/chathub/server/websocket/utils/NettyUtils.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.server.websocket.utils; 2 | 3 | import io.netty.channel.Channel; 4 | import io.netty.util.AttributeKey; 5 | 6 | /** 7 | * @Description: Netty 工具类 8 | * @Author: JollyCorivuG 9 | * @CreateTime: 2024/1/21 10 | */ 11 | public class NettyUtils { 12 | public static AttributeKey TOKEN = AttributeKey.valueOf("token"); // token 13 | public static AttributeKey UID = AttributeKey.valueOf("uid"); // uid 14 | public static AttributeKey ROOM_ID = AttributeKey.valueOf("roomId"); // 房间id 15 | 16 | public static void setAttr(Channel channel, AttributeKey attributeKey, T data) { 17 | channel.attr(attributeKey).set(data); 18 | } 19 | 20 | public static T getAttr(Channel channel, AttributeKey attributeKey) { 21 | return channel.attr(attributeKey).get(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /chathub-server/src/main/resources/application.yaml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8081 3 | address: 0.0.0.0 4 | servlet: 5 | session: 6 | cookie: 7 | max-age: 2592000 8 | spring: 9 | application: 10 | name: chathub 11 | profiles: 12 | active: dev 13 | mvc: 14 | pathmatch: 15 | matching-strategy: ant_path_matcher 16 | session: 17 | timeout: 2592000 18 | datasource: 19 | driver-class-name: com.mysql.cj.jdbc.Driver 20 | url: jdbc:mysql://${chathub.mysql.ip}:${chathub.mysql.port}/${chathub.mysql.db}?useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true 21 | username: ${chathub.mysql.username} 22 | password: ${chathub.mysql.password} 23 | hikari: 24 | minimum-idle: 3 25 | maximum-pool-size: 10 26 | max-lifetime: 30000 27 | connection-test-query: SELECT 1 28 | rabbitmq: 29 | host: ${chathub.rabbitmq.host} 30 | port: ${chathub.rabbitmq.port} 31 | username: ${chathub.rabbitmq.username} 32 | password: ${chathub.rabbitmq.password} 33 | redis: 34 | host: ${chathub.redis.host} 35 | port: ${chathub.redis.port} 36 | database: 0 37 | timeout: 1800000 38 | password: ${chathub.redis.password} 39 | lettuce: 40 | pool: 41 | max-wait: -1 42 | max-idle: 5 43 | min-idle: 0 44 | max-active: 20 45 | jackson: 46 | default-property-inclusion: non_null 47 | time-zone: GMT+8 48 | servlet: 49 | multipart: 50 | enabled: true 51 | max-file-size: 10MB 52 | max-request-size: 10MB 53 | mybatis-plus: 54 | mapper-locations: classpath:mapper/**/*.xml 55 | configuration: 56 | log-impl: org.apache.ibatis.logging.stdout.StdOutImpl 57 | map-underscore-to-camel-case: true 58 | oss: 59 | enabled: true 60 | type: minio 61 | endpoint: ${chathub.minio.endpoint} 62 | access-key: ${chathub.minio.access-key} 63 | secret-key: ${chathub.minio.secret-key} 64 | bucket-name: ${chathub.minio.bucket-name} -------------------------------------------------------------------------------- /chathub-server/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | ${CONSOLE_LOG_PATTERN} 18 | 19 | 20 | 21 | 22 | ${LOG_PATH}/${LOG_FILE}.log 23 | true 24 | 25 | ${CONSOLE_LOG_PATTERN} 26 | 27 | 28 | 29 | 30 | ${LOG_PATH}/archived/${LOG_FILE}.%d{dd-MM-yyyy}.log 31 | 32 | 33 | 30 34 | 35 | 5GB 36 | 37 | 38 | 39 | 40 | 41 | ERROR 42 | ACCEPT 43 | DENY 44 | 45 | ${LOG_PATH}/${LOG_FILE}.error.log 46 | true 47 | 48 | ${CONSOLE_LOG_PATTERN} 49 | 50 | 51 | 52 | ${LOG_PATH}/archived/${LOG_FILE}.%d{dd-MM-yyyy}.error.log 53 | 54 | 30 55 | 2GB 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /chathub-tools/chathub-frequency-control/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | bupt.edu.jhc 8 | chathub-tools 9 | 1.0-SNAPSHOT 10 | 11 | 12 | chathub-frequency-control 13 | 14 | 15 | 8 16 | 8 17 | UTF-8 18 | 19 | 20 | 21 | 22 | bupt.edu.jhc 23 | chathub-common 24 | 1.0-SNAPSHOT 25 | 26 | 27 | -------------------------------------------------------------------------------- /chathub-tools/chathub-frequency-control/src/main/java/bupt/edu/jhc/chathub/frequency_control/annotation/FrequencyControl.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.frequency_control.annotation; 2 | 3 | import java.lang.annotation.*; 4 | import java.util.concurrent.TimeUnit; 5 | 6 | /** 7 | * @Description: 频控注解 8 | * @Author: JollyCorivuG 9 | * @CreateTime: 2024/2/1 10 | */ 11 | @Repeatable(FrequencyControlContainer.class) // 可重复 12 | @Retention(RetentionPolicy.RUNTIME) // 运行时生效 13 | @Target(ElementType.METHOD) // 作用在方法上 14 | public @interface FrequencyControl { 15 | /** 16 | * key 的前缀, 默认取方法全限定名, 除非在不同方法上对同一个资源做频控, 就自己指定 17 | * @return 18 | */ 19 | String prefixKey() default ""; 20 | 21 | /** 22 | * 频控对象, 默认 el 表达式指定具体的频控对象 23 | * 对于 UID 模式, 需要是 http 入口的对象, 保证 RequestHolder 里有值 24 | * @return 25 | */ 26 | Target target() default Target.EL; 27 | 28 | /** 29 | * springEl 表达式, target = EL 必填 30 | * @return 31 | */ 32 | String spEl() default ""; 33 | 34 | /** 35 | * 频控时间范围, 默认单位秒 36 | * @return 37 | */ 38 | int time(); 39 | 40 | /** 41 | * 频控时间单位, 默认秒 42 | * @return 单位 43 | */ 44 | TimeUnit unit() default TimeUnit.SECONDS; 45 | 46 | /** 47 | * 单位时间内最大访问次数 48 | * @return 次数 49 | */ 50 | int count(); 51 | 52 | 53 | enum Target { 54 | UID, EL 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /chathub-tools/chathub-frequency-control/src/main/java/bupt/edu/jhc/chathub/frequency_control/annotation/FrequencyControlContainer.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.frequency_control.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * @Description: 频控注解容器 10 | * @Author: JollyCorivuG 11 | * @CreateTime: 2024/2/1 12 | */ 13 | @Retention(RetentionPolicy.RUNTIME) // 运行时生效 14 | @Target(ElementType.METHOD) // 作用在方法上 15 | public @interface FrequencyControlContainer { 16 | FrequencyControl[] value(); 17 | } 18 | -------------------------------------------------------------------------------- /chathub-tools/chathub-frequency-control/src/main/java/bupt/edu/jhc/chathub/frequency_control/domain/FrequencyControlDTO.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.frequency_control.domain; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | import java.util.concurrent.TimeUnit; 9 | 10 | /** 11 | * @Description: 频控策略定义 12 | * @Author: JollyCorivuG 13 | * @CreateTime: 2024/2/1 14 | */ 15 | @Data 16 | @Builder 17 | @NoArgsConstructor 18 | @AllArgsConstructor 19 | public class FrequencyControlDTO { 20 | private String key; 21 | private Integer time; 22 | private TimeUnit unit; 23 | private Integer count; 24 | } 25 | -------------------------------------------------------------------------------- /chathub-tools/chathub-frequency-control/src/main/java/bupt/edu/jhc/chathub/frequency_control/service/FrequencyControlStrategyFactory.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.frequency_control.service; 2 | 3 | import bupt.edu.jhc.chathub.frequency_control.domain.FrequencyControlDTO; 4 | 5 | import java.util.Map; 6 | import java.util.concurrent.ConcurrentHashMap; 7 | 8 | /** 9 | * @Description: 限流策略工厂 10 | * @Author: JollyCorivuG 11 | * @CreateTime: 2024/2/1 12 | */ 13 | public class FrequencyControlStrategyFactory { 14 | /** 15 | * 指定时间内总次数限流 16 | */ 17 | public static final String TOTAL_COUNT_WITH_IN_FIX_TIME_FREQUENCY_CONTROLLER = "TotalCountWithInFixTime"; 18 | 19 | /** 20 | * 限流策略集合 21 | */ 22 | static Map> frequencyControlServiceStrategyMap = new ConcurrentHashMap<>(8); 23 | 24 | public static void registerFrequencyController(String strategyName, AbstractFrequencyControlService abstractFrequencyControlService) { 25 | frequencyControlServiceStrategyMap.put(strategyName, abstractFrequencyControlService); 26 | } 27 | 28 | @SuppressWarnings("unchecked") 29 | public static AbstractFrequencyControlService getFrequencyControllerByName(String strategyName) { 30 | return (AbstractFrequencyControlService) frequencyControlServiceStrategyMap.get(strategyName); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /chathub-tools/chathub-frequency-control/src/main/java/bupt/edu/jhc/chathub/frequency_control/service/strategy/TotalCountWithInFixTimeFrequencyController.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.frequency_control.service.strategy; 2 | 3 | import bupt.edu.jhc.chathub.frequency_control.domain.FrequencyControlDTO; 4 | import bupt.edu.jhc.chathub.frequency_control.service.AbstractFrequencyControlService; 5 | import bupt.edu.jhc.chathub.frequency_control.utils.RedisUtils2; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.stereotype.Service; 8 | 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | import java.util.Map; 12 | import java.util.Objects; 13 | 14 | import static bupt.edu.jhc.chathub.frequency_control.service.FrequencyControlStrategyFactory.TOTAL_COUNT_WITH_IN_FIX_TIME_FREQUENCY_CONTROLLER; 15 | 16 | /** 17 | * @Description: 固定时间内不超过固定次数的限流类 18 | * @Author: JollyCorivuG 19 | * @CreateTime: 2024/2/1 20 | */ 21 | @Slf4j 22 | @Service 23 | public class TotalCountWithInFixTimeFrequencyController extends AbstractFrequencyControlService { 24 | 25 | 26 | /** 27 | * 是否达到限流阈值 子类实现 每个子类都可以自定义自己的限流逻辑判断 28 | * @param frequencyControlMap 定义的注解频控 Map中的Key-对应redis的单个频控的Key Map中的Value-对应redis的单个频控的Key限制的Value 29 | * @return true-方法被限流 false-方法没有被限流 30 | */ 31 | @Override 32 | protected boolean reachRateLimit(Map frequencyControlMap) { 33 | //批量获取redis统计的值 34 | List frequencyKeys = new ArrayList<>(frequencyControlMap.keySet()); 35 | List countList = RedisUtils2.mget(frequencyKeys, Integer.class); 36 | for (int i = 0; i < frequencyKeys.size(); i++) { 37 | String key = frequencyKeys.get(i); 38 | Integer count = countList.get(i); 39 | int frequencyControlCount = frequencyControlMap.get(key).getCount(); 40 | if (Objects.nonNull(count) && count >= frequencyControlCount) { 41 | //频率超过了 42 | log.warn("frequencyControl limit key:{},count:{}", key, count); 43 | return true; 44 | } 45 | } 46 | return false; 47 | } 48 | 49 | /** 50 | * 增加限流统计次数 子类实现 每个子类都可以自定义自己的限流统计信息增加的逻辑 51 | * @param frequencyControlMap 定义的注解频控 Map中的Key-对应redis的单个频控的Key Map中的Value-对应redis的单个频控的Key限制的Value 52 | */ 53 | @Override 54 | protected void addFrequencyControlStatisticsCount(Map frequencyControlMap) { 55 | frequencyControlMap.forEach((k, v) -> RedisUtils2.inc(k, v.getTime(), v.getUnit())); 56 | } 57 | 58 | @Override 59 | protected String getStrategyName() { 60 | return TOTAL_COUNT_WITH_IN_FIX_TIME_FREQUENCY_CONTROLLER; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /chathub-tools/chathub-frequency-control/src/main/java/bupt/edu/jhc/chathub/frequency_control/utils/FrequencyControlUtils.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.frequency_control.utils; 2 | 3 | import bupt.edu.jhc.chathub.common.domain.enums.ErrorStatus; 4 | import bupt.edu.jhc.chathub.common.utils.exception.ThrowUtils; 5 | import bupt.edu.jhc.chathub.frequency_control.domain.FrequencyControlDTO; 6 | import bupt.edu.jhc.chathub.frequency_control.service.AbstractFrequencyControlService; 7 | import bupt.edu.jhc.chathub.frequency_control.service.FrequencyControlStrategyFactory; 8 | import org.springframework.util.ObjectUtils; 9 | 10 | import java.util.List; 11 | 12 | /** 13 | * @Description: 频控工具类 14 | * @Author: JollyCorivuG 15 | * @CreateTime: 2024/2/1 16 | */ 17 | public class FrequencyControlUtils { 18 | public static T executeWithFrequencyControlList(String strategyName, List frequencyControlList, AbstractFrequencyControlService.SupplierThrowWithoutParam supplier) throws Throwable { 19 | boolean existsFrequencyControlHasNullKey = frequencyControlList.stream().anyMatch(frequencyControl -> ObjectUtils.isEmpty(frequencyControl.getKey())); 20 | ThrowUtils.throwIf(existsFrequencyControlHasNullKey, ErrorStatus.SYSTEM_ERROR, "限流策略的 Key 字段不允许出现空值"); 21 | AbstractFrequencyControlService frequencyController = FrequencyControlStrategyFactory.getFrequencyControllerByName(strategyName); 22 | return frequencyController.executeWithFrequencyControlList(frequencyControlList, supplier); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /chathub-tools/chathub-frequency-control/src/main/java/bupt/edu/jhc/chathub/frequency_control/utils/JsonUtils.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.frequency_control.utils; 2 | 3 | 4 | import com.fasterxml.jackson.core.JsonProcessingException; 5 | import com.fasterxml.jackson.databind.JsonNode; 6 | import com.fasterxml.jackson.databind.ObjectMapper; 7 | 8 | /** 9 | * @Description: json 工具类 10 | * @Author: JollyCorivuG 11 | * @CreateTime: 2024/2/1 12 | */ 13 | public class JsonUtils { 14 | private static final ObjectMapper jsonMapper = new ObjectMapper(); 15 | 16 | public static T toObj(String str, Class clz) { 17 | try { 18 | return jsonMapper.readValue(str, clz); 19 | } catch (JsonProcessingException e) { 20 | throw new UnsupportedOperationException(e); 21 | } 22 | } 23 | 24 | public static JsonNode toJsonNode(String str) { 25 | try { 26 | return jsonMapper.readTree(str); 27 | } catch (JsonProcessingException e) { 28 | throw new UnsupportedOperationException(e); 29 | } 30 | } 31 | 32 | public static String toStr(Object t) { 33 | try { 34 | return jsonMapper.writeValueAsString(t); 35 | } catch (Exception e) { 36 | throw new UnsupportedOperationException(e); 37 | } 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /chathub-tools/chathub-frequency-control/src/main/java/bupt/edu/jhc/chathub/frequency_control/utils/SpElUtils.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.frequency_control.utils; 2 | 3 | import org.springframework.core.DefaultParameterNameDiscoverer; 4 | import org.springframework.expression.EvaluationContext; 5 | import org.springframework.expression.Expression; 6 | import org.springframework.expression.ExpressionParser; 7 | import org.springframework.expression.spel.standard.SpelExpressionParser; 8 | import org.springframework.expression.spel.support.StandardEvaluationContext; 9 | 10 | import java.lang.reflect.Method; 11 | import java.util.Optional; 12 | 13 | /** 14 | * @Description: Spring EL 表达式解析 15 | * @Author: JollyCorivuG 16 | * @CreateTime: 2024/2/1 17 | */ 18 | public class SpElUtils { 19 | private static final ExpressionParser parser = new SpelExpressionParser(); // el 解析器 20 | private static final DefaultParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer(); // 发现方法参数的名称 21 | 22 | public static String parseSpEl(Method method, Object[] args, String spEl) { 23 | String[] params = Optional.ofNullable(parameterNameDiscoverer.getParameterNames(method)).orElse(new String[]{}); // 解析参数名 24 | EvaluationContext context = new StandardEvaluationContext(); // el解析需要的上下文对象 25 | for (int i = 0; i < params.length; i++) { 26 | context.setVariable(params[i], args[i]); // 所有参数都作为原材料扔进去 27 | } 28 | Expression expression = parser.parseExpression(spEl); 29 | return expression.getValue(context, String.class); 30 | } 31 | 32 | public static String getMethodKey(Method method) { 33 | return method.getDeclaringClass() + "#" + method.getName(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /chathub-tools/chathub-oss-starter/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | bupt.edu.jhc 8 | chathub-tools 9 | 1.0-SNAPSHOT 10 | 11 | 12 | chathub-oss-starter 13 | 14 | 15 | 8 16 | 8 17 | UTF-8 18 | 19 | 20 | 21 | 22 | org.projectlombok 23 | lombok 24 | 25 | 26 | cn.hutool 27 | hutool-all 28 | 29 | 30 | org.springframework.boot 31 | spring-boot-starter 32 | 33 | 34 | org.springframework.boot 35 | spring-boot-starter-web 36 | 37 | 38 | io.minio 39 | minio 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /chathub-tools/chathub-oss-starter/src/main/java/bupt/edu/jhc/chathub/oss/FileUtils.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.oss; 2 | 3 | import cn.hutool.core.lang.UUID; 4 | 5 | /** 6 | * @Description: 文件工具类 7 | * @Author: JollyCorivuG 8 | * @CreateTime: 2024/1/18 9 | */ 10 | public class FileUtils { 11 | /** 12 | * 生成随机文件名 13 | * @return String 14 | */ 15 | public static String generateRandomFileName() { 16 | return UUID.randomUUID().toString(true); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /chathub-tools/chathub-oss-starter/src/main/java/bupt/edu/jhc/chathub/oss/OssProperties.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.oss; 2 | 3 | import lombok.Data; 4 | import org.springframework.boot.context.properties.ConfigurationProperties; 5 | 6 | /** 7 | * @Description: OSS 参数 8 | * @Author: JollyCorivuG 9 | * @CreateTime: 2024/1/22 10 | */ 11 | @Data 12 | @ConfigurationProperties(prefix = "oss") 13 | public class OssProperties { 14 | 15 | /** 16 | * 是否开启 17 | */ 18 | Boolean enabled; 19 | 20 | /** 21 | * 存储对象服务器类型 22 | */ 23 | OssType type; 24 | 25 | /** 26 | * OSS 访问端点,集群时需提供统一入口 27 | */ 28 | String endpoint; 29 | 30 | /** 31 | * 用户名 32 | */ 33 | String accessKey; 34 | 35 | /** 36 | * 密码 37 | */ 38 | String secretKey; 39 | 40 | /** 41 | * 存储桶 42 | */ 43 | String bucketName; 44 | } 45 | -------------------------------------------------------------------------------- /chathub-tools/chathub-oss-starter/src/main/java/bupt/edu/jhc/chathub/oss/OssType.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.oss; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | 6 | /** 7 | * @Description: OSS 类型 8 | * @Author: JollyCorivuG 9 | * @CreateTime: 2024/1/22 10 | */ 11 | @Getter 12 | @AllArgsConstructor 13 | public enum OssType { 14 | /** 15 | * Minio 对象存储 16 | */ 17 | MINIO("minio", 1), 18 | 19 | /** 20 | * 华为 OBS 21 | */ 22 | OBS("obs", 2), 23 | 24 | /** 25 | * 腾讯 COS 26 | */ 27 | COS("tencent", 3), 28 | 29 | /** 30 | * 阿里巴巴 SSO 31 | */ 32 | ALIBABA("alibaba", 4), 33 | ; 34 | 35 | /** 36 | * 名称 37 | */ 38 | final String name; 39 | /** 40 | * 类型 41 | */ 42 | final int type; 43 | 44 | } 45 | 46 | -------------------------------------------------------------------------------- /chathub-tools/chathub-oss-starter/src/main/java/bupt/edu/jhc/chathub/oss/minio/MinioConfig.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.oss.minio; 2 | 3 | import bupt.edu.jhc.chathub.oss.OssProperties; 4 | import io.minio.MinioClient; 5 | import lombok.SneakyThrows; 6 | import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; 7 | import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; 8 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 9 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 10 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 11 | import org.springframework.context.annotation.Bean; 12 | import org.springframework.context.annotation.Configuration; 13 | 14 | /** 15 | * @Description: minio 配置 16 | * @Author: JollyCorivuG 17 | * @CreateTime: 2024/1/22 18 | */ 19 | @Configuration 20 | @EnableConfigurationProperties(OssProperties.class) 21 | @ConditionalOnExpression("${oss.enabled}") // 当配置文件中 oss.enabled=true 时,才会加载该配置类 22 | @ConditionalOnProperty(value = "oss.type", havingValue = "minio") // 当配置文件中 oss.type=minio 时,才会加载该配置类 23 | public class MinioConfig { 24 | @Bean 25 | @SneakyThrows 26 | @ConditionalOnMissingBean(MinioClient.class) 27 | public MinioClient minioClient(OssProperties ossProperties) { 28 | return MinioClient.builder() 29 | .endpoint(ossProperties.getEndpoint()) 30 | .credentials(ossProperties.getAccessKey(), ossProperties.getSecretKey()) 31 | .build(); 32 | } 33 | 34 | @Bean 35 | @ConditionalOnBean({MinioClient.class}) 36 | @ConditionalOnMissingBean(MinioTemplate.class) 37 | public MinioTemplate minioTemplate(MinioClient minioClient, OssProperties ossProperties) { 38 | return new MinioTemplate(minioClient, ossProperties); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /chathub-tools/chathub-oss-starter/src/main/java/bupt/edu/jhc/chathub/oss/minio/MinioTemplate.java: -------------------------------------------------------------------------------- 1 | package bupt.edu.jhc.chathub.oss.minio; 2 | 3 | import bupt.edu.jhc.chathub.oss.FileUtils; 4 | import bupt.edu.jhc.chathub.oss.OssProperties; 5 | import cn.hutool.core.date.DateUtil; 6 | import io.minio.GetPresignedObjectUrlArgs; 7 | import io.minio.MinioClient; 8 | import io.minio.PutObjectArgs; 9 | import io.minio.http.Method; 10 | import lombok.AllArgsConstructor; 11 | import org.springframework.web.multipart.MultipartFile; 12 | 13 | import java.util.Date; 14 | 15 | /** 16 | * @Description: minio 模板 17 | * @Author: JollyCorivuG 18 | * @CreateTime: 2024/1/22 19 | */ 20 | @AllArgsConstructor 21 | public class MinioTemplate { 22 | 23 | private MinioClient minioClient; 24 | 25 | private OssProperties ossProperties; 26 | 27 | public String getResignedObjectUrl(String bucketName, String objectName) throws Exception { 28 | GetPresignedObjectUrlArgs args = GetPresignedObjectUrlArgs.builder() 29 | .bucket(bucketName) 30 | .object(objectName) 31 | .method(Method.GET).build(); 32 | return minioClient.getPresignedObjectUrl(args); 33 | } 34 | 35 | public String uploadFile(Long userId, MultipartFile file) { 36 | // 1.得到上传文件用户id和当天日期, 用于构建上传目录(用户id/日期) 37 | String date = DateUtil.format(new Date(), "yyyy_MM_dd"); 38 | 39 | // 2.得到上传目录以及随机文件名 40 | String uploadDir = userId + "/" + date; 41 | String fileName = FileUtils.generateRandomFileName() + "-" + file.getOriginalFilename(); 42 | 43 | // 3.上传文件 44 | try { 45 | minioClient.putObject(PutObjectArgs.builder() 46 | .bucket(ossProperties.getBucketName()) 47 | .object(uploadDir + "/" + fileName) 48 | .stream(file.getInputStream(), file.getSize(), -1) 49 | .contentType(file.getContentType()) 50 | .build()); 51 | return getResignedObjectUrl(ossProperties.getBucketName(), uploadDir + "/" + fileName); 52 | } catch (Exception e) { 53 | throw new RuntimeException("上传文件失败"); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /chathub-tools/chathub-oss-starter/src/main/resources/META-INF/spring.factories: -------------------------------------------------------------------------------- 1 | # Auto Configure 2 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ 3 | bupt.edu.jhc.chathub.oss.minio.MinioConfig 4 | -------------------------------------------------------------------------------- /chathub-tools/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | bupt.edu.jhc 8 | chathub 9 | 1.0-SNAPSHOT 10 | 11 | 12 | chathub-tools 13 | pom 14 | 15 | chathub-oss-starter 16 | 17 | 18 | 19 | 8 20 | 8 21 | UTF-8 22 | 23 | 24 | 25 | 26 | 27 | org.springframework.boot 28 | spring-boot-maven-plugin 29 | 30 | none 31 | execute 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /docs/deployment/java.sh: -------------------------------------------------------------------------------- 1 | # 运行 jar 包 2 | nohup java -Dspring.profiles.active=prod \ 3 | -Xmx300m-Xms300m -Xmn200m \ 4 | -jar chathub-server-1.0-SNAPSHOT.jar \ 5 | > log.txt \ 6 | & -------------------------------------------------------------------------------- /docs/deployment/minio/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | services: 3 | minio: 4 | image: "quay.io/minio/minio" 5 | container_name: minio 6 | ports: 7 | - "9000:9000" # api 端口 8 | - "9001:9001" # 控制台端口 9 | environment: 10 | TZ: Asia/Shanghai # 时区上海 11 | MINIO_ROOT_USER: admin # 管理后台用户名 12 | MINIO_ROOT_PASSWORD: 87654321 # 管理后台密码,最小8个字符 13 | MINIO_COMPRESS: "off" # 开启压缩 on 开启 off 关闭 14 | MINIO_COMPRESS_EXTENSIONS: "" # 扩展名 .pdf,.doc 为空 所有类型均压缩 15 | MINIO_COMPRESS_MIME_TYPES: "" # mime 类型 application/pdf 为空 所有类型均压缩 16 | volumes: 17 | - /data/minio/data:/data/ # 映射当前目录下的data目录至容器内/data目录 18 | - /data/minio/config:/root/.minio/ # 映射配置目录 19 | command: server --address ':9000' --console-address ':9001' /data # 指定容器中的目录 /data 20 | privileged: true -------------------------------------------------------------------------------- /docs/deployment/mysql/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | services: 3 | mysql: 4 | image: mysql:8.0.32 # mysql版本 5 | container_name: mysql 6 | volumes: 7 | - /data/mysql/data:/var/lib/mysql 8 | - /data/mysql/conf/my.cnf:/etc/mysql/mysql.conf.d/mysqld.cnf 9 | restart: always 10 | ports: 11 | - "3306:3306" 12 | environment: 13 | MYSQL_ROOT_PASSWORD: 123456 # root 用户密码 14 | TZ: Asia/Shanghai 15 | command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci -------------------------------------------------------------------------------- /docs/deployment/rabbitmq/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.8" 2 | services: 3 | rabbitmq: 4 | image: rabbitmq:3-management # 镜像版本 5 | container_name: rabbitmq 6 | restart: always 7 | volumes: 8 | - ./data/:/var/lib/rabbitmq/ 9 | ports: 10 | - "5672:5672" 11 | - "15672:15672" 12 | environment: 13 | - RABBITMQ_DEFAULT_USER=admin # 默认管理员账号 14 | - RABBITMQ_DEFAULT_PASS=admin123 # 默认管理密码 -------------------------------------------------------------------------------- /docs/deployment/redis/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | services: 3 | redis: 4 | image: redis:6.2.6 5 | container_name: redis 6 | restart: always 7 | ports: 8 | - "6379:6379" 9 | volumes: 10 | - /data/redis/redis.conf:/etc/redis/redis.conf 11 | - /data/redis/data:/data 12 | - /data/redis/logs:/logs 13 | command: ["redis-server","/etc/redis/redis.conf"] -------------------------------------------------------------------------------- /docs/images/图片1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JollyCorivuG/chathub/b0e162da93380db35814866687680fd7853f754d/docs/images/图片1.jpg -------------------------------------------------------------------------------- /docs/images/图片2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JollyCorivuG/chathub/b0e162da93380db35814866687680fd7853f754d/docs/images/图片2.jpg -------------------------------------------------------------------------------- /docs/images/图片3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JollyCorivuG/chathub/b0e162da93380db35814866687680fd7853f754d/docs/images/图片3.jpg -------------------------------------------------------------------------------- /docs/images/图片4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JollyCorivuG/chathub/b0e162da93380db35814866687680fd7853f754d/docs/images/图片4.jpg -------------------------------------------------------------------------------- /docs/images/系统架构图.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JollyCorivuG/chathub/b0e162da93380db35814866687680fd7853f754d/docs/images/系统架构图.jpg --------------------------------------------------------------------------------