24 changed files with 430 additions and 186 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); |
|
||||
} |
|
||||
|
|
||||
} |
|
||||
@ -0,0 +1,33 @@ |
|||||
|
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.core.RedisTemplate; |
||||
|
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,13 @@ |
|||||
|
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){} |
||||
|
} |
||||
@ -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,109 @@ |
|||||
|
package com.bx.imcommon.mq; |
||||
|
|
||||
|
import com.alibaba.fastjson.JSONObject; |
||||
|
|
||||
|
import com.bx.imcommon.util.ThreadPoolExecutorFactory; |
||||
|
import lombok.SneakyThrows; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.boot.CommandLineRunner; |
||||
|
import org.springframework.data.redis.connection.RedisConnection; |
||||
|
import org.springframework.data.redis.core.RedisTemplate; |
||||
|
import org.springframework.stereotype.Component; |
||||
|
|
||||
|
import javax.annotation.PreDestroy; |
||||
|
import javax.annotation.Resource; |
||||
|
import java.lang.reflect.ParameterizedType; |
||||
|
import java.lang.reflect.Type; |
||||
|
import java.util.*; |
||||
|
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_SERVICE = |
||||
|
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 key = annotation.queue(); |
||||
|
int batchSize = annotation.batchSize(); |
||||
|
int period = annotation.period(); |
||||
|
// 获取泛型类型
|
||||
|
Type superClass = consumer.getClass().getGenericSuperclass(); |
||||
|
Type type = ((ParameterizedType)superClass).getActualTypeArguments()[0]; |
||||
|
EXECUTOR_SERVICE.execute(new Runnable() { |
||||
|
@Override |
||||
|
public void run() { |
||||
|
List<Object> datas = new LinkedList<>(); |
||||
|
try { |
||||
|
// 拉取一个批次的数据
|
||||
|
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("数据消费异常,队列:{}", key, e); |
||||
|
} |
||||
|
// 继续消费数据
|
||||
|
if (!EXECUTOR_SERVICE.isShutdown()) { |
||||
|
if (datas.size() < batchSize) { |
||||
|
// 数据已经消费完,等待下一个周期继续拉取
|
||||
|
EXECUTOR_SERVICE.schedule(this, period, TimeUnit.MICROSECONDS); |
||||
|
} else { |
||||
|
// 数据没有消费完,直接开启下一个消费周期
|
||||
|
EXECUTOR_SERVICE.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, 100); |
||||
|
} 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(){ |
||||
|
ThreadPoolExecutorFactory.shutDown(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,42 @@ |
|||||
|
package com.bx.imcommon.mq; |
||||
|
|
||||
|
import cn.hutool.core.util.StrUtil; |
||||
|
import org.apache.logging.log4j.util.Strings; |
||||
|
import org.springframework.data.redis.connection.RedisConnection; |
||||
|
import org.springframework.data.redis.core.RedisTemplate; |
||||
|
|
||||
|
import java.util.Properties; |
||||
|
|
||||
|
/** |
||||
|
* @author: 谢绍许 |
||||
|
* @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); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,21 @@ |
|||||
|
package com.bx.implatform.dto; |
||||
|
|
||||
|
import io.swagger.annotations.ApiModel; |
||||
|
import io.swagger.annotations.ApiModelProperty; |
||||
|
import lombok.Data; |
||||
|
|
||||
|
/** |
||||
|
* @author: Blue |
||||
|
* @date: 2024-07-14 |
||||
|
* @version: 1.0 |
||||
|
*/ |
||||
|
@Data |
||||
|
@ApiModel(description = "群组封禁") |
||||
|
public class GroupBanDTO { |
||||
|
|
||||
|
@ApiModelProperty(value = "群组id") |
||||
|
private Long id; |
||||
|
|
||||
|
@ApiModelProperty(value = "封禁原因") |
||||
|
private String reason; |
||||
|
} |
||||
@ -0,0 +1,19 @@ |
|||||
|
package com.bx.implatform.dto; |
||||
|
|
||||
|
import io.swagger.annotations.ApiModel; |
||||
|
import io.swagger.annotations.ApiModelProperty; |
||||
|
import lombok.Data; |
||||
|
|
||||
|
/** |
||||
|
* @author: Blue |
||||
|
* @date: 2024-07-14 |
||||
|
* @version: 1.0 |
||||
|
*/ |
||||
|
@Data |
||||
|
@ApiModel(description = "群组解锁") |
||||
|
public class GroupUnbanDTO { |
||||
|
|
||||
|
@ApiModelProperty(value = "群组id") |
||||
|
private Long id; |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,22 @@ |
|||||
|
package com.bx.implatform.dto; |
||||
|
|
||||
|
import io.swagger.annotations.ApiModel; |
||||
|
import io.swagger.annotations.ApiModelProperty; |
||||
|
import lombok.Data; |
||||
|
|
||||
|
/** |
||||
|
* @author: Blue |
||||
|
* @date: 2024-07-14 |
||||
|
* @version: 1.0 |
||||
|
*/ |
||||
|
@Data |
||||
|
@ApiModel("用户锁定DTO") |
||||
|
public class UserBanDTO { |
||||
|
|
||||
|
@ApiModelProperty(value = "用户id") |
||||
|
private Long id; |
||||
|
|
||||
|
@ApiModelProperty(value = "锁定原因") |
||||
|
private String reason; |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,50 @@ |
|||||
|
package com.bx.implatform.task; |
||||
|
|
||||
|
import com.bx.imclient.IMClient; |
||||
|
import com.bx.imcommon.enums.IMTerminalType; |
||||
|
import com.bx.imcommon.model.IMPrivateMessage; |
||||
|
import com.bx.imcommon.model.IMUserInfo; |
||||
|
import com.bx.imcommon.mq.RedisMQConsumer; |
||||
|
import com.bx.imcommon.mq.RedisMQListener; |
||||
|
import com.bx.implatform.contant.Constant; |
||||
|
import com.bx.implatform.contant.RedisKey; |
||||
|
import com.bx.implatform.dto.UserBanDTO; |
||||
|
import com.bx.implatform.service.IUserService; |
||||
|
import com.bx.implatform.util.BeanUtils; |
||||
|
import com.bx.implatform.vo.PrivateMessageVO; |
||||
|
import lombok.RequiredArgsConstructor; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.springframework.security.core.parameters.P; |
||||
|
import org.springframework.stereotype.Component; |
||||
|
|
||||
|
/** |
||||
|
* @author: 谢绍许 |
||||
|
* @date: 2024-07-15 |
||||
|
* @version: 1.0 |
||||
|
*/ |
||||
|
@Slf4j |
||||
|
@Component |
||||
|
@RequiredArgsConstructor |
||||
|
@RedisMQListener(queue = RedisKey.IM_QUEUE_USER_BANNED) |
||||
|
public class UserBannedConsumerTask extends RedisMQConsumer<UserBanDTO> { |
||||
|
|
||||
|
private final IMClient imClient; |
||||
|
@Override |
||||
|
public void onMessage(UserBanDTO dto) { |
||||
|
|
||||
|
log.info("用户被封禁处理,userId:{},原因:{}",dto.getId(),dto.getReason()); |
||||
|
// 推送消息
|
||||
|
|
||||
|
PrivateMessageVO msgInfo = new PrivateMessageVO(); |
||||
|
msgInfo.setRecvId(dto.getId()); |
||||
|
msgInfo.setSendId(Constant.SYS_USER_ID); |
||||
|
msgInfo.setContent(dto.getReason()); |
||||
|
IMPrivateMessage<PrivateMessageVO> sendMessage = new IMPrivateMessage<>(); |
||||
|
sendMessage.setSender(new IMUserInfo(Constant.SYS_USER_ID, IMTerminalType.WEB.code())); |
||||
|
sendMessage.setRecvId(dto.getId()); |
||||
|
sendMessage.setSendToSelf(false); |
||||
|
sendMessage.setData(msgInfo); |
||||
|
sendMessage.setSendResult(true); |
||||
|
imClient.sendPrivateMessage(sendMessage); |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue