项目初始化

This commit is contained in:
2025-08-23 13:27:32 +08:00
commit c555beca58
16 changed files with 1181 additions and 0 deletions

View File

@@ -0,0 +1,15 @@
package com.mini.capi;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.mini.capi.biz.mapper")
public class CApiApplication {
public static void main(String[] args) {
SpringApplication.run(CApiApplication.class, args);
}
}

View File

@@ -0,0 +1,84 @@
package com.mini.capi.model;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
@Data
public class ApiResult<T> implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 状态码
*/
private int code;
/**
* 提示信息
*/
private String message;
/**
* 返回数据
*/
private T result;
/* ---------------- 构造方法 ---------------- */
public ApiResult() {
}
public ApiResult(int code, String message, T data) {
this.code = code;
this.message = message;
this.result = data;
}
/* ---------------- 静态工厂方法 ---------------- */
/**
* 成功,仅返回状态码
*/
public static <T> ApiResult<T> success() {
return new ApiResult<>(ResultCodeEnum.SUCCESS.getCode(), ResultCodeEnum.SUCCESS.getMessage(), null);
}
/**
* 成功,返回数据
*/
public static <T> ApiResult<T> success(T data) {
return new ApiResult<>(ResultCodeEnum.SUCCESS.getCode(), ResultCodeEnum.SUCCESS.getMessage(), data);
}
/**
* 成功,自定义提示
*/
public static <T> ApiResult<T> success(String message, T data) {
return new ApiResult<>(ResultCodeEnum.SUCCESS.getCode(), message, data);
}
/**
* 失败,默认提示
*/
public static <T> ApiResult<T> error() {
return new ApiResult<>(ResultCodeEnum.FAIL.getCode(), ResultCodeEnum.FAIL.getMessage(), null);
}
/**
* 失败,自定义状态码与提示
*/
public static <T> ApiResult<T> error(int code, String message) {
return new ApiResult<>(code, message, null);
}
/**
* 失败,使用枚举
*/
public static <T> ApiResult<T> error(ResultCodeEnum codeEnum) {
return new ApiResult<>(codeEnum.getCode(), codeEnum.getMessage(), null);
}
}

View File

@@ -0,0 +1,42 @@
package com.mini.capi.model;
import lombok.Data;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@Data
public class PageResult<T> implements Serializable {
private final int total; // 总记录数
private final int pages; // 总页数
private final int pageNum; // 当前页
private final int pageSize; // 每页条数
private final List<T> data; // 当前页数据
private PageResult(int total, int pages, int pageNum, int pageSize, List<T> data) {
this.total = total;
this.pages = pages;
this.pageNum = pageNum;
this.pageSize = pageSize;
this.data = data;
}
public static <T> PageResult<T> of(List<T> listData, int pageNum, int pageSize) {
if (listData == null || listData.isEmpty() || pageNum <= 0 || pageSize <= 0) {
return new PageResult<>(0, 0, pageNum, pageSize, Collections.emptyList());
}
int total = listData.size();
int pages = (int) Math.ceil((double) total / pageSize);
int from = (pageNum - 1) * pageSize;
if (from >= total) {
return new PageResult<>(total, pages, pageNum, pageSize, Collections.emptyList());
}
int to = Math.min(from + pageSize, total);
List<T> list = new ArrayList<>(listData.subList(from, to));
return new PageResult<>(total, pages, pageNum, pageSize, list);
}
}

View File

@@ -0,0 +1,22 @@
package com.mini.capi.model;
import lombok.Getter;
@Getter
public enum ResultCodeEnum {
SUCCESS(200, "Success"),
FAIL(500, "Fail"),
BAD_REQUEST(400, "Bad Request"),
UNAUTHORIZED(401, "Unauthorized"),
NOT_FOUND(404, "Not Found");
private final int code;
private final String message;
ResultCodeEnum(int code, String message) {
this.code = code;
this.message = message;
}
}

View File

