实时通讯

This commit is contained in:
2025-12-01 10:08:41 +08:00
commit e6623be1e5
140 changed files with 6032 additions and 0 deletions

207
pom.xml Normal file
View File

@@ -0,0 +1,207 @@
<?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>
<groupId>com.realTime</groupId>
<artifactId>real-time-application</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>real-time-application</name>
<description>real-time-application</description>
<properties>
<java.version>17</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>3.0.2</spring-boot.version>
</properties>
<dependencies>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.9.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.5</version>
</dependency>
<!-- Apache Commons IO (通常需要一起使用) -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.11.0</version>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20231013</version> <!-- 请检查最新版本 -->
</dependency>
<dependency>
<groupId>org.java-websocket</groupId>
<artifactId>Java-WebSocket</artifactId>
<version>1.5.3</version> <!-- 请检查最新版本 -->
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.14</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
<version>3.0.2</version>
</dependency>
<!-- 音频处理(格式转换) -->
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacv-platform</artifactId>
<version>1.5.9</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-3-starter</artifactId>
<version>1.2.23</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
</dependency>
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-annotations</artifactId>
<version>2.2.25</version>
</dependency>
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-annotations-jakarta</artifactId>
<version>2.2.28</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>3.5.10.1</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-jsqlparser</artifactId>
<version>3.5.10.1</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.40</version>
</dependency>
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.5.13</version>
</dependency>
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.58</version>
</dependency>
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>javase</artifactId>
<version>3.3.3</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
<version>1.18.40</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>17</source>
<target>17</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<mainClass>com.realtime.RealTimeApplication</mainClass>
<skip>false</skip>
</configuration>
<executions>
<execution>
<id>repackage</id>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,4 @@
FROM openjdk:21-jre
VOLUME /tmp
COPY real.jar real.jar
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/real.jar"]

View File

@@ -0,0 +1,26 @@
package com.realtime;
import com.realtime.packets.server.NettyServer;
import lombok.RequiredArgsConstructor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.realtime.mappers")
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class RealTimeApplication implements CommandLineRunner {
private final NettyServer nettyServer;
public static void main(String[] args) {
SpringApplication.run(RealTimeApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
nettyServer.run();
}
}

View File

@@ -0,0 +1,77 @@
package com.realtime.config;
import com.realtime.sysconst.RTASRClient;
import com.realtime.utils.Base64ToMultipartFile;
import jakarta.websocket.*;
import jakarta.websocket.server.ServerEndpoint;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.UUID;
@Component
@ServerEndpoint("/binary")
public class AnnotationBinaryWebSocket {
private static final Logger logger = LoggerFactory.getLogger(AnnotationBinaryWebSocket.class);
@OnOpen
public void onOpen(Session session) {
logger.info("注解方式 - WebSocket 连接建立: {}", session.getId());
}
@OnMessage(maxMessageSize = 10485760) // 10MB限制
public void onBinaryMessage(byte[] data, Session session) {
logger.info("注解方式 - 收到二进制数据, 大小: {} bytes", data.length);
// 处理数据
processData(session, data);
// 发送响应
sendResponse(session, data);
}
@OnMessage
public void onTextMessage(String message, Session session) {
logger.info("注解方式 - 收到文本消息: {}", message);
}
@OnError
public void onError(Session session, Throwable error) {
logger.error("注解方式 - WebSocket 错误: {}", session.getId(), error);
}
@OnClose
public void onClose(Session session, CloseReason closeReason) {
logger.info("注解方式 - WebSocket 连接关闭: {}, 原因: {}",
session.getId(), closeReason.getReasonPhrase());
}
private void processData(Session session, byte[] data) {
// 数据处理逻辑
}
private void sendResponse(Session session, byte[] data) {
try {
MultipartFile convert = Base64ToMultipartFile.convert(data, UUID.randomUUID() + ".pcm");
RTASRClient client = new RTASRClient("5d899195","4c428332836cd9481be4127c941f4167", "ZjFiMWViMGY0NDA4ODNkNDgxYzg0Yzg3", "",convert.getInputStream(),session);
client.connect();
boolean b = client.sendAudio();
// 发送处理结果
ByteBuffer buffer = ByteBuffer.wrap(("已接收 " + data.length + " bytes").getBytes());
} catch (IOException e) {
logger.error("发送响应失败", e);
}
}
}

View File

@@ -0,0 +1,15 @@
package com.realtime.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConfigurationProperties(prefix = "baidu.tts")
@Data
public class BaiduTtsConfig {
private String appId;
private String apiKey;
private String secretKey;
}

View File

@@ -0,0 +1,90 @@
package com.realtime.config;
import com.realtime.service.DoubaoVoiceService;
import lombok.extern.slf4j.Slf4j;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.FFmpegFrameRecorder;
import org.bytedeco.javacv.Frame;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.BinaryMessage;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.BinaryWebSocketHandler;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.EOFException;
@Slf4j
@Component
public class ClientVoiceHandler extends BinaryWebSocketHandler {
private final DoubaoVoiceService doubaoVoiceService;
public ClientVoiceHandler(DoubaoVoiceService doubaoVoiceService) {
this.doubaoVoiceService = doubaoVoiceService;
}
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
// 客户端会话
// 建立与豆包 API 的连接
System.out.println(session);
doubaoVoiceService.connect(session);
}
@Override
protected void handleBinaryMessage(WebSocketSession session, BinaryMessage message) throws Exception {
System.out.println("2");
// 接收客户端的音频数据(如 PCM 片段)
byte[] audioData = message.getPayload().array();
// 预处理:确保格式符合豆包 API 要求(如采样率、位深)
byte[] processedData = preprocessAudio(audioData);
// 发送给豆包 API
doubaoVoiceService.sendAudio(processedData);
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
// 关闭与豆包 API 的连接
doubaoVoiceService.close();
}
private byte[] preprocessAudio(byte[] audioData) {
if (audioData == null || audioData.length == 0) {
log.warn("接收到空的音频数据,跳过处理");
return new byte[0]; // 返回空数组,避免后续错误
}
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
try (FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(new ByteArrayInputStream(audioData));
FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(outputStream, 1)) {
grabber.start();
// 打印输入音频信息(调试用)
log.debug("输入音频格式:{},采样率:{},声道:{}",
grabber.getFormat(), grabber.getSampleRate(), grabber.getAudioChannels());
recorder.setFormat("s16le");
recorder.setSampleRate(16000);
recorder.setAudioChannels(1);
recorder.start();
Frame frame;
int frameCount = 0;
while ((frame = grabber.grab()) != null) {
recorder.record(frame);
frameCount++;
}
log.debug("成功处理 {} 帧音频", frameCount);
recorder.stop();
grabber.stop();
return outputStream.toByteArray();
} catch (Exception e) {
log.error("音频预处理失败", e);
return new byte[0];
}
}
}

View File

@@ -0,0 +1,24 @@
package com.realtime.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
@Configuration
public class CorsConfig {
@Bean
CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOriginPattern("*");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
}

View File

@@ -0,0 +1,14 @@
package com.realtime.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Data
@Configuration
@ConfigurationProperties(prefix = "doubao.voice")
public class DoubaoVoiceConfig {
private String appId;
private String apiKey; // 豆包 API Key
private String accessToken; // 访问令牌
private String websocketUrl; // 豆包实时语音接口的 WebSocket 地址(如 wss://api.doubao.com/voice/realtime
}

View File

@@ -0,0 +1,17 @@
package com.realtime.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Data
@Component
@ConfigurationProperties(prefix = "file")
public class FileProperties {
private int picMaxSize;
private int picMaxCount;
private String picAllowedFormats;
private String region;
private String allowedFormats;
}

View File

@@ -0,0 +1,27 @@
package com.realtime.config;
import com.realtime.exception.BusinessException;
import com.realtime.sysconst.Result;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
public class GlobalException {
@ExceptionHandler(BusinessException.class)
public Result<BusinessException> handle(BusinessException e) {
return Result.systemError(e.getMessage(),e.getCode());
}
/**
* 兜底异常
* @param e 异常
* @return 异常类型
*/
@ExceptionHandler(Exception.class)
public Result<String> exceptionHandler(Exception e) {
return Result.fail(e.getMessage());
}
}

View File

@@ -0,0 +1,25 @@
package com.realtime.config;
import io.minio.MinioClient;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
@Slf4j
@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class MinioComponent {
private final MinioConfig minioConfig;
@Bean
public MinioClient minioClient() {
return MinioClient.builder()
.endpoint(minioConfig.getEndpoint())
.credentials(minioConfig.getAccessKey(), minioConfig.getSecretKey())
.build();
}
}

View File

@@ -0,0 +1,25 @@
package com.realtime.config;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
@Configuration
@Data
public class MinioConfig {
@Value("${minio.endpoint}")
private String endpoint;
@Value("${minio.access-key}")
private String accessKey;
@Value("${minio.secret-key}")
private String secretKey;
@Value("${minio.bucket-name}")
private String bucketName;
}

View File

