first commit

This commit is contained in:
xx572959496 2024-11-04 15:11:00 +08:00
parent e3f3176200
commit 28eaa63e19
26 changed files with 1087 additions and 22 deletions

52
.gitignore vendored
View File

@ -1,26 +1,34 @@
# ---> Java
# Compiled class file
*.class
HELP.md
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
# Log file
*.log
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
# BlueJ files
*.ctxt
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
replay_pid*
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/
### VS Code ###
.vscode/
/.mvn/

124
pom.xml Normal file
View File

@ -0,0 +1,124 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.dx.easychat</groupId>
<artifactId>EasyChatting</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>EasyChatting</name>
<description>EasyChatting</description>
<url/>
<licenses>
<license/>
</licenses>
<developers>
<developer/>
</developers>
<scm>
<connection/>
<developerConnection/>
<tag/>
<url/>
</scm>
<properties>
<java.version>21</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.51</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<!-- mybatis-plus的依赖 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>3.5.5</version>
</dependency>
<!-- mybatis-plus代码生成工具的依赖 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.7</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.freemarker/freemarker -->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<scope>compile</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.14.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,16 @@
package com.dx.easychat.easychatting;
import lombok.extern.log4j.Log4j2;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
@Log4j2
@Component
public class CustomerApplicationRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
log.info("启动netty");
}
}

View File

@ -0,0 +1,15 @@
package com.dx.easychat.easychatting;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication
@EnableScheduling
public class EasyChattingApplication {
public static void main(String[] args) {
SpringApplication.run(EasyChattingApplication.class, args);
}
}

View File

@ -0,0 +1,13 @@
package com.dx.easychat.easychatting;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
public class ServletInitializer extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(EasyChattingApplication.class);
}
}

View File

@ -0,0 +1,56 @@
package com.dx.easychat.easychatting.channel;
import jakarta.websocket.*;
import jakarta.websocket.server.ServerEndpoint;
import lombok.extern.log4j.Log4j2;
import java.io.IOException;
import java.time.Instant;
@Log4j2
@ServerEndpoint(value = "/channel/echo")
public class ReceiveChannel {
private Session session;
// 收到消息
@OnMessage
public void onMessage(String message) throws IOException {
log.info("[websocket] 收到消息id={}message={}", this.session.getId(), message);
if (message.equalsIgnoreCase("bye")) {
// 由服务器主动关闭连接状态码为 NORMAL_CLOSURE正常关闭
this.session.close(new CloseReason(CloseReason.CloseCodes.NORMAL_CLOSURE, "Bye"));;
return;
}
this.session.getAsyncRemote().sendText("["+ Instant.now().toEpochMilli() +"] Hello " + message);
}
// 连接打开
@OnOpen
public void onOpen(Session session, EndpointConfig endpointConfig){
// 保存 session 到对象
this.session = session;
log.info("[websocket] 新的连接id={}", this.session.getId());
}
// 连接关闭
@OnClose
public void onClose(CloseReason closeReason){
log.info("[websocket] 连接断开id={}reason={}", this.session.getId(),closeReason);
}
// 连接异常
@OnError
public void onError(Throwable throwable) throws IOException {
log.info("[websocket] 连接异常id={}throwable={}", this.session.getId(), throwable.getMessage());
// 关闭连接状态码为 UNEXPECTED_CONDITION意料之外的异常
this.session.close(new CloseReason(CloseReason.CloseCodes.UNEXPECTED_CONDITION, throwable.getMessage()));
}
}

View File

@ -0,0 +1,25 @@
package com.dx.easychat.easychatting.configuration;
import com.dx.easychat.easychatting.handler.WebSocketReceiveHandler;
import jakarta.annotation.Resource;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;
@Configuration
@EnableWebSocket
public class WebSocketConfiguration implements WebSocketConfigurer {
@Resource
private WebSocketReceiveHandler webSocketReceiveHandler;
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler( webSocketReceiveHandler, "/websocket/message")
.setAllowedOrigins("*")
.addInterceptors(new HttpSessionHandshakeInterceptor());
}
}

