From d6afa0a0543b13459f0826843cd5ec3a385eb985 Mon Sep 17 00:00:00 2001 From: yahaozhang Date: Sun, 21 Sep 2025 17:22:14 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=9C=A8ScriptClient=E4=B8=AD=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E6=93=8D=E4=BD=9C=E6=97=A5=E5=BF=97=E8=AE=B0=E5=BD=95?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=EF=BC=8C=E4=BC=98=E5=8C=96=E8=AE=BE=E5=A4=87?= =?UTF-8?q?=E7=8A=B6=E6=80=81=E6=8E=A5=E5=8F=A3=E8=B0=83=E7=94=A8=E7=9A=84?= =?UTF-8?q?=E6=88=90=E5=8A=9F=E4=B8=8E=E5=A4=B1=E8=B4=A5=E5=93=8D=E5=BA=94?= =?UTF-8?q?=E5=A4=84=E7=90=86=EF=BC=9B=E5=9C=A8DeviceStats=E4=B8=AD?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E7=99=BB=E5=BD=95=E6=97=B6=E9=97=B4=E6=A3=80?= =?UTF-8?q?=E6=9F=A5=E9=80=BB=E8=BE=91=EF=BC=9B=E5=90=AF=E7=94=A8=E5=AE=9A?= =?UTF-8?q?=E6=97=B6=E4=BB=BB=E5=8A=A1=E6=A3=80=E6=9F=A5=E7=A9=BA=E9=97=B2?= =?UTF-8?q?=E8=AE=BE=E5=A4=87=E7=8A=B6=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- logs/server.2025-09-20.0.log.gz | Bin 0 -> 21147 bytes .../ScriptOperationLogController.java | 72 ++++++ .../server/device/DeviceStats.java | 3 +- .../mapper/log/ScriptOperationLogMapper.java | 76 +++++++ .../model/entity/log/ScriptOperationLog.java | 205 ++++++++++++++++++ .../model/enums/ScriptOperationType.java | 21 ++ .../server/service/external/ScriptClient.java | 63 +++++- .../log/ScriptOperationLogService.java | 163 ++++++++++++++ .../server/task/DeviceStatusCheckTask.java | 3 +- ...V20250920__create_script_operation_log.sql | 21 ++ .../mapper/log/ScriptOperationLogMapper.xml | 128 +++++++++++ 11 files changed, 746 insertions(+), 9 deletions(-) create mode 100644 logs/server.2025-09-20.0.log.gz create mode 100644 src/main/java/com/gameplatform/server/controller/ScriptOperationLogController.java create mode 100644 src/main/java/com/gameplatform/server/mapper/log/ScriptOperationLogMapper.java create mode 100644 src/main/java/com/gameplatform/server/model/entity/log/ScriptOperationLog.java create mode 100644 src/main/java/com/gameplatform/server/model/enums/ScriptOperationType.java create mode 100644 src/main/java/com/gameplatform/server/service/log/ScriptOperationLogService.java create mode 100644 src/main/resources/db/migration/V20250920__create_script_operation_log.sql create mode 100644 src/main/resources/mapper/log/ScriptOperationLogMapper.xml diff --git a/logs/server.2025-09-20.0.log.gz b/logs/server.2025-09-20.0.log.gz new file mode 100644 index 0000000000000000000000000000000000000000..b5bd8b3cb6be32164c9895025b94e6b9ae2431a3 GIT binary patch literal 21147 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