@@ -0,0 +1,21 @@
package com.realtime.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyBatisPlusConfig {
/**
* 分页插件
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}

View File

@@ -0,0 +1,12 @@
package com.realtime.config;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.util.concurrent.GlobalEventExecutor;
import org.springframework.stereotype.Component;
@Component
public class NettyConfig {
public final static ChannelGroup group = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
}

View File

@@ -0,0 +1,14 @@
package com.realtime.config;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
public class ProtocolFrameDecoder extends LengthFieldBasedFrameDecoder {
public ProtocolFrameDecoder() {
this(1024, 12, 4, 0, 0);
}
public ProtocolFrameDecoder(int maxFrameLength, int lengthFieldOffset, int lengthFieldLength, int lengthAdjustment, int initialBytesToStrip) {
super(maxFrameLength, lengthFieldOffset, lengthFieldLength, lengthAdjustment, initialBytesToStrip);
}
}

View File

@@ -0,0 +1,20 @@
package com.realtime.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Data
@Component
@ConfigurationProperties(prefix = "rtsa")
public class RtasrConfig {
private String appId;
private String accessKeyId;
private String accessKeySecret;
private String baseWsUrl;
private String audioEncode;
private String lang;
private Integer samplerate;
private Integer audioFameSize;
private Integer frameIntervalMs;
}

View File

@@ -0,0 +1,23 @@
package com.realtime.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
@Configuration
@EnableWebSocket
public class WebSocketAnnotationConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
// 空实现,因为使用 @ServerEndpoint
}
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}

View File

@@ -0,0 +1,47 @@
package com.realtime.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.realtime.model.pojo.ChatList;
import com.realtime.model.query.ChatListPageQueryReq;
import com.realtime.service.ChatListService;
import com.realtime.sysconst.Result;
import com.realtime.vo.ChatListInfoVo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@Slf4j
@RestController
@RequestMapping("/chatList")
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class ChatListController {
private final ChatListService chatListService;
@PostMapping("/getChatFriend")
Result<IPage<ChatListInfoVo>> getChatFriend(@RequestBody ChatListPageQueryReq chatListPageQueryReq){
return chatListService.getChatList(chatListPageQueryReq);
}
@PostMapping("/save")
Result<String> saveChatList(@RequestBody List<ChatList> chatList){
return chatListService.saveSignChatList(chatList);
}
@PostMapping("/delete")
Result<String> deleteChatList(@RequestBody ChatList ids){
return chatListService.deleteSignChatList(ids);
}
@PostMapping("/update")
Result<String> updateChatList(@RequestBody List<ChatList> chatList){
return chatListService.updateChatListFName(chatList);
}
}

View File

@@ -0,0 +1,35 @@
package com.realtime.controller;
import com.realtime.service.FileService;
import com.realtime.sysconst.Result;
import com.realtime.vo.UploadVo;
import io.minio.errors.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
@Slf4j
@RestController
@RequestMapping("/file")
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class FileController {
private final FileService fileService;
@PostMapping("/upload")
Result<UploadVo> upload(@RequestPart("file") MultipartFile file) throws IOException, ServerException, InsufficientDataException, ErrorResponseException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
return fileService.uploadFile(file);
}
}

View File

@@ -0,0 +1,43 @@
package com.realtime.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.realtime.model.pojo.FriendRelationship;
import com.realtime.model.query.ChatFriendRelationshipQueryReq;
import com.realtime.service.FriendRelationshipService;
import com.realtime.sysconst.Result;
import com.realtime.vo.ChatFriendRelationshipInfoVo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@Slf4j
@RestController
@RequestMapping("/friendship")
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class FriendshipController {
private final FriendRelationshipService friendRelationshipService;
@PostMapping("/getFriendship")
Result<IPage<ChatFriendRelationshipInfoVo>> selectByPhone(@RequestBody ChatFriendRelationshipQueryReq req){
return friendRelationshipService.selectByPhone(req);
}
@PostMapping("/saveShip")
Result<String> saveShip(@RequestBody FriendRelationship req){
return friendRelationshipService.saveShip(req);
}
@PostMapping("/remove")
Result<String> remove(@RequestBody List<Long> ids){
return friendRelationshipService.removeIds(ids);
}
}

View File

@@ -0,0 +1,88 @@
package com.realtime.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.google.zxing.WriterException;
import com.realtime.model.pojo.GroupMember;
import com.realtime.model.query.GroupDetailQueryReq;
import com.realtime.model.query.GroupListQueryReq;
import com.realtime.model.query.GroupMemberListQueryReq;
import com.realtime.model.remove.DisbandGroupReq;
import com.realtime.model.remove.RemoveGroupReq;
import com.realtime.model.remove.RemovesGroupReq;
import com.realtime.model.update.GroupInventUpdateReq;
import com.realtime.service.GroupListService;
import com.realtime.service.GroupMemberService;
import com.realtime.service.GroupMessageService;
import com.realtime.sysconst.Result;
import com.realtime.vo.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
import java.util.List;
@Slf4j
@RestController
@RequestMapping("/group")
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class GroupController {
private final GroupListService groupListService;
private final GroupMessageService groupMessageService;
private final GroupMemberService groupMemberService;
@PostMapping("/getGroup")
Result<IPage<GroupListVo>> getGroup(@RequestBody GroupListQueryReq groupListQueryReq) {
return groupListService.getGroup(groupListQueryReq);
}
@PostMapping("/getGroupList")
Result<IPage<GroupListMessagesVo>> getGroupList(@RequestBody GroupListQueryReq req) {
return groupMessageService.getGroupList(req);
}
@PostMapping("/getGroupMemberList")
Result<IPage<GroupMemberListVo>> getGroupMemberList(@RequestBody GroupMemberListQueryReq req) {
return groupMemberService.getMemberList(req);
}
@PostMapping("/getGroupDetail")
Result<GroupDetailVo> getGroupDetail(@RequestBody GroupDetailQueryReq queryReq) throws IOException, WriterException {
return groupListService.getGroupDetail(queryReq);
}
@PostMapping("/updateInvent")
Result<Void> updateInvent(@RequestBody GroupInventUpdateReq groupInventUpdateReq) {
return groupListService.updateInvent(groupInventUpdateReq);
}
@PostMapping("/inventFriend")
Result<IPage<ChatListInfoVo>> getAllByGroupIdAndMemberId(@RequestBody GroupMemberListQueryReq queryReq) {
return groupMemberService.getAllByGroupIdAndMemberId(queryReq);
}
@PostMapping("/remove")
Result<String> remove(@RequestBody RemovesGroupReq ids) {
return groupMemberService.removeMembersByIds(ids);
}
@PostMapping("/saveInvent")
Result<String> saveInvent(@RequestBody List<GroupMember> groupMembers,@RequestParam("launchContactId")String launchContactId) {
return groupMemberService.saveInvent(groupMembers,launchContactId);
}
@PostMapping("/disband")
Result<String> disband(@RequestBody DisbandGroupReq disbandGroupReq) {
return groupListService.disband(disbandGroupReq);
}
@PostMapping("/quit")
Result<String> quit(@RequestBody RemoveGroupReq removeGroupReq) {
return groupMemberService.quit(removeGroupReq);
}
}

View File

@@ -0,0 +1,38 @@
package com.realtime.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.realtime.model.query.ChatQueryPageReq;
import com.realtime.model.query.MessageQueryReq;
import com.realtime.service.MessageService;
import com.realtime.sysconst.Result;
import com.realtime.vo.FriendMessageVo;
import com.realtime.vo.MessageVo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Slf4j
@RestController
@RequestMapping("/message")
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class MessageController {
private final MessageService messageService;
@PostMapping("/list")
Result<IPage<MessageVo>> getList(@RequestBody MessageQueryReq messageQueryReq){
return messageService.getList(messageQueryReq);
}
@PostMapping("/getFriendChatList")
Result<IPage<FriendMessageVo>> getFriendChatList(@RequestBody MessageQueryReq messageQueryReq){
return messageService.getFriendChatList(messageQueryReq);
}
}

View File

@@ -0,0 +1,84 @@
package com.realtime.controller;
import com.realtime.exception.BusinessException;
import com.realtime.model.query.TtsOptionsReq;
import com.realtime.service.BaiduTtsService;
import com.realtime.service.FileService;
import com.realtime.sysconst.Result;
import com.realtime.utils.InputStreamMultipartFile;
import com.realtime.vo.UploadVo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.io.ByteArrayInputStream;
import java.util.UUID;
@Slf4j
@RestController
@RequestMapping("/tts")
@RequiredArgsConstructor(onConstructor_ = {@Autowired})
public class TtsController {
private final BaiduTtsService ttsService;
private final FileService fileService;
/**
* 文本转语音,返回音频流
*/
@GetMapping("/speech")
public Result<UploadVo> textToSpeech(@RequestParam("text") String text,
@RequestParam(defaultValue = "0",value = "per") int per,
@RequestParam(defaultValue = "5",value = "speed") int speed) {
try {
TtsOptionsReq options = TtsOptionsReq.builder()
.per(per)
.speed(speed)
.build();
byte[] audioData = ttsService.textToSpeech(text, options);
if (audioData == null) {
throw new BusinessException("转换异常");
}
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(audioData);
InputStreamMultipartFile speech = new InputStreamMultipartFile(UUID.randomUUID().toString(), "speech.mp3", "audio/mp3", byteArrayInputStream);
return fileService.uploadFile(speech);
} catch (Exception e) {
throw new BusinessException(e.getMessage());
}
}
/**
* 文本转语音并下载
*/
@PostMapping("/download")
public ResponseEntity<byte[]> downloadSpeech(@RequestParam String text) {
try {
byte[] audioData = ttsService.textToSpeech(text, TtsOptionsReq.defaultOptions());
if (audioData == null) {
return ResponseEntity.badRequest().build();
}
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
headers.setContentDispositionFormData("attachment", "speech.mp3");
return ResponseEntity.ok()
.headers(headers)
.body(audioData);
} catch (Exception e) {
return ResponseEntity.internalServerError().build();
}
}
}