@@ -0,0 +1,53 @@
package com.mini.capi.mybatis;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import java.util.Collections;
public class demo {
public static void main(String[] args) {
FastAutoGenerator.create("jdbc:mysql://192.168.31.189:33069/work?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC", "dream", "info_dream")
.globalConfig(builder -> {
builder.author("gaoxq")
.outputDir(System.getProperty("user.dir") + "/src/main/java")
.disableOpenDir();
})
.packageConfig(builder -> {
builder.parent("com.mini.capi")
.moduleName("biz")
.entity("domain")
.mapper("mapper")
.xml("mapper.xml")
.service("service")
.serviceImpl("service.impl")
.controller("controller")
.pathInfo(Collections.singletonMap(OutputFile.xml, System.getProperty("user.dir") + "/src/main/resources/mapper"));
})
.strategyConfig(builder -> {
builder.addInclude("biz_combined_summary")
.addTablePrefix("biz_")
.entityBuilder()
.enableLombok()
.naming(NamingStrategy.underline_to_camel)
.columnNaming(NamingStrategy.underline_to_camel)
.idType(IdType.AUTO)
.enableTableFieldAnnotation()
.enableFileOverride()
.controllerBuilder()
.enableRestStyle()
.serviceBuilder()
.formatServiceFileName("%sService")
.formatServiceImplFileName("%sServiceImpl")
.mapperBuilder()
.enableBaseResultMap()
.enableBaseColumnList();
})
.templateEngine(new FreemarkerTemplateEngine())
.execute();
}
}

View File

@@ -0,0 +1,31 @@
package com.mini.capi.sys.controller;
import jakarta.servlet.http.HttpServletRequest; // 注意这里
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.HashMap;
import java.util.Map;
@Controller
public class CustomErrorController implements ErrorController {
@RequestMapping("/error")
public ResponseEntity<Map<String, Object>> handleError(HttpServletRequest request) {
HttpStatus status = getStatus(request);
Map<String, Object> body = new HashMap<>();
body.put("status", status.value());
body.put("error", status.getReasonPhrase());
body.put("message", "访问的资源不存在");
body.put("path", request.getRequestURI());
return ResponseEntity.status(status).body(body);
}
private HttpStatus getStatus(HttpServletRequest request) {
Integer code = (Integer) request.getAttribute("jakarta.servlet.error.status_code");
return (code != null) ? HttpStatus.valueOf(code) : HttpStatus.INTERNAL_SERVER_ERROR;
}
}

View File

