【用户组仿真操作】

This commit is contained in:
weizhihong 2023-04-17 09:43:11 +08:00
parent cfe6eb91e0
commit 8fd00384d4
9 changed files with 435 additions and 53 deletions

View File

@ -2,7 +2,7 @@ package club.joylink.rtss.controller.simulation;
import club.joylink.rtss.controller.advice.AuthenticateInterceptor; import club.joylink.rtss.controller.advice.AuthenticateInterceptor;
import club.joylink.rtss.simulation.cbtc.conversation.CommunicationObject; import club.joylink.rtss.simulation.cbtc.conversation.CommunicationObject;
import club.joylink.rtss.simulation.cbtc.conversation.ConversationGroupManagerService; import club.joylink.rtss.simulation.cbtc.conversation.ConversationGroupHandlerService;
import club.joylink.rtss.simulation.cbtc.conversation.ConversationManagerService; import club.joylink.rtss.simulation.cbtc.conversation.ConversationManagerService;
import club.joylink.rtss.simulation.cbtc.conversation.ConversationText; import club.joylink.rtss.simulation.cbtc.conversation.ConversationText;
import club.joylink.rtss.simulation.cbtc.data.vo.ConversationGroupVO; import club.joylink.rtss.simulation.cbtc.data.vo.ConversationGroupVO;
@ -25,7 +25,7 @@ public class SimulationConversationController {
private ConversationManagerService conversationManagerService; private ConversationManagerService conversationManagerService;
@Autowired @Autowired
private ConversationGroupManagerService conversationGroupManagerService; private ConversationGroupHandlerService conversationGroupHandlerService;
/** /**
*根据会话id获取仿真会话 *根据会话id获取仿真会话
@ -113,6 +113,6 @@ public class SimulationConversationController {
*/ */
@GetMapping("/group/list") @GetMapping("/group/list")
public List<ConversationGroupVO> groupList(@PathVariable String group) { public List<ConversationGroupVO> groupList(@PathVariable String group) {
return conversationGroupManagerService.getAllConversationsGroup(group); return conversationGroupHandlerService.getAllConversationsGroup(group);
} }
} }

View File