View File

@@ -0,0 +1,22 @@
package com.realtime.exception;
import com.realtime.sysconst.enumConst.ResultEnum;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = true)
public class BusinessException extends RuntimeException {
private Integer code;
private ResultEnum result;
public BusinessException(ResultEnum result) {
super(result.getMsg());
this.code = result.getCode();
}
public BusinessException(String msg) {
super(msg);
}
}

View File

@@ -0,0 +1,22 @@
package com.realtime.mappers;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.realtime.model.pojo.ChatList;
import com.realtime.model.query.ChatListPageQueryReq;
import com.realtime.vo.ChatListInfoVo;
import org.apache.ibatis.annotations.Param;
public interface ChatListMapper extends BaseMapper<ChatList> {
String getMachineIdBySenderId(@Param("id") String senderId);
IPage<ChatListInfoVo> getChatList(@Param("reqPage") IPage<ChatListPageQueryReq> page, @Param("req") ChatListPageQueryReq chatListPageQueryReq);
void deleteFriend(@Param("id") String id,@Param("receiver") String receiver);
}

View File

@@ -0,0 +1,18 @@
package com.realtime.mappers;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.realtime.model.pojo.FriendRelationship;
import com.realtime.model.query.ChatFriendRelationshipQueryReq;
import com.realtime.vo.ChatFriendRelationshipInfoVo;
import org.apache.ibatis.annotations.Param;
public interface FriendRelationshipMapper extends BaseMapper<FriendRelationship> {
IPage<ChatFriendRelationshipInfoVo> selectByPhone(@Param("page") IPage<ChatFriendRelationshipQueryReq> page, @Param("req") ChatFriendRelationshipQueryReq req);
void updateStateById(@Param("id") Long friendRelationshipId);
}

View File

@@ -0,0 +1,30 @@
package com.realtime.mappers;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.realtime.model.pojo.GroupList;
import com.realtime.model.query.GroupDetailQueryReq;
import com.realtime.model.query.GroupListQueryReq;
import com.realtime.model.remove.DisbandGroupReq;
import com.realtime.model.update.GroupInventUpdateReq;
import com.realtime.vo.GroupDetailVo;
import com.realtime.vo.GroupListVo;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface GroupListMapper extends BaseMapper<GroupList> {
IPage<GroupListVo> getGroup(@Param("reqpage")IPage<GroupListQueryReq> iPage, @Param("req") GroupListQueryReq loginIdAsLong);
GroupDetailVo getGroupDetail(@Param("req") GroupDetailQueryReq queryReq);
void updateInvent(@Param("req") GroupInventUpdateReq groupInventUpdateReq);
int isOwner(@Param("req") DisbandGroupReq disbandGroupReq);
}

View File

@@ -0,0 +1,34 @@
package com.realtime.mappers;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.realtime.model.pojo.GroupMember;
import com.realtime.model.query.GroupMemberListQueryReq;
import com.realtime.model.remove.DisbandGroupReq;
import com.realtime.model.remove.RemoveGroupReq;
import com.realtime.model.remove.RemovesGroupReq;
import com.realtime.vo.ChatListInfoVo;
import com.realtime.vo.GroupMemberListVo;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface GroupMemberMapper extends BaseMapper<GroupMember> {
IPage<GroupMemberListVo> getMemberList(@Param("page") IPage<GroupMemberListQueryReq> queryReqIPage, @Param("req") GroupMemberListQueryReq queryReq);
IPage<ChatListInfoVo> getAllByGroupIdAndMemberId(@Param("page") IPage<GroupMemberListQueryReq> queryReqIPage, @Param("req") GroupMemberListQueryReq queryReq);
void quit(RemoveGroupReq removeGroupReq);
void removeByGroupId(@Param("req") DisbandGroupReq disbandGroupReq);
void removesByGroupPhoneAndGroupId(@Param("req") RemovesGroupReq ids);
List<Long> getByGroupContactId(@Param("groupContactId")String groupContactId);
}

View File

@@ -0,0 +1,18 @@
package com.realtime.mappers;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.realtime.model.pojo.GroupMessage;
import com.realtime.model.query.GroupListQueryReq;
import com.realtime.vo.GroupListMessagesVo;
import org.apache.ibatis.annotations.Param;
public interface GroupMessageMapper extends BaseMapper<GroupMessage> {
IPage<GroupListMessagesVo> getGroupList(@Param("page") IPage<GroupListQueryReq> req, @Param("req") GroupListQueryReq req1);
}

View File

@@ -0,0 +1,24 @@
package com.realtime.mappers;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.realtime.model.pojo.Message;
import com.realtime.model.query.MessageQueryReq;
import com.realtime.vo.FriendMessageVo;
import com.realtime.vo.MessageItemVo;
import com.realtime.vo.MessageVo;
import org.apache.ibatis.annotations.Param;
public interface MessageMapper extends BaseMapper<Message> {
IPage<MessageVo> getMsgList(@Param("req") IPage<MessageQueryReq> messageQueryReqIPage, @Param("req") MessageQueryReq messageQueryReq);
IPage<FriendMessageVo> getFriendMsgList(@Param("page") IPage<MessageQueryReq> messageQueryReqIPage, @Param("req") MessageQueryReq messageQueryReq);
MessageItemVo selectLastContent(@Param("sessionId") Long sessionId);
}

View File

@@ -0,0 +1,13 @@
package com.realtime.model.baseModel;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = true)
public class BaseQueryModel<T> extends Page<T> {
private Long id;
private String contactId;
}

View File

@@ -0,0 +1,47 @@
package com.realtime.model.pojo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
@Data
public class ChatList implements Serializable {
@TableId(type = IdType.AUTO)
private Long id;
/**
* 好友昵称
*/
private String friendNickName;
/**
* 发送者用户电话
*/
private String sender;
/**
* 接受者Id
*/
private String receiver;
/**
* 是否将好友置顶
*/
private Integer isTop;
/**
* 会话Id
*/
private Long sessionId;
/**
* 用户是不可以删除助手的 只有用户与用户之间可以删除 1 = 可以 0 = 不可以
*/
private Integer isDelete;
private LocalDateTime createdTime;
@TableField(exist = false)
private Long friendRelationshipId;
}

View File

@@ -0,0 +1,11 @@
package com.realtime.model.pojo;
import lombok.Data;
import java.io.Serializable;
@Data
public class ChatListSaveReq implements Serializable {
private ChatList chatList;
private ChatList newChatList;
}

View File

@@ -0,0 +1,37 @@
package com.realtime.model.pojo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
@Data
public class FriendRelationship implements Serializable {
@TableId(type = IdType.AUTO)
private Long id;
private String sendId;
/**
* 发起添加好友的时间
*/
private LocalDateTime createdTime;
/**
* 0 = 未通过 1 = 通过好友
*/
private Integer state;
private String friendNickName;
/**
* 这个是被申请加好友的一方
*/
private String recipientId;
/**
* 写入到chat_list的session_id
*/
private Long sessionId = System.currentTimeMillis();
}

View File

@@ -0,0 +1,48 @@
package com.realtime.model.pojo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
@Data
public class GroupList implements Serializable {
@TableId(type = IdType.INPUT)
private Long id;
/**
*
*/
private String name;
/**
* 创建者
*/
private String creator;
/**
*
*/
private LocalDateTime createdTime;
/**
* 群图片
*/
private String picture;
/**
* 固定值
*/
private Integer type;
/**
* 是否需要邀请才能加入 1 = 是 0 = 否
*/
private Integer isInvent;
private Integer owner;
}

View File

@@ -0,0 +1,31 @@
package com.realtime.model.pojo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
@Data
public class GroupMember implements Serializable {
@TableId(type = IdType.AUTO)
private Long id;
/**
*
*/
private Long groupId;
/**
* 成员电话号码
*/
private String groupContactId;
/**
* 加入时间
*/
private LocalDateTime createdTime;
}

View File

@@ -0,0 +1,63 @@
package com.realtime.model.pojo;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.realtime.packets.GroupSendPacket;
import lombok.Data;
import java.io.Serializable;
@Data
public class GroupMessage implements Serializable {
/**
*
*/
@TableId(type = IdType.AUTO)
private Long id;
/**
* 消息内容
*/
private String message;
/**
* 群Id
*/
private Long groupId;
/**
* 发送者Id
*/
private String sender;
/**
*
*/
private Long createTime;
/**
* 0消息 1 =文件/文件
*/
private Integer messageType;
private String contentJson;
public GroupMessage(GroupSendPacket groupMessage) {
this.createTime = System.currentTimeMillis();
this.message = groupMessage.getMessage();
this.groupId = groupMessage.getGroupId();
this.sender = groupMessage.getSender();
this.messageType = Integer.valueOf(groupMessage.getMessageType());
if (this.messageType == 1) {
this.contentJson = JSON.toJSONString(groupMessage.getUploadVos());
}
}
}