View File

@ -0,0 +1,59 @@
package com.dx.easychat.easychatting.controller;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import jakarta.annotation.Resource;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/code")
@Log4j2
public class AutoGeneratorController {
@Resource
private DataSourceProperties dataSourceProperties;
@Value("${spring.customer.config.file-path:/opt/file_temp/}")
private String filePath;
@GetMapping("/generator")
@ResponseBody
public JSONObject generatorCode(@RequestParam String tableName) {
JSONObject jsonObject = new JSONObject();
String url = dataSourceProperties.getUrl();
String username = dataSourceProperties.getUsername();
String password = dataSourceProperties.getPassword();
FastAutoGenerator.create(url, username, password)
.globalConfig(builder -> builder
.author("xu.x")
.outputDir(filePath)
.disableOpenDir()
.commentDate("yyyy-MM-dd")
)
.packageConfig(builder -> builder
.parent("com.dx.easychat.easychatting")
.entity("entity")
.mapper("mapper")
.service("service")
.serviceImpl("service.impl")
.xml("mapper.xml")
)
.strategyConfig(builder ->
builder.addInclude(tableName) // 设置需要生成的表名
.addTablePrefix("db_") // 设置过滤表前缀
.entityBuilder()
.enableLombok()
)
.templateEngine(new FreemarkerTemplateEngine())
.execute();
jsonObject.put("code", "success");
jsonObject.put("data", null);
return jsonObject;
}
}

View File

@ -0,0 +1,109 @@
package com.dx.easychat.easychatting.controller;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.dx.easychat.easychatting.entity.FileEntity;
import com.dx.easychat.easychatting.service.FileEntityService;
import jakarta.annotation.Resource;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.*;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Date;
import java.util.UUID;
@RestController
@Log4j2
@RequestMapping("/api/file")
public class FileController {
// TODO 文件上传同名问题
// TODO 删除消息时 同步删除物理文件
@Value("${spring.customer.config.file-path:/opt/file_temp/}")
private String filePath;
@Resource
private FileEntityService fileEntityService;
@PostMapping("/upload")
@ResponseBody
public String uploadImageFile(@RequestParam("file") MultipartFile multipartFile) throws IOException {
JSONObject jsonObject = new JSONObject();
//获取文件名
String fileName = multipartFile.getOriginalFilename();
//获取文件后缀名
assert fileName != null;
String contentType = multipartFile.getContentType();
String fileType = "";
int index = fileName.lastIndexOf(".");
if (index != -1) {
fileType = fileName.substring(index + 1);
}
FileEntity fileEntity = new FileEntity();
String uuid = UUID.randomUUID().toString();
fileEntity.setUuid(uuid);
fileEntity.setFileName(fileName);
String realFilePath = filePath + uuid;
fileEntity.setFilePath(realFilePath);
fileEntity.setUploadTime(new Date());
fileEntity.setFileMimeType(contentType);
fileEntity.setFileType(fileType);
log.info("文件名:{}", fileName);
log.info("filePath{}", filePath);
File uploadFile = new File(realFilePath);
//将临时文件转存到指定磁盘位置
multipartFile.transferTo(uploadFile);
fileEntityService.save(fileEntity);
jsonObject.put("code", 200);
jsonObject.put("uuid", uuid);
jsonObject.put("fileType", fileType);
jsonObject.put("fileMimeType", contentType);
return jsonObject.toJSONString();
}
/**
* 下载文件:将输入流中的数据循环写入到响应输出流中而不是一次性读取到内存
*
* @param fileId 文档id
* @param response 返回体
*/
@GetMapping("/{fileId}")
public void downLoadFile(HttpServletResponse response, @PathVariable String fileId) throws IOException {
response.setContentType("text/json;charset=utf-8");
PrintWriter writer = response.getWriter();
try {
// 读到流中
LambdaQueryWrapper<FileEntity> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(FileEntity::getUuid, fileId);
FileEntity fileEntity = fileEntityService.getOne(lambdaQueryWrapper);
log.info("下载时的查询:{}", fileEntity);
InputStream inputStream = Files.newInputStream(Paths.get(fileEntity.getFilePath()));
String fileName = fileEntity.getFileName();
response.reset();
response.setContentType("application/octet-stream");
response.addHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(fileName, StandardCharsets.UTF_8));
ServletOutputStream outputStream = response.getOutputStream();
byte[] b = new byte[1024];
int len;
//从输入流中读取一定数量的字节并将其存储在缓冲区字节数组中读到末尾返回-1
while ((len = inputStream.read(b)) > 0) {
outputStream.write(b, 0, len);
}
inputStream.close();
} catch (Exception e) {
log.error("下载文件异常:{}", e.getMessage());
JSONObject jsonObject = new JSONObject();
jsonObject.put("code",500);
jsonObject.put("message",e.getMessage());
writer.write(jsonObject.toJSONString());
}
}
}

