fully commits

This commit is contained in:
2026-02-21 07:48:36 +08:00
parent 9816020d8a
commit 21d7d01203
3 changed files with 228 additions and 69 deletions
+50 -12
View File
@@ -25,7 +25,11 @@ jobs:
image: harbor.omniverseai.net/ljwx-base-images/maven3.9-jdk21:latest
steps:
- name: Checkout code
run: git clone --depth 1 http://192.168.1.83:33000/gao/ljwx-health.git repo
run: |
git clone http://192.168.1.83:33000/gao/ljwx-health.git repo
cd repo
git fetch --depth 1 origin "${{ github.sha }}"
git checkout --detach "${{ github.sha }}"
- name: Configure Maven Aliyun mirror
run: |
@@ -57,16 +61,24 @@ jobs:
cd repo/ljwx-boot-starter
mvn clean install -DskipTests --batch-mode
- name: Run Checkstyle (Code Quality)
- name: Run Checkstyle (Non-blocking)
run: |
cd repo/ljwx-boot
mvn checkstyle:check --batch-mode || true
- name: Run Unit Tests with Coverage
- name: Run Backend Automated Tests
run: |
cd repo/ljwx-boot
mvn clean test --batch-mode
mvn jacoco:report --batch-mode
mvn -pl ljwx-boot-admin -am clean test \
-Dmaven.test.skip=false \
-DskipTests=false \
-Dtest=MonitoringControllerTest \
-Dsurefire.failIfNoSpecifiedTests=false \
--batch-mode
mvn -pl ljwx-boot-admin -am jacoco:report \
-Dmaven.test.skip=false \
-DskipTests=false \
--batch-mode
echo "📊 Coverage report generated at target/site/jacoco/index.html"
- name: Build ljwx-boot package
@@ -81,7 +93,11 @@ jobs:
image: harbor.omniverseai.net/ljwx-base-images/node20-pnpm:latest
steps:
- name: Checkout code
run: git clone --depth 1 http://192.168.1.83:33000/gao/ljwx-health.git repo
run: |
git clone http://192.168.1.83:33000/gao/ljwx-health.git repo
cd repo
git fetch --depth 1 origin "${{ github.sha }}"
git checkout --detach "${{ github.sha }}"
- name: Install dependencies
run: |
@@ -107,7 +123,7 @@ jobs:
- name: Run Unit Tests with Coverage
run: |
cd repo/ljwx-admin
pnpm test:coverage || true
pnpm test:coverage
echo "📊 Coverage report generated at coverage/index.html"
# ==================== Job 3: Test BigScreen (ljwx-bigscreen) ====================
@@ -117,7 +133,11 @@ jobs:
image: harbor.omniverseai.net/ljwx-base-images/python3.12-pip:latest
steps:
- name: Checkout code
run: git clone --depth 1 http://192.168.1.83:33000/gao/ljwx-health.git repo
run: |
git clone http://192.168.1.83:33000/gao/ljwx-health.git repo
cd repo
git fetch --depth 1 origin "${{ github.sha }}"
git checkout --detach "${{ github.sha }}"
- name: Install dependencies
run: |
@@ -137,7 +157,13 @@ jobs:
- name: Run Pytest with Coverage
run: |
cd repo/ljwx-bigscreen
pytest --cov=bigScreen --cov-report=html --cov-report=xml --cov-report=term || true
mkdir -p reports
pytest tests/test_auth.py tests/test_integration.py \
--cov=app \
--cov-report=html \
--cov-report=xml \
--cov-report=term \
--junitxml=reports/junit.xml
- name: Run Safety Security Check
run: |
@@ -150,7 +176,11 @@ jobs:
runs-on: docker-builder
steps:
- name: Checkout code
run: git clone --depth 1 http://192.168.1.83:33000/gao/ljwx-health.git repo
run: |
git clone http://192.168.1.83:33000/gao/ljwx-health.git repo
cd repo
git fetch --depth 1 origin "${{ github.sha }}"
git checkout --detach "${{ github.sha }}"
- name: Ensure Docker CLI
run: |
@@ -206,7 +236,11 @@ jobs:
- ljwx-bigscreen
steps:
- name: Checkout code
run: git clone --depth 1 http://192.168.1.83:33000/gao/ljwx-health.git repo
run: |
git clone http://192.168.1.83:33000/gao/ljwx-health.git repo
cd repo
git fetch --depth 1 origin "${{ github.sha }}"
git checkout --detach "${{ github.sha }}"
- name: Ensure Docker CLI
run: |
@@ -336,7 +370,11 @@ jobs:
- ljwx-bigscreen
steps:
- name: Checkout code
run: git clone --depth 1 http://192.168.1.83:33000/gao/ljwx-health.git repo
run: |
git clone http://192.168.1.83:33000/gao/ljwx-health.git repo
cd repo
git fetch --depth 1 origin "${{ github.sha }}"
git checkout --detach "${{ github.sha }}"
- name: Ensure Docker CLI
run: |
@@ -2,9 +2,14 @@ package com.ljwx.admin.controller.monitoring;
import com.ljwx.modules.monitor.service.IMonSchedulerService;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.prometheus.PrometheusMeterRegistry;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.actuate.health.HealthComponent;
import org.springframework.boot.actuate.health.HealthEndpoint;
import org.springframework.boot.actuate.info.InfoEndpoint;
import org.springframework.core.env.Environment;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -31,16 +36,26 @@ import java.util.Map;
@RequestMapping("/monitoring")
public class MonitoringController {
private static final String PROMETHEUS_CONTENT_TYPE = "text/plain; version=0.0.4; charset=utf-8";
private final MeterRegistry meterRegistry;
private final PrometheusMeterRegistry prometheusMeterRegistry;
private final HealthEndpoint healthEndpoint;
private final InfoEndpoint infoEndpoint;
private final Environment environment;
private final IMonSchedulerService monSchedulerService;
@GetMapping("/health")
@Operation(summary = "健康检查")
public Map<String, Object> health() {
recordEndpointRequest("health");
Map<String, Object> health = new HashMap<>();
health.put("status", "UP");
HealthComponent actuatorHealth = healthEndpoint.health();
health.put("status", actuatorHealth.getStatus().getCode());
health.put("timestamp", System.currentTimeMillis());
health.put("actuator", actuatorHealth);
// 内存信息
MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
Map<String, Object> memory = new HashMap<>();
@@ -67,74 +82,33 @@ public class MonitoringController {
return health;
}
@GetMapping("/metrics")
@GetMapping(value = "/metrics", produces = PROMETHEUS_CONTENT_TYPE)
@Operation(summary = "Prometheus指标")
public String metrics() {
StringBuilder metrics = new StringBuilder();
// 基础指标
metrics.append("# HELP ljwx_boot_up Service status\n");
metrics.append("# TYPE ljwx_boot_up gauge\n");
metrics.append("ljwx_boot_up 1\n");
// JVM内存指标
MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
metrics.append("# HELP jvm_memory_used_bytes JVM memory used\n");
metrics.append("# TYPE jvm_memory_used_bytes gauge\n");
metrics.append("jvm_memory_used_bytes{area=\"heap\"} ").append(memoryBean.getHeapMemoryUsage().getUsed()).append("\n");
metrics.append("jvm_memory_used_bytes{area=\"nonheap\"} ").append(memoryBean.getNonHeapMemoryUsage().getUsed()).append("\n");
metrics.append("# HELP jvm_memory_max_bytes JVM memory max\n");
metrics.append("# TYPE jvm_memory_max_bytes gauge\n");
metrics.append("jvm_memory_max_bytes{area=\"heap\"} ").append(memoryBean.getHeapMemoryUsage().getMax()).append("\n");
metrics.append("jvm_memory_max_bytes{area=\"nonheap\"} ").append(memoryBean.getNonHeapMemoryUsage().getMax()).append("\n");
// 线程指标
ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
metrics.append("# HELP jvm_threads_live_threads JVM live threads\n");
metrics.append("# TYPE jvm_threads_live_threads gauge\n");
metrics.append("jvm_threads_live_threads ").append(threadBean.getThreadCount()).append("\n");
metrics.append("# HELP jvm_threads_peak_threads JVM peak threads\n");
metrics.append("# TYPE jvm_threads_peak_threads gauge\n");
metrics.append("jvm_threads_peak_threads ").append(threadBean.getPeakThreadCount()).append("\n");
// 运行时间指标
RuntimeMXBean runtimeBean = ManagementFactory.getRuntimeMXBean();
metrics.append("# HELP process_uptime_seconds Process uptime\n");
metrics.append("# TYPE process_uptime_seconds counter\n");
metrics.append("process_uptime_seconds ").append(runtimeBean.getUptime() / 1000.0).append("\n");
// HTTP请求指标 (模拟)
metrics.append("# HELP http_server_requests_seconds_count HTTP requests count\n");
metrics.append("# TYPE http_server_requests_seconds_count counter\n");
metrics.append("http_server_requests_seconds_count{method=\"GET\",status=\"200\",uri=\"/monitoring/health\"} ").append(System.currentTimeMillis() % 1000).append("\n");
metrics.append("http_server_requests_seconds_count{method=\"GET\",status=\"200\",uri=\"/monitoring/metrics\"} ").append(System.currentTimeMillis() % 800).append("\n");
metrics.append("# HELP http_server_requests_seconds HTTP request duration\n");
metrics.append("# TYPE http_server_requests_seconds histogram\n");
double responseTime = 0.1 + (System.currentTimeMillis() % 100) / 1000.0;
metrics.append("http_server_requests_seconds_sum{method=\"GET\",status=\"200\",uri=\"/monitoring/health\"} ").append(responseTime * 10).append("\n");
metrics.append("http_server_requests_seconds_count{method=\"GET\",status=\"200\",uri=\"/monitoring/health\"} 10\n");
return metrics.toString();
recordEndpointRequest("metrics");
return prometheusMeterRegistry.scrape();
}
@GetMapping("/info")
@Operation(summary = "应用信息")
public Map<String, Object> info() {
Map<String, Object> info = new HashMap<>();
info.put("app", "ljwx-boot");
info.put("version", "1.0.6-SNAPSHOT");
info.put("environment", "docker");
info.put("java_version", System.getProperty("java.version"));
info.put("spring_boot_version", "3.2.0");
recordEndpointRequest("info");
Map<String, Object> info = new HashMap<>(infoEndpoint.info());
Map<String, Object> appInfo = new HashMap<>();
appInfo.put("name", environment.getProperty("spring.application.name", "ljwx-boot"));
appInfo.put("version", resolveAppVersion());
appInfo.put("environment", resolveEnvironment());
appInfo.put("java_version", System.getProperty("java.version"));
info.put("app", appInfo);
return info;
}
@GetMapping("/scheduler/test/{id}")
@Operation(summary = "测试调度任务执行 - 无需认证")
public Map<String, Object> testScheduler(@PathVariable Long id) {
recordEndpointRequest("scheduler_test");
Map<String, Object> result = new HashMap<>();
try {
boolean success = monSchedulerService.immediateMonScheduler(id);
@@ -151,4 +125,27 @@ public class MonitoringController {
}
return result;
}
private void recordEndpointRequest(String endpoint) {
meterRegistry.counter("ljwx_monitoring_endpoint_requests_total", "endpoint", endpoint).increment();
}
private String resolveAppVersion() {
String configuredVersion = environment.getProperty("spring.application.version");
if (configuredVersion != null && !configuredVersion.isBlank()) {
return configuredVersion;
}
Package controllerPackage = MonitoringController.class.getPackage();
String implementationVersion = controllerPackage != null ? controllerPackage.getImplementationVersion() : null;
return implementationVersion != null ? implementationVersion : "unknown";
}
private String resolveEnvironment() {
String[] activeProfiles = environment.getActiveProfiles();
if (activeProfiles.length == 0) {
return "default";
}
return String.join(",", activeProfiles);
}
}
@@ -0,0 +1,124 @@
package com.ljwx.admin.controller.monitoring;
import com.ljwx.modules.monitor.service.IMonSchedulerService;
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.prometheus.PrometheusMeterRegistry;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthEndpoint;
import org.springframework.boot.actuate.info.InfoEndpoint;
import org.springframework.core.env.Environment;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class MonitoringControllerTest {
@Mock
private MeterRegistry meterRegistry;
@Mock
private Counter counter;
@Mock
private PrometheusMeterRegistry prometheusMeterRegistry;
@Mock
private HealthEndpoint healthEndpoint;
@Mock
private InfoEndpoint infoEndpoint;
@Mock
private Environment environment;
@Mock
private IMonSchedulerService monSchedulerService;
private MonitoringController monitoringController;
@BeforeEach
void setUp() {
when(meterRegistry.counter(anyString(), eq("endpoint"), anyString())).thenReturn(counter);
monitoringController = new MonitoringController(
meterRegistry,
prometheusMeterRegistry,
healthEndpoint,
infoEndpoint,
environment,
monSchedulerService
);
}
@Test
void healthShouldReturnActuatorStatus() {
when(healthEndpoint.health()).thenReturn(Health.up().withDetail("db", "UP").build());
Map<String, Object> result = monitoringController.health();
assertEquals("UP", result.get("status"));
assertTrue(result.containsKey("actuator"));
assertTrue(result.containsKey("memory"));
assertTrue(result.containsKey("threads"));
assertTrue(result.containsKey("runtime"));
}
@Test
void metricsShouldReturnPrometheusScrapeContent() {
String expected = "# HELP test_metric test\n# TYPE test_metric gauge\ntest_metric 1\n";
when(prometheusMeterRegistry.scrape()).thenReturn(expected);
String result = monitoringController.metrics();
assertEquals(expected, result);
}
@Test
void infoShouldIncludeDynamicAppMetadata() {
when(infoEndpoint.info()).thenReturn(Map.of("build", Map.of("artifact", "ljwx-boot")));
when(environment.getProperty("spring.application.name", "ljwx-boot")).thenReturn("ljwx-boot-admin");
when(environment.getProperty("spring.application.version")).thenReturn("1.0.6-SNAPSHOT");
when(environment.getActiveProfiles()).thenReturn(new String[]{"k8s", "prod"});
Map<String, Object> result = monitoringController.info();
@SuppressWarnings("unchecked")
Map<String, Object> appInfo = (Map<String, Object>) result.get("app");
assertEquals("ljwx-boot-admin", appInfo.get("name"));
assertEquals("1.0.6-SNAPSHOT", appInfo.get("version"));
assertEquals("k8s,prod", appInfo.get("environment"));
assertTrue(appInfo.containsKey("java_version"));
}
@Test
void testSchedulerShouldReturnSuccessWhenServiceSucceeds() {
when(monSchedulerService.immediateMonScheduler(100L)).thenReturn(true);
Map<String, Object> result = monitoringController.testScheduler(100L);
assertEquals(true, result.get("success"));
assertEquals(100L, result.get("schedulerId"));
}
@Test
void testSchedulerShouldReturnErrorWhenServiceThrows() {
when(monSchedulerService.immediateMonScheduler(200L)).thenThrow(new IllegalStateException("boom"));
Map<String, Object> result = monitoringController.testScheduler(200L);
assertEquals(false, result.get("success"));
assertEquals(200L, result.get("schedulerId"));
assertTrue(String.valueOf(result.get("message")).contains("boom"));
}
}