View File

@@ -0,0 +1,64 @@
package com.realtime.model.pojo;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.realtime.packets.SendMsgPackets;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Message {
/**
*
*/
@TableId(type = IdType.AUTO)
private Long id;
/**
* 发送者手机号
*/
private String sender;
/**
* 接受者id
*/
private String receiver;
/**
* 消息类型(0:文字;1:图片;2:文件)
*/
private String messageType;
/**
* 内容或url
*/
private String content;
private Long sessionId;
/**
* 发送时间
*/
private Long createTime;
private String contentJson;
public Message(SendMsgPackets params) {
this.sender = params.getSender();
this.receiver = params.getReceiver();
this.createTime = System.currentTimeMillis();
this.messageType = String.valueOf(params.getContentType());
this.content = params.getMessage();
this.sessionId = params.getSessionId();
this.contentJson = JSONObject.toJSONString(params.getUploadVos());
}
}

View File

@@ -0,0 +1,12 @@
package com.realtime.model.query;
import com.realtime.model.baseModel.BaseQueryModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Data
public class ChatFriendRelationshipQueryReq extends BaseQueryModel<ChatFriendRelationshipQueryReq> {
private String sendId;
}

View File

@@ -0,0 +1,12 @@
package com.realtime.model.query;
import com.realtime.model.baseModel.BaseQueryModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Data
public class ChatListPageQueryReq extends BaseQueryModel<ChatListPageQueryReq> {
private String sendId;
}

View File

@@ -0,0 +1,13 @@
package com.realtime.model.query;
import com.realtime.model.baseModel.BaseQueryModel;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Data
@AllArgsConstructor
public class ChatQueryPageReq extends BaseQueryModel<ChatQueryPageReq> {
}

View File

@@ -0,0 +1,12 @@
package com.realtime.model.query;
import com.realtime.model.baseModel.BaseQueryModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Data
public class GroupDetailQueryReq extends BaseQueryModel<GroupDetailQueryReq> {
private Long groupId;
}

View File

@@ -0,0 +1,14 @@
package com.realtime.model.query;
import com.realtime.model.baseModel.BaseQueryModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Data
public class GroupListQueryReq extends BaseQueryModel<GroupListQueryReq> {
private Long groupId;
private Long beginTime;
private Long endTime;
}

View File

@@ -0,0 +1,16 @@
package com.realtime.model.query;
import com.realtime.model.baseModel.BaseQueryModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDateTime;
@EqualsAndHashCode(callSuper = true)
@Data
public class GroupMemberListQueryReq extends BaseQueryModel<GroupMemberListQueryReq> {
private Long groupId;
private String name;
private LocalDateTime beginTime;
private LocalDateTime endTime;
}

View File

@@ -0,0 +1,11 @@
package com.realtime.model.query;
import lombok.Data;
import java.io.Serializable;
@Data
public class LoginQueryReq implements Serializable {
private Long phone;
private String password;
}

View File

@@ -0,0 +1,18 @@
package com.realtime.model.query;
import com.realtime.model.baseModel.BaseQueryModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Data
public class MessageQueryReq extends BaseQueryModel<MessageQueryReq> {
private Long taskId;
private Long acceptId;
private Long sessionId;
private Long beginTime;
private Long endTime;
}

View File

@@ -0,0 +1,50 @@
package com.realtime.model.query;
import lombok.Builder;
import lombok.Data;
@Data
@Builder
public class TtsOptionsReq {
/**
* 语言
*/
@Builder.Default
private String lang = "zh";
/**
* 语速0-15默认为5
*/
@Builder.Default
private int speed = 5;
/**
* 音调0-15默认为5
*/
@Builder.Default
private int pitch = 5;
/**
* 音量0-15默认为5
*/
@Builder.Default
private int volume = 5;
/**
* 发音人选择
* 0-女声1-男声3-情感合成-度逍遥4-情感合成-度丫丫
*/
@Builder.Default
private int per = 4100;
/**
* 音频格式
* 3mp3默认 4pcm-16k 5pcm-8k 6wav
*/
@Builder.Default
private String aue = "3";
public static TtsOptionsReq defaultOptions() {
return TtsOptionsReq.builder().build();
}
}

View File

@@ -0,0 +1,9 @@
package com.realtime.model.remove;
import lombok.Data;
@Data
public class DisbandGroupReq {
private Long groupId;
private String groupContactId;
}

View File

@@ -0,0 +1,9 @@
package com.realtime.model.remove;
import lombok.Data;
@Data
public class RemoveGroupReq {
private Long groupId;
private String groupContactId;
}

View File

@@ -0,0 +1,11 @@
package com.realtime.model.remove;
import lombok.Data;
import java.util.List;
@Data
public class RemovesGroupReq {
private Long groupId;
private List<String> groupContactId;
}

View File

@@ -0,0 +1,9 @@
package com.realtime.model.update;
import lombok.Data;
@Data
public class GroupInventUpdateReq {
private Long groupId;
private Integer invent;
}

View File

@@ -0,0 +1,13 @@
package com.realtime.model.update;
import lombok.Data;
import java.io.Serializable;
@Data
public class UpdateUserPasswordReq implements Serializable {
private String code;
private String password;
private Long phone;
private String type;
}

View File

@@ -0,0 +1,16 @@
package com.realtime.model.update;
import lombok.Data;
import java.io.Serializable;
@Data
public class UserInfoUpdateReq implements Serializable {
private Long id;
private Long phone;
private String nickName;
private String avatar;
private String occupation;
private String region;
private Integer sex;
}

View File

@@ -0,0 +1,18 @@
package com.realtime.packets;
import com.realtime.packets.basePackets.BasePackets;
import com.realtime.packets.command.Command;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Data
public class AnswerPacket extends BasePackets {
private String sdp;
private String type; // answer
@Override
public Byte getCommand() {
return Command.ANSWER;
}
}

View File

@@ -0,0 +1,23 @@
package com.realtime.packets;
import com.realtime.packets.basePackets.BasePackets;
import com.realtime.packets.command.Command;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
@EqualsAndHashCode(callSuper = true)
@Data
public class AutoSoftwarePacket extends BasePackets {
private String url;
private String body;
private String method;
private String commandLine;
@Override
public Byte getCommand() {
return Command.SOFT_CALL_MSG;
}
}

View File

@@ -0,0 +1,18 @@
package com.realtime.packets;
import com.realtime.packets.basePackets.BasePackets;
import com.realtime.packets.command.Command;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Data
public class CallPacket extends BasePackets {
private String sdp;
private String type; // "call", "leave", "offer", "answer", "candidate", "hangup"
@Override
public Byte getCommand() {
return Command.CALL_LING;
}
}

View File

@@ -0,0 +1,16 @@
package com.realtime.packets;
import com.realtime.packets.basePackets.BasePackets;
import com.realtime.packets.command.Command;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Data
public class ConnectPacket extends BasePackets {
@Override
public Byte getCommand() {
return Command.REGISTER;
}
}

View File

@@ -0,0 +1,18 @@
package com.realtime.packets;
import com.realtime.packets.basePackets.BasePackets;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Data
public class FilePackets extends BasePackets {
private String fileName;
private String filePath;
@Override
public Byte getCommand() {
return -1;
}
}

View File

@@ -0,0 +1,22 @@
package com.realtime.packets;
import com.realtime.packets.basePackets.BasePackets;
import com.realtime.packets.command.Command;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
@EqualsAndHashCode(callSuper = true)
@Data
public class GroupPacket extends BasePackets {
private List<String> userIds;
private Integer type = 2;
private String groupName;
@Override
public Byte getCommand() {
return Command.CREATE_GROUP;
}
}

View File

@@ -0,0 +1,21 @@
package com.realtime.packets;
import com.realtime.packets.basePackets.BasePackets;
import com.realtime.packets.command.Command;
import com.realtime.vo.UploadVo;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
@EqualsAndHashCode(callSuper = true)
@Data
public class GroupSendPacket extends BasePackets {
private Long groupId;
private Integer type = 2;
private List<UploadVo> uploadVos;
@Override
public Byte getCommand() {
return Command.GROUP_MESSAGE;
}
}

View File

@@ -0,0 +1,21 @@
package com.realtime.packets;
import com.realtime.packets.basePackets.BasePackets;
import com.realtime.packets.command.Command;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
@EqualsAndHashCode(callSuper = true)
@Data
public class InventGroupPacket extends BasePackets {
private List<String> userIds;
private Long groupId;
@Override
public Byte getCommand() {
return Command.INVENT_GROUP;
}
}

View File

@@ -0,0 +1,15 @@
package com.realtime.packets;
import com.realtime.packets.basePackets.BasePackets;
import com.realtime.packets.command.Command;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Data
public class PingPongPacket extends BasePackets {
@Override
public Byte getCommand() {
return Command.PING_PONG;
}
}

View File

@@ -0,0 +1,20 @@
package com.realtime.packets;
import com.realtime.packets.basePackets.BasePackets;
import com.realtime.packets.command.Command;
import io.netty.handler.codec.http.HttpObject;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Data
public class RealTimePacket extends BasePackets {
private String httpObject;
private String end;
@Override
public Byte getCommand() {
return Command.REAL_TIME_VOICE;
}
}

View File

