feat: 在ScriptClient中添加操作日志记录功能,优化设备状态接口调用的成功与失败响应处理;在DeviceStats中优化登录时间检查逻辑;启用定时任务检查空闲设备状态

This commit is contained in:
yahaozhang
2025-09-21 17:22:14 +08:00
parent cf3247232a
commit d6afa0a054
11 changed files with 746 additions and 9 deletions

Binary file not shown.

View File

@@ -0,0 +1,72 @@
package com.gameplatform.server.controller;
import com.gameplatform.server.mapper.log.ScriptOperationLogMapper;
import com.gameplatform.server.model.entity.log.ScriptOperationLog;
import com.gameplatform.server.model.enums.ScriptOperationType;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
/**
* 脚本操作日志查询接口
*/
@RestController
@RequestMapping("/api/admin/script-operation-log")
@Slf4j
public class ScriptOperationLogController {
@Autowired
private ScriptOperationLogMapper logMapper;
/**
* 根据设备ID查询操作记录
*/
// @GetMapping("/machine/{machineId}")
// public List<ScriptOperationLog> getByMachineId(@PathVariable String machineId,
// @RequestParam(defaultValue = "10") Integer limit) {
// return logMapper.findByMachineId(machineId, limit);
// }
// /**
// * 根据操作类型查询记录
// */
// @GetMapping("/type/{operationType}")
// public List<ScriptOperationLog> getByOperationType(@PathVariable ScriptOperationType operationType,
// @RequestParam(defaultValue = "10") Integer limit) {
// return logMapper.findByOperationType(operationType, limit);
// }
// /**
// * 查询失败的操作记录
// */
// @GetMapping("/failed")
// public List<ScriptOperationLog> getFailedOperations(@RequestParam(defaultValue = "10") Integer limit) {
// LocalDateTime yesterday = LocalDateTime.now().minusDays(1);
// LocalDateTime now = LocalDateTime.now();
// return logMapper.findFailedOperations(yesterday, now, limit);
// }
// /**
// * 获取操作统计信息
// */
// @GetMapping("/statistics")
// public List<Map<String, Object>> getStatistics(@RequestParam(defaultValue = "1") Integer days) {
// LocalDateTime startTime = LocalDateTime.now().minusDays(days);
// LocalDateTime endTime = LocalDateTime.now();
// return logMapper.getOperationStatistics(startTime, endTime);
// }
// /**
// * 查询最近的操作记录
// */
// @GetMapping("/recent")
// public List<ScriptOperationLog> getRecentOperations(@RequestParam(defaultValue = "20") Integer limit) {
// LocalDateTime yesterday = LocalDateTime.now().minusDays(1);
// LocalDateTime now = LocalDateTime.now();
// return logMapper.findByTimeRange(yesterday, now, limit);
// }
}

View File

