邮件API

This commit is contained in:
2025-11-08 15:27:21 +08:00
parent ba1bd57597
commit 50efda3957
15 changed files with 429 additions and 19 deletions

28
pom.xml
View File

@@ -79,12 +79,6 @@
<version>3.0.4</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>6.5.4</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
@@ -134,10 +128,6 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
@@ -150,12 +140,30 @@
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>6.5.4</version>
</dependency>
<!-- Actuator 核心依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>3.5.5</version>
</dependency>
<!-- WebSocket支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>

View File

@@ -0,0 +1,11 @@
package com.mini.capi.api.biz;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/bizApi")
public class bizController {
}

View File

@@ -1,4 +1,4 @@
package com.mini.capi.sys;
package com.mini.capi.api;
public class startApp {
}

View File

@@ -0,0 +1,64 @@
package com.mini.capi.api.sys;
import com.mini.capi.config.component.*;
import com.mini.capi.model.ApiResult;
import com.mini.capi.model.info.*;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/sysApi")
public class sysController {
@Resource
private AppRuntimeInfo appRuntimeInfo;
@Resource
private AppPathInfo appPathInfo;
@Resource
private AppProcessInfo appProcessInfo;
@Resource
private AppNetworkInfo appNetworkInfo;
/**
* 运行信息
*/
@GetMapping("getRunInfo")
public ApiResult<?> getRunInfo() {
try {
// 用构建者模式链式创建对象,避免重复的参数传递和构造函数调用
RunInfo runInfo = RunInfo.builder()
.runtimeInfo(RuntimeInfo.builder()
.appName(appRuntimeInfo.getAppName())
.serverPort(appRuntimeInfo.getServerPort())
.activeProfiles(appRuntimeInfo.getActiveProfiles())
.springBootVersion(appRuntimeInfo.getSpringBootVersion())
.jdkVersion(appRuntimeInfo.getJdkVersion())
.build())
.pathInfo(PathInfo.builder()
.workDir(appPathInfo.getWorkDir())
.jarDir(appPathInfo.getJarDir())
.resourceDir(appPathInfo.getResourceDir())
.build())
.processInfo(ProcessInfo.builder()
.pid(appProcessInfo.getPid())
.uptime(appProcessInfo.getUptime())
.usedHeapMemory(appProcessInfo.getUsedHeapMemory())
.maxHeapMemory(appProcessInfo.getMaxHeapMemory())
.jvmArgs(appProcessInfo.getJvmArgs())
.build())
.networkInfo(NetworkInfo.builder()
.hostName(appNetworkInfo.getHostName())
.localIp(appNetworkInfo.getLocalIp())
.allIps(appNetworkInfo.getAllIps())
.build())
.build();
return ApiResult.success(runInfo);
} catch (Exception e) {
return ApiResult.error(101, e.getMessage());
}
}
}

View File

@@ -0,0 +1,48 @@
package com.mini.capi.config.component;
import org.springframework.stereotype.Component;
import java.net.InetAddress;
import java.net.Inet4Address;
import java.net.NetworkInterface;
import java.net.UnknownHostException;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
@Component
public class AppNetworkInfo {
// 获取主机名
public String getHostName() throws UnknownHostException {
return InetAddress.getLocalHost().getHostName();
}
// 获取本地 IP 地址(可能是内网 IP
public String getLocalIp() throws UnknownHostException {
return InetAddress.getLocalHost().getHostAddress();
}
// 获取所有有效 IPv4 地址过滤回环地址、IPv6支持多网卡
public List<String> getAllIps() throws SocketException {
List<String> ips = new ArrayList<>();
Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
while (interfaces.hasMoreElements()) {
NetworkInterface ni = interfaces.nextElement();
// 跳过虚拟网卡、禁用的网卡
if (ni.isLoopback() || !ni.isUp()) {
continue;
}
Enumeration<InetAddress> addresses = ni.getInetAddresses();
while (addresses.hasMoreElements()) {
InetAddress addr = addresses.nextElement();
// 只保留 IPv4 地址
if (addr instanceof Inet4Address) {
ips.add(addr.getHostAddress());
}
}
}
return ips;
}
}

