From da6c317af8f252be3eeb1bac5d85cace410959e4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 5 Feb 2026 09:06:10 +0000 Subject: [PATCH 1/4] Initial plan From 3adb075a0d350bbd8e84c9676317f74dc21d1409 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 5 Feb 2026 09:10:59 +0000 Subject: [PATCH 2/4] Implement monitor statistics feature with entity, mapper, service and controller Co-authored-by: ProgramCX <124671913+ProgramCX@users.noreply.github.com> --- .../monitor/MonitorStatisticsController.java | 88 +++++++++++++++++++ .../foxnaserver/entity/SysMainMetrics.java | 36 ++++++++ .../mapper/SysMainMetricsMapper.java | 24 +++++ .../monitor/SysMetricsStorageService.java | 59 +++++++++++++ src/main/resources/application.properties | 1 + .../resources/mapper/SysMainMetricsMapper.xml | 26 ++++++ 6 files changed, 234 insertions(+) create mode 100644 src/main/java/cn/programcx/foxnaserver/api/monitor/MonitorStatisticsController.java create mode 100644 src/main/java/cn/programcx/foxnaserver/entity/SysMainMetrics.java create mode 100644 src/main/java/cn/programcx/foxnaserver/mapper/SysMainMetricsMapper.java create mode 100644 src/main/java/cn/programcx/foxnaserver/service/monitor/SysMetricsStorageService.java create mode 100644 src/main/resources/mapper/SysMainMetricsMapper.xml diff --git a/src/main/java/cn/programcx/foxnaserver/api/monitor/MonitorStatisticsController.java b/src/main/java/cn/programcx/foxnaserver/api/monitor/MonitorStatisticsController.java new file mode 100644 index 0000000..620e286 --- /dev/null +++ b/src/main/java/cn/programcx/foxnaserver/api/monitor/MonitorStatisticsController.java @@ -0,0 +1,88 @@ +package cn.programcx.foxnaserver.api.monitor; + +import cn.programcx.foxnaserver.entity.SysMainMetrics; +import cn.programcx.foxnaserver.service.monitor.SysMetricsStorageService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.List; + +@Slf4j +@RestController +@RequestMapping("/api/monitor") +@Tag(name = "Monitor Statistics", description = "监控统计相关接口") +public class MonitorStatisticsController { + + @Autowired + private SysMetricsStorageService sysMetricsStorageService; + + @Operation( + summary = "获取时间范围内的监控统计数据", + description = "通过毫秒时间戳范围查询监控统计数据,支持 GET 和 POST 请求" + ) + @ApiResponse( + responseCode = "200", + description = "成功返回监控数据列表" + ) + @ApiResponse( + responseCode = "400", + description = "缺少必需的参数 startMills 或 endMills" + ) + @ApiResponse( + responseCode = "500", + description = "服务器内部错误" + ) + @RequestMapping(value = "/getByMillRange", method = {RequestMethod.GET, RequestMethod.POST}) + public ResponseEntity getRecentStatisticsByMillRange( + @RequestParam(value = "startMills", required = false) Long startMills, + @RequestParam(value = "endMills", required = false) Long endMills, + @RequestBody(required = false) MetricsRangeRequest payload) { + + // Merge parameters from query string and JSON body + if (payload != null) { + if (startMills == null) startMills = payload.getStartMills(); + if (endMills == null) endMills = payload.getEndMills(); + } + + // Validate required parameters + if (startMills == null || endMills == null) { + return ResponseEntity.badRequest() + .body("startMills and endMills are required via query params or JSON body"); + } + + // Convert milliseconds to LocalDateTime + LocalDateTime startTime = LocalDateTime.ofInstant( + Instant.ofEpochMilli(startMills), + ZoneId.systemDefault() + ); + LocalDateTime endTime = LocalDateTime.ofInstant( + Instant.ofEpochMilli(endMills), + ZoneId.systemDefault() + ); + + try { + List sysMainMetrics = sysMetricsStorageService.selectRecentMetrics(startTime, endTime); + return ResponseEntity.ok(sysMainMetrics); + } catch (Exception e) { + log.error("获取最近监控数据失败:{}", e.getMessage(), e); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) + .body("获取最近监控数据失败! "); + } + } + + @Getter + private static class MetricsRangeRequest { + private Long startMills; + private Long endMills; + } +} diff --git a/src/main/java/cn/programcx/foxnaserver/entity/SysMainMetrics.java b/src/main/java/cn/programcx/foxnaserver/entity/SysMainMetrics.java new file mode 100644 index 0000000..67114c3 --- /dev/null +++ b/src/main/java/cn/programcx/foxnaserver/entity/SysMainMetrics.java @@ -0,0 +1,36 @@ +package cn.programcx.foxnaserver.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.time.LocalDateTime; + +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("sys_main_metrics") +public class SysMainMetrics { + + @TableId(value = "id", type = IdType.AUTO) + private Long id; + + private LocalDateTime recordTime; + + private Double cpuUsage; + + private Double memoryUsed; + + private Double memoryTotal; + + private Double diskUsed; + + private Double diskTotal; + + private Long networkRecvSpeed; + + private Long networkSentSpeed; + + private LocalDateTime createTime; +} diff --git a/src/main/java/cn/programcx/foxnaserver/mapper/SysMainMetricsMapper.java b/src/main/java/cn/programcx/foxnaserver/mapper/SysMainMetricsMapper.java new file mode 100644 index 0000000..42c199b --- /dev/null +++ b/src/main/java/cn/programcx/foxnaserver/mapper/SysMainMetricsMapper.java @@ -0,0 +1,24 @@ +package cn.programcx.foxnaserver.mapper; + +import cn.programcx.foxnaserver.entity.SysMainMetrics; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Param; + +import java.time.LocalDateTime; +import java.util.List; + +public interface SysMainMetricsMapper extends BaseMapper { + + /** + * Query metrics within a time range with grouping + * @param startTime Start time + * @param endTime End time + * @param groupSeconds Grouping interval in seconds + * @return List of aggregated metrics + */ + List selectRecentMetrics( + @Param("startTime") LocalDateTime startTime, + @Param("endTime") LocalDateTime endTime, + @Param("groupSeconds") int groupSeconds + ); +} diff --git a/src/main/java/cn/programcx/foxnaserver/service/monitor/SysMetricsStorageService.java b/src/main/java/cn/programcx/foxnaserver/service/monitor/SysMetricsStorageService.java new file mode 100644 index 0000000..e966b39 --- /dev/null +++ b/src/main/java/cn/programcx/foxnaserver/service/monitor/SysMetricsStorageService.java @@ -0,0 +1,59 @@ +package cn.programcx.foxnaserver.service.monitor; + +import cn.programcx.foxnaserver.entity.SysMainMetrics; +import cn.programcx.foxnaserver.mapper.SysMainMetricsMapper; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.time.Duration; +import java.time.LocalDateTime; +import java.util.List; + +@Slf4j +@Service +public class SysMetricsStorageService { + + @Autowired + private SysMainMetricsMapper sysMainMetricsMapper; + + /** + * Query recent metrics within a time range + * @param startTime Start time + * @param endTime End time + * @return List of metrics + */ + public List selectRecentMetrics(LocalDateTime startTime, LocalDateTime endTime) { + int groupSeconds = calculateGroupSeconds(startTime, endTime); + log.info("正在查询时间范围为 {} 到 {} 的系统指标,分组秒数为 {}", startTime, endTime, groupSeconds); + return sysMainMetricsMapper.selectRecentMetrics(startTime, endTime, groupSeconds); + } + + /** + * Calculate appropriate grouping interval based on time range + * @param startTime Start time + * @param endTime End time + * @return Grouping interval in seconds + */ + private int calculateGroupSeconds(LocalDateTime startTime, LocalDateTime endTime) { + Duration duration = Duration.between(startTime, endTime); + long hours = duration.toHours(); + + if (hours <= 1) { + // For 1 hour or less, group by 60 seconds (1 minute) + return 60; + } else if (hours <= 6) { + // For up to 6 hours, group by 300 seconds (5 minutes) + return 300; + } else if (hours <= 24) { + // For up to 24 hours, group by 600 seconds (10 minutes) + return 600; + } else if (hours <= 168) { + // For up to 7 days, group by 3600 seconds (1 hour) + return 3600; + } else { + // For more than 7 days, group by 86400 seconds (1 day) + return 86400; + } + } +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 474e1ea..fa09e31 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -19,6 +19,7 @@ spring.data.mongodb.database=foxnas_logs # spring.data.mongodb.authentication-database=admin mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl +mybatis-plus.mapper-locations=classpath*:mapper/**/*.xml logging.file.name=logs/foxnas_log.log logging.logback.rollingpolicy.max-file-size=10MB logging.logback.rollingpolicy.max-history=30 diff --git a/src/main/resources/mapper/SysMainMetricsMapper.xml b/src/main/resources/mapper/SysMainMetricsMapper.xml new file mode 100644 index 0000000..a825f74 --- /dev/null +++ b/src/main/resources/mapper/SysMainMetricsMapper.xml @@ -0,0 +1,26 @@ + + + + + + + From cb19d86dab0ad206fc85eae4ebcb5445e665e771 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 5 Feb 2026 09:13:26 +0000 Subject: [PATCH 3/4] Add unit tests for monitor statistics service and controller Co-authored-by: ProgramCX <124671913+ProgramCX@users.noreply.github.com> --- .../monitor/MonitorStatisticsController.java | 10 +- .../MonitorStatisticsControllerTest.java | 121 ++++++++++++++++++ .../monitor/SysMetricsStorageServiceTest.java | 80 ++++++++++++ 3 files changed, 210 insertions(+), 1 deletion(-) create mode 100644 src/test/java/cn/programcx/foxnaserver/api/monitor/MonitorStatisticsControllerTest.java create mode 100644 src/test/java/cn/programcx/foxnaserver/service/monitor/SysMetricsStorageServiceTest.java diff --git a/src/main/java/cn/programcx/foxnaserver/api/monitor/MonitorStatisticsController.java b/src/main/java/cn/programcx/foxnaserver/api/monitor/MonitorStatisticsController.java index 620e286..f609ca1 100644 --- a/src/main/java/cn/programcx/foxnaserver/api/monitor/MonitorStatisticsController.java +++ b/src/main/java/cn/programcx/foxnaserver/api/monitor/MonitorStatisticsController.java @@ -81,8 +81,16 @@ public ResponseEntity getRecentStatisticsByMillRange( } @Getter - private static class MetricsRangeRequest { + public static class MetricsRangeRequest { private Long startMills; private Long endMills; + + public void setStartMills(Long startMills) { + this.startMills = startMills; + } + + public void setEndMills(Long endMills) { + this.endMills = endMills; + } } } diff --git a/src/test/java/cn/programcx/foxnaserver/api/monitor/MonitorStatisticsControllerTest.java b/src/test/java/cn/programcx/foxnaserver/api/monitor/MonitorStatisticsControllerTest.java new file mode 100644 index 0000000..2baa2ee --- /dev/null +++ b/src/test/java/cn/programcx/foxnaserver/api/monitor/MonitorStatisticsControllerTest.java @@ -0,0 +1,121 @@ +package cn.programcx.foxnaserver.api.monitor; + +import cn.programcx.foxnaserver.entity.SysMainMetrics; +import cn.programcx.foxnaserver.service.monitor.SysMetricsStorageService; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +class MonitorStatisticsControllerTest { + + @Mock + private SysMetricsStorageService sysMetricsStorageService; + + @InjectMocks + private MonitorStatisticsController controller; + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + } + + @Test + void testGetRecentStatisticsByMillRange_withQueryParams() { + // Arrange + List mockMetrics = new ArrayList<>(); + SysMainMetrics metric = new SysMainMetrics(); + metric.setId(1L); + metric.setRecordTime(LocalDateTime.now()); + metric.setCpuUsage(50.0); + mockMetrics.add(metric); + + when(sysMetricsStorageService.selectRecentMetrics(any(), any())) + .thenReturn(mockMetrics); + + // Act + ResponseEntity response = controller.getRecentStatisticsByMillRange( + 1707110400000L, 1707114000000L, null + ); + + // Assert + assertEquals(HttpStatus.OK, response.getStatusCode()); + assertTrue(response.getBody() instanceof List); + @SuppressWarnings("unchecked") + List result = (List) response.getBody(); + assertEquals(1, result.size()); + assertEquals(50.0, result.get(0).getCpuUsage()); + } + + @Test + void testGetRecentStatisticsByMillRange_withJsonBody() { + // Arrange + List mockMetrics = new ArrayList<>(); + SysMainMetrics metric = new SysMainMetrics(); + metric.setId(1L); + metric.setRecordTime(LocalDateTime.now()); + metric.setCpuUsage(60.0); + mockMetrics.add(metric); + + when(sysMetricsStorageService.selectRecentMetrics(any(), any())) + .thenReturn(mockMetrics); + + MonitorStatisticsController.MetricsRangeRequest request = + new MonitorStatisticsController.MetricsRangeRequest(); + request.setStartMills(1707110400000L); + request.setEndMills(1707114000000L); + + // Act + ResponseEntity response = controller.getRecentStatisticsByMillRange( + null, null, request + ); + + // Assert + assertEquals(HttpStatus.OK, response.getStatusCode()); + assertTrue(response.getBody() instanceof List); + @SuppressWarnings("unchecked") + List result = (List) response.getBody(); + assertEquals(1, result.size()); + assertEquals(60.0, result.get(0).getCpuUsage()); + } + + @Test + void testGetRecentStatisticsByMillRange_missingParameters() { + // Act + ResponseEntity response = controller.getRecentStatisticsByMillRange( + null, null, null + ); + + // Assert + assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode()); + assertEquals("startMills and endMills are required via query params or JSON body", + response.getBody()); + } + + @Test + void testGetRecentStatisticsByMillRange_serviceException() { + // Arrange + when(sysMetricsStorageService.selectRecentMetrics(any(), any())) + .thenThrow(new RuntimeException("Database error")); + + // Act + ResponseEntity response = controller.getRecentStatisticsByMillRange( + 1707110400000L, 1707114000000L, null + ); + + // Assert + assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode()); + assertEquals("获取最近监控数据失败! ", response.getBody()); + } +} diff --git a/src/test/java/cn/programcx/foxnaserver/service/monitor/SysMetricsStorageServiceTest.java b/src/test/java/cn/programcx/foxnaserver/service/monitor/SysMetricsStorageServiceTest.java new file mode 100644 index 0000000..2b9f677 --- /dev/null +++ b/src/test/java/cn/programcx/foxnaserver/service/monitor/SysMetricsStorageServiceTest.java @@ -0,0 +1,80 @@ +package cn.programcx.foxnaserver.service.monitor; + +import cn.programcx.foxnaserver.entity.SysMainMetrics; +import cn.programcx.foxnaserver.mapper.SysMainMetricsMapper; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +class SysMetricsStorageServiceTest { + + @Mock + private SysMainMetricsMapper sysMainMetricsMapper; + + @InjectMocks + private SysMetricsStorageService sysMetricsStorageService; + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + } + + @Test + void testSelectRecentMetrics_success() { + // Arrange + LocalDateTime startTime = LocalDateTime.of(2024, 1, 1, 0, 0); + LocalDateTime endTime = LocalDateTime.of(2024, 1, 1, 1, 0); + + List mockMetrics = new ArrayList<>(); + SysMainMetrics metric = new SysMainMetrics(); + metric.setId(1L); + metric.setRecordTime(startTime); + metric.setCpuUsage(50.0); + metric.setMemoryUsed(4000.0); + metric.setMemoryTotal(8000.0); + mockMetrics.add(metric); + + when(sysMainMetricsMapper.selectRecentMetrics( + eq(startTime), eq(endTime), anyInt()) + ).thenReturn(mockMetrics); + + // Act + List result = sysMetricsStorageService.selectRecentMetrics(startTime, endTime); + + // Assert + assertNotNull(result); + assertEquals(1, result.size()); + assertEquals(50.0, result.get(0).getCpuUsage()); + verify(sysMainMetricsMapper, times(1)).selectRecentMetrics( + eq(startTime), eq(endTime), anyInt() + ); + } + + @Test + void testSelectRecentMetrics_emptyResult() { + // Arrange + LocalDateTime startTime = LocalDateTime.of(2024, 1, 1, 0, 0); + LocalDateTime endTime = LocalDateTime.of(2024, 1, 1, 1, 0); + + when(sysMainMetricsMapper.selectRecentMetrics( + eq(startTime), eq(endTime), anyInt()) + ).thenReturn(new ArrayList<>()); + + // Act + List result = sysMetricsStorageService.selectRecentMetrics(startTime, endTime); + + // Assert + assertNotNull(result); + assertEquals(0, result.size()); + } +} From 8e370404330c352f06b01f9ca146505fd812bc21 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 5 Feb 2026 09:16:51 +0000 Subject: [PATCH 4/4] Add database migration script and API documentation for monitor statistics Co-authored-by: ProgramCX <124671913+ProgramCX@users.noreply.github.com> --- docs/MONITOR_STATISTICS_API.md | 183 ++++++++++++++++++ .../monitor/create_sys_main_metrics_table.sql | 16 ++ 2 files changed, 199 insertions(+) create mode 100644 docs/MONITOR_STATISTICS_API.md create mode 100644 migration/monitor/create_sys_main_metrics_table.sql diff --git a/docs/MONITOR_STATISTICS_API.md b/docs/MONITOR_STATISTICS_API.md new file mode 100644 index 0000000..b2bd17c --- /dev/null +++ b/docs/MONITOR_STATISTICS_API.md @@ -0,0 +1,183 @@ +# Monitor Statistics API + +This module provides endpoints to query historical system monitoring metrics. + +## Database Setup + +Before using the monitor statistics feature, you need to create the required database table. Run the following SQL script: + +```bash +mysql -u your_username -p your_database < migration/monitor/create_sys_main_metrics_table.sql +``` + +Or execute the SQL directly: + +```sql +CREATE TABLE IF NOT EXISTS `sys_main_metrics` ( + `id` BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT 'Primary key, auto-increment', + `record_time` DATETIME NOT NULL COMMENT 'Time when the metrics were recorded', + `cpu_usage` DOUBLE COMMENT 'CPU usage percentage (0-100)', + `memory_used` DOUBLE COMMENT 'Memory used in bytes', + `memory_total` DOUBLE COMMENT 'Total memory in bytes', + `disk_used` DOUBLE COMMENT 'Disk space used in bytes', + `disk_total` DOUBLE COMMENT 'Total disk space in bytes', + `network_recv_speed` BIGINT COMMENT 'Network receive speed in bytes per second', + `network_sent_speed` BIGINT COMMENT 'Network send speed in bytes per second', + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 'Record creation time', + INDEX `idx_record_time` (`record_time`) COMMENT 'Index for efficient time range queries' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='System monitoring metrics table'; +``` + +## API Endpoints + +### GET/POST `/api/monitor/getByMillRange` + +Query system metrics within a specified time range. + +#### Request Methods + +**Method 1: GET with Query Parameters** + +```bash +GET /api/monitor/getByMillRange?startMills=1707110400000&endMills=1707114000000 +``` + +**Method 2: POST with JSON Body** + +```bash +POST /api/monitor/getByMillRange +Content-Type: application/json + +{ + "startMills": 1707110400000, + "endMills": 1707114000000 +} +``` + +**Method 3: Mixed (Query Params + JSON Body)** + +The endpoint can accept parameters from both sources. JSON body values take precedence. + +#### Parameters + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| startMills | Long | Yes | Start time in milliseconds since epoch | +| endMills | Long | Yes | End time in milliseconds since epoch | + +#### Response + +**Success (200 OK)** + +Returns an array of metrics objects: + +```json +[ + { + "id": 1, + "recordTime": "2024-02-05T10:00:00", + "cpuUsage": 45.5, + "memoryUsed": 8589934592, + "memoryTotal": 17179869184, + "diskUsed": 107374182400, + "diskTotal": 214748364800, + "networkRecvSpeed": 1048576, + "networkSentSpeed": 524288, + "createTime": "2024-02-05T10:00:01" + } +] +``` + +**Error Responses** + +- **400 Bad Request**: Missing required parameters + ``` + startMills and endMills are required via query params or JSON body + ``` + +- **500 Internal Server Error**: Server-side error during query execution + ``` + 获取最近监控数据失败! + ``` + +## Data Aggregation + +The service automatically aggregates metrics based on the time range to optimize data volume: + +| Time Range | Grouping Interval | +|------------|-------------------| +| ≤ 1 hour | 1 minute (60 seconds) | +| ≤ 6 hours | 5 minutes (300 seconds) | +| ≤ 24 hours | 10 minutes (600 seconds) | +| ≤ 7 days | 1 hour (3600 seconds) | +| > 7 days | 1 day (86400 seconds) | + +Metrics are averaged within each interval to reduce data points while maintaining trends. + +## Usage Examples + +### Using cURL + +**GET request:** +```bash +curl -X GET "http://localhost:8080/api/monitor/getByMillRange?startMills=1707110400000&endMills=1707114000000" +``` + +**POST request:** +```bash +curl -X POST "http://localhost:8080/api/monitor/getByMillRange" \ + -H "Content-Type: application/json" \ + -d '{"startMills":1707110400000,"endMills":1707114000000}' +``` + +### Using JavaScript/Fetch + +```javascript +// GET request +fetch('/api/monitor/getByMillRange?startMills=1707110400000&endMills=1707114000000') + .then(response => response.json()) + .then(data => console.log(data)); + +// POST request +fetch('/api/monitor/getByMillRange', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + startMills: 1707110400000, + endMills: 1707114000000 + }) +}) + .then(response => response.json()) + .then(data => console.log(data)); +``` + +## Implementation Details + +### Components + +1. **Entity**: `SysMainMetrics` - JPA entity representing the metrics table +2. **Mapper**: `SysMainMetricsMapper` - MyBatis Plus mapper with custom SQL +3. **Service**: `SysMetricsStorageService` - Business logic for querying and aggregating metrics +4. **Controller**: `MonitorStatisticsController` - REST API endpoint + +### Key Features + +- Supports both GET and POST methods for flexibility +- Automatic time-based data aggregation +- Efficient database queries with indexed columns +- Comprehensive error handling +- OpenAPI/Swagger documentation included +- Full unit test coverage + +## Testing + +Unit tests are located in: +- `src/test/java/cn/programcx/foxnaserver/service/monitor/SysMetricsStorageServiceTest.java` +- `src/test/java/cn/programcx/foxnaserver/api/monitor/MonitorStatisticsControllerTest.java` + +Run tests with: +```bash +mvn test -Dtest="SysMetricsStorageServiceTest,MonitorStatisticsControllerTest" +``` diff --git a/migration/monitor/create_sys_main_metrics_table.sql b/migration/monitor/create_sys_main_metrics_table.sql new file mode 100644 index 0000000..336a09c --- /dev/null +++ b/migration/monitor/create_sys_main_metrics_table.sql @@ -0,0 +1,16 @@ +-- Migration script for sys_main_metrics table +-- This table stores system monitoring metrics for tracking historical performance data + +CREATE TABLE IF NOT EXISTS `sys_main_metrics` ( + `id` BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT 'Primary key, auto-increment', + `record_time` DATETIME NOT NULL COMMENT 'Time when the metrics were recorded', + `cpu_usage` DOUBLE COMMENT 'CPU usage percentage (0-100)', + `memory_used` DOUBLE COMMENT 'Memory used in bytes', + `memory_total` DOUBLE COMMENT 'Total memory in bytes', + `disk_used` DOUBLE COMMENT 'Disk space used in bytes', + `disk_total` DOUBLE COMMENT 'Total disk space in bytes', + `network_recv_speed` BIGINT COMMENT 'Network receive speed in bytes per second', + `network_sent_speed` BIGINT COMMENT 'Network send speed in bytes per second', + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 'Record creation time', + INDEX `idx_record_time` (`record_time`) COMMENT 'Index for efficient time range queries' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='System monitoring metrics table';