@@ -0,0 +1,119 @@
package com.mini.capi.sys.controller;
import com.mini.capi.model.ApiResult;
import com.mini.capi.utils.HostRuntime;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.time.Instant;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;
@RestController
@RequestMapping("/Sys/hosts")
public class sysController {
public static class SnapshotDTO {
public String hostName;
public String timestamp;
public String cpuUsage;
public String memTotal;
public String memFree;
public String memUsed;
public String memUsage;
public String swapTotal;
public String swapUsed;
public List<DiskDTO> disks;
public String netRxBytes;
public String netTxBytes;
public double load1;
public int processCount;
public String uptimeSec;
public static SnapshotDTO from(HostRuntime.Snapshot s) {
SnapshotDTO dto = new SnapshotDTO();
dto.hostName = s.hostName;
dto.timestamp = Instant.ofEpochMilli(s.timestamp)
.atZone(ZoneId.systemDefault())
.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
dto.cpuUsage = String.format("%.2f %%", s.cpuUsage * 100);
dto.memTotal = humanBytes(s.memTotal);
dto.memFree = humanBytes(s.memFree);
dto.memUsed = humanBytes(s.memUsed);
dto.memUsage = String.format("%.2f %%", s.memUsage * 100);
dto.swapTotal = humanBytes(s.swapTotal);
dto.swapUsed = humanBytes(s.swapUsed);
dto.disks = s.disks.stream()
.map(d -> new DiskDTO(d.path,
humanBytes(d.total),
humanBytes(d.free),
humanBytes(d.used),
String.format("%.2f %%", d.usage * 100)))
.collect(Collectors.toList());
dto.netRxBytes = humanBytes(s.netRxBytes);
dto.netTxBytes = humanBytes(s.netTxBytes);
dto.load1 = s.load1;
dto.processCount = s.processCount;
dto.uptimeSec = uptimeToHuman(s.uptimeSec);
return dto;
}
private static String humanBytes(long bytes) {
if (bytes < 1024) return bytes + " B";
int exp = (int) (Math.log(bytes) / Math.log(1024));
String pre = "KMGTPE".charAt(exp - 1) + "iB";
return String.format("%.1f %s", bytes / Math.pow(1024, exp), pre);
}
private static String uptimeToHuman(long sec) {
long h = sec / 3600;
long m = (sec % 3600) / 60;
long s = sec % 60;
if (h > 0) return String.format("%d 时 %d 分 %d 秒", h, m, s);
if (m > 0) return String.format("%d 分 %d 秒", m, s);
return String.format("%d 秒", s);
}
public static class DiskDTO {
public String path;
public String total;
public String free;
public String used;
public String usage;
public DiskDTO(String path, String total, String free, String used, String usage) {
this.path = path;
this.total = total;
this.free = free;
this.used = used;
this.usage = usage;
}
}
}
private static final int MAX_SIZE = 100;
@GetMapping("/getApiInfo")
public ApiResult<List<SnapshotDTO>> getApiInfo() {
// 1. 新建一个一次性 List
List<HostRuntime.Snapshot> snapshots =
Collections.synchronizedList(new LinkedList<>());
// 2. 采集并加入
HostRuntime.Snapshot snap = HostRuntime.collect();
snapshots.add(snap);
// 3. 如果只想保留一条,直接清空多余
while (snapshots.size() > MAX_SIZE) {
((LinkedList<HostRuntime.Snapshot>) snapshots).removeFirst();
}
return ApiResult.success(Collections.singletonList(SnapshotDTO.from(snap)));
}
}

View File