View File

@ -0,0 +1,91 @@
package com.dx.easychat.easychatting.controller;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.dx.easychat.easychatting.entity.FileEntity;
import com.dx.easychat.easychatting.entity.Message;
import com.dx.easychat.easychatting.service.FileEntityService;
import com.dx.easychat.easychatting.service.MessageService;
import jakarta.annotation.Resource;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController()
@Log4j2
@RequestMapping("/api/message")
public class MessageController {
@Resource
private MessageService messageService;
@Resource
private FileEntityService fileEntityService;
@PostMapping("/config")
@ResponseBody
public JSONObject getConfig(@RequestBody JSONObject jsonObject) {
JSONObject result = new JSONObject();
String string = jsonObject.getString("id");
log.info(string);
int length = string.length();
log.info(length);
log.info(jsonObject);
return result;
}
// todo 增加校验webservice的session
@GetMapping("/get")
@ResponseBody
public JSONObject getHistoryMessage() {
JSONObject jsonObject = new JSONObject();
jsonObject.put("code", 200);
LambdaQueryWrapper<Message> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(Message::getIsDelete, 0);
List<Message> list = messageService.list(lambdaQueryWrapper);
for (Message message : list) {
if ("file".equals(message.getType())) {
LambdaQueryWrapper<FileEntity> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(FileEntity::getUuid, message.getFile());
FileEntity fileEntity = fileEntityService.getOne(queryWrapper);
if (fileEntity != null) {
String fileMimeType = fileEntity.getFileMimeType();
message.setFileMimeType(fileMimeType);
message.setFileName(fileEntity.getFileName());
}
}
}
jsonObject.put("data", list);
return jsonObject;
}
@GetMapping("/message")
@ResponseBody
public JSONObject getMessage(String message_id) {
JSONObject jsonObject = new JSONObject();
jsonObject.put("code", 200);
LambdaQueryWrapper<Message> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(Message::getIsDelete, 0);
if (StringUtils.isNotBlank(message_id)) {
lambdaQueryWrapper.and(wrapper ->
wrapper.eq(Message::getId, message_id).or()
.eq(Message::getId, 1));
}
List<Message> list = messageService.list(lambdaQueryWrapper);
for (Message message : list) {
if ("file".equals(message.getType())) {
LambdaQueryWrapper<FileEntity> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(FileEntity::getUuid, message.getFile());
List<FileEntity> fileEntityList = fileEntityService.list(queryWrapper);
if (fileEntityList != null) {
message.setFileEntityList(fileEntityList);
}
}
}
jsonObject.put("data", list);
return jsonObject;
}
}

View File

@ -0,0 +1,34 @@
package com.dx.easychat.easychatting.cron;
import com.dx.easychat.easychatting.mapper.FileEntityMapper;
import jakarta.annotation.Resource;
import lombok.extern.log4j.Log4j2;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.io.File;
import java.util.List;
/**
* 定时任务每五分钟执行一次
* 查询出已经到达过期时间的数据删除物理文件同时更新回表单
*/
@Component
@Log4j2
public class DeleteFileCronJob {
@Resource
FileEntityMapper fileEntityMapper;
@Scheduled(cron ="0 0/5 * * * ?")
public void executeCronJob() {
List<String> allDeleteFilePath = fileEntityMapper.findAllDeleteFilePath();
for (String filePath : allDeleteFilePath) {
File file = new File(filePath);
if (file.exists()) {
file.delete();
}
}
}
}