View File

@@ -0,0 +1,31 @@
package com.mini.capi.config.component;
import org.springframework.stereotype.Component;
import java.io.File;
import java.net.URISyntaxException;
import java.net.URL;
@Component
public class AppPathInfo {
// 获取当前工作目录(执行启动命令的目录)
public String getWorkDir() {
return System.getProperty("user.dir");
}
// 获取 Jar 包所在目录(仅 Jar 运行时有效IDE 中返回 classes 目录父级)
public String getJarDir() throws URISyntaxException {
URL jarUrl = getClass().getProtectionDomain().getCodeSource().getLocation();
File jarFile = new File(jarUrl.toURI());
return jarFile.getParentFile().getAbsolutePath();
}
// 获取类路径下的资源目录IDE 中是 target/classesJar 中是 jar 内部路径)
public String getResourceDir() {
URL resourceUrl = getClass().getClassLoader().getResource("");
if (resourceUrl != null) {
return new File(resourceUrl.getPath()).getAbsolutePath();
}
return "unknown-resource-dir";
}
}

View File

@@ -0,0 +1,42 @@
package com.mini.capi.config.component;
import org.springframework.stereotype.Component;
import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
import java.util.List;
@Component
public class AppProcessInfo {
// 获取进程 PID兼容 JDK 8+
public long getPid() {
RuntimeMXBean mxBean = ManagementFactory.getRuntimeMXBean();
String name = mxBean.getName(); // 格式PID@主机名
return Long.parseLong(name.split("@")[0]);
}
// 获取 JVM 启动参数(如 -Xms、-Xmx、-D 系统属性)
public List<String> getJvmArgs() {
RuntimeMXBean mxBean = ManagementFactory.getRuntimeMXBean();
return mxBean.getInputArguments();
}
// 获取已使用堆内存单位MB
public long getUsedHeapMemory() {
long total = Runtime.getRuntime().totalMemory() / 1024 / 1024;
long free = Runtime.getRuntime().freeMemory() / 1024 / 1024;
return total - free;
}
// 获取最大堆内存单位MB
public long getMaxHeapMemory() {
return Runtime.getRuntime().maxMemory() / 1024 / 1024;
}
// 获取程序运行时间(单位:秒)
public long getUptime() {
RuntimeMXBean mxBean = ManagementFactory.getRuntimeMXBean();
return mxBean.getUptime() / 1000;
}
}

View File

@@ -0,0 +1,54 @@
package com.mini.capi.config.component;
import lombok.Getter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringBootVersion;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
@Component
public class AppRuntimeInfo implements EnvironmentAware{
// 应用名(默认 unknown-app
@Getter
@Value("${spring.application.name:unknown-app}")
private String appName;
// 服务端口(默认 8080支持随机端口
@Getter
@Value("${server.port:8080}")
private Integer serverPort;
// 激活的环境(默认 default
@Getter
@Value("${spring.profiles.active:default}")
private String activeProfile;
// JDK 版本
@Getter
@Value("${java.version:unknown}")
private String jdkVersion;
// Spring 环境上下文(用于灵活获取配置)
private Environment environment;
// 获取 Spring Boot 版本
public String getSpringBootVersion() {
return SpringBootVersion.getVersion();
}
// 获取所有激活的环境(支持多环境,如 dev,test
public String[] getActiveProfiles() {
return environment.getActiveProfiles();
}
// 灵活获取配置项(支持默认值)
public String getConfigValue(String key, String defaultValue) {
return environment.getProperty(key, defaultValue);
}
// 实现 EnvironmentAware 接口,注入 Environment
@Override
public void setEnvironment(Environment environment) {
this.environment = environment;
}
}

View File

@@ -0,0 +1,24 @@
package com.mini.capi.model.info;
import lombok.Builder;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
@Data
@Builder // 生成构建者模式代码
public class NetworkInfo implements Serializable {
private String hostName;
private String localIp;
private List<String> allIps;
public NetworkInfo(String hostName, String localIp, List<String> allIps) {
this.hostName = hostName;
this.localIp = localIp;
this.allIps = allIps;
}
}