-p&~)*S(^KD0w_ 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 OO4~)xqh^uKK=U6g(R?HT literal 0 HcmV?d00001 diff --git a/src/main/java/com/gameplatform/server/controller/ScriptOperationLogController.java b/src/main/java/com/gameplatform/server/controller/ScriptOperationLogController.java new file mode 100644 index 0000000..8043d3f --- /dev/null +++ b/src/main/java/com/gameplatform/server/controller/ScriptOperationLogController.java @@ -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 getByMachineId(@PathVariable String machineId, + // @RequestParam(defaultValue = "10") Integer limit) { + // return logMapper.findByMachineId(machineId, limit); + // } + + // /** + // * 根据操作类型查询记录 + // */ + // @GetMapping("/type/{operationType}") + // public List getByOperationType(@PathVariable ScriptOperationType operationType, + // @RequestParam(defaultValue = "10") Integer limit) { + // return logMapper.findByOperationType(operationType, limit); + // } + + // /** + // * 查询失败的操作记录 + // */ + // @GetMapping("/failed") + // public List 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> 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 getRecentOperations(@RequestParam(defaultValue = "20") Integer limit) { + // LocalDateTime yesterday = LocalDateTime.now().minusDays(1); + // LocalDateTime now = LocalDateTime.now(); + // return logMapper.findByTimeRange(yesterday, now, limit); + // } +} diff --git a/src/main/java/com/gameplatform/server/device/DeviceStats.java b/src/main/java/com/gameplatform/server/device/DeviceStats.java index 08ef9f2..07a3c86 100644 --- a/src/main/java/com/gameplatform/server/device/DeviceStats.java +++ b/src/main/java/com/gameplatform/server/device/DeviceStats.java @@ -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; } diff --git a/src/main/java/com/gameplatform/server/mapper/log/ScriptOperationLogMapper.java b/src/main/java/com/gameplatform/server/mapper/log/ScriptOperationLogMapper.java new file mode 100644 index 0000000..6e2da40 --- /dev/null +++ b/src/main/java/com/gameplatform/server/mapper/log/ScriptOperationLogMapper.java @@ -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 { + + /** + * 插入操作记录 + */ + int insert(ScriptOperationLog log); + + /** + * 更新操作记录 + */ + int update(ScriptOperationLog log); + + /** + * 根据ID查询 + */ + ScriptOperationLog findById(@Param("id") Long id); + + /** + * 根据设备ID查询操作记录 + */ + List findByMachineId(@Param("machineId") String machineId, + @Param("limit") Integer limit); + + /** + * 根据操作类型查询记录 + */ + List findByOperationType(@Param("operationType") ScriptOperationType operationType, + @Param("limit") Integer limit); + + /** + * 根据时间范围查询操作记录 + */ + List findByTimeRange(@Param("startTime") LocalDateTime startTime, + @Param("endTime") LocalDateTime endTime, + @Param("limit") Integer limit); + + /** + * 查询某个设备特定操作类型的记录 + */ + List findByMachineIdAndOperationType(@Param("machineId") String machineId, + @Param("operationType") ScriptOperationType operationType, + @Param("limit") Integer limit); + + /** + * 查询失败的操作记录 + */ + List findFailedOperations(@Param("startTime") LocalDateTime startTime, + @Param("endTime") LocalDateTime endTime, + @Param("limit") Integer limit); + + /** + * 统计某个时间段内各操作类型的成功率 + */ + List> getOperationStatistics(@Param("startTime") LocalDateTime startTime, + @Param("endTime") LocalDateTime endTime); + + /** + * 删除指定时间之前的记录(用于数据清理) + */ + int deleteOldRecords(@Param("beforeTime") LocalDateTime beforeTime); +} diff --git a/src/main/java/com/gameplatform/server/model/entity/log/ScriptOperationLog.java b/src/main/java/com/gameplatform/server/model/entity/log/ScriptOperationLog.java new file mode 100644 index 0000000..fa10f23 --- /dev/null +++ b/src/main/java/com/gameplatform/server/model/entity/log/ScriptOperationLog.java @@ -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 + + '}'; + } +} diff --git a/src/main/java/com/gameplatform/server/model/enums/ScriptOperationType.java b/src/main/java/com/gameplatform/server/model/enums/ScriptOperationType.java new file mode 100644 index 0000000..60bdfef --- /dev/null +++ b/src/main/java/com/gameplatform/server/model/enums/ScriptOperationType.java @@ -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; + } +} diff --git a/src/main/java/com/gameplatform/server/service/external/ScriptClient.java b/src/main/java/com/gameplatform/server/service/external/ScriptClient.java index 4924af1..21a5248 100644 --- a/src/main/java/com/gameplatform/server/service/external/ScriptClient.java +++ b/src/main/java/com/gameplatform/server/service/external/ScriptClient.java @@ -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 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 saveTotalTimes(String meachainId,int times) { - String url = String.format(apiBaseUrl + "/yijianwan_netfile/saveMsg?文件名=总次数&%s=%d", meachainId,times); + public Mono 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); }); } } diff --git a/src/main/java/com/gameplatform/server/service/log/ScriptOperationLogService.java b/src/main/java/com/gameplatform/server/service/log/ScriptOperationLogService.java new file mode 100644 index 0000000..7cfac9f --- /dev/null +++ b/src/main/java/com/gameplatform/server/service/log/ScriptOperationLogService.java @@ -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 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 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 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 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 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; + } + } +} diff --git a/src/main/java/com/gameplatform/server/task/DeviceStatusCheckTask.java b/src/main/java/com/gameplatform/server/task/DeviceStatusCheckTask.java index c5691b1..07fb092 100644 --- a/src/main/java/com/gameplatform/server/task/DeviceStatusCheckTask.java +++ b/src/main/java/com/gameplatform/server/task/DeviceStatusCheckTask.java @@ -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("开始定时检查空闲设备"); diff --git a/src/main/resources/db/migration/V20250920__create_script_operation_log.sql b/src/main/resources/db/migration/V20250920__create_script_operation_log.sql new file mode 100644 index 0000000..6fbb4df --- /dev/null +++ b/src/main/resources/db/migration/V20250920__create_script_operation_log.sql @@ -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 '脚本操作记录表'; diff --git a/src/main/resources/mapper/log/ScriptOperationLogMapper.xml b/src/main/resources/mapper/log/ScriptOperationLogMapper.xml new file mode 100644 index 0000000..ebfddc5 --- /dev/null +++ b/src/main/resources/mapper/log/ScriptOperationLogMapper.xml @@ -0,0 +1,128 @@ + + + + + + + + + + + + + + + + + + + + + + id, operation_type, machine_id, request_url, request_params, response_result, + success, error_message, duration_ms, request_time, response_time, created_at, updated_at + + + + + + + + + + + + + + + + + + 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} + ) + + + + UPDATE script_operation_log + + response_result = #{responseResult}, + success = #{success}, + error_message = #{errorMessage}, + duration_ms = #{durationMs}, + response_time = #{responseTime}, + updated_at = #{updatedAt} + + WHERE id = #{id} + + + + DELETE FROM script_operation_log + WHERE created_at < #{beforeTime} + + +