View File

@ -0,0 +1,38 @@
package com.dx.easychat.easychatting.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.extern.log4j.Log4j2;
import java.util.Date;
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
@Log4j2
@TableName("db_file")
public class FileEntity {
@TableId(type = IdType.AUTO)
private Integer id;
private String uuid;
private String fileName;
private String fileType;
private String fileMimeType;
private String filePath;
private Integer isDelete;
private Date uploadTime;
}

View File

@ -0,0 +1,56 @@
package com.dx.easychat.easychatting.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.extern.log4j.Log4j2;
import java.util.Date;
import java.util.List;
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
@Log4j2
@TableName("db_message")
public class Message {
@TableId(type = IdType.AUTO)
private Integer id;
private String content;
private Date time;
private String type;
private String file;
private Integer isDelete;
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
private String ip;
@TableField(exist = false)
private String sessionId;
@TableField(exist = false)
private String messageType;
@TableField(exist = false)
private String fileName;
@TableField(exist = false)
private String fileMimeType;
@TableField(exist = false)
private List<FileEntity> fileEntityList;
}

View File

@ -0,0 +1,79 @@
package com.dx.easychat.easychatting.entity.vo.request;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
/**
* 前端请求的发送offer参数实体类
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class SendOfferEntity {
/**
* 是否快速入职
*/
private Integer isFastIn;
/**
* 是否快速入职
*/
private Integer isUploadMedicalReport;
/**
* 是否短信通知
*/
private Boolean isMessageNotice;
/**
* 短信内容
*/
private String messageContent;
/**
* 是否邮件通知
*/
private Boolean isEmailNotice;
/**
* 邮件标题
*/
private String emailTitle;
/**
* 邮件内容
*/
private String emailContent;
/**
* 选中的行ID 以逗号分隔
*/
private String ids;
/**
* 入职须知文件上传
*/
private List<MultipartFile> file;
@Override
public String toString() {
return "SendOfferEntity{" +
"isFastIn=" + isFastIn +
", \nisUploadMedicalReport=" + isUploadMedicalReport +
", \nisMessageNotice=" + isMessageNotice +
", \nmessageContent='" + messageContent + '\'' +
", \nisEmailNotice=" + isEmailNotice +
", \nemailTitle='" + emailTitle + '\'' +
", \nemailContent='" + emailContent + '\'' +
", \nids='" + ids + '\'' +
", \nfile=" + file +
'}';
}
}

View File

@ -0,0 +1,55 @@
package com.dx.easychat.easychatting.filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpFilter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
@Component
@Order(-102)
@Log4j2
public class CorsCustomerFilter extends HttpFilter {
@Value("${spring.security.cors.origin:http://localhost:5173,http://127.0.0.1:5173}")
private String allowOrigin;
@Override
protected void doFilter(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain) throws IOException, ServletException {
response.addHeader("Access-Control-Allow-Origin",this.resolveOrigin(request));
response.addHeader("Access-Control-Allow-Methods","GET, POST, PUT, DELETE, OPTIONS");
// response.addHeader("Access-Control-Allow-Headers","Authorization, Content-Type");
response.addHeader("Access-Control-Allow-Headers","*");
chain.doFilter(request, response);
}
private void addCorsHeader(HttpServletRequest request,
HttpServletResponse response) {
response.addHeader("Access-Control-Allow-Origin",allowOrigin);
response.addHeader("Access-Control-Allow-Methods","GET, POST, PUT, DELETE, OPTIONS");
response.addHeader("Access-Control-Allow-Headers","Authorization, Content-Type");
}
/**
* 解析配置文件中的请求原始站点
* @param request 请求
* @return 解析得到的请求头值
*/
private String resolveOrigin(HttpServletRequest request){
List<String> list = Arrays.asList(allowOrigin.split(","));
String origin = request.getHeader("Origin");
if (list.isEmpty()) return "http://localhost:5173";
if ("*".equals(allowOrigin) || list.contains(origin)) return origin;
else return list.getFirst();
}
}

