diff --git a/pom.xml b/pom.xml
index 69662a2..965c326 100644
--- a/pom.xml
+++ b/pom.xml
@@ -55,6 +55,20 @@
spring-boot-starter-aop
+
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+
+
+ io.micrometer
+ micrometer-registry-prometheus
+
+
+
org.projectlombok
lombok
@@ -78,22 +92,12 @@
3.2.0
-
+
com.yunhai.platform
- yh-auth-spring-boot-starter
-
-
- com.yunhai.platform
- yh-app-auth-spring-boot-starter
-
-
- com.yunhai.platform
- yh-openapi-spring-boot-starter
-
-
- com.yunhai.platform
- yh-api-std
+ yh-spring-boot-starter
diff --git a/src/main/java/com/yunhai/userhome/loadtestmetrics/ApiMetricsCollector.java b/src/main/java/com/yunhai/userhome/loadtestmetrics/ApiMetricsCollector.java
new file mode 100644
index 0000000..b7f0952
--- /dev/null
+++ b/src/main/java/com/yunhai/userhome/loadtestmetrics/ApiMetricsCollector.java
@@ -0,0 +1,143 @@
+package com.yunhai.userhome.loadtestmetrics;
+
+/* =============================================================================
+ * [LOADTEST-METRICS] 临时埋点 —— 《压测与oapi埋点统计方案.md》阶段二
+ * 压测结束后整体移除:删除 loadtestmetrics 包 + 回退 pom.xml/application.yml
+ * 中 [LOADTEST-METRICS] 标记段 + DROP TABLE api_metrics。
+ * 移除步骤详见仓库根《埋点移除清单.md》。
+ * ============================================================================= */
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * [LOADTEST-METRICS] UserHome 指标内存累加器(方案 §3)。
+ * 按 (uri, method, direction, isLoadTest) 维度 ConcurrentHashMap O(1) 原子累加,
+ * 不走队列、不做同步 IO;每 60s 快照落库 api_metrics,失败加回不丢数。
+ * Nacos 可下发 api.metrics.enabled=false 整体停用。
+ * (本类自带 @EnableScheduling 见 ApiMetricsConfig,UserHome 主类原本未开启定时任务,
+ * 删除本包后定时能力一并消失,不留痕迹。)
+ */
+@Slf4j
+@Component
+public class ApiMetricsCollector {
+
+ /** 聚合维度 key(uri×method×direction×压测标记) */
+ private static final class Dim {
+ final String uri;
+ final String method;
+ final String direction;
+ final boolean loadTest;
+
+ Dim(String uri, String method, String direction, boolean loadTest) {
+ this.uri = uri;
+ this.method = method;
+ this.direction = direction;
+ this.loadTest = loadTest;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof Dim)) {
+ return false;
+ }
+ Dim d = (Dim) o;
+ return loadTest == d.loadTest && uri.equals(d.uri) && method.equals(d.method)
+ && direction.equals(d.direction);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(uri, method, direction, loadTest);
+ }
+ }
+
+ /** 单维度三指标:次数 / 耗时累计 / 错误数 */
+ private static final class Counter {
+ final AtomicLong cnt = new AtomicLong();
+ final AtomicLong cost = new AtomicLong();
+ final AtomicLong err = new AtomicLong();
+ }
+
+ private final ConcurrentHashMap counters = new ConcurrentHashMap<>();
+
+ @Autowired
+ private ApiMetricsRepository repository;
+
+ /** 埋点总开关(Nacos 可下发紧急关停) */
+ @Value("${api.metrics.enabled:true}")
+ private boolean enabled;
+
+ /** 入站 Filter / 出站切面调用:一次请求(或一次出站调用)结束时记一笔 */
+ public void record(String uri, String method, String direction, long costMs, boolean error, boolean loadTest) {
+ if (!enabled) {
+ return;
+ }
+ Counter c = counters.computeIfAbsent(new Dim(uri, method, direction, loadTest), k -> new Counter());
+ c.cnt.incrementAndGet();
+ c.cost.addAndGet(costMs);
+ if (error) {
+ c.err.incrementAndGet();
+ }
+ }
+
+ /** 快照窗口(ms),对齐方案 60s */
+ @Scheduled(fixedDelayString = "${api.metrics.flush-interval-ms:60000}")
+ public void flush() {
+ if (!enabled || counters.isEmpty()) {
+ return;
+ }
+ LocalDateTime sliceTime = LocalDateTime.now();
+ List rows = new ArrayList<>();
+ List