@@ -0,0 +1,21 @@
package com.realtime.packets;
import com.realtime.packets.basePackets.BasePackets;
import com.realtime.packets.command.Command;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
@EqualsAndHashCode(callSuper = true)
@Data
public class RemoveGroupPacket extends BasePackets {
private List<String> userIds;
private Long groupId;
@Override
public Byte getCommand() {
return Command.REMOVE_GROUP;
}
}

View File

@@ -0,0 +1,31 @@
package com.realtime.packets;
import com.realtime.packets.basePackets.BasePackets;
import com.realtime.packets.command.Command;
import com.realtime.vo.UploadVo;
import io.minio.messages.Upload;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
@EqualsAndHashCode(callSuper = true)
@Data
public class SendMsgPackets extends BasePackets {
private FilePackets filePackets;
private Long sessionId;
private Integer contentType;
private String friendNickName; //朋友给我的昵称
private String friendName;// 我给朋友的昵称
private String toAvatar;
private Integer unRead = 1;//规定一条
private List<UploadVo> uploadVos;
private Integer type = 1;
@Override
public Byte getCommand() {
return Command.SINGLE_MESSAGE;
}
}

View File

@@ -0,0 +1,17 @@
package com.realtime.packets;
import com.realtime.packets.basePackets.BasePackets;
import com.realtime.packets.command.Command;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Data
public class SystemNoticePackets extends BasePackets {
@Override
public Byte getCommand() {
return Command.SYS_NOTICE;
}
}

View File

@@ -0,0 +1,24 @@
package com.realtime.packets.basePackets;
import io.netty.handler.codec.http.HttpObject;
import lombok.Data;
import java.io.Serializable;
@Data
public abstract class BasePackets implements Serializable {
private Byte version = 1;
private String sender;
private String receiver;
private Long groupId = System.currentTimeMillis();
private String nickName;
private String avatar;
private String messageType;
private String message;
private HttpObject audioByte;
private Long createTime = System.currentTimeMillis();
private Boolean isNoticeBar = Boolean.FALSE; // 是否需要显示到手机通知栏
private Boolean callBackMessage = Boolean.FALSE; //是否需要回执消息
public abstract Byte getCommand();
}

View File

@@ -0,0 +1,25 @@
package com.realtime.packets.command;
public interface Command {
Byte SELF_RESPONSE = 0; //自己发送的消息确认
Byte SINGLE_MESSAGE = 1; //私聊消息
Byte MESSAGE_RESPONSE = 2; //私聊响应
Byte CREATE_GROUP = 3; //创建群
Byte CREATE_GROUP_RESPONSE = 4; //创建群响应
Byte LOGIN_REQUEST = 5; //登录请求
Byte LOGIN_RESPONSE = 6; // 登录响应
Byte REGISTER = 7; //注册请求
Byte INVENT_GROUP = 8; //邀请好友加入群聊
Byte GROUP_MESSAGE = 9; //发送群消息
Byte REMOVE_GROUP = 10; //移除群成员
Byte HEARTBEAT_REQUEST = 11; //心跳消息请求
Byte HEARTBEAT_RESPONSE = 12; //心跳消息恢复
Byte RED_PACK_CREATED = 13; //红包发起
Byte JOIN_GROUP = 14; //加入群
Byte CALL_LING = 15;// 语音通话发起
Byte ANSWER = 16;
Byte SYS_NOTICE = 20; //系统消息
Byte PING_PONG = 21;
Byte REAL_TIME_VOICE = 22;
Byte SOFT_CALL_MSG = 23;
}

View File

@@ -0,0 +1,18 @@
package com.realtime.packets.resp;
import com.realtime.packets.basePackets.BasePackets;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class GroupResponsePacket {
private Long groupId;
private Integer groupSize;
private boolean success = Boolean.TRUE;
}

View File

@@ -0,0 +1,47 @@
package com.realtime.packets.server;
import com.realtime.packets.server.handler.SocketChannelHandler;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.concurrent.atomic.AtomicReference;
@Slf4j
@Component
@ChannelHandler.Sharable
@RequiredArgsConstructor
public class NettyServer {
private final EventLoopGroup bossGroup = new NioEventLoopGroup(8);
private final EventLoopGroup workGroup = new NioEventLoopGroup();
private final SocketChannelHandler myWebSocketChannelHandler;
private final AtomicReference<Integer> port = new AtomicReference<>(88);
public void run() {
try {
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workGroup).option(ChannelOption.SO_BACKLOG, 1024)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 1024)
.childOption(ChannelOption.SO_KEEPALIVE, true)
.childOption(ChannelOption.TCP_NODELAY, true)
.childHandler(myWebSocketChannelHandler);
Channel ch = bootstrap.bind(port.get()).sync().channel();
ch.closeFuture().sync();
} catch (Exception e) {
log.error("异常{}", e.getMessage());
} finally {
bossGroup.shutdownGracefully();
workGroup.shutdownGracefully();
}
}
}

View File

@@ -0,0 +1,59 @@
package com.realtime.packets.server.handler;
import com.realtime.packets.ConnectPacket;
import com.realtime.service.GroupMemberService;
import com.realtime.utils.SessionUtils;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
@Slf4j
@Component
@ChannelHandler.Sharable
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class ConnectMessageHandler extends SimpleChannelInboundHandler<ConnectPacket> {
private final GroupMemberService groupMemberService;
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, ConnectPacket connectPacket) throws Exception {
SessionUtils.bindChannel(connectPacket, channelHandlerContext.channel());
connectGroup(channelHandlerContext, connectPacket);
if (SessionUtils.hasLogin(channelHandlerContext.channel())) {
System.out.println(channelHandlerContext.channel());
log.info("{}用户已登录", connectPacket.getSender());
}
}
// 重连到群组
void connectGroup(ChannelHandlerContext ctx, ConnectPacket connectPacket) {
List<Long> byGroupContactId = groupMemberService.getByGroupContactId(connectPacket.getSender());
if (!byGroupContactId.isEmpty()) {
byGroupContactId.forEach(item -> {
ChannelGroup channelGroup = SessionUtils.getChannelGroup(item);
if (channelGroup == null) {
ChannelGroup channels = new DefaultChannelGroup(ctx.executor());
SessionUtils.bindChannelGroup(item, channels);
Channel channel = SessionUtils.getChannel(connectPacket.getSender());
channels.add(channel);
} else {
Channel channel = SessionUtils.getChannel(connectPacket.getSender());
boolean contains = channelGroup.contains(channel);
if (!contains) {
channelGroup.add(channel);
}
}
});
}
}
}

View File

@@ -0,0 +1,34 @@
package com.realtime.packets.server.handler;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import lombok.extern.slf4j.Slf4j;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Slf4j
@Sharable
public class ExceptionHandler extends ChannelDuplexHandler {
private final Map<String,Object> map = new ConcurrentHashMap<>();
public static final ExceptionHandler INSTANCE_EXCEPTION = new ExceptionHandler();
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
if (cause instanceof RuntimeException) {
ByteBuf byteBuf = ctx.alloc().buffer();
map.put("errorCode",-10000);
map.put("errorMessage",cause.getMessage());
byteBuf.writeBytes(map.toString().getBytes(StandardCharsets.UTF_8));
ctx.channel().writeAndFlush(new TextWebSocketFrame(byteBuf));
}
}
}

View File

@@ -0,0 +1,48 @@
package com.realtime.packets.server.handler;
import com.alibaba.fastjson2.JSONObject;
import com.realtime.model.pojo.GroupMessage;
import com.realtime.packets.GroupSendPacket;
import com.realtime.service.GroupMessageService;
import com.realtime.utils.SessionUtils;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.nio.charset.StandardCharsets;
@Slf4j
@Component
@ChannelHandler.Sharable
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class GroupMessageHandler extends SimpleChannelInboundHandler<GroupSendPacket> {
private final GroupMessageService groupMessageService;
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, GroupSendPacket groupSendPacket) throws Exception {
ChannelGroup group = SessionUtils.getChannelGroup(groupSendPacket.getGroupId());
ByteBuf buff = getBuff(channelHandlerContext, groupSendPacket);
assert group != null;
group.writeAndFlush(new TextWebSocketFrame(buff));
}
ByteBuf getBuff(ChannelHandlerContext channelHandlerContext, GroupSendPacket groupSendPacket) {
ByteBuf byteBuf = channelHandlerContext.alloc().buffer();
byte[] bytes = JSONObject.toJSONString(groupSendPacket).getBytes(StandardCharsets.UTF_8);
byteBuf.writeBytes(bytes);
groupMessageService.save(new GroupMessage(groupSendPacket));
return byteBuf;
}
}

View File

