移动app
This commit is contained in:
@@ -18,26 +18,26 @@
|
||||
|
||||
<dependencies>
|
||||
<!-- rabbitmq -->
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>org.springframework.boot</groupId>-->
|
||||
<!-- <artifactId>spring-boot-starter-amqp</artifactId>-->
|
||||
<!-- </dependency>-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-amqp</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>com.github.sgroschupf</groupId>-->
|
||||
<!-- <artifactId>zkclient</artifactId>-->
|
||||
<!-- </dependency>-->
|
||||
<dependency>
|
||||
<groupId>com.github.sgroschupf</groupId>
|
||||
<artifactId>zkclient</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- redis -->
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>org.springframework.boot</groupId>-->
|
||||
<!-- <artifactId>spring-boot-starter-data-redis</artifactId>-->
|
||||
<!-- </dependency>-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- spring boot web -->
|
||||
<dependency>
|
||||
@@ -45,11 +45,11 @@
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>com.lld</groupId>-->
|
||||
<!-- <artifactId>codec</artifactId>-->
|
||||
<!-- <version>1.0.0-SNAPSHOT</version>-->
|
||||
<!-- </dependency>-->
|
||||
<dependency>
|
||||
<groupId>com.lld</groupId>
|
||||
<artifactId>im-codec</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
<!-- hutool -->
|
||||
<dependency>
|
||||
@@ -87,6 +87,12 @@
|
||||
<artifactId>common</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.lld</groupId>
|
||||
<artifactId>im-system</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
@@ -98,4 +104,4 @@
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
</project>
|
||||
|
||||
@@ -4,8 +4,10 @@ import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
@SpringBootApplication(scanBasePackages = {"com.lld.im.service",
|
||||
"com.lld.im.common"})
|
||||
@MapperScan("com.lld.im.service.*.dao.mapper")
|
||||
//导入用户资料,删除用户资料,修改用户资料,查询用户资料
|
||||
public class Application {
|
||||
|
||||
public static void main(String[] args) {
|
||||
@@ -17,4 +19,3 @@ public class Application {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,22 @@
|
||||
package com.lld.im.service.config;
|
||||
|
||||
import com.lld.im.common.config.AppConfig;
|
||||
import com.lld.im.common.enums.ImUrlRouteWayEnum;
|
||||
import com.lld.im.common.enums.RouteHashMethodEnum;
|
||||
import com.lld.im.common.route.RouteHandle;
|
||||
import com.lld.im.common.route.algorithm.consistenthash.AbstractConsistentHash;
|
||||
import com.lld.im.common.route.algorithm.consistenthash.ConsistentHashHandle;
|
||||
import com.lld.im.common.route.algorithm.consistenthash.TreeMapConsistentHash;
|
||||
import com.lld.im.common.route.algorithm.loop.LoopHandle;
|
||||
import com.lld.im.common.route.algorithm.random.RandomHandle;
|
||||
import com.lld.im.service.utils.SnowflakeIdWorker;
|
||||
import org.I0Itec.zkclient.ZkClient;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* @description:
|
||||
* @author: lld
|
||||
@@ -10,4 +25,50 @@ import org.springframework.context.annotation.Configuration;
|
||||
@Configuration
|
||||
public class BeanConfig {
|
||||
|
||||
@Autowired
|
||||
AppConfig appConfig;
|
||||
|
||||
@Bean
|
||||
public ZkClient buildZKClient() {
|
||||
return new ZkClient(appConfig.getZkAddr(),
|
||||
appConfig.getZkConnectTimeOut());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RouteHandle routeHandle() throws Exception {
|
||||
|
||||
Integer imRouteWay = appConfig.getImRouteWay();
|
||||
String routWay = "";
|
||||
|
||||
ImUrlRouteWayEnum handler = ImUrlRouteWayEnum.getHandler(imRouteWay);
|
||||
routWay = handler.getClazz();
|
||||
|
||||
RouteHandle routeHandle = (RouteHandle) Class.forName(routWay).newInstance();
|
||||
if(handler == ImUrlRouteWayEnum.HASH){
|
||||
|
||||
Method setHash = Class.forName(routWay).getMethod("setHash", AbstractConsistentHash.class);
|
||||
Integer consistentHashWay = appConfig.getConsistentHashWay();
|
||||
String hashWay = "";
|
||||
|
||||
RouteHashMethodEnum hashHandler = RouteHashMethodEnum.getHandler(consistentHashWay);
|
||||
hashWay = hashHandler.getClazz();
|
||||
AbstractConsistentHash consistentHash
|
||||
= (AbstractConsistentHash) Class.forName(hashWay).newInstance();
|
||||
setHash.invoke(routeHandle,consistentHash);
|
||||
}
|
||||
|
||||
return routeHandle;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public EasySqlInjector easySqlInjector () {
|
||||
return new EasySqlInjector();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SnowflakeIdWorker buildSnowflakeSeq() throws Exception {
|
||||
return new SnowflakeIdWorker(0);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.lld.im.service.config;
|
||||
|
||||
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
|
||||
import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector;
|
||||
import com.baomidou.mybatisplus.extension.injector.methods.InsertBatchSomeColumn;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class EasySqlInjector extends DefaultSqlInjector {
|
||||
@Override
|
||||
public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
|
||||
List<AbstractMethod> methodList = super.getMethodList(mapperClass);
|
||||
methodList.add(new InsertBatchSomeColumn()); // 添加InsertBatchSomeColumn方法
|
||||
return methodList;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.lld.im.service.config;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.PropertyAccessor;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
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.Jackson2JsonRedisSerializer;
|
||||
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||
|
||||
/**
|
||||
* @author: Chackylee
|
||||
* @description:
|
||||
**/
|
||||
@Configuration
|
||||
public class RedisConfig {
|
||||
|
||||
@Autowired
|
||||
RedisConnectionFactory redisConnectionFactory;
|
||||
|
||||
@Bean
|
||||
public RedisTemplate<Object, Object> redisTemplate() {
|
||||
RedisTemplate<Object, Object> template = new RedisTemplate<>();
|
||||
template.setConnectionFactory(redisConnectionFactory);
|
||||
|
||||
//使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值
|
||||
//Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer(Object.class);
|
||||
//使用Fastjson2JsonRedisSerializer来序列化和反序列化redis的value值 by zhengkai
|
||||
Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer(Object.class);
|
||||
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
|
||||
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
|
||||
serializer.setObjectMapper(mapper);
|
||||
|
||||
template.setValueSerializer(new StringRedisSerializer());
|
||||
//使用StringRedisSerializer来序列化和反序列化redis的key值
|
||||
template.setKeySerializer(new StringRedisSerializer());
|
||||
template.afterPropertiesSet();
|
||||
template.setHashKeySerializer(new StringRedisSerializer());
|
||||
template.setHashValueSerializer(serializer);
|
||||
|
||||
return template;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,10 @@
|
||||
package com.lld.im.service.config;
|
||||
|
||||
import com.lld.im.service.interceptor.GateWayInterceptor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
/**
|
||||
@@ -12,6 +15,16 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
@Configuration
|
||||
public class WebConfig implements WebMvcConfigurer {
|
||||
|
||||
@Autowired
|
||||
GateWayInterceptor gateWayInterceptor;
|
||||
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
registry.addInterceptor(gateWayInterceptor)
|
||||
.addPathPatterns("/**")
|
||||
.excludePathPatterns("/v1/user/login")
|
||||
.excludePathPatterns("/v1/message/checkSend");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addCorsMappings(CorsRegistry registry) {
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.lld.im.service.conversation.controller;
|
||||
|
||||
import com.lld.im.common.ResponseVO;
|
||||
import com.lld.im.common.model.SyncReq;
|
||||
import com.lld.im.service.conversation.model.DeleteConversationReq;
|
||||
import com.lld.im.service.conversation.model.UpdateConversationReq;
|
||||
import com.lld.im.service.conversation.service.ConversationService;
|
||||
import com.lld.im.service.group.model.req.ImportGroupReq;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* @description:
|
||||
* @author: lld
|
||||
* @version: 1.0
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("v1/conversation")
|
||||
public class ConversationController {
|
||||
|
||||
@Autowired
|
||||
ConversationService conversationService;
|
||||
|
||||
@RequestMapping("/deleteConversation")
|
||||
public ResponseVO deleteConversation(@RequestBody @Validated DeleteConversationReq
|
||||
req, Integer appId, String identifier) {
|
||||
req.setAppId(appId);
|
||||
// req.setOperater(identifier);
|
||||
return conversationService.deleteConversation(req);
|
||||
}
|
||||
|
||||
@RequestMapping("/updateConversation")
|
||||
public ResponseVO updateConversation(@RequestBody @Validated UpdateConversationReq
|
||||
req, Integer appId, String identifier) {
|
||||
req.setAppId(appId);
|
||||
// req.setOperater(identifier);
|
||||
return conversationService.updateConversation(req);
|
||||
}
|
||||
|
||||
@RequestMapping("/syncConversationList")
|
||||
public ResponseVO syncFriendShipList(@RequestBody @Validated SyncReq req, Integer appId) {
|
||||
req.setAppId(appId);
|
||||
return conversationService.syncConversationSet(req);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.lld.im.service.conversation.dao;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author: Chackylee
|
||||
* @description:
|
||||
**/
|
||||
@Data
|
||||
@TableName("im_conversation_set")
|
||||
public class ImConversationSetEntity {
|
||||
|
||||
//会话id 0_fromId_toId
|
||||
private String conversationId;
|
||||
|
||||
//会话类型
|
||||
private Integer conversationType;
|
||||
|
||||
private String fromId;
|
||||
|
||||
private String toId;
|
||||
|
||||
private int isMute;
|
||||
|
||||
private int isTop;
|
||||
|
||||
private Long sequence;
|
||||
|
||||
private Long readedSequence;
|
||||
|
||||
private Integer appId;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.lld.im.service.conversation.dao.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.lld.im.service.conversation.dao.ImConversationSetEntity;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
import org.apache.ibatis.annotations.Update;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
/**
|
||||
* @description:
|
||||
* @author: lld
|
||||
* @version: 1.0
|
||||
*/
|
||||
@Repository
|
||||
public interface ImConversationSetMapper extends BaseMapper<ImConversationSetEntity> {
|
||||
|
||||
@Update(" update im_conversation_set set readed_sequence = #{readedSequence},sequence = #{sequence} " +
|
||||
" where conversation_id = #{conversationId} and app_id = #{appId} AND readed_sequence < #{readedSequence}")
|
||||
public void readMark(ImConversationSetEntity imConversationSetEntity);
|
||||
|
||||
@Select(" select max(sequence) from im_conversation_set where app_id = #{appId} AND from_id = #{userId} ")
|
||||
Long geConversationSetMaxSeq(Integer appId, String userId);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.lld.im.service.conversation.model;
|
||||
|
||||
import com.lld.im.common.model.RequestBase;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
/**
|
||||
* @description:
|
||||
* @author: lld
|
||||
* @version: 1.0
|
||||
*/
|
||||
@Data
|
||||
public class DeleteConversationReq extends RequestBase {
|
||||
|
||||
@NotBlank(message = "会话id不能为空")
|
||||
private String conversationId;
|
||||
|
||||
@NotBlank(message = "fromId不能为空")
|
||||
private String fromId;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.lld.im.service.conversation.model;
|
||||
|
||||
import com.lld.im.common.model.RequestBase;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @description:
|
||||
* @author: lld
|
||||
* @version: 1.0
|
||||
*/
|
||||
@Data
|
||||
public class UpdateConversationReq extends RequestBase {
|
||||
|
||||
private String conversationId;
|
||||
|
||||
private Integer isMute;
|
||||
|
||||
private Integer isTop;
|
||||
|
||||
private String fromId;
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
package com.lld.im.service.conversation.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
|
||||
import com.lld.im.codec.pack.conversation.DeleteConversationPack;
|
||||
import com.lld.im.codec.pack.conversation.UpdateConversationPack;
|
||||
import com.lld.im.common.ResponseVO;
|
||||
import com.lld.im.common.config.AppConfig;
|
||||
import com.lld.im.common.constant.Constants;
|
||||
import com.lld.im.common.enums.ConversationErrorCode;
|
||||
import com.lld.im.common.enums.ConversationTypeEnum;
|
||||
import com.lld.im.common.enums.command.ConversationEventCommand;
|
||||
import com.lld.im.common.model.ClientInfo;
|
||||
import com.lld.im.common.model.SyncReq;
|
||||
import com.lld.im.common.model.SyncResp;
|
||||
import com.lld.im.common.model.message.MessageReadedContent;
|
||||
import com.lld.im.service.conversation.dao.ImConversationSetEntity;
|
||||
import com.lld.im.service.conversation.dao.mapper.ImConversationSetMapper;
|
||||
import com.lld.im.service.conversation.model.DeleteConversationReq;
|
||||
import com.lld.im.service.conversation.model.UpdateConversationReq;
|
||||
import com.lld.im.service.friendship.dao.ImFriendShipEntity;
|
||||
import com.lld.im.service.seq.RedisSeq;
|
||||
import com.lld.im.service.utils.MessageProducer;
|
||||
import com.lld.im.service.utils.WriteUserSeq;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @description:
|
||||
* @author: lld
|
||||
* @version: 1.0
|
||||
*/
|
||||
@Service
|
||||
public class ConversationService {
|
||||
|
||||
@Autowired
|
||||
ImConversationSetMapper imConversationSetMapper;
|
||||
|
||||
@Autowired
|
||||
MessageProducer messageProducer;
|
||||
|
||||
@Autowired
|
||||
AppConfig appConfig;
|
||||
|
||||
@Autowired
|
||||
RedisSeq redisSeq;
|
||||
|
||||
@Autowired
|
||||
WriteUserSeq writeUserSeq;
|
||||
|
||||
public String convertConversationId(Integer type,String fromId,String toId){
|
||||
return type + "_" + fromId + "_" + toId;
|
||||
}
|
||||
|
||||
public void messageMarkRead(MessageReadedContent messageReadedContent){
|
||||
|
||||
String toId = messageReadedContent.getToId();
|
||||
if(messageReadedContent.getConversationType() == ConversationTypeEnum.GROUP.getCode()){
|
||||
toId = messageReadedContent.getGroupId();
|
||||
}
|
||||
String conversationId = convertConversationId(messageReadedContent.getConversationType(),
|
||||
messageReadedContent.getFromId(), toId);
|
||||
QueryWrapper<ImConversationSetEntity> query = new QueryWrapper<>();
|
||||
query.eq("conversation_id",conversationId);
|
||||
query.eq("app_id",messageReadedContent.getAppId());
|
||||
ImConversationSetEntity imConversationSetEntity = imConversationSetMapper.selectOne(query);
|
||||
if(imConversationSetEntity == null){
|
||||
imConversationSetEntity = new ImConversationSetEntity();
|
||||
long seq = redisSeq.doGetSeq(messageReadedContent.getAppId() + ":" + Constants.SeqConstants.Conversation);
|
||||
imConversationSetEntity.setConversationId(conversationId);
|
||||
BeanUtils.copyProperties(messageReadedContent,imConversationSetEntity);
|
||||
imConversationSetEntity.setReadedSequence(messageReadedContent.getMessageSequence());
|
||||
imConversationSetEntity.setToId(toId);
|
||||
imConversationSetEntity.setSequence(seq);
|
||||
imConversationSetMapper.insert(imConversationSetEntity);
|
||||
writeUserSeq.writeUserSeq(messageReadedContent.getAppId(),
|
||||
messageReadedContent.getFromId(),Constants.SeqConstants.Conversation,seq);
|
||||
}else{
|
||||
long seq = redisSeq.doGetSeq(messageReadedContent.getAppId() + ":" + Constants.SeqConstants.Conversation);
|
||||
imConversationSetEntity.setSequence(seq);
|
||||
imConversationSetEntity.setReadedSequence(messageReadedContent.getMessageSequence());
|
||||
imConversationSetMapper.readMark(imConversationSetEntity);
|
||||
writeUserSeq.writeUserSeq(messageReadedContent.getAppId(),
|
||||
messageReadedContent.getFromId(),Constants.SeqConstants.Conversation,seq);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 删除会话
|
||||
* @param
|
||||
* @return com.lld.im.common.ResponseVO
|
||||
* @author lld
|
||||
*/
|
||||
public ResponseVO deleteConversation(DeleteConversationReq req){
|
||||
|
||||
//置顶 有免打扰
|
||||
// QueryWrapper<ImConversationSetEntity> queryWrapper = new QueryWrapper<>();
|
||||
// queryWrapper.eq("conversation_id",req.getConversationId());
|
||||
// queryWrapper.eq("app_id",req.getAppId());
|
||||
// ImConversationSetEntity imConversationSetEntity = imConversationSetMapper.selectOne(queryWrapper);
|
||||
// if(imConversationSetEntity != null){
|
||||
// imConversationSetEntity.setIsMute(0);
|
||||
// imConversationSetEntity.setIsTop(0);
|
||||
// imConversationSetMapper.update(imConversationSetEntity,queryWrapper);
|
||||
// }
|
||||
|
||||
if(appConfig.getDeleteConversationSyncMode() == 1){
|
||||
DeleteConversationPack pack = new DeleteConversationPack();
|
||||
pack.setConversationId(req.getConversationId());
|
||||
messageProducer.sendToUserExceptClient(req.getFromId(),
|
||||
ConversationEventCommand.CONVERSATION_DELETE,
|
||||
pack,new ClientInfo(req.getAppId(),req.getClientType(),
|
||||
req.getImei()));
|
||||
}
|
||||
return ResponseVO.successResponse();
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 更新会话 置顶or免打扰
|
||||
* @param
|
||||
* @return com.lld.im.common.ResponseVO
|
||||
* @author lld
|
||||
*/
|
||||
public ResponseVO updateConversation(UpdateConversationReq req){
|
||||
|
||||
|
||||
|
||||
|
||||
if(req.getIsTop() == null && req.getIsMute() == null){
|
||||
return ResponseVO.errorResponse(ConversationErrorCode.CONVERSATION_UPDATE_PARAM_ERROR);
|
||||
}
|
||||
QueryWrapper<ImConversationSetEntity> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("conversation_id",req.getConversationId());
|
||||
queryWrapper.eq("app_id",req.getAppId());
|
||||
ImConversationSetEntity imConversationSetEntity = imConversationSetMapper.selectOne(queryWrapper);
|
||||
if(imConversationSetEntity != null){
|
||||
long seq = redisSeq.doGetSeq(req.getAppId() + ":" + Constants.SeqConstants.Conversation);
|
||||
|
||||
if(req.getIsMute() != null){
|
||||
imConversationSetEntity.setIsTop(req.getIsTop());
|
||||
}
|
||||
if(req.getIsMute() != null){
|
||||
imConversationSetEntity.setIsMute(req.getIsMute());
|
||||
}
|
||||
imConversationSetEntity.setSequence(seq);
|
||||
imConversationSetMapper.update(imConversationSetEntity,queryWrapper);
|
||||
writeUserSeq.writeUserSeq(req.getAppId(), req.getFromId(),
|
||||
Constants.SeqConstants.Conversation, seq);
|
||||
|
||||
UpdateConversationPack pack = new UpdateConversationPack();
|
||||
pack.setConversationId(req.getConversationId());
|
||||
pack.setIsMute(imConversationSetEntity.getIsMute());
|
||||
pack.setIsTop(imConversationSetEntity.getIsTop());
|
||||
pack.setSequence(seq);
|
||||
pack.setConversationType(imConversationSetEntity.getConversationType());
|
||||
messageProducer.sendToUserExceptClient(req.getFromId(),
|
||||
ConversationEventCommand.CONVERSATION_UPDATE,
|
||||
pack,new ClientInfo(req.getAppId(),req.getClientType(),
|
||||
req.getImei()));
|
||||
}
|
||||
return ResponseVO.successResponse();
|
||||
}
|
||||
|
||||
public ResponseVO syncConversationSet(SyncReq req) {
|
||||
if(req.getMaxLimit() > 100){
|
||||
req.setMaxLimit(100);
|
||||
}
|
||||
|
||||
SyncResp<ImConversationSetEntity> resp = new SyncResp<>();
|
||||
//seq > req.getseq limit maxLimit
|
||||
QueryWrapper<ImConversationSetEntity> queryWrapper =
|
||||
new QueryWrapper<>();
|
||||
queryWrapper.eq("from_id",req.getOperater());
|
||||
queryWrapper.gt("sequence",req.getLastSequence());
|
||||
queryWrapper.eq("app_id",req.getAppId());
|
||||
queryWrapper.last(" limit " + req.getMaxLimit());
|
||||
queryWrapper.orderByAsc("sequence");
|
||||
List<ImConversationSetEntity> list = imConversationSetMapper
|
||||
.selectList(queryWrapper);
|
||||
|
||||
if(!CollectionUtils.isEmpty(list)){
|
||||
ImConversationSetEntity maxSeqEntity = list.get(list.size() - 1);
|
||||
resp.setDataList(list);
|
||||
//设置最大seq
|
||||
Long friendShipMaxSeq = imConversationSetMapper.geConversationSetMaxSeq(req.getAppId(), req.getOperater());
|
||||
resp.setMaxSequence(friendShipMaxSeq);
|
||||
//设置是否拉取完毕
|
||||
resp.setCompleted(maxSeqEntity.getSequence() >= friendShipMaxSeq);
|
||||
return ResponseVO.successResponse(resp);
|
||||
}
|
||||
|
||||
resp.setCompleted(true);
|
||||
return ResponseVO.successResponse(resp);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@ public interface ImFriendShipMapper extends BaseMapper<ImFriendShipEntity> {
|
||||
"<foreach collection='toIds' index='index' item='id' separator=',' close = ')' open='(' > " +
|
||||
"#{id}" +
|
||||
"</foreach>" +
|
||||
" and app_id = #{appId} " +
|
||||
"</script>")
|
||||
public List<CheckFriendShipResp> checkFriendShip(CheckFriendShipReq req);
|
||||
|
||||
@@ -88,4 +89,11 @@ public interface ImFriendShipMapper extends BaseMapper<ImFriendShipEntity> {
|
||||
)
|
||||
List<CheckFriendShipResp> checkFriendShipBlackBoth(CheckFriendShipReq toId);
|
||||
|
||||
@Select(" select max(friend_sequence) from im_friendship where app_id = #{appId} AND from_id = #{userId} ")
|
||||
Long getFriendShipMaxSeq(Integer appId,String userId);
|
||||
|
||||
@Select(
|
||||
" select to_id from im_friendship where from_id = #{userId} AND app_id = #{appId} and status = 1 and black = 1 "
|
||||
)
|
||||
List<String> getAllFriendId(String userId,Integer appId);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.lld.im.service.friendship.model.callback;
|
||||
|
||||
import com.lld.im.service.friendship.model.req.FriendDto;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @description:
|
||||
* @author: lld
|
||||
* @version: 1.0
|
||||
*/
|
||||
@Data
|
||||
public class AddFriendAfterCallbackDto {
|
||||
|
||||
private String fromId;
|
||||
|
||||
private FriendDto toItem;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.lld.im.service.friendship.model.callback;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @description:
|
||||
* @author: lld
|
||||
* @version: 1.0
|
||||
*/
|
||||
@Data
|
||||
public class AddFriendBlackAfterCallbackDto {
|
||||
|
||||
private String fromId;
|
||||
|
||||
private String toId;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.lld.im.service.friendship.model.callback;
|
||||
|
||||
import com.lld.im.service.friendship.model.req.FriendDto;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @description:
|
||||
* @author: lld
|
||||
* @version: 1.0
|
||||
*/
|
||||
@Data
|
||||
public class DeleteFriendAfterCallbackDto {
|
||||
|
||||
private String fromId;
|
||||
|
||||
private String toId;
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package com.lld.im.service.friendship.service;
|
||||
|
||||
import com.lld.im.common.ResponseVO;
|
||||
import com.lld.im.common.model.RequestBase;
|
||||
import com.lld.im.common.model.SyncReq;
|
||||
import com.lld.im.service.friendship.model.req.*;
|
||||
|
||||
import java.util.List;
|
||||
@@ -37,4 +38,7 @@ public interface ImFriendService {
|
||||
|
||||
public ResponseVO checkBlck(CheckFriendShipReq req);
|
||||
|
||||
public ResponseVO syncFriendshipList(SyncReq req);
|
||||
|
||||
public List<String> getAllFriendId(String userId, Integer appId);
|
||||
}
|
||||
|
||||
@@ -17,5 +17,5 @@ public interface ImFriendShipGroupService {
|
||||
|
||||
public ResponseVO<ImFriendShipGroupEntity> getGroup(String fromId, String groupName, Integer appId);
|
||||
|
||||
|
||||
public Long updateSeq(String fromId, String groupName, Integer appId);
|
||||
}
|
||||
|
||||
@@ -4,22 +4,36 @@ import com.alibaba.fastjson.JSONObject;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
|
||||
import com.lld.im.codec.pack.friendship.*;
|
||||
import com.lld.im.codec.proto.Message;
|
||||
import com.lld.im.common.ResponseVO;
|
||||
import com.lld.im.common.config.AppConfig;
|
||||
import com.lld.im.common.constant.Constants;
|
||||
import com.lld.im.common.enums.AllowFriendTypeEnum;
|
||||
import com.lld.im.common.enums.CheckFriendShipTypeEnum;
|
||||
import com.lld.im.common.enums.FriendShipErrorCode;
|
||||
import com.lld.im.common.enums.FriendShipStatusEnum;
|
||||
import com.lld.im.common.enums.command.FriendshipEventCommand;
|
||||
import com.lld.im.common.exception.ApplicationException;
|
||||
import com.lld.im.common.model.RequestBase;
|
||||
import com.lld.im.common.model.SyncReq;
|
||||
import com.lld.im.common.model.SyncResp;
|
||||
import com.lld.im.service.friendship.dao.ImFriendShipEntity;
|
||||
import com.lld.im.service.friendship.dao.mapper.ImFriendShipMapper;
|
||||
import com.lld.im.service.friendship.model.callback.AddFriendAfterCallbackDto;
|
||||
import com.lld.im.service.friendship.model.callback.AddFriendBlackAfterCallbackDto;
|
||||
import com.lld.im.service.friendship.model.callback.DeleteFriendAfterCallbackDto;
|
||||
import com.lld.im.service.friendship.model.req.*;
|
||||
import com.lld.im.service.friendship.model.resp.CheckFriendShipResp;
|
||||
import com.lld.im.service.friendship.model.resp.ImportFriendShipResp;
|
||||
import com.lld.im.service.friendship.service.ImFriendService;
|
||||
import com.lld.im.service.friendship.service.ImFriendShipRequestService;
|
||||
import com.lld.im.service.seq.RedisSeq;
|
||||
import com.lld.im.service.user.dao.ImUserDataEntity;
|
||||
import com.lld.im.service.user.service.ImUserService;
|
||||
import com.lld.im.service.utils.CallbackService;
|
||||
import com.lld.im.service.utils.MessageProducer;
|
||||
import com.lld.im.service.utils.WriteUserSeq;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -46,12 +60,27 @@ public class ImFriendServiceImpl implements ImFriendService {
|
||||
@Autowired
|
||||
ImUserService imUserService;
|
||||
|
||||
@Autowired
|
||||
AppConfig appConfig;
|
||||
|
||||
@Autowired
|
||||
CallbackService callbackService;
|
||||
|
||||
@Autowired
|
||||
MessageProducer messageProducer;
|
||||
|
||||
@Autowired
|
||||
ImFriendService imFriendService;
|
||||
|
||||
@Autowired
|
||||
ImFriendShipRequestService imFriendShipRequestService;
|
||||
|
||||
@Autowired
|
||||
RedisSeq redisSeq;
|
||||
|
||||
@Autowired
|
||||
WriteUserSeq writeUserSeq;
|
||||
|
||||
@Override
|
||||
public ResponseVO importFriendShip(ImporFriendShipReq req) {
|
||||
|
||||
@@ -101,6 +130,16 @@ public class ImFriendServiceImpl implements ImFriendService {
|
||||
return toInfo;
|
||||
}
|
||||
|
||||
if(appConfig.isAddFriendBeforeCallback()){
|
||||
ResponseVO callbackResp = callbackService
|
||||
.beforeCallback(req.getAppId(),
|
||||
Constants.CallbackCommand.AddFriendBefore
|
||||
,JSONObject.toJSONString(req));
|
||||
if(!callbackResp.isOk()){
|
||||
return callbackResp;
|
||||
}
|
||||
}
|
||||
|
||||
ImUserDataEntity data = toInfo.getData();
|
||||
|
||||
if(data.getFriendAllowType() != null && data.getFriendAllowType() == AllowFriendTypeEnum.NOT_NEED.getCode()){
|
||||
@@ -141,6 +180,23 @@ public class ImFriendServiceImpl implements ImFriendService {
|
||||
}
|
||||
|
||||
ResponseVO responseVO = this.doUpdate(req.getFromId(), req.getToItem(), req.getAppId());
|
||||
if(responseVO.isOk()){
|
||||
UpdateFriendPack updateFriendPack = new UpdateFriendPack();
|
||||
updateFriendPack.setRemark(req.getToItem().getRemark());
|
||||
updateFriendPack.setToId(req.getToItem().getToId());
|
||||
messageProducer.sendToUser(req.getFromId(),
|
||||
req.getClientType(),req.getImei(),FriendshipEventCommand
|
||||
.FRIEND_UPDATE,updateFriendPack,req.getAppId());
|
||||
|
||||
if (appConfig.isModifyFriendAfterCallback()) {
|
||||
AddFriendAfterCallbackDto callbackDto = new AddFriendAfterCallbackDto();
|
||||
callbackDto.setFromId(req.getFromId());
|
||||
callbackDto.setToItem(req.getToItem());
|
||||
callbackService.beforeCallback(req.getAppId(),
|
||||
Constants.CallbackCommand.UpdateFriendAfter, JSONObject
|
||||
.toJSONString(callbackDto));
|
||||
}
|
||||
}
|
||||
return responseVO;
|
||||
}
|
||||
|
||||
@@ -148,9 +204,11 @@ public class ImFriendServiceImpl implements ImFriendService {
|
||||
public ResponseVO doUpdate(String fromId, FriendDto dto,Integer appId){
|
||||
|
||||
|
||||
long seq = redisSeq.doGetSeq(appId + ":" + Constants.SeqConstants.Friendship);
|
||||
UpdateWrapper<ImFriendShipEntity> updateWrapper = new UpdateWrapper<>();
|
||||
updateWrapper.lambda().set(ImFriendShipEntity::getAddSource,dto.getAddSource())
|
||||
.set(ImFriendShipEntity::getExtra,dto.getExtra())
|
||||
.set(ImFriendShipEntity::getFriendSequence,seq)
|
||||
.set(ImFriendShipEntity::getRemark,dto.getRemark())
|
||||
.eq(ImFriendShipEntity::getAppId,appId)
|
||||
.eq(ImFriendShipEntity::getToId,dto.getToId())
|
||||
@@ -158,13 +216,22 @@ public class ImFriendServiceImpl implements ImFriendService {
|
||||
|
||||
int update = imFriendShipMapper.update(null, updateWrapper);
|
||||
if(update == 1){
|
||||
//之后回调
|
||||
// if (appConfig.isModifyFriendAfterCallback()){
|
||||
// AddFriendAfterCallbackDto callbackDto = new AddFriendAfterCallbackDto();
|
||||
// callbackDto.setFromId(fromId);
|
||||
// callbackDto.setToItem(dto);
|
||||
// callbackService.beforeCallback(appId,
|
||||
// Constants.CallbackCommand.UpdateFriendAfter, JSONObject
|
||||
// .toJSONString(callbackDto));
|
||||
// }
|
||||
writeUserSeq.writeUserSeq(appId,fromId,Constants.SeqConstants.Friendship,seq);
|
||||
return ResponseVO.successResponse();
|
||||
}
|
||||
|
||||
return ResponseVO.errorResponse();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public ResponseVO doAddFriend(RequestBase requestBase,String fromId, FriendDto dto, Integer appId){
|
||||
|
||||
@@ -177,10 +244,13 @@ public class ImFriendServiceImpl implements ImFriendService {
|
||||
query.eq("from_id",fromId);
|
||||
query.eq("to_id",dto.getToId());
|
||||
ImFriendShipEntity fromItem = imFriendShipMapper.selectOne(query);
|
||||
long seq = 0L;
|
||||
if(fromItem == null){
|
||||
//走添加逻辑。
|
||||
fromItem = new ImFriendShipEntity();
|
||||
seq = redisSeq.doGetSeq(appId+":"+Constants.SeqConstants.Friendship);
|
||||
fromItem.setAppId(appId);
|
||||
fromItem.setFriendSequence(seq);
|
||||
fromItem.setFromId(fromId);
|
||||
// entity.setToId(to);
|
||||
BeanUtils.copyProperties(dto,fromItem);
|
||||
@@ -190,8 +260,10 @@ public class ImFriendServiceImpl implements ImFriendService {
|
||||
if(insert != 1){
|
||||
return ResponseVO.errorResponse(FriendShipErrorCode.ADD_FRIEND_ERROR);
|
||||
}
|
||||
writeUserSeq.writeUserSeq(appId,fromId,Constants.SeqConstants.Friendship,seq);
|
||||
} else{
|
||||
//如果存在则判断状态,如果是已添加,则提示已添加,如果是未添加,则修改状态
|
||||
|
||||
if(fromItem.getStatus() == FriendShipStatusEnum.FRIEND_STATUS_NORMAL.getCode()){
|
||||
return ResponseVO.errorResponse(FriendShipErrorCode.TO_IS_YOUR_FRIEND);
|
||||
} else{
|
||||
@@ -208,12 +280,15 @@ public class ImFriendServiceImpl implements ImFriendService {
|
||||
if(StringUtils.isNotBlank(dto.getExtra())){
|
||||
update.setExtra(dto.getExtra());
|
||||
}
|
||||
seq = redisSeq.doGetSeq(appId+":"+Constants.SeqConstants.Friendship);
|
||||
update.setFriendSequence(seq);
|
||||
update.setStatus(FriendShipStatusEnum.FRIEND_STATUS_NORMAL.getCode());
|
||||
|
||||
int result = imFriendShipMapper.update(update, query);
|
||||
if(result != 1){
|
||||
return ResponseVO.errorResponse(FriendShipErrorCode.ADD_FRIEND_ERROR);
|
||||
}
|
||||
writeUserSeq.writeUserSeq(appId,fromId,Constants.SeqConstants.Friendship,seq);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -229,18 +304,53 @@ public class ImFriendServiceImpl implements ImFriendService {
|
||||
toItem.setFromId(dto.getToId());
|
||||
BeanUtils.copyProperties(dto,toItem);
|
||||
toItem.setToId(fromId);
|
||||
toItem.setFriendSequence(seq);
|
||||
toItem.setStatus(FriendShipStatusEnum.FRIEND_STATUS_NORMAL.getCode());
|
||||
toItem.setCreateTime(System.currentTimeMillis());
|
||||
// toItem.setBlack(FriendShipStatusEnum.BLACK_STATUS_NORMAL.getCode());
|
||||
int insert = imFriendShipMapper.insert(toItem);
|
||||
writeUserSeq.writeUserSeq(appId,dto.getToId(),Constants.SeqConstants.Friendship,seq);
|
||||
}else{
|
||||
if(FriendShipStatusEnum.FRIEND_STATUS_NORMAL.getCode() !=
|
||||
toItem.getStatus()){
|
||||
ImFriendShipEntity update = new ImFriendShipEntity();
|
||||
update.setFriendSequence(seq);
|
||||
update.setStatus(FriendShipStatusEnum.FRIEND_STATUS_NORMAL.getCode());
|
||||
imFriendShipMapper.update(update,toQuery);
|
||||
writeUserSeq.writeUserSeq(appId,dto.getToId(),Constants.SeqConstants.Friendship,seq);
|
||||
}
|
||||
}
|
||||
|
||||
//发送给from
|
||||
AddFriendPack addFriendPack = new AddFriendPack();
|
||||
BeanUtils.copyProperties(fromItem,addFriendPack);
|
||||
addFriendPack.setSequence(seq);
|
||||
if(requestBase != null){
|
||||
messageProducer.sendToUser(fromId,requestBase.getClientType(),
|
||||
requestBase.getImei(), FriendshipEventCommand.FRIEND_ADD,addFriendPack
|
||||
,requestBase.getAppId());
|
||||
}else {
|
||||
messageProducer.sendToUser(fromId,
|
||||
FriendshipEventCommand.FRIEND_ADD,addFriendPack
|
||||
,requestBase.getAppId());
|
||||
}
|
||||
|
||||
AddFriendPack addFriendToPack = new AddFriendPack();
|
||||
BeanUtils.copyProperties(toItem,addFriendPack);
|
||||
messageProducer.sendToUser(toItem.getFromId(),
|
||||
FriendshipEventCommand.FRIEND_ADD,addFriendToPack
|
||||
,requestBase.getAppId());
|
||||
|
||||
//之后回调
|
||||
if (appConfig.isAddFriendAfterCallback()){
|
||||
AddFriendAfterCallbackDto callbackDto = new AddFriendAfterCallbackDto();
|
||||
callbackDto.setFromId(fromId);
|
||||
callbackDto.setToItem(dto);
|
||||
callbackService.beforeCallback(appId,
|
||||
Constants.CallbackCommand.AddFriendAfter, JSONObject
|
||||
.toJSONString(callbackDto));
|
||||
}
|
||||
|
||||
return ResponseVO.successResponse();
|
||||
}
|
||||
|
||||
@@ -258,8 +368,29 @@ public class ImFriendServiceImpl implements ImFriendService {
|
||||
}else{
|
||||
if(fromItem.getStatus() != null && fromItem.getStatus() == FriendShipStatusEnum.FRIEND_STATUS_NORMAL.getCode()){
|
||||
ImFriendShipEntity update = new ImFriendShipEntity();
|
||||
long seq = redisSeq.doGetSeq(req.getAppId() + ":" + Constants.SeqConstants.Friendship);
|
||||
update.setFriendSequence(seq);
|
||||
update.setStatus(FriendShipStatusEnum.FRIEND_STATUS_DELETE.getCode());
|
||||
imFriendShipMapper.update(update,query);
|
||||
writeUserSeq.writeUserSeq(req.getAppId(),req.getFromId(),Constants.SeqConstants.Friendship,seq);
|
||||
DeleteFriendPack deleteFriendPack = new DeleteFriendPack();
|
||||
deleteFriendPack.setFromId(req.getFromId());
|
||||
deleteFriendPack.setSequence(seq);
|
||||
deleteFriendPack.setToId(req.getToId());
|
||||
messageProducer.sendToUser(req.getFromId(),
|
||||
req.getClientType(), req.getImei(),
|
||||
FriendshipEventCommand.FRIEND_DELETE,
|
||||
deleteFriendPack, req.getAppId());
|
||||
|
||||
//之后回调
|
||||
if (appConfig.isAddFriendAfterCallback()){
|
||||
DeleteFriendAfterCallbackDto callbackDto = new DeleteFriendAfterCallbackDto();
|
||||
callbackDto.setFromId(req.getFromId());
|
||||
callbackDto.setToId(req.getToId());
|
||||
callbackService.beforeCallback(req.getAppId(),
|
||||
Constants.CallbackCommand.DeleteFriendAfter, JSONObject
|
||||
.toJSONString(callbackDto));
|
||||
}
|
||||
|
||||
}else{
|
||||
return ResponseVO.errorResponse(FriendShipErrorCode.FRIEND_IS_DELETED);
|
||||
@@ -278,6 +409,12 @@ public class ImFriendServiceImpl implements ImFriendService {
|
||||
ImFriendShipEntity update = new ImFriendShipEntity();
|
||||
update.setStatus(FriendShipStatusEnum.FRIEND_STATUS_DELETE.getCode());
|
||||
imFriendShipMapper.update(update,query);
|
||||
|
||||
DeleteAllFriendPack deleteFriendPack = new DeleteAllFriendPack();
|
||||
deleteFriendPack.setFromId(req.getFromId());
|
||||
messageProducer.sendToUser(req.getFromId(), req.getClientType(), req.getImei(), FriendshipEventCommand.FRIEND_ALL_DELETE,
|
||||
deleteFriendPack, req.getAppId());
|
||||
|
||||
return ResponseVO.successResponse();
|
||||
}
|
||||
|
||||
@@ -335,6 +472,42 @@ public class ImFriendServiceImpl implements ImFriendService {
|
||||
return ResponseVO.successResponse(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseVO syncFriendshipList(SyncReq req) {
|
||||
|
||||
if(req.getMaxLimit() > 100){
|
||||
req.setMaxLimit(100);
|
||||
}
|
||||
|
||||
SyncResp<ImFriendShipEntity> resp = new SyncResp<>();
|
||||
//seq > req.getseq limit maxLimit
|
||||
QueryWrapper<ImFriendShipEntity> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("from_id",req.getOperater());
|
||||
queryWrapper.gt("friend_sequence",req.getLastSequence());
|
||||
queryWrapper.eq("app_id",req.getAppId());
|
||||
queryWrapper.last(" limit " + req.getMaxLimit());
|
||||
queryWrapper.orderByAsc("friend_sequence");
|
||||
List<ImFriendShipEntity> list = imFriendShipMapper.selectList(queryWrapper);
|
||||
|
||||
if(!CollectionUtils.isEmpty(list)){
|
||||
ImFriendShipEntity maxSeqEntity = list.get(list.size() - 1);
|
||||
resp.setDataList(list);
|
||||
//设置最大seq
|
||||
Long friendShipMaxSeq = imFriendShipMapper.getFriendShipMaxSeq(req.getAppId(), req.getOperater());
|
||||
resp.setMaxSequence(friendShipMaxSeq);
|
||||
//设置是否拉取完毕
|
||||
resp.setCompleted(maxSeqEntity.getFriendSequence() >= friendShipMaxSeq);
|
||||
return ResponseVO.successResponse(resp);
|
||||
}
|
||||
|
||||
resp.setCompleted(true);
|
||||
return ResponseVO.successResponse(resp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getAllFriendId(String userId, Integer appId) {
|
||||
return imFriendShipMapper.getAllFriendId(userId,appId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseVO addBlack(AddFriendShipBlackReq req) {
|
||||
@@ -354,11 +527,15 @@ public class ImFriendServiceImpl implements ImFriendService {
|
||||
query.eq("to_id",req.getToId());
|
||||
|
||||
ImFriendShipEntity fromItem = imFriendShipMapper.selectOne(query);
|
||||
Long seq = 0L;
|
||||
if(fromItem == null){
|
||||
//走添加逻辑。
|
||||
seq = redisSeq.doGetSeq(req.getAppId() + ":" + Constants.SeqConstants.Friendship);
|
||||
|
||||
fromItem = new ImFriendShipEntity();
|
||||
fromItem.setFromId(req.getFromId());
|
||||
fromItem.setToId(req.getToId());
|
||||
fromItem.setFriendSequence(seq);
|
||||
fromItem.setAppId(req.getAppId());
|
||||
fromItem.setBlack(FriendShipStatusEnum.BLACK_STATUS_BLACKED.getCode());
|
||||
fromItem.setCreateTime(System.currentTimeMillis());
|
||||
@@ -366,21 +543,47 @@ public class ImFriendServiceImpl implements ImFriendService {
|
||||
if(insert != 1){
|
||||
return ResponseVO.errorResponse(FriendShipErrorCode.ADD_FRIEND_ERROR);
|
||||
}
|
||||
writeUserSeq.writeUserSeq(req.getAppId(),req.getFromId(),Constants.SeqConstants.Friendship,seq);
|
||||
|
||||
} else{
|
||||
//如果存在则判断状态,如果是拉黑,则提示已拉黑,如果是未拉黑,则修改状态
|
||||
if(fromItem.getBlack() != null && fromItem.getBlack() == FriendShipStatusEnum.BLACK_STATUS_BLACKED.getCode()){
|
||||
return ResponseVO.errorResponse(FriendShipErrorCode.FRIEND_IS_BLACK);
|
||||
} else {
|
||||
}
|
||||
|
||||
else {
|
||||
seq = redisSeq.doGetSeq(req.getAppId() + ":" + Constants.SeqConstants.Friendship);
|
||||
|
||||
ImFriendShipEntity update = new ImFriendShipEntity();
|
||||
update.setFriendSequence(seq);
|
||||
update.setBlack(FriendShipStatusEnum.BLACK_STATUS_BLACKED.getCode());
|
||||
int result = imFriendShipMapper.update(update, query);
|
||||
if(result != 1){
|
||||
return ResponseVO.errorResponse(FriendShipErrorCode.ADD_BLACK_ERROR);
|
||||
}
|
||||
writeUserSeq.writeUserSeq(req.getAppId(),req.getFromId(),Constants.SeqConstants.Friendship,seq);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
AddFriendBlackPack addFriendBlackPack = new AddFriendBlackPack();
|
||||
addFriendBlackPack.setFromId(req.getFromId());
|
||||
addFriendBlackPack.setSequence(seq);
|
||||
addFriendBlackPack.setToId(req.getToId());
|
||||
//发送tcp通知
|
||||
messageProducer.sendToUser(req.getFromId(), req.getClientType(), req.getImei(),
|
||||
FriendshipEventCommand.FRIEND_BLACK_ADD, addFriendBlackPack, req.getAppId());
|
||||
|
||||
//之后回调
|
||||
if (appConfig.isAddFriendShipBlackAfterCallback()){
|
||||
AddFriendBlackAfterCallbackDto callbackDto = new AddFriendBlackAfterCallbackDto();
|
||||
callbackDto.setFromId(req.getFromId());
|
||||
callbackDto.setToId(req.getToId());
|
||||
callbackService.beforeCallback(req.getAppId(),
|
||||
Constants.CallbackCommand.AddBlackAfter, JSONObject
|
||||
.toJSONString(callbackDto));
|
||||
}
|
||||
|
||||
return ResponseVO.successResponse();
|
||||
}
|
||||
|
||||
@@ -395,13 +598,32 @@ public class ImFriendServiceImpl implements ImFriendService {
|
||||
throw new ApplicationException(FriendShipErrorCode.FRIEND_IS_NOT_YOUR_BLACK);
|
||||
}
|
||||
|
||||
long seq = redisSeq.doGetSeq(req.getAppId() + ":" + Constants.SeqConstants.Friendship);
|
||||
|
||||
ImFriendShipEntity update = new ImFriendShipEntity();
|
||||
update.setFriendSequence(seq);
|
||||
update.setBlack(FriendShipStatusEnum.BLACK_STATUS_NORMAL.getCode());
|
||||
int update1 = imFriendShipMapper.update(update, queryFrom);
|
||||
if(update1 == 1){
|
||||
return ResponseVO.successResponse();
|
||||
writeUserSeq.writeUserSeq(req.getAppId(),req.getFromId(),Constants.SeqConstants.Friendship,seq);
|
||||
DeleteBlackPack deleteFriendPack = new DeleteBlackPack();
|
||||
deleteFriendPack.setFromId(req.getFromId());
|
||||
deleteFriendPack.setSequence(seq);
|
||||
deleteFriendPack.setToId(req.getToId());
|
||||
messageProducer.sendToUser(req.getFromId(), req.getClientType(), req.getImei(), FriendshipEventCommand.FRIEND_BLACK_DELETE,
|
||||
deleteFriendPack, req.getAppId());
|
||||
|
||||
//之后回调
|
||||
if (appConfig.isAddFriendShipBlackAfterCallback()){
|
||||
AddFriendBlackAfterCallbackDto callbackDto = new AddFriendBlackAfterCallbackDto();
|
||||
callbackDto.setFromId(req.getFromId());
|
||||
callbackDto.setToId(req.getToId());
|
||||
callbackService.beforeCallback(req.getAppId(),
|
||||
Constants.CallbackCommand.DeleteBlack, JSONObject
|
||||
.toJSONString(callbackDto));
|
||||
}
|
||||
}
|
||||
return ResponseVO.errorResponse();
|
||||
return ResponseVO.successResponse();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -436,4 +658,7 @@ public class ImFriendServiceImpl implements ImFriendService {
|
||||
return ResponseVO.successResponse(resp);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
package com.lld.im.service.friendship.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.lld.im.codec.pack.friendship.AddFriendGroupMemberPack;
|
||||
import com.lld.im.codec.pack.friendship.DeleteFriendGroupMemberPack;
|
||||
import com.lld.im.codec.proto.Message;
|
||||
import com.lld.im.common.ResponseVO;
|
||||
import com.lld.im.common.enums.command.FriendshipEventCommand;
|
||||
import com.lld.im.common.model.ClientInfo;
|
||||
import com.lld.im.service.friendship.dao.ImFriendShipGroupEntity;
|
||||
import com.lld.im.service.friendship.dao.ImFriendShipGroupMemberEntity;
|
||||
import com.lld.im.service.friendship.dao.mapper.ImFriendShipGroupMemberMapper;
|
||||
@@ -11,6 +16,7 @@ import com.lld.im.service.friendship.service.ImFriendShipGroupMemberService;
|
||||
import com.lld.im.service.friendship.service.ImFriendShipGroupService;
|
||||
import com.lld.im.service.user.dao.ImUserDataEntity;
|
||||
import com.lld.im.service.user.service.ImUserService;
|
||||
import com.lld.im.service.utils.MessageProducer;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -38,6 +44,9 @@ public class ImFriendShipGroupMemberServiceImpl
|
||||
@Autowired
|
||||
ImFriendShipGroupMemberService thisService;
|
||||
|
||||
@Autowired
|
||||
MessageProducer messageProducer;
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public ResponseVO addGroupMember(AddFriendShipGroupMemberReq req) {
|
||||
@@ -59,6 +68,15 @@ public class ImFriendShipGroupMemberServiceImpl
|
||||
}
|
||||
}
|
||||
|
||||
Long seq = imFriendShipGroupService.updateSeq(req.getFromId(), req.getGroupName(), req.getAppId());
|
||||
AddFriendGroupMemberPack pack = new AddFriendGroupMemberPack();
|
||||
pack.setFromId(req.getFromId());
|
||||
pack.setGroupName(req.getGroupName());
|
||||
pack.setToIds(successId);
|
||||
pack.setSequence(seq);
|
||||
messageProducer.sendToUserExceptClient(req.getFromId(), FriendshipEventCommand.FRIEND_GROUP_MEMBER_ADD,
|
||||
pack,new ClientInfo(req.getAppId(),req.getClientType(),req.getImei()));
|
||||
|
||||
return ResponseVO.successResponse(successId);
|
||||
}
|
||||
|
||||
@@ -70,17 +88,26 @@ public class ImFriendShipGroupMemberServiceImpl
|
||||
return group;
|
||||
}
|
||||
|
||||
ArrayList list = new ArrayList();
|
||||
List<String> successId = new ArrayList<>();
|
||||
for (String toId : req.getToIds()) {
|
||||
ResponseVO<ImUserDataEntity> singleUserInfo = imUserService.getSingleUserInfo(toId, req.getAppId());
|
||||
if(singleUserInfo.isOk()){
|
||||
int i = deleteGroupMember(group.getData().getGroupId(), toId);
|
||||
if(i == 1){
|
||||
list.add(toId);
|
||||
successId.add(toId);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ResponseVO.successResponse(list);
|
||||
|
||||
Long seq = imFriendShipGroupService.updateSeq(req.getFromId(), req.getGroupName(), req.getAppId());
|
||||
DeleteFriendGroupMemberPack pack = new DeleteFriendGroupMemberPack();
|
||||
pack.setFromId(req.getFromId());
|
||||
pack.setGroupName(req.getGroupName());
|
||||
pack.setToIds(successId);
|
||||
pack.setSequence(seq);
|
||||
messageProducer.sendToUserExceptClient(req.getFromId(), FriendshipEventCommand.FRIEND_GROUP_MEMBER_DELETE,
|
||||
pack,new ClientInfo(req.getAppId(),req.getClientType(),req.getImei()));
|
||||
return ResponseVO.successResponse(successId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -88,6 +115,7 @@ public class ImFriendShipGroupMemberServiceImpl
|
||||
ImFriendShipGroupMemberEntity imFriendShipGroupMemberEntity = new ImFriendShipGroupMemberEntity();
|
||||
imFriendShipGroupMemberEntity.setGroupId(groupId);
|
||||
imFriendShipGroupMemberEntity.setToId(toId);
|
||||
|
||||
try {
|
||||
int insert = imFriendShipGroupMemberMapper.insert(imFriendShipGroupMemberEntity);
|
||||
return insert;
|
||||
|
||||
@@ -2,9 +2,14 @@ package com.lld.im.service.friendship.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.lld.im.codec.pack.friendship.AddFriendGroupPack;
|
||||
import com.lld.im.codec.pack.friendship.DeleteFriendGroupPack;
|
||||
import com.lld.im.common.ResponseVO;
|
||||
import com.lld.im.common.constant.Constants;
|
||||
import com.lld.im.common.enums.DelFlagEnum;
|
||||
import com.lld.im.common.enums.FriendShipErrorCode;
|
||||
import com.lld.im.common.enums.command.FriendshipEventCommand;
|
||||
import com.lld.im.common.model.ClientInfo;
|
||||
import com.lld.im.service.friendship.dao.ImFriendShipGroupEntity;
|
||||
import com.lld.im.service.friendship.dao.mapper.ImFriendShipGroupMapper;
|
||||
import com.lld.im.service.friendship.model.req.AddFriendShipGroupMemberReq;
|
||||
@@ -12,7 +17,10 @@ import com.lld.im.service.friendship.model.req.AddFriendShipGroupReq;
|
||||
import com.lld.im.service.friendship.model.req.DeleteFriendShipGroupReq;
|
||||
import com.lld.im.service.friendship.service.ImFriendShipGroupMemberService;
|
||||
import com.lld.im.service.friendship.service.ImFriendShipGroupService;
|
||||
import com.lld.im.service.seq.RedisSeq;
|
||||
import com.lld.im.service.user.service.ImUserService;
|
||||
import com.lld.im.service.utils.MessageProducer;
|
||||
import com.lld.im.service.utils.WriteUserSeq;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.dao.DuplicateKeyException;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -30,6 +38,15 @@ public class ImFriendShipGroupServiceImpl implements ImFriendShipGroupService {
|
||||
@Autowired
|
||||
ImUserService imUserService;
|
||||
|
||||
@Autowired
|
||||
RedisSeq redisSeq;
|
||||
|
||||
@Autowired
|
||||
MessageProducer messageProducer;
|
||||
|
||||
@Autowired
|
||||
WriteUserSeq writeUserSeq;
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public ResponseVO addGroup(AddFriendShipGroupReq req) {
|
||||
@@ -52,6 +69,8 @@ public class ImFriendShipGroupServiceImpl implements ImFriendShipGroupService {
|
||||
insert.setCreateTime(System.currentTimeMillis());
|
||||
insert.setDelFlag(DelFlagEnum.NORMAL.getCode());
|
||||
insert.setGroupName(req.getGroupName());
|
||||
long seq = redisSeq.doGetSeq(req.getAppId() + ":" + Constants.SeqConstants.FriendshipGroup);
|
||||
insert.setSequence(seq);
|
||||
insert.setFromId(req.getFromId());
|
||||
try {
|
||||
int insert1 = imFriendShipGroupMapper.insert(insert);
|
||||
@@ -59,7 +78,6 @@ public class ImFriendShipGroupServiceImpl implements ImFriendShipGroupService {
|
||||
if (insert1 != 1) {
|
||||
return ResponseVO.errorResponse(FriendShipErrorCode.FRIEND_SHIP_GROUP_CREATE_ERROR);
|
||||
}
|
||||
|
||||
if (insert1 == 1 && CollectionUtil.isNotEmpty(req.getToIds())) {
|
||||
AddFriendShipGroupMemberReq addFriendShipGroupMemberReq = new AddFriendShipGroupMemberReq();
|
||||
addFriendShipGroupMemberReq.setFromId(req.getFromId());
|
||||
@@ -73,6 +91,16 @@ public class ImFriendShipGroupServiceImpl implements ImFriendShipGroupService {
|
||||
e.getStackTrace();
|
||||
return ResponseVO.errorResponse(FriendShipErrorCode.FRIEND_SHIP_GROUP_IS_EXIST);
|
||||
}
|
||||
|
||||
AddFriendGroupPack addFriendGropPack = new AddFriendGroupPack();
|
||||
addFriendGropPack.setFromId(req.getFromId());
|
||||
addFriendGropPack.setGroupName(req.getGroupName());
|
||||
addFriendGropPack.setSequence(seq);
|
||||
messageProducer.sendToUserExceptClient(req.getFromId(), FriendshipEventCommand.FRIEND_GROUP_ADD,
|
||||
addFriendGropPack,new ClientInfo(req.getAppId(),req.getClientType(),req.getImei()));
|
||||
//写入seq
|
||||
writeUserSeq.writeUserSeq(req.getAppId(), req.getFromId(), Constants.SeqConstants.FriendshipGroup, seq);
|
||||
|
||||
return ResponseVO.successResponse();
|
||||
}
|
||||
|
||||
@@ -90,12 +118,22 @@ public class ImFriendShipGroupServiceImpl implements ImFriendShipGroupService {
|
||||
ImFriendShipGroupEntity entity = imFriendShipGroupMapper.selectOne(query);
|
||||
|
||||
if (entity != null) {
|
||||
long seq = redisSeq.doGetSeq(req.getAppId() + ":" + Constants.SeqConstants.FriendshipGroup);
|
||||
ImFriendShipGroupEntity update = new ImFriendShipGroupEntity();
|
||||
update.setSequence(seq);
|
||||
update.setGroupId(entity.getGroupId());
|
||||
update.setDelFlag(DelFlagEnum.DELETE.getCode());
|
||||
imFriendShipGroupMapper.updateById(update);
|
||||
imFriendShipGroupMemberService.clearGroupMember(entity.getGroupId());
|
||||
|
||||
DeleteFriendGroupPack deleteFriendGroupPack = new DeleteFriendGroupPack();
|
||||
deleteFriendGroupPack.setFromId(req.getFromId());
|
||||
deleteFriendGroupPack.setGroupName(groupName);
|
||||
deleteFriendGroupPack.setSequence(seq);
|
||||
//TCP通知
|
||||
messageProducer.sendToUserExceptClient(req.getFromId(), FriendshipEventCommand.FRIEND_GROUP_DELETE,
|
||||
deleteFriendGroupPack,new ClientInfo(req.getAppId(),req.getClientType(),req.getImei()));
|
||||
//写入seq
|
||||
writeUserSeq.writeUserSeq(req.getAppId(), req.getFromId(), Constants.SeqConstants.FriendshipGroup, seq);
|
||||
}
|
||||
}
|
||||
return ResponseVO.successResponse();
|
||||
@@ -116,4 +154,22 @@ public class ImFriendShipGroupServiceImpl implements ImFriendShipGroupService {
|
||||
return ResponseVO.successResponse(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long updateSeq(String fromId, String groupName, Integer appId) {
|
||||
QueryWrapper<ImFriendShipGroupEntity> query = new QueryWrapper<>();
|
||||
query.eq("group_name", groupName);
|
||||
query.eq("app_id", appId);
|
||||
query.eq("from_id", fromId);
|
||||
|
||||
ImFriendShipGroupEntity entity = imFriendShipGroupMapper.selectOne(query);
|
||||
|
||||
long seq = redisSeq.doGetSeq(appId + ":" + Constants.SeqConstants.FriendshipGroup);
|
||||
|
||||
ImFriendShipGroupEntity group = new ImFriendShipGroupEntity();
|
||||
group.setGroupId(entity.getGroupId());
|
||||
group.setSequence(seq);
|
||||
imFriendShipGroupMapper.updateById(group);
|
||||
return seq;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
package com.lld.im.service.friendship.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.lld.im.codec.pack.friendship.ApproverFriendRequestPack;
|
||||
import com.lld.im.codec.pack.friendship.ReadAllFriendRequestPack;
|
||||
import com.lld.im.codec.proto.Message;
|
||||
import com.lld.im.common.ResponseVO;
|
||||
import com.lld.im.common.constant.Constants;
|
||||
import com.lld.im.common.enums.ApproverFriendRequestStatusEnum;
|
||||
import com.lld.im.common.enums.FriendShipErrorCode;
|
||||
import com.lld.im.common.enums.command.FriendshipEventCommand;
|
||||
import com.lld.im.common.exception.ApplicationException;
|
||||
import com.lld.im.service.friendship.dao.ImFriendShipRequestEntity;
|
||||
import com.lld.im.service.friendship.dao.mapper.ImFriendShipRequestMapper;
|
||||
@@ -12,6 +17,9 @@ import com.lld.im.service.friendship.model.req.FriendDto;
|
||||
import com.lld.im.service.friendship.model.req.ReadFriendShipRequestReq;
|
||||
import com.lld.im.service.friendship.service.ImFriendService;
|
||||
import com.lld.im.service.friendship.service.ImFriendShipRequestService;
|
||||
import com.lld.im.service.seq.RedisSeq;
|
||||
import com.lld.im.service.utils.MessageProducer;
|
||||
import com.lld.im.service.utils.WriteUserSeq;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -29,6 +37,15 @@ public class ImFriendShipRequestServiceImpl implements ImFriendShipRequestServic
|
||||
@Autowired
|
||||
ImFriendService imFriendShipService;
|
||||
|
||||
@Autowired
|
||||
MessageProducer messageProducer;
|
||||
|
||||
@Autowired
|
||||
RedisSeq redisSeq;
|
||||
|
||||
@Autowired
|
||||
WriteUserSeq writeUserSeq;
|
||||
|
||||
@Override
|
||||
public ResponseVO getFriendRequest(String fromId, Integer appId) {
|
||||
|
||||
@@ -52,10 +69,14 @@ public class ImFriendShipRequestServiceImpl implements ImFriendShipRequestServic
|
||||
queryWrapper.eq("to_id",dto.getToId());
|
||||
ImFriendShipRequestEntity request = imFriendShipRequestMapper.selectOne(queryWrapper);
|
||||
|
||||
long seq = redisSeq.doGetSeq(appId+":"+
|
||||
Constants.SeqConstants.FriendshipRequest);
|
||||
|
||||
if(request == null){
|
||||
request = new ImFriendShipRequestEntity();
|
||||
request.setAddSource(dto.getAddSource());
|
||||
request.setAddWording(dto.getAddWording());
|
||||
request.setSequence(seq);
|
||||
request.setAppId(appId);
|
||||
request.setFromId(fromId);
|
||||
request.setToId(dto.getToId());
|
||||
@@ -76,11 +97,19 @@ public class ImFriendShipRequestServiceImpl implements ImFriendShipRequestServic
|
||||
if(StringUtils.isNotBlank(dto.getAddWording())){
|
||||
request.setAddWording(dto.getAddWording());
|
||||
}
|
||||
request.setSequence(seq);
|
||||
request.setApproveStatus(0);
|
||||
request.setReadStatus(0);
|
||||
imFriendShipRequestMapper.updateById(request);
|
||||
}
|
||||
|
||||
writeUserSeq.writeUserSeq(appId,dto.getToId(),
|
||||
Constants.SeqConstants.FriendshipRequest,seq);
|
||||
|
||||
//发送好友申请的tcp给接收方
|
||||
messageProducer.sendToUser(dto.getToId(),
|
||||
null, "", FriendshipEventCommand.FRIEND_REQUEST,
|
||||
request, appId);
|
||||
return ResponseVO.successResponse();
|
||||
}
|
||||
|
||||
@@ -98,12 +127,19 @@ public class ImFriendShipRequestServiceImpl implements ImFriendShipRequestServic
|
||||
throw new ApplicationException(FriendShipErrorCode.NOT_APPROVER_OTHER_MAN_REQUEST);
|
||||
}
|
||||
|
||||
long seq = redisSeq.doGetSeq(req.getAppId()+":"+
|
||||
Constants.SeqConstants.FriendshipRequest);
|
||||
|
||||
ImFriendShipRequestEntity update = new ImFriendShipRequestEntity();
|
||||
update.setApproveStatus(req.getStatus());
|
||||
update.setUpdateTime(System.currentTimeMillis());
|
||||
update.setSequence(seq);
|
||||
update.setId(req.getId());
|
||||
imFriendShipRequestMapper.updateById(update);
|
||||
|
||||
writeUserSeq.writeUserSeq(req.getAppId(),req.getOperater(),
|
||||
Constants.SeqConstants.FriendshipRequest,seq);
|
||||
|
||||
if(ApproverFriendRequestStatusEnum.AGREE.getCode() == req.getStatus()){
|
||||
//同意 ===> 去执行添加好友逻辑
|
||||
FriendDto dto = new FriendDto();
|
||||
@@ -121,6 +157,12 @@ public class ImFriendShipRequestServiceImpl implements ImFriendShipRequestServic
|
||||
}
|
||||
}
|
||||
|
||||
ApproverFriendRequestPack approverFriendRequestPack = new ApproverFriendRequestPack();
|
||||
approverFriendRequestPack.setId(req.getId());
|
||||
approverFriendRequestPack.setSequence(seq);
|
||||
approverFriendRequestPack.setStatus(req.getStatus());
|
||||
messageProducer.sendToUser(imFriendShipRequestEntity.getToId(),req.getClientType(),req.getImei(), FriendshipEventCommand
|
||||
.FRIEND_REQUEST_APPROVER,approverFriendRequestPack,req.getAppId());
|
||||
return ResponseVO.successResponse();
|
||||
}
|
||||
|
||||
@@ -130,9 +172,20 @@ public class ImFriendShipRequestServiceImpl implements ImFriendShipRequestServic
|
||||
query.eq("app_id", req.getAppId());
|
||||
query.eq("to_id", req.getFromId());
|
||||
|
||||
long seq = redisSeq.doGetSeq(req.getAppId()+":"+
|
||||
Constants.SeqConstants.FriendshipRequest);
|
||||
ImFriendShipRequestEntity update = new ImFriendShipRequestEntity();
|
||||
update.setReadStatus(1);
|
||||
update.setSequence(seq);
|
||||
imFriendShipRequestMapper.update(update, query);
|
||||
writeUserSeq.writeUserSeq(req.getAppId(),req.getOperater(),
|
||||
Constants.SeqConstants.FriendshipRequest,seq);
|
||||
//TCP通知
|
||||
ReadAllFriendRequestPack readAllFriendRequestPack = new ReadAllFriendRequestPack();
|
||||
readAllFriendRequestPack.setFromId(req.getFromId());
|
||||
readAllFriendRequestPack.setSequence(seq);
|
||||
messageProducer.sendToUser(req.getFromId(),req.getClientType(),req.getImei(),FriendshipEventCommand
|
||||
.FRIEND_REQUEST_READ,readAllFriendRequestPack,req.getAppId());
|
||||
|
||||
return ResponseVO.successResponse();
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package com.lld.im.service.group.controller;
|
||||
|
||||
import com.lld.im.common.ResponseVO;
|
||||
import com.lld.im.common.model.SyncReq;
|
||||
import com.lld.im.service.group.model.req.*;
|
||||
import com.lld.im.service.group.service.GroupMessageService;
|
||||
import com.lld.im.service.group.service.ImGroupService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
@@ -21,6 +23,9 @@ public class ImGroupController {
|
||||
@Autowired
|
||||
ImGroupService groupService;
|
||||
|
||||
@Autowired
|
||||
GroupMessageService groupMessageService;
|
||||
|
||||
@RequestMapping("/importGroup")
|
||||
public ResponseVO importGroup(@RequestBody @Validated ImportGroupReq req, Integer appId, String identifier) {
|
||||
req.setAppId(appId);
|
||||
@@ -77,4 +82,19 @@ public class ImGroupController {
|
||||
return groupService.muteGroup(req);
|
||||
}
|
||||
|
||||
@RequestMapping("/sendMessage")
|
||||
public ResponseVO sendMessage(@RequestBody @Validated SendGroupMessageReq
|
||||
req, Integer appId,
|
||||
String identifier) {
|
||||
req.setAppId(appId);
|
||||
req.setOperater(identifier);
|
||||
return ResponseVO.successResponse(groupMessageService.send(req));
|
||||
}
|
||||
|
||||
@RequestMapping("/syncJoinedGroup")
|
||||
public ResponseVO syncJoinedGroup(@RequestBody @Validated SyncReq req, Integer appId, String identifier) {
|
||||
req.setAppId(appId);
|
||||
return groupService.syncJoinedGroupList(req);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -10,4 +10,17 @@ import java.util.Collection;
|
||||
@Repository
|
||||
public interface ImGroupMapper extends BaseMapper<ImGroupEntity> {
|
||||
|
||||
/**
|
||||
* @description 获取加入的群的最大seq
|
||||
* @author chackylee
|
||||
* @param []
|
||||
* @return java.lang.Long
|
||||
*/
|
||||
@Select(" <script> " +
|
||||
" select max(sequence) from im_group where app_id = #{appId} and group_id in " +
|
||||
"<foreach collection='groupId' index='index' item='id' separator=',' close=')' open='('>" +
|
||||
" #{id} " +
|
||||
"</foreach>" +
|
||||
" </script> ")
|
||||
Long getGroupMaxSeq(Collection<String> groupId, Integer appId);
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@ public interface ImGroupMemberMapper extends BaseMapper<ImGroupMemberEntity> {
|
||||
@Select("select group_id from im_group_member where app_id = #{appId} AND member_id = #{memberId} ")
|
||||
public List<String> getJoinedGroupId(Integer appId, String memberId);
|
||||
|
||||
@Select("select group_id from im_group_member where app_id = #{appId} AND member_id = #{memberId} and role != #{role}" )
|
||||
public List<String> syncJoinedGroupId(Integer appId, String memberId, int role);
|
||||
|
||||
|
||||
@Results({
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.lld.im.service.group.model.callback;
|
||||
|
||||
import com.lld.im.service.group.model.resp.AddMemberResp;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @description:
|
||||
* @author: lld
|
||||
* @version: 1.0
|
||||
*/
|
||||
@Data
|
||||
public class AddMemberAfterCallback {
|
||||
private String groupId;
|
||||
private Integer groupType;
|
||||
private String operater;
|
||||
private List<AddMemberResp> memberId;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.lld.im.service.group.model.callback;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @description:
|
||||
* @author: lld
|
||||
* @version: 1.0
|
||||
*/
|
||||
@Data
|
||||
public class DestroyGroupCallbackDto {
|
||||
|
||||
private String groupId;
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package com.lld.im.service.group.mq;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.alibaba.fastjson.TypeReference;
|
||||
import com.lld.im.common.constant.Constants;
|
||||
import com.lld.im.common.enums.command.GroupEventCommand;
|
||||
import com.lld.im.common.enums.command.MessageCommand;
|
||||
import com.lld.im.common.model.message.GroupChatMessageContent;
|
||||
import com.lld.im.common.model.message.MessageReadedContent;
|
||||
import com.lld.im.service.group.service.GroupMessageService;
|
||||
import com.lld.im.service.message.service.MessageSyncService;
|
||||
import com.lld.im.service.message.service.P2PMessageService;
|
||||
import com.rabbitmq.client.Channel;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.amqp.core.Message;
|
||||
import org.springframework.amqp.rabbit.annotation.Exchange;
|
||||
import org.springframework.amqp.rabbit.annotation.Queue;
|
||||
import org.springframework.amqp.rabbit.annotation.QueueBinding;
|
||||
import org.springframework.amqp.rabbit.annotation.RabbitListener;
|
||||
import org.springframework.amqp.support.AmqpHeaders;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.messaging.handler.annotation.Headers;
|
||||
import org.springframework.messaging.handler.annotation.Payload;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @description:
|
||||
* @author: lld
|
||||
* @version: 1.0
|
||||
*/
|
||||
@Component
|
||||
public class GroupChatOperateReceiver {
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger(GroupChatOperateReceiver.class);
|
||||
|
||||
// @Autowired
|
||||
// P2PMessageService p2PMessageService;
|
||||
@Autowired
|
||||
GroupMessageService groupMessageService;
|
||||
|
||||
@Autowired
|
||||
MessageSyncService messageSyncService;
|
||||
|
||||
@RabbitListener(
|
||||
bindings = @QueueBinding(
|
||||
value = @Queue(value = Constants.RabbitConstants.Im2GroupService,durable = "true"),
|
||||
exchange = @Exchange(value = Constants.RabbitConstants.Im2GroupService,durable = "true")
|
||||
),concurrency = "1"
|
||||
)
|
||||
public void onChatMessage(@Payload Message message,
|
||||
@Headers Map<String,Object> headers,
|
||||
Channel channel) throws Exception {
|
||||
String msg = new String(message.getBody(),"utf-8");
|
||||
logger.info("CHAT MSG FORM QUEUE ::: {}", msg);
|
||||
Long deliveryTag = (Long) headers.get(AmqpHeaders.DELIVERY_TAG);
|
||||
try {
|
||||
JSONObject jsonObject = JSON.parseObject(msg);
|
||||
Integer command = jsonObject.getInteger("command");
|
||||
if(command.equals(GroupEventCommand.MSG_GROUP.getCommand())){
|
||||
//处理消息
|
||||
GroupChatMessageContent messageContent
|
||||
= jsonObject.toJavaObject(GroupChatMessageContent.class);
|
||||
// p2PMessageService.process(messageContent);
|
||||
groupMessageService.process(messageContent);
|
||||
}else if (command.equals(GroupEventCommand.MSG_GROUP_READED.getCommand())) {
|
||||
MessageReadedContent messageReaded = JSON.parseObject(msg, new TypeReference<MessageReadedContent>() {
|
||||
}.getType());
|
||||
messageSyncService.groupReadMark(messageReaded);
|
||||
}
|
||||
channel.basicAck(deliveryTag, false);
|
||||
}catch (Exception e){
|
||||
logger.error("处理消息出现异常:{}", e.getMessage());
|
||||
logger.error("RMQ_CHAT_TRAN_ERROR", e);
|
||||
logger.error("NACK_MSG:{}", msg);
|
||||
//第一个false 表示不批量拒绝,第二个false表示不重回队列
|
||||
channel.basicNack(deliveryTag, false, false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
package com.lld.im.service.group.service;
|
||||
|
||||
import com.lld.im.codec.pack.message.ChatMessageAck;
|
||||
import com.lld.im.common.ResponseVO;
|
||||
import com.lld.im.common.constant.Constants;
|
||||
import com.lld.im.common.enums.command.GroupEventCommand;
|
||||
import com.lld.im.common.enums.command.MessageCommand;
|
||||
import com.lld.im.common.model.ClientInfo;
|
||||
import com.lld.im.common.model.message.GroupChatMessageContent;
|
||||
import com.lld.im.common.model.message.MessageContent;
|
||||
import com.lld.im.common.model.message.OfflineMessageContent;
|
||||
import com.lld.im.service.group.model.req.SendGroupMessageReq;
|
||||
import com.lld.im.service.message.model.resp.SendMessageResp;
|
||||
import com.lld.im.service.message.service.CheckSendMessageService;
|
||||
import com.lld.im.service.message.service.MessageStoreService;
|
||||
import com.lld.im.service.seq.RedisSeq;
|
||||
import com.lld.im.service.utils.MessageProducer;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* @description:
|
||||
* @author: lld
|
||||
* @version: 1.0
|
||||
*/
|
||||
@Service
|
||||
public class GroupMessageService {
|
||||
|
||||
@Autowired
|
||||
CheckSendMessageService checkSendMessageService;
|
||||
|
||||
@Autowired
|
||||
MessageProducer messageProducer;
|
||||
|
||||
@Autowired
|
||||
ImGroupMemberService imGroupMemberService;
|
||||
|
||||
@Autowired
|
||||
MessageStoreService messageStoreService;
|
||||
|
||||
@Autowired
|
||||
RedisSeq redisSeq;
|
||||
|
||||
private final ThreadPoolExecutor threadPoolExecutor;
|
||||
|
||||
{
|
||||
AtomicInteger num = new AtomicInteger(0);
|
||||
threadPoolExecutor = new ThreadPoolExecutor(8, 8, 60, TimeUnit.SECONDS,
|
||||
new LinkedBlockingQueue<>(1000), new ThreadFactory() {
|
||||
@Override
|
||||
public Thread newThread(Runnable r) {
|
||||
Thread thread = new Thread(r);
|
||||
thread.setDaemon(true);
|
||||
thread.setName("message-group-thread-" + num.getAndIncrement());
|
||||
return thread;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void process(GroupChatMessageContent messageContent){
|
||||
String fromId = messageContent.getFromId();
|
||||
String groupId = messageContent.getGroupId();
|
||||
Integer appId = messageContent.getAppId();
|
||||
//前置校验
|
||||
//这个用户是否被禁言 是否被禁用
|
||||
//发送方和接收方是否是好友
|
||||
GroupChatMessageContent messageFromMessageIdCache = messageStoreService.getMessageFromMessageIdCache(messageContent.getAppId(),
|
||||
messageContent.getMessageId(), GroupChatMessageContent.class);
|
||||
if(messageFromMessageIdCache != null){
|
||||
threadPoolExecutor.execute(() ->{
|
||||
//1.回ack成功给自己
|
||||
ack(messageContent,ResponseVO.successResponse());
|
||||
//2.发消息给同步在线端
|
||||
syncToSender(messageContent,messageContent);
|
||||
//3.发消息给对方在线端
|
||||
dispatchMessage(messageContent);
|
||||
});
|
||||
}
|
||||
long seq = redisSeq.doGetSeq(messageContent.getAppId() + ":" + Constants.SeqConstants.GroupMessage
|
||||
+ messageContent.getGroupId());
|
||||
messageContent.setMessageSequence(seq);
|
||||
threadPoolExecutor.execute(() ->{
|
||||
messageStoreService.storeGroupMessage(messageContent);
|
||||
|
||||
List<String> groupMemberId = imGroupMemberService.getGroupMemberId(messageContent.getGroupId(),
|
||||
messageContent.getAppId());
|
||||
messageContent.setMemberId(groupMemberId);
|
||||
|
||||
OfflineMessageContent offlineMessageContent = new OfflineMessageContent();
|
||||
BeanUtils.copyProperties(messageContent,offlineMessageContent);
|
||||
offlineMessageContent.setToId(messageContent.getGroupId());
|
||||
messageStoreService.storeGroupOfflineMessage(offlineMessageContent,groupMemberId);
|
||||
|
||||
//1.回ack成功给自己
|
||||
ack(messageContent,ResponseVO.successResponse());
|
||||
//2.发消息给同步在线端
|
||||
syncToSender(messageContent,messageContent);
|
||||
//3.发消息给对方在线端
|
||||
dispatchMessage(messageContent);
|
||||
|
||||
messageStoreService.setMessageFromMessageIdCache(messageContent.getAppId(),
|
||||
messageContent.getMessageId(),messageContent);
|
||||
});
|
||||
}
|
||||
|
||||
private void dispatchMessage(GroupChatMessageContent messageContent){
|
||||
for (String memberId : messageContent.getMemberId()) {
|
||||
if(!memberId.equals(messageContent.getFromId())){
|
||||
messageProducer.sendToUser(memberId,
|
||||
GroupEventCommand.MSG_GROUP,
|
||||
messageContent,messageContent.getAppId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ack(MessageContent messageContent,ResponseVO responseVO){
|
||||
|
||||
ChatMessageAck chatMessageAck = new ChatMessageAck(messageContent.getMessageId());
|
||||
responseVO.setData(chatMessageAck);
|
||||
//發消息
|
||||
messageProducer.sendToUser(messageContent.getFromId(),
|
||||
GroupEventCommand.GROUP_MSG_ACK,
|
||||
responseVO,messageContent
|
||||
);
|
||||
}
|
||||
|
||||
private void syncToSender(GroupChatMessageContent messageContent, ClientInfo clientInfo){
|
||||
messageProducer.sendToUserExceptClient(messageContent.getFromId(),
|
||||
GroupEventCommand.MSG_GROUP,messageContent,messageContent);
|
||||
}
|
||||
|
||||
private ResponseVO imServerPermissionCheck(String fromId, String toId,Integer appId){
|
||||
ResponseVO responseVO = checkSendMessageService
|
||||
.checkGroupMessage(fromId, toId,appId);
|
||||
return responseVO;
|
||||
}
|
||||
|
||||
public SendMessageResp send(SendGroupMessageReq req) {
|
||||
|
||||
SendMessageResp sendMessageResp = new SendMessageResp();
|
||||
GroupChatMessageContent message = new GroupChatMessageContent();
|
||||
BeanUtils.copyProperties(req,message);
|
||||
|
||||
messageStoreService.storeGroupMessage(message);
|
||||
|
||||
sendMessageResp.setMessageKey(message.getMessageKey());
|
||||
sendMessageResp.setMessageTime(System.currentTimeMillis());
|
||||
//2.发消息给同步在线端
|
||||
syncToSender(message,message);
|
||||
//3.发消息给对方在线端
|
||||
dispatchMessage(message);
|
||||
|
||||
return sendMessageResp;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -40,4 +40,5 @@ public interface ImGroupMemberService {
|
||||
|
||||
public ResponseVO speak(SpeaMemberReq req);
|
||||
|
||||
ResponseVO<Collection<String>> syncMemberJoinedGroup(String operater, Integer appId);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.lld.im.service.group.service;
|
||||
|
||||
import com.lld.im.common.ResponseVO;
|
||||
import com.lld.im.common.model.SyncReq;
|
||||
import com.lld.im.service.group.dao.ImGroupEntity;
|
||||
import com.lld.im.service.group.model.req.*;
|
||||
|
||||
@@ -29,4 +30,7 @@ public interface ImGroupService {
|
||||
|
||||
public ResponseVO muteGroup(MuteGroupReq req);
|
||||
|
||||
ResponseVO syncJoinedGroupList(SyncReq req);
|
||||
|
||||
Long getUserGroupMaxSeq(String userId, Integer appId);
|
||||
}
|
||||
|
||||
@@ -1,18 +1,30 @@
|
||||
package com.lld.im.service.group.service.impl;
|
||||
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.lld.im.codec.pack.group.AddGroupMemberPack;
|
||||
import com.lld.im.codec.pack.group.GroupMemberSpeakPack;
|
||||
import com.lld.im.codec.pack.group.RemoveGroupMemberPack;
|
||||
import com.lld.im.codec.pack.group.UpdateGroupMemberPack;
|
||||
import com.lld.im.common.ResponseVO;
|
||||
import com.lld.im.common.config.AppConfig;
|
||||
import com.lld.im.common.constant.Constants;
|
||||
import com.lld.im.common.enums.GroupErrorCode;
|
||||
import com.lld.im.common.enums.GroupMemberRoleEnum;
|
||||
import com.lld.im.common.enums.GroupStatusEnum;
|
||||
import com.lld.im.common.enums.GroupTypeEnum;
|
||||
import com.lld.im.common.enums.command.GroupEventCommand;
|
||||
import com.lld.im.common.exception.ApplicationException;
|
||||
import com.lld.im.common.model.ClientInfo;
|
||||
import com.lld.im.service.group.dao.ImGroupEntity;
|
||||
import com.lld.im.service.group.dao.ImGroupMemberEntity;
|
||||
import com.lld.im.service.group.dao.mapper.ImGroupMemberMapper;
|
||||
import com.lld.im.service.group.model.callback.AddMemberAfterCallback;
|
||||
import com.lld.im.service.group.model.callback.DestroyGroupCallbackDto;
|
||||
import com.lld.im.service.group.model.req.*;
|
||||
import com.lld.im.service.group.model.resp.AddMemberResp;
|
||||
import com.lld.im.service.group.model.resp.GetRoleInGroupResp;
|
||||
@@ -20,6 +32,8 @@ import com.lld.im.service.group.service.ImGroupMemberService;
|
||||
import com.lld.im.service.group.service.ImGroupService;
|
||||
import com.lld.im.service.user.dao.ImUserDataEntity;
|
||||
import com.lld.im.service.user.service.ImUserService;
|
||||
import com.lld.im.service.utils.CallbackService;
|
||||
import com.lld.im.service.utils.GroupMessageProducer;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
@@ -47,9 +61,18 @@ public class ImGroupMemberServiceImpl implements ImGroupMemberService {
|
||||
@Autowired
|
||||
ImGroupMemberService groupMemberService;
|
||||
|
||||
@Autowired
|
||||
AppConfig appConfig;
|
||||
|
||||
@Autowired
|
||||
CallbackService callbackService;
|
||||
|
||||
@Autowired
|
||||
ImUserService imUserService;
|
||||
|
||||
@Autowired
|
||||
GroupMessageProducer groupMessageProducer;
|
||||
|
||||
@Override
|
||||
public ResponseVO importGroupMember(ImportGroupMemberReq req) {
|
||||
|
||||
@@ -95,7 +118,7 @@ public class ImGroupMemberServiceImpl implements ImGroupMemberService {
|
||||
public ResponseVO addGroupMember(String groupId, Integer appId, GroupMemberDto dto) {
|
||||
|
||||
ResponseVO<ImUserDataEntity> singleUserInfo = imUserService.getSingleUserInfo(dto.getMemberId(), appId);
|
||||
if (!singleUserInfo.isOk()) {
|
||||
if(!singleUserInfo.isOk()){
|
||||
return singleUserInfo;
|
||||
}
|
||||
|
||||
@@ -155,7 +178,7 @@ public class ImGroupMemberServiceImpl implements ImGroupMemberService {
|
||||
public ResponseVO removeGroupMember(String groupId, Integer appId, String memberId) {
|
||||
|
||||
ResponseVO<ImUserDataEntity> singleUserInfo = imUserService.getSingleUserInfo(memberId, appId);
|
||||
if (!singleUserInfo.isOk()) {
|
||||
if(!singleUserInfo.isOk()){
|
||||
return singleUserInfo;
|
||||
}
|
||||
|
||||
@@ -242,14 +265,33 @@ public class ImGroupMemberServiceImpl implements ImGroupMemberService {
|
||||
}
|
||||
|
||||
List<GroupMemberDto> memberDtos = req.getMembers();
|
||||
if(appConfig.isAddGroupMemberBeforeCallback()){
|
||||
|
||||
ResponseVO responseVO = callbackService.beforeCallback(req.getAppId(), Constants.CallbackCommand.GroupMemberAddBefore
|
||||
, JSONObject.toJSONString(req));
|
||||
if(!responseVO.isOk()){
|
||||
return responseVO;
|
||||
}
|
||||
|
||||
try {
|
||||
memberDtos
|
||||
= JSONArray.parseArray(JSONObject.toJSONString(responseVO.getData()), GroupMemberDto.class);
|
||||
}catch (Exception e){
|
||||
e.printStackTrace();
|
||||
log.error("GroupMemberAddBefore 回调失败:{}",req.getAppId());
|
||||
}
|
||||
}
|
||||
|
||||
ImGroupEntity group = groupResp.getData();
|
||||
|
||||
|
||||
/**
|
||||
* 私有群(private) 类似普通微信群,创建后仅支持已在群内的好友邀请加群,且无需被邀请方同意或群主审批
|
||||
* 公开群(Public) 类似 QQ 群,创建后群主可以指定群管理员,需要群主或管理员审批通过才能入群
|
||||
* 群类型 1私有群(类似微信) 2公开群(类似qq)
|
||||
*
|
||||
*/
|
||||
|
||||
if (!isAdmin && GroupTypeEnum.PUBLIC.getCode() == group.getGroupType()) {
|
||||
throw new ApplicationException(GroupErrorCode.THIS_OPERATE_NEED_APPMANAGER_ROLE);
|
||||
}
|
||||
@@ -279,12 +321,30 @@ public class ImGroupMemberServiceImpl implements ImGroupMemberService {
|
||||
resp.add(addMemberResp);
|
||||
}
|
||||
|
||||
AddGroupMemberPack addGroupMemberPack = new AddGroupMemberPack();
|
||||
addGroupMemberPack.setGroupId(req.getGroupId());
|
||||
addGroupMemberPack.setMembers(successId);
|
||||
groupMessageProducer.producer(req.getOperater(), GroupEventCommand.ADDED_MEMBER, addGroupMemberPack
|
||||
, new ClientInfo(req.getAppId(), req.getClientType(), req.getImei()));
|
||||
|
||||
if(appConfig.isAddGroupMemberAfterCallback()){
|
||||
AddMemberAfterCallback dto = new AddMemberAfterCallback();
|
||||
dto.setGroupId(req.getGroupId());
|
||||
dto.setGroupType(group.getGroupType());
|
||||
dto.setMemberId(resp);
|
||||
dto.setOperater(req.getOperater());
|
||||
callbackService.callback(req.getAppId()
|
||||
,Constants.CallbackCommand.GroupMemberAddAfter,
|
||||
JSONObject.toJSONString(dto));
|
||||
}
|
||||
|
||||
return ResponseVO.successResponse(resp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseVO removeMember(RemoveGroupMemberReq req) {
|
||||
|
||||
List<AddMemberResp> resp = new ArrayList<>();
|
||||
boolean isAdmin = false;
|
||||
ResponseVO<ImGroupEntity> groupResp = groupService.getGroup(req.getGroupId(), req.getAppId());
|
||||
if (!groupResp.isOk()) {
|
||||
@@ -337,6 +397,20 @@ public class ImGroupMemberServiceImpl implements ImGroupMemberService {
|
||||
}
|
||||
}
|
||||
ResponseVO responseVO = groupMemberService.removeGroupMember(req.getGroupId(), req.getAppId(), req.getMemberId());
|
||||
if(responseVO.isOk()){
|
||||
|
||||
RemoveGroupMemberPack removeGroupMemberPack = new RemoveGroupMemberPack();
|
||||
removeGroupMemberPack.setGroupId(req.getGroupId());
|
||||
removeGroupMemberPack.setMember(req.getMemberId());
|
||||
groupMessageProducer.producer(req.getMemberId(), GroupEventCommand.DELETED_MEMBER, removeGroupMemberPack
|
||||
, new ClientInfo(req.getAppId(), req.getClientType(), req.getImei()));
|
||||
if(appConfig.isDeleteGroupMemberAfterCallback()){
|
||||
callbackService.callback(req.getAppId(),
|
||||
Constants.CallbackCommand.GroupMemberDeleteAfter,
|
||||
JSONObject.toJSONString(req));
|
||||
}
|
||||
}
|
||||
|
||||
return responseVO;
|
||||
}
|
||||
|
||||
@@ -379,25 +453,24 @@ public class ImGroupMemberServiceImpl implements ImGroupMemberService {
|
||||
if (StringUtils.isBlank(req.getAlias()) && !isMeOperate) {
|
||||
return ResponseVO.errorResponse(GroupErrorCode.THIS_OPERATE_NEED_ONESELF);
|
||||
}
|
||||
//私有群不能设置管理员
|
||||
if (groupData.getGroupType() == GroupTypeEnum.PRIVATE.getCode() &&
|
||||
req.getRole() != null && (req.getRole() == GroupMemberRoleEnum.MAMAGER.getCode() ||
|
||||
req.getRole() == GroupMemberRoleEnum.OWNER.getCode())) {
|
||||
return ResponseVO.errorResponse(GroupErrorCode.THIS_OPERATE_NEED_MANAGER_ROLE);
|
||||
}
|
||||
|
||||
//如果要修改权限相关的则走下面的逻辑
|
||||
if (req.getRole() != null) {
|
||||
//私有群不能设置管理员
|
||||
if (groupData.getGroupType() == GroupTypeEnum.PRIVATE.getCode() &&
|
||||
req.getRole() != null && (req.getRole() == GroupMemberRoleEnum.MAMAGER.getCode() ||
|
||||
req.getRole() == GroupMemberRoleEnum.OWNER.getCode())) {
|
||||
return ResponseVO.errorResponse(GroupErrorCode.THIS_OPERATE_NEED_APPMANAGER_ROLE);
|
||||
}
|
||||
|
||||
if(req.getRole() != null){
|
||||
//获取被操作人的是否在群内
|
||||
ResponseVO<GetRoleInGroupResp> roleInGroupOne = this.getRoleInGroupOne(req.getGroupId(), req.getMemberId(), req.getAppId());
|
||||
if (!roleInGroupOne.isOk()) {
|
||||
if(!roleInGroupOne.isOk()){
|
||||
return roleInGroupOne;
|
||||
}
|
||||
|
||||
//获取操作人权限
|
||||
ResponseVO<GetRoleInGroupResp> operateRoleInGroupOne = this.getRoleInGroupOne(req.getGroupId(), req.getOperater(), req.getAppId());
|
||||
if (!operateRoleInGroupOne.isOk()) {
|
||||
if(!operateRoleInGroupOne.isOk()){
|
||||
return operateRoleInGroupOne;
|
||||
}
|
||||
|
||||
@@ -407,12 +480,12 @@ public class ImGroupMemberServiceImpl implements ImGroupMemberService {
|
||||
boolean isManager = roleInfo == GroupMemberRoleEnum.MAMAGER.getCode();
|
||||
|
||||
//不是管理员不能修改权限
|
||||
if (req.getRole() != null && !isOwner && !isManager) {
|
||||
if(req.getRole() != null && !isOwner && !isManager){
|
||||
return ResponseVO.errorResponse(GroupErrorCode.THIS_OPERATE_NEED_MANAGER_ROLE);
|
||||
}
|
||||
|
||||
//管理员只有群主能够设置
|
||||
if (req.getRole() != null && req.getRole() == GroupMemberRoleEnum.MAMAGER.getCode() && !isOwner) {
|
||||
if(req.getRole() != null && req.getRole() == GroupMemberRoleEnum.MAMAGER.getCode() && !isOwner){
|
||||
return ResponseVO.errorResponse(GroupErrorCode.THIS_OPERATE_NEED_OWNER_ROLE);
|
||||
}
|
||||
|
||||
@@ -436,6 +509,11 @@ public class ImGroupMemberServiceImpl implements ImGroupMemberService {
|
||||
objectUpdateWrapper.eq("group_id", req.getGroupId());
|
||||
imGroupMemberMapper.update(update, objectUpdateWrapper);
|
||||
|
||||
UpdateGroupMemberPack pack = new UpdateGroupMemberPack();
|
||||
BeanUtils.copyProperties(req, pack);
|
||||
groupMessageProducer.producer(req.getOperater(), GroupEventCommand.UPDATED_MEMBER, pack, new ClientInfo(req.getAppId(), req.getClientType(), req.getImei()));
|
||||
|
||||
|
||||
return ResponseVO.successResponse();
|
||||
}
|
||||
|
||||
@@ -513,7 +591,7 @@ public class ImGroupMemberServiceImpl implements ImGroupMemberService {
|
||||
}
|
||||
|
||||
ImGroupMemberEntity imGroupMemberEntity = new ImGroupMemberEntity();
|
||||
if (memberRole == null) {
|
||||
if(memberRole == null){
|
||||
//获取被操作的权限
|
||||
ResponseVO<GetRoleInGroupResp> roleInGroupOne = this.getRoleInGroupOne(req.getGroupId(), req.getMemberId(), req.getAppId());
|
||||
if (!roleInGroupOne.isOk()) {
|
||||
@@ -523,15 +601,26 @@ public class ImGroupMemberServiceImpl implements ImGroupMemberService {
|
||||
}
|
||||
|
||||
imGroupMemberEntity.setGroupMemberId(memberRole.getGroupMemberId());
|
||||
if (req.getSpeakDate() > 0) {
|
||||
if(req.getSpeakDate() > 0){
|
||||
imGroupMemberEntity.setSpeakDate(System.currentTimeMillis() + req.getSpeakDate());
|
||||
} else {
|
||||
}else{
|
||||
imGroupMemberEntity.setSpeakDate(req.getSpeakDate());
|
||||
}
|
||||
|
||||
int i = imGroupMemberMapper.updateById(imGroupMemberEntity);
|
||||
if(i == 1){
|
||||
GroupMemberSpeakPack pack = new GroupMemberSpeakPack();
|
||||
BeanUtils.copyProperties(req,pack);
|
||||
groupMessageProducer.producer(req.getOperater(),GroupEventCommand.SPEAK_GOUP_MEMBER,pack,
|
||||
new ClientInfo(req.getAppId(),req.getClientType(),req.getImei()));
|
||||
}
|
||||
return ResponseVO.successResponse();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseVO<Collection<String>> syncMemberJoinedGroup(String operater, Integer appId) {
|
||||
return ResponseVO.successResponse(imGroupMemberMapper.syncJoinedGroupId(appId,operater,GroupMemberRoleEnum.LEAVE.getCode()));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,23 +1,37 @@
|
||||
package com.lld.im.service.group.service.impl;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
|
||||
import com.lld.im.codec.pack.group.CreateGroupPack;
|
||||
import com.lld.im.codec.pack.group.DestroyGroupPack;
|
||||
import com.lld.im.codec.pack.group.UpdateGroupInfoPack;
|
||||
import com.lld.im.common.ResponseVO;
|
||||
import com.lld.im.common.config.AppConfig;
|
||||
import com.lld.im.common.constant.Constants;
|
||||
import com.lld.im.common.enums.GroupErrorCode;
|
||||
import com.lld.im.common.enums.GroupMemberRoleEnum;
|
||||
import com.lld.im.common.enums.GroupStatusEnum;
|
||||
import com.lld.im.common.enums.GroupTypeEnum;
|
||||
import com.lld.im.common.enums.command.GroupEventCommand;
|
||||
import com.lld.im.common.exception.ApplicationException;
|
||||
import com.lld.im.common.model.ClientInfo;
|
||||
import com.lld.im.common.model.SyncReq;
|
||||
import com.lld.im.common.model.SyncResp;
|
||||
import com.lld.im.service.conversation.dao.ImConversationSetEntity;
|
||||
import com.lld.im.service.group.dao.ImGroupEntity;
|
||||
import com.lld.im.service.group.dao.mapper.ImGroupMapper;
|
||||
import com.lld.im.service.group.model.callback.DestroyGroupCallbackDto;
|
||||
import com.lld.im.service.group.model.req.*;
|
||||
import com.lld.im.service.group.model.resp.GetGroupResp;
|
||||
import com.lld.im.service.group.model.resp.GetJoinedGroupResp;
|
||||
import com.lld.im.service.group.model.resp.GetRoleInGroupResp;
|
||||
import com.lld.im.service.group.service.ImGroupMemberService;
|
||||
import com.lld.im.service.group.service.ImGroupService;
|
||||
import com.lld.im.service.seq.RedisSeq;
|
||||
import com.lld.im.service.utils.CallbackService;
|
||||
import com.lld.im.service.utils.GroupMessageProducer;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -43,6 +57,17 @@ public class ImGroupServiceImpl implements ImGroupService {
|
||||
@Autowired
|
||||
ImGroupMemberService groupMemberService;
|
||||
|
||||
@Autowired
|
||||
AppConfig appConfig;
|
||||
|
||||
@Autowired
|
||||
CallbackService callbackService;
|
||||
|
||||
@Autowired
|
||||
GroupMessageProducer groupMessageProducer;
|
||||
|
||||
@Autowired
|
||||
RedisSeq redisSeq;
|
||||
|
||||
@Override
|
||||
public ResponseVO importGroup(ImportGroupReq req) {
|
||||
@@ -110,6 +135,8 @@ public class ImGroupServiceImpl implements ImGroupService {
|
||||
}
|
||||
|
||||
ImGroupEntity imGroupEntity = new ImGroupEntity();
|
||||
long seq = redisSeq.doGetSeq(req.getAppId() + ":" + Constants.SeqConstants.Group);
|
||||
imGroupEntity.setSequence(seq);
|
||||
imGroupEntity.setCreateTime(System.currentTimeMillis());
|
||||
imGroupEntity.setStatus(GroupStatusEnum.NORMAL.getCode());
|
||||
BeanUtils.copyProperties(req, imGroupEntity);
|
||||
@@ -126,6 +153,15 @@ public class ImGroupServiceImpl implements ImGroupService {
|
||||
groupMemberService.addGroupMember(req.getGroupId(), req.getAppId(), dto);
|
||||
}
|
||||
|
||||
if(appConfig.isCreateGroupAfterCallback()){
|
||||
callbackService.callback(req.getAppId(), Constants.CallbackCommand.CreateGroupAfter,
|
||||
JSONObject.toJSONString(imGroupEntity));
|
||||
}
|
||||
|
||||
CreateGroupPack createGroupPack = new CreateGroupPack();
|
||||
BeanUtils.copyProperties(imGroupEntity, createGroupPack);
|
||||
groupMessageProducer.producer(req.getOperater(), GroupEventCommand.CREATED_GROUP, createGroupPack
|
||||
, new ClientInfo(req.getAppId(), req.getClientType(), req.getImei()));
|
||||
return ResponseVO.successResponse();
|
||||
}
|
||||
|
||||
@@ -176,13 +212,26 @@ public class ImGroupServiceImpl implements ImGroupService {
|
||||
}
|
||||
|
||||
ImGroupEntity update = new ImGroupEntity();
|
||||
long seq = redisSeq.doGetSeq(req.getAppId() + ":" + Constants.SeqConstants.Group);
|
||||
BeanUtils.copyProperties(req, update);
|
||||
update.setUpdateTime(System.currentTimeMillis());
|
||||
update.setSequence(seq);
|
||||
int row = imGroupDataMapper.update(update, query);
|
||||
if (row != 1) {
|
||||
throw new ApplicationException(GroupErrorCode.THIS_OPERATE_NEED_MANAGER_ROLE);
|
||||
}
|
||||
|
||||
if(appConfig.isModifyGroupAfterCallback()){
|
||||
callbackService.callback(req.getAppId(),Constants.CallbackCommand.UpdateGroupAfter,
|
||||
JSONObject.toJSONString(imGroupDataMapper.selectOne(query)));
|
||||
}
|
||||
|
||||
UpdateGroupInfoPack pack = new UpdateGroupInfoPack();
|
||||
BeanUtils.copyProperties(req, pack);
|
||||
groupMessageProducer.producer(req.getOperater(), GroupEventCommand.UPDATED_GROUP,
|
||||
pack, new ClientInfo(req.getAppId(), req.getClientType(), req.getImei()));
|
||||
|
||||
|
||||
return ResponseVO.successResponse();
|
||||
}
|
||||
|
||||
@@ -264,12 +313,29 @@ public class ImGroupServiceImpl implements ImGroupService {
|
||||
}
|
||||
|
||||
ImGroupEntity update = new ImGroupEntity();
|
||||
long seq = redisSeq.doGetSeq(req.getAppId() + ":" + Constants.SeqConstants.Group);
|
||||
|
||||
update.setStatus(GroupStatusEnum.DESTROY.getCode());
|
||||
update.setSequence(seq);
|
||||
int update1 = imGroupDataMapper.update(update, objectQueryWrapper);
|
||||
if (update1 != 1) {
|
||||
throw new ApplicationException(GroupErrorCode.UPDATE_GROUP_BASE_INFO_ERROR);
|
||||
}
|
||||
|
||||
if(appConfig.isModifyGroupAfterCallback()){
|
||||
DestroyGroupCallbackDto dto = new DestroyGroupCallbackDto();
|
||||
dto.setGroupId(req.getGroupId());
|
||||
callbackService.callback(req.getAppId()
|
||||
,Constants.CallbackCommand.DestoryGroupAfter,
|
||||
JSONObject.toJSONString(dto));
|
||||
}
|
||||
|
||||
DestroyGroupPack pack = new DestroyGroupPack();
|
||||
pack.setSequence(seq);
|
||||
pack.setGroupId(req.getGroupId());
|
||||
groupMessageProducer.producer(req.getOperater(),
|
||||
GroupEventCommand.DESTROY_GROUP, pack, new ClientInfo(req.getAppId(), req.getClientType(), req.getImei()));
|
||||
|
||||
return ResponseVO.successResponse();
|
||||
}
|
||||
|
||||
@@ -390,4 +456,56 @@ public class ImGroupServiceImpl implements ImGroupService {
|
||||
return ResponseVO.successResponse();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseVO syncJoinedGroupList(SyncReq req) {
|
||||
if(req.getMaxLimit() > 100){
|
||||
req.setMaxLimit(100);
|
||||
}
|
||||
|
||||
SyncResp<ImGroupEntity> resp = new SyncResp<>();
|
||||
|
||||
ResponseVO<Collection<String>> memberJoinedGroup = groupMemberService.syncMemberJoinedGroup(req.getOperater(), req.getAppId());
|
||||
if(memberJoinedGroup.isOk()){
|
||||
|
||||
Collection<String> data = memberJoinedGroup.getData();
|
||||
QueryWrapper<ImGroupEntity> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("app_id",req.getAppId());
|
||||
queryWrapper.in("group_id",data);
|
||||
queryWrapper.gt("sequence",req.getLastSequence());
|
||||
queryWrapper.last(" limit " + req.getMaxLimit());
|
||||
queryWrapper.orderByAsc("sequence");
|
||||
|
||||
List<ImGroupEntity> list = imGroupDataMapper.selectList(queryWrapper);
|
||||
|
||||
if(!CollectionUtils.isEmpty(list)){
|
||||
ImGroupEntity maxSeqEntity
|
||||
= list.get(list.size() - 1);
|
||||
resp.setDataList(list);
|
||||
//设置最大seq
|
||||
Long maxSeq =
|
||||
imGroupDataMapper.getGroupMaxSeq(data, req.getAppId());
|
||||
resp.setMaxSequence(maxSeq);
|
||||
//设置是否拉取完毕
|
||||
resp.setCompleted(maxSeqEntity.getSequence() >= maxSeq);
|
||||
return ResponseVO.successResponse(resp);
|
||||
}
|
||||
|
||||
}
|
||||
resp.setCompleted(true);
|
||||
return ResponseVO.successResponse(resp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getUserGroupMaxSeq(String userId, Integer appId) {
|
||||
|
||||
ResponseVO<Collection<String>> memberJoinedGroup = groupMemberService.syncMemberJoinedGroup(userId, appId);
|
||||
if(!memberJoinedGroup.isOk()){
|
||||
throw new ApplicationException(500,"");
|
||||
}
|
||||
Long maxSeq =
|
||||
imGroupDataMapper.getGroupMaxSeq(memberJoinedGroup.getData(),
|
||||
appId);
|
||||
return maxSeq;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
package com.lld.im.service.interceptor;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.lld.im.common.BaseErrorCode;
|
||||
import com.lld.im.common.ResponseVO;
|
||||
import com.lld.im.common.enums.GateWayErrorCode;
|
||||
import com.lld.im.common.exception.ApplicationExceptionEnum;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
/**
|
||||
* @description:
|
||||
* @author: lld
|
||||
* @version: 1.0
|
||||
*/
|
||||
@Component
|
||||
public class GateWayInterceptor implements HandlerInterceptor {
|
||||
|
||||
@Autowired
|
||||
IdentityCheck identityCheck;
|
||||
|
||||
|
||||
//appService -》im接口 -》 userSign
|
||||
//appService(gen userSig)
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
|
||||
|
||||
// if (1 == 1){
|
||||
// return true;
|
||||
// }
|
||||
|
||||
//获取appId 操作人 userSign
|
||||
String appIdStr = request.getParameter("appId");
|
||||
if(StringUtils.isBlank(appIdStr)){
|
||||
resp(ResponseVO.errorResponse(GateWayErrorCode
|
||||
.APPID_NOT_EXIST),response);
|
||||
return false;
|
||||
}
|
||||
|
||||
String identifier = request.getParameter("identifier");
|
||||
if(StringUtils.isBlank(identifier)){
|
||||
resp(ResponseVO.errorResponse(GateWayErrorCode
|
||||
.OPERATER_NOT_EXIST),response);
|
||||
return false;
|
||||
}
|
||||
|
||||
String userSign = request.getParameter("userSign");
|
||||
if(StringUtils.isBlank(userSign)){
|
||||
resp(ResponseVO.errorResponse(GateWayErrorCode
|
||||
.USERSIGN_NOT_EXIST),response);
|
||||
return false;
|
||||
}
|
||||
|
||||
//签名和操作人和appid是否匹配
|
||||
ApplicationExceptionEnum applicationExceptionEnum = identityCheck.checkUserSig(identifier, appIdStr, userSign);
|
||||
if(applicationExceptionEnum != BaseErrorCode.SUCCESS){
|
||||
resp(ResponseVO.errorResponse(applicationExceptionEnum),response);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private void resp(ResponseVO respVo ,HttpServletResponse response){
|
||||
|
||||
PrintWriter writer = null;
|
||||
response.setCharacterEncoding("UTF-8");
|
||||
response.setContentType("text/html; charset=utf-8");
|
||||
try {
|
||||
String resp = JSONObject.toJSONString(respVo);
|
||||
|
||||
response.setCharacterEncoding("UTF-8");
|
||||
response.setHeader("Content-type", "application/json;charset=UTF-8");
|
||||
response.setHeader("Access-Control-Allow-Origin","*");
|
||||
response.setHeader("Access-Control-Allow-Credentials","true");
|
||||
response.setHeader("Access-Control-Allow-Methods","*");
|
||||
response.setHeader("Access-Control-Allow-Headers","*");
|
||||
response.setHeader("Access-Control-Max-Age","3600");
|
||||
|
||||
writer = response.getWriter();
|
||||
writer.write(resp);
|
||||
} catch (Exception e){
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if(writer != null){
|
||||
writer.checkError();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
package com.lld.im.service.interceptor;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.lld.im.common.BaseErrorCode;
|
||||
import com.lld.im.common.ResponseVO;
|
||||
import com.lld.im.common.config.AppConfig;
|
||||
import com.lld.im.common.constant.Constants;
|
||||
import com.lld.im.common.enums.GateWayErrorCode;
|
||||
import com.lld.im.common.enums.ImUserTypeEnum;
|
||||
import com.lld.im.common.exception.ApplicationExceptionEnum;
|
||||
import com.lld.im.common.utils.SigAPI;
|
||||
import com.lld.im.service.user.dao.ImUserDataEntity;
|
||||
import com.lld.im.service.user.service.ImUserService;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
import sun.rmi.runtime.Log;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @description:
|
||||
* @author: lld
|
||||
* @version: 1.0
|
||||
*/
|
||||
@Component
|
||||
public class IdentityCheck {
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger(IdentityCheck.class);
|
||||
|
||||
@Autowired
|
||||
ImUserService imUserService;
|
||||
|
||||
//10000 123456 10001 123456789
|
||||
@Autowired
|
||||
AppConfig appConfig;
|
||||
|
||||
@Autowired
|
||||
StringRedisTemplate stringRedisTemplate;
|
||||
|
||||
public ApplicationExceptionEnum checkUserSig(String identifier,
|
||||
String appId,String userSig){
|
||||
|
||||
String cacheUserSig = stringRedisTemplate.opsForValue()
|
||||
.get(appId + ":" + Constants.RedisConstants.userSign + ":"
|
||||
+ identifier + userSig);
|
||||
if(!StringUtils.isBlank(cacheUserSig) && Long.valueOf(cacheUserSig)
|
||||
> System.currentTimeMillis() / 1000){
|
||||
this.setIsAdmin(identifier,Integer.valueOf(appId));
|
||||
return BaseErrorCode.SUCCESS;
|
||||
}
|
||||
|
||||
//获取秘钥
|
||||
String privateKey = appConfig.getPrivateKey();
|
||||
|
||||
//根据appid + 秘钥创建sigApi
|
||||
SigAPI sigAPI = new SigAPI(Long.valueOf(appId), privateKey);
|
||||
|
||||
//调用sigApi对userSig解密
|
||||
JSONObject jsonObject = sigAPI.decodeUserSig(userSig);
|
||||
|
||||
//取出解密后的appid 和 操作人 和 过期时间做匹配,不通过则提示错误
|
||||
Long expireTime = 0L;
|
||||
Long expireSec = 0L;
|
||||
Long time = 0L;
|
||||
String decoerAppId = "";
|
||||
String decoderidentifier = "";
|
||||
|
||||
try {
|
||||
decoerAppId = jsonObject.getString("TLS.appId");
|
||||
decoderidentifier = jsonObject.getString("TLS.identifier");
|
||||
String expireStr = jsonObject.get("TLS.expire").toString();
|
||||
String expireTimeStr = jsonObject.get("TLS.expireTime").toString();
|
||||
time = Long.valueOf(expireTimeStr);
|
||||
expireSec = Long.valueOf(expireStr);
|
||||
expireTime = Long.valueOf(expireTimeStr) + expireSec;
|
||||
}catch (Exception e){
|
||||
e.printStackTrace();
|
||||
logger.error("checkUserSig-error:{}",e.getMessage());
|
||||
}
|
||||
|
||||
if(!decoderidentifier.equals(identifier)){
|
||||
return GateWayErrorCode.USERSIGN_OPERATE_NOT_MATE;
|
||||
}
|
||||
|
||||
if(!decoerAppId.equals(appId)){
|
||||
return GateWayErrorCode.USERSIGN_IS_ERROR;
|
||||
}
|
||||
|
||||
if(expireSec == 0L){
|
||||
return GateWayErrorCode.USERSIGN_IS_EXPIRED;
|
||||
}
|
||||
|
||||
if(expireTime < System.currentTimeMillis() / 1000){
|
||||
return GateWayErrorCode.USERSIGN_IS_EXPIRED;
|
||||
}
|
||||
|
||||
//appid + "xxx" + userId + sign
|
||||
String genSig = sigAPI.genUserSig(identifier, expireSec,time,null);
|
||||
if (genSig.toLowerCase().equals(userSig.toLowerCase()))
|
||||
{
|
||||
String key = appId + ":" + Constants.RedisConstants.userSign + ":"
|
||||
+identifier + userSig;
|
||||
|
||||
Long etime = expireTime - System.currentTimeMillis() / 1000;
|
||||
stringRedisTemplate.opsForValue().set(
|
||||
key,expireTime.toString(),etime, TimeUnit.SECONDS
|
||||
);
|
||||
this.setIsAdmin(identifier,Integer.valueOf(appId));
|
||||
return BaseErrorCode.SUCCESS;
|
||||
}
|
||||
|
||||
return GateWayErrorCode.USERSIGN_IS_ERROR;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 根据appid,identifier判断是否App管理员,并设置到RequestHolder
|
||||
* @param identifier
|
||||
* @param appId
|
||||
* @return
|
||||
*/
|
||||
public void setIsAdmin(String identifier, Integer appId) {
|
||||
//去DB或Redis中查找, 后面写
|
||||
ResponseVO<ImUserDataEntity> singleUserInfo = imUserService.getSingleUserInfo(identifier, appId);
|
||||
if(singleUserInfo.isOk()){
|
||||
RequestHolder.set(singleUserInfo.getData().getUserType() == ImUserTypeEnum.APP_ADMIN.getCode());
|
||||
}else{
|
||||
RequestHolder.set(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.lld.im.service.interceptor;
|
||||
|
||||
/**
|
||||
* @author: Chackylee
|
||||
* @description:
|
||||
**/
|
||||
public class RequestHolder {
|
||||
|
||||
private final static ThreadLocal<Boolean> requestHolder = new ThreadLocal<>();
|
||||
|
||||
public static void set(Boolean isadmin) {
|
||||
requestHolder.set(isadmin);
|
||||
}
|
||||
|
||||
public static Boolean get() {
|
||||
return requestHolder.get();
|
||||
}
|
||||
|
||||
public static void remove() {
|
||||
requestHolder.remove();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.lld.im.service.message.controller;
|
||||
|
||||
import com.lld.im.common.ResponseVO;
|
||||
import com.lld.im.common.model.SyncReq;
|
||||
import com.lld.im.common.model.message.CheckSendMessageReq;
|
||||
import com.lld.im.service.message.model.req.SendMessageReq;
|
||||
import com.lld.im.service.message.service.MessageSyncService;
|
||||
import com.lld.im.service.message.service.P2PMessageService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* @description:
|
||||
* @author: lld
|
||||
* @version: 1.0
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("v1/message")
|
||||
public class MessageController {
|
||||
|
||||
@Autowired
|
||||
P2PMessageService p2PMessageService;
|
||||
|
||||
@Autowired
|
||||
MessageSyncService messageSyncService;
|
||||
|
||||
@RequestMapping("/send")
|
||||
public ResponseVO send(@RequestBody @Validated SendMessageReq req, Integer appId) {
|
||||
req.setAppId(appId);
|
||||
return ResponseVO.successResponse(p2PMessageService.send(req));
|
||||
}
|
||||
|
||||
@RequestMapping("/checkSend")
|
||||
public ResponseVO checkSend(@RequestBody @Validated CheckSendMessageReq req) {
|
||||
return p2PMessageService.imServerPermissionCheck(req.getFromId(),req.getToId()
|
||||
,req.getAppId());
|
||||
}
|
||||
|
||||
@RequestMapping("/syncOfflineMessage")
|
||||
public ResponseVO syncOfflineMessage(@RequestBody
|
||||
@Validated SyncReq req, Integer appId) {
|
||||
req.setAppId(appId);
|
||||
return messageSyncService.syncOfflineMessage(req);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.lld.im.service.message.dao;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author: Chackylee
|
||||
* @description:
|
||||
**/
|
||||
@Data
|
||||
@TableName("im_message_body")
|
||||
public class ImMessageBodyEntity {
|
||||
|
||||
private Integer appId;
|
||||
|
||||
/** messageBodyId*/
|
||||
private Long messageKey;
|
||||
|
||||
/** messageBody*/
|
||||
private String messageBody;
|
||||
|
||||
private String securityKey;
|
||||
|
||||
private Long messageTime;
|
||||
|
||||
private Long createTime;
|
||||
|
||||
private String extra;
|
||||
|
||||
private Integer delFlag;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.lld.im.service.message.dao;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author: Chackylee
|
||||
* @description:
|
||||
**/
|
||||
@Data
|
||||
@TableName("im_message_history")
|
||||
public class ImMessageHistoryEntity {
|
||||
|
||||
private Integer appId;
|
||||
|
||||
private String fromId;
|
||||
|
||||
private String toId;
|
||||
|
||||
private String ownerId;
|
||||
|
||||
/** messageBodyId*/
|
||||
private Long messageKey;
|
||||
/** 序列号*/
|
||||
private Long sequence;
|
||||
|
||||
private String messageRandom;
|
||||
|
||||
private Long messageTime;
|
||||
|
||||
private Long createTime;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.lld.im.service.message.dao.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.lld.im.service.message.dao.ImMessageBodyEntity;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface ImMessageBodyMapper extends BaseMapper<ImMessageBodyEntity> {
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.lld.im.service.message.dao.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.lld.im.service.message.dao.ImMessageHistoryEntity;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
@Repository
|
||||
public interface ImMessageHistoryMapper extends BaseMapper<ImMessageHistoryEntity> {
|
||||
|
||||
/**
|
||||
* 批量插入(mysql)
|
||||
* @param entityList
|
||||
* @return
|
||||
*/
|
||||
Integer insertBatchSomeColumn(Collection<ImMessageHistoryEntity> entityList);
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.lld.im.service.message.model.req;
|
||||
|
||||
import com.lld.im.common.model.RequestBase;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author: Chackylee
|
||||
* @description:
|
||||
**/
|
||||
@Data
|
||||
public class SendMessageReq extends RequestBase {
|
||||
|
||||
//客户端传的messageId
|
||||
private String messageId;
|
||||
|
||||
private String fromId;
|
||||
|
||||
private String toId;
|
||||
|
||||
private int messageRandom;
|
||||
|
||||
private long messageTime;
|
||||
|
||||
private String messageBody;
|
||||
/**
|
||||
* 这个字段缺省或者为 0 表示需要计数,为 1 表示本条消息不需要计数,即右上角图标数字不增加
|
||||
*/
|
||||
private int badgeMode;
|
||||
|
||||
private Long messageLifeTime;
|
||||
|
||||
private Integer appId;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.lld.im.service.message.model.resp;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @description:
|
||||
* @author: lld
|
||||
* @version: 1.0
|
||||
*/
|
||||
@Data
|
||||
public class SendMessageResp {
|
||||
|
||||
private Long messageKey;
|
||||
|
||||
private Long messageTime;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package com.lld.im.service.message.mq;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.alibaba.fastjson.TypeReference;
|
||||
import com.lld.im.common.constant.Constants;
|
||||
import com.lld.im.common.enums.command.MessageCommand;
|
||||
import com.lld.im.common.model.message.MessageContent;
|
||||
import com.lld.im.common.model.message.MessageReadedContent;
|
||||
import com.lld.im.common.model.message.MessageReciveAckContent;
|
||||
import com.lld.im.common.model.message.RecallMessageContent;
|
||||
import com.lld.im.service.message.service.MessageSyncService;
|
||||
import com.lld.im.service.message.service.P2PMessageService;
|
||||
import com.rabbitmq.client.Channel;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.amqp.core.Message;
|
||||
import org.springframework.amqp.rabbit.annotation.Exchange;
|
||||
import org.springframework.amqp.rabbit.annotation.Queue;
|
||||
import org.springframework.amqp.rabbit.annotation.QueueBinding;
|
||||
import org.springframework.amqp.rabbit.annotation.RabbitListener;
|
||||
import org.springframework.amqp.support.AmqpHeaders;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.messaging.handler.annotation.Headers;
|
||||
import org.springframework.messaging.handler.annotation.Payload;
|
||||
import org.springframework.stereotype.Component;
|
||||
import sun.nio.cs.ext.MS874;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @description:
|
||||
* @author: lld
|
||||
* @version: 1.0
|
||||
*/
|
||||
@Component
|
||||
public class ChatOperateReceiver {
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger(ChatOperateReceiver.class);
|
||||
|
||||
@Autowired
|
||||
P2PMessageService p2PMessageService;
|
||||
|
||||
@Autowired
|
||||
MessageSyncService messageSyncService;
|
||||
|
||||
@RabbitListener(
|
||||
bindings = @QueueBinding(
|
||||
value = @Queue(value = Constants.RabbitConstants.Im2MessageService,durable = "true"),
|
||||
exchange = @Exchange(value = Constants.RabbitConstants.Im2MessageService,durable = "true")
|
||||
),concurrency = "1"
|
||||
)
|
||||
public void onChatMessage(@Payload Message message,
|
||||
@Headers Map<String,Object> headers,
|
||||
Channel channel) throws Exception {
|
||||
String msg = new String(message.getBody(),"utf-8");
|
||||
logger.info("CHAT MSG FORM QUEUE ::: {}", msg);
|
||||
Long deliveryTag = (Long) headers.get(AmqpHeaders.DELIVERY_TAG);
|
||||
try {
|
||||
JSONObject jsonObject = JSON.parseObject(msg);
|
||||
Integer command = jsonObject.getInteger("command");
|
||||
if(command.equals(MessageCommand.MSG_P2P.getCommand())){
|
||||
//处理消息
|
||||
MessageContent messageContent
|
||||
= jsonObject.toJavaObject(MessageContent.class);
|
||||
p2PMessageService.process(messageContent);
|
||||
}else if(command.equals(MessageCommand.MSG_RECIVE_ACK.getCommand())){
|
||||
//消息接收确认
|
||||
MessageReciveAckContent messageContent
|
||||
= jsonObject.toJavaObject(MessageReciveAckContent.class);
|
||||
messageSyncService.receiveMark(messageContent);
|
||||
}else if(command.equals(MessageCommand.MSG_READED.getCommand())){
|
||||
//消息接收确认
|
||||
MessageReadedContent messageContent
|
||||
= jsonObject.toJavaObject(MessageReadedContent.class);
|
||||
messageSyncService.readMark(messageContent);
|
||||
}else if (Objects.equals(command, MessageCommand.MSG_RECALL.getCommand())) {
|
||||
// 撤回消息
|
||||
RecallMessageContent messageContent = JSON.parseObject(msg, new TypeReference<RecallMessageContent>() {
|
||||
}.getType());
|
||||
messageSyncService.recallMessage(messageContent);
|
||||
}
|
||||
channel.basicAck(deliveryTag, false);
|
||||
}catch (Exception e){
|
||||
logger.error("处理消息出现异常:{}", e.getMessage());
|
||||
logger.error("RMQ_CHAT_TRAN_ERROR", e);
|
||||
logger.error("NACK_MSG:{}", msg);
|
||||
//第一个false 表示不批量拒绝,第二个false表示不重回队列
|
||||
channel.basicNack(deliveryTag, false, false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
package com.lld.im.service.message.service;
|
||||
|
||||
import com.lld.im.common.ResponseVO;
|
||||
import com.lld.im.common.config.AppConfig;
|
||||
import com.lld.im.common.enums.*;
|
||||
import com.lld.im.service.friendship.dao.ImFriendShipEntity;
|
||||
import com.lld.im.service.friendship.model.req.GetRelationReq;
|
||||
import com.lld.im.service.friendship.service.ImFriendService;
|
||||
import com.lld.im.service.group.dao.ImGroupEntity;
|
||||
import com.lld.im.service.group.model.resp.GetRoleInGroupResp;
|
||||
import com.lld.im.service.group.service.ImGroupMemberService;
|
||||
import com.lld.im.service.group.service.ImGroupService;
|
||||
import com.lld.im.service.user.dao.ImUserDataEntity;
|
||||
import com.lld.im.service.user.service.ImUserService;
|
||||
import com.sun.org.apache.regexp.internal.RE;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* @description:
|
||||
* @author: lld
|
||||
* @version: 1.0
|
||||
*/
|
||||
@Service
|
||||
public class CheckSendMessageService {
|
||||
|
||||
@Autowired
|
||||
ImUserService imUserService;
|
||||
|
||||
@Autowired
|
||||
ImFriendService imFriendService;
|
||||
|
||||
@Autowired
|
||||
ImGroupService imGroupService;
|
||||
|
||||
@Autowired
|
||||
ImGroupMemberService imGroupMemberService;
|
||||
|
||||
@Autowired
|
||||
AppConfig appConfig;
|
||||
|
||||
|
||||
public ResponseVO checkSenderForvidAndMute(String fromId,Integer appId){
|
||||
|
||||
ResponseVO<ImUserDataEntity> singleUserInfo
|
||||
= imUserService.getSingleUserInfo(fromId, appId);
|
||||
if(!singleUserInfo.isOk()){
|
||||
return singleUserInfo;
|
||||
}
|
||||
|
||||
ImUserDataEntity user = singleUserInfo.getData();
|
||||
if(user.getForbiddenFlag() == UserForbiddenFlagEnum.FORBIBBEN.getCode()){
|
||||
return ResponseVO.errorResponse(MessageErrorCode.FROMER_IS_FORBIBBEN);
|
||||
}else if (user.getSilentFlag() == UserSilentFlagEnum.MUTE.getCode()){
|
||||
return ResponseVO.errorResponse(MessageErrorCode.FROMER_IS_MUTE);
|
||||
}
|
||||
|
||||
return ResponseVO.successResponse();
|
||||
}
|
||||
|
||||
public ResponseVO checkFriendShip(String fromId,String toId,Integer appId){
|
||||
|
||||
if(appConfig.isSendMessageCheckFriend()){
|
||||
GetRelationReq fromReq = new GetRelationReq();
|
||||
fromReq.setFromId(fromId);
|
||||
fromReq.setToId(toId);
|
||||
fromReq.setAppId(appId);
|
||||
ResponseVO<ImFriendShipEntity> fromRelation = imFriendService.getRelation(fromReq);
|
||||
if(!fromRelation.isOk()){
|
||||
return fromRelation;
|
||||
}
|
||||
GetRelationReq toReq = new GetRelationReq();
|
||||
fromReq.setFromId(toId);
|
||||
fromReq.setToId(fromId);
|
||||
fromReq.setAppId(appId);
|
||||
ResponseVO<ImFriendShipEntity> toRelation = imFriendService.getRelation(fromReq);
|
||||
if(!toRelation.isOk()){
|
||||
return toRelation;
|
||||
}
|
||||
|
||||
if(FriendShipStatusEnum.FRIEND_STATUS_NORMAL.getCode()
|
||||
!= fromRelation.getData().getStatus()){
|
||||
return ResponseVO.errorResponse(FriendShipErrorCode.FRIEND_IS_DELETED);
|
||||
}
|
||||
|
||||
if(FriendShipStatusEnum.FRIEND_STATUS_NORMAL.getCode()
|
||||
!= toRelation.getData().getStatus()){
|
||||
return ResponseVO.errorResponse(FriendShipErrorCode.FRIEND_IS_DELETED);
|
||||
}
|
||||
|
||||
if(appConfig.isSendMessageCheckBlack()){
|
||||
if(FriendShipStatusEnum.BLACK_STATUS_NORMAL.getCode()
|
||||
!= fromRelation.getData().getBlack()){
|
||||
return ResponseVO.errorResponse(FriendShipErrorCode.FRIEND_IS_BLACK);
|
||||
}
|
||||
|
||||
if(FriendShipStatusEnum.BLACK_STATUS_NORMAL.getCode()
|
||||
!= toRelation.getData().getBlack()){
|
||||
return ResponseVO.errorResponse(FriendShipErrorCode.TARGET_IS_BLACK_YOU);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ResponseVO.successResponse();
|
||||
}
|
||||
public ResponseVO checkGroupMessage(String fromId,String groupId,Integer appId){
|
||||
|
||||
ResponseVO responseVO = checkSenderForvidAndMute(fromId, appId);
|
||||
if(!responseVO.isOk()){
|
||||
return responseVO;
|
||||
}
|
||||
|
||||
//判断群逻辑
|
||||
ResponseVO<ImGroupEntity> group = imGroupService.getGroup(groupId, appId);
|
||||
if(!group.isOk()){
|
||||
return group;
|
||||
}
|
||||
|
||||
//判断群成员是否在群内
|
||||
ResponseVO<GetRoleInGroupResp> roleInGroupOne = imGroupMemberService.getRoleInGroupOne(groupId, fromId, appId);
|
||||
if(!roleInGroupOne.isOk()){
|
||||
return roleInGroupOne;
|
||||
}
|
||||
GetRoleInGroupResp data = roleInGroupOne.getData();
|
||||
|
||||
//判断群是否被禁言
|
||||
//如果禁言 只有裙管理和群主可以发言
|
||||
ImGroupEntity groupData = group.getData();
|
||||
if(groupData.getMute() == GroupMuteTypeEnum.MUTE.getCode()
|
||||
&& (data.getRole() == GroupMemberRoleEnum.MAMAGER.getCode() ||
|
||||
data.getRole() == GroupMemberRoleEnum.OWNER.getCode() )){
|
||||
return ResponseVO.errorResponse(GroupErrorCode.THIS_GROUP_IS_MUTE);
|
||||
}
|
||||
|
||||
if(data.getSpeakDate() != null && data.getSpeakDate() > System.currentTimeMillis()){
|
||||
return ResponseVO.errorResponse(GroupErrorCode.GROUP_MEMBER_IS_SPEAK);
|
||||
}
|
||||
|
||||
return ResponseVO.successResponse();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,226 @@
|
||||
package com.lld.im.service.message.service;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.lld.im.common.config.AppConfig;
|
||||
import com.lld.im.common.constant.Constants;
|
||||
import com.lld.im.common.enums.ConversationTypeEnum;
|
||||
import com.lld.im.common.enums.DelFlagEnum;
|
||||
import com.lld.im.common.model.message.*;
|
||||
import com.lld.im.service.conversation.service.ConversationService;
|
||||
import com.lld.im.service.group.dao.ImGroupMessageHistoryEntity;
|
||||
import com.lld.im.service.group.dao.mapper.ImGroupMessageHistoryMapper;
|
||||
import com.lld.im.service.message.dao.ImMessageBodyEntity;
|
||||
import com.lld.im.service.message.dao.ImMessageHistoryEntity;
|
||||
import com.lld.im.service.message.dao.mapper.ImMessageBodyMapper;
|
||||
import com.lld.im.service.message.dao.mapper.ImMessageHistoryMapper;
|
||||
import com.lld.im.service.utils.SnowflakeIdWorker;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.data.redis.core.ZSetOperations;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @description:
|
||||
* @author: lld
|
||||
* @version: 1.0
|
||||
*/
|
||||
@Service
|
||||
public class MessageStoreService {
|
||||
|
||||
@Autowired
|
||||
ImMessageHistoryMapper imMessageHistoryMapper;
|
||||
|
||||
@Autowired
|
||||
ImMessageBodyMapper imMessageBodyMapper;
|
||||
|
||||
@Autowired
|
||||
SnowflakeIdWorker snowflakeIdWorker;
|
||||
|
||||
@Autowired
|
||||
ImGroupMessageHistoryMapper imGroupMessageHistoryMapper;
|
||||
|
||||
@Autowired
|
||||
RabbitTemplate rabbitTemplate;
|
||||
|
||||
@Autowired
|
||||
StringRedisTemplate stringRedisTemplate;
|
||||
|
||||
@Autowired
|
||||
ConversationService conversationService;
|
||||
|
||||
@Autowired
|
||||
AppConfig appConfig;
|
||||
|
||||
@Transactional
|
||||
public void storeP2PMessage(MessageContent messageContent){
|
||||
//messageContent 转化成 messageBody
|
||||
// ImMessageBody imMessageBodyEntity = extractMessageBody(messageContent);
|
||||
//插入messageBody
|
||||
// imMessageBodyMapper.insert(imMessageBodyEntity);
|
||||
// //转化成MessageHistory
|
||||
// List<ImMessageHistoryEntity> imMessageHistoryEntities = extractToP2PMessageHistory(messageContent, imMessageBodyEntity);
|
||||
// //批量插入
|
||||
// imMessageHistoryMapper.insertBatchSomeColumn(imMessageHistoryEntities);
|
||||
// messageContent.setMessageKey(imMessageBodyEntity.getMessageKey());
|
||||
ImMessageBody imMessageBodyEntity = extractMessageBody(messageContent);
|
||||
DoStoreP2PMessageDto dto = new DoStoreP2PMessageDto();
|
||||
dto.setMessageContent(messageContent);
|
||||
dto.setMessageBody(imMessageBodyEntity);
|
||||
messageContent.setMessageKey(imMessageBodyEntity.getMessageKey());
|
||||
rabbitTemplate.convertAndSend(Constants.RabbitConstants.StoreP2PMessage,"",
|
||||
JSONObject.toJSONString(dto));
|
||||
}
|
||||
|
||||
public ImMessageBody extractMessageBody(MessageContent messageContent){
|
||||
ImMessageBody messageBody = new ImMessageBody();
|
||||
messageBody.setAppId(messageContent.getAppId());
|
||||
messageBody.setMessageKey(snowflakeIdWorker.nextId());
|
||||
messageBody.setCreateTime(System.currentTimeMillis());
|
||||
messageBody.setSecurityKey("");
|
||||
messageBody.setExtra(messageContent.getExtra());
|
||||
messageBody.setDelFlag(DelFlagEnum.NORMAL.getCode());
|
||||
messageBody.setMessageTime(messageContent.getMessageTime());
|
||||
messageBody.setMessageBody(messageContent.getMessageBody());
|
||||
return messageBody;
|
||||
}
|
||||
|
||||
public List<ImMessageHistoryEntity> extractToP2PMessageHistory(MessageContent messageContent,
|
||||
ImMessageBodyEntity imMessageBodyEntity){
|
||||
List<ImMessageHistoryEntity> list = new ArrayList<>();
|
||||
ImMessageHistoryEntity fromHistory = new ImMessageHistoryEntity();
|
||||
BeanUtils.copyProperties(messageContent,fromHistory);
|
||||
fromHistory.setOwnerId(messageContent.getFromId());
|
||||
fromHistory.setMessageKey(imMessageBodyEntity.getMessageKey());
|
||||
fromHistory.setCreateTime(System.currentTimeMillis());
|
||||
|
||||
ImMessageHistoryEntity toHistory = new ImMessageHistoryEntity();
|
||||
BeanUtils.copyProperties(messageContent,toHistory);
|
||||
toHistory.setOwnerId(messageContent.getToId());
|
||||
toHistory.setMessageKey(imMessageBodyEntity.getMessageKey());
|
||||
toHistory.setCreateTime(System.currentTimeMillis());
|
||||
|
||||
list.add(fromHistory);
|
||||
list.add(toHistory);
|
||||
return list;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void storeGroupMessage(GroupChatMessageContent messageContent){
|
||||
ImMessageBody imMessageBody = extractMessageBody(messageContent);
|
||||
DoStoreGroupMessageDto dto = new DoStoreGroupMessageDto();
|
||||
dto.setMessageBody(imMessageBody);
|
||||
dto.setGroupChatMessageContent(messageContent);
|
||||
rabbitTemplate.convertAndSend(Constants.RabbitConstants.StoreGroupMessage,
|
||||
"",
|
||||
JSONObject.toJSONString(dto));
|
||||
messageContent.setMessageKey(imMessageBody.getMessageKey());
|
||||
}
|
||||
|
||||
private ImGroupMessageHistoryEntity extractToGroupMessageHistory(GroupChatMessageContent
|
||||
messageContent ,ImMessageBodyEntity messageBodyEntity){
|
||||
ImGroupMessageHistoryEntity result = new ImGroupMessageHistoryEntity();
|
||||
BeanUtils.copyProperties(messageContent,result);
|
||||
result.setGroupId(messageContent.getGroupId());
|
||||
result.setMessageKey(messageBodyEntity.getMessageKey());
|
||||
result.setCreateTime(System.currentTimeMillis());
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setMessageFromMessageIdCache(Integer appId,String messageId,Object messageContent){
|
||||
//appid : cache : messageId
|
||||
String key =appId + ":" + Constants.RedisConstants.cacheMessage + ":" + messageId;
|
||||
stringRedisTemplate.opsForValue().set(key,JSONObject.toJSONString(messageContent),300, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
public <T> T getMessageFromMessageIdCache(Integer appId,
|
||||
String messageId,Class<T> clazz){
|
||||
//appid : cache : messageId
|
||||
String key = appId + ":" + Constants.RedisConstants.cacheMessage + ":" + messageId;
|
||||
String msg = stringRedisTemplate.opsForValue().get(key);
|
||||
if(StringUtils.isBlank(msg)){
|
||||
return null;
|
||||
}
|
||||
return JSONObject.parseObject(msg, clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 存储单人离线消息
|
||||
* @param
|
||||
* @return void
|
||||
* @author lld
|
||||
*/
|
||||
public void storeOfflineMessage(OfflineMessageContent offlineMessage){
|
||||
|
||||
// 找到fromId的队列
|
||||
String fromKey = offlineMessage.getAppId() + ":" + Constants.RedisConstants.OfflineMessage + ":" + offlineMessage.getFromId();
|
||||
// 找到toId的队列
|
||||
String toKey = offlineMessage.getAppId() + ":" + Constants.RedisConstants.OfflineMessage + ":" + offlineMessage.getToId();
|
||||
|
||||
ZSetOperations<String, String> operations = stringRedisTemplate.opsForZSet();
|
||||
//判断 队列中的数据是否超过设定值
|
||||
if(operations.zCard(fromKey) > appConfig.getOfflineMessageCount()){
|
||||
operations.removeRange(fromKey,0,0);
|
||||
}
|
||||
offlineMessage.setConversationId(conversationService.convertConversationId(
|
||||
ConversationTypeEnum.P2P.getCode(),offlineMessage.getFromId(),offlineMessage.getToId()
|
||||
));
|
||||
// 插入 数据 根据messageKey 作为分值
|
||||
operations.add(fromKey,JSONObject.toJSONString(offlineMessage),
|
||||
offlineMessage.getMessageKey());
|
||||
|
||||
//判断 队列中的数据是否超过设定值
|
||||
if(operations.zCard(toKey) > appConfig.getOfflineMessageCount()){
|
||||
operations.removeRange(toKey,0,0);
|
||||
}
|
||||
|
||||
offlineMessage.setConversationId(conversationService.convertConversationId(
|
||||
ConversationTypeEnum.P2P.getCode(),offlineMessage.getToId(),offlineMessage.getFromId()
|
||||
));
|
||||
// 插入 数据 根据messageKey 作为分值
|
||||
operations.add(toKey,JSONObject.toJSONString(offlineMessage),
|
||||
offlineMessage.getMessageKey());
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @description: 存储单人离线消息
|
||||
* @param
|
||||
* @return void
|
||||
* @author lld
|
||||
*/
|
||||
public void storeGroupOfflineMessage(OfflineMessageContent offlineMessage
|
||||
,List<String> memberIds){
|
||||
|
||||
ZSetOperations<String, String> operations = stringRedisTemplate.opsForZSet();
|
||||
//判断 队列中的数据是否超过设定值
|
||||
offlineMessage.setConversationType(ConversationTypeEnum.GROUP.getCode());
|
||||
|
||||
for (String memberId : memberIds) {
|
||||
// 找到toId的队列
|
||||
String toKey = offlineMessage.getAppId() + ":" +
|
||||
Constants.RedisConstants.OfflineMessage + ":" +
|
||||
memberId;
|
||||
offlineMessage.setConversationId(conversationService.convertConversationId(
|
||||
ConversationTypeEnum.GROUP.getCode(),memberId,offlineMessage.getToId()
|
||||
));
|
||||
if(operations.zCard(toKey) > appConfig.getOfflineMessageCount()){
|
||||
operations.removeRange(toKey,0,0);
|
||||
}
|
||||
// 插入 数据 根据messageKey 作为分值
|
||||
operations.add(toKey,JSONObject.toJSONString(offlineMessage),
|
||||
offlineMessage.getMessageKey());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,255 @@
|
||||
package com.lld.im.service.message.service;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
|
||||
import com.lld.im.codec.pack.message.MessageReadedPack;
|
||||
import com.lld.im.codec.pack.message.RecallMessageNotifyPack;
|
||||
import com.lld.im.codec.proto.Message;
|
||||
import com.lld.im.common.ResponseVO;
|
||||
import com.lld.im.common.constant.Constants;
|
||||
import com.lld.im.common.enums.ConversationTypeEnum;
|
||||
import com.lld.im.common.enums.DelFlagEnum;
|
||||
import com.lld.im.common.enums.MessageErrorCode;
|
||||
import com.lld.im.common.enums.command.Command;
|
||||
import com.lld.im.common.enums.command.GroupEventCommand;
|
||||
import com.lld.im.common.enums.command.MessageCommand;
|
||||
import com.lld.im.common.model.ClientInfo;
|
||||
import com.lld.im.common.model.SyncReq;
|
||||
import com.lld.im.common.model.SyncResp;
|
||||
import com.lld.im.common.model.message.MessageReadedContent;
|
||||
import com.lld.im.common.model.message.MessageReciveAckContent;
|
||||
import com.lld.im.common.model.message.OfflineMessageContent;
|
||||
import com.lld.im.common.model.message.RecallMessageContent;
|
||||
import com.lld.im.service.conversation.service.ConversationService;
|
||||
import com.lld.im.service.group.service.ImGroupMemberService;
|
||||
import com.lld.im.service.message.dao.ImMessageBodyEntity;
|
||||
import com.lld.im.service.message.dao.mapper.ImMessageBodyMapper;
|
||||
import com.lld.im.service.seq.RedisSeq;
|
||||
import com.lld.im.service.utils.ConversationIdGenerate;
|
||||
import com.lld.im.service.utils.GroupMessageProducer;
|
||||
import com.lld.im.service.utils.MessageProducer;
|
||||
import com.lld.im.service.utils.SnowflakeIdWorker;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.DefaultTypedTuple;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.core.ZSetOperations;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @description:
|
||||
* @author: lld
|
||||
* @version: 1.0
|
||||
*/
|
||||
@Service
|
||||
public class MessageSyncService {
|
||||
|
||||
@Autowired
|
||||
MessageProducer messageProducer;
|
||||
|
||||
@Autowired
|
||||
ConversationService conversationService;
|
||||
|
||||
@Autowired
|
||||
RedisTemplate redisTemplate;
|
||||
|
||||
@Autowired
|
||||
ImMessageBodyMapper imMessageBodyMapper;
|
||||
|
||||
@Autowired
|
||||
RedisSeq redisSeq;
|
||||
|
||||
@Autowired
|
||||
SnowflakeIdWorker snowflakeIdWorker;
|
||||
|
||||
@Autowired
|
||||
ImGroupMemberService imGroupMemberService;
|
||||
|
||||
@Autowired
|
||||
GroupMessageProducer groupMessageProducer;
|
||||
|
||||
|
||||
public void receiveMark(MessageReciveAckContent messageReciveAckContent){
|
||||
messageProducer.sendToUser(messageReciveAckContent.getToId(),
|
||||
MessageCommand.MSG_RECIVE_ACK,messageReciveAckContent,messageReciveAckContent.getAppId());
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 消息已读。更新会话的seq,通知在线的同步端发送指定command ,发送已读回执通知对方(消息发起方)我已读
|
||||
* @param
|
||||
* @return void
|
||||
* @author lld
|
||||
*/
|
||||
public void readMark(MessageReadedContent messageContent) {
|
||||
conversationService.messageMarkRead(messageContent);
|
||||
MessageReadedPack messageReadedPack = new MessageReadedPack();
|
||||
BeanUtils.copyProperties(messageContent,messageReadedPack);
|
||||
syncToSender(messageReadedPack,messageContent,MessageCommand.MSG_READED_NOTIFY);
|
||||
//发送给对方
|
||||
messageProducer.sendToUser(messageContent.getToId(),
|
||||
MessageCommand.MSG_READED_RECEIPT,messageReadedPack,messageContent.getAppId());
|
||||
}
|
||||
|
||||
private void syncToSender(MessageReadedPack pack, MessageReadedContent content, Command command){
|
||||
MessageReadedPack messageReadedPack = new MessageReadedPack();
|
||||
// BeanUtils.copyProperties(messageReadedContent,messageReadedPack);
|
||||
//发送给自己的其他端
|
||||
messageProducer.sendToUserExceptClient(pack.getFromId(),
|
||||
command,pack,
|
||||
content);
|
||||
}
|
||||
|
||||
public void groupReadMark(MessageReadedContent messageReaded) {
|
||||
conversationService.messageMarkRead(messageReaded);
|
||||
MessageReadedPack messageReadedPack = new MessageReadedPack();
|
||||
BeanUtils.copyProperties(messageReaded,messageReadedPack);
|
||||
syncToSender(messageReadedPack,messageReaded, GroupEventCommand.MSG_GROUP_READED_NOTIFY
|
||||
);
|
||||
if(!messageReaded.getFromId().equals(messageReaded.getToId())){
|
||||
messageProducer.sendToUser(messageReadedPack.getToId(),GroupEventCommand.MSG_GROUP_READED_RECEIPT
|
||||
,messageReaded,messageReaded.getAppId());
|
||||
}
|
||||
}
|
||||
|
||||
public ResponseVO syncOfflineMessage(SyncReq req) {
|
||||
|
||||
SyncResp<OfflineMessageContent> resp = new SyncResp<>();
|
||||
|
||||
String key = req.getAppId() + ":" + Constants.RedisConstants.OfflineMessage + ":" + req.getOperater();
|
||||
//获取最大的seq
|
||||
Long maxSeq = 0L;
|
||||
ZSetOperations zSetOperations = redisTemplate.opsForZSet();
|
||||
Set set = zSetOperations.reverseRangeWithScores(key, 0, 0);
|
||||
if(!CollectionUtils.isEmpty(set)){
|
||||
List list = new ArrayList(set);
|
||||
DefaultTypedTuple o = (DefaultTypedTuple) list.get(0);
|
||||
maxSeq = o.getScore().longValue();
|
||||
}
|
||||
|
||||
List<OfflineMessageContent> respList = new ArrayList<>();
|
||||
resp.setMaxSequence(maxSeq);
|
||||
|
||||
Set<ZSetOperations.TypedTuple> querySet = zSetOperations.rangeByScoreWithScores(key,
|
||||
req.getLastSequence(), maxSeq, 0, req.getMaxLimit());
|
||||
for (ZSetOperations.TypedTuple<String> typedTuple : querySet) {
|
||||
String value = typedTuple.getValue();
|
||||
OfflineMessageContent offlineMessageContent = JSONObject.parseObject(value, OfflineMessageContent.class);
|
||||
respList.add(offlineMessageContent);
|
||||
}
|
||||
resp.setDataList(respList);
|
||||
|
||||
if(!CollectionUtils.isEmpty(respList)){
|
||||
OfflineMessageContent offlineMessageContent = respList.get(respList.size() - 1);
|
||||
resp.setCompleted(maxSeq <= offlineMessageContent.getMessageKey());
|
||||
}
|
||||
|
||||
return ResponseVO.successResponse(resp);
|
||||
}
|
||||
|
||||
//修改历史消息的状态
|
||||
//修改离线消息的状态
|
||||
//ack给发送方
|
||||
//发送给同步端
|
||||
//分发给消息的接收方
|
||||
public void recallMessage(RecallMessageContent content) {
|
||||
|
||||
Long messageTime = content.getMessageTime();
|
||||
Long now = System.currentTimeMillis();
|
||||
|
||||
RecallMessageNotifyPack pack = new RecallMessageNotifyPack();
|
||||
BeanUtils.copyProperties(content,pack);
|
||||
|
||||
if(120000L < now - messageTime){
|
||||
recallAck(pack,ResponseVO.errorResponse(MessageErrorCode.MESSAGE_RECALL_TIME_OUT),content);
|
||||
return;
|
||||
}
|
||||
|
||||
QueryWrapper<ImMessageBodyEntity> query = new QueryWrapper<>();
|
||||
query.eq("app_id",content.getAppId());
|
||||
query.eq("message_key",content.getMessageKey());
|
||||
ImMessageBodyEntity body = imMessageBodyMapper.selectOne(query);
|
||||
|
||||
if(body == null){
|
||||
//TODO ack失败 不存在的消息不能撤回
|
||||
recallAck(pack,ResponseVO.errorResponse(MessageErrorCode.MESSAGEBODY_IS_NOT_EXIST),content);
|
||||
return;
|
||||
}
|
||||
|
||||
if(body.getDelFlag() == DelFlagEnum.DELETE.getCode()){
|
||||
recallAck(pack,ResponseVO.errorResponse(MessageErrorCode.MESSAGE_IS_RECALLED),content);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
body.setDelFlag(DelFlagEnum.DELETE.getCode());
|
||||
imMessageBodyMapper.update(body,query);
|
||||
|
||||
if(content.getConversationType() == ConversationTypeEnum.P2P.getCode()){
|
||||
|
||||
// 找到fromId的队列
|
||||
String fromKey = content.getAppId() + ":" + Constants.RedisConstants.OfflineMessage + ":" + content.getFromId();
|
||||
// 找到toId的队列
|
||||
String toKey = content.getAppId() + ":" + Constants.RedisConstants.OfflineMessage + ":" + content.getToId();
|
||||
|
||||
OfflineMessageContent offlineMessageContent = new OfflineMessageContent();
|
||||
BeanUtils.copyProperties(content,offlineMessageContent);
|
||||
offlineMessageContent.setDelFlag(DelFlagEnum.DELETE.getCode());
|
||||
offlineMessageContent.setMessageKey(content.getMessageKey());
|
||||
offlineMessageContent.setConversationType(ConversationTypeEnum.P2P.getCode());
|
||||
offlineMessageContent.setConversationId(conversationService.convertConversationId(offlineMessageContent.getConversationType()
|
||||
,content.getFromId(),content.getToId()));
|
||||
offlineMessageContent.setMessageBody(body.getMessageBody());
|
||||
|
||||
long seq = redisSeq.doGetSeq(content.getAppId() + ":" + Constants.SeqConstants.Message + ":" + ConversationIdGenerate.generateP2PId(content.getFromId(),content.getToId()));
|
||||
offlineMessageContent.setMessageSequence(seq);
|
||||
|
||||
long messageKey = SnowflakeIdWorker.nextId();
|
||||
|
||||
redisTemplate.opsForZSet().add(fromKey,JSONObject.toJSONString(offlineMessageContent),messageKey);
|
||||
redisTemplate.opsForZSet().add(toKey,JSONObject.toJSONString(offlineMessageContent),messageKey);
|
||||
|
||||
//ack
|
||||
recallAck(pack,ResponseVO.successResponse(),content);
|
||||
//分发给同步端
|
||||
messageProducer.sendToUserExceptClient(content.getFromId(),
|
||||
MessageCommand.MSG_RECALL_NOTIFY,pack,content);
|
||||
//分发给对方
|
||||
messageProducer.sendToUser(content.getToId(),MessageCommand.MSG_RECALL_NOTIFY,
|
||||
pack,content.getAppId());
|
||||
}else{
|
||||
List<String> groupMemberId = imGroupMemberService.getGroupMemberId(content.getToId(), content.getAppId());
|
||||
long seq = redisSeq.doGetSeq(content.getAppId() + ":" + Constants.SeqConstants.Message + ":" + ConversationIdGenerate.generateP2PId(content.getFromId(),content.getToId()));
|
||||
//ack
|
||||
recallAck(pack,ResponseVO.successResponse(),content);
|
||||
//发送给同步端
|
||||
messageProducer.sendToUserExceptClient(content.getFromId(), MessageCommand.MSG_RECALL_NOTIFY, pack
|
||||
, content);
|
||||
for (String memberId : groupMemberId) {
|
||||
String toKey = content.getAppId() + ":" + Constants.SeqConstants.Message + ":" + memberId;
|
||||
OfflineMessageContent offlineMessageContent = new OfflineMessageContent();
|
||||
offlineMessageContent.setDelFlag(DelFlagEnum.DELETE.getCode());
|
||||
BeanUtils.copyProperties(content,offlineMessageContent);
|
||||
offlineMessageContent.setConversationType(ConversationTypeEnum.GROUP.getCode());
|
||||
offlineMessageContent.setConversationId(conversationService.convertConversationId(offlineMessageContent.getConversationType()
|
||||
,content.getFromId(),content.getToId()));
|
||||
offlineMessageContent.setMessageBody(body.getMessageBody());
|
||||
offlineMessageContent.setMessageSequence(seq);
|
||||
redisTemplate.opsForZSet().add(toKey,JSONObject.toJSONString(offlineMessageContent),seq);
|
||||
|
||||
groupMessageProducer.producer(content.getFromId(), MessageCommand.MSG_RECALL_NOTIFY, pack,content);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
private void recallAck(RecallMessageNotifyPack recallPack, ResponseVO<Object> success, ClientInfo clientInfo) {
|
||||
ResponseVO<Object> wrappedResp = success;
|
||||
messageProducer.sendToUser(recallPack.getFromId(),
|
||||
MessageCommand.MSG_RECALL_ACK, wrappedResp, clientInfo);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,238 @@
|
||||
package com.lld.im.service.message.service;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.lld.im.codec.pack.message.ChatMessageAck;
|
||||
import com.lld.im.codec.pack.message.MessageReciveServerAckPack;
|
||||
import com.lld.im.codec.proto.Message;
|
||||
import com.lld.im.common.ResponseVO;
|
||||
import com.lld.im.common.config.AppConfig;
|
||||
import com.lld.im.common.constant.Constants;
|
||||
import com.lld.im.common.enums.ConversationTypeEnum;
|
||||
import com.lld.im.common.enums.command.MessageCommand;
|
||||
import com.lld.im.common.model.ClientInfo;
|
||||
import com.lld.im.common.model.message.MessageContent;
|
||||
import com.lld.im.common.model.message.OfflineMessageContent;
|
||||
import com.lld.im.service.message.model.req.SendMessageReq;
|
||||
import com.lld.im.service.message.model.resp.SendMessageResp;
|
||||
import com.lld.im.service.seq.RedisSeq;
|
||||
import com.lld.im.service.utils.CallbackService;
|
||||
import com.lld.im.service.utils.ConversationIdGenerate;
|
||||
import com.lld.im.service.utils.MessageProducer;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.LinkedBlockingDeque;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* @description:
|
||||
* @author: lld
|
||||
* @version: 1.0
|
||||
*/
|
||||
@Service
|
||||
public class P2PMessageService {
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger(P2PMessageService.class);
|
||||
|
||||
@Autowired
|
||||
CheckSendMessageService checkSendMessageService;
|
||||
|
||||
@Autowired
|
||||
MessageProducer messageProducer;
|
||||
|
||||
@Autowired
|
||||
MessageStoreService messageStoreService;
|
||||
|
||||
@Autowired
|
||||
RedisSeq redisSeq;
|
||||
|
||||
@Autowired
|
||||
AppConfig appConfig;
|
||||
|
||||
@Autowired
|
||||
CallbackService callbackService;
|
||||
|
||||
|
||||
private final ThreadPoolExecutor threadPoolExecutor;
|
||||
|
||||
{
|
||||
final AtomicInteger num = new AtomicInteger(0);
|
||||
threadPoolExecutor = new ThreadPoolExecutor(8, 8, 60, TimeUnit.SECONDS,
|
||||
new LinkedBlockingDeque<>(1000), new ThreadFactory() {
|
||||
@Override
|
||||
public Thread newThread(Runnable r) {
|
||||
Thread thread = new Thread(r);
|
||||
thread.setDaemon(true);
|
||||
thread.setName("message-process-thread-" + num.getAndIncrement());
|
||||
return thread;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//离线
|
||||
//存储介质
|
||||
//1.mysql
|
||||
//2.redis
|
||||
//怎么存?
|
||||
//list
|
||||
|
||||
|
||||
//历史消息
|
||||
|
||||
//发送方客户端时间
|
||||
//messageKey
|
||||
//redis 1 2 3
|
||||
public void process(MessageContent messageContent){
|
||||
|
||||
logger.info("消息开始处理:{}",messageContent.getMessageId());
|
||||
String fromId = messageContent.getFromId();
|
||||
String toId = messageContent.getToId();
|
||||
Integer appId = messageContent.getAppId();
|
||||
|
||||
MessageContent messageFromMessageIdCache = messageStoreService.getMessageFromMessageIdCache
|
||||
(messageContent.getAppId(), messageContent.getMessageId(),MessageContent.class);
|
||||
if (messageFromMessageIdCache != null){
|
||||
threadPoolExecutor.execute(() ->{
|
||||
ack(messageContent,ResponseVO.successResponse());
|
||||
//2.发消息给同步在线端
|
||||
syncToSender(messageFromMessageIdCache,messageFromMessageIdCache);
|
||||
//3.发消息给对方在线端
|
||||
List<ClientInfo> clientInfos = dispatchMessage(messageFromMessageIdCache);
|
||||
if(clientInfos.isEmpty()){
|
||||
//发送接收确认给发送方,要带上是服务端发送的标识
|
||||
reciverAck(messageFromMessageIdCache);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
//回调
|
||||
ResponseVO responseVO = ResponseVO.successResponse();
|
||||
if(appConfig.isSendMessageAfterCallback()){
|
||||
responseVO = callbackService.beforeCallback(messageContent.getAppId(), Constants.CallbackCommand.SendMessageBefore
|
||||
, JSONObject.toJSONString(messageContent));
|
||||
}
|
||||
|
||||
if(!responseVO.isOk()){
|
||||
ack(messageContent,responseVO);
|
||||
return;
|
||||
}
|
||||
|
||||
long seq = redisSeq.doGetSeq(messageContent.getAppId() + ":"
|
||||
+ Constants.SeqConstants.Message+ ":" + ConversationIdGenerate.generateP2PId(
|
||||
messageContent.getFromId(),messageContent.getToId()
|
||||
));
|
||||
messageContent.setMessageSequence(seq);
|
||||
|
||||
//前置校验
|
||||
//这个用户是否被禁言 是否被禁用
|
||||
//发送方和接收方是否是好友
|
||||
// ResponseVO responseVO = imServerPermissionCheck(fromId, toId, appId);
|
||||
// if(responseVO.isOk()){
|
||||
threadPoolExecutor.execute(() ->{
|
||||
//appId + Seq + (from + to) groupId
|
||||
messageStoreService.storeP2PMessage(messageContent);
|
||||
|
||||
OfflineMessageContent offlineMessageContent = new OfflineMessageContent();
|
||||
BeanUtils.copyProperties(messageContent,offlineMessageContent);
|
||||
offlineMessageContent.setConversationType(ConversationTypeEnum.P2P.getCode());
|
||||
messageStoreService.storeOfflineMessage(offlineMessageContent);
|
||||
|
||||
//插入数据
|
||||
//1.回ack成功给自己
|
||||
ack(messageContent,ResponseVO.successResponse());
|
||||
//2.发消息给同步在线端
|
||||
syncToSender(messageContent,messageContent);
|
||||
//3.发消息给对方在线端
|
||||
List<ClientInfo> clientInfos = dispatchMessage(messageContent);
|
||||
|
||||
messageStoreService.setMessageFromMessageIdCache(messageContent.getAppId(),
|
||||
messageContent.getMessageId(),messageContent);
|
||||
if(clientInfos.isEmpty()){
|
||||
//发送接收确认给发送方,要带上是服务端发送的标识
|
||||
reciverAck(messageContent);
|
||||
}
|
||||
|
||||
if(appConfig.isSendMessageAfterCallback()){
|
||||
callbackService.callback(messageContent.getAppId(),Constants.CallbackCommand.SendMessageAfter,
|
||||
JSONObject.toJSONString(messageContent));
|
||||
}
|
||||
|
||||
logger.info("消息处理完成:{}",messageContent.getMessageId());
|
||||
});
|
||||
// }else{
|
||||
// //告诉客户端失败了
|
||||
// //ack
|
||||
// ack(messageContent,responseVO);
|
||||
// }
|
||||
}
|
||||
|
||||
private List<ClientInfo> dispatchMessage(MessageContent messageContent){
|
||||
List<ClientInfo> clientInfos = messageProducer.sendToUser(messageContent.getToId(), MessageCommand.MSG_P2P,
|
||||
messageContent, messageContent.getAppId());
|
||||
return clientInfos;
|
||||
}
|
||||
|
||||
private void ack(MessageContent messageContent,ResponseVO responseVO){
|
||||
logger.info("msg ack,msgId={},checkResut{}",messageContent.getMessageId(),responseVO.getCode());
|
||||
|
||||
ChatMessageAck chatMessageAck = new
|
||||
ChatMessageAck(messageContent.getMessageId(),messageContent.getMessageSequence());
|
||||
responseVO.setData(chatMessageAck);
|
||||
//發消息
|
||||
messageProducer.sendToUser(messageContent.getFromId(), MessageCommand.MSG_ACK,
|
||||
responseVO,messageContent
|
||||
);
|
||||
}
|
||||
|
||||
public void reciverAck(MessageContent messageContent){
|
||||
MessageReciveServerAckPack pack = new MessageReciveServerAckPack();
|
||||
pack.setFromId(messageContent.getToId());
|
||||
pack.setToId(messageContent.getFromId());
|
||||
pack.setMessageKey(messageContent.getMessageKey());
|
||||
pack.setMessageSequence(messageContent.getMessageSequence());
|
||||
pack.setServerSend(true);
|
||||
messageProducer.sendToUser(messageContent.getFromId(),MessageCommand.MSG_RECIVE_ACK,
|
||||
pack,new ClientInfo(messageContent.getAppId(),messageContent.getClientType()
|
||||
,messageContent.getImei()));
|
||||
}
|
||||
|
||||
private void syncToSender(MessageContent messageContent, ClientInfo clientInfo){
|
||||
messageProducer.sendToUserExceptClient(messageContent.getFromId(),
|
||||
MessageCommand.MSG_P2P,messageContent,messageContent);
|
||||
}
|
||||
|
||||
public ResponseVO imServerPermissionCheck(String fromId,String toId,
|
||||
Integer appId){
|
||||
ResponseVO responseVO = checkSendMessageService.checkSenderForvidAndMute(fromId, appId);
|
||||
if(!responseVO.isOk()){
|
||||
return responseVO;
|
||||
}
|
||||
responseVO = checkSendMessageService.checkFriendShip(fromId, toId, appId);
|
||||
return responseVO;
|
||||
}
|
||||
|
||||
public SendMessageResp send(SendMessageReq req) {
|
||||
|
||||
SendMessageResp sendMessageResp = new SendMessageResp();
|
||||
MessageContent message = new MessageContent();
|
||||
BeanUtils.copyProperties(req,message);
|
||||
//插入数据
|
||||
messageStoreService.storeP2PMessage(message);
|
||||
sendMessageResp.setMessageKey(message.getMessageKey());
|
||||
sendMessageResp.setMessageTime(System.currentTimeMillis());
|
||||
|
||||
//2.发消息给同步在线端
|
||||
syncToSender(message,message);
|
||||
//3.发消息给对方在线端
|
||||
dispatchMessage(message);
|
||||
return sendMessageResp;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.lld.im.service.seq;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* @description:
|
||||
* @author: lld
|
||||
* @version: 1.0
|
||||
*/
|
||||
@Service
|
||||
public class RedisSeq {
|
||||
|
||||
@Autowired
|
||||
StringRedisTemplate stringRedisTemplate;
|
||||
|
||||
public long doGetSeq(String key){
|
||||
return stringRedisTemplate.opsForValue().increment(key);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,14 +1,22 @@
|
||||
package com.lld.im.service.user.controller;
|
||||
|
||||
import com.lld.im.common.ClientType;
|
||||
import com.lld.im.common.ResponseVO;
|
||||
import com.lld.im.common.route.RouteHandle;
|
||||
import com.lld.im.common.route.RouteInfo;
|
||||
import com.lld.im.common.utils.RouteInfoParseUtil;
|
||||
import com.lld.im.service.user.model.req.*;
|
||||
import com.lld.im.service.user.service.ImUserService;
|
||||
import com.lld.im.service.user.service.ImUserStatusService;
|
||||
import com.lld.im.service.utils.ZKit;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @description:
|
||||
@@ -21,15 +29,96 @@ public class ImUserController {
|
||||
@Autowired
|
||||
ImUserService imUserService;
|
||||
|
||||
@Autowired
|
||||
RouteHandle routeHandle;
|
||||
|
||||
@Autowired
|
||||
ImUserStatusService imUserStatusService;
|
||||
|
||||
@Autowired
|
||||
ZKit zKit;
|
||||
|
||||
@RequestMapping("importUser")
|
||||
public ResponseVO importUser(@RequestBody ImportUserReq req, Integer appId) {
|
||||
req.setAppId(appId);
|
||||
return imUserService.importUser(req);
|
||||
}
|
||||
|
||||
|
||||
@RequestMapping("/deleteUser")
|
||||
public ResponseVO deleteUser(@RequestBody @Validated DeleteUserReq req, Integer appId) {
|
||||
req.setAppId(appId);
|
||||
return imUserService.deleteUser(req);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [req]
|
||||
* @return com.lld.im.common.ResponseVO
|
||||
* @description im的登录接口,返回im地址
|
||||
* @author chackylee
|
||||
*/
|
||||
@RequestMapping("/login")
|
||||
public ResponseVO login(@RequestBody @Validated LoginReq req, Integer appId) {
|
||||
req.setAppId(appId);
|
||||
|
||||
ResponseVO login = imUserService.login(req);
|
||||
if (login.isOk()) {
|
||||
List<String> allNode = new ArrayList<>();
|
||||
if (req.getClientType() == ClientType.WEB.getCode()) {
|
||||
allNode = zKit.getAllWebNode();
|
||||
} else {
|
||||
allNode = zKit.getAllTcpNode();
|
||||
}
|
||||
String s = routeHandle.routeServer(allNode, req
|
||||
.getUserId());
|
||||
RouteInfo parse = RouteInfoParseUtil.parse(s);
|
||||
return ResponseVO.successResponse(parse);
|
||||
}
|
||||
|
||||
return ResponseVO.errorResponse();
|
||||
}
|
||||
|
||||
@RequestMapping("/getUserSequence")
|
||||
public ResponseVO getUserSequence(@RequestBody @Validated
|
||||
GetUserSequenceReq req, Integer appId) {
|
||||
req.setAppId(appId);
|
||||
return imUserService.getUserSequence(req);
|
||||
}
|
||||
|
||||
@RequestMapping("/subscribeUserOnlineStatus")
|
||||
public ResponseVO subscribeUserOnlineStatus(@RequestBody @Validated
|
||||
SubscribeUserOnlineStatusReq req, Integer appId,String identifier) {
|
||||
req.setAppId(appId);
|
||||
req.setOperater(identifier);
|
||||
imUserStatusService.subscribeUserOnlineStatus(req);
|
||||
return ResponseVO.successResponse();
|
||||
}
|
||||
|
||||
@RequestMapping("/setUserCustomerStatus")
|
||||
public ResponseVO setUserCustomerStatus(@RequestBody @Validated
|
||||
SetUserCustomerStatusReq req, Integer appId,String identifier) {
|
||||
req.setAppId(appId);
|
||||
req.setOperater(identifier);
|
||||
imUserStatusService.setUserCustomerStatus(req);
|
||||
return ResponseVO.successResponse();
|
||||
}
|
||||
|
||||
@RequestMapping("/queryFriendOnlineStatus")
|
||||
public ResponseVO queryFriendOnlineStatus(@RequestBody @Validated
|
||||
PullFriendOnlineStatusReq req, Integer appId,String identifier) {
|
||||
req.setAppId(appId);
|
||||
req.setOperater(identifier);
|
||||
return ResponseVO.successResponse(imUserStatusService.queryFriendOnlineStatus(req));
|
||||
}
|
||||
|
||||
@RequestMapping("/queryUserOnlineStatus")
|
||||
public ResponseVO queryUserOnlineStatus(@RequestBody @Validated
|
||||
PullUserOnlineStatusReq req, Integer appId,String identifier) {
|
||||
req.setAppId(appId);
|
||||
req.setOperater(identifier);
|
||||
return ResponseVO.successResponse(imUserStatusService.queryUserOnlineStatus(req));
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.lld.im.service.user.model.req;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
|
||||
/**
|
||||
* @author: Chackylee
|
||||
* @description:
|
||||
**/
|
||||
@Data
|
||||
public class LoginReq {
|
||||
|
||||
@NotNull(message = "用户id不能位空")
|
||||
private String userId;
|
||||
|
||||
@NotNull(message = "appId不能为空")
|
||||
private Integer appId;
|
||||
|
||||
private Integer clientType;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.lld.im.service.user.model.req;
|
||||
|
||||
import com.lld.im.common.model.RequestBase;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @description:
|
||||
* @author: lld
|
||||
* @version: 1.0
|
||||
*/
|
||||
@Data
|
||||
public class PullFriendOnlineStatusReq extends RequestBase {
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.lld.im.service.user.model.req;
|
||||
|
||||
import com.lld.im.common.model.RequestBase;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @description:
|
||||
* @author: lld
|
||||
* @version: 1.0
|
||||
*/
|
||||
@Data
|
||||
public class PullUserOnlineStatusReq extends RequestBase {
|
||||
|
||||
private List<String> userList;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.lld.im.service.user.model.req;
|
||||
|
||||
import com.lld.im.common.model.RequestBase;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @description:
|
||||
* @author: lld
|
||||
* @version: 1.0
|
||||
*/
|
||||
@Data
|
||||
public class SetUserCustomerStatusReq extends RequestBase {
|
||||
|
||||
private String userId;
|
||||
|
||||
private String customText;
|
||||
|
||||
private Integer customStatus;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.lld.im.service.user.model.req;
|
||||
|
||||
import com.lld.im.common.model.RequestBase;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @description:
|
||||
* @author: lld
|
||||
* @version: 1.0
|
||||
*/
|
||||
@Data
|
||||
public class SubscribeUserOnlineStatusReq extends RequestBase {
|
||||
|
||||
private List<String> subUserId;
|
||||
|
||||
private Long subTime;
|
||||
|
||||
|
||||
}
|
||||
@@ -22,5 +22,8 @@ public interface ImUserService {
|
||||
|
||||
public ResponseVO modifyUserInfo(ModifyUserInfoReq req);
|
||||
|
||||
public ResponseVO login(LoginReq req);
|
||||
|
||||
ResponseVO getUserSequence(GetUserSequenceReq req);
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.lld.im.service.user.service;
|
||||
|
||||
import com.lld.im.common.ResponseVO;
|
||||
import com.lld.im.service.user.model.UserStatusChangeNotifyContent;
|
||||
import com.lld.im.service.user.model.req.PullFriendOnlineStatusReq;
|
||||
import com.lld.im.service.user.model.req.PullUserOnlineStatusReq;
|
||||
import com.lld.im.service.user.model.req.SetUserCustomerStatusReq;
|
||||
import com.lld.im.service.user.model.req.SubscribeUserOnlineStatusReq;
|
||||
import com.lld.im.service.user.model.resp.UserOnlineStatusResp;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @description:
|
||||
* @author: lld
|
||||
* @version: 1.0
|
||||
*/
|
||||
public interface ImUserStatusService {
|
||||
|
||||
public void processUserOnlineStatusNotify(UserStatusChangeNotifyContent content);
|
||||
|
||||
void subscribeUserOnlineStatus(SubscribeUserOnlineStatusReq req);
|
||||
|
||||
void setUserCustomerStatus(SetUserCustomerStatusReq req);
|
||||
|
||||
Map<String, UserOnlineStatusResp> queryFriendOnlineStatus(PullFriendOnlineStatusReq req);
|
||||
|
||||
Map<String, UserOnlineStatusResp> queryUserOnlineStatus(PullUserOnlineStatusReq req);
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
package com.lld.im.service.user.service.impl;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.lld.im.codec.pack.user.UserCustomStatusChangeNotifyPack;
|
||||
import com.lld.im.codec.pack.user.UserStatusChangeNotifyPack;
|
||||
import com.lld.im.common.ResponseVO;
|
||||
import com.lld.im.common.constant.Constants;
|
||||
import com.lld.im.common.enums.command.UserEventCommand;
|
||||
import com.lld.im.common.model.ClientInfo;
|
||||
import com.lld.im.common.model.UserSession;
|
||||
import com.lld.im.service.friendship.service.ImFriendService;
|
||||
import com.lld.im.service.user.model.UserStatusChangeNotifyContent;
|
||||
import com.lld.im.service.user.model.req.PullFriendOnlineStatusReq;
|
||||
import com.lld.im.service.user.model.req.PullUserOnlineStatusReq;
|
||||
import com.lld.im.service.user.model.req.SetUserCustomerStatusReq;
|
||||
import com.lld.im.service.user.model.req.SubscribeUserOnlineStatusReq;
|
||||
import com.lld.im.service.user.model.resp.UserOnlineStatusResp;
|
||||
import com.lld.im.service.user.service.ImUserStatusService;
|
||||
import com.lld.im.service.utils.MessageProducer;
|
||||
import com.lld.im.service.utils.UserSessionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @description:
|
||||
* @author: lld
|
||||
* @version: 1.0
|
||||
*/
|
||||
@Service
|
||||
public class ImUserStatusServiceImpl implements ImUserStatusService {
|
||||
|
||||
@Autowired
|
||||
UserSessionUtils userSessionUtils;
|
||||
|
||||
@Autowired
|
||||
MessageProducer messageProducer;
|
||||
|
||||
@Autowired
|
||||
ImFriendService imFriendService;
|
||||
|
||||
@Autowired
|
||||
StringRedisTemplate stringRedisTemplate;
|
||||
|
||||
@Override
|
||||
public void processUserOnlineStatusNotify(UserStatusChangeNotifyContent content) {
|
||||
|
||||
List<UserSession> userSession = userSessionUtils.getUserSession(content.getAppId(), content.getUserId());
|
||||
UserStatusChangeNotifyPack userStatusChangeNotifyPack = new UserStatusChangeNotifyPack();
|
||||
BeanUtils.copyProperties(content,userStatusChangeNotifyPack);
|
||||
userStatusChangeNotifyPack.setClient(userSession);
|
||||
|
||||
syncSender(userStatusChangeNotifyPack,content.getUserId(),
|
||||
content);
|
||||
|
||||
dispatcher(userStatusChangeNotifyPack,content.getUserId(),
|
||||
content.getAppId());
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void syncSender(Object pack, String userId, ClientInfo clientInfo){
|
||||
messageProducer.sendToUserExceptClient(userId,
|
||||
UserEventCommand.USER_ONLINE_STATUS_CHANGE_NOTIFY_SYNC,
|
||||
pack,clientInfo);
|
||||
}
|
||||
|
||||
private void dispatcher(Object pack,String userId,Integer appId){
|
||||
List<String> allFriendId = imFriendService.getAllFriendId(userId, appId);
|
||||
for (String fid : allFriendId) {
|
||||
messageProducer.sendToUser(fid,UserEventCommand.USER_ONLINE_STATUS_CHANGE_NOTIFY,
|
||||
pack,appId);
|
||||
}
|
||||
|
||||
String userKey = appId + ":" + Constants.RedisConstants.subscribe + userId;
|
||||
Set<Object> keys = stringRedisTemplate.opsForHash().keys(userKey);
|
||||
for (Object key : keys) {
|
||||
String filed = (String) key;
|
||||
Long expire = Long.valueOf((String) stringRedisTemplate.opsForHash().get(userKey, filed));
|
||||
if(expire > 0 && expire > System.currentTimeMillis()){
|
||||
messageProducer.sendToUser(filed,UserEventCommand.USER_ONLINE_STATUS_CHANGE_NOTIFY,
|
||||
pack,appId);
|
||||
}else{
|
||||
stringRedisTemplate.opsForHash().delete(userKey,filed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @description:
|
||||
* @param
|
||||
* @return void
|
||||
* @author lld
|
||||
*/
|
||||
@Override
|
||||
public void subscribeUserOnlineStatus(SubscribeUserOnlineStatusReq req) {
|
||||
// A
|
||||
// Z
|
||||
// A - B C D
|
||||
// C:A Z F
|
||||
//hash
|
||||
// B - [A:xxxx,C:xxxx]
|
||||
// C - []
|
||||
// D - []
|
||||
Long subExpireTime = 0L;
|
||||
if(req != null && req.getSubTime() > 0){
|
||||
subExpireTime = System.currentTimeMillis() + req.getSubTime();
|
||||
}
|
||||
|
||||
for (String beSubUserId : req.getSubUserId()) {
|
||||
String userKey = req.getAppId() + ":" + Constants.RedisConstants.subscribe + ":" + beSubUserId;
|
||||
stringRedisTemplate.opsForHash().put(userKey,req.getOperater(),subExpireTime.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 设置自定义状态
|
||||
* @param
|
||||
* @return void
|
||||
* @author lld
|
||||
*/
|
||||
@Override
|
||||
public void setUserCustomerStatus(SetUserCustomerStatusReq req) {
|
||||
UserCustomStatusChangeNotifyPack userCustomStatusChangeNotifyPack = new UserCustomStatusChangeNotifyPack();
|
||||
userCustomStatusChangeNotifyPack.setCustomStatus(req.getCustomStatus());
|
||||
userCustomStatusChangeNotifyPack.setCustomText(req.getCustomText());
|
||||
userCustomStatusChangeNotifyPack.setUserId(req.getUserId());
|
||||
stringRedisTemplate.opsForValue().set(req.getAppId()
|
||||
+":"+ Constants.RedisConstants.userCustomerStatus + ":" + req.getUserId()
|
||||
,JSONObject.toJSONString(userCustomStatusChangeNotifyPack));
|
||||
|
||||
syncSender(userCustomStatusChangeNotifyPack,
|
||||
req.getUserId(),new ClientInfo(req.getAppId(),req.getClientType(),req.getImei()));
|
||||
dispatcher(userCustomStatusChangeNotifyPack,req.getUserId(),req.getAppId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, UserOnlineStatusResp> queryFriendOnlineStatus(PullFriendOnlineStatusReq req) {
|
||||
|
||||
List<String> allFriendId = imFriendService.getAllFriendId(req.getOperater(), req.getAppId());
|
||||
return getUserOnlineStatus(allFriendId,req.getAppId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, UserOnlineStatusResp> queryUserOnlineStatus(PullUserOnlineStatusReq req) {
|
||||
return getUserOnlineStatus(req.getUserList(),req.getAppId());
|
||||
}
|
||||
|
||||
private Map<String, UserOnlineStatusResp> getUserOnlineStatus(List<String> userId,Integer appId){
|
||||
|
||||
Map<String, UserOnlineStatusResp> result = new HashMap<>(userId.size());
|
||||
for (String uid : userId) {
|
||||
|
||||
UserOnlineStatusResp resp = new UserOnlineStatusResp();
|
||||
List<UserSession> userSession = userSessionUtils.getUserSession(appId, uid);
|
||||
resp.setSession(userSession);
|
||||
String userKey = appId + ":" + Constants.RedisConstants.userCustomerStatus + ":" + uid;
|
||||
String s = stringRedisTemplate.opsForValue().get(userKey);
|
||||
if(StringUtils.isNotBlank(s)){
|
||||
JSONObject parse = (JSONObject) JSON.parse(s);
|
||||
resp.setCustomText(parse.getString("customText"));
|
||||
resp.setCustomStatus(parse.getInteger("customStatus"));
|
||||
}
|
||||
result.put(uid,resp);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,9 +2,13 @@ package com.lld.im.service.user.service.impl;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.lld.im.codec.pack.user.UserModifyPack;
|
||||
import com.lld.im.common.ResponseVO;
|
||||
import com.lld.im.common.config.AppConfig;
|
||||
import com.lld.im.common.constant.Constants;
|
||||
import com.lld.im.common.enums.DelFlagEnum;
|
||||
import com.lld.im.common.enums.UserErrorCode;
|
||||
import com.lld.im.common.enums.command.UserEventCommand;
|
||||
import com.lld.im.common.exception.ApplicationException;
|
||||
import com.lld.im.service.group.service.ImGroupService;
|
||||
import com.lld.im.service.user.dao.ImUserDataEntity;
|
||||
@@ -13,8 +17,11 @@ import com.lld.im.service.user.model.req.*;
|
||||
import com.lld.im.service.user.model.resp.GetUserInfoResp;
|
||||
import com.lld.im.service.user.model.resp.ImportUserResp;
|
||||
import com.lld.im.service.user.service.ImUserService;
|
||||
import com.lld.im.service.utils.CallbackService;
|
||||
import com.lld.im.service.utils.MessageProducer;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@@ -34,6 +41,17 @@ public class ImUserviceImpl implements ImUserService {
|
||||
@Autowired
|
||||
ImUserDataMapper imUserDataMapper;
|
||||
|
||||
@Autowired
|
||||
AppConfig appConfig;
|
||||
|
||||
@Autowired
|
||||
CallbackService callbackService;
|
||||
|
||||
@Autowired
|
||||
MessageProducer messageProducer;
|
||||
|
||||
@Autowired
|
||||
StringRedisTemplate stringRedisTemplate;
|
||||
|
||||
@Autowired
|
||||
ImGroupService imGroupService;
|
||||
@@ -165,9 +183,31 @@ public class ImUserviceImpl implements ImUserService {
|
||||
update.setUserId(null);
|
||||
int update1 = imUserDataMapper.update(update, query);
|
||||
if(update1 == 1){
|
||||
UserModifyPack pack = new UserModifyPack();
|
||||
BeanUtils.copyProperties(req,pack);
|
||||
messageProducer.sendToUser(req.getUserId(),req.getClientType(),req.getImei(),
|
||||
UserEventCommand.USER_MODIFY,pack,req.getAppId());
|
||||
|
||||
if(appConfig.isModifyUserAfterCallback()){
|
||||
callbackService.callback(req.getAppId(),
|
||||
Constants.CallbackCommand.ModifyUserAfter,
|
||||
JSONObject.toJSONString(req));
|
||||
}
|
||||
return ResponseVO.successResponse();
|
||||
}
|
||||
throw new ApplicationException(UserErrorCode.MODIFY_USER_ERROR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseVO login(LoginReq req) {
|
||||
return ResponseVO.successResponse();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseVO getUserSequence(GetUserSequenceReq req) {
|
||||
Map<Object, Object> map = stringRedisTemplate.opsForHash().entries(req.getAppId() + ":" + Constants.RedisConstants.SeqPrefix + ":" + req.getUserId());
|
||||
Long groupSeq = imGroupService.getUserGroupMaxSeq(req.getUserId(),req.getAppId());
|
||||
map.put(Constants.SeqConstants.Group,groupSeq);
|
||||
return ResponseVO.successResponse(map);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.lld.im.service.utils;
|
||||
|
||||
import com.lld.im.common.ResponseVO;
|
||||
import com.lld.im.common.config.AppConfig;
|
||||
import com.lld.im.common.utils.HttpRequestUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @description:
|
||||
* @author: lld
|
||||
* @version: 1.0
|
||||
*/
|
||||
@Component
|
||||
public class CallbackService {
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(CallbackService.class);
|
||||
|
||||
@Autowired
|
||||
HttpRequestUtils httpRequestUtils;
|
||||
|
||||
@Autowired
|
||||
AppConfig appConfig;
|
||||
|
||||
@Autowired
|
||||
ShareThreadPool shareThreadPool;
|
||||
|
||||
|
||||
public void callback(Integer appId,String callbackCommand,String jsonBody){
|
||||
shareThreadPool.submit(() -> {
|
||||
try {
|
||||
httpRequestUtils.doPost(appConfig.getCallbackUrl(),Object.class,builderUrlParams(appId,callbackCommand),
|
||||
jsonBody,null);
|
||||
}catch (Exception e){
|
||||
logger.error("callback 回调{} : {}出现异常 : {} ",callbackCommand , appId, e.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public ResponseVO beforeCallback(Integer appId,String callbackCommand,String jsonBody){
|
||||
try {
|
||||
ResponseVO responseVO = httpRequestUtils.doPost("", ResponseVO.class, builderUrlParams(appId, callbackCommand),
|
||||
jsonBody, null);
|
||||
return responseVO;
|
||||
}catch (Exception e){
|
||||
logger.error("callback 之前 回调{} : {}出现异常 : {} ",callbackCommand , appId, e.getMessage());
|
||||
return ResponseVO.successResponse();
|
||||
}
|
||||
}
|
||||
|
||||
public Map builderUrlParams(Integer appId, String command) {
|
||||
Map map = new HashMap();
|
||||
map.put("appId", appId);
|
||||
map.put("command", command);
|
||||
return map;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.lld.im.service.utils;
|
||||
|
||||
|
||||
/**
|
||||
* @author: Chackylee
|
||||
**/
|
||||
public class ConversationIdGenerate {
|
||||
|
||||
//A|B
|
||||
//B A
|
||||
public static String generateP2PId(String fromId,String toId){
|
||||
int i = fromId.compareTo(toId);
|
||||
if(i < 0){
|
||||
return toId+"|"+fromId;
|
||||
}else if(i > 0){
|
||||
return fromId+"|"+toId;
|
||||
}
|
||||
|
||||
throw new RuntimeException("");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
package com.lld.im.service.utils;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.lld.im.codec.pack.group.AddGroupMemberPack;
|
||||
import com.lld.im.codec.pack.group.RemoveGroupMemberPack;
|
||||
import com.lld.im.codec.pack.group.UpdateGroupMemberPack;
|
||||
import com.lld.im.common.ClientType;
|
||||
import com.lld.im.common.enums.command.Command;
|
||||
import com.lld.im.common.enums.command.GroupEventCommand;
|
||||
import com.lld.im.common.model.ClientInfo;
|
||||
import com.lld.im.service.group.model.req.GroupMemberDto;
|
||||
import com.lld.im.service.group.service.ImGroupMemberService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @description:
|
||||
* @author: lld
|
||||
* @version: 1.0
|
||||
*/
|
||||
@Component
|
||||
public class GroupMessageProducer {
|
||||
|
||||
@Autowired
|
||||
MessageProducer messageProducer;
|
||||
|
||||
@Autowired
|
||||
ImGroupMemberService imGroupMemberService;
|
||||
|
||||
public void producer(String userId, Command command, Object data,
|
||||
ClientInfo clientInfo){
|
||||
JSONObject o = (JSONObject) JSONObject.toJSON(data);
|
||||
String groupId = o.getString("groupId");
|
||||
List<String> groupMemberId = imGroupMemberService
|
||||
.getGroupMemberId(groupId, clientInfo.getAppId());
|
||||
|
||||
if(command.equals(GroupEventCommand.ADDED_MEMBER)){
|
||||
//发送给管理员和被加入人本身
|
||||
List<GroupMemberDto> groupManager = imGroupMemberService.getGroupManager(groupId, clientInfo.getAppId());
|
||||
AddGroupMemberPack addGroupMemberPack
|
||||
= o.toJavaObject(AddGroupMemberPack.class);
|
||||
List<String> members = addGroupMemberPack.getMembers();
|
||||
for (GroupMemberDto groupMemberDto : groupManager) {
|
||||
if(clientInfo.getClientType() != ClientType.WEBAPI.getCode() && groupMemberDto.getMemberId().equals(userId)){
|
||||
messageProducer.sendToUserExceptClient(groupMemberDto.getMemberId(),command,data,clientInfo);
|
||||
}else{
|
||||
messageProducer.sendToUser(groupMemberDto.getMemberId(),command,data,clientInfo.getAppId());
|
||||
}
|
||||
}
|
||||
for (String member : members) {
|
||||
if(clientInfo.getClientType() != ClientType.WEBAPI.getCode() && member.equals(userId)){
|
||||
messageProducer.sendToUserExceptClient(member,command,data,clientInfo);
|
||||
}else{
|
||||
messageProducer.sendToUser(member,command,data,clientInfo.getAppId());
|
||||
}
|
||||
}
|
||||
}else if(command.equals(GroupEventCommand.DELETED_MEMBER)){
|
||||
RemoveGroupMemberPack pack = o.toJavaObject(RemoveGroupMemberPack.class);
|
||||
String member = pack.getMember();
|
||||
List<String> members = imGroupMemberService.getGroupMemberId(groupId, clientInfo.getAppId());
|
||||
members.add(member);
|
||||
for (String memberId : members) {
|
||||
if(clientInfo.getClientType() != ClientType.WEBAPI.getCode() && member.equals(userId)){
|
||||
messageProducer.sendToUserExceptClient(memberId,command,data,clientInfo);
|
||||
}else{
|
||||
messageProducer.sendToUser(memberId,command,data,clientInfo.getAppId());
|
||||
}
|
||||
}
|
||||
}else if(command.equals(GroupEventCommand.UPDATED_MEMBER)){
|
||||
UpdateGroupMemberPack pack =
|
||||
o.toJavaObject(UpdateGroupMemberPack.class);
|
||||
String memberId = pack.getMemberId();
|
||||
List<GroupMemberDto> groupManager = imGroupMemberService.getGroupManager(groupId, clientInfo.getAppId());
|
||||
GroupMemberDto groupMemberDto = new GroupMemberDto();
|
||||
groupMemberDto.setMemberId(memberId);
|
||||
groupManager.add(groupMemberDto);
|
||||
for (GroupMemberDto member : groupManager) {
|
||||
if(clientInfo.getClientType() != ClientType.WEBAPI.getCode() && member.equals(userId)){
|
||||
messageProducer.sendToUserExceptClient(member.getMemberId(),command,data,clientInfo);
|
||||
}else{
|
||||
messageProducer.sendToUser(member.getMemberId(),command,data,clientInfo.getAppId());
|
||||
}
|
||||
}
|
||||
}else {
|
||||
for (String memberId : groupMemberId) {
|
||||
if(clientInfo.getClientType() != null && clientInfo.getClientType() !=
|
||||
ClientType.WEBAPI.getCode() && memberId.equals(userId)){
|
||||
messageProducer.sendToUserExceptClient(memberId,command,
|
||||
data,clientInfo);
|
||||
}else{
|
||||
messageProducer.sendToUser(memberId,command,data,clientInfo.getAppId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
package com.lld.im.service.utils;
|
||||
|
||||
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/**
|
||||
* @author: Chackylee
|
||||
* @description: 消息key生成
|
||||
**/
|
||||
public class MessageKeyGenerate {
|
||||
|
||||
//标识从2020.1.1开始
|
||||
private static final long T202001010000 = 1577808000000L;
|
||||
|
||||
// private Lock lock = new ReentrantLock();
|
||||
AtomicReference<Thread> owner = new AtomicReference<>();
|
||||
|
||||
private static volatile int rotateId = 0;
|
||||
private static int rotateIdWidth = 15;
|
||||
|
||||
private static int rotateIdMask = 32767;
|
||||
private static volatile long timeId = 0;
|
||||
|
||||
private int nodeId = 0;
|
||||
private static int nodeIdWidth = 6;
|
||||
private static int nodeIdMask = 63;
|
||||
|
||||
|
||||
public void setNodeId(int nodeId) {
|
||||
this.nodeId = nodeId;
|
||||
}
|
||||
|
||||
/**
|
||||
* ID = timestamp(43) + nodeId(6) + rotateId(15)
|
||||
*
|
||||
*/
|
||||
public synchronized long generateId() throws Exception {
|
||||
|
||||
// lock.lock();
|
||||
|
||||
this.lock();
|
||||
|
||||
rotateId = rotateId + 1;
|
||||
|
||||
long id = System.currentTimeMillis() - T202001010000;
|
||||
|
||||
//不同毫秒数生成的id要重置timeId和自选次数
|
||||
if (id > timeId) {
|
||||
timeId = id;
|
||||
rotateId = 1;
|
||||
} else if (id == timeId) {
|
||||
//表示是同一毫秒的请求
|
||||
if (rotateId == rotateIdMask) {
|
||||
//一毫秒只能发送32768到这里表示当前毫秒数已经超过了
|
||||
while (id <= timeId) {
|
||||
//重新给id赋值
|
||||
id = System.currentTimeMillis() - T202001010000;
|
||||
}
|
||||
this.unLock();
|
||||
return generateId();
|
||||
}
|
||||
}
|
||||
|
||||
id <<= nodeIdWidth;
|
||||
id += (nodeId & nodeIdMask);
|
||||
|
||||
|
||||
id <<= rotateIdWidth;
|
||||
id += rotateId;
|
||||
|
||||
// lock.unlock();
|
||||
this.unLock();
|
||||
return id;
|
||||
}
|
||||
|
||||
public static int getSharding(long mid) {
|
||||
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
|
||||
mid >>= nodeIdWidth;
|
||||
mid >>= rotateIdWidth;
|
||||
|
||||
calendar.setTime(new Date(T202001010000 + mid));
|
||||
|
||||
int month = calendar.get(Calendar.MONTH);
|
||||
int year = calendar.get(Calendar.YEAR);
|
||||
year %= 3;
|
||||
|
||||
return (year * 12 + month);
|
||||
}
|
||||
|
||||
public static long getMsgIdFromTimestamp(long timestamp) {
|
||||
long id = timestamp - T202001010000;
|
||||
|
||||
id <<= rotateIdWidth;
|
||||
id <<= nodeIdWidth;
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
public void lock() {
|
||||
Thread cur = Thread.currentThread();
|
||||
//lock函数将owner设置为当前线程,并且预测原来的值为空。
|
||||
// unlock函数将owner设置为null,并且预测值为当前线程。
|
||||
// 当有第二个线程调用lock操作时由于owner值不为空,导致循环
|
||||
//一直被执行,直至第一个线程调用unlock函数将owner设置为null,第二个线程才能进入临界区。
|
||||
while (!owner.compareAndSet(null, cur)){
|
||||
}
|
||||
}
|
||||
public void unLock() {
|
||||
Thread cur = Thread.currentThread();
|
||||
owner.compareAndSet(cur, null);
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
// try {
|
||||
// Calendar calendar = Calendar.getInstance();
|
||||
// MessageKeyGenerate messageKeyGenerate = new MessageKeyGenerate();
|
||||
// long msgIdFromTimestamp = getMsgIdFromTimestamp(1678459712000L);
|
||||
// System.out.println(getSharding(msgIdFromTimestamp));
|
||||
// } catch (Exception e) {
|
||||
//
|
||||
// }
|
||||
MessageKeyGenerate messageKeyGenerate = new MessageKeyGenerate();
|
||||
for (int i = 0; i < 10; i++) {
|
||||
long l = messageKeyGenerate.generateId();
|
||||
System.out.println(l);
|
||||
}
|
||||
// MessageKeyGenerate messageKeyGenerate = new MessageKeyGenerate();
|
||||
// long l = messageKeyGenerate.generateId();
|
||||
// System.out.println("生成了一个id:" + l);
|
||||
// int sharding = getSharding(l);
|
||||
// System.out.println("解密id的时间戳:" + sharding);
|
||||
|
||||
//im_message_history_12
|
||||
|
||||
|
||||
//10000 10001
|
||||
//0 1
|
||||
|
||||
long msgIdFromTimestamp = getMsgIdFromTimestamp(1734529845000L);
|
||||
int sharding = getSharding(msgIdFromTimestamp);
|
||||
System.out.println(sharding);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
package com.lld.im.service.utils;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.lld.im.codec.proto.MessagePack;
|
||||
import com.lld.im.common.ClientType;
|
||||
import com.lld.im.common.constant.Constants;
|
||||
import com.lld.im.common.enums.command.Command;
|
||||
import com.lld.im.common.model.ClientInfo;
|
||||
import com.lld.im.common.model.UserSession;
|
||||
import jdk.nashorn.internal.scripts.JO;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @description:
|
||||
* @author: lld
|
||||
* @version: 1.0
|
||||
*/
|
||||
@Service
|
||||
public class MessageProducer {
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger(MessageProducer.class);
|
||||
|
||||
@Autowired
|
||||
RabbitTemplate rabbitTemplate;
|
||||
|
||||
@Autowired
|
||||
UserSessionUtils userSessionUtils;
|
||||
|
||||
private String queueName = Constants.RabbitConstants.MessageService2Im;
|
||||
|
||||
public boolean sendMessage(UserSession session,Object msg){
|
||||
try {
|
||||
logger.info("send message == " + msg);
|
||||
rabbitTemplate.convertAndSend(queueName,session.getBrokerId()+"",msg);
|
||||
return true;
|
||||
}catch (Exception e){
|
||||
logger.error("send error :" + e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//包装数据,调用sendMessage
|
||||
public boolean sendPack(String toId, Command command,Object msg,UserSession session){
|
||||
MessagePack messagePack = new MessagePack();
|
||||
messagePack.setCommand(command.getCommand());
|
||||
messagePack.setToId(toId);
|
||||
messagePack.setClientType(session.getClientType());
|
||||
messagePack.setAppId(session.getAppId());
|
||||
messagePack.setImei(session.getImei());
|
||||
JSONObject jsonObject = JSONObject.parseObject(JSONObject.toJSONString(msg));
|
||||
messagePack.setData(jsonObject);
|
||||
|
||||
String body = JSONObject.toJSONString(messagePack);
|
||||
return sendMessage(session, body);
|
||||
}
|
||||
|
||||
//发送给所有端的方法
|
||||
public List<ClientInfo> sendToUser(String toId,Command command,Object data,Integer appId){
|
||||
List<UserSession> userSession
|
||||
= userSessionUtils.getUserSession(appId, toId);
|
||||
List<ClientInfo> list = new ArrayList<>();
|
||||
for (UserSession session : userSession) {
|
||||
boolean b = sendPack(toId, command, data, session);
|
||||
if(b){
|
||||
list.add(new ClientInfo(session.getAppId(),session.getClientType(),session.getImei()));
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public void sendToUser(String toId, Integer clientType,String imei, Command command,
|
||||
Object data, Integer appId){
|
||||
if(clientType != null && StringUtils.isNotBlank(imei)){
|
||||
ClientInfo clientInfo = new ClientInfo(appId, clientType, imei);
|
||||
sendToUserExceptClient(toId,command,data,clientInfo);
|
||||
}else{
|
||||
sendToUser(toId,command,data,appId);
|
||||
}
|
||||
}
|
||||
|
||||
//发送给某个用户的指定客户端
|
||||
public void sendToUser(String toId, Command command
|
||||
, Object data, ClientInfo clientInfo){
|
||||
UserSession userSession = userSessionUtils.getUserSession(clientInfo.getAppId(), toId, clientInfo.getClientType(),
|
||||
clientInfo.getImei());
|
||||
sendPack(toId,command,data,userSession);
|
||||
}
|
||||
|
||||
private boolean isMatch(UserSession sessionDto, ClientInfo clientInfo) {
|
||||
return Objects.equals(sessionDto.getAppId(), clientInfo.getAppId())
|
||||
&& Objects.equals(sessionDto.getImei(), clientInfo.getImei())
|
||||
&& Objects.equals(sessionDto.getClientType(), clientInfo.getClientType());
|
||||
}
|
||||
|
||||
//发送给除了某一端的其他端
|
||||
public void sendToUserExceptClient(String toId, Command command
|
||||
, Object data, ClientInfo clientInfo){
|
||||
List<UserSession> userSession = userSessionUtils
|
||||
.getUserSession(clientInfo.getAppId(),
|
||||
toId);
|
||||
for (UserSession session : userSession) {
|
||||
if(!isMatch(session,clientInfo)){
|
||||
sendPack(toId,command,data,session);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package com.lld.im.service.utils;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/**
|
||||
* @description 共享线程池
|
||||
* @author chackylee
|
||||
* @param
|
||||
* @return
|
||||
*/
|
||||
@Service
|
||||
public class ShareThreadPool {
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(ShareThreadPool.class);
|
||||
|
||||
private final ThreadPoolExecutor threadPoolExecutor;
|
||||
|
||||
{
|
||||
final AtomicInteger tNum = new AtomicInteger(0);
|
||||
|
||||
threadPoolExecutor = new ThreadPoolExecutor(8, 8, 120, TimeUnit.SECONDS, new LinkedBlockingQueue<>(2 << 20), new ThreadFactory() {
|
||||
@Override
|
||||
public Thread newThread(Runnable r) {
|
||||
Thread t = new Thread(r);
|
||||
t.setDaemon(true);
|
||||
t.setName("SHARE-Processor-" + tNum.getAndIncrement());
|
||||
return t;
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
private AtomicLong ind = new AtomicLong(0);
|
||||
|
||||
public void submit(Runnable r) {
|
||||
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
|
||||
|
||||
ind.incrementAndGet();
|
||||
|
||||
threadPoolExecutor.submit(() -> {
|
||||
long start = System.currentTimeMillis();
|
||||
try {
|
||||
r.run();
|
||||
} catch (Exception e) {
|
||||
logger.error("ShareThreadPool_ERROR", e);
|
||||
} finally {
|
||||
long end = System.currentTimeMillis();
|
||||
long dur = end - start;
|
||||
long andDecrement = ind.decrementAndGet();
|
||||
if (dur > 1000) {
|
||||
logger.warn("ShareThreadPool executed taskDone,remanent num = {},slow task fatal warning,costs time = {},stack: {}", andDecrement, dur, stackTrace);
|
||||
} else if (dur > 300) {
|
||||
logger.warn("ShareThreadPool executed taskDone,remanent num = {},slow task warning: {},costs time = {},", andDecrement,r, dur);
|
||||
} else {
|
||||
logger.debug("ShareThreadPool executed taskDone,remanent num = {}", andDecrement);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
package com.lld.im.service.utils;
|
||||
|
||||
|
||||
import cn.hutool.core.date.SystemClock;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @author: Chackylee
|
||||
* @description:
|
||||
**/
|
||||
@Slf4j
|
||||
public class SnowflakeIdWorker {
|
||||
|
||||
/**
|
||||
* 初始偏移时间戳
|
||||
*/
|
||||
private static final long OFFSET = 1546300800L;
|
||||
|
||||
/**
|
||||
* 机器id (0~15 保留 16~31作为备份机器)
|
||||
*/
|
||||
private static long WORKER_ID;
|
||||
|
||||
/**
|
||||
* 机器id所占位数 (5bit, 支持最大机器数 2^5 = 32)
|
||||
*/
|
||||
private static final long WORKER_ID_BITS = 5L;
|
||||
/**
|
||||
* 自增序列所占位数 (16bit, 支持最大每秒生成 2^16 = 65536)
|
||||
*/
|
||||
private static final long SEQUENCE_ID_BITS = 16L;
|
||||
/**
|
||||
* 机器id偏移位数
|
||||
*/
|
||||
private static final long WORKER_SHIFT_BITS = SEQUENCE_ID_BITS;
|
||||
/**
|
||||
* 自增序列偏移位数
|
||||
*/
|
||||
private static final long OFFSET_SHIFT_BITS = SEQUENCE_ID_BITS + WORKER_ID_BITS;
|
||||
/**
|
||||
* 机器标识最大值 (2^5 / 2 - 1 = 15)
|
||||
*/
|
||||
private static final long WORKER_ID_MAX = ((1 << WORKER_ID_BITS) - 1) >> 1;
|
||||
/**
|
||||
* 备份机器ID开始位置 (2^5 / 2 = 16)
|
||||
*/
|
||||
private static final long BACK_WORKER_ID_BEGIN = (1 << WORKER_ID_BITS) >> 1;
|
||||
/**
|
||||
* 自增序列最大值 (2^16 - 1 = 65535)
|
||||
*/
|
||||
private static final long SEQUENCE_MAX = (1 << SEQUENCE_ID_BITS) - 1;
|
||||
/**
|
||||
* 发生时间回拨时容忍的最大回拨时间 (秒)
|
||||
*/
|
||||
private static final long BACK_TIME_MAX = 1L;
|
||||
|
||||
/**
|
||||
* 上次生成ID的时间戳 (秒)
|
||||
*/
|
||||
private static long lastTimestamp = 0L;
|
||||
/**
|
||||
* 当前秒内序列 (2^16)
|
||||
*/
|
||||
private static long sequence = 0L;
|
||||
/**
|
||||
* 备份机器上次生成ID的时间戳 (秒)
|
||||
*/
|
||||
private static long lastTimestampBak = 0L;
|
||||
/**
|
||||
* 备份机器当前秒内序列 (2^16)
|
||||
*/
|
||||
private static long sequenceBak = 0L;
|
||||
|
||||
//==============================Constructors====================
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
*
|
||||
* @param workerId 工作ID (0~31)
|
||||
*/
|
||||
public SnowflakeIdWorker(long workerId) {
|
||||
if (workerId < 0 || workerId > WORKER_ID_MAX) {
|
||||
throw new IllegalArgumentException(String.format("cmallshop.workerId范围: 0 ~ %d 目前: %d", WORKER_ID_MAX, workerId));
|
||||
}
|
||||
WORKER_ID = workerId;
|
||||
}
|
||||
|
||||
// ==============================Methods=================================
|
||||
public static long nextId() {
|
||||
return nextId(SystemClock.now() / 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* 主机器自增序列
|
||||
*
|
||||
* @param timestamp 当前Unix时间戳
|
||||
* @return long
|
||||
*/
|
||||
private static synchronized long nextId(long timestamp) {
|
||||
// 时钟回拨检查
|
||||
if (timestamp < lastTimestamp) {
|
||||
// 发生时钟回拨
|
||||
log.warn("时钟回拨, 启用备份机器ID: now: [{}] last: [{}]", timestamp, lastTimestamp);
|
||||
return nextIdBackup(timestamp);
|
||||
}
|
||||
|
||||
// 开始下一秒
|
||||
if (timestamp != lastTimestamp) {
|
||||
lastTimestamp = timestamp;
|
||||
sequence = 0L;
|
||||
}
|
||||
if (0L == (++sequence & SEQUENCE_MAX)) {
|
||||
// 秒内序列用尽
|
||||
// log.warn("秒内[{}]序列用尽, 启用备份机器ID序列", timestamp);
|
||||
sequence--;
|
||||
return nextIdBackup(timestamp);
|
||||
}
|
||||
|
||||
return ((timestamp - OFFSET) << OFFSET_SHIFT_BITS) | (WORKER_ID << WORKER_SHIFT_BITS) | sequence;
|
||||
}
|
||||
|
||||
/**
|
||||
* 阻塞到下一个毫秒,直到获得新的时间戳
|
||||
*
|
||||
* @param lastTimestamp 上次生成ID的时间截
|
||||
* @return 当前时间戳
|
||||
*/
|
||||
protected long tilNextMillis(long lastTimestamp) {
|
||||
long timestamp = timeGen();
|
||||
while (timestamp <= lastTimestamp) {
|
||||
timestamp = timeGen();
|
||||
}
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* 备份机器自增序列
|
||||
* @param timestamp timestamp 当前Unix时间戳
|
||||
* @return long
|
||||
*/
|
||||
private static long nextIdBackup(long timestamp) {
|
||||
if (timestamp < lastTimestampBak) {
|
||||
if (lastTimestampBak - SystemClock.now() / 1000 <= BACK_TIME_MAX) {
|
||||
timestamp = lastTimestampBak;
|
||||
} else {
|
||||
throw new RuntimeException(String.format("时钟回拨: now: [%d] last: [%d]", timestamp, lastTimestampBak));
|
||||
}
|
||||
}
|
||||
|
||||
if (timestamp != lastTimestampBak) {
|
||||
lastTimestampBak = timestamp;
|
||||
sequenceBak = 0L;
|
||||
}
|
||||
|
||||
if (0L == (++sequenceBak & SEQUENCE_MAX)) {
|
||||
// 秒内序列用尽
|
||||
// logger.warn("秒内[{}]序列用尽, 备份机器ID借取下一秒序列", timestamp);
|
||||
return nextIdBackup(timestamp + 1);
|
||||
}
|
||||
|
||||
return ((timestamp - OFFSET) << OFFSET_SHIFT_BITS) | ((WORKER_ID ^ BACK_WORKER_ID_BEGIN) << WORKER_SHIFT_BITS) | sequenceBak;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 返回以毫秒为单位的当前时间
|
||||
*
|
||||
* @return 当前时间(毫秒)
|
||||
*/
|
||||
protected long timeGen() {
|
||||
return System.currentTimeMillis();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package com.lld.im.service.utils;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.lld.im.common.constant.Constants;
|
||||
import com.lld.im.common.enums.ImConnectStatusEnum;
|
||||
import com.lld.im.common.model.UserSession;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @description:
|
||||
* @author: lld
|
||||
* @version: 1.0
|
||||
*/
|
||||
@Component
|
||||
public class UserSessionUtils {
|
||||
|
||||
public Object get;
|
||||
@Autowired
|
||||
StringRedisTemplate stringRedisTemplate;
|
||||
|
||||
//1.获取用户所有的session
|
||||
|
||||
public List<UserSession> getUserSession(Integer appId,String userId){
|
||||
|
||||
String userSessionKey = appId + Constants.RedisConstants.UserSessionConstants
|
||||
+ userId;
|
||||
Map<Object, Object> entries =
|
||||
stringRedisTemplate.opsForHash().entries(userSessionKey);
|
||||
List<UserSession> list = new ArrayList<>();
|
||||
Collection<Object> values = entries.values();
|
||||
for (Object o : values){
|
||||
String str = (String) o;
|
||||
UserSession session =
|
||||
JSONObject.parseObject(str, UserSession.class);
|
||||
if(session.getConnectState() == ImConnectStatusEnum.ONLINE_STATUS.getCode()){
|
||||
list.add(session);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
//2.获取用户除了本端的session
|
||||
|
||||
//1.获取用户所有的session
|
||||
|
||||
public UserSession getUserSession(Integer appId,String userId
|
||||
,Integer clientType,String imei){
|
||||
|
||||
String userSessionKey = appId + Constants.RedisConstants.UserSessionConstants
|
||||
+ userId;
|
||||
String hashKey = clientType + ":" + imei;
|
||||
Object o = stringRedisTemplate.opsForHash().get(userSessionKey, hashKey);
|
||||
UserSession session =
|
||||
JSONObject.parseObject(o.toString(), UserSession.class);
|
||||
return session;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.lld.im.service.utils;
|
||||
|
||||
import com.lld.im.common.constant.Constants;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* @description:
|
||||
* @author: lld
|
||||
* @version: 1.0
|
||||
*/
|
||||
@Service
|
||||
public class WriteUserSeq {
|
||||
|
||||
//redis
|
||||
//uid friend 10
|
||||
// group 12
|
||||
// conversation 123
|
||||
@Autowired
|
||||
RedisTemplate redisTemplate;
|
||||
|
||||
public void writeUserSeq(Integer appId,String userId,String type,Long seq){
|
||||
String key = appId + ":" + Constants.RedisConstants.SeqPrefix + ":" + userId;
|
||||
redisTemplate.opsForHash().put(key,type,seq);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.lld.im.service.utils;
|
||||
|
||||
import com.lld.im.common.constant.Constants;
|
||||
import org.I0Itec.zkclient.ZkClient;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author: Chackylee
|
||||
* @description: Zookeeper 工具
|
||||
**/
|
||||
@Component
|
||||
public class ZKit {
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger(ZKit.class);
|
||||
|
||||
@Autowired
|
||||
private ZkClient zkClient;
|
||||
/**
|
||||
* get all TCP server node from zookeeper
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public List<String> getAllTcpNode() {
|
||||
List<String> children = zkClient.getChildren(Constants.ImCoreZkRoot + Constants.ImCoreZkRootTcp);
|
||||
// logger.info("Query all node =[{}] success.", JSON.toJSONString(children));
|
||||
return children;
|
||||
}
|
||||
|
||||
/**
|
||||
* get all WEB server node from zookeeper
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public List<String> getAllWebNode() {
|
||||
List<String> children = zkClient.getChildren(Constants.ImCoreZkRoot + Constants.ImCoreZkRootWeb);
|
||||
// logger.info("Query all node =[{}] success.", JSON.toJSONString(children));
|
||||
return children;
|
||||
}
|
||||
}
|
||||
@@ -7,17 +7,96 @@ spring:
|
||||
url: jdbc:mysql://192.168.2.201:3306/im-core?serverTimezone=UTC&useSSL=false&characterEncoding=UTF8
|
||||
username: root
|
||||
|
||||
redis:
|
||||
host: 43.139.191.204
|
||||
port: 6379
|
||||
database: 8
|
||||
jedis:
|
||||
pool:
|
||||
max-active: 100
|
||||
max-idle: 100
|
||||
max-wait: 1000
|
||||
min-idle: 10
|
||||
password: dSMIXBQrCBXiHHjk123
|
||||
rabbitmq:
|
||||
host: 192.168.2.180
|
||||
port: 5672
|
||||
addresses: 192.168.2.180
|
||||
username: guest
|
||||
password: guest
|
||||
# virtual-host:
|
||||
listener:
|
||||
simple:
|
||||
concurrency: 5
|
||||
max-concurrency: 10
|
||||
acknowledge-mode: MANUAL
|
||||
prefetch: 1
|
||||
publisher-confirms: true
|
||||
publisher-returns: true
|
||||
template:
|
||||
mandatory: true
|
||||
cache:
|
||||
connection:
|
||||
mode: channel
|
||||
channel:
|
||||
size: 36
|
||||
checkout-timeout: 0
|
||||
application:
|
||||
name: im-core
|
||||
|
||||
|
||||
# logger 配置
|
||||
logging:
|
||||
config: classpath:logback-spring.xml
|
||||
|
||||
server:
|
||||
port: 8000
|
||||
port: 28000
|
||||
|
||||
appConfig:
|
||||
appId: 10000
|
||||
privateKey: 123456
|
||||
zkAddr: 192.168.2.180:2181 # zk连接地址
|
||||
zkConnectTimeOut: 50000 #zk超时时间
|
||||
imRouteWay: 3 # 路由策略1轮训 2随机 3hash
|
||||
consistentHashWay: 1 # 如果选用一致性hash的话具体hash算法 1 TreeMap 2 自定义Map
|
||||
tcpPort: 19000 # tcp端口
|
||||
webSocketPort: 29000 # webSocket端口
|
||||
needWebSocket: true #是否需要开启webSocket
|
||||
loginModel: 1
|
||||
messageRecallTimeOut : 1200000000 #消息可撤回时间,单位毫秒
|
||||
# * 多端同步模式:1 只允许一端在线,手机/电脑/web 踢掉除了本client+imel的设备
|
||||
# * 2 允许手机/电脑的一台设备 + web在线 踢掉除了本client+imel的非web端设备
|
||||
# * 3 允许手机和电脑单设备 + web 同时在线 踢掉非本client+imel的同端设备
|
||||
# * 4 允许所有端多设备登录 不踢任何设备
|
||||
groupMaxMemberCount: 500
|
||||
sendMessageCheckFriend: false # 发送消息是否校验关系链
|
||||
sendMessageCheckBlack: false # 发送消息是否校验黑名单
|
||||
callbackUrl: http://127.0.0.1:8000/callback
|
||||
modifyUserAfterCallback: false # 用户资料变更之后回调开关
|
||||
addFriendAfterCallback: false # 添加好友之后回调开关
|
||||
addFriendBeforeCallback: false # 添加好友之前回调开关
|
||||
modifyFriendAfterCallback: false # 修改好友之后回调开关
|
||||
deleteFriendAfterCallback: false # 删除好友之后回调开关
|
||||
addFriendShipBlackAfterCallback: false #添加黑名单之后回调开关
|
||||
deleteFriendShipBlackAfterCallback: false #删除黑名单之后回调开关
|
||||
createGroupAfterCallback: false # 创建群聊之后回调开关
|
||||
modifyGroupAfterCallback: false # 修改群聊之后回调开关
|
||||
destroyGroupAfterCallback: false # 解散群聊之后回调开关
|
||||
deleteGroupMemberAfterCallback: false # 删除群成员之后回调
|
||||
addGroupMemberAfterCallback: false # 拉人入群之后回调
|
||||
addGroupMemberBeforeCallback: false # 拉人入群之前回调
|
||||
sendMessageAfterCallback: false # 发送单聊消息之后
|
||||
sendMessageBeforeCallback: false # 发送单聊消息之前
|
||||
sendGroupMessageAfterCallback: false # 发送群聊消息之后
|
||||
sendGroupMessageBeforeCallback: false # 发送群聊消息之前
|
||||
offlineMessageCount: 1000 #离线消息存储条数
|
||||
deleteConversationSyncMode: 1 #1多段同步
|
||||
|
||||
|
||||
mqQueueName: 123
|
||||
|
||||
mybatis-plus:
|
||||
|
||||
configuration:
|
||||
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
||||
mapper-locations: classpath*:mapper/*.xml
|
||||
@@ -36,3 +115,6 @@ httpclient:
|
||||
connectionRequestTimeout: 2000
|
||||
socketTimeout: 5000
|
||||
staleConnectionCheckEnabled: true
|
||||
|
||||
mpp:
|
||||
entityBasePath: com.lld.im.service.friendship.dao
|
||||
|
||||
Reference in New Issue
Block a user