@ -46,6 +46,7 @@ import java.time.ZoneId;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
@ -149,6 +150,11 @@ public class Simulation extends club.joylink.rtss.simulation.Simulation<Simulati
*/ */
private Map<String, Conversation> simulationConversationMap = new ConcurrentHashMap<>(); private Map<String, Conversation> simulationConversationMap = new ConcurrentHashMap<>();
/**
* 会话群组ID
*/
private AtomicLong conversationGroupId = new AtomicLong(0l);
/** /**
* 用户组会话 * 用户组会话
* key-用户组ID * key-用户组ID
@ -320,8 +326,19 @@ public class Simulation extends club.joylink.rtss.simulation.Simulation<Simulati
this.simulationConversationGroupMap.put(group.getId(), group); this.simulationConversationGroupMap.put(group.getId(), group);
} }
public void addConversationGroupMap(Map<Long, ConversationGroup> map) { public void initDefaultConversationGroupMap(Map<Long, ConversationGroup> map) {
this.simulationConversationGroupMap.putAll(map); this.simulationConversationGroupMap.putAll(map);
Long groupId = map.keySet().stream().max(Comparator.comparing(Long::longValue)).orElse(0L);
this.conversationGroupId.set(groupId);
}
public ConversationGroup getConversationGroupByName(String name) {
return this.simulationConversationGroupMap.values().stream().filter(group -> Objects.equals(group.getName(), name))
.findAny().orElse(null);
}
public Long getMaxConversationGroupId() {
return conversationGroupId.incrementAndGet();
} }
public ConversationGroup getConversationGroup(Long id) { public ConversationGroup getConversationGroup(Long id) {

View File

@ -390,6 +390,6 @@ public class SimulationServiceImpl implements SimulationService {
} }
Map<Long, ConversationGroup> groupMap = conversationGroupVOList.stream().map(g -> new ConversationGroup(simulation, g)) Map<Long, ConversationGroup> groupMap = conversationGroupVOList.stream().map(g -> new ConversationGroup(simulation, g))
.collect(Collectors.toMap(ConversationGroup::getId, group -> group)); .collect(Collectors.toMap(ConversationGroup::getId, group -> group));
simulation.addConversationGroupMap(groupMap); simulation.initDefaultConversationGroupMap(groupMap);
} }
} }

View File

@ -45,6 +45,11 @@ public abstract class Chat {
return false; return false;
} }
public ConversationMember getConversionMember(SimulationMember member) {
return this.memberList.stream().filter(m -> Objects.equals(m.getMember().getId(), member.getId()))
.findFirst().orElse(null);
}
public boolean isCreator(SimulationMember member) { public boolean isCreator(SimulationMember member) {
return Objects.equals(member, this.creator); return Objects.equals(member, this.creator);
} }

View File

@ -8,6 +8,7 @@ import lombok.Data;
import lombok.Getter; import lombok.Getter;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import java.time.LocalDateTime;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
@ -42,14 +43,31 @@ public class ConversationGroup extends Chat {
public ConversationGroup(Simulation simulation, ConversationGroupVO info) { public ConversationGroup(Simulation simulation, ConversationGroupVO info) {
this.id = info.getId(); this.id = info.getId();
this.name = info.getName(); this.name = info.getName();
setTime(simulation.getCorrectSystemTime());
SimulationMember leader = null; SimulationMember leader = null;
if (info.getLeaderId() != null) { if (info.getLeaderId() != null) {
leader = simulation.getSimulationMemberById(info.getLeaderId()); leader = simulation.getSimulationMemberById(info.getLeaderId());
setCreator(leader); setCreator(leader);
} }
List<ConversationMember> memberList = null; List<ConversationMember> memberList = null;
if (CollectionUtils.isEmpty(info.getMemberIds())) { if (!CollectionUtils.isEmpty(info.getMemberIds())) {
memberList = info.getMemberIds().stream().map(simulation::getSimulationMemberById).map(ConversationMember::new).collect(Collectors.toList()); memberList = info.getMemberIds().stream().distinct().map(simulation::getSimulationMemberById)
.map(ConversationMember::new).collect(Collectors.toList());
setMemberList(memberList);
}
}
public ConversationGroup(Long id, String name, LocalDateTime time, SimulationMember leader, List<SimulationMember> simulationMembers){
this.id = id;
this.name = name;
setTime(time);
setCreator(leader);
if (!CollectionUtils.isEmpty(simulationMembers)) {
List<ConversationMember> memberList = simulationMembers.stream().map(member -> {
ConversationMember conversationMember = new ConversationMember(member);
conversationMember.setTime(time);
return conversationMember;
}).collect(Collectors.toList());
setMemberList(memberList); setMemberList(memberList);
} }
} }
@ -74,34 +92,40 @@ public class ConversationGroup extends Chat {
* @return 消息列表 * @return 消息列表
*/ */
public List<ConversationGroupMessage> getUnreadMessages(SimulationMember member) { public List<ConversationGroupMessage> getUnreadMessages(SimulationMember member) {
ConversationMember conversationMember = getConversionMember(member);
// 没有消息不是本群人员 // 没有消息不是本群人员
if (CollectionUtils.isEmpty(getMessageList()) && !isCreator(member) && !isConversationMember(member)) { if (CollectionUtils.isEmpty(getMessageList()) && !isCreator(member) && conversationMember != null) {
return List.of(); return List.of();
} }
return messageList.stream().filter(message -> { return messageList.stream().filter(message -> {
if (message.isAllRead()) { // 全部已读 if (message.isAllRead()) { // 全部已读
return false; return false;
} }
if (message.getTime().isBefore(conversationMember.getTime())) { // 在用户加入之前发的消息
return false;
}
return !message.getReaderSet().contains(member.getId()); // 在已读名单中 return !message.getReaderSet().contains(member.getId()); // 在已读名单中
}).collect(Collectors.toList()); }).collect(Collectors.toList());
} }
/** /**
* 用户读消息 * 修改群组名称
* * @param name 名称
* @param member 角色信息
*/ */
public void readMessage(SimulationMember member){ public void setName(String name) {
// 没有消息不是本群人员 this.name = name;
List<ConversationGroupMessage> messageList = getUnreadMessages(member); }
if (CollectionUtils.isEmpty(messageList)) {
return; /**
* 增加群组成员
* @param member 成员
* @param time 时间
*/
public void addSimulationMember(SimulationMember member, LocalDateTime time) {
if (!contains(member)) {
ConversationMember conversationMember = new ConversationMember(member);
conversationMember.setTime(time);
getMemberList().add(conversationMember);
} }
messageList.forEach(message -> {
message.read(member);
if (message.getReaderSet().size() >= getMemberList().size()) { // 全部用户已读
message.finishRead();
}
});
} }
} }