@@ -0,0 +1,60 @@
package com.realtime.packets.server.handler;
import com.alibaba.fastjson2.JSONObject;
import com.realtime.packets.InventGroupPacket;
import com.realtime.utils.SessionUtils;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.nio.charset.StandardCharsets;
@Slf4j
@Component
@ChannelHandler.Sharable
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class InventGroupHandler extends SimpleChannelInboundHandler<InventGroupPacket> {
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, InventGroupPacket inventGroupPacket) {
Long groupId = inventGroupPacket.getGroupId();
if (SessionUtils.getChannelGroup(groupId) == null) {
ChannelGroup channels = new DefaultChannelGroup(channelHandlerContext.executor());
SessionUtils.bindChannelGroup(groupId, channels);
inventGroupPacket.getUserIds().forEach(item -> {
Channel userChannel = SessionUtils.getChannel(item);
if (userChannel != null) {
channels.add(userChannel);
}
});
ByteBuf buff = getBuff(channelHandlerContext, inventGroupPacket);
channels.writeAndFlush(new TextWebSocketFrame(buff.toString(StandardCharsets.UTF_8)));
} else {
ChannelGroup channelGroup = SessionUtils.getChannelGroup(groupId);
inventGroupPacket.getUserIds().forEach(item -> {
Channel userChannel = SessionUtils.getChannel(item);
if (userChannel != null) {
channelGroup.add(userChannel);
}
});
ByteBuf buff = getBuff(channelHandlerContext, inventGroupPacket);
channelGroup.writeAndFlush(new TextWebSocketFrame(buff.toString(StandardCharsets.UTF_8)));
}
}
ByteBuf getBuff(ChannelHandlerContext channelHandlerContext, InventGroupPacket inventGroupPacket) {
ByteBuf byteBuf = channelHandlerContext.alloc().buffer();
byte[] bytes = JSONObject.toJSONString(inventGroupPacket).getBytes(StandardCharsets.UTF_8);
byteBuf.writeBytes(bytes);
return byteBuf;
}
}

View File

@@ -0,0 +1,58 @@
package com.realtime.packets.server.handler;
import com.alibaba.fastjson2.JSONObject;
import com.realtime.packets.GroupPacket;
import com.realtime.service.GroupListService;
import com.realtime.utils.SessionUtils;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.nio.charset.StandardCharsets;
@Slf4j
@Component
@ChannelHandler.Sharable
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class JoinGroupHandler extends SimpleChannelInboundHandler<GroupPacket> {
private final GroupListService groupListService;
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, GroupPacket groupPacket) throws Exception {
ChannelGroup channels = new DefaultChannelGroup(channelHandlerContext.executor());
Long groupId = groupPacket.getGroupId();
SessionUtils.bindChannelGroup(groupPacket.getGroupId(), channels);
groupPacket.getUserIds().add(groupPacket.getSender());//把自己添加到群聊
groupPacket.getUserIds().forEach(item -> {
Channel userChannel = SessionUtils.getChannel(item);
if (userChannel != null) {
channels.add(userChannel);
}
});
System.out.println(channels.size()+"");
ByteBuf buff = getBuff(channelHandlerContext, channels, groupId, groupPacket);
channels.writeAndFlush(new TextWebSocketFrame(buff.toString(StandardCharsets.UTF_8)));
}
ByteBuf getBuff(ChannelHandlerContext channelHandlerContext, ChannelGroup channels, Long groupId, GroupPacket groupPacket) {
ByteBuf byteBuf = channelHandlerContext.alloc().buffer();
groupPacket.setGroupId(groupId);
byte[] bytes = JSONObject.toJSONString(groupPacket).getBytes(StandardCharsets.UTF_8);
byteBuf.writeBytes(bytes);
groupListService.saveGroupList(groupPacket, groupId);
return byteBuf;
}
}

View File

@@ -0,0 +1,78 @@
package com.realtime.packets.server.handler;
import com.alibaba.fastjson2.JSONObject;
import com.realtime.config.NettyConfig;
import com.realtime.packets.basePackets.BasePackets;
import com.realtime.packets.strategy.PacketService;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.nio.charset.StandardCharsets;
@Slf4j
@Service
@ChannelHandler.Sharable
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class MessageSocketHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
private final PacketService packetService;
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, TextWebSocketFrame binaryWebSocketFrame) throws Exception {
dealWithMessage(channelHandlerContext, binaryWebSocketFrame);
}
private void dealWithMessage(ChannelHandlerContext context, WebSocketFrame webSocketFrame) {
if (webSocketFrame instanceof CloseWebSocketFrame) {
context.channel().close();
}
if (webSocketFrame instanceof PingWebSocketFrame) {
context.channel().write(new PongWebSocketFrame(webSocketFrame.content().retain()));
}
ByteBuf byteBuf = webSocketFrame.content();
String content = byteBuf.toString(StandardCharsets.UTF_8);
JSONObject dataObject = JSONObject.parseObject(content);
JSONObject body = dataObject.getJSONObject("body");
BasePackets basePackets = packetService.getCurrent(body);
if (basePackets.getCallBackMessage()) {
TextWebSocketFrame tws = new TextWebSocketFrame(String.valueOf(JSONObject.toJSONString(basePackets)));
context.channel().writeAndFlush(tws);
}
context.fireChannelRead(basePackets);
}
@Override
public void channelActive(ChannelHandlerContext ctx) {
NettyConfig.group.add(ctx.channel());
}
@Override
public void channelInactive(ChannelHandlerContext ctx) {
NettyConfig.group.remove(ctx.channel());
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) {
ctx.flush();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
// ctx.close();
}
}

View File

@@ -0,0 +1,41 @@
package com.realtime.packets.server.handler;
import com.alibaba.fastjson2.JSONObject;
import com.realtime.packets.PingPongPacket;
import com.realtime.utils.SessionUtils;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.atomic.AtomicInteger;
@Slf4j
@Component
@ChannelHandler.Sharable
public class PingPongHartHandler extends SimpleChannelInboundHandler<PingPongPacket> {
private final AtomicInteger times = new AtomicInteger(10);
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, PingPongPacket pingPongPacket) throws Exception {
Channel channel = SessionUtils.getChannel(pingPongPacket.getSender());
ByteBuf buff = getBuff(channelHandlerContext, pingPongPacket);
assert channel != null;
channel.writeAndFlush(new TextWebSocketFrame(buff));
}
ByteBuf getBuff(ChannelHandlerContext channelHandlerContext, PingPongPacket pingPongPacket) {
ByteBuf byteBuf = channelHandlerContext.alloc().buffer();
pingPongPacket.setMessage("pong");
byte[] bytes = JSONObject.toJSONString(pingPongPacket).getBytes(StandardCharsets.UTF_8);
byteBuf.writeBytes(bytes);
return byteBuf;
}
}

View File

@@ -0,0 +1,51 @@
package com.realtime.packets.server.handler;
import com.alibaba.fastjson2.JSONObject;
import com.realtime.mappers.MessageMapper;
import com.realtime.model.pojo.Message;
import com.realtime.packets.SendMsgPackets;
import com.realtime.service.MessageService;
import com.realtime.utils.SessionUtils;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.nio.charset.StandardCharsets;
import java.util.Objects;
@Slf4j
@Component
@ChannelHandler.Sharable
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class PrivateMessageHandler extends SimpleChannelInboundHandler<SendMsgPackets> {
private final MessageMapper messageMapper;
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, SendMsgPackets sendMsgPackets) throws Exception {
messageMapper.insert(new Message(sendMsgPackets));
Channel chanel = SessionUtils.getChannel(sendMsgPackets.getReceiver());
ByteBuf buff = getBuff(channelHandlerContext, sendMsgPackets);
Objects.requireNonNull(chanel).writeAndFlush(new TextWebSocketFrame(buff));
System.out.println("发送私聊消息");
}
ByteBuf getBuff(ChannelHandlerContext channelHandlerContext, SendMsgPackets sendMsgPackets) {
ByteBuf byteBuf = channelHandlerContext.alloc().buffer();
byte[] bytes = JSONObject.toJSONString(sendMsgPackets).getBytes(StandardCharsets.UTF_8);
byteBuf.writeBytes(bytes);
return byteBuf;
}
}

View File

@@ -0,0 +1,44 @@
package com.realtime.packets.server.handler;
import com.realtime.config.RtasrConfig;
import com.realtime.packets.RealTimePacket;
import com.realtime.sysconst.RTASRClient;
import com.realtime.utils.Base64ToMultipartFile;
import com.realtime.utils.SessionUtils;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean;
@Slf4j
@Component
@ChannelHandler.Sharable
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class RealTimeTransferHandler extends SimpleChannelInboundHandler<RealTimePacket> {
// private final RealTimeVoiceService realTimeVoiceService;
//
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, RealTimePacket realTimePacket) throws Exception {
}
}

View File

@@ -0,0 +1,46 @@
package com.realtime.packets.server.handler;
import com.alibaba.fastjson2.JSONObject;
import com.realtime.packets.RemoveGroupPacket;
import com.realtime.utils.SessionUtils;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.nio.charset.StandardCharsets;
@Slf4j
@Component
@ChannelHandler.Sharable
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class RemoveGroupHandler extends SimpleChannelInboundHandler<RemoveGroupPacket> {
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, RemoveGroupPacket removeGroupPacket) {
Long groupId = removeGroupPacket.getGroupId();
ChannelGroup channelGroup = SessionUtils.getChannelGroup(groupId);
removeGroupPacket.getUserIds().forEach(userId -> {
Channel channel = SessionUtils.getChannel(userId);
assert channelGroup != null;
channelGroup.remove(channel);
});
ByteBuf buff = getBuff(channelHandlerContext, removeGroupPacket);
assert channelGroup != null;
channelGroup.writeAndFlush(new TextWebSocketFrame(buff.toString(StandardCharsets.UTF_8)));
}
ByteBuf getBuff(ChannelHandlerContext channelHandlerContext, RemoveGroupPacket removeGroupPacket) {
ByteBuf byteBuf = channelHandlerContext.alloc().buffer();
byte[] bytes = JSONObject.toJSONString(removeGroupPacket).getBytes(StandardCharsets.UTF_8);
byteBuf.writeBytes(bytes);
return byteBuf;
}
}