View File

@ -0,0 +1,147 @@
package com.dx.easychat.easychatting.handler;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.dx.easychat.easychatting.entity.FileEntity;
import com.dx.easychat.easychatting.entity.Message;
import com.dx.easychat.easychatting.service.FileEntityService;
import com.dx.easychat.easychatting.service.impl.MessageServiceImpl;
import jakarta.annotation.Resource;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.*;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import java.io.File;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
@Log4j2
@Component
public class WebSocketReceiveHandler extends TextWebSocketHandler{
@Resource
private MessageServiceImpl messageService;
@Resource
private FileEntityService fileEntityService;
public static ConcurrentHashMap<String, WebSocketSession> SessionsPool = new ConcurrentHashMap<>();
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
log.info("建立ws链接: id={}", session.getId());
SessionsPool.put(session.getId(), session);
JSONObject jsonObject = new JSONObject();
jsonObject.put("id", session.getId());
jsonObject.put("messageType", "heartbeat");
jsonObject.put("status", true);
jsonObject.put("onlineNumber", SessionsPool.size());
session.sendMessage(new TextMessage(jsonObject.toJSONString()));
super.afterConnectionEstablished(session);
}
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
String payload = message.getPayload();
InetSocketAddress remoteAddress = session.getRemoteAddress();
log.info("接收到消息id={},message={},ip={}", session.getId(), payload, remoteAddress);
boolean valid = JSON.isValid(payload);
if (valid) {
Message messageEntity = JSONObject.parseObject(payload, Message.class);
log.info("转换实体类:{}", messageEntity);
JSONObject jsonObject = new JSONObject();
jsonObject.put("id", session.getId());
jsonObject.put("messageType", "heartbeat");
jsonObject.put("status", true);
jsonObject.put("onlineNumber", SessionsPool.size());
if (StringUtils.isNoneEmpty(messageEntity.getSessionId()) && SessionsPool.containsKey(session.getId())) {
switch (messageEntity.getMessageType()) {
case "delete":
messageService.updateById(messageEntity);
if ("file".equals(messageEntity.getType())){
LambdaQueryWrapper<FileEntity> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(FileEntity::getUuid, messageEntity.getFile());
FileEntity fileEntity = fileEntityService.getOne(lambdaQueryWrapper);
if (fileEntity != null) {
File file = new File(fileEntity.getFilePath());
if (file.exists()) {
file.delete();
}
fileEntityService.removeById(fileEntity.getId());
}
}
break;
case "heartbeat":
session.sendMessage(new TextMessage(jsonObject.toJSONString()));
break;
case "send":
Message messageDto = new Message();
BeanUtils.copyProperties(messageEntity, messageDto);
messageDto.setIp(Objects.requireNonNull(session.getRemoteAddress()).getAddress().getHostAddress());
messageService.save(messageDto);
if ("file".equals(messageEntity.getType())){
LambdaQueryWrapper<FileEntity> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(FileEntity::getUuid, messageEntity.getFile());
FileEntity fileEntity = fileEntityService.getOne(lambdaQueryWrapper);
String fileMimeType = fileEntity.getFileMimeType();
messageEntity.setFileMimeType(fileMimeType);
messageEntity.setFileName(fileEntity.getFileName());
}
messageEntity.setMessageType("receive");
SessionsPool.forEach((key, value) -> {
try {
value.sendMessage(new TextMessage(JSONObject.toJSONString(messageEntity)));
} catch (IOException e) {
log.info("广播消息:{}", e.getMessage());
}
});
break;
default:
log.error("未知类型数据");
}
}else {
log.error("请求sessionID不存在重新获取session");
jsonObject.put("status", false);
jsonObject.put("message", "请求sessionID不存在重新下发session");
session.sendMessage(new TextMessage(jsonObject.toJSONString()));
}
}else {
JSONObject jsonObject = new JSONObject();
jsonObject.put("code", -1);
jsonObject.put("message", "请求错误");
jsonObject.put("time", System.currentTimeMillis());
session.sendMessage(new TextMessage(jsonObject.toJSONString()));
}
super.handleTextMessage(session, message);
}
@Override
protected void handleBinaryMessage(WebSocketSession session, BinaryMessage message) {
log.info("接收到二进制消息id={},message={}", session.getId(), message.getPayload());
super.handleBinaryMessage(session, message);
}
@Override
public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
log.error("出现异常id={},message={}", session.getId(), exception.getMessage());
super.handleTransportError(session, exception);
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
String sessionId = session.getId();
log.info("断开链接id={},status={}", sessionId, status);
WebSocketSession remove = SessionsPool.remove(sessionId);
if (remove != null) {
remove.close();
}
super.afterConnectionClosed(session, status);
}
}