@@ -308,7 +308,8 @@ public class DeviceStats {
int completed = 0;
for (LinkTask task : loggedInTasks) {
LocalDateTime loginAt = task.getLoginAt();
boolean over3m = loginAt != null && loginAt.isBefore(now.minusMinutes(3));
// 检查登录时间到现在是否已经超过3分钟
boolean over3m = loginAt != null && Duration.between(loginAt, now).toMinutes() >= 3;
if (!over3m) {
continue;
}

View File

@@ -0,0 +1,76 @@
package com.gameplatform.server.mapper.log;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.gameplatform.server.model.entity.log.ScriptOperationLog;
import com.gameplatform.server.model.enums.ScriptOperationType;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.time.LocalDateTime;
import java.util.List;
/**
* 脚本操作记录Mapper
*/
@Mapper
public interface ScriptOperationLogMapper extends BaseMapper<ScriptOperationLog> {
/**
* 插入操作记录
*/
int insert(ScriptOperationLog log);
/**
* 更新操作记录
*/
int update(ScriptOperationLog log);
/**
* 根据ID查询
*/
ScriptOperationLog findById(@Param("id") Long id);
/**
* 根据设备ID查询操作记录
*/
List<ScriptOperationLog> findByMachineId(@Param("machineId") String machineId,
@Param("limit") Integer limit);
/**
* 根据操作类型查询记录
*/
List<ScriptOperationLog> findByOperationType(@Param("operationType") ScriptOperationType operationType,
@Param("limit") Integer limit);
/**
* 根据时间范围查询操作记录
*/
List<ScriptOperationLog> findByTimeRange(@Param("startTime") LocalDateTime startTime,
@Param("endTime") LocalDateTime endTime,
@Param("limit") Integer limit);
/**
* 查询某个设备特定操作类型的记录
*/
List<ScriptOperationLog> findByMachineIdAndOperationType(@Param("machineId") String machineId,
@Param("operationType") ScriptOperationType operationType,
@Param("limit") Integer limit);
/**
* 查询失败的操作记录
*/
List<ScriptOperationLog> findFailedOperations(@Param("startTime") LocalDateTime startTime,
@Param("endTime") LocalDateTime endTime,
@Param("limit") Integer limit);
/**
* 统计某个时间段内各操作类型的成功率
*/
List<java.util.Map<String, Object>> getOperationStatistics(@Param("startTime") LocalDateTime startTime,
@Param("endTime") LocalDateTime endTime);
/**
* 删除指定时间之前的记录(用于数据清理)
*/
int deleteOldRecords(@Param("beforeTime") LocalDateTime beforeTime);
}

View File

@@ -0,0 +1,205 @@
package com.gameplatform.server.model.entity.log;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.gameplatform.server.model.enums.ScriptOperationType;
import java.time.LocalDateTime;
/**
* 脚本操作记录实体
*/
@TableName("script_operation_log")
public class ScriptOperationLog {
@TableId(value = "id", type = IdType.AUTO)
private Long id;
@TableField("operation_type")
private ScriptOperationType operationType;
@TableField("machine_id")
private String machineId;
@TableField("request_url")
private String requestUrl;
@TableField("request_params")
private String requestParams; // JSON字符串
@TableField("response_result")
private String responseResult;
@TableField("success")
private Boolean success;
@TableField("error_message")
private String errorMessage;
@TableField("duration_ms")
private Integer durationMs;
@TableField("request_time")
private LocalDateTime requestTime;
@TableField("response_time")
private LocalDateTime responseTime;
@TableField("created_at")
private LocalDateTime createdAt;
@TableField("updated_at")
private LocalDateTime updatedAt;
// 构造函数
public ScriptOperationLog() {}
public ScriptOperationLog(ScriptOperationType operationType, String machineId, String requestUrl) {
this.operationType = operationType;
this.machineId = machineId;
this.requestUrl = requestUrl;
this.requestTime = LocalDateTime.now();
this.createdAt = LocalDateTime.now();
this.updatedAt = LocalDateTime.now();
this.success = false; // 默认失败,成功时再更新
}
// Getter和Setter方法
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public ScriptOperationType getOperationType() {
return operationType;
}
public void setOperationType(ScriptOperationType operationType) {
this.operationType = operationType;
}
public String getMachineId() {
return machineId;
}
public void setMachineId(String machineId) {
this.machineId = machineId;
}
public String getRequestUrl() {
return requestUrl;
}
public void setRequestUrl(String requestUrl) {
this.requestUrl = requestUrl;
}
public String getRequestParams() {
return requestParams;
}
public void setRequestParams(String requestParams) {
this.requestParams = requestParams;
}
public String getResponseResult() {
return responseResult;
}
public void setResponseResult(String responseResult) {
this.responseResult = responseResult;
}
public Boolean getSuccess() {
return success;
}
public void setSuccess(Boolean success) {
this.success = success;
}
public String getErrorMessage() {
return errorMessage;
}
public void setErrorMessage(String errorMessage) {
this.errorMessage = errorMessage;
}
public Integer getDurationMs() {
return durationMs;
}
public void setDurationMs(Integer durationMs) {
this.durationMs = durationMs;
}
public LocalDateTime getRequestTime() {
return requestTime;
}
public void setRequestTime(LocalDateTime requestTime) {
this.requestTime = requestTime;
}
public LocalDateTime getResponseTime() {
return responseTime;
}
public void setResponseTime(LocalDateTime responseTime) {
this.responseTime = responseTime;
}
public LocalDateTime getCreatedAt() {
return createdAt;
}
public void setCreatedAt(LocalDateTime createdAt) {
this.createdAt = createdAt;
}
public LocalDateTime getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(LocalDateTime updatedAt) {
this.updatedAt = updatedAt;
}
// 便捷方法:设置成功响应
public void setSuccessResponse(String result, LocalDateTime responseTime, long durationMs) {
this.responseResult = result;
this.success = true;
this.responseTime = responseTime;
this.durationMs = (int) durationMs;
this.updatedAt = LocalDateTime.now();
}
// 便捷方法:设置失败响应
public void setFailureResponse(String errorMessage, LocalDateTime responseTime, long durationMs) {
this.errorMessage = errorMessage;
this.success = false;
this.responseTime = responseTime;
this.durationMs = (int) durationMs;
this.updatedAt = LocalDateTime.now();
}
@Override
public String toString() {
return "ScriptOperationLog{" +
"id=" + id +
", operationType=" + operationType +
", machineId='" + machineId + '\'' +
", requestUrl='" + requestUrl + '\'' +
", success=" + success +
", durationMs=" + durationMs +
", requestTime=" + requestTime +
", responseTime=" + responseTime +
'}';
}
}

View File

@@ -0,0 +1,21 @@
package com.gameplatform.server.model.enums;
/**
* 脚本操作类型枚举
*/
public enum ScriptOperationType {
SELECT_REGION("选区操作"),
REFRESH("判断刷新"),
SAVE_TOTAL_TIMES("保存总次数"),
REFUND_ORDER("退单操作");
private final String description;
ScriptOperationType(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
}

View File

@@ -2,7 +2,9 @@ package com.gameplatform.server.service.external;
import com.gameplatform.server.annotation.RepeatCall;
import com.gameplatform.server.model.dto.device.DeviceStatusResponse;
import com.gameplatform.server.model.entity.log.ScriptOperationLog;
import com.gameplatform.server.service.device.DeviceStatusService;
import com.gameplatform.server.service.log.ScriptOperationLogService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
@@ -27,6 +29,7 @@ public class ScriptClient {
private final String appBaseUrl;
private final DeviceStatusService deviceStatusService;
private final ApplicationEventPublisher eventPublisher;
private final ScriptOperationLogService operationLogService;
public ScriptClient(
@Value("${script.base-url}") String baseUrl,
@@ -35,13 +38,15 @@ public class ScriptClient {
@Value("${script.read-timeout-ms:5000}") int readTimeoutMs,
@Value("${app.base-url}") String appBaseUrl,
DeviceStatusService deviceStatusService,
ApplicationEventPublisher eventPublisher
ApplicationEventPublisher eventPublisher,
ScriptOperationLogService operationLogService
) {
this.baseUrl = baseUrl;
this.apiBaseUrl = apiBaseUrl;
this.appBaseUrl = appBaseUrl;
this.deviceStatusService = deviceStatusService;
this.eventPublisher = eventPublisher;
this.operationLogService = operationLogService;
this.webClient = WebClient.builder()
.baseUrl(baseUrl)
.exchangeStrategies(ExchangeStrategies.builder()
@@ -188,11 +193,16 @@ public class ScriptClient {
log.warn("选区操作失败: 区域={} 必须是Q或V", region);
return Mono.just("选区操作失败");
}
// 构建选区URL使用设备编号作为参数名区域作为参数值
// 示例: {apiBaseUrl}/yijianwan_netfile/saveMsg?文件名=判断系统&f1=Q
String url = String.format(apiBaseUrl + "/yijianwan_netfile/saveMsg?文件名=判断系统&%s=%s", deviceId, region);
log.info("选区操作: 设备={}, 区域={}, url={}", deviceId, region, url);
// 创建操作日志记录
ScriptOperationLog operationLog = operationLogService.logSelectRegion(deviceId, url, region);
long startTime = System.currentTimeMillis();
return webClient.get() // 根据您的curl示例这应该是GET请求
.uri(url)
.accept(MediaType.TEXT_PLAIN)
@@ -201,9 +211,13 @@ public class ScriptClient {
.timeout(Duration.ofSeconds(10))
.doOnSuccess(result -> {
log.info("选区操作成功: 设备={}, 区域={}, 结果={}", deviceId, region, result);
// 记录成功响应
operationLogService.recordSuccess(operationLog, result, startTime);
})
.doOnError(e -> {
log.error("选区操作失败: 设备={}, 区域={}, 错误={}", deviceId, region, e.toString());
// 记录失败响应
operationLogService.recordFailure(operationLog, e.toString(), startTime);
});
}
@@ -216,14 +230,27 @@ public class ScriptClient {
public Mono<String> refresh(String machineId) {
String url = String.format(apiBaseUrl + "/yijianwan_netfile/saveMsg?文件名=判断刷新&%s=刷新", machineId);
log.info("调用判断刷新接口: {}", url);
// 创建操作日志记录
ScriptOperationLog operationLog = operationLogService.logRefresh(machineId, url);
long startTime = System.currentTimeMillis();
return webClient.get()
.uri(url)
.accept(MediaType.TEXT_PLAIN)
.retrieve()
.bodyToMono(String.class)
.timeout(Duration.ofSeconds(10))
.doOnSuccess(result -> log.info("判断刷新接口调用成功: result={}", result))
.doOnError(e -> log.warn("判断刷新接口调用失败: {}", e.toString()));
.doOnSuccess(result -> {
log.info("判断刷新接口调用成功: result={}", result);
// 记录成功响应
operationLogService.recordSuccess(operationLog, result, startTime);
})
.doOnError(e -> {
log.warn("判断刷新接口调用失败: {}", e.toString());
// 记录失败响应
operationLogService.recordFailure(operationLog, e.toString(), startTime);
});
}
/**
@@ -346,22 +373,36 @@ public class ScriptClient {
/**
* 保存总次数使用f4参数格式
* @param machineId 设备ID
* @param times 总次数
* @return 保存结果
*/
@RepeatCall(times = 3, description = "保存总次数")
public Mono<String> saveTotalTimes(String meachainId,int times) {
String url = String.format(apiBaseUrl + "/yijianwan_netfile/saveMsg?文件名=总次数&%s=%d", meachainId,times);
public Mono<String> saveTotalTimes(String machineId, int times) {
String url = String.format(apiBaseUrl + "/yijianwan_netfile/saveMsg?文件名=总次数&%s=%d", machineId, times);
log.info("保存刷副本的次数的url: {}", url);
log.info("开始调用保存总次数接口: times={}, url={}", times, url);
// 创建操作日志记录
ScriptOperationLog operationLog = operationLogService.logSaveTotalTimes(machineId, url, times);
long startTime = System.currentTimeMillis();
return webClient.get()
.uri(url)
.accept(MediaType.TEXT_PLAIN)
.retrieve()
.bodyToMono(String.class)
.timeout(Duration.ofSeconds(10))
.doOnSuccess(result -> log.info("保存总次数接口调用成功: url={}, result={}", url, result))
.doOnError(e -> log.warn("保存总次数接口调用失败: times={}, error={}", times, e.toString()));
.doOnSuccess(result -> {
log.info("保存总次数接口调用成功: url={}, result={}", url, result);
// 记录成功响应
operationLogService.recordSuccess(operationLog, result, startTime);
})
.doOnError(e -> {
log.warn("保存总次数接口调用失败: times={}, error={}", times, e.toString());
// 记录失败响应
operationLogService.recordFailure(operationLog, e.toString(), startTime);
});
}
/**
@@ -403,6 +444,10 @@ public class ScriptClient {
String url = String.format(apiBaseUrl + "/yijianwan_netfile/saveMsg?文件名=判断退单&%s=T", machineId);
log.info("调用退单接口: 设备={}, url={}", machineId, url);
// 创建操作日志记录
ScriptOperationLog operationLog = operationLogService.logRefundOrder(machineId, url);
long startTime = System.currentTimeMillis();
return webClient.get()
.uri(url)
.accept(MediaType.TEXT_PLAIN)
@@ -411,9 +456,13 @@ public class ScriptClient {
.timeout(Duration.ofSeconds(10))
.doOnSuccess(result -> {
log.info("退单接口调用成功: 设备={}, 结果={}", machineId, result);
// 记录成功响应
operationLogService.recordSuccess(operationLog, result, startTime);
})
.doOnError(e -> {
log.error("退单接口调用失败: 设备={}, 错误={}", machineId, e.toString());
// 记录失败响应
operationLogService.recordFailure(operationLog, e.toString(), startTime);
});
}
}

View File

@@ -0,0 +1,163 @@
package com.gameplatform.server.service.log;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.gameplatform.server.mapper.log.ScriptOperationLogMapper;
import com.gameplatform.server.model.entity.log.ScriptOperationLog;
import com.gameplatform.server.model.enums.ScriptOperationType;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
/**
* 脚本操作日志服务
*/
@Service
@Slf4j
public class ScriptOperationLogService {
@Autowired
private ScriptOperationLogMapper logMapper;
@Autowired
private ObjectMapper objectMapper;
/**
* 创建操作记录
*/
public ScriptOperationLog createOperationLog(ScriptOperationType operationType, String machineId, String requestUrl) {
ScriptOperationLog operationLog = new ScriptOperationLog(operationType, machineId, requestUrl);
try {
logMapper.insert(operationLog);
log.debug("创建脚本操作记录: id={}, type={}, machine={}", operationLog.getId(), operationType, machineId);
return operationLog;
} catch (Exception e) {
log.error("创建脚本操作记录失败: type={}, machine={}, error={}", operationType, machineId, e.getMessage(), e);
return operationLog; // 即使保存失败也返回对象,避免影响主流程
}
}
/**
* 记录选区操作
*/
public ScriptOperationLog logSelectRegion(String machineId, String requestUrl, String region) {
ScriptOperationLog log = createOperationLog(ScriptOperationType.SELECT_REGION, machineId, requestUrl);
// 设置请求参数
Map<String, Object> params = new HashMap<>();
params.put("region", region);
setRequestParams(log, params);
return log;
}
/**
* 记录刷新操作
*/
public ScriptOperationLog logRefresh(String machineId, String requestUrl) {
ScriptOperationLog log = createOperationLog(ScriptOperationType.REFRESH, machineId, requestUrl);
// 设置请求参数
Map<String, Object> params = new HashMap<>();
params.put("action", "刷新");
setRequestParams(log, params);
return log;
}
/**
* 记录保存总次数操作
*/
public ScriptOperationLog logSaveTotalTimes(String machineId, String requestUrl, int times) {
ScriptOperationLog log = createOperationLog(ScriptOperationType.SAVE_TOTAL_TIMES, machineId, requestUrl);
// 设置请求参数
Map<String, Object> params = new HashMap<>();
params.put("times", times);
setRequestParams(log, params);
return log;
}
/**
* 记录退单操作
*/
public ScriptOperationLog logRefundOrder(String machineId, String requestUrl) {
ScriptOperationLog log = createOperationLog(ScriptOperationType.REFUND_ORDER, machineId, requestUrl);
// 设置请求参数
Map<String, Object> params = new HashMap<>();
params.put("action", "T");
setRequestParams(log, params);
return log;
}
/**
* 记录成功响应
*/
public void recordSuccess(ScriptOperationLog operationLog, String result, long startTime) {
try {
LocalDateTime responseTime = LocalDateTime.now();
long duration = System.currentTimeMillis() - startTime;
operationLog.setSuccessResponse(result, responseTime, duration);
logMapper.update(operationLog);
log.debug("记录操作成功: id={}, duration={}ms", operationLog.getId(), duration);
} catch (Exception e) {
log.error("记录操作成功失败: id={}, error={}", operationLog.getId(), e.getMessage(), e);
}
}
/**
* 记录失败响应
*/
public void recordFailure(ScriptOperationLog operationLog, String errorMessage, long startTime) {
try {
LocalDateTime responseTime = LocalDateTime.now();
long duration = System.currentTimeMillis() - startTime;
operationLog.setFailureResponse(errorMessage, responseTime, duration);
logMapper.update(operationLog);
log.debug("记录操作失败: id={}, duration={}ms, error={}", operationLog.getId(), duration, errorMessage);
} catch (Exception e) {
log.error("记录操作失败失败: id={}, error={}", operationLog.getId(), e.getMessage(), e);
}
}
/**
* 设置请求参数转换为JSON字符串
*/
private void setRequestParams(ScriptOperationLog operationLog, Map<String, Object> params) {
try {
String paramsJson = objectMapper.writeValueAsString(params);
operationLog.setRequestParams(paramsJson);
logMapper.update(operationLog);
} catch (JsonProcessingException e) {
log.warn("转换请求参数为JSON失败: params={}, error={}", params, e.getMessage());
} catch (Exception e) {
log.error("设置请求参数失败: id={}, error={}", operationLog.getId(), e.getMessage(), e);
}
}
/**
* 清理旧记录(可以通过定时任务调用)
*/
public int cleanOldRecords(int daysToKeep) {
try {
LocalDateTime cutoffTime = LocalDateTime.now().minusDays(daysToKeep);
int deletedCount = logMapper.deleteOldRecords(cutoffTime);
log.info("清理脚本操作记录: 删除{}条{}天前的记录", deletedCount, daysToKeep);
return deletedCount;
} catch (Exception e) {
log.error("清理旧记录失败: error={}", e.getMessage(), e);
return 0;
}
}
}

View File

@@ -4,6 +4,7 @@ import com.gameplatform.server.model.dto.device.DeviceStatusResponse;
import com.gameplatform.server.service.device.DeviceStatusCheckService;
import com.gameplatform.server.service.external.ScriptClient;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import reactor.core.Exceptions;
import reactor.core.publisher.Mono;
@@ -29,7 +30,7 @@ public class DeviceStatusCheckTask {
/**
* 每分钟检查一次空闲设备,并更新相关链接任务状态
*/
// @Scheduled(fixedRate = 60000) // 每60秒执行一次
@Scheduled(fixedRate = 60000) // 每60秒执行一次
public void checkIdleDevicesAndUpdateTasks() {
log.debug("开始定时检查空闲设备");

View File

@@ -0,0 +1,21 @@
-- 脚本操作记录表
CREATE TABLE script_operation_log (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
operation_type VARCHAR(50) NOT NULL COMMENT '操作类型: SELECT_REGION, REFRESH, SAVE_TOTAL_TIMES, REFUND_ORDER',
machine_id VARCHAR(50) NOT NULL COMMENT '设备/机器ID',
request_url TEXT NOT NULL COMMENT '请求URL',
request_params JSON COMMENT '请求参数(JSON格式)',
response_result TEXT COMMENT '响应结果',
success BOOLEAN NOT NULL DEFAULT FALSE COMMENT '是否成功',
error_message TEXT COMMENT '错误信息',
duration_ms INT COMMENT '请求耗时(毫秒)',
request_time DATETIME NOT NULL COMMENT '请求发送时间',
response_time DATETIME COMMENT '响应接收时间',
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_operation_type (operation_type),
INDEX idx_machine_id (machine_id),
INDEX idx_request_time (request_time),
INDEX idx_success (success)
) COMMENT '脚本操作记录表';

View File

@@ -0,0 +1,128 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.gameplatform.server.mapper.log.ScriptOperationLogMapper">
<resultMap id="ScriptOperationLogMap" type="com.gameplatform.server.model.entity.log.ScriptOperationLog">
<id property="id" column="id" />
<result property="operationType" column="operation_type" typeHandler="org.apache.ibatis.type.EnumTypeHandler" />
<result property="machineId" column="machine_id" />
<result property="requestUrl" column="request_url" />
<result property="requestParams" column="request_params" />
<result property="responseResult" column="response_result" />
<result property="success" column="success" />
<result property="errorMessage" column="error_message" />
<result property="durationMs" column="duration_ms" />
<result property="requestTime" column="request_time" />
<result property="responseTime" column="response_time" />
<result property="createdAt" column="created_at" />
<result property="updatedAt" column="updated_at" />
</resultMap>
<sql id="BaseColumns">
id, operation_type, machine_id, request_url, request_params, response_result,
success, error_message, duration_ms, request_time, response_time, created_at, updated_at
</sql>
<select id="findById" parameterType="long" resultMap="ScriptOperationLogMap">
SELECT <include refid="BaseColumns" />
FROM script_operation_log
WHERE id = #{id}
LIMIT 1
</select>
<select id="findByMachineId" resultMap="ScriptOperationLogMap">
SELECT <include refid="BaseColumns" />
FROM script_operation_log
WHERE machine_id = #{machineId}
ORDER BY request_time DESC
<if test="limit != null">
LIMIT #{limit}
</if>
</select>
<select id="findByOperationType" resultMap="ScriptOperationLogMap">
SELECT <include refid="BaseColumns" />
FROM script_operation_log
WHERE operation_type = #{operationType}
ORDER BY request_time DESC
<if test="limit != null">
LIMIT #{limit}
</if>
</select>
<select id="findByTimeRange" resultMap="ScriptOperationLogMap">
SELECT <include refid="BaseColumns" />
FROM script_operation_log
WHERE request_time BETWEEN #{startTime} AND #{endTime}
ORDER BY request_time DESC
<if test="limit != null">
LIMIT #{limit}
</if>
</select>
<select id="findByMachineIdAndOperationType" resultMap="ScriptOperationLogMap">
SELECT <include refid="BaseColumns" />
FROM script_operation_log
WHERE machine_id = #{machineId} AND operation_type = #{operationType}
ORDER BY request_time DESC
<if test="limit != null">
LIMIT #{limit}
</if>
</select>
<select id="findFailedOperations" resultMap="ScriptOperationLogMap">
SELECT <include refid="BaseColumns" />
FROM script_operation_log
WHERE success = false
<if test="startTime != null and endTime != null">
AND request_time BETWEEN #{startTime} AND #{endTime}
</if>
ORDER BY request_time DESC
<if test="limit != null">
LIMIT #{limit}
</if>
</select>
<select id="getOperationStatistics" resultType="java.util.Map">
SELECT
operation_type,
COUNT(*) as total_count,
SUM(CASE WHEN success = true THEN 1 ELSE 0 END) as success_count,
SUM(CASE WHEN success = false THEN 1 ELSE 0 END) as failed_count,
ROUND(AVG(duration_ms), 2) as avg_duration_ms,
ROUND(SUM(CASE WHEN success = true THEN 1 ELSE 0 END) * 100.0 / COUNT(*), 2) as success_rate
FROM script_operation_log
WHERE request_time BETWEEN #{startTime} AND #{endTime}
GROUP BY operation_type
ORDER BY operation_type
</select>
<insert id="insert" parameterType="com.gameplatform.server.model.entity.log.ScriptOperationLog" useGeneratedKeys="true" keyProperty="id">
INSERT INTO script_operation_log (
operation_type, machine_id, request_url, request_params, response_result,
success, error_message, duration_ms, request_time, response_time, created_at, updated_at
) VALUES (
#{operationType}, #{machineId}, #{requestUrl}, #{requestParams}, #{responseResult},
#{success}, #{errorMessage}, #{durationMs}, #{requestTime}, #{responseTime}, #{createdAt}, #{updatedAt}
)
</insert>
<update id="update" parameterType="com.gameplatform.server.model.entity.log.ScriptOperationLog">
UPDATE script_operation_log
<set>
<if test="responseResult != null">response_result = #{responseResult},</if>
<if test="success != null">success = #{success},</if>
<if test="errorMessage != null">error_message = #{errorMessage},</if>
<if test="durationMs != null">duration_ms = #{durationMs},</if>
<if test="responseTime != null">response_time = #{responseTime},</if>
updated_at = #{updatedAt}
</set>
WHERE id = #{id}
</update>
<delete id="deleteOldRecords">
DELETE FROM script_operation_log
WHERE created_at &lt; #{beforeTime}
</delete>
</mapper>