committed by
Gitee
354 changed files with 4803 additions and 4382 deletions
@ -1,31 +0,0 @@ |
|||
package com.bx.imclient.config; |
|||
|
|||
import com.alibaba.fastjson.support.spring.FastJsonRedisSerializer; |
|||
import org.springframework.context.annotation.Bean; |
|||
import org.springframework.context.annotation.Configuration; |
|||
import org.springframework.data.redis.connection.RedisConnectionFactory; |
|||
import org.springframework.data.redis.core.RedisTemplate; |
|||
import org.springframework.data.redis.serializer.StringRedisSerializer; |
|||
|
|||
@Configuration("IMRedisConfig") |
|||
public class RedisConfig { |
|||
|
|||
@Bean("IMRedisTemplate") |
|||
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) { |
|||
RedisTemplate<String, Object> redisTemplate = new RedisTemplate(); |
|||
redisTemplate.setConnectionFactory(redisConnectionFactory); |
|||
// 设置值(value)的序列化采用FastJsonRedisSerializer
|
|||
redisTemplate.setValueSerializer(fastJsonRedisSerializer()); |
|||
redisTemplate.setHashValueSerializer(fastJsonRedisSerializer()); |
|||
// 设置键(key)的序列化采用StringRedisSerializer。
|
|||
redisTemplate.setKeySerializer(new StringRedisSerializer()); |
|||
redisTemplate.setHashKeySerializer(new StringRedisSerializer()); |
|||
redisTemplate.afterPropertiesSet(); |
|||
return redisTemplate; |
|||
} |
|||
|
|||
public FastJsonRedisSerializer fastJsonRedisSerializer(){ |
|||
return new FastJsonRedisSerializer<>(Object.class); |
|||
} |
|||
|
|||
} |
|||
@ -1,44 +1,21 @@ |
|||
package com.bx.imclient.task; |
|||
|
|||
import com.bx.imcommon.util.ThreadPoolExecutorFactory; |
|||
import lombok.SneakyThrows; |
|||
import cn.hutool.core.util.StrUtil; |
|||
import com.bx.imcommon.mq.RedisMQConsumer; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.boot.CommandLineRunner; |
|||
|
|||
import javax.annotation.PreDestroy; |
|||
import java.util.concurrent.ExecutorService; |
|||
import org.springframework.beans.factory.annotation.Value; |
|||
|
|||
@Slf4j |
|||
public abstract class AbstractMessageResultTask implements CommandLineRunner { |
|||
public abstract class AbstractMessageResultTask<T> extends RedisMQConsumer<T> { |
|||
|
|||
private static final ExecutorService EXECUTOR_SERVICE = ThreadPoolExecutorFactory.getThreadPoolExecutor(); |
|||
@Value("${spring.application.name}") |
|||
private String appName; |
|||
|
|||
@Override |
|||
public void run(String... args) { |
|||
// 初始化定时器
|
|||
EXECUTOR_SERVICE.execute(new Runnable() { |
|||
@SneakyThrows |
|||
@Override |
|||
public void run() { |
|||
try { |
|||
pullMessage(); |
|||
} catch (Exception e) { |
|||
log.error("任务调度异常", e); |
|||
} |
|||
if (!EXECUTOR_SERVICE.isShutdown()) { |
|||
Thread.sleep(100); |
|||
EXECUTOR_SERVICE.execute(this); |
|||
} |
|||
} |
|||
}); |
|||
public String generateKey() { |
|||
return StrUtil.join(":", super.generateKey(), appName); |
|||
} |
|||
|
|||
|
|||
@PreDestroy |
|||
public void destroy() { |
|||
log.info("{}线程任务关闭", this.getClass().getSimpleName()); |
|||
EXECUTOR_SERVICE.shutdown(); |
|||
} |
|||
|
|||
public abstract void pullMessage() throws InterruptedException; |
|||
} |
|||
|
|||
@ -1,59 +1,27 @@ |
|||
package com.bx.imclient.task; |
|||
|
|||
import cn.hutool.core.util.StrUtil; |
|||
import com.alibaba.fastjson.JSONObject; |
|||
import com.bx.imclient.listener.MessageListenerMulticaster; |
|||
import com.bx.imcommon.contant.IMRedisKey; |
|||
import com.bx.imcommon.enums.IMListenerType; |
|||
import com.bx.imcommon.model.IMSendResult; |
|||
import com.bx.imcommon.mq.RedisMQListener; |
|||
import lombok.RequiredArgsConstructor; |
|||
import org.springframework.beans.factory.annotation.Value; |
|||
import org.springframework.data.redis.core.RedisTemplate; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
import javax.annotation.Resource; |
|||
import java.util.LinkedList; |
|||
import java.util.List; |
|||
import java.util.Objects; |
|||
|
|||
|
|||
@Component |
|||
@RequiredArgsConstructor |
|||
public class GroupMessageResultResultTask extends AbstractMessageResultTask { |
|||
|
|||
@Resource(name = "IMRedisTemplate") |
|||
private RedisTemplate<String,Object> redisTemplate; |
|||
|
|||
@Value("${spring.application.name}") |
|||
private String appName; |
|||
|
|||
@Value("${im.result.batch:100}") |
|||
private int batchSize; |
|||
@RedisMQListener(queue = IMRedisKey.IM_RESULT_GROUP_QUEUE, batchSize = 100) |
|||
public class GroupMessageResultResultTask extends AbstractMessageResultTask<IMSendResult> { |
|||
|
|||
private final MessageListenerMulticaster listenerMulticaster; |
|||
|
|||
@Override |
|||
public void pullMessage() { |
|||
List<IMSendResult> results; |
|||
do { |
|||
results = loadBatch(); |
|||
if(!results.isEmpty()){ |
|||
listenerMulticaster.multicast(IMListenerType.GROUP_MESSAGE, results); |
|||
} |
|||
} while (results.size() >= batchSize); |
|||
public void onMessage(List<IMSendResult> results) { |
|||
listenerMulticaster.multicast(IMListenerType.GROUP_MESSAGE, results); |
|||
} |
|||
|
|||
List<IMSendResult> loadBatch() { |
|||
String key = StrUtil.join(":", IMRedisKey.IM_RESULT_GROUP_QUEUE, appName); |
|||
//这个接口redis6.2以上才支持
|
|||
//List<Object> list = redisTemplate.opsForList().leftPop(key, batchSize);
|
|||
List<IMSendResult> results = new LinkedList<>(); |
|||
JSONObject jsonObject = (JSONObject) redisTemplate.opsForList().leftPop(key); |
|||
while (!Objects.isNull(jsonObject) && results.size() < batchSize) { |
|||
results.add(jsonObject.toJavaObject(IMSendResult.class)); |
|||
jsonObject = (JSONObject) redisTemplate.opsForList().leftPop(key); |
|||
} |
|||
return results; |
|||
} |
|||
|
|||
} |
|||
|
|||
@ -1,60 +1,26 @@ |
|||
package com.bx.imclient.task; |
|||
|
|||
import cn.hutool.core.util.StrUtil; |
|||
import com.alibaba.fastjson.JSONObject; |
|||
import com.bx.imclient.listener.MessageListenerMulticaster; |
|||
import com.bx.imcommon.contant.IMRedisKey; |
|||
import com.bx.imcommon.enums.IMListenerType; |
|||
import com.bx.imcommon.model.IMSendResult; |
|||
import com.bx.imcommon.mq.RedisMQListener; |
|||
import lombok.RequiredArgsConstructor; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.beans.factory.annotation.Value; |
|||
import org.springframework.data.redis.core.RedisTemplate; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
import javax.annotation.Resource; |
|||
import java.util.LinkedList; |
|||
import java.util.List; |
|||
import java.util.Objects; |
|||
|
|||
@Slf4j |
|||
|
|||
@Component |
|||
@RequiredArgsConstructor |
|||
public class PrivateMessageResultResultTask extends AbstractMessageResultTask { |
|||
|
|||
@Resource(name = "IMRedisTemplate") |
|||
private RedisTemplate<String, Object> redisTemplate; |
|||
|
|||
@Value("${spring.application.name}") |
|||
private String appName; |
|||
|
|||
@Value("${im.result.batch:100}") |
|||
private int batchSize; |
|||
@RedisMQListener(queue = IMRedisKey.IM_RESULT_PRIVATE_QUEUE, batchSize = 100) |
|||
public class PrivateMessageResultResultTask extends AbstractMessageResultTask<IMSendResult> { |
|||
|
|||
private final MessageListenerMulticaster listenerMulticaster; |
|||
|
|||
@Override |
|||
public void pullMessage() { |
|||
List<IMSendResult> results; |
|||
do { |
|||
results = loadBatch(); |
|||
if(!results.isEmpty()){ |
|||
listenerMulticaster.multicast(IMListenerType.PRIVATE_MESSAGE, results); |
|||
} |
|||
} while (results.size() >= batchSize); |
|||
} |
|||
|
|||
List<IMSendResult> loadBatch() { |
|||
String key = StrUtil.join(":", IMRedisKey.IM_RESULT_PRIVATE_QUEUE, appName); |
|||
//这个接口redis6.2以上才支持
|
|||
//List<Object> list = redisTemplate.opsForList().leftPop(key, batchSize);
|
|||
List<IMSendResult> results = new LinkedList<>(); |
|||
JSONObject jsonObject = (JSONObject) redisTemplate.opsForList().leftPop(key); |
|||
while (!Objects.isNull(jsonObject) && results.size() < batchSize) { |
|||
results.add(jsonObject.toJavaObject(IMSendResult.class)); |
|||
jsonObject = (JSONObject) redisTemplate.opsForList().leftPop(key); |
|||
} |
|||
return results; |
|||
public void onMessage(List<IMSendResult> results) { |
|||
listenerMulticaster.multicast(IMListenerType.PRIVATE_MESSAGE, results); |
|||
} |
|||
|
|||
} |
|||
|
|||
@ -0,0 +1,25 @@ |
|||
package com.bx.imclient.task; |
|||
|
|||
import com.bx.imclient.listener.MessageListenerMulticaster; |
|||
import com.bx.imcommon.contant.IMRedisKey; |
|||
import com.bx.imcommon.enums.IMListenerType; |
|||
import com.bx.imcommon.model.IMSendResult; |
|||
import com.bx.imcommon.mq.RedisMQListener; |
|||
import lombok.RequiredArgsConstructor; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
import java.util.List; |
|||
|
|||
@Component |
|||
@RequiredArgsConstructor |
|||
@RedisMQListener(queue = IMRedisKey.IM_RESULT_SYSTEM_QUEUE, batchSize = 100) |
|||
public class SystemMessageResultResultTask extends AbstractMessageResultTask<IMSendResult> { |
|||
|
|||
private final MessageListenerMulticaster listenerMulticaster; |
|||
|
|||
@Override |
|||
public void onMessage(List<IMSendResult> results) { |
|||
listenerMulticaster.multicast(IMListenerType.SYSTEM_MESSAGE, results); |
|||
} |
|||
|
|||
} |
|||
@ -1,2 +0,0 @@ |
|||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ |
|||
com.bx.imclient.IMAutoConfiguration |
|||
@ -0,0 +1 @@ |
|||
com.bx.imclient.IMAutoConfiguration |
|||
@ -0,0 +1,34 @@ |
|||
package com.bx.imcommon.model; |
|||
|
|||
import com.bx.imcommon.enums.IMTerminalType; |
|||
import lombok.Data; |
|||
|
|||
import java.util.LinkedList; |
|||
import java.util.List; |
|||
|
|||
@Data |
|||
public class IMSystemMessage<T> { |
|||
|
|||
|
|||
/** |
|||
* 接收者id列表,为空表示向所有在线用户广播 |
|||
*/ |
|||
private List<Long> recvIds = new LinkedList<>(); |
|||
|
|||
/** |
|||
* 接收者终端类型,默认全部 |
|||
*/ |
|||
private List<Integer> recvTerminals = IMTerminalType.codes(); |
|||
|
|||
/** |
|||
* 是否需要回推发送结果,默认true |
|||
*/ |
|||
private Boolean sendResult = true; |
|||
|
|||
/** |
|||
* 消息内容 |
|||
*/ |
|||
private T data; |
|||
|
|||
|
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
package com.bx.imcommon.mq; |
|||
|
|||
import com.alibaba.fastjson.support.spring.FastJsonRedisSerializer; |
|||
import org.springframework.context.annotation.Bean; |
|||
import org.springframework.context.annotation.Configuration; |
|||
import org.springframework.data.redis.connection.RedisConnectionFactory; |
|||
import org.springframework.data.redis.serializer.StringRedisSerializer; |
|||
|
|||
|
|||
@Configuration |
|||
public class RedisMQConfig { |
|||
|
|||
@Bean |
|||
public RedisMQTemplate redisMQTemplate(RedisConnectionFactory redisConnectionFactory) { |
|||
RedisMQTemplate redisMQTemplate = new RedisMQTemplate(); |
|||
redisMQTemplate.setConnectionFactory(redisConnectionFactory); |
|||
// 设置值(value)的序列化采用FastJsonRedisSerializer
|
|||
redisMQTemplate.setValueSerializer(fastJsonRedisSerializer()); |
|||
redisMQTemplate.setHashValueSerializer(fastJsonRedisSerializer()); |
|||
// 设置键(key)的序列化采用StringRedisSerializer。
|
|||
redisMQTemplate.setKeySerializer(new StringRedisSerializer()); |
|||
redisMQTemplate.setHashKeySerializer(new StringRedisSerializer()); |
|||
redisMQTemplate.afterPropertiesSet(); |
|||
return redisMQTemplate; |
|||
} |
|||
|
|||
@Bean |
|||
public FastJsonRedisSerializer fastJsonRedisSerializer(){ |
|||
return new FastJsonRedisSerializer<>(Object.class); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,35 @@ |
|||
package com.bx.imcommon.mq; |
|||
|
|||
import java.util.List; |
|||
|
|||
/** |
|||
* redis 队列消费者抽象类 |
|||
*/ |
|||
public abstract class RedisMQConsumer<T> { |
|||
|
|||
/** |
|||
* 消费消息回调(单条) |
|||
*/ |
|||
public void onMessage(T data){} |
|||
|
|||
/** |
|||
* 消费消息回调(批量) |
|||
*/ |
|||
public void onMessage(List<T> datas){} |
|||
|
|||
/** |
|||
* 生成redis队列完整key |
|||
*/ |
|||
public String generateKey(){ |
|||
// 默认队列名就是redis的key
|
|||
RedisMQListener annotation = this.getClass().getAnnotation(RedisMQListener.class); |
|||
return annotation.queue(); |
|||
} |
|||
|
|||
/** |
|||
* 队列是否就绪,返回true才会开始消费 |
|||
*/ |
|||
public Boolean isReady(){ |
|||
return true; |
|||
} |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
package com.bx.imcommon.mq; |
|||
|
|||
import java.lang.annotation.ElementType; |
|||
import java.lang.annotation.Retention; |
|||
import java.lang.annotation.RetentionPolicy; |
|||
import java.lang.annotation.Target; |
|||
|
|||
/** |
|||
* redis 队列消费监听注解 |
|||
*/ |
|||
@Retention(RetentionPolicy.RUNTIME) |
|||
@Target(ElementType.TYPE) |
|||
public @interface RedisMQListener { |
|||
|
|||
/** |
|||
* 队列,也是redis的key |
|||
*/ |
|||
String queue(); |
|||
|
|||
/** |
|||
* 一次性拉取的数据数量 |
|||
*/ |
|||
int batchSize() default 1; |
|||
|
|||
/** |
|||
* 拉取间隔周期,单位:ms |
|||
*/ |
|||
int period() default 100; |
|||
} |
|||
@ -0,0 +1,114 @@ |
|||
package com.bx.imcommon.mq; |
|||
|
|||
import com.alibaba.fastjson.JSONObject; |
|||
import com.bx.imcommon.util.ThreadPoolExecutorFactory; |
|||
import jakarta.annotation.PreDestroy; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.boot.CommandLineRunner; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
import java.lang.reflect.ParameterizedType; |
|||
import java.lang.reflect.Type; |
|||
import java.util.Collections; |
|||
import java.util.LinkedList; |
|||
import java.util.List; |
|||
import java.util.Objects; |
|||
import java.util.concurrent.ScheduledThreadPoolExecutor; |
|||
import java.util.concurrent.TimeUnit; |
|||
|
|||
/** |
|||
* reids 队列拉取定时任务 |
|||
* |
|||
* @author: Blue |
|||
* @date: 2024-07-15 |
|||
* @version: 1.0 |
|||
*/ |
|||
@Slf4j |
|||
@Component |
|||
public class RedisMQPullTask implements CommandLineRunner { |
|||
|
|||
private static final ScheduledThreadPoolExecutor EXECUTOR = ThreadPoolExecutorFactory.getThreadPoolExecutor(); |
|||
|
|||
@Autowired(required = false) |
|||
private List<RedisMQConsumer> consumers = Collections.emptyList(); |
|||
|
|||
@Autowired |
|||
private RedisMQTemplate redisMQTemplate; |
|||
|
|||
@Override |
|||
public void run(String... args) { |
|||
consumers.forEach((consumer -> { |
|||
// 注解参数
|
|||
RedisMQListener annotation = consumer.getClass().getAnnotation(RedisMQListener.class); |
|||
String queue = annotation.queue(); |
|||
int batchSize = annotation.batchSize(); |
|||
int period = annotation.period(); |
|||
// 获取泛型类型
|
|||
Type superClass = consumer.getClass().getGenericSuperclass(); |
|||
Type type = ((ParameterizedType)superClass).getActualTypeArguments()[0]; |
|||
EXECUTOR.execute(new Runnable() { |
|||
@Override |
|||
public void run() { |
|||
List<Object> datas = new LinkedList<>(); |
|||
try { |
|||
if(redisMQTemplate.isClose()){ |
|||
return; |
|||
} |
|||
if (consumer.isReady()) { |
|||
String key = consumer.generateKey(); |
|||
// 拉取一个批次的数据
|
|||
List<Object> objects = pullBatch(key, batchSize); |
|||
for (Object obj : objects) { |
|||
if (obj instanceof JSONObject) { |
|||
JSONObject jsonObject = (JSONObject)obj; |
|||
Object data = jsonObject.toJavaObject(type); |
|||
consumer.onMessage(data); |
|||
datas.add(data); |
|||
} |
|||
} |
|||
if (!datas.isEmpty()) { |
|||
consumer.onMessage(datas); |
|||
} |
|||
} |
|||
} catch (Exception e) { |
|||
log.error("数据消费异常,队列:{}", queue, e); |
|||
return; |
|||
} |
|||
// 继续消费数据
|
|||
if (!EXECUTOR.isShutdown()) { |
|||
if (datas.size() < batchSize) { |
|||
// 数据已经消费完,等待下一个周期继续拉取
|
|||
EXECUTOR.schedule(this, period, TimeUnit.MICROSECONDS); |
|||
} else { |
|||
// 数据没有消费完,直接开启下一个消费周期
|
|||
EXECUTOR.execute(this); |
|||
} |
|||
} |
|||
} |
|||
}); |
|||
})); |
|||
} |
|||
|
|||
private List<Object> pullBatch(String key, Integer batchSize) { |
|||
List<Object> objects = new LinkedList<>(); |
|||
if (redisMQTemplate.isSupportBatchPull()) { |
|||
// 版本大于6.2,支持批量拉取
|
|||
objects = redisMQTemplate.opsForList().leftPop(key, batchSize); |
|||
} else { |
|||
// 版本小于6.2,只能逐条拉取
|
|||
Object obj = redisMQTemplate.opsForList().leftPop(key); |
|||
while (!Objects.isNull(obj) && objects.size() < batchSize) { |
|||
objects.add(obj); |
|||
obj = redisMQTemplate.opsForList().leftPop(key); |
|||
} |
|||
} |
|||
return objects; |
|||
} |
|||
|
|||
@PreDestroy |
|||
public void destory() { |
|||
log.info("消费线程停止..."); |
|||
ThreadPoolExecutorFactory.shutDown(); |
|||
} |
|||
} |
|||
@ -0,0 +1,52 @@ |
|||
package com.bx.imcommon.mq; |
|||
|
|||
import org.apache.logging.log4j.util.Strings; |
|||
import org.springframework.data.redis.connection.RedisConnection; |
|||
import org.springframework.data.redis.connection.RedisConnectionFactory; |
|||
import org.springframework.data.redis.core.RedisTemplate; |
|||
|
|||
import java.util.Objects; |
|||
import java.util.Properties; |
|||
|
|||
/** |
|||
* @author: Blue |
|||
* @date: 2024-07-16 |
|||
* @version: 1.0 |
|||
*/ |
|||
public class RedisMQTemplate extends RedisTemplate<String, Object> { |
|||
|
|||
private String version = Strings.EMPTY; |
|||
|
|||
public String getVersion() { |
|||
if (version.isEmpty()) { |
|||
RedisConnection redisConnection = this.getConnectionFactory().getConnection(); |
|||
Properties properties = redisConnection.info(); |
|||
version = properties.getProperty("redis_version"); |
|||
} |
|||
return version; |
|||
} |
|||
|
|||
/** |
|||
* 是否支持批量拉取,redis版本大于6.2支持批量拉取 |
|||
* @return |
|||
*/ |
|||
Boolean isSupportBatchPull() { |
|||
String version = getVersion(); |
|||
String[] arr = version.split("\\."); |
|||
if (arr.length < 2) { |
|||
return false; |
|||
} |
|||
Integer firVersion = Integer.valueOf(arr[0]); |
|||
Integer secVersion = Integer.valueOf(arr[1]); |
|||
return firVersion > 6 || (firVersion == 6 && secVersion >= 2); |
|||
} |
|||
|
|||
|
|||
Boolean isClose(){ |
|||
try { |
|||
return getConnectionFactory().getConnection().isClosed(); |
|||
}catch (Exception e){ |
|||
return true; |
|||
} |
|||
} |
|||
} |
|||
@ -1,26 +0,0 @@ |
|||
# docker 构建和部署指南 |
|||
|
|||
## 配置 |
|||
|
|||
请配置以下文件的挂载目录、端口号、环境变量、账户信息等。 |
|||
|
|||
- box-im/im-docker-compose/im-coturn/docker-compose.yml |
|||
- box-im/im-docker-compose/im-service/docker-compose.yml |
|||
- box-im/im-docker-compose/im-coturn/im-platform.env |
|||
- box-im/im-docker-compose/im-coturn/im-server.env |
|||
|
|||
## 构建 |
|||
|
|||
可以在部署的目标服务器上在下面两个目录下构建出 im-server 和 im-platform 的 docker images. |
|||
|
|||
- box-im/im-server/Dockerfile |
|||
- box-im/im-platform/Dockerfile |
|||
|
|||
## 部署 |
|||
|
|||
在部署的目标服务器上通过 docker-compose 启动所有容器, |
|||
|
|||
```bash |
|||
docker-compose -f im-docker-compose/im-coturn/docker-compose.yml up -d |
|||
docker-compose -f im-docker-compose/im-service/docker-compose.yml up -d |
|||
``` |
|||
@ -1,27 +0,0 @@ |
|||
|
|||
version: "3" |
|||
|
|||
services: |
|||
im-coturn: |
|||
container_name: im-coturn |
|||
image: coturn/coturn:4.6.2-alpine |
|||
command: turnserver -v -a -c /etc/coturn/conf/coturn.conf |
|||
restart: always |
|||
ports: |
|||
- PORT_COTURN_LISTEN_OS:3478 |
|||
- PORT_COTURN_LISTEN_OS:3478/udp |
|||
- PORT_COTURN_LISTEN_TLS_OS:5349 |
|||
- PORT_COTURN_LISTEN_TLS_OS:5349/udp |
|||
- PORT_COTURN_RELAY_BEGIN_OS-PORT_COTURN_RELAY_END_OS:PORT_COTURN_RELAY_BEGIN_OS-PORT_COTURN_RELAY_END_OS/udp |
|||
# network_mode: "host" |
|||
environment: |
|||
- DETECT_EXTERNAL_IP=yes |
|||
- DETECT_RELAY_IP=yes |
|||
volumes: |
|||
- deploydir/docker_volumes_data/coturn/conf:/etc/coturn/conf |
|||
- deploydir/docker_volumes_data/coturn/certs:/etc/coturn/ssl |
|||
- deploydir/docker_volumes_data/coturn/log:/var/log/coturn/ |
|||
networks: |
|||
default: |
|||
external: |
|||
name: turn-network |
|||
@ -1,99 +0,0 @@ |
|||
|
|||
version: "3" |
|||
|
|||
services: |
|||
im-mysql: |
|||
container_name: im-mysql |
|||
image: mysql:5.7 |
|||
command: --default-authentication-plugin=mysql_native_password |
|||
restart: always |
|||
environment: |
|||
- TZ=Asia/Shanghai |
|||
- MYSQL_ROOT_PASSWORD=MYSQL_ROOT_USER_PASSWORD |
|||
- MYSQL_ROOT_HOST=% |
|||
- MYSQL_IM_USERNAME=MYSQL_USERNAME |
|||
- MYSQL_IM_PASSWORD=MYSQL_PASSWORD |
|||
volumes: |
|||
- deploydir/docker_volumes_data/mysql/data:/var/lib/mysql |
|||
- deploydir/docker_volumes_data/mysql/mysql.conf.d:/etc/mysql/mysql.conf.d |
|||
|
|||
im-redis: |
|||
container_name: im-redis |
|||
image: redis:6-alpine |
|||
command: redis-server --requirepass REDIS_PASSWORD --appendonly yes --appendfsync everysec --auto-aof-rewrite-percentage 100 --auto-aof-rewrite-min-size 100mb |
|||
restart: always |
|||
# ports: |
|||
# - "127.0.0.1:6379:6379" |
|||
volumes: |
|||
- deploydir/docker_volumes_data/redis/data:/data |
|||
|
|||
im-minio: |
|||
container_name: im-minio |
|||
image: bitnami/minio:2024 |
|||
restart: always |
|||
# ports: |
|||
# - 9001:9001 |
|||
# - 9002:9002 |
|||
privileged: true |
|||
environment: |
|||
- MINIO_SKIP_CLIENT=yes |
|||
- MINIO_API_PORT_NUMBER=9001 |
|||
- MINIO_CONSOLE_PORT_NUMBER=9002 |
|||
# - MINIO_OPTS="--console-address :9002 --address :9001" |
|||
# - MINIO_DOMAIN=im_minio:9001 |
|||
- MINIO_DATA_DIR=/data/minio |
|||
- MINIO_ROOT_USER=MINIO_USERNAME |
|||
- MINIO_ROOT_PASSWORD=MINIO_PASSWORD |
|||
volumes: |
|||
- deploydir/docker_volumes_data/minio/data:/data/minio |
|||
|
|||
im-nginx: |
|||
container_name: im-nginx |
|||
image: openresty/openresty:1.21.4.1-0-alpine |
|||
restart: always |
|||
ports: |
|||
- PORT_NGINX_HTTP_OS:80 |
|||
- PORT_NGINX_HTTPS_OS:443 |
|||
- PORT_NGINX_WSS_OS:81 |
|||
depends_on: |
|||
im-platform: |
|||
condition: service_started |
|||
im-server: |
|||
condition: service_started |
|||
volumes: |
|||
- deploydir/docker_volumes_data/nginx/data/conf:/etc/nginx ## configs |
|||
- deploydir/docker_volumes_data/nginx/data/certs:/etc/certs ## cert files |
|||
- deploydir/docker_volumes_data/nginx/data/web:/usr/share/nginx ## web |
|||
|
|||
im-platform: |
|||
container_name: im-platform |
|||
image: im-platform:latest |
|||
restart: always |
|||
depends_on: |
|||
im-mysql: |
|||
condition: service_started |
|||
im-redis: |
|||
condition: service_started |
|||
im-minio: |
|||
condition: service_started |
|||
env_file: |
|||
- im-platform.env |
|||
|
|||
im-server: |
|||
container_name: im-server |
|||
image: im-server:latest |
|||
restart: always |
|||
depends_on: |
|||
im-mysql: |
|||
condition: service_started |
|||
im-redis: |
|||
condition: service_started |
|||
im-minio: |
|||
condition: service_started |
|||
env_file: |
|||
- im-server.env |
|||
networks: |
|||
default: |
|||
external: |
|||
name: im-network |
|||
|
|||
@ -1,28 +0,0 @@ |
|||
spring_datasource_username=MYSQL_USERNAME |
|||
spring_datasource_password=MYSQL_PASSWORD |
|||
spring_redis_password=REDIS_PASSWORD |
|||
|
|||
spring_datasource_url=jdbc:mysql://im-mysql:3306/box-im?useSSL=false&useUnicode=true&characterEncoding=utf-8 |
|||
spring_datasource_password=MYSQL_PASSWORD |
|||
spring_redis_host=im-redis |
|||
spring_redis_port=6379 |
|||
spring_redis_password=REDIS_PASSWORD |
|||
|
|||
minio_accessKey=MINIO_USERNAME |
|||
minio_secretKey=MINIO_PASSWORD |
|||
minio_public=https://IM_DOMAIN:PORT_NGINX_HTTPS_EXTERNAL/file |
|||
minio_endpoint=http://im-minio:9001 |
|||
|
|||
ICE_SERVER_1_URL=TURN_DOMAIN:PORT_COTURN_LISTEN_EXTERNAL |
|||
ICE_SERVER_1_USERNAME=TURN_USERNAME |
|||
ICE_SERVER_1_CREDENTIAL=TURN_PASSWORD |
|||
|
|||
ICE_SERVER_2_URL=TURN_DOMAIN:PORT_COTURN_LISTEN_EXTERNAL |
|||
ICE_SERVER_2_USERNAME=TURN_USERNAME |
|||
ICE_SERVER_2_CREDENTIAL=TURN_PASSWORD |
|||
|
|||
jwt_accessToken_secret=JWT_ACCESSTOKEN_SECRET |
|||
jwt_accessToken_expireIn=JWT_ACCESSTOKEN_EXPIREDIN |
|||
jwt_refreshToken_secret=JWT_REFRESHTOKEN_SECRET |
|||
jwt_refreshToken_expireIn=JWT_REFRESHTOKEN_EXPIREDIN |
|||
|
|||
@ -1,4 +0,0 @@ |
|||
spring_redis_host=im-redis |
|||
spring_redis_port=6379 |
|||
spring_redis_password=REDIS_PASSWORD |
|||
jwt_accessToken_secret=JWT_ACCESSTOKEN_SECRET |
|||
@ -1,41 +1,38 @@ |
|||
package com.bx.implatform.config; |
|||
|
|||
import io.swagger.annotations.ApiOperation; |
|||
|
|||
import io.swagger.v3.oas.models.OpenAPI; |
|||
import io.swagger.v3.oas.models.info.Contact; |
|||
import io.swagger.v3.oas.models.info.Info; |
|||
import io.swagger.v3.oas.models.info.License; |
|||
import org.springdoc.core.models.GroupedOpenApi; |
|||
import org.springframework.context.annotation.Bean; |
|||
import org.springframework.context.annotation.Configuration; |
|||
import springfox.documentation.builders.ApiInfoBuilder; |
|||
import springfox.documentation.builders.PathSelectors; |
|||
import springfox.documentation.builders.RequestHandlerSelectors; |
|||
import springfox.documentation.service.ApiInfo; |
|||
import springfox.documentation.spi.DocumentationType; |
|||
import springfox.documentation.spring.web.plugins.Docket; |
|||
import springfox.documentation.swagger2.annotations.EnableSwagger2; |
|||
|
|||
@Configuration |
|||
@EnableSwagger2 |
|||
public class SwaggerConfig { |
|||
|
|||
@Bean |
|||
public Docket createRestApi() { |
|||
|
|||
return new Docket(DocumentationType.SWAGGER_2) |
|||
.apiInfo(apiInfo()) |
|||
.select() |
|||
//这里采用包含注解的方式来确定要显示的接口
|
|||
.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class)) |
|||
//这里采用包扫描的方式来确定要显示的接口
|
|||
.paths(PathSelectors.any()) |
|||
.build(); |
|||
|
|||
public GroupedOpenApi userApi() { |
|||
String[] paths = {"/**"}; |
|||
String[] packagedToMatch = {"com.bx"}; |
|||
return GroupedOpenApi.builder().group("IM-Platform") |
|||
.pathsToMatch(paths) |
|||
.packagesToScan(packagedToMatch).build(); |
|||
} |
|||
|
|||
private ApiInfo apiInfo() { |
|||
return new ApiInfoBuilder() |
|||
.title("IM Platform doc") |
|||
.description("盒子IM API文档") |
|||
.termsOfServiceUrl("http://8.134.92.70/") |
|||
.version("1.0") |
|||
.build(); |
|||
@Bean |
|||
public OpenAPI customOpenAPI() { |
|||
Contact contact = new Contact(); |
|||
contact.setName("Blue"); |
|||
return new OpenAPI().info(new Info() |
|||
.title("盒子IM接口文档") |
|||
.description("盒子IM业务平台服务") |
|||
.contact(contact) |
|||
.version("3.0") |
|||
.termsOfService("https://www.boxim.online") |
|||
.license(new License().name("MIT") |
|||
.url("https://www.boxim.online"))); |
|||
} |
|||
|
|||
} |
|||
|
|||
@ -1,126 +1,124 @@ |
|||
package com.bx.implatform.controller; |
|||
|
|||
import com.bx.implatform.config.WebrtcConfig; |
|||
import com.bx.implatform.dto.*; |
|||
import com.bx.implatform.result.Result; |
|||
import com.bx.implatform.result.ResultUtils; |
|||
import com.bx.implatform.service.IWebrtcGroupService; |
|||
import com.bx.implatform.service.WebrtcGroupService; |
|||
import com.bx.implatform.vo.WebrtcGroupInfoVO; |
|||
import io.swagger.annotations.Api; |
|||
import io.swagger.annotations.ApiOperation; |
|||
import io.swagger.v3.oas.annotations.Operation; |
|||
import io.swagger.v3.oas.annotations.tags.Tag; |
|||
import jakarta.validation.Valid; |
|||
import lombok.RequiredArgsConstructor; |
|||
import org.springframework.web.bind.annotation.*; |
|||
|
|||
import javax.validation.Valid; |
|||
|
|||
/** |
|||
* @author: Blue |
|||
* @date: 2024-06-01 |
|||
* @version: 1.0 |
|||
*/ |
|||
@Api(tags = "webrtc视频多人通话") |
|||
@Tag(name = "多人通话") |
|||
@RestController |
|||
@RequestMapping("/webrtc/group") |
|||
@RequiredArgsConstructor |
|||
public class WebrtcGroupController { |
|||
|
|||
private final IWebrtcGroupService webrtcGroupService; |
|||
private final WebrtcGroupService webrtcGroupService; |
|||
|
|||
@ApiOperation(httpMethod = "POST", value = "发起群视频通话") |
|||
@Operation(summary = "发起群视频通话") |
|||
@PostMapping("/setup") |
|||
public Result setup(@Valid @RequestBody WebrtcGroupSetupDTO dto) { |
|||
webrtcGroupService.setup(dto); |
|||
return ResultUtils.success(); |
|||
} |
|||
|
|||
@ApiOperation(httpMethod = "POST", value = "接受通话") |
|||
@Operation(summary = "接受通话") |
|||
@PostMapping("/accept") |
|||
public Result accept(@RequestParam Long groupId) { |
|||
public Result accept(@RequestParam("groupId") Long groupId) { |
|||
webrtcGroupService.accept(groupId); |
|||
return ResultUtils.success(); |
|||
} |
|||
|
|||
@ApiOperation(httpMethod = "POST", value = "拒绝通话") |
|||
@Operation(summary = "拒绝通话") |
|||
@PostMapping("/reject") |
|||
public Result reject(@RequestParam Long groupId) { |
|||
public Result reject(@RequestParam("groupId") Long groupId) { |
|||
webrtcGroupService.reject(groupId); |
|||
return ResultUtils.success(); |
|||
} |
|||
|
|||
@ApiOperation(httpMethod = "POST", value = "通话失败") |
|||
@Operation(summary = "通话失败") |
|||
@PostMapping("/failed") |
|||
public Result failed(@Valid @RequestBody WebrtcGroupFailedDTO dto) { |
|||
webrtcGroupService.failed(dto); |
|||
return ResultUtils.success(); |
|||
} |
|||
|
|||
@ApiOperation(httpMethod = "POST", value = "进入视频通话") |
|||
@Operation(summary = "进入视频通话") |
|||
@PostMapping("/join") |
|||
public Result join(@RequestParam Long groupId) { |
|||
public Result join(@RequestParam("groupId") Long groupId) { |
|||
webrtcGroupService.join(groupId); |
|||
return ResultUtils.success(); |
|||
} |
|||
|
|||
@ApiOperation(httpMethod = "POST", value = "取消通话") |
|||
@Operation(summary = "取消通话") |
|||
@PostMapping("/cancel") |
|||
public Result cancel(@RequestParam Long groupId) { |
|||
public Result cancel(@RequestParam("groupId") Long groupId) { |
|||
webrtcGroupService.cancel(groupId); |
|||
return ResultUtils.success(); |
|||
} |
|||
|
|||
@ApiOperation(httpMethod = "POST", value = "离开视频通话") |
|||
@Operation(summary = "离开视频通话") |
|||
@PostMapping("/quit") |
|||
public Result quit(@RequestParam Long groupId) { |
|||
public Result quit(@RequestParam("groupId") Long groupId) { |
|||
webrtcGroupService.quit(groupId); |
|||
return ResultUtils.success(); |
|||
} |
|||
|
|||
@ApiOperation(httpMethod = "POST", value = "推送offer信息") |
|||
@Operation(summary = "推送offer信息") |
|||
@PostMapping("/offer") |
|||
public Result offer(@Valid @RequestBody WebrtcGroupOfferDTO dto) { |
|||
webrtcGroupService.offer(dto); |
|||
return ResultUtils.success(); |
|||
} |
|||
|
|||
@ApiOperation(httpMethod = "POST", value = "推送answer信息") |
|||
@Operation(summary = "推送answer信息") |
|||
@PostMapping("/answer") |
|||
public Result answer(@Valid @RequestBody WebrtcGroupAnswerDTO dto) { |
|||
webrtcGroupService.answer(dto); |
|||
return ResultUtils.success(); |
|||
} |
|||
|
|||
@ApiOperation(httpMethod = "POST", value = "邀请用户进入视频通话") |
|||
@Operation(summary = "邀请用户进入视频通话") |
|||
@PostMapping("/invite") |
|||
public Result invite(@Valid @RequestBody WebrtcGroupInviteDTO dto) { |
|||
webrtcGroupService.invite(dto); |
|||
return ResultUtils.success(); |
|||
} |
|||
|
|||
@ApiOperation(httpMethod = "POST", value = "同步candidate") |
|||
@Operation(summary = "同步candidate") |
|||
@PostMapping("/candidate") |
|||
public Result candidate(@Valid @RequestBody WebrtcGroupCandidateDTO dto) { |
|||
webrtcGroupService.candidate(dto); |
|||
return ResultUtils.success(); |
|||
} |
|||
|
|||
@ApiOperation(httpMethod = "POST", value = "设备操作") |
|||
@Operation(summary = "设备操作") |
|||
@PostMapping("/device") |
|||
public Result device(@Valid @RequestBody WebrtcGroupDeviceDTO dto) { |
|||
webrtcGroupService.device(dto); |
|||
return ResultUtils.success(); |
|||
} |
|||
|
|||
@ApiOperation(httpMethod = "GET", value = "获取通话信息") |
|||
@Operation(summary = "获取通话信息") |
|||
@GetMapping("/info") |
|||
public Result<WebrtcGroupInfoVO> info(@RequestParam Long groupId) { |
|||
public Result<WebrtcGroupInfoVO> info(@RequestParam("groupId") Long groupId) { |
|||
return ResultUtils.success(webrtcGroupService.info(groupId)); |
|||
} |
|||
|
|||
@ApiOperation(httpMethod = "POST", value = "获取通话信息") |
|||
@Operation(summary = "获取通话信息") |
|||
@PostMapping("/heartbeat") |
|||
public Result heartbeat(@RequestParam Long groupId) { |
|||
public Result heartbeat(@RequestParam("groupId") Long groupId) { |
|||
webrtcGroupService.heartbeat(groupId); |
|||
return ResultUtils.success(); |
|||
} |
|||
|
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,21 @@ |
|||
package com.bx.implatform.dto; |
|||
|
|||
|
|||
import io.swagger.v3.oas.annotations.media.Schema; |
|||
import lombok.Data; |
|||
|
|||
/** |
|||
* @author: Blue |
|||
* @date: 2024-07-14 |
|||
* @version: 1.0 |
|||
*/ |
|||
@Data |
|||
@Schema(description = "群组封禁") |
|||
public class GroupBanDTO { |
|||
|
|||
@Schema(description = "群组id") |
|||
private Long id; |
|||
|
|||
@Schema(description = "封禁原因") |
|||
private String reason; |
|||
} |
|||
@ -1,36 +1,35 @@ |
|||
package com.bx.implatform.dto; |
|||
|
|||
import io.swagger.annotations.ApiModel; |
|||
import io.swagger.annotations.ApiModelProperty; |
|||
import io.swagger.v3.oas.annotations.media.Schema; |
|||
import jakarta.validation.constraints.NotEmpty; |
|||
import jakarta.validation.constraints.NotNull; |
|||
import jakarta.validation.constraints.Size; |
|||
import lombok.Data; |
|||
import org.hibernate.validator.constraints.Length; |
|||
|
|||
import javax.validation.constraints.NotEmpty; |
|||
import javax.validation.constraints.NotNull; |
|||
import javax.validation.constraints.Size; |
|||
import java.util.List; |
|||
|
|||
@Data |
|||
@ApiModel("群聊消息DTO") |
|||
@Schema(description = "群聊消息DTO") |
|||
public class GroupMessageDTO { |
|||
|
|||
@NotNull(message = "群聊id不可为空") |
|||
@ApiModelProperty(value = "群聊id") |
|||
@Schema(description = "群聊id") |
|||
private Long groupId; |
|||
|
|||
@Length(max = 1024, message = "发送内容长度不得大于1024") |
|||
@NotEmpty(message = "发送内容不可为空") |
|||
@ApiModelProperty(value = "发送内容") |
|||
@Schema(description = "发送内容") |
|||
private String content; |
|||
|
|||
@NotNull(message = "消息类型不可为空") |
|||
@ApiModelProperty(value = "消息类型") |
|||
@Schema(description = "消息类型 0:文字 1:图片 2:文件 3:语音 4:视频") |
|||
private Integer type; |
|||
|
|||
@ApiModelProperty(value = "是否回执消息") |
|||
@Schema(description = "是否回执消息") |
|||
private Boolean receipt = false; |
|||
|
|||
@Size(max = 20, message = "一次最多只能@20个小伙伴哦") |
|||
@ApiModelProperty(value = "被@用户列表") |
|||
@Schema(description = "被@用户列表") |
|||
private List<Long> atUserIds; |
|||
} |
|||
|
|||
@ -0,0 +1,18 @@ |
|||
package com.bx.implatform.dto; |
|||
|
|||
import io.swagger.v3.oas.annotations.media.Schema; |
|||
import lombok.Data; |
|||
|
|||
/** |
|||
* @author: Blue |
|||
* @date: 2024-07-14 |
|||
* @version: 1.0 |
|||
*/ |
|||
@Data |
|||
@Schema(description = "群组解锁") |
|||
public class GroupUnbanDTO { |
|||
|
|||
@Schema(description = "群组id") |
|||
private Long id; |
|||
|
|||
} |
|||
@ -1,30 +1,28 @@ |
|||
package com.bx.implatform.dto; |
|||
|
|||
import io.swagger.annotations.ApiModel; |
|||
import io.swagger.annotations.ApiModelProperty; |
|||
import io.swagger.v3.oas.annotations.media.Schema; |
|||
import jakarta.validation.constraints.Max; |
|||
import jakarta.validation.constraints.Min; |
|||
import jakarta.validation.constraints.NotEmpty; |
|||
import jakarta.validation.constraints.NotNull; |
|||
import lombok.Data; |
|||
|
|||
import javax.validation.constraints.Max; |
|||
import javax.validation.constraints.Min; |
|||
import javax.validation.constraints.NotEmpty; |
|||
import javax.validation.constraints.NotNull; |
|||
|
|||
@Data |
|||
@ApiModel("用户登录DTO") |
|||
@Schema(description = "用户登录DTO") |
|||
public class LoginDTO { |
|||
|
|||
@Max(value = 2, message = "登录终端类型取值范围:0,2") |
|||
@Min(value = 0, message = "登录终端类型取值范围:0,2") |
|||
@NotNull(message = "登录终端类型不可为空") |
|||
@ApiModelProperty(value = "登录终端 0:web 1:app 2:pc") |
|||
@Schema(description = "登录终端 0:web 1:app 2:pc") |
|||
private Integer terminal; |
|||
|
|||
@NotEmpty(message = "用户名不可为空") |
|||
@ApiModelProperty(value = "用户名") |
|||
@Schema(description = "用户名") |
|||
private String userName; |
|||
|
|||
@NotEmpty(message = "用户密码不可为空") |
|||
@ApiModelProperty(value = "用户密码") |
|||
@Schema(description = "用户密码") |
|||
private String password; |
|||
|
|||
} |
|||
|
|||
@ -1,21 +1,19 @@ |
|||
package com.bx.implatform.dto; |
|||
|
|||
import io.swagger.annotations.ApiModel; |
|||
import io.swagger.annotations.ApiModelProperty; |
|||
import io.swagger.v3.oas.annotations.media.Schema; |
|||
import jakarta.validation.constraints.NotEmpty; |
|||
import lombok.Data; |
|||
|
|||
import javax.validation.constraints.NotEmpty; |
|||
|
|||
@Data |
|||
@ApiModel("修改密码DTO") |
|||
@Schema(description = "修改密码DTO") |
|||
public class ModifyPwdDTO { |
|||
|
|||
@NotEmpty(message = "旧用户密码不可为空") |
|||
@ApiModelProperty(value = "旧用户密码") |
|||
@Schema(description = "旧用户密码") |
|||
private String oldPassword; |
|||
|
|||
@NotEmpty(message = "新用户密码不可为空") |
|||
@ApiModelProperty(value = "新用户密码") |
|||
@Schema(description = "新用户密码") |
|||
private String newPassword; |
|||
|
|||
} |
|||
|
|||
@ -1,29 +1,27 @@ |
|||
package com.bx.implatform.dto; |
|||
|
|||
import io.swagger.annotations.ApiModel; |
|||
import io.swagger.annotations.ApiModelProperty; |
|||
import io.swagger.v3.oas.annotations.media.Schema; |
|||
import jakarta.validation.constraints.NotEmpty; |
|||
import jakarta.validation.constraints.NotNull; |
|||
import lombok.Data; |
|||
import org.hibernate.validator.constraints.Length; |
|||
|
|||
import javax.validation.constraints.NotEmpty; |
|||
import javax.validation.constraints.NotNull; |
|||
|
|||
@Data |
|||
@ApiModel("私聊消息DTO") |
|||
@Schema(description = "私聊消息DTO") |
|||
public class PrivateMessageDTO { |
|||
|
|||
@NotNull(message = "接收用户id不可为空") |
|||
@ApiModelProperty(value = "接收用户id") |
|||
@Schema(description = "接收用户id") |
|||
private Long recvId; |
|||
|
|||
|
|||
@Length(max = 1024, message = "内容长度不得大于1024") |
|||
@NotEmpty(message = "发送内容不可为空") |
|||
@ApiModelProperty(value = "发送内容") |
|||
@Schema(description = "发送内容") |
|||
private String content; |
|||
|
|||
@NotNull(message = "消息类型不可为空") |
|||
@ApiModelProperty(value = "消息类型") |
|||
@Schema(description = "消息类型 0:文字 1:图片 2:文件 3:语音 4:视频") |
|||
private Integer type; |
|||
|
|||
} |
|||
|
|||
@ -0,0 +1,21 @@ |
|||
package com.bx.implatform.dto; |
|||
|
|||
import io.swagger.v3.oas.annotations.media.Schema; |
|||
import lombok.Data; |
|||
|
|||
/** |
|||
* @author: Blue |
|||
* @date: 2024-07-14 |
|||
* @version: 1.0 |
|||
*/ |
|||
@Data |
|||
@Schema(description = "用户锁定DTO") |
|||
public class UserBanDTO { |
|||
|
|||
@Schema(description = "用户id") |
|||
private Long id; |
|||
|
|||
@Schema(description = "锁定原因") |
|||
private String reason; |
|||
|
|||
} |
|||
@ -1,31 +1,29 @@ |
|||
package com.bx.implatform.dto; |
|||
|
|||
import io.swagger.annotations.ApiModel; |
|||
import io.swagger.annotations.ApiModelProperty; |
|||
import io.swagger.v3.oas.annotations.media.Schema; |
|||
import jakarta.validation.constraints.NotEmpty; |
|||
import jakarta.validation.constraints.NotNull; |
|||
import lombok.Data; |
|||
|
|||
import javax.validation.constraints.NotEmpty; |
|||
import javax.validation.constraints.NotNull; |
|||
|
|||
/** |
|||
* @author: Blue |
|||
* @date: 2024-06-01 |
|||
* @version: 1.0 |
|||
*/ |
|||
@Data |
|||
@ApiModel("回复用户连接请求DTO") |
|||
@Schema(description = "回复用户连接请求DTO") |
|||
public class WebrtcGroupAnswerDTO { |
|||
|
|||
@NotNull(message = "群聊id不可为空") |
|||
@ApiModelProperty(value = "群聊id") |
|||
@Schema(description = "群聊id") |
|||
private Long groupId; |
|||
|
|||
@NotNull(message = "用户id不可为空") |
|||
@ApiModelProperty(value = "用户id,代表回复谁的连接请求") |
|||
@Schema(description = "用户id,代表回复谁的连接请求") |
|||
private Long userId; |
|||
|
|||
@NotEmpty(message = "anwer不可为空") |
|||
@ApiModelProperty(value = "用户本地anwer信息") |
|||
@Schema(description = "用户本地anwer信息") |
|||
private String answer; |
|||
|
|||
} |
|||
|
|||
@ -1,32 +1,29 @@ |
|||
package com.bx.implatform.dto; |
|||
|
|||
import io.swagger.annotations.ApiModel; |
|||
import io.swagger.annotations.ApiModelProperty; |
|||
import io.swagger.v3.oas.annotations.media.Schema; |
|||
import jakarta.validation.constraints.NotEmpty; |
|||
import jakarta.validation.constraints.NotNull; |
|||
import lombok.Data; |
|||
|
|||
import javax.validation.constraints.NotEmpty; |
|||
import javax.validation.constraints.NotNull; |
|||
import java.util.List; |
|||
|
|||
/** |
|||
* @author: Blue |
|||
* @date: 2024-06-01 |
|||
* @version: 1.0 |
|||
*/ |
|||
@Data |
|||
@ApiModel("发起群视频通话DTO") |
|||
@Schema(description = "发起群视频通话DTO") |
|||
public class WebrtcGroupCandidateDTO { |
|||
|
|||
@NotNull(message = "群聊id不可为空") |
|||
@ApiModelProperty(value = "群聊id") |
|||
@Schema(description = "群聊id") |
|||
private Long groupId; |
|||
|
|||
@NotNull(message = "用户id不可为空") |
|||
@ApiModelProperty(value = "用户id") |
|||
@Schema(description = "用户id") |
|||
private Long userId; |
|||
|
|||
@NotEmpty(message = "candidate信息不可为空") |
|||
@ApiModelProperty(value = "candidate信息") |
|||
@Schema(description = "candidate信息") |
|||
private String candidate; |
|||
|
|||
} |
|||
|
|||
@ -1,29 +1,26 @@ |
|||
package com.bx.implatform.dto; |
|||
|
|||
import io.swagger.annotations.ApiModel; |
|||
import io.swagger.annotations.ApiModelProperty; |
|||
import io.swagger.v3.oas.annotations.media.Schema; |
|||
import jakarta.validation.constraints.NotNull; |
|||
import lombok.Data; |
|||
|
|||
import javax.validation.constraints.NotEmpty; |
|||
import javax.validation.constraints.NotNull; |
|||
|
|||
/** |
|||
* @author: Blue |
|||
* @date: 2024-06-01 |
|||
* @version: 1.0 |
|||
*/ |
|||
@Data |
|||
@ApiModel("用户设备操作DTO") |
|||
@Schema(description = "用户设备操作DTO") |
|||
public class WebrtcGroupDeviceDTO { |
|||
|
|||
@NotNull(message = "群聊id不可为空") |
|||
@ApiModelProperty(value = "群聊id") |
|||
@Schema(description = "群聊id") |
|||
private Long groupId; |
|||
|
|||
@ApiModelProperty(value = "是否开启摄像头") |
|||
@Schema(description = "是否开启摄像头") |
|||
private Boolean isCamera; |
|||
|
|||
@ApiModelProperty(value = "是否开启麦克风") |
|||
@Schema(description = "是否开启麦克风") |
|||
private Boolean isMicroPhone; |
|||
|
|||
} |
|||
|
|||
@ -1,25 +1,23 @@ |
|||
package com.bx.implatform.dto; |
|||
|
|||
import io.swagger.annotations.ApiModel; |
|||
import io.swagger.annotations.ApiModelProperty; |
|||
import io.swagger.v3.oas.annotations.media.Schema; |
|||
import jakarta.validation.constraints.NotNull; |
|||
import lombok.Data; |
|||
|
|||
import javax.validation.constraints.NotNull; |
|||
|
|||
/** |
|||
* @author: Blue |
|||
* @date: 2024-06-01 |
|||
* @version: 1.0 |
|||
*/ |
|||
@Data |
|||
@ApiModel("用户通话失败DTO") |
|||
@Schema(description = "用户通话失败DTO") |
|||
public class WebrtcGroupFailedDTO { |
|||
|
|||
@NotNull(message = "群聊id不可为空") |
|||
@ApiModelProperty(value = "群聊id") |
|||
@Schema(description = "群聊id") |
|||
private Long groupId; |
|||
|
|||
@ApiModelProperty(value = "失败原因") |
|||
@Schema(description = "失败原因") |
|||
private String reason; |
|||
|
|||
} |
|||
|
|||
@ -1,23 +1,20 @@ |
|||
package com.bx.implatform.dto; |
|||
|
|||
import io.swagger.annotations.ApiModel; |
|||
import io.swagger.annotations.ApiModelProperty; |
|||
import io.swagger.v3.oas.annotations.media.Schema; |
|||
import jakarta.validation.constraints.NotNull; |
|||
import lombok.Data; |
|||
|
|||
import javax.validation.constraints.NotEmpty; |
|||
import javax.validation.constraints.NotNull; |
|||
|
|||
/** |
|||
* @author: Blue |
|||
* @date: 2024-06-01 |
|||
* @version: 1.0 |
|||
*/ |
|||
@Data |
|||
@ApiModel("进入群视频通话DTO") |
|||
@Schema(description = "进入群视频通话DTO") |
|||
public class WebrtcGroupJoinDTO { |
|||
|
|||
@NotNull(message = "群聊id不可为空") |
|||
@ApiModelProperty(value = "群聊id") |
|||
@Schema(description = "群聊id") |
|||
private Long groupId; |
|||
|
|||
} |
|||
|
|||
@ -1,31 +1,29 @@ |
|||
package com.bx.implatform.dto; |
|||
|
|||
import io.swagger.annotations.ApiModel; |
|||
import io.swagger.annotations.ApiModelProperty; |
|||
import io.swagger.v3.oas.annotations.media.Schema; |
|||
import jakarta.validation.constraints.NotEmpty; |
|||
import jakarta.validation.constraints.NotNull; |
|||
import lombok.Data; |
|||
|
|||
import javax.validation.constraints.NotEmpty; |
|||
import javax.validation.constraints.NotNull; |
|||
|
|||
/** |
|||
* @author: Blue |
|||
* @date: 2024-06-01 |
|||
* @version: 1.0 |
|||
*/ |
|||
@Data |
|||
@ApiModel("回复用户连接请求DTO") |
|||
@Schema(description = "回复用户连接请求DTO") |
|||
public class WebrtcGroupOfferDTO { |
|||
|
|||
@NotNull(message = "群聊id不可为空") |
|||
@ApiModelProperty(value = "群聊id") |
|||
@Schema(description = "群聊id") |
|||
private Long groupId; |
|||
|
|||
@NotNull(message = "用户id不可为空") |
|||
@ApiModelProperty(value = "用户id,代表回复谁的连接请求") |
|||
@Schema(description = "用户id,代表回复谁的连接请求") |
|||
private Long userId; |
|||
|
|||
@NotEmpty(message = "offer不可为空") |
|||
@ApiModelProperty(value = "用户offer信息") |
|||
@Schema(description = "用户offer信息") |
|||
private String offer; |
|||
|
|||
} |
|||
|
|||
@ -0,0 +1,50 @@ |
|||
package com.bx.implatform.entity; |
|||
|
|||
import com.baomidou.mybatisplus.annotation.FieldFill; |
|||
import com.baomidou.mybatisplus.annotation.TableField; |
|||
import com.baomidou.mybatisplus.annotation.TableId; |
|||
import com.baomidou.mybatisplus.annotation.TableName; |
|||
import lombok.Data; |
|||
|
|||
import java.time.LocalDateTime; |
|||
|
|||
/** |
|||
* 敏感词 |
|||
* |
|||
* @author Blue |
|||
* @since 1.0.0 2024-07-20 |
|||
*/ |
|||
|
|||
@Data |
|||
@TableName("im_sensitive_word") |
|||
public class SensitiveWord { |
|||
/** |
|||
* id |
|||
*/ |
|||
@TableId |
|||
private Long id; |
|||
|
|||
/** |
|||
* 敏感词内容 |
|||
*/ |
|||
private String content; |
|||
|
|||
/** |
|||
* 是否启用 |
|||
*/ |
|||
private Boolean enabled; |
|||
|
|||
/** |
|||
* 创建者 |
|||
*/ |
|||
@TableField(fill = FieldFill.INSERT) |
|||
private Long creator; |
|||
|
|||
/** |
|||
* 创建时间 |
|||
*/ |
|||
@TableField(fill = FieldFill.INSERT) |
|||
private LocalDateTime createTime; |
|||
|
|||
|
|||
} |
|||
@ -1,102 +0,0 @@ |
|||
package com.bx.implatform.generator; |
|||
|
|||
import com.baomidou.mybatisplus.annotation.IdType; |
|||
import com.baomidou.mybatisplus.core.toolkit.StringPool; |
|||
import com.baomidou.mybatisplus.generator.AutoGenerator; |
|||
import com.baomidou.mybatisplus.generator.InjectionConfig; |
|||
import com.baomidou.mybatisplus.generator.config.*; |
|||
import com.baomidou.mybatisplus.generator.config.po.TableInfo; |
|||
import com.baomidou.mybatisplus.generator.config.rules.DateType; |
|||
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy; |
|||
import com.baomidou.mybatisplus.generator.engine.VelocityTemplateEngine; |
|||
|
|||
import java.util.ArrayList; |
|||
import java.util.List; |
|||
|
|||
|
|||
public class CodeGenerator { |
|||
public static void main(String[] args) { |
|||
// 代码生成器
|
|||
AutoGenerator mpg = new AutoGenerator(); |
|||
|
|||
// 全局配置
|
|||
GlobalConfig gc = new GlobalConfig(); |
|||
//生成的代码输出路径,自己根据需要修改
|
|||
String projectPath = "d:\\work\\project\\code"; |
|||
gc.setOutputDir(projectPath + "/src/main/java"); |
|||
gc.setAuthor("blue"); |
|||
gc.setOpen(false); |
|||
gc.setFileOverride(true); |
|||
gc.setActiveRecord(true); |
|||
gc.setBaseColumnList(true); |
|||
gc.setBaseResultMap(true); |
|||
gc.setIdType(IdType.AUTO); |
|||
gc.setDateType(DateType.ONLY_DATE); |
|||
mpg.setGlobalConfig(gc); |
|||
|
|||
// 数据源配置
|
|||
DataSourceConfig dsc = new DataSourceConfig(); |
|||
dsc.setUrl("jdbc:mysql://localhost:3306/box-im?useUnicode=true&characterEncoding=utf-8"); |
|||
dsc.setDriverName("com.mysql.jdbc.Driver"); |
|||
dsc.setUsername("root"); |
|||
dsc.setPassword("root"); |
|||
mpg.setDataSource(dsc); |
|||
|
|||
// 包配置
|
|||
PackageConfig pc = new PackageConfig(); |
|||
pc.setModuleName(""); |
|||
pc.setParent("com.bx"); |
|||
mpg.setPackageInfo(pc); |
|||
|
|||
// 如果模板引擎是 velocity
|
|||
String templatePath = "/templates/mapper.xml.vm"; |
|||
|
|||
// 自定义输出配置
|
|||
List<FileOutConfig> focList = new ArrayList<>(); |
|||
// 自定义配置会被优先输出
|
|||
focList.add(new FileOutConfig(templatePath) { |
|||
@Override |
|||
public String outputFile(TableInfo tableInfo) { |
|||
// 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!!
|
|||
return projectPath + "/src/main/resources/mapper/" |
|||
+ tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML; |
|||
} |
|||
}); |
|||
|
|||
// 自定义配置
|
|||
InjectionConfig cfg = new InjectionConfig() { |
|||
@Override |
|||
public void initMap() { |
|||
// to do nothing
|
|||
} |
|||
}; |
|||
cfg.setFileOutConfigList(focList); |
|||
mpg.setCfg(cfg); |
|||
|
|||
// 配置模板
|
|||
TemplateConfig templateConfig = new TemplateConfig(); |
|||
templateConfig.setXml(null); |
|||
mpg.setTemplate(templateConfig); |
|||
|
|||
// 策略配置
|
|||
StrategyConfig strategy = new StrategyConfig(); |
|||
// 下划线转驼峰
|
|||
strategy.setNaming(NamingStrategy.underline_to_camel); |
|||
strategy.setColumnNaming(NamingStrategy.underline_to_camel); |
|||
strategy.setEntityTableFieldAnnotationEnable(true); |
|||
strategy.setVersionFieldName("version"); |
|||
//逻辑删除的字段
|
|||
strategy.setLogicDeleteFieldName("deleted"); |
|||
strategy.setEntityLombokModel(true); |
|||
strategy.setRestControllerStyle(true); |
|||
|
|||
|
|||
//多张表的时候直接在代码中写表名
|
|||
strategy.setInclude("friends"); |
|||
strategy.setTablePrefix(""); |
|||
mpg.setStrategy(strategy); |
|||
|
|||
mpg.setTemplateEngine(new VelocityTemplateEngine()); |
|||
mpg.execute(); |
|||
} |
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
package com.bx.implatform.listener; |
|||
|
|||
import com.bx.imclient.annotation.IMListener; |
|||
import com.bx.imclient.listener.MessageListener; |
|||
import com.bx.imcommon.enums.IMListenerType; |
|||
import com.bx.imcommon.enums.IMSendCode; |
|||
import com.bx.imcommon.model.IMSendResult; |
|||
import com.bx.implatform.vo.SystemMessageVO; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
|
|||
import java.util.List; |
|||
|
|||
@Slf4j |
|||
@IMListener(type = IMListenerType.SYSTEM_MESSAGE) |
|||
public class SystemMessageListener implements MessageListener<SystemMessageVO> { |
|||
|
|||
@Override |
|||
public void process(List<IMSendResult<SystemMessageVO>> results) { |
|||
for(IMSendResult<SystemMessageVO> result : results){ |
|||
SystemMessageVO messageInfo = result.getData(); |
|||
if (result.getCode().equals(IMSendCode.SUCCESS.code())) { |
|||
log.info("消息送达,消息id:{},接收者:{},终端:{}", messageInfo.getId(), result.getReceiver().getId(), result.getReceiver().getTerminal()); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,16 @@ |
|||
package com.bx.implatform.mapper; |
|||
|
|||
import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
|||
import com.bx.implatform.entity.SensitiveWord; |
|||
import org.apache.ibatis.annotations.Mapper; |
|||
|
|||
/** |
|||
* 敏感词 |
|||
* |
|||
* @author Blue |
|||
* @since 1.0.0 2024-07-20 |
|||
*/ |
|||
@Mapper |
|||
public interface SensitiveWordMapper extends BaseMapper<SensitiveWord> { |
|||
|
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
package com.bx.implatform.service; |
|||
|
|||
import com.baomidou.mybatisplus.extension.service.IService; |
|||
import com.bx.implatform.dto.LoginDTO; |
|||
import com.bx.implatform.dto.ModifyPwdDTO; |
|||
import com.bx.implatform.dto.RegisterDTO; |
|||
import com.bx.implatform.entity.SensitiveWord; |
|||
import com.bx.implatform.entity.User; |
|||
import com.bx.implatform.vo.LoginVO; |
|||
import com.bx.implatform.vo.OnlineTerminalVO; |
|||
import com.bx.implatform.vo.UserVO; |
|||
|
|||
import java.util.List; |
|||
|
|||
public interface SensitiveWordService extends IService<SensitiveWord> { |
|||
|
|||
/** |
|||
* 查询所有开启的敏感词 |
|||
* @return |
|||
*/ |
|||
List<String> findAllEnabledWords(); |
|||
} |
|||
@ -1,10 +1,9 @@ |
|||
package com.bx.implatform.service; |
|||
|
|||
import com.bx.implatform.config.WebrtcConfig; |
|||
import com.bx.implatform.dto.*; |
|||
import com.bx.implatform.vo.WebrtcGroupInfoVO; |
|||
|
|||
public interface IWebrtcGroupService { |
|||
public interface WebrtcGroupService { |
|||
|
|||
/** |
|||
* 发起通话 |
|||
@ -1,15 +1,11 @@ |
|||
package com.bx.implatform.service; |
|||
|
|||
import com.bx.implatform.config.ICEServer; |
|||
|
|||
import java.util.List; |
|||
|
|||
/** |
|||
* webrtc 通信服务 |
|||
* |
|||
* @author |
|||
*/ |
|||
public interface IWebrtcPrivateService { |
|||
public interface WebrtcPrivateService { |
|||
|
|||
void call(Long uid, String mode,String offer); |
|||
|
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue