commit c555beca58c6cbff3741935e0aaf75dc907486d2 Author: gaoxq <376340421@qq.com> Date: Sat Aug 23 13:27:32 2025 +0800 项目初始化 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..3b41682 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +/mvnw text eol=lf +*.cmd text eol=crlf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..667aaef --- /dev/null +++ b/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..12fbe1e --- /dev/null +++ b/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,19 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +wrapperVersion=3.3.2 +distributionType=only-script +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.11/apache-maven-3.9.11-bin.zip diff --git a/mvnw b/mvnw new file mode 100644 index 0000000..19529dd --- /dev/null +++ b/mvnw @@ -0,0 +1,259 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Apache Maven Wrapper startup batch script, version 3.3.2 +# +# Optional ENV vars +# ----------------- +# JAVA_HOME - location of a JDK home dir, required when download maven via java source +# MVNW_REPOURL - repo url base for downloading maven distribution +# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output +# ---------------------------------------------------------------------------- + +set -euf +[ "${MVNW_VERBOSE-}" != debug ] || set -x + +# OS specific support. +native_path() { printf %s\\n "$1"; } +case "$(uname)" in +CYGWIN* | MINGW*) + [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")" + native_path() { cygpath --path --windows "$1"; } + ;; +esac + +# set JAVACMD and JAVACCMD +set_java_home() { + # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched + if [ -n "${JAVA_HOME-}" ]; then + if [ -x "$JAVA_HOME/jre/sh/java" ]; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACCMD="$JAVA_HOME/jre/sh/javac" + else + JAVACMD="$JAVA_HOME/bin/java" + JAVACCMD="$JAVA_HOME/bin/javac" + + if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then + echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2 + echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2 + return 1 + fi + fi + else + JAVACMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v java + )" || : + JAVACCMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v javac + )" || : + + if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then + echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2 + return 1 + fi + fi +} + +# hash string like Java String::hashCode +hash_string() { + str="${1:-}" h=0 + while [ -n "$str" ]; do + char="${str%"${str#?}"}" + h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296)) + str="${str#?}" + done + printf %x\\n $h +} + +verbose() { :; } +[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; } + +die() { + printf %s\\n "$1" >&2 + exit 1 +} + +trim() { + # MWRAPPER-139: + # Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds. + # Needed for removing poorly interpreted newline sequences when running in more + # exotic environments such as mingw bash on Windows. + printf "%s" "${1}" | tr -d '[:space:]' +} + +# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties +while IFS="=" read -r key value; do + case "${key-}" in + distributionUrl) distributionUrl=$(trim "${value-}") ;; + distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;; + esac +done <"${0%/*}/.mvn/wrapper/maven-wrapper.properties" +[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in ${0%/*}/.mvn/wrapper/maven-wrapper.properties" + +case "${distributionUrl##*/}" in +maven-mvnd-*bin.*) + MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ + case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in + *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;; + :Darwin*x86_64) distributionPlatform=darwin-amd64 ;; + :Darwin*arm64) distributionPlatform=darwin-aarch64 ;; + :Linux*x86_64*) distributionPlatform=linux-amd64 ;; + *) + echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2 + distributionPlatform=linux-amd64 + ;; + esac + distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip" + ;; +maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;; +*) MVN_CMD="mvn${0##*/mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;; +esac + +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}" +distributionUrlName="${distributionUrl##*/}" +distributionUrlNameMain="${distributionUrlName%.*}" +distributionUrlNameMain="${distributionUrlNameMain%-bin}" +MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}" +MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")" + +exec_maven() { + unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || : + exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD" +} + +if [ -d "$MAVEN_HOME" ]; then + verbose "found existing MAVEN_HOME at $MAVEN_HOME" + exec_maven "$@" +fi + +case "${distributionUrl-}" in +*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;; +*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;; +esac + +# prepare tmp dir +if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then + clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; } + trap clean HUP INT TERM EXIT +else + die "cannot create temp dir" +fi + +mkdir -p -- "${MAVEN_HOME%/*}" + +# Download and Install Apache Maven +verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +verbose "Downloading from: $distributionUrl" +verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +# select .zip or .tar.gz +if ! command -v unzip >/dev/null; then + distributionUrl="${distributionUrl%.zip}.tar.gz" + distributionUrlName="${distributionUrl##*/}" +fi + +# verbose opt +__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR='' +[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v + +# normalize http auth +case "${MVNW_PASSWORD:+has-password}" in +'') MVNW_USERNAME='' MVNW_PASSWORD='' ;; +has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;; +esac + +if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then + verbose "Found wget ... using wget" + wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl" +elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then + verbose "Found curl ... using curl" + curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl" +elif set_java_home; then + verbose "Falling back to use Java to download" + javaSource="$TMP_DOWNLOAD_DIR/Downloader.java" + targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName" + cat >"$javaSource" <<-END + public class Downloader extends java.net.Authenticator + { + protected java.net.PasswordAuthentication getPasswordAuthentication() + { + return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() ); + } + public static void main( String[] args ) throws Exception + { + setDefault( new Downloader() ); + java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() ); + } + } + END + # For Cygwin/MinGW, switch paths to Windows format before running javac and java + verbose " - Compiling Downloader.java ..." + "$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java" + verbose " - Running Downloader.java ..." + "$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")" +fi + +# If specified, validate the SHA-256 sum of the Maven distribution zip file +if [ -n "${distributionSha256Sum-}" ]; then + distributionSha256Result=false + if [ "$MVN_CMD" = mvnd.sh ]; then + echo "Checksum validation is not supported for maven-mvnd." >&2 + echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + elif command -v sha256sum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c >/dev/null 2>&1; then + distributionSha256Result=true + fi + elif command -v shasum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then + distributionSha256Result=true + fi + else + echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2 + echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + fi + if [ $distributionSha256Result = false ]; then + echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2 + echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2 + exit 1 + fi +fi + +# unzip and move +if command -v unzip >/dev/null; then + unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip" +else + tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar" +fi +printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/mvnw.url" +mv -- "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME" + +clean || : +exec_maven "$@" diff --git a/mvnw.cmd b/mvnw.cmd new file mode 100644 index 0000000..249bdf3 --- /dev/null +++ b/mvnw.cmd @@ -0,0 +1,149 @@ +<# : batch portion +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Apache Maven Wrapper startup batch script, version 3.3.2 +@REM +@REM Optional ENV vars +@REM MVNW_REPOURL - repo url base for downloading maven distribution +@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output +@REM ---------------------------------------------------------------------------- + +@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0) +@SET __MVNW_CMD__= +@SET __MVNW_ERROR__= +@SET __MVNW_PSMODULEP_SAVE=%PSModulePath% +@SET PSModulePath= +@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @( + IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B) +) +@SET PSModulePath=%__MVNW_PSMODULEP_SAVE% +@SET __MVNW_PSMODULEP_SAVE= +@SET __MVNW_ARG0_NAME__= +@SET MVNW_USERNAME= +@SET MVNW_PASSWORD= +@IF NOT "%__MVNW_CMD__%"=="" (%__MVNW_CMD__% %*) +@echo Cannot start maven from wrapper >&2 && exit /b 1 +@GOTO :EOF +: end batch / begin powershell #> + +$ErrorActionPreference = "Stop" +if ($env:MVNW_VERBOSE -eq "true") { + $VerbosePreference = "Continue" +} + +# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties +$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl +if (!$distributionUrl) { + Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" +} + +switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) { + "maven-mvnd-*" { + $USE_MVND = $true + $distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip" + $MVN_CMD = "mvnd.cmd" + break + } + default { + $USE_MVND = $false + $MVN_CMD = $script -replace '^mvnw','mvn' + break + } +} + +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +if ($env:MVNW_REPOURL) { + $MVNW_REPO_PATTERN = if ($USE_MVND) { "/org/apache/maven/" } else { "/maven/mvnd/" } + $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace '^.*'+$MVNW_REPO_PATTERN,'')" +} +$distributionUrlName = $distributionUrl -replace '^.*/','' +$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$','' +$MAVEN_HOME_PARENT = "$HOME/.m2/wrapper/dists/$distributionUrlNameMain" +if ($env:MAVEN_USER_HOME) { + $MAVEN_HOME_PARENT = "$env:MAVEN_USER_HOME/wrapper/dists/$distributionUrlNameMain" +} +$MAVEN_HOME_NAME = ([System.Security.Cryptography.MD5]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join '' +$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME" + +if (Test-Path -Path "$MAVEN_HOME" -PathType Container) { + Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME" + Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" + exit $? +} + +if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) { + Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl" +} + +# prepare tmp dir +$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile +$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir" +$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null +trap { + if ($TMP_DOWNLOAD_DIR.Exists) { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } + } +} + +New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null + +# Download and Install Apache Maven +Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +Write-Verbose "Downloading from: $distributionUrl" +Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +$webclient = New-Object System.Net.WebClient +if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) { + $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD) +} +[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 +$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null + +# If specified, validate the SHA-256 sum of the Maven distribution zip file +$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum +if ($distributionSha256Sum) { + if ($USE_MVND) { + Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." + } + Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash + if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) { + Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property." + } +} + +# unzip and move +Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null +Rename-Item -Path "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" -NewName $MAVEN_HOME_NAME | Out-Null +try { + Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null +} catch { + if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) { + Write-Error "fail to move MAVEN_HOME" + } +} finally { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } +} + +Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..170b0bc --- /dev/null +++ b/pom.xml @@ -0,0 +1,149 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 3.5.5 + + + com.mini + cApi + 0.0.1-SNAPSHOT + cApi + cApi + + + + + + + + + + + + + + + 17 + + + + + commons-net + commons-net + 3.11.0 + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + 3.0.4 + + + org.springframework.security + spring-security-core + + + + com.alibaba + fastjson + 2.0.52 + + + + com.baomidou + mybatis-plus-boot-starter + 3.5.7 + + + com.baomidou + mybatis-plus-extension + 3.5.7 + + + com.baomidou + mybatis-plus-generator + 3.5.7 + + + org.apache.velocity + velocity-engine-core + 2.3 + + + org.apache.commons + commons-lang3 + + + + + org.freemarker + freemarker + + + cn.hutool + hutool-all + 5.8.31 + + + org.springframework.boot + spring-boot-starter-data-jdbc + + + org.springframework.boot + spring-boot-starter-jdbc + + + org.springframework.boot + spring-boot-starter-web + + + + com.mysql + mysql-connector-j + runtime + + + org.projectlombok + lombok + true + + + org.springframework.boot + spring-boot-starter-test + test + + + + + cApi + + + org.apache.maven.plugins + maven-compiler-plugin + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + + + org.projectlombok + lombok + + + + + + + + diff --git a/src/main/java/com/mini/capi/CApiApplication.java b/src/main/java/com/mini/capi/CApiApplication.java new file mode 100644 index 0000000..232d363 --- /dev/null +++ b/src/main/java/com/mini/capi/CApiApplication.java @@ -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); + } + +} diff --git a/src/main/java/com/mini/capi/model/ApiResult.java b/src/main/java/com/mini/capi/model/ApiResult.java new file mode 100644 index 0000000..c50cf55 --- /dev/null +++ b/src/main/java/com/mini/capi/model/ApiResult.java @@ -0,0 +1,84 @@ +package com.mini.capi.model; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +@Data +public class ApiResult 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 ApiResult success() { + return new ApiResult<>(ResultCodeEnum.SUCCESS.getCode(), ResultCodeEnum.SUCCESS.getMessage(), null); + } + + /** + * 成功,返回数据 + */ + public static ApiResult success(T data) { + return new ApiResult<>(ResultCodeEnum.SUCCESS.getCode(), ResultCodeEnum.SUCCESS.getMessage(), data); + } + + /** + * 成功,自定义提示 + */ + public static ApiResult success(String message, T data) { + return new ApiResult<>(ResultCodeEnum.SUCCESS.getCode(), message, data); + } + + /** + * 失败,默认提示 + */ + public static ApiResult error() { + return new ApiResult<>(ResultCodeEnum.FAIL.getCode(), ResultCodeEnum.FAIL.getMessage(), null); + } + + /** + * 失败,自定义状态码与提示 + */ + public static ApiResult error(int code, String message) { + return new ApiResult<>(code, message, null); + } + + /** + * 失败,使用枚举 + */ + public static ApiResult error(ResultCodeEnum codeEnum) { + return new ApiResult<>(codeEnum.getCode(), codeEnum.getMessage(), null); + } + +} diff --git a/src/main/java/com/mini/capi/model/PageResult.java b/src/main/java/com/mini/capi/model/PageResult.java new file mode 100644 index 0000000..a84dbd2 --- /dev/null +++ b/src/main/java/com/mini/capi/model/PageResult.java @@ -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 implements Serializable { + + private final int total; // 总记录数 + private final int pages; // 总页数 + private final int pageNum; // 当前页 + private final int pageSize; // 每页条数 + private final List data; // 当前页数据 + + private PageResult(int total, int pages, int pageNum, int pageSize, List data) { + this.total = total; + this.pages = pages; + this.pageNum = pageNum; + this.pageSize = pageSize; + this.data = data; + } + + public static PageResult of(List 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 list = new ArrayList<>(listData.subList(from, to)); + return new PageResult<>(total, pages, pageNum, pageSize, list); + } +} diff --git a/src/main/java/com/mini/capi/model/ResultCodeEnum.java b/src/main/java/com/mini/capi/model/ResultCodeEnum.java new file mode 100644 index 0000000..17d3192 --- /dev/null +++ b/src/main/java/com/mini/capi/model/ResultCodeEnum.java @@ -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; + } +} diff --git a/src/main/java/com/mini/capi/mybatis/demo.java b/src/main/java/com/mini/capi/mybatis/demo.java new file mode 100644 index 0000000..a9122a3 --- /dev/null +++ b/src/main/java/com/mini/capi/mybatis/demo.java @@ -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(); + } +} diff --git a/src/main/java/com/mini/capi/sys/controller/CustomErrorController.java b/src/main/java/com/mini/capi/sys/controller/CustomErrorController.java new file mode 100644 index 0000000..8d97c03 --- /dev/null +++ b/src/main/java/com/mini/capi/sys/controller/CustomErrorController.java @@ -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> handleError(HttpServletRequest request) { + HttpStatus status = getStatus(request); + Map 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; + } +} diff --git a/src/main/java/com/mini/capi/sys/controller/sysController.java b/src/main/java/com/mini/capi/sys/controller/sysController.java new file mode 100644 index 0000000..5e45def --- /dev/null +++ b/src/main/java/com/mini/capi/sys/controller/sysController.java @@ -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 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> getApiInfo() { + // 1. 新建一个一次性 List + List snapshots = + Collections.synchronizedList(new LinkedList<>()); + // 2. 采集并加入 + HostRuntime.Snapshot snap = HostRuntime.collect(); + snapshots.add(snap); + // 3. 如果只想保留一条,直接清空多余 + while (snapshots.size() > MAX_SIZE) { + ((LinkedList) snapshots).removeFirst(); + } + return ApiResult.success(Collections.singletonList(SnapshotDTO.from(snap))); + } + +} diff --git a/src/main/java/com/mini/capi/utils/HostRuntime.java b/src/main/java/com/mini/capi/utils/HostRuntime.java new file mode 100644 index 0000000..b52e64f --- /dev/null +++ b/src/main/java/com/mini/capi/utils/HostRuntime.java @@ -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 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.OperatingSystemMXBean(JDK 自带) + 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}; + } +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties new file mode 100644 index 0000000..84ad254 --- /dev/null +++ b/src/main/resources/application.properties @@ -0,0 +1,8 @@ +spring.application.name=cApi +server.port=31001 +server.servlet.context-path=/cApi + +spring.datasource.url=jdbc:mysql://192.168.31.189:33069/work?useSSL=false&serverTimezone=UTC&characterEncoding=utf8 +spring.datasource.username=dream +spring.datasource.password=info_dream +spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver diff --git a/src/test/java/com/mini/capi/CApiApplicationTests.java b/src/test/java/com/mini/capi/CApiApplicationTests.java new file mode 100644 index 0000000..3597c94 --- /dev/null +++ b/src/test/java/com/mini/capi/CApiApplicationTests.java @@ -0,0 +1,13 @@ +package com.mini.capi; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class CApiApplicationTests { + + @Test + void contextLoads() { + } + +}