View File

@ -0,0 +1,281 @@
package club.joylink.rtss.simulation.cbtc.conversation;
import club.joylink.rtss.services.voice.VoiceService;
import club.joylink.rtss.simulation.cbtc.GroupSimulationCache;
import club.joylink.rtss.simulation.cbtc.Simulation;
import club.joylink.rtss.simulation.cbtc.data.vo.ConversationGroupMessageVO;
import club.joylink.rtss.simulation.cbtc.data.vo.ConversationGroupVO;
import club.joylink.rtss.simulation.cbtc.exception.SimulationException;
import club.joylink.rtss.simulation.cbtc.exception.SimulationExceptionType;
import club.joylink.rtss.simulation.cbtc.member.SimulationMember;
import club.joylink.rtss.vo.client.voice.VoiceRecognitionResult;
import club.joylink.rtss.vo.client.voice.VoiceRecognitionVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.util.*;
import java.util.function.Supplier;
import java.util.stream.Collectors;
@Slf4j
@Service
public class ConversationGroupHandlerService {
@Autowired
private GroupSimulationCache groupSimulationCache;
@Autowired
private VoiceService iVoiceService;
@Autowired
private SimulationVoiceHandler simulationVoiceHandler;
/**
* 获取所有群组信息
* @param group
* @return
*/
public List<ConversationGroupVO> getAllConversationsGroup(String group) {
Simulation simulation = groupSimulationCache.getSimulationByGroup(group);
List<ConversationGroup> allGroup = simulation.queryAllConversationGroup();
return ConversationGroupVO.convert2VOList(allGroup);
}
/**
* 创建群组会话
*
* @param simulation 仿真
* @param member 用户
* @param name 群组名称
* @param memberIds 群组成员
*/
public ConversationGroupVO createConversationGroup(Simulation simulation, SimulationMember member, String name, List<String> memberIds) {
ConversationGroup conversationGroup = simulation.getConversationGroupByName(name);
if (conversationGroup != null) {
throw new SimulationException(SimulationExceptionType.Illegal_Argument, "" + name + "】已存在");
}
List<SimulationMember> simulationMembers = memberIds.stream().map(simulation::getSimulationMemberById).collect(Collectors.toList());
Long groupId = simulation.getMaxConversationGroupId();
conversationGroup = new ConversationGroup(groupId, name, simulation.getCorrectSystemTime(), member, simulationMembers);
simulation.addConversationGroup(conversationGroup);
// 通知用户消息 TODO
return new ConversationGroupVO(conversationGroup);
}
/**
* 修改会话群组名称
*
* @param simulation 仿真
* @param member 用户
* @param groupId 群组主键
* @param name 群组名称
*/
public void updateConversationGroup(Simulation simulation, SimulationMember member, Long groupId, String name) {
ConversationGroup conversationGroup = checkGroupIdAndReturn(simulation, member, groupId);
// 查找名称是否已存在
ConversationGroup nameConversationGroup = simulation.getConversationGroupByName(name);
if (nameConversationGroup != null && !Objects.equals(groupId, nameConversationGroup.getId())) {
throw new SimulationException(SimulationExceptionType.Illegal_Argument, "" + name + "】已存在");
} else if (nameConversationGroup != null && Objects.equals(groupId, nameConversationGroup.getId())) {
return;
}
conversationGroup.setName(name);
// 通知其他用户群名变更 TODO
}
/**
* 设置会话群群主
*
* @param simulation 仿真
* @param member 用户
* @param groupId 群组主键
* @param memberId 目标用户
*/
public void setConversationGroupLeader(Simulation simulation, SimulationMember member, Long groupId, String memberId) {
ConversationGroup conversationGroup = checkGroupIdAndReturn(simulation, member, groupId);
SimulationMember simulationMember = simulation.getSimulationMemberById(memberId);
if (!conversationGroup.isConversationMember(simulationMember)) {
conversationGroup.addSimulationMember(simulationMember, simulation.getCorrectSystemTime());
}
// 设置群组
conversationGroup.setCreator(simulationMember);
// 发送通知消息 TODO
}
/**
* 邀请用户进群
* @param simulation 仿真
* @param member 用户
* @param groupId 群组主键
* @param memberIds 角色ID
*/
public void inviteMembersInConversationGroup(Simulation simulation, SimulationMember member, Long groupId, List<String> memberIds) {
ConversationGroup conversationGroup = checkGroupIdAndReturn(simulation, null, groupId);
if (!conversationGroup.isConversationMember(member)) {
throw new SimulationException(SimulationExceptionType.Illegal_Argument, "非本组成员,不能邀请用户");
}
List<String> groupMemberIds = conversationGroup.getSimulationMemberIds();
List<String> newMemberIds = memberIds.stream().filter(mid -> !groupMemberIds.contains(mid)).collect(Collectors.toList());
newMemberIds.forEach(memberId -> {
SimulationMember newMember = simulation.getSimulationMemberById(memberId);
conversationGroup.addSimulationMember(newMember, simulation.getCorrectSystemTime());
});
// 给新加入的用户发送消息 TODO
}
/**
* 退出群组
*
* @param simulation 仿真
* @param member 用户
*/
public void exitConversationGroup(Simulation simulation, SimulationMember member, Long groupId) {
ConversationGroup conversationGroup = checkGroupIdAndReturn(simulation, null, groupId);
if (!conversationGroup.isConversationMember(member)) {
throw new SimulationException(SimulationExceptionType.Illegal_Argument, "非本组成员,不能邀请用户");
}
if (conversationGroup.isCreator(member)) {
throw new SimulationException(SimulationExceptionType.Illegal_Argument, "请先变更群主");
}
conversationGroup.exit(member);
// 发送退群消息 TODO
}
/**
* 语音文件聊天
*
* @param simulation 仿真
* @param member 用户
* @param groupId 用户组
* @param audioPath 文件路径
* @param content 文件内容
* @return 消息信息实体
*/
public ConversationGroupMessageVO audioChat(Simulation simulation, SimulationMember member, Long groupId, String audioPath, String content) {
ConversationGroupMessage message = sendMessage(simulation, member, groupId, () -> {
VoiceRecognitionResult recognitionResult = new VoiceRecognitionResult();
recognitionResult.setFilePath(audioPath);
recognitionResult.setResult(content);
return recognitionResult;
});
return new ConversationGroupMessageVO(message);
}
/**
* 文字聊天
*
* @param simulation 仿真
* @param member 用户
* @param groupId 用户组
* @param content 文字信息
* @return 消息信息实体
*/
public ConversationGroupMessageVO textChat(Simulation simulation, SimulationMember member, Long groupId, String content) {
ConversationGroupMessage message = sendMessage(simulation, member, groupId, () -> {
VoiceRecognitionResult recognitionResult = new VoiceRecognitionResult();
recognitionResult.setResult(content);
return recognitionResult;
});
return new ConversationGroupMessageVO(message);
}
/**
* 发送Base64文件消息
* @param simulation 仿真
* @param member 成员
* @param groupId 用户群组ID
* @param fileBase64Str base64文件内容
* @return 消息信息实体
*/
public ConversationGroupMessageVO audioBase64(Simulation simulation, SimulationMember member, Long groupId, String fileBase64Str) {
ConversationGroupMessage message = sendMessage(simulation, member, groupId, () -> {
VoiceRecognitionVO vo = VoiceRecognitionVO.load(fileBase64Str);
VoiceRecognitionResult recognitionResult = iVoiceService.voiceRecognition(vo);
return recognitionResult;
});
return new ConversationGroupMessageVO(message);
}
/**
* 群组用户读取消息
*
* @param simulation 仿真
* @param member 群组成员
* @param groupId 群组ID
* @return 未读的消息列表
*/
public List<ConversationGroupMessageVO> readConversationGroup(Simulation simulation, SimulationMember member, Long groupId) {
ConversationGroup conversationGroup = simulation.getConversationGroup(groupId);
// 没有消息不是本群人员
List<ConversationGroupMessage> messageList = conversationGroup.getUnreadMessages(member);
if (CollectionUtils.isEmpty(messageList)) {
return List.of();
}
int memberSize = conversationGroup.getMemberList().size();
messageList.forEach(message -> {
message.read(member);
if (message.getReaderSet().size() >= memberSize) { // 全部用户已读
message.finishRead();
}
});
return ConversationGroupMessageVO.convert2VO(messageList);
}
/**
* 对群组操作时基础判断
*
* @param simulation 仿真
* @param member 操作人员
* @param groupId 群组ID
* @return 群组信息
*/
private ConversationGroup checkGroupIdAndReturn(Simulation simulation, SimulationMember member, Long groupId) {
ConversationGroup conversationGroup = simulation.getConversationGroup(groupId);
if (conversationGroup == null) {
throw new SimulationException(SimulationExceptionType.Illegal_Argument, "群组不存在");
}
if (member != null && !Objects.equals(member.getId(), conversationGroup.getCreator().getId())) {
throw new SimulationException(SimulationExceptionType.Illegal_Argument, "您无操作权限");
}
return conversationGroup;
}
/**
* 处理并发送消息方法
* @param simulation 仿真
* @param member 操作人
* @param groupId 用户组
* @param handleSupplier 处理消息方法
* @return 用户组消息
*/
private ConversationGroupMessage sendMessage(Simulation simulation, SimulationMember member, Long groupId
, Supplier<VoiceRecognitionResult> handleSupplier) {
ConversationGroup conversationGroup = checkGroupIdAndReturn(simulation, null, groupId);
if (!conversationGroup.isConversationMember(member)) {
throw new SimulationException(SimulationExceptionType.Illegal_Argument, "非本组成员,不能邀请用户");
}
VoiceRecognitionResult recognitionResult = handleSupplier.get();
String upperCaseResult = recognitionResult.getResult().toUpperCase();
String handledContent = simulationVoiceHandler.handle(upperCaseResult);
// 创建消息信息
String messageId = conversationGroup.generateMessageId();
ConversationGroupMessage message = new ConversationGroupMessage(messageId, member,
simulation.getCorrectSystemTime(), handledContent, recognitionResult.getFilePath());
conversationGroup.addMessage(message);
// 发送消息
return message;
}
}

