【方案设计】针对监控服务-功能时长统计的几个实现方案
实现前后端针对用户功能时长的使用,可以从记录用户操作开始,到存储和展示功能使用时长。以下是几个实现方案的简单介绍:
方案一:基于时间戳记录
这种方案通过记录用户开始和结束使用功能的时间戳,然后在后端计算时长并存储,前端从后端获取数据进行展示。
前端实现
- 记录开始时间:在用户进入功能页面时,使用
Date.now()
获取当前时间戳并存储在本地变量中。
// 在进入功能页面时
let startTime = Date.now();
- 记录结束时间:在用户离开功能页面或完成操作时,再次获取当前时间戳。
// 在离开功能页面时
let endTime = Date.now();
- 发送时长数据到后端:将计算出的时长(
endTime - startTime
)发送到后端服务器。可以使用fetch
或axios
等库进行 HTTP 请求。
import axios from 'axios';
let duration = endTime - startTime;
axios.post('/api/function-usage', {
functionName: '特定功能名称',
duration: duration
}).then(response => {
console.log('时长数据发送成功');
}).catch(error => {
console.log('发送时长数据失败', error);
});
后端实现(以 Spring Boot 为例)
- 接收前端数据:创建一个控制器来接收前端发送的时长数据。
@RestController
@RequestMapping("/api")
public class FunctionUsageController {
@Autowired
private FunctionUsageService functionUsageService;
@PostMapping("/function-usage")
public ResponseEntity<String> saveFunctionUsage(@RequestBody FunctionUsage functionUsage) {
functionUsageService.saveFunctionUsage(functionUsage);
return ResponseEntity.ok("时长数据保存成功");
}
}
- 存储数据:创建一个服务类和对应的实体类来处理数据存储。
@Service
public class FunctionUsageService {
@Autowired
private FunctionUsageRepository functionUsageRepository;
public void saveFunctionUsage(FunctionUsage functionUsage) {
functionUsageRepository.save(functionUsage);
}
}
@Entity
@Table(name = "function_usage")
public class FunctionUsage {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String functionName;
private Long duration;
// 可以添加用户标识字段,如 userId
private Long userId;
// 构造函数、getter 和 setter
public FunctionUsage() {}
public FunctionUsage(String functionName, Long duration, Long userId) {
this.functionName = functionName;
this.duration = duration;
this.userId = userId;
}
// getters 和 setters
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getFunctionName() {
return functionName;
}
public void setFunctionName(String functionName) {
this.functionName = functionName;
}
public Long getDuration() {
return duration;
}
public void setDuration(Long duration) {
this.duration = duration;
}
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
}
@Repository
public interface FunctionUsageRepository extends JpaRepository<FunctionUsage, Long> {
}
方案二:使用 WebSocket 实时记录
这种方案通过 WebSocket 实时传输用户操作状态,后端可以更精确地记录功能使用时长。
前端实现
- 连接 WebSocket:使用
WebSocket
API 连接到后端服务器。
let socket = new WebSocket('ws://localhost:8080/function-usage');
socket.onopen = function (event) {
console.log('WebSocket 连接成功');
// 发送用户进入功能的消息
socket.send(JSON.stringify({
action: 'enter',
functionName: '特定功能名称',
userId: 1 // 假设用户 ID
}));
};
socket.onclose = function (event) {
console.log('WebSocket 连接关闭');
// 发送用户离开功能的消息
socket.send(JSON.stringify({
action: 'leave',
functionName: '特定功能名称',
userId: 1
}));
};
后端实现(以 Spring Boot + Spring WebSocket 为例)
- 配置 WebSocket:在 Spring Boot 中配置 WebSocket。
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(functionUsageHandler(), "/function-usage").withSockJS();
}
@Bean
public WebSocketHandler functionUsageHandler() {
return new FunctionUsageHandler();
}
}
- 处理 WebSocket 消息:创建一个
WebSocketHandler
来处理前端发送的消息。
@Component
public class FunctionUsageHandler extends TextWebSocketHandler {
private final Map<String, Long> userFunctionStartTime = new HashMap<>();
private final FunctionUsageService functionUsageService;
public FunctionUsageHandler(FunctionUsageService functionUsageService) {
this.functionUsageService = functionUsageService;
}
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
FunctionUsageMessage usageMessage = new ObjectMapper().readValue(message.getPayload(), FunctionUsageMessage.class);
String userId = session.getId(); // 简单示例,实际应从消息中获取用户标识
if ("enter".equals(usageMessage.getAction())) {
userFunctionStartTime.put(userId, System.currentTimeMillis());
} else if ("leave".equals(usageMessage.getAction())) {
Long startTime = userFunctionStartTime.remove(userId);
if (startTime!= null) {
Long duration = System.currentTimeMillis() - startTime;
FunctionUsage functionUsage = new FunctionUsage(usageMessage.getFunctionName(), duration, Long.parseLong(userId));
functionUsageService.saveFunctionUsage(functionUsage);
}
}
}
}
public class FunctionUsageMessage {
private String action;
private String functionName;
private Long userId;
// 构造函数、getter 和 setter
public FunctionUsageMessage() {}
public FunctionUsageMessage(String action, String functionName, Long userId) {
this.action = action;
this.functionName = functionName;
this.userId = userId;
}
public String getAction() {
return action;
}
public void setAction(String action) {
this.action = action;
}
public String getFunctionName() {
return functionName;
}
public void setFunctionName(String functionName) {
this.functionName = functionName;
}
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
}
方案三:前端定时上报
前端定时向后端发送用户是否正在使用功能的状态,后端根据这些状态记录时长。
前端实现
- 定时发送状态:使用
setInterval
定时发送用户状态。
let isUsingFunction = true; // 假设用户正在使用功能
let intervalId = setInterval(() => {
axios.post('/api/function-status', {
functionName: '特定功能名称',
isUsing: isUsingFunction,
userId: 1
}).then(response => {
console.log('状态数据发送成功');
}).catch(error => {
console.log('发送状态数据失败', error);
});
}, 60 * 1000); // 每分钟发送一次
后端实现
- 接收状态数据:创建控制器接收前端发送的状态数据。
@RestController
@RequestMapping("/api")
public class FunctionStatusController {
@Autowired
private FunctionUsageService functionUsageService;
@PostMapping("/function-status")
public ResponseEntity<String> saveFunctionStatus(@RequestBody FunctionStatus functionStatus) {
functionUsageService.saveFunctionStatus(functionStatus);
return ResponseEntity.ok("状态数据保存成功");
}
}
- 处理状态数据并计算时长:在服务类中根据状态数据计算时长。
@Service
public class FunctionUsageService {
private final Map<Long, FunctionStatus> userFunctionStatusMap = new HashMap<>();
public void saveFunctionStatus(FunctionStatus functionStatus) {
Long userId = functionStatus.getUserId();
FunctionStatus previousStatus = userFunctionStatusMap.get(userId);
if (previousStatus!= null && previousStatus.isUsing() &&!functionStatus.isUsing()) {
// 用户停止使用功能,计算时长
Long duration = System.currentTimeMillis() - previousStatus.getLastUpdateTime();
// 保存时长数据
FunctionUsage functionUsage = new FunctionUsage(functionStatus.getFunctionName(), duration, userId);
// 这里可以添加实际的存储逻辑
}
userFunctionStatusMap.put(userId, functionStatus);
}
}
public class FunctionStatus {
private String functionName;
private boolean isUsing;
private Long userId;
private Long lastUpdateTime;
// 构造函数、getter 和 setter
public FunctionStatus() {
this.lastUpdateTime = System.currentTimeMillis();
}
public FunctionStatus(String functionName, boolean isUsing, Long userId) {
this.functionName = functionName;
this.isUsing = isUsing;
this.userId = userId;
this.lastUpdateTime = System.currentTimeMillis();
}
public String getFunctionName() {
return functionName;
}
public void setFunctionName(String functionName) {
this.functionName = functionName;
}
public boolean isUsing() {
return isUsing;
}
public void setIsUsing(boolean isUsing) {
this.isUsing = isUsing;
}
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public Long getLastUpdateTime() {
return lastUpdateTime;
}
public void setLastUpdateTime(Long lastUpdateTime) {
this.lastUpdateTime = lastUpdateTime;
}
}
以上方案可以根据项目的具体需求和技术栈进行选择和调整,实现对用户功能时长的有效记录和使用。