@@ -0,0 +1,183 @@
package com.mini.capi.utils;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.lang.management.OperatingSystemMXBean;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
public class HostRuntime {
/**
* 统一的数据载体
*/
public static class Snapshot {
public String hostName; // 主机名
public long timestamp; // 采集时间戳(毫秒)
public double cpuUsage; // CPU 使用率 0~1
public long memTotal; // 内存总量Byte
public long memFree; // 空闲内存Byte
public long memUsed; // 已用内存Byte
public double memUsage; // 内存使用率 0~1
public long swapTotal; // Swap 总量
public long swapUsed; // Swap 已用
public List<DiskUsage> disks; // 各挂载点磁盘
public long netRxBytes; // 累计接收字节
public long netTxBytes; // 累计发送字节
public double load1; // 1 分钟平均负载
public int processCount; // 进程数
public long uptimeSec; // 开机时间(秒)
@Override
public String toString() {
return "Snapshot{" +
"hostName='" + hostName + '\'' +
", ts=" + Instant.ofEpochMilli(timestamp) +
", cpu=" + String.format("%.2f", cpuUsage * 100) + "%" +
", mem=" + String.format("%.2f", memUsage * 100) + "%" +
", load=" + load1 +
", uptime=" + uptimeSec + "s}";
}
}
public static class DiskUsage {
public String path;
public long total;
public long free;
public long used;
public double usage;
public DiskUsage(String path, long total, long free, long used, double usage) {
this.path = path;
this.total = total;
this.free = free;
this.used = used;
this.usage = usage;
}
}
/**
* 采集一次主机状态
*/
public static Snapshot collect() {
Snapshot s = new Snapshot();
s.timestamp = System.currentTimeMillis();
try {
s.hostName = InetAddress.getLocalHost().getHostName();
} catch (UnknownHostException e) {
s.hostName = "unknown";
}
OperatingSystemMXBean os = ManagementFactory.getOperatingSystemMXBean();
s.load1 = os.getSystemLoadAverage();
s.uptimeSec = getUptimeSec();
s.processCount = getProcessCount();
/* ------ CPU ------ */
s.cpuUsage = getCpuUsage();
/* ------ Memory ------ */
long[] mem = getMemInfo();
s.memTotal = mem[0];
s.memFree = mem[1];
s.memUsed = s.memTotal - s.memFree;
s.memUsage = s.memTotal == 0 ? 0 : (double) s.memUsed / s.memTotal;
s.swapTotal = mem[2];
s.swapUsed = mem[3];
/* ------ Disk ------ */
s.disks = new ArrayList<>();
java.io.File[] roots = java.io.File.listRoots();
for (java.io.File f : roots) {
long total = f.getTotalSpace();
long free = f.getFreeSpace();
long used = total - free;
double usage = total == 0 ? 0 : (double) used / total;
s.disks.add(new DiskUsage(f.getPath(), total, free, used, usage));
}
/* ------ Network ------ */
long[] net = getNetInfo();
s.netRxBytes = net[0];
s.netTxBytes = net[1];
return s;
}
/* ----------------- 私有工具方法 ----------------- */
private static double getCpuUsage() {
// 利用 com.sun.management.OperatingSystemMXBeanJDK 自带)
com.sun.management.OperatingSystemMXBean sunOs =
(com.sun.management.OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean();
return sunOs.getProcessCpuLoad(); // 进程级
// 若想系统级sunOs.getSystemCpuLoad();
}
private static long getUptimeSec() {
// 通过 JVM 启动时间计算
return ManagementFactory.getRuntimeMXBean().getUptime() / 1000;
}
private static int getProcessCount() {
// 简单实现:线程数,不够精确;若要精确需解析 /proc
return ManagementFactory.getOperatingSystemMXBean().getAvailableProcessors(); // 仅作示例
}
private static long[] getMemInfo() {
long total = 0, free = 0, swapTotal = 0, swapUsed = 0;
try (BufferedReader br = new BufferedReader(new FileReader("/proc/meminfo"))) {
String line;
while ((line = br.readLine()) != null) {
if (line.startsWith("MemTotal:")) total = parseMemLine(line);
if (line.startsWith("MemAvailable:")) free = parseMemLine(line);
if (line.startsWith("SwapTotal:")) swapTotal = parseMemLine(line);
if (line.startsWith("SwapFree:")) swapUsed = swapTotal - parseMemLine(line);
}
} catch (IOException | NumberFormatException ignore) {
// fallback to OperatingSystemMXBean
OperatingSystemMXBean os = ManagementFactory.getOperatingSystemMXBean();
if (os instanceof com.sun.management.OperatingSystemMXBean) {
com.sun.management.OperatingSystemMXBean sunOs =
(com.sun.management.OperatingSystemMXBean) os;
total = sunOs.getTotalMemorySize();
free = sunOs.getFreeMemorySize();
}
}
return new long[]{total, free, swapTotal, swapUsed};
}
private static long parseMemLine(String line) {
String[] parts = line.split("\\s+");
return Long.parseLong(parts[1]) * 1024; // kB -> Byte
}
private static long[] getNetInfo() {
// 读取 /proc/net/dev 第一行即可拿到累计字节
long rx = 0, tx = 0;
try (BufferedReader br = new BufferedReader(new FileReader("/proc/net/dev"))) {
br.readLine();
br.readLine(); // 跳过表头
String line;
while ((line = br.readLine()) != null) {
line = line.trim();
if (line.startsWith("lo:")) continue; // 忽略回环
String[] parts = line.split("\\s+");
if (parts.length >= 10) {
rx += Long.parseLong(parts[1]);
tx += Long.parseLong(parts[9]);
}
}
} catch (IOException | NumberFormatException ignore) {
// 非 Linux 平台直接返回 0
}
return new long[]{rx, tx};
}
}