View File

@@ -0,0 +1,69 @@
package com.realtime.packets.server.handler;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.*;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.CharsetUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import static com.realtime.packets.server.handler.ExceptionHandler.INSTANCE_EXCEPTION;
@Slf4j
@Service
@ChannelHandler.Sharable
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class SocketChannelHandler extends ChannelInitializer<SocketChannel> {
private final MessageSocketHandler myWebSocketHandler;
private final GroupMessageHandler groupMessageHandler;
private final ConnectMessageHandler connectMessageHandler;
private final JoinGroupHandler joinGroupHandler;
private final PrivateMessageHandler privateMessageHandler;
private final PingPongHartHandler pingPongHartHandler;
private final InventGroupHandler inventGroupHandler;
private final RealTimeTransferHandler realTimeTransferHandler;
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
int maxFrameSize = 10 * 1024 * 1024;
socketChannel.pipeline()
.addLast("http-codec", new HttpServerCodec())
.addLast("http-chunked", new ChunkedWriteHandler())
.addLast(new WebSocketServerProtocolHandler("/ws", null,true, maxFrameSize))
.addLast()
.addLast(new StringDecoder(CharsetUtil.UTF_8))
.addLast(new StringEncoder(CharsetUtil.UTF_8))
.addLast("myWebSocketHandler", myWebSocketHandler)
.addLast("groupMessageHandler", groupMessageHandler)
.addLast("connectHandler", connectMessageHandler)
.addLast("joinGroupHandler", joinGroupHandler)
.addLast("privateMessageHandler", privateMessageHandler)
.addLast("inventGroupHandler", inventGroupHandler)
.addLast("pingPongHartHandler", pingPongHartHandler)
.addLast("realTimeTransferHandler", realTimeTransferHandler)
// .addLast(new IdleStateHandler(10,0,0))
.addLast("selfException", INSTANCE_EXCEPTION);
}
}

View File

@@ -0,0 +1,18 @@
package com.realtime.packets.server.handler;
import com.realtime.packets.SystemNoticePackets;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@ChannelHandler.Sharable
public class SystemNoticeMessageHandler extends SimpleChannelInboundHandler<SystemNoticePackets> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, SystemNoticePackets msg) throws Exception {
}
}

View File

@@ -0,0 +1,12 @@
package com.realtime.packets.strategy;
import com.alibaba.fastjson2.JSONObject;
import com.realtime.packets.basePackets.BasePackets;
public interface PackStrategy {
boolean getCurrent(Byte commandLine);
BasePackets getCurrentPacket(JSONObject jsonObject);
}

View File

@@ -0,0 +1,12 @@
package com.realtime.packets.strategy;
import com.alibaba.fastjson2.JSONObject;
import com.realtime.packets.basePackets.BasePackets;
public interface PacketService {
BasePackets getCurrent(JSONObject jsonObject);
}

View File

@@ -0,0 +1,24 @@
package com.realtime.packets.strategy.impl;
import com.alibaba.fastjson2.JSONObject;
import com.realtime.packets.AutoSoftwarePacket;
import com.realtime.packets.ConnectPacket;
import com.realtime.packets.basePackets.BasePackets;
import com.realtime.packets.command.Command;
import com.realtime.packets.strategy.PackStrategy;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class AutoSoftwareStrategyImpl implements PackStrategy {
@Override
public boolean getCurrent(Byte commandLine) {
return Command.SOFT_CALL_MSG.equals(commandLine);
}
@Override
public BasePackets getCurrentPacket(JSONObject jsonObject) {
return JSONObject.parseObject(jsonObject.toString(), AutoSoftwarePacket.class);
}
}

View File

@@ -0,0 +1,26 @@
package com.realtime.packets.strategy.impl;
import com.alibaba.fastjson2.JSONObject;
import com.realtime.packets.ConnectPacket;
import com.realtime.packets.SendMsgPackets;
import com.realtime.packets.basePackets.BasePackets;
import com.realtime.packets.command.Command;
import com.realtime.packets.strategy.PackStrategy;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class ConnectPackStrategyImpl implements PackStrategy {
@Override
public boolean getCurrent(Byte commandLine) {
return Command.REGISTER.equals(commandLine);
}
@Override
public BasePackets getCurrentPacket(JSONObject jsonObject) {
return JSONObject.parseObject(jsonObject.toString(), ConnectPacket.class);
}
}

View File

@@ -0,0 +1,24 @@
package com.realtime.packets.strategy.impl;
import com.alibaba.fastjson2.JSONObject;
import com.realtime.packets.GroupPacket;
import com.realtime.packets.basePackets.BasePackets;
import com.realtime.packets.command.Command;
import com.realtime.packets.strategy.PackStrategy;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class GroupJoinPackStrategyImpl implements PackStrategy {
@Override
public boolean getCurrent(Byte commandLine) {
return Command.CREATE_GROUP.equals(commandLine);
}
@Override
public BasePackets getCurrentPacket(JSONObject jsonObject) {
return JSONObject.parseObject(jsonObject.toString(), GroupPacket.class);
}
}

View File

@@ -0,0 +1,26 @@
package com.realtime.packets.strategy.impl;
import com.alibaba.fastjson2.JSONObject;
import com.realtime.packets.GroupSendPacket;
import com.realtime.packets.basePackets.BasePackets;
import com.realtime.packets.command.Command;
import com.realtime.packets.strategy.PackStrategy;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class GroupSendPackStrategyImpl implements PackStrategy {
@Override
public boolean getCurrent(Byte commandLine) {
return Command.GROUP_MESSAGE.equals(commandLine);
}
@Override
public BasePackets getCurrentPacket(JSONObject jsonObject) {
return JSONObject.parseObject(jsonObject.toString(), GroupSendPacket.class);
}
}

View File

@@ -0,0 +1,27 @@
package com.realtime.packets.strategy.impl;
import com.alibaba.fastjson2.JSONObject;
import com.realtime.packets.InventGroupPacket;
import com.realtime.packets.basePackets.BasePackets;
import com.realtime.packets.command.Command;
import com.realtime.packets.strategy.PackStrategy;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class InventStrategyImpl implements PackStrategy {
@Override
public boolean getCurrent(Byte commandLine) {
return Command.INVENT_GROUP.equals(commandLine);
}
@Override
public BasePackets getCurrentPacket(JSONObject jsonObject) {
return JSONObject.parseObject(jsonObject.toString(), InventGroupPacket.class);
}
}

View File

@@ -0,0 +1,35 @@
package com.realtime.packets.strategy.impl;
import com.alibaba.fastjson2.JSONObject;
import com.realtime.exception.BusinessException;
import com.realtime.packets.basePackets.BasePackets;
import com.realtime.packets.strategy.PackStrategy;
import com.realtime.packets.strategy.PacketService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
@Slf4j
@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class PacketServiceImpl implements PacketService {
private final List<PackStrategy> packStrategies;
@Override
public BasePackets getCurrent(JSONObject jsonObject) {
Byte command = jsonObject.getByte("command");
return getCurrent(command).getCurrentPacket(jsonObject);
}
private PackStrategy getCurrent(Byte commandLine) {
long size = packStrategies.stream().filter(item -> item.getCurrent(commandLine)).count();
if (size <= 0) {
throw new BusinessException("没有可用的频道!!");
}
return packStrategies.stream().filter(item -> item.getCurrent(commandLine)).toList().get(0);
}
}

View File

@@ -0,0 +1,24 @@
package com.realtime.packets.strategy.impl;
import com.alibaba.fastjson2.JSONObject;
import com.realtime.packets.PingPongPacket;
import com.realtime.packets.basePackets.BasePackets;
import com.realtime.packets.command.Command;
import com.realtime.packets.strategy.PackStrategy;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class PingPongPackStrategyImpl implements PackStrategy {
@Override
public boolean getCurrent(Byte commandLine) {
return Command.PING_PONG.equals(commandLine);
}
@Override
public BasePackets getCurrentPacket(JSONObject jsonObject) {
return JSONObject.parseObject(jsonObject.toString(), PingPongPacket.class);
}
}

View File

@@ -0,0 +1,26 @@
package com.realtime.packets.strategy.impl;
import com.alibaba.fastjson2.JSONObject;
import com.realtime.packets.SendMsgPackets;
import com.realtime.packets.basePackets.BasePackets;
import com.realtime.packets.command.Command;
import com.realtime.packets.strategy.PackStrategy;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class PrivatePackStrategyImpl implements PackStrategy {
@Override
public boolean getCurrent(Byte commandLine) {
return Command.SINGLE_MESSAGE.equals(commandLine);
}
@Override
public BasePackets getCurrentPacket(JSONObject jsonObject) {
return JSONObject.parseObject(jsonObject.toString(), SendMsgPackets.class);
}
}

View File