View File

@ -0,0 +1,16 @@
package com.dx.easychat.easychatting.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.dx.easychat.easychatting.entity.FileEntity;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;
@Mapper
public interface FileEntityMapper extends BaseMapper<FileEntity> {
@Select("SELECT file_path FROM db_message t1 LEFT JOIN db_file t2 on t1.file = t2.uuid WHERE type = 'file' AND t1.is_delete = 1")
List<String> findAllDeleteFilePath();
}

View File

@ -0,0 +1,9 @@
package com.dx.easychat.easychatting.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.dx.easychat.easychatting.entity.Message;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface MessageMapper extends BaseMapper<Message> {
}

View File

@ -0,0 +1,7 @@
package com.dx.easychat.easychatting.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.dx.easychat.easychatting.entity.FileEntity;
public interface FileEntityService extends IService<FileEntity> {
}

View File

@ -0,0 +1,7 @@
package com.dx.easychat.easychatting.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.dx.easychat.easychatting.entity.Message;
public interface MessageService extends IService<Message> {
}

View File

@ -0,0 +1,12 @@
package com.dx.easychat.easychatting.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dx.easychat.easychatting.entity.FileEntity;
import com.dx.easychat.easychatting.mapper.FileEntityMapper;
import com.dx.easychat.easychatting.service.FileEntityService;
import org.springframework.stereotype.Service;
@Service
public class FileEntityServiceImpl extends ServiceImpl<FileEntityMapper, FileEntity> implements FileEntityService {
}

View File

@ -0,0 +1,12 @@
package com.dx.easychat.easychatting.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dx.easychat.easychatting.entity.Message;
import com.dx.easychat.easychatting.mapper.MessageMapper;
import com.dx.easychat.easychatting.service.MessageService;
import org.springframework.stereotype.Service;
@Service
public class MessageServiceImpl extends ServiceImpl<MessageMapper, Message> implements MessageService {
}

View File

@ -0,0 +1,31 @@
spring:
servlet:
multipart:
enabled: true
max-file-size: 20MB
max-request-size: 20MB
security:
filter:
order: -100
cors:
origin: http://127.0.0.1:5173,http://localhost:5173
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/project
username: root
password: XXdove521..
customer:
config:
file-path: /Users/xx/Dev/file/
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
call-setters-on-nulls: false
logging:
file:
path: ./
name: spring.log
level:
com.dx.easychat.easychatting.service: info

View File

@ -0,0 +1,30 @@
spring:
servlet:
multipart:
enabled: true
max-file-size: 20MB
max-request-size: 20MB
security:
filter:
order: -100
cors:
origin: http://127.0.0.1:5173,http://localhost:5173
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
# 生产环境
url: jdbc:mysql://127.0.0.1:3306/project
username: xiaoxu
password: xxdove521
customer:
config:
file-path: /opt/easychatting/file_temp
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
call-setters-on-nulls: false
logging:
file:
path: ./
name: spring.log
server:
port: 8020

View File

@ -0,0 +1,3 @@
spring:
profiles:
active: 'dev'

View File

@ -0,0 +1,13 @@
package com.dx.easychat.easychatting;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class EasyChattingApplicationTests {
@Test
void contextLoads() {
}
}