仿真使用情况记录
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;
|
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.ATSMessageCollectAndDispatcher;
|
||||||
import club.joylink.rtss.simulation.cbtc.ATS.operation.Operation;
|
import club.joylink.rtss.simulation.cbtc.ATS.operation.Operation;
|
||||||
import club.joylink.rtss.simulation.cbtc.GroupSimulationCache;
|
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.robot.RobotLogicLoop;
|
||||||
import club.joylink.rtss.simulation.cbtc.script.ScriptActionBO;
|
import club.joylink.rtss.simulation.cbtc.script.ScriptActionBO;
|
||||||
import club.joylink.rtss.simulation.cbtc.script.ScriptBO;
|
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.LoginUserInfoVO;
|
||||||
import club.joylink.rtss.vo.UserVO;
|
import club.joylink.rtss.vo.UserVO;
|
||||||
import club.joylink.rtss.vo.client.SocketMessageVO;
|
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.script.ScriptVO;
|
||||||
import club.joylink.rtss.vo.client.simulationv1.SimulationMemberVO;
|
import club.joylink.rtss.vo.client.simulationv1.SimulationMemberVO;
|
||||||
import club.joylink.rtss.websocket.StompMessageService;
|
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.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.context.event.EventListener;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.util.CollectionUtils;
|
import org.springframework.util.CollectionUtils;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -195,7 +193,7 @@ public class ScriptSimulationService implements IScriptSimulationService {
|
|||||||
}
|
}
|
||||||
// 构建一个仿真
|
// 构建一个仿真
|
||||||
Simulation simulation = this.groupSimulationService.create(loginUserInfoVO, draftScript.getMapId(), null,
|
Simulation simulation = this.groupSimulationService.create(loginUserInfoVO, draftScript.getMapId(), null,
|
||||||
Simulation.FunctionalType.SIMULATION);
|
Simulation.FunctionalType.SCRIPT_PREVIEW);
|
||||||
loadDraftScript(simulation, draftScriptId);
|
loadDraftScript(simulation, draftScriptId);
|
||||||
return simulation.getGroup();
|
return simulation.getGroup();
|
||||||
}
|
}
|
||||||
|
@ -16,12 +16,11 @@ import org.springframework.stereotype.Service;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class UserSimulationStatStatService implements IUserSimulationStatService {
|
public class UserSimulationStatService implements IUserSimulationStatService {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private UserSimulationStatsDAO userSimulationStatsDAO;
|
private UserSimulationStatsDAO userSimulationStatsDAO;
|
@ -211,6 +211,10 @@ public class Simulation {
|
|||||||
return Objects.equals(this.buildParams.getFunctionalType(), FunctionalType.SCRIPT_MAKING);
|
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.simulation.cbtc.event.*;
|
||||||
import club.joylink.rtss.websocket.WebsocketConfig;
|
import club.joylink.rtss.websocket.WebsocketConfig;
|
||||||
|
import lombok.Getter;
|
||||||
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 org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
@ -48,6 +49,7 @@ public class SimulationSubscribeManager {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 发布仿真连接/断连事件
|
* 发布仿真连接/断连事件
|
||||||
|
*
|
||||||
* @param user
|
* @param user
|
||||||
* @param subscribeTopic
|
* @param subscribeTopic
|
||||||
* @param destination
|
* @param destination
|
||||||
@ -58,8 +60,14 @@ public class SimulationSubscribeManager {
|
|||||||
String destination, boolean sub) {
|
String destination, boolean sub) {
|
||||||
if (sub) {
|
if (sub) {
|
||||||
log.info(String.format("用户[%s]订阅仿真[%s]", user.getName(), destination));
|
log.info(String.format("用户[%s]订阅仿真[%s]", user.getName(), destination));
|
||||||
|
this.applicationContext.publishEvent(new SimulationSubscribeEvent(this, user.getUser().getId(), subscribeTopic.getId(destination)));
|
||||||
} else {
|
} else {
|
||||||
log.info(String.format("用户[%s]取消订阅仿真[%s]", user.getName(), destination));
|
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) {
|
switch (subscribeTopic) {
|
||||||
case Main:
|
case Main:
|
||||||
@ -150,6 +158,7 @@ public class SimulationSubscribeManager {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 断开连接,删除此连接的所有订阅,并查询此用户所有客户端都不再订阅的路径返回
|
* 断开连接,删除此连接的所有订阅,并查询此用户所有客户端都不再订阅的路径返回
|
||||||
|
*
|
||||||
* @param wsSessionId
|
* @param wsSessionId
|
||||||
* @return 此用户所有客户端都没有订阅的路径
|
* @return 此用户所有客户端都没有订阅的路径
|
||||||
*/
|
*/
|
||||||
@ -177,9 +186,24 @@ public class SimulationSubscribeManager {
|
|||||||
return false;
|
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 {
|
class SubscribeInfo {
|
||||||
String wsSessionId;
|
String wsSessionId;
|
||||||
String subId;
|
String subId;
|
||||||
|
@Getter
|
||||||
String destination;
|
String destination;
|
||||||
|
|
||||||
public SubscribeInfo(String wsSessionId, String subId, String destination) {
|
public SubscribeInfo(String wsSessionId, String subId, String destination) {
|
||||||
|
Loading…
Reference in New Issue
Block a user