View File

@ -1,30 +0,0 @@
package club.joylink.rtss.simulation.cbtc.conversation;
import club.joylink.rtss.simulation.cbtc.GroupSimulationCache;
import club.joylink.rtss.simulation.cbtc.Simulation;
import club.joylink.rtss.simulation.cbtc.data.vo.ConversationGroupVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.*;
@Slf4j
@Service
public class ConversationGroupManagerService {
@Autowired
private GroupSimulationCache groupSimulationCache;
/**
* 获取所有群组信息
* @param group
* @return
*/
public List<ConversationGroupVO> getAllConversationsGroup(String group) {
Simulation simulation = groupSimulationCache.getSimulationByGroup(group);
List<ConversationGroup> allGroup = simulation.queryAllConversationGroup();
return ConversationGroupVO.convert2VOList(allGroup);
}
}

View File

@ -1,9 +1,15 @@
package club.joylink.rtss.simulation.cbtc.conversation; package club.joylink.rtss.simulation.cbtc.conversation;
import club.joylink.rtss.simulation.cbtc.ATS.operation.annotation.OperateHandler; import club.joylink.rtss.simulation.cbtc.ATS.operation.annotation.OperateHandler;
import club.joylink.rtss.simulation.cbtc.Simulation;
import club.joylink.rtss.simulation.cbtc.data.vo.ConversationGroupMessageVO;
import club.joylink.rtss.simulation.cbtc.data.vo.ConversationGroupVO;
import club.joylink.rtss.simulation.cbtc.member.SimulationMember;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;
/** /**
* 群组会话操作 * 群组会话操作
*/ */
@ -11,7 +17,77 @@ import org.springframework.beans.factory.annotation.Autowired;
@Slf4j @Slf4j
public class ConversationGroupOperateHandler { public class ConversationGroupOperateHandler {
@Autowired @Autowired
private ConversationGroupManagerService conversationGroupManagerService; private ConversationGroupHandlerService conversationGroupManagerService;
/**
* 创建群组信息
* @param simulation 仿真
* @param member 创建人
* @param name
* @param memberIds
*/
public ConversationGroupVO createConversationGroup(Simulation simulation, SimulationMember member, String name, List<String> memberIds) {
return conversationGroupManagerService.createConversationGroup(simulation, member, name, memberIds);
}
/**
* 修改群组名称等基本信息
*/
public void updateConversationGroup(Simulation simulation, SimulationMember member, Long groupId, String name) {
conversationGroupManagerService.updateConversationGroup(simulation, member, groupId, name);
}
/**
* 设定群组群主
*
* @param simulation 仿真
* @param member 用户
* @param groupId 群组ID
* @param memberId 用户ID
*/
public void setConversationGroupLeader(Simulation simulation, SimulationMember member, Long groupId, String memberId) {
conversationGroupManagerService.setConversationGroupLeader(simulation, member, groupId, memberId);
}
/**
* 邀请角色进群组
*/
public void inviteMembersInConversationGroup(Simulation simulation, SimulationMember member, Long groupId, List<String> memberIds) {
conversationGroupManagerService.inviteMembersInConversationGroup(simulation, member, groupId, memberIds);
}
/**
* 退出群组信息
*/
public void exitConversationGroup(Simulation simulation, SimulationMember member, Long groupId) {
conversationGroupManagerService.exitConversationGroup(simulation, member, groupId);
}
/**
* 发送语音信息
*/
public ConversationGroupMessageVO audioChat(Simulation simulation, SimulationMember member, Long groupId, String audioPath, String content) {
return conversationGroupManagerService.audioChat(simulation, member, groupId, audioPath, content);
}
/**
* 发送文字信息
*/
public ConversationGroupMessageVO textChat(Simulation simulation, SimulationMember member, Long groupId, String content) {
return conversationGroupManagerService.textChat(simulation, member, groupId, content);
}
/**
* 发送Base64音频文件
*/
public ConversationGroupMessageVO audioBase64(Simulation simulation, SimulationMember member, Long groupId, String fileBase64Str) {
return conversationGroupManagerService.audioBase64(simulation, member, groupId, fileBase64Str);
}
/**
* 阅读群组信息
*/
public List<ConversationGroupMessageVO> readConversationGroup(Simulation simulation, SimulationMember member, Long groupId) {
return conversationGroupManagerService.readConversationGroup(simulation, member, groupId);
}
} }

View File

@ -2,6 +2,9 @@ package club.joylink.rtss.simulation.cbtc.conversation;
import club.joylink.rtss.simulation.cbtc.member.SimulationMember; import club.joylink.rtss.simulation.cbtc.member.SimulationMember;
import lombok.Getter; import lombok.Getter;
import lombok.Setter;
import java.time.LocalDateTime;
@Getter @Getter
public class ConversationMember { public class ConversationMember {
@ -10,6 +13,12 @@ public class ConversationMember {
private boolean connect; private boolean connect;
/**
* 加入时间
*/
@Setter
private LocalDateTime time;
public ConversationMember(SimulationMember member) { public ConversationMember(SimulationMember member) {
this.member = member; this.member = member;
this.connect = false; this.connect = false;