@@ -0,0 +1,24 @@
package com.realtime.packets.strategy.impl;
import com.alibaba.fastjson2.JSONObject;
import com.realtime.packets.RealTimePacket;
import com.realtime.packets.basePackets.BasePackets;
import com.realtime.packets.command.Command;
import com.realtime.packets.strategy.PackStrategy;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class RealTimePackStrategyImpl implements PackStrategy {
@Override
public boolean getCurrent(Byte commandLine) {
return Command.REAL_TIME_VOICE.equals(commandLine);
}
@Override
public BasePackets getCurrentPacket(JSONObject jsonObject) {
return JSONObject.parseObject(jsonObject.toString(), RealTimePacket.class);
}
}

View File

@@ -0,0 +1,27 @@
package com.realtime.packets.strategy.impl;
import com.alibaba.fastjson2.JSONObject;
import com.realtime.packets.RemoveGroupPacket;
import com.realtime.packets.basePackets.BasePackets;
import com.realtime.packets.command.Command;
import com.realtime.packets.strategy.PackStrategy;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class RemoveStrategyImpl implements PackStrategy {
@Override
public boolean getCurrent(Byte commandLine) {
return Command.REMOVE_GROUP.equals(commandLine);
}
@Override
public BasePackets getCurrentPacket(JSONObject jsonObject) {
return JSONObject.parseObject(jsonObject.toString(), RemoveGroupPacket.class);
}
}

View File

@@ -0,0 +1,46 @@
package com.realtime.service;
import com.alibaba.fastjson2.JSONObject;
import com.realtime.config.BaiduTtsConfig;
import lombok.RequiredArgsConstructor;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class BaiduAuthService {
private final BaiduTtsConfig ttsConfig;
/**
* 获取百度语音合成访问令牌
*/
public String getAccessToken() {
String authHost = "https://aip.baidubce.com/oauth/2.0/token?";
String getAccessTokenUrl = authHost
+ "grant_type=client_credentials"
+ "&client_id=" + ttsConfig.getApiKey()
+ "&client_secret=" + ttsConfig.getSecretKey();
try {
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpGet get = new HttpGet(getAccessTokenUrl);
HttpResponse response = httpClient.execute(get);
String result = EntityUtils.toString(response.getEntity());
JSONObject jsonObject = JSONObject.parseObject(result);
return jsonObject.getString("access_token");
} catch (Exception e) {
throw new RuntimeException("获取百度语音合成Token失败", e);
}
}
}

View File

@@ -0,0 +1,94 @@
package com.realtime.service;
import com.google.common.io.Files;
import com.realtime.model.query.TtsOptionsReq;
import lombok.RequiredArgsConstructor;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.io.File;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
@Service
@RequiredArgsConstructor(onConstructor_ = {@Autowired})
public class BaiduTtsService {
private static final String TTS_URL = "http://tsn.baidu.com/text2audio";
private final BaiduAuthService authService;
/**
* 文本转语音
* @param text 要转换的文本
* @param options 合成参数
* @return 音频字节数组
*/
public byte[] textToSpeech(String text, TtsOptionsReq options) {
try {
String accessToken = authService.getAccessToken();
Map<String, String> params = new HashMap<>();
params.put("tex", URLEncoder.encode(text, StandardCharsets.UTF_8));
params.put("tok", accessToken);
params.put("cuid", UUID.randomUUID().toString().replaceAll("-", ""));
params.put("ctp", "1");
params.put("lan", options.getLang());
params.put("spd", String.valueOf(options.getSpeed()));
params.put("pit", String.valueOf(options.getPitch()));
params.put("vol", String.valueOf(options.getVolume()));
params.put("per", String.valueOf(options.getPer()));
params.put("aue", options.getAue());
return postForm(params);
} catch (Exception e) {
throw new RuntimeException("语音合成失败", e);
}
}
/**
* 发送POST请求
*/
private byte[] postForm(Map<String, String> params) throws Exception {
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpPost post = new HttpPost(BaiduTtsService.TTS_URL);
// 设置表单参数
List<NameValuePair> paramList = new ArrayList<>();
for (Map.Entry<String, String> param : params.entrySet()) {
paramList.add(new BasicNameValuePair(param.getKey(), param.getValue()));
}
post.setEntity(new UrlEncodedFormEntity(paramList, "UTF-8"));
// 执行请求
HttpResponse response = httpClient.execute(post);
HttpEntity entity = response.getEntity();
if (entity != null) {
return EntityUtils.toByteArray(entity);
}
return null;
}
/**
* 文本转语音并保存为文件
*/
public void textToSpeechFile(String text, TtsOptionsReq options, String filePath) throws IOException {
byte[] audioData = textToSpeech(text, options);
if (audioData != null) {
Files.write(audioData,new File(filePath));
}
}
}

View File

@@ -0,0 +1,28 @@
package com.realtime.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
import com.realtime.model.pojo.ChatList;
import com.realtime.model.query.ChatListPageQueryReq;
import com.realtime.sysconst.Result;
import com.realtime.vo.ChatListInfoVo;
import java.util.List;
public interface ChatListService extends IService<ChatList> {
void saveChatList(ChatList chatList);
String getMachineIdBySenderId(String senderId);
Result<IPage<ChatListInfoVo>> getChatList(ChatListPageQueryReq chatListPageQueryReq);
Result<String> saveSignChatList(List<ChatList> chatList);
Result<String> updateChatListFName(List<ChatList> chatList);
Result<String> deleteSignChatList(ChatList ids);
}

View File

@@ -0,0 +1,105 @@
package com.realtime.service;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.realtime.config.DoubaoVoiceConfig;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import okio.ByteString;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Service;
import org.springframework.web.socket.BinaryMessage;
import org.springframework.web.socket.WebSocketSession;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@Slf4j
@Service
public class DoubaoVoiceService {
private final DoubaoVoiceConfig config;
private final OkHttpClient okHttpClient;
private WebSocket doubaoWebSocket; // 与豆包 API 的 WebSocket 连接
public DoubaoVoiceService(DoubaoVoiceConfig config) {
this.config = config;
this.okHttpClient = new OkHttpClient.Builder()
.readTimeout(0, TimeUnit.MILLISECONDS) // 实时流禁用超时
.build();
}
/**
* 建立与豆包实时语音接口的 WebSocket 连接
* @param clientSession 客户端的 WebSocket 会话(用于将结果返回给客户端)
*/
public void connect(WebSocketSession clientSession) {
Request request = new Request.Builder()
.addHeader("X-Api-App-ID",config.getAppId())
.addHeader("X-Api-Access-Key",config.getAccessToken())
.addHeader("X-Api-Resource-Id","volc.speech.dialog").addHeader("X-Api-App-Key",config.getApiKey())
.url("wss://openspeech.bytedance.com/api/v3/realtime/dialogue")
.build();
// 连接豆包 API 并处理回调
doubaoWebSocket = okHttpClient.newWebSocket(request, new WebSocketListener() {
@Override
public void onOpen(@NotNull WebSocket webSocket, @NotNull Response response) {
// 连接成功:可发送初始化参数(如语音类型、采样率)
sendInitParams(webSocket);
}
@Override
public void onMessage(@NotNull WebSocket webSocket, ByteString bytes) {
// 接收豆包返回的实时结果(如识别文本、合成语音)
try {
// 将结果转发给客户端
clientSession.sendMessage(new BinaryMessage(bytes.toByteArray()));
} catch (IOException e) {
log.error("转发结果到客户端失败", e);
}
}
@Override
public void onFailure(WebSocket webSocket, Throwable t, Response response) {
System.out.println(t.getMessage());
log.error("豆包 WebSocket 连接失败", t);
}
});
}
/**
* 向豆包 API 发送音频数据(实时流)
*/
public void sendAudio(byte[] audioData) {
if (doubaoWebSocket != null) {
doubaoWebSocket.send(ByteString.of(audioData));
}
}
/**
* 发送初始化参数(根据豆包 API 要求)
*/
private void sendInitParams(WebSocket webSocket) {
Map<String, Object> params = new HashMap<>();
params.put("action", "init");
params.put("format", "pcm"); // 音频格式
params.put("sample_rate", 16000); // 采样率
params.put("mode", "realtime"); // 实时模式
try {
webSocket.send(new ObjectMapper().writeValueAsString(params));
} catch (JsonProcessingException e) {
log.error("发送初始化参数失败", e);
}
}
/**
* 关闭连接
*/
public void close() {
if (doubaoWebSocket != null) {
doubaoWebSocket.close(1000, "正常关闭");
}
}
}

View File

@@ -0,0 +1,18 @@
package com.realtime.service;
import com.realtime.sysconst.Result;
import com.realtime.vo.UploadVo;
import io.minio.errors.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
public interface FileService {
Result<UploadVo> uploadFile(MultipartFile file) throws IOException, ServerException, InsufficientDataException, ErrorResponseException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException;
}

View File

@@ -0,0 +1,22 @@
package com.realtime.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
import com.realtime.model.pojo.FriendRelationship;
import com.realtime.model.query.ChatFriendRelationshipQueryReq;
import com.realtime.sysconst.Result;
import com.realtime.vo.ChatFriendRelationshipInfoVo;
import java.util.List;
public interface FriendRelationshipService extends IService<FriendRelationship> {
Result<IPage<ChatFriendRelationshipInfoVo>> selectByPhone(ChatFriendRelationshipQueryReq req);
Result<String> saveShip(FriendRelationship friendRelationship);
void updateStateById(Long friendRelationshipId);
Result<String> removeIds(List<Long> ids);
}

Some files were not shown because too many files have changed in this diff Show More