仿真使用情况记录
This commit is contained in:
parent
15166c7fbd
commit
9562c726e3
2
sql/20201208.sql
Normal file
2
sql/20201208.sql
Normal file
@ -0,0 +1,2 @@
|
||||
alter table user_simulation_stats modify role varchar(32) null comment '用户角色';
|
||||
|
@ -1,6 +1,7 @@
|
||||
package club.joylink.rtss.services.script;
|
||||
|
||||
import club.joylink.rtss.services.script.IScriptSimulationService;
|
||||
import club.joylink.rtss.entity.ScriptDraftWithBLOBs;
|
||||
import club.joylink.rtss.services.IScriptDraftService;
|
||||
import club.joylink.rtss.simulation.cbtc.ATS.ATSMessageCollectAndDispatcher;
|
||||
import club.joylink.rtss.simulation.cbtc.ATS.operation.Operation;
|
||||
import club.joylink.rtss.simulation.cbtc.GroupSimulationCache;
|
||||
@ -22,9 +23,6 @@ import club.joylink.rtss.simulation.cbtc.member.SimulationMember;
|
||||
import club.joylink.rtss.simulation.cbtc.robot.RobotLogicLoop;
|
||||
import club.joylink.rtss.simulation.cbtc.script.ScriptActionBO;
|
||||
import club.joylink.rtss.simulation.cbtc.script.ScriptBO;
|
||||
import club.joylink.rtss.entity.ScriptDraftWithBLOBs;
|
||||
import club.joylink.rtss.services.IScriptDraftService;
|
||||
import club.joylink.rtss.services.script.IScriptService;
|
||||
import club.joylink.rtss.vo.LoginUserInfoVO;
|
||||
import club.joylink.rtss.vo.UserVO;
|
||||
import club.joylink.rtss.vo.client.SocketMessageVO;
|
||||
@ -36,11 +34,11 @@ import club.joylink.rtss.vo.client.script.ScriptActionVO;
|
||||
import club.joylink.rtss.vo.client.script.ScriptVO;
|
||||
import club.joylink.rtss.vo.client.simulationv1.SimulationMemberVO;
|
||||
import club.joylink.rtss.websocket.StompMessageService;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@ -195,7 +193,7 @@ public class ScriptSimulationService implements IScriptSimulationService {
|
||||
}
|
||||
// 构建一个仿真
|
||||
Simulation simulation = this.groupSimulationService.create(loginUserInfoVO, draftScript.getMapId(), null,
|
||||
Simulation.FunctionalType.SIMULATION);
|
||||
Simulation.FunctionalType.SCRIPT_PREVIEW);
|
||||
loadDraftScript(simulation, draftScriptId);
|
||||
return simulation.getGroup();
|
||||
}
|
||||
|
@ -16,12 +16,11 @@ import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
public class UserSimulationStatStatService implements IUserSimulationStatService {
|
||||
public class UserSimulationStatService implements IUserSimulationStatService {
|
||||
|
||||
@Autowired
|
||||
private UserSimulationStatsDAO userSimulationStatsDAO;
|
@ -211,6 +211,10 @@ public class Simulation {
|
||||
return Objects.equals(this.buildParams.getFunctionalType(), FunctionalType.SCRIPT_MAKING);
|
||||
}
|
||||
|
||||
public boolean isScriptPreviewSimulation() {
|
||||
return FunctionalType.SCRIPT_PREVIEW.equals(buildParams.getFunctionalType());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取该角色类型的所有成员
|
||||
*/
|
||||
@ -484,7 +488,11 @@ public class Simulation {
|
||||
/**
|
||||
* 剧本编制
|
||||
*/
|
||||
SCRIPT_MAKING;
|
||||
SCRIPT_MAKING,
|
||||
/**
|
||||
* 剧本预览
|
||||
*/
|
||||
SCRIPT_PREVIEW;
|
||||
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,19 @@
|
||||
package club.joylink.rtss.simulation.cbtc.event;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class SimulationSubscribeEvent extends ApplicationEvent {
|
||||
private Long userId;
|
||||
|
||||
private String group;
|
||||
|
||||
public SimulationSubscribeEvent(Object source, Long userId, String group) {
|
||||
super(source);
|
||||
this.userId = userId;
|
||||
this.group = group;
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package club.joylink.rtss.simulation.cbtc.event;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
|
||||
/**
|
||||
* 用户退订仿真的所有订阅路径
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class SimulationUnsubscribeAllEvent extends ApplicationEvent {
|
||||
private Long userId;
|
||||
|
||||
private String group;
|
||||
|
||||
public SimulationUnsubscribeAllEvent(Object source, Long userId, String group) {
|
||||
super(source);
|
||||
this.userId = userId;
|
||||
this.group = group;
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
package club.joylink.rtss.simulation.cbtc.message;
|
||||
|
||||
import club.joylink.rtss.simulation.cbtc.event.SimulationDestroyEvent;
|
||||
import club.joylink.rtss.simulation.cbtc.event.SimulationSubscribeEvent;
|
||||
import club.joylink.rtss.simulation.cbtc.event.SimulationUnsubscribeAllEvent;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class UserSimulationStatsListen {
|
||||
|
||||
@Autowired
|
||||
private UserSimulationStatsManager userSimulationStatsManager;
|
||||
|
||||
@Async("nsExecutor")
|
||||
@EventListener
|
||||
public void subscribeSimulation(SimulationSubscribeEvent event) {
|
||||
userSimulationStatsManager.subscribeSimulation(event.getUserId(), event.getGroup());
|
||||
}
|
||||
|
||||
@Async("nsExecutor")
|
||||
@EventListener
|
||||
public void unsubscribeAll(SimulationUnsubscribeAllEvent event) {
|
||||
userSimulationStatsManager.unsubscribeAll(event.getUserId(), event.getGroup());
|
||||
}
|
||||
|
||||
@Async("nsExecutor")
|
||||
@EventListener
|
||||
public void simulationDestroy(SimulationDestroyEvent event) {
|
||||
userSimulationStatsManager.simulationDestroy(event.getSimulation());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,161 @@
|
||||
package club.joylink.rtss.simulation.cbtc.message;
|
||||
|
||||
import club.joylink.rtss.exception.BusinessExceptionAssertEnum;
|
||||
import club.joylink.rtss.services.user.IUserSimulationStatService;
|
||||
import club.joylink.rtss.simulation.cbtc.GroupSimulationService;
|
||||
import club.joylink.rtss.simulation.cbtc.Simulation;
|
||||
import club.joylink.rtss.simulation.cbtc.build.SimulationBuildParams;
|
||||
import club.joylink.rtss.simulation.cbtc.member.SimulationMember;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* 用户使用仿真情况记录
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class UserSimulationStatsManager {
|
||||
@Autowired
|
||||
private GroupSimulationService groupSimulationService;
|
||||
|
||||
@Autowired
|
||||
private IUserSimulationStatService iUserSimulationStatService;
|
||||
|
||||
/**
|
||||
* k - userId
|
||||
* v - 用户订阅的仿真的groups
|
||||
*/
|
||||
private final Map<Long, Set<SimulationUseInfo>> userAndUseInfosMap = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* k - group
|
||||
* v - 仿真使用记录
|
||||
*/
|
||||
private final Map<String, Set<SimulationUseInfo>> simulationAndUseInfosMap = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 仿真被订阅,记录仿真使用记录
|
||||
*/
|
||||
public void subscribeSimulation(Long userId, String group) {
|
||||
Simulation simulation = groupSimulationService.getSimulationByGroup(group);
|
||||
if (simulation.isScriptMakingSimulation() || simulation.isScriptPreviewSimulation()) {
|
||||
return;
|
||||
}
|
||||
Set<SimulationUseInfo> useInfos = userAndUseInfosMap.computeIfAbsent(userId, k -> new HashSet<>());
|
||||
Optional<SimulationUseInfo> infoOptional = useInfos.stream().filter(info -> group.equals(info.getGroup())).limit(1).findFirst();
|
||||
if (infoOptional.isPresent()) { //如果记录已经存在
|
||||
SimulationUseInfo useInfo = infoOptional.get();
|
||||
useInfo.restart();
|
||||
} else { //如果记录不存在
|
||||
SimulationUseInfo newInfo = new SimulationUseInfo(group, userId);
|
||||
useInfos.add(newInfo);
|
||||
Set<SimulationUseInfo> groupKeyUseInfos = simulationAndUseInfosMap.computeIfAbsent(group, k -> new HashSet<>());
|
||||
groupKeyUseInfos.add(newInfo);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户将仿真所有订阅退订,则仿真使用记录暂停
|
||||
*/
|
||||
public void unsubscribeAll(Long userId, String group) {
|
||||
Simulation simulation = groupSimulationService.getSimulationByGroup(group);
|
||||
if (simulation.isScriptMakingSimulation() || simulation.isScriptPreviewSimulation()) {
|
||||
return;
|
||||
}
|
||||
SimulationUseInfo useInfo = findUseInfo(userId, group);
|
||||
BusinessExceptionAssertEnum.SYSTEM_EXCEPTION.assertNotNull(useInfo, String.format("仿真[%s]的使用未被记录", group));
|
||||
useInfo.pause();
|
||||
}
|
||||
|
||||
/**
|
||||
* 仿真销毁,持久化数据
|
||||
*/
|
||||
public void simulationDestroy(Simulation simulation) {
|
||||
if (simulation.isScriptMakingSimulation() || simulation.isScriptPreviewSimulation()) { //剧本编制和预览仿真不记录
|
||||
return;
|
||||
}
|
||||
|
||||
//暂停并从map中移除仿真使用记录
|
||||
String group = simulation.getGroup();
|
||||
Set<SimulationUseInfo> useInfos = simulationAndUseInfosMap.remove(group);
|
||||
BusinessExceptionAssertEnum.SYSTEM_EXCEPTION.assertCollectionNotEmpty(useInfos, String.format("仿真[%s]的使用未被记录", group));
|
||||
useInfos.forEach(info -> {
|
||||
info.pause();
|
||||
Set<SimulationUseInfo> infos = userAndUseInfosMap.get(info.getUserId());
|
||||
infos.removeIf(i -> group.equals(i.getGroup()));
|
||||
});
|
||||
SimulationBuildParams buildParams = simulation.getBuildParams();
|
||||
useInfos.forEach(info -> {
|
||||
Long userId = info.getUserId();
|
||||
SimulationMember member = simulation.findMemberByUserId(userId);
|
||||
String memberType = member == null ? null : member.getType().name();
|
||||
String prdType = buildParams.getProdType() == null ? null : buildParams.getProdType().getCode();
|
||||
iUserSimulationStatService.addUserSimulationStats(userId, buildParams.getMap().getId(),
|
||||
prdType, info.getDuration(), memberType);
|
||||
});
|
||||
}
|
||||
|
||||
private SimulationUseInfo findUseInfo(Long userId, String group) {
|
||||
Set<SimulationUseInfo> infos = userAndUseInfosMap.get(userId);
|
||||
if (CollectionUtils.isEmpty(infos)) {
|
||||
return null;
|
||||
} else {
|
||||
Optional<SimulationUseInfo> optional = infos.stream().filter(info -> group.equals(info.getGroup())).limit(1).findFirst();
|
||||
return optional.orElse(null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 仿真使用信息
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class SimulationUseInfo {
|
||||
private String group;
|
||||
|
||||
private Long userId;
|
||||
|
||||
private LocalDateTime startTime = LocalDateTime.now();
|
||||
|
||||
private int duration;
|
||||
|
||||
private boolean pause;
|
||||
|
||||
public SimulationUseInfo(String group, Long userId) {
|
||||
this.group = group;
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 暂停计时
|
||||
*/
|
||||
public void pause() {
|
||||
if (!pause) {
|
||||
duration = (int) (duration + Duration.between(startTime, LocalDateTime.now()).toSeconds());
|
||||
this.pause = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 重新开始计时
|
||||
*/
|
||||
public void restart() {
|
||||
if (pause) {
|
||||
startTime = LocalDateTime.now();
|
||||
pause = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@ package club.joylink.rtss.simulation.cbtc.message.websocket;
|
||||
|
||||
import club.joylink.rtss.simulation.cbtc.event.*;
|
||||
import club.joylink.rtss.websocket.WebsocketConfig;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
@ -48,6 +49,7 @@ public class SimulationSubscribeManager {
|
||||
|
||||
/**
|
||||
* 发布仿真连接/断连事件
|
||||
*
|
||||
* @param user
|
||||
* @param subscribeTopic
|
||||
* @param destination
|
||||
@ -58,12 +60,18 @@ public class SimulationSubscribeManager {
|
||||
String destination, boolean sub) {
|
||||
if (sub) {
|
||||
log.info(String.format("用户[%s]订阅仿真[%s]", user.getName(), destination));
|
||||
this.applicationContext.publishEvent(new SimulationSubscribeEvent(this, user.getUser().getId(), subscribeTopic.getId(destination)));
|
||||
} else {
|
||||
log.info(String.format("用户[%s]取消订阅仿真[%s]", user.getName(), destination));
|
||||
//如果某个仿真的所有订阅都被取消
|
||||
SimulationUserSubscribeInfo simulationUserSubscribeInfo = userSubInfoMap.get(user.getName());
|
||||
if (simulationUserSubscribeInfo == null || simulationUserSubscribeInfo.isSubscribeInfoEmpty(destination)) {
|
||||
this.applicationContext.publishEvent(new SimulationUnsubscribeAllEvent(this, user.getUser().getId(), subscribeTopic.getId(destination)));
|
||||
}
|
||||
}
|
||||
switch (subscribeTopic) {
|
||||
case Main:
|
||||
case WeChatMini:{
|
||||
case WeChatMini: {
|
||||
if (sub) {
|
||||
this.applicationContext.publishEvent(new SimulationUserConnectEvent(user.getUser().getId(), subscribeTopic.getId(destination), this));
|
||||
} else {
|
||||
@ -71,7 +79,7 @@ public class SimulationSubscribeManager {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SandBox:{
|
||||
case SandBox: {
|
||||
if (sub) {
|
||||
this.applicationContext.publishEvent(new SandboxUserConnectEvent(user.getUser().getId(), subscribeTopic.getId(destination), this));
|
||||
} else {
|
||||
@ -79,7 +87,7 @@ public class SimulationSubscribeManager {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Drive:{
|
||||
case Drive: {
|
||||
if (sub) {
|
||||
this.applicationContext.publishEvent(new Drive3DUserConnectEvent(user.getUser().getId(), subscribeTopic.getId(destination), this));
|
||||
} else {
|
||||
@ -150,6 +158,7 @@ public class SimulationSubscribeManager {
|
||||
|
||||
/**
|
||||
* 断开连接,删除此连接的所有订阅,并查询此用户所有客户端都不再订阅的路径返回
|
||||
*
|
||||
* @param wsSessionId
|
||||
* @return 此用户所有客户端都没有订阅的路径
|
||||
*/
|
||||
@ -177,9 +186,24 @@ public class SimulationSubscribeManager {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isSubscribeInfoEmpty(String destination) {
|
||||
if (sessionSubMap == null || sessionSubMap.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
for (Set<SubscribeInfo> infos : sessionSubMap.values()) {
|
||||
for (SubscribeInfo info : infos) {
|
||||
if (destination.equals(info.getDestination())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
class SubscribeInfo {
|
||||
String wsSessionId;
|
||||
String subId;
|
||||
@Getter
|
||||
String destination;
|
||||
|
||||
public SubscribeInfo(String wsSessionId, String subId, String destination) {
|
||||
|
Loading…
Reference in New Issue
Block a user