View File

@@ -0,0 +1,23 @@
package com.mini.capi.model.info;
import lombok.Builder;
import lombok.Data;
import java.io.Serializable;
@Data
@Builder // 生成构建者模式代码
public class PathInfo implements Serializable {
private String workDir;
private String jarDir;
private String resourceDir;
public PathInfo(String workDir, String jarDir, String resourceDir) {
this.workDir = workDir;
this.jarDir = jarDir;
this.resourceDir = resourceDir;
}
}

View File

@@ -0,0 +1,30 @@
package com.mini.capi.model.info;
import lombok.Builder;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
@Data
@Builder // 生成构建者模式代码
public class ProcessInfo implements Serializable {
private long pid;
private Long uptime;
private Long usedHeapMemory;
private Long maxHeapMemory;
private List<String> jvmArgs;
public ProcessInfo(long pid, Long uptime, Long usedHeapMemory, Long maxHeapMemory, List<String> jvmArgs) {
this.pid = pid;
this.uptime = uptime;
this.usedHeapMemory = usedHeapMemory;
this.maxHeapMemory = maxHeapMemory;
this.jvmArgs = jvmArgs;
}
}

View File

@@ -0,0 +1,23 @@
package com.mini.capi.model.info;
import lombok.Builder;
import lombok.Data;
import java.io.Serializable;
@Data
@Builder // 生成构建者模式代码
public class RunInfo implements Serializable {
private RuntimeInfo runtimeInfo;
private PathInfo pathInfo;
private ProcessInfo processInfo;
private NetworkInfo networkInfo;
public RunInfo(RuntimeInfo runtimeInfo, PathInfo pathInfo, ProcessInfo processInfo, NetworkInfo networkInfo) {
this.runtimeInfo = runtimeInfo;
this.pathInfo = pathInfo;
this.processInfo = processInfo;
this.networkInfo = networkInfo;
}
}

View File

@@ -0,0 +1,30 @@
package com.mini.capi.model.info;
import lombok.Builder;
import lombok.Data;
import java.io.Serializable;
@Data
@Builder // 生成构建者模式代码
public class RuntimeInfo implements Serializable {
private String appName;
private Integer serverPort;
private String[] activeProfiles; // 支持多环境
private String springBootVersion;
private String jdkVersion;
public RuntimeInfo(String appName, Integer serverPort, String[] activeProfiles, String springBootVersion, String jdkVersion) {
this.appName = appName;
this.serverPort = serverPort;
this.activeProfiles = activeProfiles;
this.springBootVersion = springBootVersion;
this.jdkVersion = jdkVersion;
}
}

View File

@@ -1,6 +0,0 @@
package com.mini.capi.sys;
public class ApiController {
}

View File

@@ -29,5 +29,33 @@ springdoc.swagger-ui.disable-swagger-default-url=true
# ========================================
springdoc.api-docs.path=/v3/api-docs
springdoc.api-docs.groups.enabled=true
springdoc.packages-to-scan=com.mini.capi.sys
springdoc.paths-to-match=/**
springdoc.packages-to-scan=com.mini.capi.api
springdoc.paths-to-match=/**
# ===================== Actuator ???? =====================
management.endpoints.web.exposure.include=health,info,metrics,process
management.endpoints.web.exposure.exclude=shutdown
# ??????
management.endpoint.health.show-details=always
management.endpoint.health.show-components=always
management.endpoint.health.status.aggregation-strategy=strict
# ?????????????
management.endpoints.web.base-path=/monitor
# CORS ?????????????
management.endpoints.web.cors.allowed-origins=*
management.endpoints.web.cors.allowed-methods=GET,POST,OPTIONS
management.endpoints.web.cors.allowed-headers=*
management.endpoints.web.cors.max-age=3600
# ????????????
logging.level.root=INFO
logging.level.com.yourpackage=DEBUG
# ===================== ???????????? =====================
# ???????
management.endpoint.env.show-values=when_authorized
# ??????
management.endpoint.health.cache.time-to-live=3000ms