OCT REM: 1. 初始化Agent框架Java SpringBoot3
This commit is contained in:
parent
8b4aa29f8f
commit
10d91f9c8d
|
@ -0,0 +1,2 @@
|
|||
/mvnw text eol=lf
|
||||
*.cmd text eol=crlf
|
|
@ -0,0 +1,37 @@
|
|||
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/
|
||||
**/logs/
|
||||
**/git.properties
|
||||
**/logs/*.log
|
||||
/.mvn/
|
|
@ -0,0 +1,43 @@
|
|||
server :
|
||||
port : 9276
|
||||
servlet :
|
||||
context-path: /cmhi
|
||||
compression:
|
||||
# 开启响应压缩
|
||||
enabled : true
|
||||
mime-types :
|
||||
- application/json # RESTful API JSON
|
||||
# 进行压缩的最小体积
|
||||
min-response-size: 1KB
|
||||
|
||||
# Crypto Configure
|
||||
jasypt :
|
||||
encryptor:
|
||||
algorithm: PBEWITHHMACSHA512ANDAES_256
|
||||
password :
|
||||
|
||||
spring :
|
||||
mvc :
|
||||
throw-exception-if-no-handler-found: true
|
||||
web :
|
||||
resources:
|
||||
add-mappings: false
|
||||
|
||||
jackson:
|
||||
date-format : yyyy-MM-dd HH:mm:ss.SSS
|
||||
timezone : GMT+8
|
||||
default-property-inclusion: non_null
|
||||
mapper :
|
||||
default-view-inclusion: true
|
||||
deserialization :
|
||||
fail-on-unknown-properties: false
|
||||
|
||||
log4j :
|
||||
logger:
|
||||
org:
|
||||
mybatis: info
|
||||
|
||||
# swagger-ui custom path
|
||||
springdoc:
|
||||
swagger-ui:
|
||||
path: /swagger-ui.html
|
|
@ -0,0 +1,3 @@
|
|||
#config log
|
||||
logging:
|
||||
config: file:config/logback.xml
|
|
@ -0,0 +1,6 @@
|
|||
#config log
|
||||
logging:
|
||||
config: file:./config/logback.xml
|
||||
|
||||
common :
|
||||
locale: en_US
|
|
@ -0,0 +1,11 @@
|
|||
common :
|
||||
token-expired-of-seconds: 600
|
||||
allow-passwd-retry-times: 3
|
||||
show-sql-command : false
|
||||
locale : zh_CN
|
||||
|
||||
protocol:
|
||||
check-timestamp : true
|
||||
timeout-of-seconds: 600
|
||||
crypto-type : 0
|
||||
#crypto-key: 12354
|
|
@ -0,0 +1,3 @@
|
|||
spring:
|
||||
profiles:
|
||||
active: local, common, user
|
|
@ -0,0 +1,108 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<configuration scan="true">
|
||||
|
||||
<property name="LOG_PATH" value="./logs"/>
|
||||
<property name="LOG_LEVEL" value="info"/>
|
||||
|
||||
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder charset="UTF-8">
|
||||
<pattern>[%d{yy-MM-dd HH:mm:ss:SSS}][%-5p][%c{0}][%M\(%L\)][%t]: %m%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<appender name="BIZ"
|
||||
class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${LOG_PATH}/biz.log</file>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<fileNamePattern>${LOG_PATH}/biz.log.%d{yyyyMMdd}
|
||||
</fileNamePattern>
|
||||
</rollingPolicy>
|
||||
<encoder charset="UTF-8">
|
||||
<pattern>[%d{yy-MM-dd HH:mm:ss:SSS}][%-5p][%c{0}][%M\(%L\)][%t]: %m%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
<appender name="SYSTEM-LOG-FILE"
|
||||
class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${LOG_PATH}/system.log</file>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<fileNamePattern>${LOG_PATH}/system.log.%d{yyyyMMdd}
|
||||
</fileNamePattern>
|
||||
</rollingPolicy>
|
||||
<encoder charset="UTF-8">
|
||||
<pattern>[%d{yy-MM-dd HH:mm:ss:SSS}][%-5p][%c{0}][%M\(%L\)][%t]: %m%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<appender name="DATA"
|
||||
class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${LOG_PATH}/data.log</file>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<fileNamePattern>${LOG_PATH}/data.log.%d{yyyyMMdd}
|
||||
</fileNamePattern>
|
||||
</rollingPolicy>
|
||||
<encoder charset="UTF-8">
|
||||
<pattern>[%d{yy-MM-dd HH:mm:ss:SSS}][%-5p][%c{0}][%M\(%L\)][%t]: %m%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<logger name="com.cmhi.gds.mapper" level="${LOG_LEVEL}" additivity="false">
|
||||
<appender-ref ref="DATA"/>
|
||||
<appender-ref ref="CONSOLE"/>
|
||||
</logger>
|
||||
|
||||
<logger name="org.mybatis" level="${LOG_LEVEL}" additivity="false">
|
||||
<appender-ref ref="DATA"/>
|
||||
<appender-ref ref="CONSOLE"/>
|
||||
</logger>
|
||||
|
||||
<logger name="org.apache.ibatis" level="${LOG_LEVEL}"
|
||||
additivity="false">
|
||||
<appender-ref ref="DATA"/>
|
||||
</logger>
|
||||
|
||||
<logger name="org.mybatis.spring" level="${LOG_LEVEL}"
|
||||
additivity="false">
|
||||
<appender-ref ref="DATA"/>
|
||||
</logger>
|
||||
|
||||
<logger name="org.springframework.jdbc" level="${LOG_LEVEL}"
|
||||
additivity="false">
|
||||
<appender-ref ref="DATA"/>
|
||||
<appender-ref ref="CONSOLE"/>
|
||||
</logger>
|
||||
<logger name="org.springframework.orm" level="${LOG_LEVEL}"
|
||||
additivity="false">
|
||||
<appender-ref ref="DATA"/>
|
||||
</logger>
|
||||
<logger name="com.mysql" level="${LOG_LEVEL}" additivity="false">
|
||||
<appender-ref ref="DATA"/>
|
||||
<appender-ref ref="CONSOLE"/>
|
||||
</logger>
|
||||
<logger name="java.sql" level="${LOG_LEVEL}" additivity="false">
|
||||
<appender-ref ref="DATA"/>
|
||||
<appender-ref ref="CONSOLE"/>
|
||||
</logger>
|
||||
<logger name="javax.sql" level="${LOG_LEVEL}" additivity="false">
|
||||
<appender-ref ref="DATA"/>
|
||||
</logger>
|
||||
|
||||
<logger name="org.springframework.security" level="${LOG_LEVEL}" additivity="false">
|
||||
<appender-ref ref="DATA"/>
|
||||
<appender-ref ref="CONSOLE"/>
|
||||
</logger>
|
||||
|
||||
<logger name="org.casbin.jcasbin" level="error" additivity="false">
|
||||
<appender-ref ref="DATA"/>
|
||||
<appender-ref ref="CONSOLE"/>
|
||||
</logger>
|
||||
|
||||
<logger name="com.ulisesbocchio.jasyptspringboot" level="error" additivity="false">
|
||||
<appender-ref ref="DATA"/>
|
||||
<appender-ref ref="CONSOLE"/>
|
||||
</logger>
|
||||
|
||||
<root level="${LOG_LEVEL}">
|
||||
<appender-ref ref="SYSTEM-LOG-FILE"/>
|
||||
<appender-ref ref="CONSOLE"/>
|
||||
</root>
|
||||
</configuration>
|
|
@ -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-<version>,maven-mvnd-<version>-<platform>}/<hash>
|
||||
[ -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 "$@"
|
|
@ -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-<version>,maven-mvnd-<version>-<platform>}/<hash>
|
||||
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"
|
|
@ -0,0 +1,220 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>3.4.1</version>
|
||||
<relativePath/> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<groupId>com.cmhi</groupId>
|
||||
<artifactId>middleware-agent</artifactId>
|
||||
<version>0.1.0-SNAPSHOT</version>
|
||||
<name>middleware-agent</name>
|
||||
<description>middleware-agent</description>
|
||||
<url/>
|
||||
<licenses>
|
||||
<license/>
|
||||
</licenses>
|
||||
<developers>
|
||||
<developer/>
|
||||
</developers>
|
||||
<scm>
|
||||
<connection/>
|
||||
<developerConnection/>
|
||||
<tag/>
|
||||
<url/>
|
||||
</scm>
|
||||
<properties>
|
||||
<java.version>17</java.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.36</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-tomcat</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-undertow</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains</groupId>
|
||||
<artifactId>annotations</artifactId>
|
||||
<version>26.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.swagger.core.v3</groupId>
|
||||
<artifactId>swagger-annotations-jakarta</artifactId>
|
||||
<version>2.2.27</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jacoco</groupId>
|
||||
<artifactId>jacoco-maven-plugin</artifactId>
|
||||
<version>0.8.12</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-starter-webmvc-api</artifactId>
|
||||
<version>2.8.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
|
||||
<version>2.8.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mapstruct</groupId>
|
||||
<artifactId>mapstruct</artifactId>
|
||||
<version>1.6.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.oshi</groupId>
|
||||
<artifactId>oshi-core-java11</artifactId>
|
||||
<version>6.6.5</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<version>2.18.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.aspectj</groupId>
|
||||
<artifactId>aspectjweaver</artifactId>
|
||||
<version>1.9.21</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.jayway.jsonpath</groupId>
|
||||
<artifactId>json-path</artifactId>
|
||||
<version>2.9.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-tx</artifactId>
|
||||
<version>6.1.5</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<annotationProcessorPaths>
|
||||
<path>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>${lombok.version}</version>
|
||||
</path>
|
||||
<path>
|
||||
<groupId>org.mapstruct</groupId>
|
||||
<artifactId>mapstruct-processor</artifactId>
|
||||
<version>1.6.3</version>
|
||||
</path>
|
||||
</annotationProcessorPaths>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<excludes>
|
||||
<exclude>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.jacoco</groupId>
|
||||
<artifactId>jacoco-maven-plugin</artifactId>
|
||||
<version>0.8.12</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>prepare-agent</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>report</id>
|
||||
<phase>prepare-package</phase>
|
||||
<goals>
|
||||
<goal>report</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>pl.project13.maven</groupId>
|
||||
<artifactId>git-commit-id-plugin</artifactId>
|
||||
<version>4.9.10</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>get-the-git-infos</id>
|
||||
<goals>
|
||||
<goal>revision</goal>
|
||||
</goals>
|
||||
<phase>initialize</phase>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<failOnNoGitDirectory>false</failOnNoGitDirectory>
|
||||
<verbose>false</verbose>
|
||||
<offline>true</offline>
|
||||
<dateFormat>yyyy-MM-dd'T'HH:mm:ssZ</dateFormat>
|
||||
<generateGitPropertiesFile>true</generateGitPropertiesFile>
|
||||
<generateGitPropertiesFilename>${project.basedir}/src/main/resources/git.properties
|
||||
</generateGitPropertiesFilename>
|
||||
<excludeProperties>
|
||||
<excludeProperty>git.commit.message.*</excludeProperty>
|
||||
<excludeProperty>git.commit.user.*</excludeProperty>
|
||||
<excludeProperty>git.remote.origin.*</excludeProperty>
|
||||
</excludeProperties>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<argLine>-Dspring.config.location=file:${project.basedir}/config/</argLine>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,44 @@
|
|||
package com.cmhi.magent;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
/**
|
||||
* 主应用程序启动类 {@code MiddlewareAgentApplication}。
|
||||
* <p>
|
||||
* 本类是基于 Spring Boot 的应用程序入口,标注了 {@link SpringBootApplication} 注解,
|
||||
* 自动启用了 Spring Boot 的组件扫描、自动配置以及其他核心功能。
|
||||
* 通过运行此类的 {@code main} 方法,启动整个应用程序。
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 核心功能:
|
||||
* <ul>
|
||||
* <li>启动 Spring Boot 应用程序。</li>
|
||||
* <li>加载应用程序上下文并初始化相关组件。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* @see SpringBootApplication
|
||||
* @see SpringApplication
|
||||
* @author huangxin@cmhi.chinamobile.com
|
||||
* @version 1.0.0
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
@SpringBootApplication
|
||||
public class MiddlewareAgentApplication {
|
||||
|
||||
/**
|
||||
* 应用程序启动入口。
|
||||
* <p>
|
||||
* 该方法通过调用 {@link SpringApplication#run(Class, String...)} 方法,启动整个 Spring Boot 应用程序。
|
||||
* 运行后,Spring 应用上下文将被加载,并启动所有相关的 Spring 组件,如 Web 控制器、服务、配置等。
|
||||
* </p>
|
||||
*
|
||||
* @param args 命令行参数,可以通过 JVM 启动选项传递给应用程序。
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(MiddlewareAgentApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package com.cmhi.magent.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 解密协议注解。
|
||||
*
|
||||
* <p>该注解用于标记需要解密处理的类或方法。使用该注解后,可通过拦截器或 AOP 等机制,
|
||||
* 实现对请求或响应数据的解密逻辑。</p>
|
||||
*
|
||||
* <p>使用场景:
|
||||
* <ul>
|
||||
* <li>标记在控制器方法上时,表示该方法的请求参数或响应数据需要解密。</li>
|
||||
* <li>标记在类上时,表示该类中所有的方法均需要进行解密处理。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* <p>该注解的保留策略为 {@code RUNTIME},允许在运行时通过反射机制获取注解信息。</p>
|
||||
*
|
||||
* @author huangxin@cmhi.chinamobile.com
|
||||
* @version 1.0.0
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
@Target({ElementType.TYPE, ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface DecryptionProtocol {
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
package com.cmhi.magent.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 加密协议注解。
|
||||
*
|
||||
* <p>该注解用于标记需要加密处理的类或方法。使用该注解后,可通过拦截器或 AOP 等机制,
|
||||
* 实现对请求或响应数据的加密逻辑。</p>
|
||||
*
|
||||
* <p>使用场景:
|
||||
* <ul>
|
||||
* <li>标记在控制器方法上时,表示该方法的请求参数或响应数据需要进行加密。</li>
|
||||
* <li>标记在类上时,表示该类中所有的方法均需要进行加密处理。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* <p>该注解的保留策略为 {@code RUNTIME},允许在运行时通过反射机制获取注解信息。</p>
|
||||
*
|
||||
* <p>配合 {@link DecryptionProtocol} 注解使用时,可实现加密和解密的全流程数据处理。</p>
|
||||
*
|
||||
* @see DecryptionProtocol
|
||||
* @author huangxin@cmhi.chinamobile.com
|
||||
* @version 1.0.0
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
@Target({ElementType.TYPE, ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface EncryptionProtocol {
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
package com.cmhi.magent.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 操作日志注解。
|
||||
*
|
||||
* <p>该注解用于标记需要记录操作日志的方法或参数。在标注的方法执行时,可以通过 AOP 或其他拦截机制,
|
||||
* 自动记录操作日志信息,包括操作模块、操作类型和操作说明。</p>
|
||||
*
|
||||
* <p>使用场景:
|
||||
* <ul>
|
||||
* <li>标记在方法上时,用于记录该方法涉及的操作模块、类型及描述。</li>
|
||||
* <li>标记在方法参数上时,可配合日志框架提取参数中的操作信息。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* <p>通过运行时保留策略({@code RUNTIME}),注解信息可在程序运行时通过反射机制获取。</p>
|
||||
*
|
||||
* <p>核心字段说明:
|
||||
* <ul>
|
||||
* <li>{@code OperationModule}:操作模块的名称,用于标识业务模块或功能点。</li>
|
||||
* <li>{@code OperationType}:操作的类型(如:新增、修改、删除等)。</li>
|
||||
* <li>{@code OperationDesc}:操作的详细描述,便于日志分析。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* @author
|
||||
* @version 1.0.0
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
@Target({ElementType.PARAMETER, ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@SuppressWarnings("java:S100")
|
||||
public @interface OperationLogAnnotation {
|
||||
|
||||
/**
|
||||
* 操作模块。
|
||||
*
|
||||
* <p>用于标识业务模块或功能点(如:用户管理、订单管理)。</p>
|
||||
*
|
||||
* @return 操作模块的名称,默认为空字符串。
|
||||
*/
|
||||
String OperationModule() default "";
|
||||
|
||||
/**
|
||||
* 操作类型。
|
||||
*
|
||||
* <p>用于记录操作的具体类型(如:新增、新建、修改、删除等)。</p>
|
||||
*
|
||||
* @return 操作类型,默认为空字符串。
|
||||
*/
|
||||
String OperationType() default "";
|
||||
|
||||
/**
|
||||
* 操作说明。
|
||||
*
|
||||
* <p>对操作的详细描述,便于后续日志排查和分析。</p>
|
||||
*
|
||||
* @return 操作说明,默认为空字符串。
|
||||
*/
|
||||
String OperationDesc() default "";
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package com.cmhi.magent.common;
|
||||
|
||||
/**
|
||||
* 定义通用的枚举接口。
|
||||
* <p>
|
||||
* {@code BaseEnum} 接口提供了获取枚举值和描述的抽象方法,所有实现该接口的枚举类需要提供具体实现。
|
||||
* <p>
|
||||
* 此接口适用于统一管理枚举类型的数据,便于在项目中实现标准化的处理逻辑。
|
||||
* <p>
|
||||
* @author huangxin@cmhi.chinamobile.com
|
||||
* @version 1.0.0
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
public interface BaseEnum {
|
||||
|
||||
/**
|
||||
* 获取枚举的值。
|
||||
* <p>
|
||||
* @return 枚举值,通常是一个唯一的整数。
|
||||
*/
|
||||
Integer getValue();
|
||||
|
||||
/**
|
||||
* 获取枚举的描述信息。
|
||||
* <p>
|
||||
* @return 描述信息的字符串。
|
||||
*/
|
||||
String getDescription();
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
package com.cmhi.magent.common;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 枚举处理器工具类,用于操作实现 {@code BaseEnum} 接口的枚举类型。
|
||||
* <p>
|
||||
* {@code CommonEnumHandler} 提供了统一的枚举管理功能,包括提取枚举值和通过值查找枚举对象的功能。
|
||||
* 这种设计使得业务代码在处理通用枚举逻辑时更加简洁和高效。
|
||||
*
|
||||
* <p> 泛型约束:<br>
|
||||
* - {@code E} 必须是实现了 {@code BaseEnum} 接口的枚举类型。
|
||||
* <p>
|
||||
* @param <E> 枚举类型,必须同时是 {@code Enum} 和 {@code BaseEnum}。
|
||||
* <p>
|
||||
* @author huangxin@cmhi.chinamobile.com
|
||||
* @version 1.0.0
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
public final class CommonEnumHandler<E extends BaseEnum> {
|
||||
|
||||
/**
|
||||
* 被处理的枚举类型。
|
||||
*/
|
||||
private final Class<E> enumType;
|
||||
|
||||
/**
|
||||
* 枚举值的列表。
|
||||
*/
|
||||
private final List<E> enums;
|
||||
|
||||
/**
|
||||
* 构造函数,根据枚举类型初始化枚举处理器。
|
||||
*
|
||||
* <p>此构造函数会提取所有的枚举常量,并存储为列表以供后续操作。
|
||||
*
|
||||
* @param type 枚举类型(不能为 {@code null})。
|
||||
* @throws IllegalArgumentException 如果 {@code type} 参数为 {@code null}。
|
||||
*/
|
||||
public CommonEnumHandler(Class<E> type) {
|
||||
if (type == null) {
|
||||
throw new IllegalArgumentException("Type argument cannot be null");
|
||||
}
|
||||
|
||||
this.enumType = type;
|
||||
this.enums = Arrays.asList(type.getEnumConstants());
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据枚举值查找对应的枚举常量。
|
||||
*
|
||||
* <p>此静态方法可以从枚举类中根据值(code)高效查找对应的枚举对象。
|
||||
*
|
||||
* @param <E> 枚举类型,必须同时实现 {@code Enum} 和 {@code BaseEnum}。
|
||||
* @param enumClass 枚举类(不能为 {@code null})。
|
||||
* @param code 枚举值(code),用于匹配枚举常量。
|
||||
* @return 匹配的枚举对象,如果未找到则返回 {@code null}。
|
||||
* @throws IllegalArgumentException 如果 {@code enumClass} 参数为 {@code null}。
|
||||
*/
|
||||
public static <E extends Enum<?> & BaseEnum> E codeOf(Class<E> enumClass, int code) {
|
||||
if (enumClass == null) {
|
||||
throw new IllegalArgumentException("Enum class cannot be null");
|
||||
}
|
||||
|
||||
E[] enumConstants = enumClass.getEnumConstants();
|
||||
|
||||
for (E e : enumConstants) {
|
||||
// 使用 Integer 比较以更安全地处理可能的 null 值
|
||||
if (e.getValue() != null && e.getValue().equals(code)) {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
package com.cmhi.magent.common;
|
||||
|
||||
import com.cmhi.magent.misc.ApiContextUtils;
|
||||
import com.cmhi.magent.misc.MessageUtil;
|
||||
|
||||
/**
|
||||
* 通用状态枚举类。
|
||||
*
|
||||
* <p>该枚举表示实体或操作的通用状态,包括“正常”、“已锁定”、“已禁用”和“已删除”等状态。
|
||||
* 每个状态都包含一个唯一的数值(`code`)和对应的状态描述(`readme`)。
|
||||
*
|
||||
* <p>本枚举实现了 {@code BaseEnum} 接口,提供了一致的接口方法用于获取状态值和描述信息。
|
||||
* 另外,通过 {@code MessageUtil} 和 {@code ApiContextUtils} 实现了国际化支持。
|
||||
* <p>
|
||||
* @author huangxin@cmhi.chinamobile.com
|
||||
* @version 1.0.0
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
public enum CommonStatus implements BaseEnum {
|
||||
|
||||
/**
|
||||
* 正常状态。
|
||||
*
|
||||
* <p>状态码:{@code 0}。
|
||||
*/
|
||||
NORMAL(0, "正常"),
|
||||
|
||||
/**
|
||||
* 已锁定状态。
|
||||
*
|
||||
* <p>状态码:{@code 1}。
|
||||
*/
|
||||
LOCKED(1, "已锁定"),
|
||||
|
||||
/**
|
||||
* 已禁用状态。
|
||||
*
|
||||
* <p>状态码:{@code 2}。
|
||||
*/
|
||||
DISABLED(2, "已禁用"),
|
||||
|
||||
/**
|
||||
* 已删除状态。
|
||||
*
|
||||
* <p>状态码:{@code 3}。
|
||||
*/
|
||||
DELETED(3, "已删除");
|
||||
|
||||
/**
|
||||
* 状态对应的唯一数值。
|
||||
*/
|
||||
private final Integer code;
|
||||
|
||||
/**
|
||||
* 状态的默认描述信息。
|
||||
*/
|
||||
private final String readme;
|
||||
|
||||
/**
|
||||
* 构造函数,用于初始化状态的数值和描述信息。
|
||||
*
|
||||
* @param code 状态码。
|
||||
* @param readme 状态描述信息。
|
||||
*/
|
||||
CommonStatus(int code, String readme) {
|
||||
this.code = code;
|
||||
this.readme = readme;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取状态的枚举名称。
|
||||
*
|
||||
* <p>例如,当状态为 {@code NORMAL} 时,返回 "NORMAL"。
|
||||
*
|
||||
* @return 状态的枚举名称字符串。
|
||||
*/
|
||||
public String getStringValue() {
|
||||
return this.name();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取状态的数值。
|
||||
*
|
||||
* @return 状态对应的数值(`code`)。
|
||||
*/
|
||||
@Override
|
||||
public Integer getValue() {
|
||||
return this.code;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取状态的描述信息(支持国际化)。
|
||||
*
|
||||
* <p>描述信息通过 {@code MessageUtil} 和 {@code ApiContextUtils} 动态获取,
|
||||
* 以支持多语言环境。
|
||||
*
|
||||
* @return 状态的描述信息。
|
||||
*/
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return MessageUtil.get(getStringValue(), ApiContextUtils.getLanguage());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
package com.cmhi.magent.common;
|
||||
|
||||
/**
|
||||
* 常量值工具类,主要用于存储项目中使用的全局静态常量。
|
||||
*
|
||||
* <p>该类包含正则表达式、时间相关的常量、HTTP头字段常量以及其他通用数据。
|
||||
* 所有的常量均为 {@code static final},不可修改。
|
||||
* <p>
|
||||
* 本类为工具类,禁止实例化。
|
||||
*
|
||||
* @author huangxin@cmhi.chinamobile.com
|
||||
* @version 1.0.0
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
public final class ConstValue {
|
||||
|
||||
/**
|
||||
* 私有构造函数,防止实例化。
|
||||
*
|
||||
* <p>尝试实例化该类时将抛出 {@code AssertionError}。
|
||||
*/
|
||||
private ConstValue() {
|
||||
throw new AssertionError("This is a utility class and cannot be instantiated.");
|
||||
}
|
||||
|
||||
/**
|
||||
* 用于验证输入字符串的正则表达式,禁止包含非法字符。
|
||||
*
|
||||
* <p>匹配规则:字符串中不包含以下字符:空格、`--`、`*`、`%`、`+`、单引号(`'`)和分号(`;`)。
|
||||
*/
|
||||
public static final String UN_EXPECT_REGEX_CHARS = "^((?!(--|\\s|\\*|%|\\+|'|;])).)*$";
|
||||
|
||||
/**
|
||||
* HTTP认证头部的前缀字符串。
|
||||
*
|
||||
* <p>通常用于在请求头中添加认证令牌,例如 {@code Authorization: Bearer <token>}。
|
||||
*/
|
||||
public static final String STRING_HTTP_AUTH_HEAD = "Bearer ";
|
||||
|
||||
/**
|
||||
* HTTP请求头中的语言标识字段。
|
||||
*
|
||||
* <p>该字段通常用于传递客户端的语言环境,例如 {@code zh-CN} 或 {@code en-US}。
|
||||
*/
|
||||
public static final String LANGUAGE_HEAD = "language";
|
||||
|
||||
/**
|
||||
* 一秒的毫秒数(1000毫秒)。
|
||||
*/
|
||||
public static final long MS_OF_SECOND = 1000L;
|
||||
|
||||
/**
|
||||
* 一分钟的秒数(60秒)。
|
||||
*/
|
||||
public static final long SECOND_OF_MINUTE = 60L;
|
||||
|
||||
/**
|
||||
* IPv4地址的最大长度。
|
||||
*
|
||||
* <p>IPv4地址的长度范围为 7 到 15 字符,例如 `255.255.255.255`。
|
||||
*/
|
||||
public static final int MAX_IPV4_LEN = 15;
|
||||
|
||||
/**
|
||||
* 匹配IP地址的正则表达式,支持IPv4和IPv6格式。
|
||||
*
|
||||
* <p>匹配规则:
|
||||
* <ul>
|
||||
* <li>IPv4地址:如 {@code 192.168.1.1}</li>
|
||||
* <li>IPv6地址:如 {@code 2001:0db8:85a3:0000:0000:8a2e:0370:7334}</li>
|
||||
* <li>简化形式的IPv6地址:如 {@code ::1} 或 {@code 2001:db8::1}</li>
|
||||
* </ul>
|
||||
*/
|
||||
public static final String IP_ADDRESS_REGEX =
|
||||
"^((25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)$" +
|
||||
"|^([\\da-fA-F]{1,4}:){7}[\\da-fA-F]{1,4}$" +
|
||||
"|^::([\\da-fA-F]{1,4}:){0,6}[\\da-fA-F]{1,4}$" +
|
||||
"|^([\\da-fA-F]{1,4}:){1,7}:$" +
|
||||
"|^([\\da-fA-F]{1,4}:){1,6}:[\\da-fA-F]{1,4}$";
|
||||
|
||||
/**
|
||||
* 协议相关的常量。
|
||||
*
|
||||
* <p>该内部类用于存储与通信协议相关的常量。
|
||||
*/
|
||||
public static final class Protocol {
|
||||
|
||||
/**
|
||||
* 协议的版本号。
|
||||
*/
|
||||
public static final int VERSION = 1;
|
||||
|
||||
/**
|
||||
* 私有构造函数,防止实例化。
|
||||
*
|
||||
* <p>尝试实例化该类时将抛出 {@code AssertionError}。
|
||||
*/
|
||||
private Protocol() {
|
||||
throw new AssertionError("This is a utility class and cannot be instantiated.");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,255 @@
|
|||
package com.cmhi.magent.common;
|
||||
|
||||
import com.cmhi.magent.misc.ApiContextUtils;
|
||||
import com.cmhi.magent.misc.MessageUtil;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* {@code ErrorCode} 定义应用程序中的错误代码和描述。
|
||||
* <p>
|
||||
* 此枚举类提供了各种错误代码及其对应的描述信息,同时支持获取相关的 HTTP 状态码。
|
||||
* <p>
|
||||
* @author huangxin@cmhi.chinamobile.com
|
||||
* @version 1.0.0
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
public enum ErrorCode implements BaseEnum {
|
||||
|
||||
/** 成功 */
|
||||
ERR_OK(0, "成功"),
|
||||
|
||||
/** 密码错误 */
|
||||
ERR_PASSWORD(1, "密码错误"),
|
||||
|
||||
/** 用户不存在 */
|
||||
ERR_USERNOTFOUND(2, "用户不存在"),
|
||||
|
||||
/** 连续密码错误达上限,再次输入错误将锁定用户 */
|
||||
ERR_PASSWORDMORE(3, "连续密码错误达上限,再次输入错误将锁定用户"),
|
||||
|
||||
/** 密码错误达上限,用户被锁定 */
|
||||
ERR_USERLOCK(4, "密码错误达上限,用户被锁定"),
|
||||
|
||||
/** 密码已经过期 */
|
||||
ERR_PASSWORD_EXPIRED(5, "密码已经过期"),
|
||||
|
||||
/** 用户账户异常 */
|
||||
ERR_ACCOUNT(6, "用户账户异常"),
|
||||
|
||||
/** 该用户已经存在 */
|
||||
ERR_USEREXIST(7, "该用户已经存在"),
|
||||
|
||||
/** 用户密码强度不符合要求 */
|
||||
ERR_PASSWORDSIMPLE(8, "用户密码强度不符合要求"),
|
||||
|
||||
/** 输入信息格式有误 */
|
||||
ERR_INPUTFORMAT(9, "输入信息格式有误"),
|
||||
|
||||
/** 缺少必要输入信息 */
|
||||
ERR_INPUTMISS(10, "缺少必要输入信息"),
|
||||
|
||||
/** 操作员权限不足 */
|
||||
ERR_PERMISSION(11, "操作员权限不足"),
|
||||
|
||||
/** 请求超时 */
|
||||
ERR_REQTIMEOUT(12, "请求超时"),
|
||||
|
||||
/** 参数错误 */
|
||||
ERR_PARAMS(13, "参数错误"),
|
||||
|
||||
/** 系统异常 */
|
||||
ERR_SYSTEMEXCEPTION(14, "系统异常"),
|
||||
|
||||
/** 未知命令 */
|
||||
ERR_UNKNOWNCMD(15, "未知命令"),
|
||||
|
||||
/** 用户未登录 */
|
||||
ERR_LOGOUT(16, "用户未登录"),
|
||||
|
||||
/** Token 超时 */
|
||||
ERR_TOKENTIMEOUT(17, "Token超时"),
|
||||
|
||||
/** 非法 Token */
|
||||
ERR_TOKENNOTFOUND(18, "非法Token"),
|
||||
|
||||
/** Token 秘钥错误 */
|
||||
ERR_TOKEN_KEY(19, "Token 秘钥错误"),
|
||||
|
||||
/** Http 请求缺少认证头部 */
|
||||
ERR_MISSAUTHHEAD(20, "Http 请求缺少认证头部"),
|
||||
|
||||
/** 没有该内容 */
|
||||
ERR_NOSUCHITEM(21, "没有该内容"),
|
||||
|
||||
/** 该内容已经存在 */
|
||||
ERR_ITEMEXISTS(22, "该内容已经存在"),
|
||||
|
||||
/** 参数异常 */
|
||||
ERR_PARAMEXCEPTION(23, "参数异常"),
|
||||
|
||||
/** 设备已锁定 */
|
||||
ERR_DEVICELOCKED(24, "设备已锁定"),
|
||||
|
||||
/** 协议版本不兼容,请升级系统 */
|
||||
ERR_VERSION(25, "协议版本不兼容,请升级系统"),
|
||||
|
||||
/** 没有这个类型的设备 */
|
||||
ERR_NOSUCHTYPE(26, "没有这个类型的设备"),
|
||||
|
||||
/** 禁止同时删除多个设备 */
|
||||
ERR_REMOVEMORE(27, "禁止同时删除多个设备"),
|
||||
|
||||
/** 同类任务正在运行 */
|
||||
ERR_TASKRUNNING(28, "同类任务正在运行"),
|
||||
|
||||
/** 不支持的操作 */
|
||||
ERR_UNSUPPORT(29, "不支持的操作"),
|
||||
|
||||
/** 操作中断 */
|
||||
ERR_INTERRUPT(30, "操作中断"),
|
||||
|
||||
/** 调用设备失败 */
|
||||
ERR_CALLDEVICE(31, "调用设备失败"),
|
||||
|
||||
/** 没有该任务 */
|
||||
ERR_NOSUCHTASK(32, "没有该任务"),
|
||||
|
||||
/** 该任务没有运行 */
|
||||
ERR_TASKNOTRUNNING(33, "该任务没有运行"),
|
||||
|
||||
/** 请求超时 */
|
||||
ERR_REQUESTTIMEOUT(34, "请求超时"),
|
||||
|
||||
/** 无法处置该 IP */
|
||||
ERR_UNABLEDISPOSEIP(35, "无法处置该IP"),
|
||||
|
||||
/** 操作数据库失败 */
|
||||
ERR_DATABASE(36, "操作数据库失败"),
|
||||
|
||||
/** 未经授权的客户端 */
|
||||
ERR_UNTRUSTHOST(37, "未经授权的客户端"),
|
||||
|
||||
/** 未经授权的 Token */
|
||||
ERR_UNTRUSTTOKEN(38, "未经授权的Token"),
|
||||
|
||||
/** 未提供该接口 */
|
||||
ERR_UNKNOWNINTERFACE(39, "未提供该接口"),
|
||||
|
||||
/** BASE64 解密失败 */
|
||||
ERR_DECRYPT_BASE64(40, "BASE64解密失败"),
|
||||
|
||||
/** BASE64 加密失败 */
|
||||
ERR_ENCRYPT_BASE64(41, "BASE64加密失败"),
|
||||
|
||||
/** AES128 解密失败 */
|
||||
ERR_DECRYPT_AES128(42, "AES128解密失败"),
|
||||
|
||||
/** AES128 加密失败 */
|
||||
ERR_ENCRYPT_AES128(43, "AES128加密失败"),
|
||||
|
||||
/** 3DES 解密失败 */
|
||||
ERR_DECRYPT_3DES(44, "3DES解密失败"),
|
||||
|
||||
/** 3DES 加密失败 */
|
||||
ERR_ENCRYPT_3DES(45, "3DES加密失败"),
|
||||
|
||||
/** 不支持的解密算法 */
|
||||
ERR_DECRYPT_UNKNOWN(46, "不支持的解密算法"),
|
||||
|
||||
/** 不支持的加密算法 */
|
||||
ERR_ENCRYPT_UNKNOWN(47, "不支持的加密算法"),
|
||||
|
||||
/** Json 序列化错误 */
|
||||
ERR_JSON_ENCODE(48, "Json 序列号错误"),
|
||||
|
||||
/** Json 反序列化错误 */
|
||||
ERR_JSON_DECODE(49, "Json 反序列化错误"),
|
||||
|
||||
/** AES256 加密失败 */
|
||||
ERR_ENCRYPT_AES256(50, "AES256加密失败"),
|
||||
|
||||
/** AES256 解密失败 */
|
||||
ERR_DECRYPT_AES256(51, "AES256解密失败"),
|
||||
|
||||
/** 错误的秘钥 */
|
||||
ERR_CRYPTO_KEY(52, "错误的秘钥"),
|
||||
|
||||
/** 用户角色不存在 */
|
||||
ERR_USER_ROLE_NOTEXISTS(53, "用户角色不存在"),
|
||||
|
||||
/** 资源被占用 */
|
||||
ERR_RESOURCE_USED(54, "资源被占用"),
|
||||
;
|
||||
|
||||
private final int errCode;
|
||||
private final String errMsg;
|
||||
|
||||
/**
|
||||
* 构造函数。
|
||||
* <p>
|
||||
* @param err 错误代码。
|
||||
* @param msg 错误消息。
|
||||
*/
|
||||
ErrorCode(int err, String msg) {
|
||||
this.errCode = err;
|
||||
this.errMsg = msg;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取错误代码。
|
||||
* <p>
|
||||
* @return 错误代码。
|
||||
*/
|
||||
public int getCode() {
|
||||
return errCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对应的 HTTP 状态码。
|
||||
* <p>
|
||||
* 根据错误代码映射到适当的 HTTP 状态码。
|
||||
* <p>
|
||||
* @return HTTP 状态码。
|
||||
*/
|
||||
public int getHttpCode() {
|
||||
return switch (this) {
|
||||
case ERR_OK -> HttpServletResponse.SC_OK;
|
||||
case ERR_SYSTEMEXCEPTION, ERR_PARAMEXCEPTION -> HttpServletResponse.SC_EXPECTATION_FAILED;
|
||||
case ERR_TOKENTIMEOUT, ERR_REQTIMEOUT -> HttpServletResponse.SC_REQUEST_TIMEOUT;
|
||||
case ERR_UNTRUSTTOKEN, ERR_UNTRUSTHOST, ERR_LOGOUT -> HttpServletResponse.SC_UNAUTHORIZED;
|
||||
case ERR_MISSAUTHHEAD, ERR_PARAMS, ERR_INPUTFORMAT, ERR_INPUTMISS -> HttpServletResponse.SC_BAD_REQUEST;
|
||||
case ERR_UNSUPPORT -> HttpServletResponse.SC_METHOD_NOT_ALLOWED;
|
||||
case ERR_UNKNOWNINTERFACE -> HttpServletResponse.SC_NOT_FOUND;
|
||||
default -> HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取错误名称。
|
||||
* <p>
|
||||
* @return 错误名称。
|
||||
*/
|
||||
public String getStringValue() {
|
||||
return this.name();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取错误代码值。
|
||||
* <p>
|
||||
* @return 错误代码值。
|
||||
*/
|
||||
@Override
|
||||
public Integer getValue() {
|
||||
return this.errCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取错误描述。
|
||||
* <p>
|
||||
* @return 错误描述。
|
||||
*/
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return MessageUtil.get(getStringValue(), ApiContextUtils.getLanguage());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
package com.cmhi.magent.common;
|
||||
|
||||
import com.cmhi.magent.misc.ApiContextUtils;
|
||||
import com.cmhi.magent.misc.MessageUtil;
|
||||
|
||||
/**
|
||||
* 协议加密类型枚举类。
|
||||
*
|
||||
* <p>该枚举定义了不同的协议加密方式,包括不加密、Base64编码、AES加密(128位和256位)以及DES加密。
|
||||
* 每种加密类型均包含对应的编码值(`code`)和默认描述信息(`readme`)。
|
||||
*
|
||||
* <p>本枚举实现了 {@code BaseEnum} 接口,提供了统一的接口方法,用以获取编码值和描述信息。
|
||||
* 同时,通过 {@code MessageUtil} 和 {@code ApiContextUtils} 实现了国际化支持。
|
||||
*
|
||||
* @author huangxin@cmhi.chinamobile.com
|
||||
* @version 1.0.0
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
public enum ProtoCryptoType implements BaseEnum {
|
||||
|
||||
/**
|
||||
* 不加密。
|
||||
*
|
||||
* <p>编码值:{@code 0}。
|
||||
*/
|
||||
CRYPTO_NONE(0, "不加密"),
|
||||
|
||||
/**
|
||||
* Base64编码。
|
||||
*
|
||||
* <p>编码值:{@code 1}。
|
||||
*/
|
||||
CRYPTO_BASE64(1, "Base64编码"),
|
||||
|
||||
/**
|
||||
* AES128加密。
|
||||
*
|
||||
* <p>编码值:{@code 2}。
|
||||
*/
|
||||
CRYPTO_AES128(2, "AES128加密"),
|
||||
|
||||
/**
|
||||
* DES对称加密。
|
||||
*
|
||||
* <p>编码值:{@code 3}。
|
||||
*/
|
||||
CRYPTO_DES(3, "DES对称加密"),
|
||||
|
||||
/**
|
||||
* AES256加密。
|
||||
*
|
||||
* <p>编码值:{@code 4}。
|
||||
*/
|
||||
CRYPTO_AES256(4, "AES256加密");
|
||||
|
||||
/**
|
||||
* 加密方式的唯一编码值。
|
||||
*/
|
||||
private final int code;
|
||||
|
||||
/**
|
||||
* 加密方式的默认描述信息。
|
||||
*/
|
||||
private final String readme;
|
||||
|
||||
/**
|
||||
* 构造函数,用于初始化加密类型的编码值和描述信息。
|
||||
*
|
||||
* @param code 加密方式的编码值。
|
||||
* @param readme 加密方式的描述信息。
|
||||
*/
|
||||
ProtoCryptoType(int code, String readme) {
|
||||
this.code = code;
|
||||
this.readme = readme;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取加密方式的描述信息。
|
||||
*
|
||||
* <p>此方法直接返回枚举定义时的描述信息(`readme`)。
|
||||
*
|
||||
* @return 加密方式的描述信息。
|
||||
*/
|
||||
public String getStringValue() {
|
||||
return this.readme;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取加密方式的编码值。
|
||||
*
|
||||
* @return 加密方式的编码值(`code`)。
|
||||
*/
|
||||
@Override
|
||||
public Integer getValue() {
|
||||
return this.code;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取加密方式的描述信息(支持国际化)。
|
||||
*
|
||||
* <p>描述信息通过 {@code MessageUtil} 和 {@code ApiContextUtils} 动态获取,
|
||||
* 从而支持多语言环境下的描述值。
|
||||
*
|
||||
* @return 加密方式的国际化描述信息。
|
||||
*/
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return MessageUtil.get(getStringValue(), ApiContextUtils.getLanguage());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
package com.cmhi.magent.config;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.autoconfigure.web.ServerProperties;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* @class CommonConfigure
|
||||
* @brief 配置类,用于管理通用的应用程序配置。
|
||||
* <p>
|
||||
* 此类用于加载和管理应用中常见的配置项,例如令牌过期时间、本地化设置等,
|
||||
* 并提供全局变量初始化功能。
|
||||
* <p>
|
||||
* @author huangxin@cmhi.chinamobile.com
|
||||
* @version 1.0.0
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
@Data
|
||||
@ConfigurationProperties(prefix = "common")
|
||||
@Configuration
|
||||
@Slf4j
|
||||
@SuppressWarnings({"java:S3008", "java:S1444", "java:S1104"})
|
||||
public class CommonConfigure {
|
||||
|
||||
/** 全局变量,基准 URL */
|
||||
public static String BASEURL;
|
||||
|
||||
/** 全局变量,项目的前缀 URL */
|
||||
public static String PROJECT_PREFIX_URL;
|
||||
|
||||
/** 注入的 Spring Boot 服务器属性 */
|
||||
@Resource
|
||||
private ServerProperties serverProperties;
|
||||
|
||||
/** 令牌过期时间(单位:秒) */
|
||||
private Integer tokenExpiredOfSeconds;
|
||||
|
||||
/** 密码允许错误次数 */
|
||||
private Integer allowPasswdRetryTimes;
|
||||
|
||||
/** 本地化语言设置 */
|
||||
private String locale;
|
||||
|
||||
/** 是否显示 SQL 命令日志 */
|
||||
private boolean showSqlCommand;
|
||||
|
||||
/**
|
||||
* 设置全局变量。
|
||||
* <p>
|
||||
* @param prefixUrl 项目前缀 URL。
|
||||
* @param baseUrl 基础 URL。
|
||||
*/
|
||||
private static void setGlobalVars(String prefixUrl, String baseUrl) {
|
||||
PROJECT_PREFIX_URL = prefixUrl;
|
||||
BASEURL = baseUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取默认的 Locale 设置。
|
||||
* <p>
|
||||
* 根据配置的 `locale` 字符串解析为对应的 Locale,如果格式不正确,则返回系统默认的 {@link Locale#CHINA}。
|
||||
* <p>
|
||||
* @return 配置的 Locale 对象。
|
||||
*/
|
||||
public Locale getDefaultLocale() {
|
||||
// 按下划线分割
|
||||
String[] parts = locale.split("_");
|
||||
if (2 == parts.length) {
|
||||
return new Locale(parts[0], parts[1]);
|
||||
}
|
||||
// 如果格式不正确,则返回系统默认 Locale.CHINA
|
||||
return Locale.CHINA;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化全局变量。
|
||||
* <p>
|
||||
* 在 Spring 容器加载时,通过 @PostConstruct 注解调用该方法,
|
||||
* 完成全局变量的初始化和本地化设置。
|
||||
*/
|
||||
@PostConstruct
|
||||
private void initGlobalValue() {
|
||||
log.info("Current: tokenExpiredOfSeconds = {}, allowPasswdRetryTimes = {}, showSqlCommand = {}",
|
||||
tokenExpiredOfSeconds, allowPasswdRetryTimes, showSqlCommand);
|
||||
|
||||
String addr = "localhost";
|
||||
try {
|
||||
addr = InetAddress.getLocalHost().getHostAddress();
|
||||
} catch (Exception e) {
|
||||
log.error("Unable get local ip address: {}", e.getMessage());
|
||||
} finally {
|
||||
setGlobalVars(serverProperties.getServlet().getContextPath(),
|
||||
"http://" + addr + ":" + serverProperties.getPort() + serverProperties.getServlet().getContextPath());
|
||||
log.info("baseUrl: {}", BASEURL);
|
||||
}
|
||||
|
||||
Locale.setDefault(getDefaultLocale());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
package com.cmhi.magent.config;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
|
||||
import org.springframework.context.support.ResourceBundleMessageSource;
|
||||
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
|
||||
|
||||
/**
|
||||
* 国际化与本地化配置类。
|
||||
*
|
||||
* <p>该类负责配置 Spring 的国际化功能,包括消息资源文件的加载和校验消息的国际化支持。
|
||||
* 配置的资源文件路径和名称可以根据项目需求进行调整。
|
||||
* <p>
|
||||
* 项目中的国际化实现依赖于 Spring 的 {@code MessageSource} 和 JSR-303 校验框架。
|
||||
* 通过此配置类,开发者可以动态加载不同语言的文本信息,并支持多语言环境的表单校验消息。
|
||||
* </p>
|
||||
*
|
||||
* @author huangxin@cmhi.chinamobile.com
|
||||
* @version 1.0.0
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
@Configuration
|
||||
@Slf4j
|
||||
public class LocaleConfig {
|
||||
|
||||
/**
|
||||
* 配置主消息资源文件的加载。
|
||||
*
|
||||
* <p>该方法定义了一个 {@code ResourceBundleMessageSource} Bean 用于加载主要的国际化资源文件,
|
||||
* 如 `messages`、`errorMessage`、`enumMessage` 和 `syslogMessage` 等。资源文件位于 `i18n` 目录下。
|
||||
* </p>
|
||||
*
|
||||
* @return 配置好的 {@code ResourceBundleMessageSource} 实例。
|
||||
*/
|
||||
@Bean
|
||||
public ResourceBundleMessageSource messageSource() {
|
||||
ResourceBundleMessageSource source = new ResourceBundleMessageSource();
|
||||
// 设置国际化文件存储路径和资源文件的前缀名
|
||||
source.setBasenames("i18n/message", "i18n/errorMessage", "i18n/enumMessage", "i18n/syslogMessage");
|
||||
|
||||
// 当找不到对应的 key 值时,返回 key 本身作为默认信息
|
||||
source.setUseCodeAsDefaultMessage(true);
|
||||
|
||||
// 设置默认的字符编码为 UTF-8,支持多语言字符
|
||||
source.setDefaultEncoding("UTF-8");
|
||||
return source;
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置校验框架的消息国际化支持。
|
||||
*
|
||||
* <p>该方法定义了一个 {@code LocalValidatorFactoryBean} Bean,用于为 JSR-303 校验框架
|
||||
* 提供国际化支持。例如,可以将校验注解的错误消息动态地从国际化资源文件中获取。
|
||||
* </p>
|
||||
*
|
||||
* @return 配置好的 {@code LocalValidatorFactoryBean} 实例。
|
||||
*/
|
||||
@Bean
|
||||
public LocalValidatorFactoryBean localValidatorFactoryBean() {
|
||||
// 初始化校验工厂 Bean
|
||||
LocalValidatorFactoryBean localValidatorFactoryBean = new LocalValidatorFactoryBean();
|
||||
|
||||
// 使用 ReloadableResourceBundleMessageSource 动态加载国际化消息文件
|
||||
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
|
||||
// 设置校验消息的资源文件路径(i18n/validationMessage)
|
||||
messageSource.setBasename("i18n/validationMessage");
|
||||
// 设置消息文件的默认字符编码为 UTF-8
|
||||
messageSource.setDefaultEncoding("UTF-8");
|
||||
|
||||
// 将校验消息源设置到校验工厂 Bean 中
|
||||
localValidatorFactoryBean.setValidationMessageSource(messageSource);
|
||||
return localValidatorFactoryBean;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
package com.cmhi.magent.config;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.Setter;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 提供全局的 ObjectMapper 实例访问。
|
||||
*
|
||||
* <p>该类实现了 {@code ApplicationContextAware} 接口,用于从 Spring 容器中获取 ObjectMapper Bean。
|
||||
* 通过该类,可以方便地在需要的地方获取 ObjectMapper 实例,而不需要多次注入或手动创建。
|
||||
* </p>
|
||||
*
|
||||
* <p>ObjectMapper 是 Jackson 的核心类,用于处理 JSON 数据的序列化和反序列化。
|
||||
* 通过与 Spring 容器的集成,确保 ObjectMapper 使用了统一的配置。
|
||||
* </p>
|
||||
*
|
||||
* @author huangxin@cmhi.chinamobile.com
|
||||
* @version 1.0.0
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
@Component
|
||||
public class ObjectMapperProvider implements ApplicationContextAware {
|
||||
|
||||
/**
|
||||
* 应用上下文对象,用于获取 Spring 容器中的 Bean。
|
||||
*/
|
||||
@Setter
|
||||
private static ApplicationContext context;
|
||||
|
||||
/**
|
||||
* 设置应用上下文。
|
||||
*
|
||||
* <p>该方法由 Spring 容器在启动时调用,用于将 ApplicationContext 注入到当前类中。
|
||||
* </p>
|
||||
*
|
||||
* @param applicationContext Spring 应用上下文对象。
|
||||
*/
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) {
|
||||
setContext(applicationContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取全局的 ObjectMapper 实例。
|
||||
*
|
||||
* <p>该方法从 Spring 容器中获取 ObjectMapper Bean,确保所有地方使用的 ObjectMapper
|
||||
* 均为同一个实例,避免重复创建和配置不一致。
|
||||
* </p>
|
||||
*
|
||||
* @return 全局唯一的 ObjectMapper 实例。
|
||||
*/
|
||||
public static ObjectMapper getMapper() {
|
||||
return context.getBean(ObjectMapper.class);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
package com.cmhi.magent.config;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.PropertySource;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 项目 Git 版本信息配置类。
|
||||
*
|
||||
* <p>该类读取并存储项目在构建过程中生成的 Git 信息,包括提交 ID、提交时间、分支名称、
|
||||
* 构建时间等。Git 信息通过 `git.properties` 文件注入到 Spring 容器中。
|
||||
* </p>
|
||||
*
|
||||
* <p>此外,该类支持在项目启动时通过日志记录版本信息,方便追踪和调试。
|
||||
* </p>
|
||||
*
|
||||
* @author huangxin@cmhi.chinamobile.com
|
||||
* @version 1.0.0
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@Component
|
||||
@Slf4j
|
||||
@PropertySource("classpath:git.properties")
|
||||
public class ProjectGitVersionInfo {
|
||||
|
||||
/**
|
||||
* Git 提交 ID。
|
||||
*/
|
||||
@Value("${git.commit.id}")
|
||||
private String commitId;
|
||||
|
||||
/**
|
||||
* Git 提交描述信息。
|
||||
*/
|
||||
@Value("${git.commit.id.describe}")
|
||||
private String commitDescribe;
|
||||
|
||||
/**
|
||||
* Git 提交时间。
|
||||
*/
|
||||
@Value("${git.commit.time}")
|
||||
private String commitTime;
|
||||
|
||||
/**
|
||||
* Git 最近的标签名称。
|
||||
*/
|
||||
@Value("${git.closest.tag.name}")
|
||||
private String tagName;
|
||||
|
||||
/**
|
||||
* 构建时间。
|
||||
*/
|
||||
@Value("${git.build.time}")
|
||||
private String buildTime;
|
||||
|
||||
/**
|
||||
* 当前 Git 分支。
|
||||
*/
|
||||
@Value("${git.branch}")
|
||||
private String gitBranch;
|
||||
|
||||
/**
|
||||
* 初始化全局版本信息。
|
||||
*
|
||||
* <p>该方法在 Spring 容器完成依赖注入后自动调用,用于记录版本信息到日志中。
|
||||
* 通过组合构建时间、提交 ID、Git 分支和标签名称,生成完整的版本信息。
|
||||
* </p>
|
||||
*/
|
||||
@PostConstruct
|
||||
private void initGlobalValue() {
|
||||
// 组合版本信息
|
||||
String version = buildTime + " " + commitId + " " + gitBranch + " " + tagName;
|
||||
// 记录版本信息到日志
|
||||
log.info("Version: {}", version.trim());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
package com.cmhi.magent.config;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 协议配置类。
|
||||
*
|
||||
* <p>该类用于加载 `protocol` 前缀下的配置信息,并将部分配置值同步到全局静态变量中。
|
||||
* 配置内容包括时间戳校验开关、超时时间、加密类型和加密密钥等内容。
|
||||
* </p>
|
||||
*
|
||||
* <p>类中包含一个自动初始化方法,用于在 Spring 容器构建完成后,将配置值加载并初始化全局静态变量,
|
||||
* 从而方便在应用的其他地方直接访问协议相关的全局配置。
|
||||
* </p>
|
||||
*
|
||||
* @author huangxin@cmhi.chinamobile.com
|
||||
* @version 1.0.0
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
@Data
|
||||
@ConfigurationProperties(prefix = "protocol")
|
||||
@Configuration
|
||||
@Slf4j
|
||||
@SuppressWarnings({"java:S3008", "java:S1444", "java:S1104"})
|
||||
public class ProtocolConfigure {
|
||||
|
||||
/**
|
||||
* 是否校验时间戳的全局标志位。
|
||||
*/
|
||||
public static boolean CHECK_TIMESTAMP;
|
||||
|
||||
/**
|
||||
* 请求的全局超时时间(单位:秒)。
|
||||
*/
|
||||
public static long TIMEOUT_OF_SECONDS;
|
||||
|
||||
/**
|
||||
* 安全协议类型的全局标志位。
|
||||
*/
|
||||
public static int SECURITY_PROTOCOL_TYPE;
|
||||
|
||||
/**
|
||||
* 是否校验时间戳。
|
||||
*/
|
||||
private Boolean checkTimestamp;
|
||||
|
||||
/**
|
||||
* 请求的超时时间(单位:秒)。
|
||||
*/
|
||||
private Integer timeoutOfSeconds;
|
||||
|
||||
/**
|
||||
* 加密类型。
|
||||
*/
|
||||
private Integer cryptoType;
|
||||
|
||||
/**
|
||||
* 加密密钥。
|
||||
*/
|
||||
private String cryptoKey;
|
||||
|
||||
/**
|
||||
* 设置全局静态变量的值。
|
||||
*
|
||||
* <p>通过将配置值赋值给静态变量,便于应用中其他非 Spring 管理的类或组件访问这些全局配置信息。
|
||||
* </p>
|
||||
*
|
||||
* @param chkTimeStamp 是否校验时间戳。
|
||||
* @param timeOfSecond 超时时间(单位:秒)。
|
||||
* @param secProType 安全协议类型。
|
||||
*/
|
||||
private static void setGlobalVars(boolean chkTimeStamp, long timeOfSecond, int secProType) {
|
||||
ProtocolConfigure.CHECK_TIMESTAMP = chkTimeStamp;
|
||||
ProtocolConfigure.TIMEOUT_OF_SECONDS = timeOfSecond;
|
||||
ProtocolConfigure.SECURITY_PROTOCOL_TYPE = secProType;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化全局静态变量。
|
||||
*
|
||||
* <p>该方法在 Spring 容器构建完成后自动调用,用于从配置中加载值,并初始化全局静态变量。
|
||||
* 如果某些配置值为空,则提供默认值:时间戳校验默认为 `false`,超时时间默认为 `30` 秒,
|
||||
* 加密类型默认为 `0`。
|
||||
* </p>
|
||||
*/
|
||||
@PostConstruct
|
||||
private void initGlobalValue() {
|
||||
log.info("Current: checkTimestamp = {}, timeoutOfSeconds = {}", checkTimestamp, timeoutOfSeconds);
|
||||
|
||||
setGlobalVars(Optional.ofNullable(checkTimestamp).orElse(false), Optional.ofNullable(timeoutOfSeconds).orElse(30),
|
||||
Optional.ofNullable(cryptoType).orElse(0));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取超时时间(单位:毫秒)。
|
||||
*
|
||||
* <p>通过将配置的超时时间从秒转换为毫秒,便于需要毫秒级时间的逻辑使用。
|
||||
* 如果超时时间未配置,默认为 `30000` 毫秒(即 30 秒)。
|
||||
* </p>
|
||||
*
|
||||
* @return 超时时间(单位:毫秒)。
|
||||
*/
|
||||
public Long getTimeoutOfMillisecond() {
|
||||
return (long) (timeoutOfSeconds * 1000);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
package com.cmhi.magent.controller;
|
||||
|
||||
import com.cmhi.magent.annotation.DecryptionProtocol;
|
||||
import com.cmhi.magent.annotation.EncryptionProtocol;
|
||||
import com.cmhi.magent.annotation.OperationLogAnnotation;
|
||||
import com.cmhi.magent.config.ProjectGitVersionInfo;
|
||||
import com.cmhi.magent.pojo.po.VersionInfo;
|
||||
import com.cmhi.magent.pojo.vo.ProtocolResp;
|
||||
import com.cmhi.magent.pojo.vo.VersionResp;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* 通用接口控制器。
|
||||
*
|
||||
* <p>该控制器包含通用的系统接口,例如获取版本信息接口。
|
||||
* 支持加密与解密协议,并记录操作日志。</p>
|
||||
*
|
||||
* <p>接口描述:</p>
|
||||
* <ul>
|
||||
* <li>POST /api/version:获取系统的版本信息,带加解密支持。</li>
|
||||
* <li>GET /api/version:获取系统版本信息的另一个版本(示例)。</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author huangxin@cmhi.chinamobile.com
|
||||
* @version 1.0.0
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
@RestController
|
||||
@Slf4j
|
||||
@Tag(name = "通用接口")
|
||||
@RequestMapping(value = "/api")
|
||||
public class CommonFrameworkApi {
|
||||
|
||||
/**
|
||||
* 项目版本信息配置。
|
||||
*
|
||||
* <p>{@link ProjectGitVersionInfo} 提供了系统版本信息的配置数据,
|
||||
* 包括 Git 提交 ID、提交描述、构建时间、分支名称等。</p>
|
||||
*/
|
||||
@Resource
|
||||
private ProjectGitVersionInfo prgVer;
|
||||
|
||||
/**
|
||||
* 获取系统版本信息(POST 请求)。
|
||||
*
|
||||
* <p>该接口返回系统的版本信息,包括提交 ID、描述、时间、标签名称、构建时间以及分支名称。
|
||||
* 接口支持加密和解密协议,并记录操作日志。</p>
|
||||
*
|
||||
* @return 封装了版本信息的响应对象。
|
||||
*/
|
||||
@EncryptionProtocol
|
||||
@DecryptionProtocol
|
||||
@PostMapping("/version")
|
||||
@OperationLogAnnotation(OperationModule = "SYSLOG_MOD_COMMON",
|
||||
OperationType = "SYSLOG_TYPE_READ",
|
||||
OperationDesc = "SYSLOG_DESC_GET_VERSION")
|
||||
public ProtocolResp<VersionResp> getVersion() {
|
||||
return ProtocolResp.result(VersionResp.builder().version(VersionInfo.builder()
|
||||
.commitId(prgVer.getCommitId())
|
||||
.commitDescribe(prgVer.getCommitDescribe())
|
||||
.commitTime(prgVer.getCommitTime())
|
||||
.tagName(prgVer.getTagName())
|
||||
.buildTime(prgVer.getBuildTime())
|
||||
.gitBranch(prgVer.getGitBranch())
|
||||
.build()).build());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取系统版本信息(GET 请求)。
|
||||
*
|
||||
* <p>该接口提供系统版本信息,与 POST 接口返回的数据相同。
|
||||
* 支持加密协议和操作日志记录。</p>
|
||||
*
|
||||
* @return 封装了版本信息的响应对象。
|
||||
*/
|
||||
@GetMapping("/version")
|
||||
@EncryptionProtocol
|
||||
@OperationLogAnnotation(OperationModule = "SYSLOG_MOD_COMMON",
|
||||
OperationType = "SYSLOG_TYPE_READ",
|
||||
OperationDesc = "SYSLOG_DESC_GET_VERSION")
|
||||
public ProtocolResp<VersionResp> getVersionV2() {
|
||||
return ProtocolResp.result(VersionResp.builder().version(VersionInfo.builder()
|
||||
.commitId(prgVer.getCommitId())
|
||||
.commitDescribe(prgVer.getCommitDescribe())
|
||||
.commitTime(prgVer.getCommitTime())
|
||||
.tagName(prgVer.getTagName())
|
||||
.buildTime(prgVer.getBuildTime())
|
||||
.gitBranch(prgVer.getGitBranch())
|
||||
.build()).build());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,119 @@
|
|||
package com.cmhi.magent.controller;
|
||||
|
||||
import com.cmhi.magent.annotation.EncryptionProtocol;
|
||||
import com.cmhi.magent.annotation.OperationLogAnnotation;
|
||||
import com.cmhi.magent.pojo.vo.GetHwInfoResp;
|
||||
import com.cmhi.magent.pojo.vo.GetOsInfoResp;
|
||||
import com.cmhi.magent.pojo.vo.GetProcessorInfoResp;
|
||||
import com.cmhi.magent.pojo.vo.ProtocolResp;
|
||||
import com.cmhi.magent.service.SystemInfoService;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
|
||||
/**
|
||||
* 系统信息接口控制器。
|
||||
*
|
||||
* <p>该控制器包含 3 个接口,用于获取操作系统信息、处理器信息和硬件信息。
|
||||
* 所有接口均支持加密协议(通过 {@code @EncryptionProtocol} 注解)以及操作日志记录(通过 {@code @OperationLogAnnotation} 注解)。</p>
|
||||
*
|
||||
* <p>接口描述:</p>
|
||||
* <ul>
|
||||
* <li>GET /osInfo:获取操作系统的相关信息。</li>
|
||||
* <li>GET /processorInfo:获取处理器的相关信息。</li>
|
||||
* <li>GET /hwInfo:获取硬件的相关信息。</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author huangxin@cmhi.chinamobile.com
|
||||
* @version 1.0.0
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
@RestController
|
||||
@Slf4j
|
||||
@Tag(name = "系统信息接口")
|
||||
@RequestMapping(value = "/api/systemInfo")
|
||||
public class SystemInfoApi {
|
||||
|
||||
/**
|
||||
* 系统信息服务类。
|
||||
*
|
||||
* <p>通过该服务类调用底层逻辑,获取操作系统信息、处理器信息和硬件信息。</p>
|
||||
*/
|
||||
@Resource
|
||||
private SystemInfoService systemInfoService;
|
||||
|
||||
/**
|
||||
* 获取操作系统信息。
|
||||
*
|
||||
* <p>该接口返回操作系统名称以及系统启动时间。返回数据通过加密协议进行保护,
|
||||
* 并记录操作日志。</p>
|
||||
*
|
||||
* @return 封装了操作系统信息的响应对象。
|
||||
*/
|
||||
@GetMapping("/osInfo")
|
||||
@EncryptionProtocol
|
||||
@OperationLogAnnotation(
|
||||
OperationModule = "SYSLOG_MOD_SYSINFO",
|
||||
OperationType = "SYSLOG_TYPE_READ",
|
||||
OperationDesc = "SYSLOG_DESC_GET_OS_INFO"
|
||||
)
|
||||
public ProtocolResp<GetOsInfoResp> getOsInfo() {
|
||||
return ProtocolResp.result(
|
||||
GetOsInfoResp.builder()
|
||||
.os(systemInfoService.getOsName())
|
||||
.bootTime(new Timestamp(systemInfoService.getOsBootTimeStamp()))
|
||||
.build()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取处理器信息。
|
||||
*
|
||||
* <p>该接口返回处理器的相关信息(如 CPU 详情)。返回数据通过加密协议进行保护,
|
||||
* 并记录操作日志。</p>
|
||||
*
|
||||
* @return 封装了处理器信息的响应对象。
|
||||
*/
|
||||
@GetMapping("/processorInfo")
|
||||
@EncryptionProtocol
|
||||
@OperationLogAnnotation(
|
||||
OperationModule = "SYSLOG_MOD_SYSINFO",
|
||||
OperationType = "SYSLOG_TYPE_READ",
|
||||
OperationDesc = "SYSLOG_DESC_GET_CPU_INFO"
|
||||
)
|
||||
public ProtocolResp<GetProcessorInfoResp> getProcessorInfo() {
|
||||
return ProtocolResp.result(
|
||||
GetProcessorInfoResp.builder()
|
||||
.cpuInfo(systemInfoService.getProcessInfo())
|
||||
.build()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取硬件信息。
|
||||
*
|
||||
* <p>该接口返回系统硬件的相关信息。返回数据通过加密协议进行保护,
|
||||
* 并记录操作日志。</p>
|
||||
*
|
||||
* @return 封装了硬件信息的响应对象。
|
||||
*/
|
||||
@GetMapping("/hwInfo")
|
||||
@EncryptionProtocol
|
||||
@OperationLogAnnotation(
|
||||
OperationModule = "SYSLOG_MOD_SYSINFO",
|
||||
OperationType = "SYSLOG_TYPE_READ",
|
||||
OperationDesc = "SYSLOG_DESC_GET_HW_INFO"
|
||||
)
|
||||
public ProtocolResp<GetHwInfoResp> getHwInfo() {
|
||||
return ProtocolResp.result(
|
||||
GetHwInfoResp.builder()
|
||||
.hwInfo(systemInfoService.getHardwareInfo())
|
||||
.build()
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
package com.cmhi.magent.crypto;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpInputMessage;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* 解密请求协议类。
|
||||
*
|
||||
* <p>该类实现了 {@link HttpInputMessage} 接口,用于对传入的 HTTP 请求消息进行解密处理。</p>
|
||||
*
|
||||
* <p>解密后的内容可通过 {@link #getBody()} 方法获取,同时保留了原始的请求头。</p>
|
||||
*
|
||||
* <p>注意:该类依赖于 Apache Commons IO 和 Lombok 库。</p>
|
||||
*
|
||||
* @author huangxin@cmhi.chinamobile.com
|
||||
* @version 1.0.0
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
@Slf4j
|
||||
public class DecryptRequestProtocol implements HttpInputMessage {
|
||||
/**
|
||||
* 原始的 HTTP 输入消息
|
||||
*/
|
||||
private final HttpInputMessage inputMessage;
|
||||
|
||||
/**
|
||||
* 解密后的消息内容
|
||||
*/
|
||||
private final String msgContent;
|
||||
|
||||
/**
|
||||
* 构造方法。
|
||||
*
|
||||
* @param inputMessage 原始的 {@link HttpInputMessage} 对象
|
||||
* @param msgContent 解密后的消息内容
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
@Contract(pure = true)
|
||||
public DecryptRequestProtocol(HttpInputMessage inputMessage, String msgContent) {
|
||||
this.inputMessage = inputMessage;
|
||||
this.msgContent = msgContent;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取解密后的请求体。
|
||||
*
|
||||
* @return 解密后的 {@link InputStream} 对象
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
@Override
|
||||
@NotNull
|
||||
public InputStream getBody() {
|
||||
// 将解密后的消息内容转换为 InputStream 并返回
|
||||
return IOUtils.toInputStream(msgContent, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取原始的 HTTP 请求头。
|
||||
*
|
||||
* @return 原始的 {@link HttpHeaders} 对象
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
@Override
|
||||
@NotNull
|
||||
public HttpHeaders getHeaders() {
|
||||
// 返回原始 HTTP 输入消息的头信息
|
||||
return inputMessage.getHeaders();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,134 @@
|
|||
package com.cmhi.magent.crypto;
|
||||
|
||||
import com.cmhi.magent.annotation.DecryptionProtocol;
|
||||
import com.cmhi.magent.service.ProtocolSecurityService;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.http.HttpInputMessage;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
/**
|
||||
* 请求协议安全处理类。
|
||||
*
|
||||
* <p>该类实现了 {@link RequestBodyAdvice} 接口,用于在读取请求体之前对其进行自定义处理。</p>
|
||||
*
|
||||
* <p>功能:</p>
|
||||
* <ul>
|
||||
* <li>判断是否需要对请求内容进行解密处理</li>
|
||||
* <li>在读取请求体之前调用自定义解密逻辑</li>
|
||||
* <li>支持空请求体和解密后的请求体的处理</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>在使用该类时,需要在控制器类或方法上添加 {@link DecryptionProtocol} 注解,以启用解密功能。</p>
|
||||
*
|
||||
* @author huangxin@cmhi.chinamobile.com
|
||||
* @version 1.0.0
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
@Slf4j
|
||||
@RestControllerAdvice
|
||||
public class RequestProtocolSecurity implements RequestBodyAdvice {
|
||||
|
||||
/**
|
||||
* 协议安全服务,用于处理解密逻辑。
|
||||
*/
|
||||
@Resource
|
||||
private ProtocolSecurityService protocolSecurityService;
|
||||
|
||||
/**
|
||||
* 判断是否支持解密逻辑。
|
||||
*
|
||||
* <p>当目标类或方法上存在 {@link DecryptionProtocol} 注解时,启用解密功能。</p>
|
||||
*
|
||||
* @param methodParameter 方法参数信息
|
||||
* @param type 请求体的类型
|
||||
* @param aClass 消息转换器的类
|
||||
* @return 如果需要解密返回 true;否则返回 false
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
@Override
|
||||
public boolean supports(@NotNull MethodParameter methodParameter,
|
||||
@NotNull Type type,
|
||||
@NotNull Class<? extends HttpMessageConverter<?>> aClass) {
|
||||
// 检查类级别或方法级别上是否存在 @DecryptionProtocol 注解
|
||||
return methodParameter.getContainingClass().isAnnotationPresent(DecryptionProtocol.class)
|
||||
|| methodParameter.hasMethodAnnotation(DecryptionProtocol.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 在读取请求体之前执行解密逻辑。
|
||||
*
|
||||
* <p>调用 {@link ProtocolSecurityService#decryptProtocol(HttpInputMessage)} 对请求体进行解密。</p>
|
||||
*
|
||||
* @param httpInputMessage 原始 HTTP 请求体
|
||||
* @param methodParameter 方法参数信息
|
||||
* @param type 请求体的类型
|
||||
* @param aClass 消息转换器的类
|
||||
* @return 包含解密后数据的 {@link HttpInputMessage}
|
||||
* @throws IOException 如果解密过程中发生 I/O 错误
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
@Override
|
||||
@NotNull
|
||||
public HttpInputMessage beforeBodyRead(@NotNull HttpInputMessage httpInputMessage,
|
||||
@NotNull MethodParameter methodParameter,
|
||||
@NotNull Type type,
|
||||
@NotNull Class<? extends HttpMessageConverter<?>> aClass) throws IOException {
|
||||
// 调用协议安全服务进行解密,并返回解密后的 HttpInputMessage
|
||||
return protocolSecurityService.decryptProtocol(httpInputMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理空请求体的逻辑。
|
||||
*
|
||||
* <p>如果请求体为空,则默认直接返回空对象。</p>
|
||||
*
|
||||
* @param o 原始请求体对象(通常为 null)
|
||||
* @param httpInputMessage 原始 HTTP 请求体
|
||||
* @param methodParameter 方法参数信息
|
||||
* @param type 请求体的类型
|
||||
* @param aClass 消息转换器的类
|
||||
* @return 处理后的对象(如果请求体为空,则返回原始对象)
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
@Override
|
||||
public Object handleEmptyBody(Object o,
|
||||
@NotNull HttpInputMessage httpInputMessage,
|
||||
@NotNull MethodParameter methodParameter,
|
||||
@NotNull Type type,
|
||||
@NotNull Class<? extends HttpMessageConverter<?>> aClass) {
|
||||
// 对空请求体无特殊处理,直接返回空对象
|
||||
return o;
|
||||
}
|
||||
|
||||
/**
|
||||
* 在读取请求体之后的处理逻辑。
|
||||
*
|
||||
* <p>读取请求体完成后,直接返回解密后的对象。</p>
|
||||
*
|
||||
* @param o 解密后的请求体对象
|
||||
* @param httpInputMessage 原始 HTTP 请求体
|
||||
* @param methodParameter 方法参数信息
|
||||
* @param type 请求体的类型
|
||||
* @param aClass 消息转换器的类
|
||||
* @return 解密后的请求体对象
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
@Override
|
||||
@NotNull
|
||||
public Object afterBodyRead(@NotNull Object o,
|
||||
@NotNull HttpInputMessage httpInputMessage,
|
||||
@NotNull MethodParameter methodParameter,
|
||||
@NotNull Type type,
|
||||
@NotNull Class<? extends HttpMessageConverter<?>> aClass) {
|
||||
// 读取请求体完成后,直接返回解密后的请求体对象
|
||||
return o;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
package com.cmhi.magent.crypto;
|
||||
|
||||
import com.cmhi.magent.annotation.EncryptionProtocol;
|
||||
import com.cmhi.magent.common.ProtoCryptoType;
|
||||
import com.cmhi.magent.config.ProtocolConfigure;
|
||||
import com.cmhi.magent.pojo.vo.ProtocolResp;
|
||||
import com.cmhi.magent.service.ProtocolSecurityService;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.server.ServerHttpRequest;
|
||||
import org.springframework.http.server.ServerHttpResponse;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 响应协议安全处理类。
|
||||
*
|
||||
* <p>该类实现了 {@link ResponseBodyAdvice} 接口,用于对响应体在返回客户端之前进行自定义处理。</p>
|
||||
*
|
||||
* <p>功能:</p>
|
||||
* <ul>
|
||||
* <li>判断响应是否需要加密处理</li>
|
||||
* <li>根据配置动态决定加密逻辑,支持无加密模式(CRYPTO_NONE)</li>
|
||||
* <li>设置 HTTP 状态码以匹配 ProtocolResp 的响应码</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>在使用该类时,需要在控制器类或方法上添加 {@link EncryptionProtocol} 注解,以启用加密功能。</p>
|
||||
*
|
||||
* @author huangxin@cmhi.chinamobile.com
|
||||
* @version 1.0.0
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
@Slf4j
|
||||
@RestControllerAdvice
|
||||
public class ResponseProtocolSecurity implements ResponseBodyAdvice<Object> {
|
||||
|
||||
/**
|
||||
* 协议安全服务,用于处理加密逻辑。
|
||||
*/
|
||||
@Resource
|
||||
private ProtocolSecurityService protocolSecurityService;
|
||||
|
||||
/**
|
||||
* 协议配置服务,用于获取当前加密类型。
|
||||
*/
|
||||
@Resource
|
||||
private ProtocolConfigure protocolConfigure;
|
||||
|
||||
/**
|
||||
* 判断是否支持加密逻辑。
|
||||
*
|
||||
* <p>当目标类或方法上存在 {@link EncryptionProtocol} 注解时,启用加密功能。</p>
|
||||
*
|
||||
* @param methodParameter 方法参数信息
|
||||
* @param aClass 消息转换器的类
|
||||
* @return 如果需要加密返回 true;否则返回 false
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
@Override
|
||||
public boolean supports(@NotNull MethodParameter methodParameter,
|
||||
@NotNull Class<? extends HttpMessageConverter<?>> aClass) {
|
||||
// 检查类级别或方法级别上是否存在 @EncryptionProtocol 注解
|
||||
return methodParameter.getContainingClass().isAnnotationPresent(EncryptionProtocol.class)
|
||||
|| methodParameter.hasMethodAnnotation(EncryptionProtocol.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 在写入响应体之前执行加密逻辑。
|
||||
*
|
||||
* <p>根据配置的加密类型对响应体进行加密处理,同时设置 HTTP 状态码。</p>
|
||||
*
|
||||
* @param o 原始响应体对象
|
||||
* @param methodParameter 方法参数信息
|
||||
* @param mediaType 响应的媒体类型
|
||||
* @param aClass 消息转换器的类
|
||||
* @param serverHttpRequest HTTP 请求信息
|
||||
* @param serverHttpResponse HTTP 响应信息
|
||||
* @return 加密后的响应体对象(或原始对象)
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
@Override
|
||||
@NotNull
|
||||
public Object beforeBodyWrite(Object o,
|
||||
@NotNull MethodParameter methodParameter,
|
||||
@NotNull MediaType mediaType,
|
||||
@NotNull Class<? extends HttpMessageConverter<?>> aClass,
|
||||
@NotNull ServerHttpRequest serverHttpRequest,
|
||||
@NotNull ServerHttpResponse serverHttpResponse) {
|
||||
// 如果配置为 "无加密模式",直接返回原始响应
|
||||
if (Objects.equals(protocolConfigure.getCryptoType(), ProtoCryptoType.CRYPTO_NONE.getValue())) {
|
||||
if (o instanceof ProtocolResp) {
|
||||
// 设置 HTTP 响应状态码
|
||||
serverHttpResponse.setStatusCode(org.springframework.http.HttpStatus.valueOf(((ProtocolResp<?>) o).getCode()));
|
||||
}
|
||||
return o;
|
||||
} else {
|
||||
// 调用协议安全服务进行加密,返回加密后的 ProtocolResp
|
||||
ProtocolResp<String> rspInfo = protocolSecurityService.encryptProtocol(o, protocolConfigure.getCryptoType());
|
||||
// 设置 HTTP 响应状态码
|
||||
serverHttpResponse.setStatusCode(org.springframework.http.HttpStatus.valueOf(rspInfo.getCode()));
|
||||
return rspInfo;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,240 @@
|
|||
package com.cmhi.magent.crypto.arithmetic;
|
||||
|
||||
import javax.crypto.BadPaddingException;
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.IllegalBlockSizeException;
|
||||
import javax.crypto.KeyGenerator;
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Base64;
|
||||
|
||||
/**
|
||||
* 加密与解密工具类。
|
||||
*
|
||||
* <p>提供以下功能:</p>
|
||||
* <ul>
|
||||
* <li>Base64 的编码与解码</li>
|
||||
* <li>SHA-256 和 MD5 哈希算法</li>
|
||||
* <li>AES(128 和 256 位)加密与解密</li>
|
||||
* <li>DES 加密与解密</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>注意:所有方法均为静态,无法实例化该类。</p>
|
||||
*
|
||||
* @author
|
||||
* huangxin@cmhi.chinamobile.com
|
||||
* @version 1.0.0
|
||||
* @since 2023-10
|
||||
*/
|
||||
@SuppressWarnings({"java:S5542", "java:S5547", "java:S4790"})
|
||||
public class CryptoHelper {
|
||||
|
||||
private static final String AES_ALGORITHM_STR = "AES/ECB/PKCS5Padding";
|
||||
private static final String DES_ALGORITHM_STR = "DES/ECB/PKCS5Padding";
|
||||
|
||||
private static final String SHA1_PRNG = "SHA1PRNG";
|
||||
|
||||
/**
|
||||
* 私有化构造方法以防止实例化。
|
||||
*/
|
||||
private CryptoHelper() {
|
||||
throw new AssertionError("Instantiating utility class.");
|
||||
}
|
||||
|
||||
/**
|
||||
* 解码 Base64 编码的字符串。
|
||||
*
|
||||
* @param ciphertext Base64 编码的密文
|
||||
* @return 解码后的字节数组
|
||||
*/
|
||||
public static byte[] base64Decryption(String ciphertext) {
|
||||
return Base64.getDecoder().decode(ciphertext);
|
||||
}
|
||||
|
||||
/**
|
||||
* 对字节数组进行 Base64 编码。
|
||||
*
|
||||
* @param plaintext 明文字节数组
|
||||
* @return Base64 编码后的字符串
|
||||
*/
|
||||
public static String base64Encryption(byte[] plaintext) {
|
||||
return Base64.getEncoder().encodeToString(plaintext);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用 SHA-256 哈希算法对文本进行哈希处理。
|
||||
*
|
||||
* @param plaintext 输入的明文字符串
|
||||
* @return 哈希后的字节数组
|
||||
* @throws NoSuchAlgorithmException 如果系统不支持 SHA-256 算法
|
||||
*/
|
||||
public static byte[] sha256Encryption(String plaintext) throws NoSuchAlgorithmException {
|
||||
MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
|
||||
messageDigest.update(plaintext.getBytes(StandardCharsets.UTF_8));
|
||||
return messageDigest.digest();
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用 MD5 哈希算法对文本进行哈希处理,并返回 Base64 编码的结果。
|
||||
*
|
||||
* @param plaintext 输入的明文字符串
|
||||
* @return MD5 哈希后的 Base64 字符串
|
||||
* @throws NoSuchAlgorithmException 如果系统不支持 MD5 算法
|
||||
*/
|
||||
public static String md5Encryption(String plaintext) throws NoSuchAlgorithmException {
|
||||
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
|
||||
messageDigest.update(plaintext.getBytes(StandardCharsets.UTF_8));
|
||||
return base64Encryption(messageDigest.digest());
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用 AES-128 加密字节数组。
|
||||
*
|
||||
* @param plaintext 明文字节数组
|
||||
* @param aesKey AES 密钥字符串
|
||||
* @return 加密后的字节数组
|
||||
* @throws NoSuchAlgorithmException 如果系统不支持 AES 算法
|
||||
* @throws NoSuchPaddingException 如果填充机制不可用
|
||||
* @throws InvalidKeyException 如果密钥无效
|
||||
* @throws BadPaddingException 如果填充无效
|
||||
* @throws IllegalBlockSizeException 如果数据块大小不合法
|
||||
*/
|
||||
public static byte[] aes128Encryption(byte[] plaintext,
|
||||
String aesKey) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException,
|
||||
BadPaddingException, IllegalBlockSizeException {
|
||||
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
|
||||
SecureRandom secureRandom = SecureRandom.getInstance(SHA1_PRNG);
|
||||
secureRandom.setSeed(aesKey.getBytes());
|
||||
|
||||
keyGen.init(128, secureRandom);
|
||||
Cipher cipher = Cipher.getInstance(AES_ALGORITHM_STR);
|
||||
SecretKeySpec key = new SecretKeySpec(keyGen.generateKey().getEncoded(), "AES");
|
||||
cipher.init(Cipher.ENCRYPT_MODE, key);
|
||||
return cipher.doFinal(plaintext);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用 AES-128 解密字节数组。
|
||||
*
|
||||
* @param ciphertext 密文字节数组
|
||||
* @param aesKey AES 密钥字符串
|
||||
* @return 解密后的字节数组
|
||||
* @throws NoSuchAlgorithmException 如果系统不支持 AES 算法
|
||||
* @throws NoSuchPaddingException 如果填充机制不可用
|
||||
* @throws InvalidKeyException 如果密钥无效
|
||||
* @throws BadPaddingException 如果填充无效
|
||||
* @throws IllegalBlockSizeException 如果数据块大小不合法
|
||||
*/
|
||||
public static byte[] aes128Decryption(byte[] ciphertext,
|
||||
String aesKey) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException,
|
||||
BadPaddingException, IllegalBlockSizeException {
|
||||
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
|
||||
SecureRandom secureRandom = SecureRandom.getInstance(SHA1_PRNG);
|
||||
secureRandom.setSeed(aesKey.getBytes());
|
||||
|
||||
keyGen.init(128, secureRandom);
|
||||
Cipher cipher = Cipher.getInstance(AES_ALGORITHM_STR);
|
||||
SecretKeySpec key = new SecretKeySpec(keyGen.generateKey().getEncoded(), "AES");
|
||||
cipher.init(Cipher.DECRYPT_MODE, key);
|
||||
return cipher.doFinal(ciphertext);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用 AES-256 加密字节数组。
|
||||
*
|
||||
* @param plaintext 明文字节数组
|
||||
* @param aesKey AES 密钥字符串
|
||||
* @return 加密后的字节数组
|
||||
* @throws NoSuchAlgorithmException 如果系统不支持 AES 算法
|
||||
* @throws NoSuchPaddingException 如果填充机制不可用
|
||||
* @throws InvalidKeyException 如果密钥无效
|
||||
* @throws BadPaddingException 如果填充无效
|
||||
* @throws IllegalBlockSizeException 如果数据块大小不合法
|
||||
*/
|
||||
public static byte[] aes256Encryption(byte[] plaintext,
|
||||
String aesKey) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException,
|
||||
BadPaddingException, IllegalBlockSizeException {
|
||||
Cipher cipher = Cipher.getInstance(AES_ALGORITHM_STR);
|
||||
SecretKeySpec key = new SecretKeySpec(sha256Encryption(aesKey), "AES");
|
||||
cipher.init(Cipher.ENCRYPT_MODE, key);
|
||||
return cipher.doFinal(plaintext);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用 AES-256 解密字节数组。
|
||||
*
|
||||
* @param ciphertext 密文字节数组
|
||||
* @param aesKey AES 密钥字符串
|
||||
* @return 解密后的字节数组
|
||||
* @throws NoSuchAlgorithmException 如果系统不支持 AES 算法
|
||||
* @throws NoSuchPaddingException 如果填充机制不可用
|
||||
* @throws InvalidKeyException 如果密钥无效
|
||||
* @throws BadPaddingException 如果填充无效
|
||||
* @throws IllegalBlockSizeException 如果数据块大小不合法
|
||||
*/
|
||||
public static byte[] aes256Decryption(byte[] ciphertext,
|
||||
String aesKey) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException,
|
||||
BadPaddingException, IllegalBlockSizeException {
|
||||
Cipher cipher = Cipher.getInstance(AES_ALGORITHM_STR);
|
||||
SecretKeySpec key = new SecretKeySpec(sha256Encryption(aesKey), "AES");
|
||||
cipher.init(Cipher.DECRYPT_MODE, key);
|
||||
return cipher.doFinal(ciphertext);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用 DES 加密字节数组。
|
||||
*
|
||||
* @param plaintext 明文字节数组
|
||||
* @param desKey DES 密钥字符串
|
||||
* @return 加密后的字节数组
|
||||
* @throws NoSuchAlgorithmException 如果系统不支持 DES 算法
|
||||
* @throws NoSuchPaddingException 如果填充机制不可用
|
||||
* @throws InvalidKeyException 如果密钥无效
|
||||
* @throws BadPaddingException 如果填充无效
|
||||
* @throws IllegalBlockSizeException 如果数据块大小不合法
|
||||
*/
|
||||
public static byte[] desEncryption(byte[] plaintext,
|
||||
String desKey) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException,
|
||||
BadPaddingException, IllegalBlockSizeException {
|
||||
KeyGenerator keyGen = KeyGenerator.getInstance("DES");
|
||||
SecureRandom secureRandom = SecureRandom.getInstance(SHA1_PRNG);
|
||||
secureRandom.setSeed(desKey.getBytes());
|
||||
|
||||
keyGen.init(56, secureRandom);
|
||||
Cipher cipher = Cipher.getInstance(DES_ALGORITHM_STR);
|
||||
SecretKeySpec key = new SecretKeySpec(keyGen.generateKey().getEncoded(), "DES");
|
||||
cipher.init(Cipher.ENCRYPT_MODE, key);
|
||||
return cipher.doFinal(plaintext);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用 DES 解密字节数组。
|
||||
*
|
||||
* @param ciphertext 密文字节数组
|
||||
* @param desKey DES 密钥字符串
|
||||
* @return 解密后的字节数组
|
||||
* @throws NoSuchAlgorithmException 如果系统不支持 DES 算法
|
||||
* @throws NoSuchPaddingException 如果填充机制不可用
|
||||
* @throws InvalidKeyException 如果密钥无效
|
||||
* @throws BadPaddingException 如果填充无效
|
||||
* @throws IllegalBlockSizeException 如果数据块大小不合法
|
||||
*/
|
||||
public static byte[] desDecryption(byte[] ciphertext,
|
||||
String desKey) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException,
|
||||
BadPaddingException, IllegalBlockSizeException {
|
||||
KeyGenerator keyGen = KeyGenerator.getInstance("DES");
|
||||
SecureRandom secureRandom = SecureRandom.getInstance(SHA1_PRNG);
|
||||
secureRandom.setSeed(desKey.getBytes());
|
||||
|
||||
keyGen.init(56, secureRandom);
|
||||
Cipher cipher = Cipher.getInstance(DES_ALGORITHM_STR);
|
||||
SecretKeySpec key = new SecretKeySpec(keyGen.generateKey().getEncoded(), "DES");
|
||||
cipher.init(Cipher.DECRYPT_MODE, key);
|
||||
return cipher.doFinal(ciphertext);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
package com.cmhi.magent.exception;
|
||||
|
||||
import com.cmhi.magent.common.ErrorCode;
|
||||
|
||||
/**
|
||||
* 通用错误码异常类。
|
||||
*
|
||||
* <p>该异常类封装了错误码({@link ErrorCode})以及可选的描述信息,便于在业务中统一处理异常。</p>
|
||||
*
|
||||
* <p>开发者可以通过如下方式使用该类:</p>
|
||||
* <pre>
|
||||
* throw new CommonErrorCodeException(ErrorCode.INVALID_PARAMETER, "Invalid input provided.");
|
||||
* </pre>
|
||||
*
|
||||
* <p>支持以下三种构造方式:</p>
|
||||
* <ul>
|
||||
* <li>仅通过错误码实例化异常,默认使用错误码中的描述信息。</li>
|
||||
* <li>通过错误码和自定义描述信息实例化异常。</li>
|
||||
* <li>通过错误码和多条描述信息实例化异常。</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author huangxin@cmhi.chinamobile.com
|
||||
* @version 1.0.0
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
@SuppressWarnings("java:S1068")
|
||||
public class CommonErrorCodeException extends RuntimeException {
|
||||
|
||||
/**
|
||||
* 错误码实例,表示具体的错误类型。
|
||||
*/
|
||||
private final ErrorCode err;
|
||||
|
||||
/**
|
||||
* 错误描述信息,可以包含单条或多条描述内容。
|
||||
*/
|
||||
private final String[] description;
|
||||
|
||||
/**
|
||||
* 使用错误码实例化异常。
|
||||
*
|
||||
* <p>该构造方法会自动使用 {@link ErrorCode#getDescription()} 作为异常的描述信息。</p>
|
||||
*
|
||||
* @param err 错误码 {@link ErrorCode}
|
||||
*/
|
||||
public CommonErrorCodeException(ErrorCode err) {
|
||||
super(err.getDescription());
|
||||
this.err = err;
|
||||
this.description = new String[] {err.getDescription()};
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用错误码和单条自定义描述信息实例化异常。
|
||||
*
|
||||
* @param err 错误码 {@link ErrorCode}
|
||||
* @param readme 自定义描述信息
|
||||
*/
|
||||
public CommonErrorCodeException(ErrorCode err, String readme) {
|
||||
super(readme);
|
||||
this.err = err;
|
||||
this.description = new String[] {readme};
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用错误码和多条描述信息实例化异常。
|
||||
*
|
||||
* @param err 错误码 {@link ErrorCode}
|
||||
* @param readme 多条自定义描述信息
|
||||
*/
|
||||
public CommonErrorCodeException(ErrorCode err, String[] readme) {
|
||||
super(err.getDescription());
|
||||
this.err = err;
|
||||
this.description = readme;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
package com.cmhi.magent.exception;
|
||||
|
||||
import com.cmhi.magent.common.ErrorCode;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* 安全协议异常类。
|
||||
*
|
||||
* <p>该异常用于在安全协议相关的业务逻辑中抛出特定的错误码({@link ErrorCode})。
|
||||
* 开发者可以通过该异常封装错误码及描述信息,以便定位和处理安全相关问题。</p>
|
||||
*
|
||||
* <p>支持两种构造方式:</p>
|
||||
* <ul>
|
||||
* <li>仅通过错误码创建异常,自动使用错误码中的默认描述信息。</li>
|
||||
* <li>通过错误码和自定义描述信息创建异常。</li>
|
||||
* </ul>
|
||||
*
|
||||
* 示例用法:
|
||||
* <pre>
|
||||
* throw new SecurityProtocolException(ErrorCode.UNAUTHORIZED_ACCESS, "User is not authorized.");
|
||||
* </pre>
|
||||
*
|
||||
* @author huangxin@cmhi.chinamobile.com
|
||||
* @version 1.0.0
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class SecurityProtocolException extends RuntimeException {
|
||||
|
||||
/**
|
||||
* 错误码实例,表示具体的错误类型。
|
||||
*/
|
||||
private final ErrorCode err;
|
||||
|
||||
/**
|
||||
* 描述信息,说明错误的具体内容。
|
||||
*/
|
||||
private final String description;
|
||||
|
||||
/**
|
||||
* 使用错误码实例化异常。
|
||||
*
|
||||
* <p>该构造方法会自动使用错误码中的描述信息 {@link ErrorCode#getDescription()}。</p>
|
||||
*
|
||||
* @param err 错误码 {@link ErrorCode}
|
||||
*/
|
||||
public SecurityProtocolException(ErrorCode err) {
|
||||
super(err.getDescription());
|
||||
this.err = err;
|
||||
this.description = err.getDescription();
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用错误码和自定义描述信息实例化异常。
|
||||
*
|
||||
* @param err 错误码 {@link ErrorCode}
|
||||
* @param readme 自定义描述信息
|
||||
*/
|
||||
public SecurityProtocolException(ErrorCode err, String readme) {
|
||||
super(readme);
|
||||
this.err = err;
|
||||
this.description = readme;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,156 @@
|
|||
package com.cmhi.magent.interceptor;
|
||||
|
||||
import com.cmhi.magent.common.ConstValue;
|
||||
import com.cmhi.magent.misc.ApiContextUtils;
|
||||
import com.cmhi.magent.misc.HelperUtils;
|
||||
import com.cmhi.magent.misc.ProtocolJsonUtils;
|
||||
import jakarta.servlet.ReadListener;
|
||||
import jakarta.servlet.ServletInputStream;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletRequestWrapper;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* RequestBodyCacheWrapper 是一个自定义的 HttpServletRequest 包装类。
|
||||
*
|
||||
* <p>该类的主要功能如下:</p>
|
||||
* <ul>
|
||||
* <li>缓存请求体(Request Body),以便多次读取。</li>
|
||||
* <li>提取请求头(Headers),并存储为一个键值对的 Map。</li>
|
||||
* <li>提供对缓存数据(请求体和请求头)的访问接口。</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>该类适用于需要多次读取 HTTP 请求体内容的场景,例如拦截器中读取请求体进行验证或记录。</p>
|
||||
*
|
||||
* 示例用法:
|
||||
* <pre>
|
||||
* HttpServletRequest request = new RequestBodyCacheWrapper(originalRequest);
|
||||
* String bodyContent = request.getReader().lines().collect(Collectors.joining(System.lineSeparator()));
|
||||
* </pre>
|
||||
*
|
||||
* @author huangxin@cmhi.chinamobile.com
|
||||
* @version 1.0.0
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
@Getter
|
||||
@Slf4j
|
||||
public class RequestBodyCacheWrapper extends HttpServletRequestWrapper {
|
||||
|
||||
/**
|
||||
* 请求的时间戳,用于记录请求时间。
|
||||
*/
|
||||
private final Long reqTimeStamp = System.currentTimeMillis();
|
||||
|
||||
/**
|
||||
* 缓存的请求头信息,存储为键值对形式。
|
||||
*/
|
||||
private final Map<String, String> headers = new HashMap<>();
|
||||
|
||||
/**
|
||||
* 缓存的请求体内容。
|
||||
*/
|
||||
private String body;
|
||||
|
||||
/**
|
||||
* 构造方法,用于初始化请求体和请求头缓存。
|
||||
*
|
||||
* @param request 原始的 {@link HttpServletRequest} 对象
|
||||
* @throws IOException 如果处理输入流时发生 I/O 错误
|
||||
*/
|
||||
public RequestBodyCacheWrapper(HttpServletRequest request) throws IOException {
|
||||
super(request);
|
||||
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
BufferedReader bufferedReader = null;
|
||||
|
||||
try (InputStream inputStream = request.getInputStream()) {
|
||||
if (inputStream != null) {
|
||||
bufferedReader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
|
||||
char[] charBuffer = new char[128];
|
||||
int bytesRead;
|
||||
while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
|
||||
stringBuilder.append(charBuffer, 0, bytesRead);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("RequestWrapper read error :{}", e.getMessage());
|
||||
} finally {
|
||||
if (bufferedReader != null) {
|
||||
bufferedReader.close();
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// 使用工具类解析请求体,并格式化为 JSON
|
||||
Object obj = ProtocolJsonUtils.jsonGetObject(stringBuilder.toString(), Object.class);
|
||||
body = HelperUtils.getJson(obj);
|
||||
|
||||
// 读取请求头并存储到 Map
|
||||
Enumeration<String> enumeration = request.getHeaderNames();
|
||||
while (enumeration.hasMoreElements()) {
|
||||
String name = enumeration.nextElement();
|
||||
String value = request.getHeader(name);
|
||||
headers.put(name, value);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
body = "";
|
||||
} finally {
|
||||
// 设置请求的语言上下文和其他信息
|
||||
String language = request.getHeader(ConstValue.LANGUAGE_HEAD);
|
||||
ApiContextUtils.setRequestLocal(language);
|
||||
ApiContextUtils.setRequestInfo(body, reqTimeStamp);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 重写 {@link HttpServletRequest#getInputStream()} 方法,返回缓存的请求体输入流。
|
||||
*
|
||||
* @return 缓存的 {@link ServletInputStream} 对象
|
||||
*/
|
||||
@Override
|
||||
public ServletInputStream getInputStream() {
|
||||
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes(StandardCharsets.UTF_8));
|
||||
return new ServletInputStream() {
|
||||
@Override
|
||||
public boolean isFinished() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReady() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReadListener(ReadListener readListener) {
|
||||
// 当前实现不支持异步读取
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() {
|
||||
return byteArrayInputStream.read();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 重写 {@link HttpServletRequest#getReader()} 方法,返回缓存的请求体 BufferedReader。
|
||||
*
|
||||
* @return 缓存的 {@link BufferedReader} 对象
|
||||
*/
|
||||
@Override
|
||||
public BufferedReader getReader() {
|
||||
return new BufferedReader(new InputStreamReader(this.getInputStream(), StandardCharsets.UTF_8));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
package com.cmhi.magent.interceptor;
|
||||
|
||||
import jakarta.servlet.Filter;
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.FilterConfig;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.ServletRequest;
|
||||
import jakarta.servlet.ServletResponse;
|
||||
import jakarta.servlet.annotation.WebFilter;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* RequestBodyFilter 是一个过滤器,用于拦截 HTTP 请求并包装 {@link HttpServletRequest},
|
||||
* 以支持多次读取请求体内容。
|
||||
*
|
||||
* <p>该过滤器在请求进入应用程序之前,使用 {@link RequestBodyCacheWrapper} 包装原始的
|
||||
* {@link HttpServletRequest} 对象,并将包装后的请求对象传递给后续的处理链。</p>
|
||||
*
|
||||
* 功能特点:
|
||||
* <ul>
|
||||
* <li>缓存请求体,支持多次读取。</li>
|
||||
* <li>在大多数情况下,原始的 {@link HttpServletRequest} 中的输入流(InputStream)只能读取一次。
|
||||
* 使用该过滤器后,可以通过包装后的对象多次读取请求体内容。</li>
|
||||
* </ul>
|
||||
*
|
||||
* 配置说明:
|
||||
* <ul>
|
||||
* <li>通过 {@link WebFilter} 注解配置过滤器名称和 URL 模式。</li>
|
||||
* <li>通过 {@link Order} 注解设置过滤器的执行顺序,值越小优先级越高。</li>
|
||||
* </ul>
|
||||
*
|
||||
* 示例应用场景:
|
||||
* <ul>
|
||||
* <li>日志记录:需要记录完整的请求体内容。</li>
|
||||
* <li>安全校验:在进入控制器之前,对请求体内容进行校验或处理。</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author huangxin@cmhi.chinamobile.com
|
||||
* @version 1.0.0
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
@Component
|
||||
@WebFilter(filterName = "RequestBodyFilter", urlPatterns = "/*")
|
||||
@Order(10000)
|
||||
public class RequestBodyFilter implements Filter {
|
||||
|
||||
/**
|
||||
* 初始化过滤器方法。
|
||||
*
|
||||
* <p>该方法在过滤器实例被容器初始化时调用,通常用于资源初始化操作。</p>
|
||||
*
|
||||
* @param filterConfig 过滤器配置对象
|
||||
* @throws ServletException 如果初始化失败
|
||||
*/
|
||||
@Override
|
||||
public void init(FilterConfig filterConfig) throws ServletException {
|
||||
// 调用父类的默认实现(可选)
|
||||
Filter.super.init(filterConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* 核心过滤逻辑。
|
||||
*
|
||||
* <p>该方法会拦截所有 HTTP 请求,将 {@link HttpServletRequest} 包装为
|
||||
* {@link RequestBodyCacheWrapper},并将包装后的请求对象传递给后续的过滤链。</p>
|
||||
*
|
||||
* @param servletRequest 原始的请求对象
|
||||
* @param servletResponse 原始的响应对象
|
||||
* @param filterChain 过滤器链,用于将请求和响应传递给下一个过滤器或目标资源
|
||||
* @throws IOException 如果处理输入流或输出流时发生 I/O 错误
|
||||
* @throws ServletException 如果发生其他错误
|
||||
*/
|
||||
@Override
|
||||
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
|
||||
throws IOException, ServletException {
|
||||
// 定义包装后的请求对象
|
||||
ServletRequest requestWrapper = null;
|
||||
|
||||
// 如果请求是 HttpServletRequest 类型,则使用 RequestBodyCacheWrapper 包装它
|
||||
if (servletRequest instanceof HttpServletRequest httpServletRequest) {
|
||||
requestWrapper = new RequestBodyCacheWrapper(httpServletRequest);
|
||||
}
|
||||
//获取请求中的流如何,将取出来的字符串,再次转换成流,然后把它放入到新request对象中
|
||||
// 在chain.doFiler方法中传递新的request对象
|
||||
if (null == requestWrapper) {
|
||||
filterChain.doFilter(servletRequest, servletResponse);
|
||||
} else {
|
||||
filterChain.doFilter(requestWrapper, servletResponse);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
package com.cmhi.magent.interceptor;
|
||||
|
||||
import com.cmhi.magent.annotation.OperationLogAnnotation;
|
||||
import com.cmhi.magent.common.CommonEnumHandler;
|
||||
import com.cmhi.magent.common.ErrorCode;
|
||||
import com.cmhi.magent.pojo.po.BaseRespStatus;
|
||||
import com.cmhi.magent.pojo.vo.ProtocolResp;
|
||||
import com.cmhi.magent.service.OperationLogService;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.aspectj.lang.JoinPoint;
|
||||
import org.aspectj.lang.annotation.AfterReturning;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Pointcut;
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.context.request.RequestAttributes;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* RestfulLogAspect 是一个 AOP 切面类,用于记录 RESTful 控制器方法的操作日志。
|
||||
*
|
||||
* <p>该切面通过拦截控制器中的方法调用,提取相关信息(如请求信息、方法信息、返回结果等),
|
||||
* 并将其交给 {@link OperationLogService} 进行日志记录或存储。</p>
|
||||
*
|
||||
* 功能点:
|
||||
* <ul>
|
||||
* <li>拦截控制器方法的调用。</li>
|
||||
* <li>记录方法的操作日志,包括方法名称、请求内容、返回结果等。</li>
|
||||
* <li>支持通过 {@link OperationLogAnnotation} 注解扩展操作信息。</li>
|
||||
* </ul>
|
||||
*
|
||||
* 使用场景:
|
||||
* <ul>
|
||||
* <li>用户操作日志记录。</li>
|
||||
* <li>接口访问日志记录。</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author huangxin@cmhi.chinamobile.com
|
||||
* @version 1.0.0
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
@Aspect
|
||||
@Component
|
||||
@Order(1)
|
||||
@Slf4j
|
||||
public class RestfulLogAspect {
|
||||
|
||||
@Resource
|
||||
private OperationLogService optLogDbService;
|
||||
|
||||
/**
|
||||
* 定义切点,拦截所有 RESTful 控制器中的公共方法。
|
||||
*
|
||||
* <p>切点表达式 `execution(public * com.cmhi.magent.controller.*.*(..))`
|
||||
* 表示拦截 `com.cmhi.magent.controller` 包内所有类的公共方法。</p>
|
||||
*/
|
||||
@Pointcut("execution(public * com.cmhi.magent.controller.*.*(..))")
|
||||
public void restfulLog() {
|
||||
// 切点方法无需实现,作为标记使用
|
||||
}
|
||||
|
||||
/**
|
||||
* 在目标方法正常返回后执行操作日志记录。
|
||||
*
|
||||
* @param joinPoint 切入点信息,包含被拦截方法的上下文信息
|
||||
* @param result 方法的返回结果
|
||||
*/
|
||||
@AfterReturning(returning = "result", value = "restfulLog()")
|
||||
public void saveOperationLog(JoinPoint joinPoint, Object result) {
|
||||
// 获取 RequestAttributes 对象,用于获取当前请求信息
|
||||
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
|
||||
if (requestAttributes == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 从 RequestAttributes 获取 HttpServletRequest 对象
|
||||
HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);
|
||||
if (request == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取被拦截的方法签名
|
||||
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
|
||||
// 获取具体的被调用方法
|
||||
Method method = signature.getMethod();
|
||||
|
||||
// 获取方法上的自定义注解信息
|
||||
OperationLogAnnotation annotation = method.getAnnotation(OperationLogAnnotation.class);
|
||||
|
||||
// 根据返回结果类型,记录操作日志
|
||||
if (result instanceof ProtocolResp<?> protocolResp && protocolResp.getMsgContent() instanceof BaseRespStatus respStatus) {
|
||||
// 从返回结果中提取错误码
|
||||
ErrorCode errorCode = CommonEnumHandler.codeOf(ErrorCode.class, respStatus.getStatus());
|
||||
// 记录操作日志
|
||||
optLogDbService.systemOperationLog(method.getName(), request, result, annotation, errorCode);
|
||||
} else {
|
||||
// 如果返回结果不符合预期,记录为系统异常
|
||||
optLogDbService.systemOperationLog(method.getName(), request, result, annotation, ErrorCode.ERR_SYSTEMEXCEPTION);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
package com.cmhi.magent.misc;
|
||||
|
||||
import com.cmhi.magent.pojo.po.ApiContext;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* ApiContextUtils 是一个线程安全的工具类,用于管理当前线程的 {@link ApiContext} 对象。
|
||||
*
|
||||
* <p>基于 ThreadLocal 的方式,为每个线程维护独立的上下文信息(如请求体、请求时间、本地化语言等)。
|
||||
* 该类提供了静态方法来获取、设置或清除线程的上下文数据。</p>
|
||||
*
|
||||
* <p>使用场景:</p>
|
||||
* <ul>
|
||||
* <li>存储与当前请求相关联的上下文信息。</li>
|
||||
* <li>在应用程序的不同模块中共享请求上下文(如用户信息、本地化语言等)。</li>
|
||||
* <li>线程结束时,确保调用 {@link #clear()} 方法清理上下文,避免内存泄漏。</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>注意:此类不可实例化,应通过静态方法使用。</p>
|
||||
*
|
||||
* @author huangxin@cmhi.chinamobile.com
|
||||
* @version 1.0.0
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
public class ApiContextUtils {
|
||||
|
||||
/**
|
||||
* 私有构造方法,防止实例化。
|
||||
*/
|
||||
private ApiContextUtils() {
|
||||
throw new AssertionError("Instantiating utility class.");
|
||||
}
|
||||
|
||||
/**
|
||||
* ThreadLocal 变量,用于存储当前线程的 {@link ApiContext} 对象。
|
||||
*/
|
||||
private static final ThreadLocal<ApiContext> API_CONTEXT = new ThreadLocal<>();
|
||||
|
||||
/**
|
||||
* 获取当前线程的 {@link ApiContext} 对象。如果不存在则创建一个新的实例。
|
||||
*
|
||||
* @return 当前线程的 {@link ApiContext} 对象
|
||||
*/
|
||||
public static ApiContext get() {
|
||||
ApiContext context = API_CONTEXT.get();
|
||||
if (context == null) {
|
||||
// 如果当前线程没有上下文信息,则创建一个新的
|
||||
context = new ApiContext();
|
||||
API_CONTEXT.set(context);
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置请求体信息和请求时间到当前线程的 {@link ApiContext} 对象。
|
||||
*
|
||||
* @param reqBody 请求体内容
|
||||
* @param reqTime 请求时间戳
|
||||
*/
|
||||
public static void setRequestInfo(String reqBody, Long reqTime) {
|
||||
ApiContext context = get();
|
||||
context.setRequestBody(reqBody);
|
||||
context.setRequestTime(reqTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前线程的语言环境。如果没有设置,则返回系统默认语言。
|
||||
*
|
||||
* @return 当前线程的语言环境字符串(如 "en", "zh")
|
||||
*/
|
||||
public static String getLanguage() {
|
||||
ApiContext context = get();
|
||||
// 如果没有设置语言,则返回系统默认语言
|
||||
return HelperUtils.stringNotEmptyOrNull(context.getReqLocal()) ? context.getReqLocal() : Locale.getDefault().getLanguage();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置请求的语言环境到当前线程的 {@link ApiContext} 对象。
|
||||
*
|
||||
* @param local 语言环境字符串(如 "en", "zh")
|
||||
*/
|
||||
public static void setRequestLocal(String local) {
|
||||
ApiContext context = get();
|
||||
context.setReqLocal(local);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置用户相关的 JWT 信息到当前线程的 {@link ApiContext} 对象。
|
||||
*
|
||||
* @param jwt 用户的 JWT 字符串
|
||||
* @param username 用户名
|
||||
* @param uid 用户唯一标识符
|
||||
* @param id 用户 ID
|
||||
*/
|
||||
public static void setJwtUserInfo(String jwt, String username, String uid, Long id) {
|
||||
ApiContext context = get();
|
||||
context.setJwt(jwt);
|
||||
context.setUsername(username);
|
||||
context.setUid(uid);
|
||||
context.setId(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除当前线程的 {@link ApiContext} 对象,避免内存泄漏。
|
||||
*
|
||||
* <p>该方法应在线程结束时调用,尤其是在使用线程池时,确保清理上下文数据。</p>
|
||||
*/
|
||||
public static void clear() {
|
||||
API_CONTEXT.remove();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,248 @@
|
|||
package com.cmhi.magent.misc;
|
||||
|
||||
import com.cmhi.magent.config.ObjectMapperProvider;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.validation.ConstraintViolation;
|
||||
import org.springframework.context.i18n.LocaleContextHolder;
|
||||
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.sql.Timestamp;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* HelperUtils 是一个工具类,提供常见的实用方法,如 JSON 转换、字符串处理、时间操作、校验工具等。
|
||||
*
|
||||
* <p>使用场景:</p>
|
||||
* <ul>
|
||||
* <li>处理 JSON 数据。</li>
|
||||
* <li>解析请求头、获取客户端 IP。</li>
|
||||
* <li>字符串截断、流转换。</li>
|
||||
* <li>格式化时间、校验对象。</li>
|
||||
* </ul>
|
||||
*
|
||||
* 注意:此类不可实例化,应通过静态方法使用。
|
||||
*
|
||||
* @author huangxin@cmhi.chinamobile.com
|
||||
* @version 1.0.0
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
public class HelperUtils {
|
||||
|
||||
/**
|
||||
* 静态 ObjectMapper 实例,用于 JSON 操作。
|
||||
*/
|
||||
private static final ObjectMapper OBJ_MAPPER = ObjectMapperProvider.getMapper();
|
||||
|
||||
/**
|
||||
* 私有构造方法,防止实例化。
|
||||
*/
|
||||
private HelperUtils() {
|
||||
throw new AssertionError("Instantiating utility class.");
|
||||
}
|
||||
|
||||
/**
|
||||
* 将对象转换为 JSON 字符串。
|
||||
*
|
||||
* @param obj 任意对象
|
||||
* @param <T> 对象的类型
|
||||
* @return 转换后的 JSON 字符串
|
||||
* @throws JsonProcessingException 如果 JSON 序列化失败
|
||||
*/
|
||||
public static <T> String getJson(T obj) throws JsonProcessingException {
|
||||
return OBJ_MAPPER.writeValueAsString(obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* 截断字符串到指定长度,超出部分以 "..." 表示。
|
||||
*
|
||||
* @param orgString 原始字符串
|
||||
* @param maxLength 最大长度
|
||||
* @return 截断后的字符串
|
||||
*/
|
||||
public static String truncateString(String orgString, int maxLength) {
|
||||
if (!stringNotEmptyOrNull(orgString) || orgString.length() <= maxLength) {
|
||||
return orgString;
|
||||
} else {
|
||||
return orgString.substring(0, maxLength - 4) + "...";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字节数组转换为十六进制字符串表示。
|
||||
*
|
||||
* @param bArray 字节数组
|
||||
* @return 十六进制字符串
|
||||
*/
|
||||
public static String bytesToHexString(byte[] bArray) {
|
||||
StringBuilder sb = new StringBuilder(bArray.length);
|
||||
for (byte b : bArray) {
|
||||
String hex = Integer.toHexString(0xFF & b);
|
||||
if (hex.length() < 2) {
|
||||
sb.append('0');
|
||||
}
|
||||
sb.append(hex.toUpperCase());
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字符串转换为 GB2312 编码的字节数组。
|
||||
*
|
||||
* @param str 原始字符串
|
||||
* @return GB2312 编码的字节数组
|
||||
* @throws UnsupportedEncodingException 如果编码不被支持
|
||||
*/
|
||||
public static byte[] convertStringToGb2312(String str) throws UnsupportedEncodingException {
|
||||
return str.getBytes("GB2312");
|
||||
}
|
||||
|
||||
/**
|
||||
* 将输入流转换为字符串。
|
||||
*
|
||||
* @param inputStream 输入流
|
||||
* @return 转换后的字符串
|
||||
* @throws IOException 如果读取流时发生错误
|
||||
*/
|
||||
public static String inputStream2String(InputStream inputStream) throws IOException {
|
||||
if (inputStream == null) {
|
||||
return "";
|
||||
}
|
||||
StringBuilder out = new StringBuilder();
|
||||
try (Reader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8)) {
|
||||
char[] buffer = new char[1024];
|
||||
int charsRead;
|
||||
while ((charsRead = reader.read(buffer)) > 0) {
|
||||
out.append(buffer, 0, charsRead);
|
||||
}
|
||||
}
|
||||
return out.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将时间字符串转换为时间戳(毫秒)。
|
||||
*
|
||||
* @param dateTime 时间字符串(格式:yyyy-MM-dd HH:mm:ss)
|
||||
* @return 时间戳(毫秒)
|
||||
*/
|
||||
public static long getTimestampMilliSecond(String dateTime) {
|
||||
return Timestamp.valueOf(dateTime).toInstant().toEpochMilli();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前时间的字符串表示,格式为 yyyy-MM-dd HH:mm:ss。
|
||||
*
|
||||
* @return 当前时间的字符串表示
|
||||
*/
|
||||
public static String getCurrentDatetime() {
|
||||
return LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验对象是否符合约束条件。
|
||||
*
|
||||
* @param t 待校验的对象
|
||||
* @param groups 校验组
|
||||
* @param <T> 对象类型
|
||||
* @return 校验错误的消息列表,如果无错误则返回空列表
|
||||
*/
|
||||
public static <T> List<String> validate(T t, Class<?>... groups) {
|
||||
LocaleContextHolder.setLocale(Locale.forLanguageTag(ApiContextUtils.getLanguage().replace('_', '-')));
|
||||
LocalValidatorFactoryBean validatorFactory = SpringBootBeanUtils.getBean(LocalValidatorFactoryBean.class);
|
||||
Set<ConstraintViolation<T>> violations = (groups != null && groups.length > 0)
|
||||
? validatorFactory.getValidator().validate(t, groups)
|
||||
: validatorFactory.getValidator().validate(t);
|
||||
|
||||
List<String> errors = new ArrayList<>();
|
||||
violations.forEach(violation -> errors.add(violation.getMessage()));
|
||||
return errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断字符串是否非空且不为 null。
|
||||
*
|
||||
* @param str 待检查的字符串
|
||||
* @return 如果字符串非空且不为 null,则返回 true;否则返回 false
|
||||
*/
|
||||
public static boolean stringNotEmptyOrNull(String str) {
|
||||
return str != null && !str.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并数据库字符串值:如果字符串为空则返回 null。
|
||||
*
|
||||
* @param str 原始字符串
|
||||
* @return 如果非空返回原字符串,否则返回 null
|
||||
*/
|
||||
public static String meagreDbStringValue(String str) {
|
||||
return stringNotEmptyOrNull(str) ? str : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 HTTP 请求头的 JSON 表示。
|
||||
*
|
||||
* @param request HTTP 请求
|
||||
* @return 请求头的 JSON 表示
|
||||
* @throws JsonProcessingException 如果 JSON 转换失败
|
||||
*/
|
||||
public static String getHttpRequestHeaders(HttpServletRequest request) throws JsonProcessingException {
|
||||
if (request == null) {
|
||||
return "{}";
|
||||
}
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
Enumeration<String> headerNames = request.getHeaderNames();
|
||||
while (headerNames.hasMoreElements()) {
|
||||
String name = headerNames.nextElement();
|
||||
headers.put(name, request.getHeader(name));
|
||||
}
|
||||
return getJson(headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 HTTP 请求的源 IP 地址。
|
||||
*
|
||||
* @param request HTTP 请求
|
||||
* @return 源 IP 地址
|
||||
* @throws UnknownHostException 如果无法获取本机地址
|
||||
*/
|
||||
public static String getHttpRequestSrcIp(HttpServletRequest request) throws UnknownHostException {
|
||||
if (request == null) {
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
String ip = request.getHeader("x-forwarded-for");
|
||||
String unknown = "unknown";
|
||||
String localhostIp = "127.0.0.1";
|
||||
String localhostIpv6 = "::1";
|
||||
String separator = ",";
|
||||
|
||||
if (ip == null || ip.isEmpty() || unknown.equalsIgnoreCase(ip)) {
|
||||
ip = request.getRemoteAddr();
|
||||
}
|
||||
if (localhostIp.equals(ip) || localhostIpv6.equals(ip)) {
|
||||
InetAddress inetAddress = InetAddress.getLocalHost();
|
||||
ip = inetAddress.getHostAddress();
|
||||
}
|
||||
if (ip != null && ip.contains(separator)) {
|
||||
ip = ip.split(separator)[0];
|
||||
}
|
||||
return localhostIpv6.equals(ip) ? localhostIp : ip;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,203 @@
|
|||
package com.cmhi.magent.misc;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.reflect.MalformedParameterizedTypeException;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.lang.reflect.TypeVariable;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 自定义的 {@link ParameterizedType} 实现,用于描述带泛型参数的类型。
|
||||
*
|
||||
* <p>此类广泛用于序列化和反序列化场景,尤其适用于 JSON 解析框架(如 Jackson、Gson),
|
||||
* 可以构造复杂的泛型类型(例如:`List<String>`、`Map<String, Integer>`)。</p>
|
||||
*
|
||||
* 功能特点:
|
||||
* <ul>
|
||||
* <li>支持自定义泛型类型创建。</li>
|
||||
* <li>实现了 {@code ParameterizedType} 的所有方法。</li>
|
||||
* <li>提供友好的 `toString` 表示,用于调试或日志打印。</li>
|
||||
* </ul>
|
||||
*
|
||||
* 使用场景:
|
||||
* <ul>
|
||||
* <li>动态构造复杂的泛型类型。</li>
|
||||
* <li>解决类型擦除问题,特别是在运行时需要保留泛型信息的场景。</li>
|
||||
* </ul>
|
||||
*
|
||||
* 示例:
|
||||
* <pre>{@code
|
||||
* JsonParameterizedType type = JsonParameterizedType.make(
|
||||
* List.class,
|
||||
* new Type[]{String.class},
|
||||
* null
|
||||
* );
|
||||
* System.out.println(type); // 输出:java.util.List<java.lang.String>
|
||||
* }</pre>
|
||||
*
|
||||
* @author huangxin@cmhi.chinamobile.com
|
||||
* @version 1.0.0
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
public class JsonParameterizedType implements ParameterizedType {
|
||||
|
||||
/**
|
||||
* 泛型参数的实际类型列表。
|
||||
*/
|
||||
private final Type[] actualTypeArguments;
|
||||
|
||||
/**
|
||||
* 原始类型,表示泛型的主类型(如 `List` 或 `Map`)。
|
||||
*/
|
||||
private final Class<?> rawType;
|
||||
|
||||
/**
|
||||
* 拥有者类型,表示该泛型所属的外部类(如果存在)。
|
||||
*/
|
||||
private final Type ownerType;
|
||||
|
||||
/**
|
||||
* 私有构造函数,用于创建泛型类型。
|
||||
*
|
||||
* @param rawType 原始类型(如 `List` 或 `Map`)
|
||||
* @param actualTypeArguments 泛型参数的实际类型
|
||||
* @param ownerType 拥有者类型(可以为 null)
|
||||
*/
|
||||
private JsonParameterizedType(Class<?> rawType, Type[] actualTypeArguments, Type ownerType) {
|
||||
this.actualTypeArguments = actualTypeArguments;
|
||||
this.rawType = rawType;
|
||||
this.ownerType = (ownerType != null) ? ownerType : rawType.getDeclaringClass();
|
||||
this.validateConstructorArguments();
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验构造函数的参数是否合法。
|
||||
*
|
||||
* <p>确保原始类型声明的泛型参数数量与传入的实际类型参数数量一致。</p>
|
||||
*
|
||||
* @throws MalformedParameterizedTypeException 如果参数数量不匹配
|
||||
*/
|
||||
private void validateConstructorArguments() {
|
||||
TypeVariable<?>[] typeParameters = this.rawType.getTypeParameters();
|
||||
if (typeParameters.length != this.actualTypeArguments.length) {
|
||||
throw new MalformedParameterizedTypeException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取泛型参数的实际类型列表。
|
||||
*
|
||||
* @return 泛型参数的实际类型数组
|
||||
*/
|
||||
@Override
|
||||
public Type @NotNull [] getActualTypeArguments() {
|
||||
// 返回副本,保证安全性
|
||||
return this.actualTypeArguments.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取原始类型(未带泛型参数的主类型)。
|
||||
*
|
||||
* @return 原始类型
|
||||
*/
|
||||
@Override
|
||||
public @NotNull Type getRawType() {
|
||||
return this.rawType;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取拥有者类型(外部类的类型)。
|
||||
*
|
||||
* @return 拥有者类型,如果无外部类则返回 null
|
||||
*/
|
||||
@Override
|
||||
public Type getOwnerType() {
|
||||
return this.ownerType;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断该泛型类型是否与另一个泛型类型相等。
|
||||
*
|
||||
* @param obj 要比较的对象
|
||||
* @return 如果两者相等,则返回 true;否则返回 false
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj instanceof ParameterizedType other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
return Objects.equals(this.ownerType, other.getOwnerType())
|
||||
&& Objects.equals(this.rawType, other.getRawType())
|
||||
&& Arrays.equals(this.actualTypeArguments, other.getActualTypeArguments());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算当前泛型类型的哈希值。
|
||||
*
|
||||
* @return 哈希值
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Arrays.hashCode(this.actualTypeArguments)
|
||||
^ Objects.hashCode(this.ownerType)
|
||||
^ Objects.hashCode(this.rawType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回该泛型类型的字符串表示(调试友好)。
|
||||
*
|
||||
* @return 字符串表示形式
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("java:S3740")
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (this.ownerType != null) {
|
||||
if (this.ownerType instanceof Class<?> ownerClass) {
|
||||
sb.append(ownerClass.getName());
|
||||
} else {
|
||||
sb.append(this.ownerType);
|
||||
}
|
||||
sb.append("$");
|
||||
if (this.ownerType instanceof JsonParameterizedType jsonType) {
|
||||
sb.append(this.rawType.getName().replace(jsonType.rawType.getName() + "$", ""));
|
||||
} else {
|
||||
sb.append(this.rawType.getSimpleName());
|
||||
}
|
||||
} else {
|
||||
sb.append(this.rawType.getName());
|
||||
}
|
||||
|
||||
if (this.actualTypeArguments != null && this.actualTypeArguments.length > 0) {
|
||||
sb.append("<");
|
||||
boolean first = true;
|
||||
for (Type typeArg : this.actualTypeArguments) {
|
||||
if (!first) {
|
||||
sb.append(", ");
|
||||
}
|
||||
sb.append(typeArg.getTypeName());
|
||||
first = false;
|
||||
}
|
||||
sb.append(">");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 静态工厂方法,用于创建 {@link JsonParameterizedType} 实例。
|
||||
*
|
||||
* @param rawType 原始类型(如 `List` 或 `Map`)
|
||||
* @param actualTypeArguments 泛型参数的实际类型
|
||||
* @param ownerType 拥有者类型(可以为 null)
|
||||
* @return 构造的 {@code JsonParameterizedType} 实例
|
||||
*/
|
||||
public static JsonParameterizedType make(Class<?> rawType, Type[] actualTypeArguments, Type ownerType) {
|
||||
return new JsonParameterizedType(rawType, actualTypeArguments, ownerType);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,141 @@
|
|||
package com.cmhi.magent.misc;
|
||||
|
||||
import io.micrometer.common.util.StringUtils;
|
||||
import org.springframework.context.MessageSource;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* 国际化消息工具类,用于根据指定的语言或区域获取对应的消息内容。
|
||||
*
|
||||
* <p>此类基于 Spring 的 {@link MessageSource},提供了简化的消息获取方法。
|
||||
* 它支持通过指定语言代码(如 "en_US")动态获取对应语言的消息,同时
|
||||
* 支持参数化的消息内容。</p>
|
||||
*
|
||||
* 功能特点:
|
||||
* <ul>
|
||||
* <li>支持基于语言代码的消息解析。</li>
|
||||
* <li>支持默认语言(应用程序配置的默认 {@code Locale})。</li>
|
||||
* <li>支持带参数的消息格式化。</li>
|
||||
* <li>使用懒加载模式获取 {@code MessageSource},避免过早初始化。</li>
|
||||
* </ul>
|
||||
*
|
||||
* 使用示例:
|
||||
* <pre>{@code
|
||||
* // 获取英文消息
|
||||
* String message = MessageUtil.get("welcome.message", "en_US");
|
||||
*
|
||||
* // 获取带参数的中文消息
|
||||
* String message = MessageUtil.get("welcome.message", new Object[]{"John"}, "zh_CN");
|
||||
* }</pre>
|
||||
*
|
||||
* 注意:此类不可实例化,应通过静态方法使用。
|
||||
*
|
||||
* @author huangxin@cmhi.chinamobile.com
|
||||
* @version 1.0.0
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
public class MessageUtil {
|
||||
|
||||
/**
|
||||
* 语言代码的分割大小,例如 "en_US" 应被分割为 ["en", "US"]。
|
||||
*/
|
||||
private static final int ARRAY_SIZE = 2;
|
||||
|
||||
/**
|
||||
* 私有构造方法,防止工具类被实例化。
|
||||
*/
|
||||
private MessageUtil() {
|
||||
throw new AssertionError("Instantiating utility class.");
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据指定的键和语言代码获取国际化消息。
|
||||
* 如果语言代码无效,将使用默认语言。
|
||||
*
|
||||
* @param key 消息的键
|
||||
* @param language 语言代码(如 "en_US" 或 "zh_CN")
|
||||
* @return 对应语言的消息,如果未找到消息则返回键本身
|
||||
*/
|
||||
public static String get(String key, String language) {
|
||||
if (!StringUtils.isEmpty(language)) {
|
||||
String[] parts = language.split("_");
|
||||
if (parts.length == ARRAY_SIZE) {
|
||||
return get(key, new Locale(parts[0], parts[1]));
|
||||
}
|
||||
}
|
||||
// 使用默认的国际化语言
|
||||
return get(key, Locale.getDefault());
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据指定的键、参数和语言代码获取国际化消息。
|
||||
* 如果语言代码无效,将使用默认语言。
|
||||
*
|
||||
* @param key 消息的键
|
||||
* @param params 消息中的参数
|
||||
* @param language 语言代码(如 "en_US" 或 "zh_CN")
|
||||
* @return 格式化后的语言消息,如果未找到消息则返回键本身
|
||||
*/
|
||||
public static String get(String key, Object[] params, String language) {
|
||||
if (!StringUtils.isEmpty(language)) {
|
||||
String[] parts = language.split("_");
|
||||
if (parts.length == ARRAY_SIZE) {
|
||||
return get(key, params, new Locale(parts[0], parts[1]));
|
||||
}
|
||||
}
|
||||
return get(key, params, Locale.getDefault());
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据指定的键和语言获取国际化消息。
|
||||
*
|
||||
* @param key 消息的键
|
||||
* @param language 语言({@link Locale})
|
||||
* @return 对应语言的消息,如果未找到消息则返回键本身
|
||||
*/
|
||||
private static String get(String key, Locale language) {
|
||||
return get(key, new Object[0], language);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据指定的键、参数和语言获取国际化消息。
|
||||
*
|
||||
* @param key 消息的键
|
||||
* @param params 消息中的参数
|
||||
* @param language 语言({@link Locale})
|
||||
* @return 格式化后的语言消息,如果未找到消息则返回键本身
|
||||
*/
|
||||
private static String get(String key, Object[] params, Locale language) {
|
||||
// 从 MessageSource 获取消息
|
||||
String message = getInstance().getMessage(key, params, language);
|
||||
|
||||
// 如果消息不为空,直接返回
|
||||
if (HelperUtils.stringNotEmptyOrNull(message)) {
|
||||
return message;
|
||||
}
|
||||
|
||||
// 如果消息不存在,返回原始键,避免返回空值
|
||||
return key;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 {@link MessageSource} 的实例。
|
||||
* <p>使用懒加载模式,确保在需要时才初始化。</p>
|
||||
*
|
||||
* @return {@link MessageSource} 实例
|
||||
*/
|
||||
private static MessageSource getInstance() {
|
||||
return LazyHolder.MESSAGE_SOURCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* 懒加载的内部类,用于安全地初始化 {@link MessageSource}。
|
||||
*/
|
||||
private static class LazyHolder {
|
||||
/**
|
||||
* 从 Spring 容器中获取 {@link MessageSource} 实例。
|
||||
*/
|
||||
private static final MessageSource MESSAGE_SOURCE = SpringBootBeanUtils.getBean(MessageSource.class);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,209 @@
|
|||
package com.cmhi.magent.misc;
|
||||
|
||||
import com.cmhi.magent.config.ObjectMapperProvider;
|
||||
import com.cmhi.magent.pojo.po.BaseRespStatus;
|
||||
import com.cmhi.magent.pojo.vo.ProtocolResp;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import jakarta.servlet.ServletInputStream;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.Stack;
|
||||
|
||||
/**
|
||||
* JSON 协议工具类,用于处理 JSON 数据的序列化和反序列化。
|
||||
*
|
||||
* <p>支持通用的 JSON 对象转换,以及针对 {@link ProtocolResp} 协议响应的数据结构处理。
|
||||
* 工具类基于 Jackson 实现,确保高效解析和生成 JSON 数据。</p>
|
||||
*
|
||||
* 功能特点:
|
||||
* <ul>
|
||||
* <li>支持对象到字节数组的 JSON 序列化。</li>
|
||||
* <li>支持从字符串或输入流中反序列化 JSON 对象。</li>
|
||||
* <li>支持动态泛型类型的 JSON 解析,解决运行时泛型丢失问题。</li>
|
||||
* </ul>
|
||||
*
|
||||
* 注意:此工具类不可实例化,所有方法均为静态方法。
|
||||
*
|
||||
* @author huangxin@cmhi.chinamobile.com
|
||||
* @version 1.1.0
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
public class ProtocolJsonUtils {
|
||||
|
||||
/**
|
||||
* 私有构造方法,防止工具类被实例化。
|
||||
*/
|
||||
private ProtocolJsonUtils() {
|
||||
throw new AssertionError("Instantiating utility class.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Jackson 的 ObjectMapper 实例,用于 JSON 解析和生成。
|
||||
*/
|
||||
private static final ObjectMapper OBJ_MAPPER = ObjectMapperProvider.getMapper();
|
||||
|
||||
/**
|
||||
* 序列化对象为 JSON 字节数组,支持自定义字符集。
|
||||
*
|
||||
* @param obj 要序列化的对象
|
||||
* @param charset 字符集
|
||||
* @param <T> 对象的类型
|
||||
* @return JSON 字节数组
|
||||
* @throws JsonProcessingException 如果序列化失败
|
||||
*/
|
||||
public static <T> byte[] getJsonBytes(T obj, Charset charset) throws JsonProcessingException {
|
||||
return OBJ_MAPPER.writeValueAsString(obj).getBytes(charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* 序列化对象为 JSON 字节数组,使用默认 UTF-8 编码。
|
||||
*
|
||||
* @param obj 要序列化的对象
|
||||
* @param <T> 对象的类型
|
||||
* @return JSON 字节数组
|
||||
* @throws JsonProcessingException 如果序列化失败
|
||||
*/
|
||||
public static <T> byte[] getJsonBytes(T obj) throws JsonProcessingException {
|
||||
return getJsonBytes(obj, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 JSON 字符串反序列化为指定类型的对象。
|
||||
*
|
||||
* @param json JSON 字符串
|
||||
* @param valueType 要转换的目标类型
|
||||
* @param <T> 目标类型
|
||||
* @return 目标对象
|
||||
* @throws JsonProcessingException 如果反序列化失败
|
||||
*/
|
||||
public static <T> T jsonGetObject(String json, Class<T> valueType) throws JsonProcessingException {
|
||||
return OBJ_MAPPER.readValue(json, valueType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从输入流反序列化为泛型类型的对象。
|
||||
*
|
||||
* @param is 输入流
|
||||
* @param valueTypeRef 泛型类型引用
|
||||
* @param <T> 泛型类型
|
||||
* @return 目标对象
|
||||
* @throws IOException 如果读取流或反序列化失败
|
||||
*/
|
||||
public static <T> T jsonGetObject(ServletInputStream is, TypeReference<T> valueTypeRef) throws IOException {
|
||||
return OBJ_MAPPER.readValue(HelperUtils.inputStream2String(is), valueTypeRef);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 JSON 字符串反序列化为泛型类型的对象。
|
||||
*
|
||||
* @param json JSON 字符串
|
||||
* @param valueTypeRef 泛型类型引用
|
||||
* @param <T> 泛型类型
|
||||
* @return 目标对象
|
||||
* @throws IOException 如果反序列化失败
|
||||
*/
|
||||
public static <T> T jsonGetObject(String json, TypeReference<T> valueTypeRef) throws IOException {
|
||||
return OBJ_MAPPER.readValue(json, valueTypeRef);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个动态泛型的响应类型。
|
||||
*
|
||||
* @param c 目标泛型类型
|
||||
* @return 动态泛型类型
|
||||
*/
|
||||
private static <T> Type createRespType(Class<T> c) {
|
||||
return new ParameterizedType() {
|
||||
@Override
|
||||
public Type @NotNull [] getActualTypeArguments() {
|
||||
return new Type[] {c};
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Type getRawType() {
|
||||
return ProtocolResp.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getOwnerType() {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 JSON 字符串反序列化为 {@link ProtocolResp} 对象。
|
||||
* 泛型类型通过指定的子类参数动态生成。
|
||||
*
|
||||
* @param jsonString JSON 字符串
|
||||
* @param subClass 响应数据的目标类型
|
||||
* @param <T> 响应数据的泛型类型
|
||||
* @return ProtocolResp 对象
|
||||
* @throws JsonProcessingException 如果反序列化失败
|
||||
*/
|
||||
@SuppressWarnings("java:S1452")
|
||||
public static <T> ProtocolResp<? extends BaseRespStatus> jsonGetProtocolResp(String jsonString,
|
||||
Class<T> subClass) throws JsonProcessingException {
|
||||
return OBJ_MAPPER.readValue(jsonString, new TypeReference<>() {
|
||||
@Override
|
||||
public Type getType() {
|
||||
return createRespType(subClass);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个多级嵌套泛型的响应类型。
|
||||
*
|
||||
* @param c 泛型类型数组
|
||||
* @return 动态嵌套泛型类型
|
||||
*/
|
||||
@SuppressWarnings("java:S1149")
|
||||
private static Type createRespType(Class<?>[] c) {
|
||||
if (c.length == 1) {
|
||||
return createRespType(c[0]);
|
||||
}
|
||||
|
||||
Stack<Class<?>> stack = new Stack<>();
|
||||
Arrays.stream(c).forEach(stack::push);
|
||||
|
||||
Class<?> first = stack.pop();
|
||||
Class<?> second = stack.pop();
|
||||
|
||||
JsonParameterizedType type = JsonParameterizedType.make(second, new Type[] {first}, second.getDeclaringClass());
|
||||
|
||||
while (!stack.isEmpty()) {
|
||||
Class<?> current = stack.pop();
|
||||
type = JsonParameterizedType.make(current, new Type[] {type}, current.getDeclaringClass());
|
||||
}
|
||||
|
||||
return JsonParameterizedType.make(ProtocolResp.class, new Type[] {type}, ProtocolResp.class.getDeclaringClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 JSON 字符串反序列化为多级嵌套泛型的 {@link ProtocolResp} 对象。
|
||||
*
|
||||
* @param jsonString JSON 字符串
|
||||
* @param subClass 嵌套类型数组
|
||||
* @return ProtocolResp 对象
|
||||
* @throws JsonProcessingException 如果反序列化失败
|
||||
*/
|
||||
@SuppressWarnings("java:S1452")
|
||||
public static ProtocolResp<? extends BaseRespStatus> jsonGetProtocolResp(String jsonString,
|
||||
Class<?>[] subClass) throws JsonProcessingException {
|
||||
return OBJ_MAPPER.readValue(jsonString, new TypeReference<>() {
|
||||
@Override
|
||||
public Type getType() {
|
||||
return createRespType(subClass);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
package com.cmhi.magent.misc;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* Spring 容器工具类,用于在非 Spring 管理的类中获取 Bean 实例。
|
||||
*
|
||||
* <p>通过实现 {@link ApplicationContextAware} 接口,获取 Spring 容器的 {@link ApplicationContext},
|
||||
* 并将其保存为静态变量,便于其他类在任何地方访问 Spring 容器。</p>
|
||||
*
|
||||
* <p>功能特点:</p>
|
||||
* <ul>
|
||||
* <li>支持通过 Bean 名称获取 Bean 实例。</li>
|
||||
* <li>支持通过 Bean 类型获取 Bean 实例。</li>
|
||||
* <li>支持通过 Bean 名称和类型同时获取 Bean 实例。</li>
|
||||
* </ul>
|
||||
*
|
||||
* 使用场景:
|
||||
* <ul>
|
||||
* <li>在非 Spring 管理的类中(如工具类、线程、第三方库回调方法),需要访问 Spring 容器中的 Bean。</li>
|
||||
* <li>简化代码中对 Spring 容器的 Bean 获取操作。</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p><strong>注意事项:</strong>此工具类需要在 Spring 容器初始化时正确加载。</p>
|
||||
*
|
||||
* 示例:
|
||||
* <pre>{@code
|
||||
* // 获取名为 "myService" 的 Bean
|
||||
* MyService myService = (MyService) SpringBootBeanUtils.getBean("myService");
|
||||
*
|
||||
* // 获取类型为 MyService 的 Bean
|
||||
* MyService myService = SpringBootBeanUtils.getBean(MyService.class);
|
||||
*
|
||||
* // 获取名为 "myService" 且类型为 MyService 的 Bean
|
||||
* MyService myService = SpringBootBeanUtils.getBean("myService", MyService.class);
|
||||
* }</pre>
|
||||
*
|
||||
* @author huangxin@cmhi.chinamobile.com
|
||||
* @version 1.0.0
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
@Component
|
||||
public class SpringBootBeanUtils implements ApplicationContextAware {
|
||||
|
||||
/**
|
||||
* Spring 应用上下文,用于获取 Spring 容器中的 Bean。
|
||||
* 使用 {@code @Getter} 和 {@code @Setter} 提供线程安全的访问和修改方法。
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
private static ApplicationContext appContext;
|
||||
|
||||
/**
|
||||
* 根据 Bean 名称获取 Bean 实例。
|
||||
*
|
||||
* @param name Bean 的名称
|
||||
* @return Bean 实例
|
||||
* @throws BeansException 如果无法通过名称获取 Bean
|
||||
*/
|
||||
public static Object getBean(String name) {
|
||||
return getAppContext().getBean(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 Bean 类型获取 Bean 实例。
|
||||
*
|
||||
* @param clazz Bean 的类型
|
||||
* @param <T> Bean 的类型参数
|
||||
* @return Bean 实例
|
||||
* @throws BeansException 如果无法通过类型获取 Bean
|
||||
*/
|
||||
public static <T> T getBean(Class<T> clazz) {
|
||||
return getAppContext().getBean(clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 Bean 名称和类型获取 Bean 实例。
|
||||
*
|
||||
* @param name Bean 的名称
|
||||
* @param clazz Bean 的类型
|
||||
* @param <T> Bean 的类型参数
|
||||
* @return Bean 实例
|
||||
* @throws BeansException 如果无法通过名称和类型获取 Bean
|
||||
*/
|
||||
public static <T> T getBean(String name, Class<T> clazz) {
|
||||
return getAppContext().getBean(name, clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 Spring 应用上下文。
|
||||
*
|
||||
* <p>此方法由 Spring 容器调用,通过 {@link ApplicationContextAware} 接口注入应用上下文。</p>
|
||||
*
|
||||
* @param applicationContext Spring 应用上下文
|
||||
* @throws BeansException 如果上下文注入失败
|
||||
*/
|
||||
@Override
|
||||
public void setApplicationContext(
|
||||
@NotNull @org.jetbrains.annotations.NotNull ApplicationContext applicationContext) throws BeansException {
|
||||
setAppContext(applicationContext);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,184 @@
|
|||
package com.cmhi.magent.pojo.base;
|
||||
|
||||
import com.cmhi.magent.validation.group.ValidGroups;
|
||||
import com.cmhi.magent.validation.valids.ValidProtocolTimestamp;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.hibernate.validator.constraints.Range;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 通用协议基类 {@code BaseProtocol},用于封装协议的基本结构。
|
||||
* <p>
|
||||
* 该类是一个泛型类,支持通过泛型参数 {@code T} 指定消息内容的类型。它包含了协议的通用字段,
|
||||
* 如协议版本号、加密类型、时间戳和具体消息内容。并通过 Jakarta Bean Validation 提供字段校验支持,
|
||||
* 确保协议数据的完整性和有效性。
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 核心功能:
|
||||
* <ul>
|
||||
* <li>支持协议版本号、加密类型和时间戳等通用字段的定义。</li>
|
||||
* <li>提供字段的校验规则,包括非空校验、范围校验和自定义校验。</li>
|
||||
* <li>支持通过泛型 {@code T} 动态指定消息内容的类型。</li>
|
||||
* <li>支持 JSON 序列化/反序列化,通过 Jackson 注解配置字段顺序和忽略规则。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 使用场景:
|
||||
* <ul>
|
||||
* <li>适用于需要标准化协议格式的场景,如网络通信中的消息封装。</li>
|
||||
* <li>适用于需要对协议字段进行校验的场景,确保数据合法性。</li>
|
||||
* <li>适用于需要通过泛型动态定义消息内容结构的场景。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 使用示例:
|
||||
* <pre>
|
||||
* BaseProtocol<MyMessage> protocol = new BaseProtocol<>();
|
||||
* protocol.setVer(1);
|
||||
* protocol.setCryptoType(0);
|
||||
* protocol.setTimeStamp(System.currentTimeMillis());
|
||||
* protocol.setMsgContent(new MyMessage());
|
||||
* </pre>
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* JSON 输出示例:
|
||||
* <pre>
|
||||
* {
|
||||
* "ver": 1,
|
||||
* "cryptoType": 0,
|
||||
* "timeStamp": 1672531199000,
|
||||
* "msgContent": {
|
||||
* // 消息内容(根据 T 类型决定具体结构)
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
* </p>
|
||||
*
|
||||
* @param <T> 消息内容的类型,由具体业务决定。
|
||||
* @see jakarta.validation.constraints.NotNull
|
||||
* @see org.hibernate.validator.constraints.Range
|
||||
* @see com.cmhi.magent.validation.valids.ValidProtocolTimestamp
|
||||
* @see com.fasterxml.jackson.annotation.JsonPropertyOrder
|
||||
* @see com.fasterxml.jackson.annotation.JsonInclude
|
||||
* @see lombok.Data
|
||||
* @see lombok.NoArgsConstructor
|
||||
* @author huangxin@cmhi.chinamobile.com
|
||||
* @version 1.0.0
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@JsonPropertyOrder({"ver", "cryptoType", "timeStamp", "msgContent"})
|
||||
@JsonIgnoreProperties
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
@SuppressWarnings("java:S1948")
|
||||
public class BaseProtocol<T> implements Serializable {
|
||||
/**
|
||||
* 序列化唯一标识符。
|
||||
*/
|
||||
@Serial
|
||||
private static final long serialVersionUID = -32326523643641L;
|
||||
|
||||
/**
|
||||
* 协议版本号。
|
||||
* <p>
|
||||
* 必填字段,定义协议的版本号,用于兼容性控制。
|
||||
* </p>
|
||||
* <p>
|
||||
* 校验规则:
|
||||
* <ul>
|
||||
* <li>非空校验:必须提供版本号。</li>
|
||||
* <li>值范围校验:1 到 9999。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
* <p>
|
||||
* 示例:
|
||||
* <pre>
|
||||
* protocol.setVer(1);
|
||||
* </pre>
|
||||
* </p>
|
||||
*/
|
||||
@NotNull(message = "ver {item.not_null}", groups = ValidGroups.ProtocolCommonValid.class)
|
||||
@Range(min = 1, max = 9999, message = "ver {item.value_range}", groups = ValidGroups.ProtocolCommonValid.class)
|
||||
private Integer ver;
|
||||
|
||||
/**
|
||||
* 加密类型。
|
||||
* <p>
|
||||
* 必填字段,定义协议使用的加密方式。
|
||||
* </p>
|
||||
* <p>
|
||||
* 校验规则:
|
||||
* <ul>
|
||||
* <li>非空校验:必须提供加密类型。</li>
|
||||
* <li>值范围校验:0 到 4。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
* <p>
|
||||
* 示例:
|
||||
* <pre>
|
||||
* protocol.setCryptoType(0); // 未加密
|
||||
* protocol.setCryptoType(1); // AES 加密
|
||||
* </pre>
|
||||
* </p>
|
||||
*/
|
||||
@NotNull(message = "cryptoType {item.not_null}", groups = ValidGroups.ProtocolCommonValid.class)
|
||||
@Range(min = 0, max = 4, message = "cryptoType {item.value_range}", groups = ValidGroups.ProtocolCommonValid.class)
|
||||
private Integer cryptoType;
|
||||
|
||||
/**
|
||||
* 时间戳。
|
||||
* <p>
|
||||
* 必填字段,定义协议的时间戳,用于记录消息的发送时间。
|
||||
* </p>
|
||||
* <p>
|
||||
* 校验规则:
|
||||
* <ul>
|
||||
* <li>非空校验:必须提供时间戳。</li>
|
||||
* <li>自定义校验:使用 {@link ValidProtocolTimestamp} 验证时间戳的合法性。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
* <p>
|
||||
* 示例:
|
||||
* <pre>
|
||||
* protocol.setTimeStamp(System.currentTimeMillis());
|
||||
* </pre>
|
||||
* </p>
|
||||
*/
|
||||
@NotNull(message = "timeStamp {item.not_null}", groups = ValidGroups.ProtocolCommonValid.class)
|
||||
@ValidProtocolTimestamp(message = "timeStamp {timestamp.timeout}", groups = ValidGroups.ProtocolCommonValid.class)
|
||||
private Long timeStamp;
|
||||
|
||||
/**
|
||||
* 消息内容。
|
||||
* <p>
|
||||
* 可选字段,表示具体的消息内容,由泛型参数 {@code T} 决定其类型。
|
||||
* </p>
|
||||
* <p>
|
||||
* 校验规则:
|
||||
* <ul>
|
||||
* <li>嵌套校验:对 {@code msgContent} 内容进行校验(如果存在)。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
* <p>
|
||||
* 示例:
|
||||
* <pre>
|
||||
* protocol.setMsgContent(new MyMessage());
|
||||
* </pre>
|
||||
* </p>
|
||||
*/
|
||||
@Valid
|
||||
private T msgContent;
|
||||
}
|
|
@ -0,0 +1,142 @@
|
|||
package com.cmhi.magent.pojo.dto;
|
||||
|
||||
import com.cmhi.magent.validation.group.ValidGroups;
|
||||
import com.cmhi.magent.validation.valids.ValidPageSize;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.hibernate.validator.constraints.Range;
|
||||
|
||||
/**
|
||||
* 基础分页请求类 {@code BasePagedReq},用于封装分页请求的基础参数。
|
||||
* <p>
|
||||
* 该类提供了常用的分页参数,包括分页号(pageNumber)、分页大小(pageSize)以及数据总数(totalSize)。
|
||||
* 支持字段的校验规则,确保分页参数的合法性,同时通过 Swagger 注解生成 OpenAPI 文档。
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 核心功能:
|
||||
* <ul>
|
||||
* <li>支持分页请求的通用参数封装。</li>
|
||||
* <li>为分页字段提供校验规则,如分页号的最小值校验、分页大小的范围校验等。</li>
|
||||
* <li>支持通过 Swagger 提供字段的描述信息,用于自动生成接口文档。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 使用场景:
|
||||
* <ul>
|
||||
* <li>适用于需要分页功能的 API 请求,例如列表数据查询接口。</li>
|
||||
* <li>适用于需要对分页参数进行校验的场景,确保请求参数合法性。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 使用示例:
|
||||
* <pre>
|
||||
* BasePagedReq pagedReq = new BasePagedReq();
|
||||
* pagedReq.setPageNumber(1L);
|
||||
* pagedReq.setPageSize(20L);
|
||||
* pagedReq.setTotalSize(100L);
|
||||
* </pre>
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* JSON 输出示例:
|
||||
* <pre>
|
||||
* {
|
||||
* "pageNumber": 1,
|
||||
* "pageSize": 20,
|
||||
* "totalSize": 100
|
||||
* }
|
||||
* </pre>
|
||||
* </p>
|
||||
*
|
||||
* @see jakarta.validation.constraints.NotNull
|
||||
* @see jakarta.validation.constraints.Min
|
||||
* @see org.hibernate.validator.constraints.Range
|
||||
* @see com.cmhi.magent.validation.valids.ValidPageSize
|
||||
* @see io.swagger.v3.oas.annotations.media.Schema
|
||||
* @see lombok.Data
|
||||
* @see lombok.NoArgsConstructor
|
||||
* @see lombok.AllArgsConstructor
|
||||
* @author huangxin@cmhi.chinamobile.com
|
||||
* @version 1.0.0
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
@Schema(name = "BasePagedReq", description = "分页请求基础参数")
|
||||
public class BasePagedReq {
|
||||
/**
|
||||
* 分页号。
|
||||
* <p>
|
||||
* 必填字段,用于指定当前请求的分页页码。最小值为 1。
|
||||
* </p>
|
||||
* <p>
|
||||
* 校验规则:
|
||||
* <ul>
|
||||
* <li>非空校验:分页号不能为空。</li>
|
||||
* <li>最小值校验:分页号必须大于或等于 1。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
* <p>
|
||||
* 示例:
|
||||
* <pre>
|
||||
* pagedReq.setPageNumber(1L);
|
||||
* </pre>
|
||||
* </p>
|
||||
*/
|
||||
@Schema(description = "分页号,最小值为1")
|
||||
@NotNull(message = "pageNumber {item.not_null}", groups = ValidGroups.BasePagedReqValid.class)
|
||||
@Min(value = 1, message = "pageNumber 最小值为1", groups = ValidGroups.BasePagedReqValid.class)
|
||||
private Long pageNumber;
|
||||
|
||||
/**
|
||||
* 分页大小。
|
||||
* <p>
|
||||
* 必填字段,用于指定每页返回的数据条数。最小值为 5,最大值为 100,且必须为 5 的整数倍。
|
||||
* </p>
|
||||
* <p>
|
||||
* 校验规则:
|
||||
* <ul>
|
||||
* <li>非空校验:分页大小不能为空。</li>
|
||||
* <li>范围校验:分页大小必须在 5 到 100 之间。</li>
|
||||
* <li>自定义校验:分页大小必须为 5 的整数倍。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
* <p>
|
||||
* 示例:
|
||||
* <pre>
|
||||
* pagedReq.setPageSize(20L);
|
||||
* </pre>
|
||||
* </p>
|
||||
*/
|
||||
@Schema(description = "分页大小,最小值为5, 最大值100, 取值必须为5的整数倍")
|
||||
@NotNull(message = "pageSize {item.not_null}", groups = ValidGroups.BasePagedReqValid.class)
|
||||
@ValidPageSize(message = "pageSize {page.item_size}", groups = ValidGroups.BasePagedReqValid.class)
|
||||
@Range(min = 5, max = 100, message = "pageSize {item.value_range}", groups = ValidGroups.BasePagedReqValid.class)
|
||||
private Long pageSize;
|
||||
|
||||
/**
|
||||
* 数据总数。
|
||||
* <p>
|
||||
* 可选字段,用于指定分页对应的数据总条数。如果未知,可以设置为空或小于 0。
|
||||
* 该字段在某些场景下可帮助后端优化查询性能。
|
||||
* </p>
|
||||
* <p>
|
||||
* 示例:
|
||||
* <pre>
|
||||
* pagedReq.setTotalSize(100L);
|
||||
* </pre>
|
||||
* </p>
|
||||
*/
|
||||
@Schema(description = "数据总数,未知时可以为空或者小于0, 存在时可以加快后端数据库查询速度")
|
||||
private Long totalSize;
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
package com.cmhi.magent.pojo.dto;
|
||||
|
||||
import com.cmhi.magent.pojo.base.BaseProtocol;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.ToString;
|
||||
|
||||
/**
|
||||
* 协议请求类 {@code ProtocolReq}。
|
||||
* <p>
|
||||
* 该类是一个泛型类,用于封装具体的协议请求数据。它继承自 {@link BaseProtocol},
|
||||
* 保留了协议的通用字段(如版本号、加密类型、时间戳等),同时可以通过泛型 {@code T}
|
||||
* 指定具体的请求内容结构。
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 核心功能:
|
||||
* <ul>
|
||||
* <li>继承自 {@link BaseProtocol},提供协议基础结构的支持。</li>
|
||||
* <li>通过泛型 {@code T} 支持动态定义协议请求的内容。</li>
|
||||
* <li>便于与 `BaseProtocol` 配套使用,用于封装请求数据。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 使用场景:
|
||||
* <ul>
|
||||
* <li>用于封装网络通信中具体的请求协议数据。</li>
|
||||
* <li>用于需要统一协议格式的场景,尤其是与 {@link BaseProtocol} 配套使用。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 继承关系:
|
||||
* <ul>
|
||||
* <li>继承自 {@link BaseProtocol},复用其通用字段和功能。</li>
|
||||
* <li>支持完全相同的字段校验规则。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 使用示例:
|
||||
* <pre>
|
||||
* ProtocolReq<MyRequestData> request = new ProtocolReq<>();
|
||||
* request.setVer(1);
|
||||
* request.setCryptoType(0);
|
||||
* request.setTimeStamp(System.currentTimeMillis());
|
||||
* request.setMsgContent(new MyRequestData());
|
||||
* </pre>
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* JSON 输出示例:
|
||||
* <pre>
|
||||
* {
|
||||
* "ver": 1,
|
||||
* "cryptoType": 0,
|
||||
* "timeStamp": 1672531199000,
|
||||
* "msgContent": {
|
||||
* // 请求内容(根据 T 类型决定具体结构)
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
* </p>
|
||||
*
|
||||
* @param <T> 请求内容的类型,由具体业务决定。
|
||||
* @see BaseProtocol
|
||||
* @see lombok.NoArgsConstructor
|
||||
* @see lombok.ToString
|
||||
* @author huangxin@cmhi.chinamobile.com
|
||||
* @version 1.0.0
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
@NoArgsConstructor
|
||||
@ToString
|
||||
public class ProtocolReq<T> extends BaseProtocol<T> {
|
||||
}
|
|
@ -0,0 +1,157 @@
|
|||
package com.cmhi.magent.pojo.mapper;
|
||||
|
||||
import com.cmhi.magent.pojo.po.HwFirmware;
|
||||
import com.cmhi.magent.pojo.po.HwInfo;
|
||||
import com.cmhi.magent.pojo.po.HwMotherBoard;
|
||||
import com.cmhi.magent.pojo.po.ProcessorInfo;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.Mappings;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
import oshi.hardware.Baseboard;
|
||||
import oshi.hardware.CentralProcessor;
|
||||
import oshi.hardware.ComputerSystem;
|
||||
import oshi.hardware.Firmware;
|
||||
|
||||
/**
|
||||
* 接口 {@code IObjectConvert} 用于对象之间的转换操作,基于 MapStruct 框架实现。
|
||||
* <p>
|
||||
* 该接口的作用是将硬件相关信息从 OSHI 库定义的对象转换为自定义的 PO 对象,
|
||||
* 包括硬件固件信息、主板信息、计算机系统信息以及处理器信息等。
|
||||
* 使用 MapStruct 提供的注解和方法,自动生成代码以实现高效的对象映射。
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 核心功能:
|
||||
* <ul>
|
||||
* <li>将 OSHI 硬件对象(如 {@link Firmware}, {@link Baseboard})映射为自定义 PO 对象。</li>
|
||||
* <li>支持嵌套对象的转换(如将 {@link ComputerSystem} 的固件和主板信息转换为对应的自定义对象)。</li>
|
||||
* <li>通过 MapStruct 的表达式功能实现自定义字段映射逻辑。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* @see Mapper
|
||||
* @see org.mapstruct.factory.Mappers
|
||||
* @see HwFirmware
|
||||
* @see HwMotherBoard
|
||||
* @see HwInfo
|
||||
* @see ProcessorInfo
|
||||
* @see Firmware
|
||||
* @see Baseboard
|
||||
* @see ComputerSystem
|
||||
* @see CentralProcessor
|
||||
* @author huangxin@cmhi.chinamobile.com
|
||||
* @version 1.0.0
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
@Mapper
|
||||
@SuppressWarnings("java:S1710")
|
||||
public interface IObjectConvert {
|
||||
/**
|
||||
* 单例实例,通过 MapStruct 自动生成的实现类进行实例化。
|
||||
*/
|
||||
IObjectConvert INSTANCE = Mappers.getMapper(IObjectConvert.class);
|
||||
|
||||
/**
|
||||
* 将 OSHI 的 {@link Firmware} 对象映射为自定义的 {@link HwFirmware} 对象。
|
||||
* <p>
|
||||
* 字段映射规则:
|
||||
* <ul>
|
||||
* <li>{@code Firmware.getName()} -> {@code HwFirmware.name}</li>
|
||||
* <li>{@code Firmware.getVersion()} -> {@code HwFirmware.version}</li>
|
||||
* <li>{@code Firmware.getDescription()} -> {@code HwFirmware.description}</li>
|
||||
* <li>{@code Firmware.getManufacturer()} -> {@code HwFirmware.manufacturer}</li>
|
||||
* <li>{@code Firmware.getReleaseDate()} -> {@code HwFirmware.releaseDate}</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* @param f {@link Firmware} 对象,表示硬件固件信息。
|
||||
* @return {@link HwFirmware} 对象,表示自定义的硬件固件信息。
|
||||
*/
|
||||
@Mappings({@Mapping(expression = "java(f.getName())", target = "name"),
|
||||
@Mapping(expression = "java(f.getVersion())", target = "version"),
|
||||
@Mapping(expression = "java(f.getDescription())", target = "description"),
|
||||
@Mapping(expression = "java(f.getManufacturer())", target = "manufacturer"),
|
||||
@Mapping(expression = "java(f.getReleaseDate())", target = "releaseDate")})
|
||||
HwFirmware toHwFirmware(Firmware f);
|
||||
|
||||
/**
|
||||
* 将 OSHI 的 {@link Baseboard} 对象映射为自定义的 {@link HwMotherBoard} 对象。
|
||||
* <p>
|
||||
* 字段映射规则:
|
||||
* <ul>
|
||||
* <li>{@code Baseboard.getVersion()} -> {@code HwMotherBoard.version}(去除前后空格)</li>
|
||||
* <li>{@code Baseboard.getManufacturer()} -> {@code HwMotherBoard.manufacturer}</li>
|
||||
* <li>{@code Baseboard.getModel()} -> {@code HwMotherBoard.model}</li>
|
||||
* <li>{@code Baseboard.getSerialNumber()} -> {@code HwMotherBoard.serialNumber}</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* @param b {@link Baseboard} 对象,表示主板信息。
|
||||
* @return {@link HwMotherBoard} 对象,表示自定义的主板信息。
|
||||
*/
|
||||
@Mappings({@Mapping(expression = "java(b.getVersion().trim())", target = "version"),
|
||||
@Mapping(expression = "java(b.getManufacturer())", target = "manufacturer"),
|
||||
@Mapping(expression = "java(b.getModel())", target = "model"),
|
||||
@Mapping(expression = "java(b.getSerialNumber())", target = "serialNumber")})
|
||||
HwMotherBoard toHwMotherBoard(Baseboard b);
|
||||
|
||||
/**
|
||||
* 将 OSHI 的 {@link ComputerSystem} 对象映射为自定义的 {@link HwInfo} 对象。
|
||||
* <p>
|
||||
* 字段映射规则:
|
||||
* <ul>
|
||||
* <li>{@code ComputerSystem.getModel()} -> {@code HwInfo.model}</li>
|
||||
* <li>{@code ComputerSystem.getManufacturer()} -> {@code HwInfo.manufacturer}</li>
|
||||
* <li>{@code ComputerSystem.getHardwareUUID()} -> {@code HwInfo.uuid}</li>
|
||||
* <li>{@code ComputerSystem.getSerialNumber()} -> {@code HwInfo.serialNumber}</li>
|
||||
* <li>{@code ComputerSystem.getFirmware()} -> {@code HwInfo.firmware}</li>
|
||||
* <li>{@code ComputerSystem.getBaseboard()} -> {@code HwInfo.mb}</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* @param c {@link ComputerSystem} 对象,表示计算机系统信息。
|
||||
* @return {@link HwInfo} 对象,表示自定义的计算机系统信息。
|
||||
*/
|
||||
@Mappings({@Mapping(expression = "java(c.getModel())", target = "model"),
|
||||
@Mapping(expression = "java(c.getManufacturer())", target = "manufacturer"),
|
||||
@Mapping(expression = "java(c.getHardwareUUID())", target = "uuid"),
|
||||
@Mapping(expression = "java(c.getSerialNumber())", target = "serialNumber"),
|
||||
@Mapping(expression = "java(toHwFirmware(c.getFirmware()))", target = "firmware"),
|
||||
@Mapping(expression = "java(toHwMotherBoard(c.getBaseboard()))", target = "mb")})
|
||||
HwInfo toHwInfo(ComputerSystem c);
|
||||
|
||||
/**
|
||||
* 将 OSHI 的 {@link CentralProcessor} 对象映射为自定义的 {@link ProcessorInfo} 对象。
|
||||
* <p>
|
||||
* 字段映射规则:
|
||||
* <ul>
|
||||
* <li>{@code CentralProcessor.getProcessorIdentifier().getVendor()} -> {@code ProcessorInfo.cpuVendor}</li>
|
||||
* <li>{@code CentralProcessor.getProcessorIdentifier().getName()} -> {@code ProcessorInfo.cpuName}</li>
|
||||
* <li>{@code CentralProcessor.getProcessorIdentifier().getProcessorID()} -> {@code ProcessorInfo.processorId}</li>
|
||||
* <li>{@code CentralProcessor.getProcessorIdentifier().getIdentifier()} -> {@code ProcessorInfo.cpuIdentifier}</li>
|
||||
* <li>{@code CentralProcessor.getProcessorIdentifier().getMicroarchitecture()} -> {@code ProcessorInfo.microArchitecture}</li>
|
||||
* <li>{@code CentralProcessor.getProcessorIdentifier().isCpu64bit()} -> {@code ProcessorInfo.cpu64bit}</li>
|
||||
* <li>{@code CentralProcessor.getMaxFreq()} -> {@code ProcessorInfo.cpuVendorFreq}</li>
|
||||
* <li>{@code CentralProcessor.getPhysicalPackageCount()} -> {@code ProcessorInfo.physicalCpus}</li>
|
||||
* <li>{@code CentralProcessor.getPhysicalProcessorCount()} -> {@code ProcessorInfo.physicalCores}</li>
|
||||
* <li>{@code CentralProcessor.getLogicalProcessorCount()} -> {@code ProcessorInfo.logicalCpu}</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* @param p {@link CentralProcessor} 对象,表示处理器信息。
|
||||
* @param i {@link CentralProcessor.ProcessorIdentifier} 对象,表示详细的处理器标识信息。
|
||||
* @return {@link ProcessorInfo} 对象,表示自定义的处理器信息。
|
||||
*/
|
||||
@Mappings({@Mapping(expression = "java(p.getProcessorIdentifier().getVendor())", target = "cpuVendor"),
|
||||
@Mapping(expression = "java(p.getProcessorIdentifier().getName())", target = "cpuName"),
|
||||
@Mapping(expression = "java(p.getProcessorIdentifier().getProcessorID())", target = "processorId"),
|
||||
@Mapping(expression = "java(p.getProcessorIdentifier().getIdentifier())", target = "cpuIdentifier"),
|
||||
@Mapping(expression = "java(p.getProcessorIdentifier().getMicroarchitecture())", target = "microArchitecture"),
|
||||
@Mapping(expression = "java(p.getProcessorIdentifier().isCpu64bit())", target = "cpu64bit"),
|
||||
@Mapping(expression = "java(p.getMaxFreq())", target = "cpuVendorFreq"),
|
||||
@Mapping(expression = "java(p.getPhysicalPackageCount())", target = "physicalCpus"),
|
||||
@Mapping(expression = "java(p.getPhysicalProcessorCount())", target = "physicalCores"),
|
||||
@Mapping(expression = "java(p.getLogicalProcessorCount())", target = "logicalCpu")})
|
||||
ProcessorInfo toProcessorInfo(CentralProcessor p, CentralProcessor.ProcessorIdentifier i);
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
package com.cmhi.magent.pojo.po;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* API 上下文信息封装类 {@code ApiContext}。
|
||||
* <p>
|
||||
* 该类用于封装与 API 调用相关的上下文信息,常用于追踪请求、认证和用户信息。
|
||||
* 通过封装请求的本地信息、请求体、时间戳、用户信息等字段,便于后续的日志记录、调试和分析。
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 核心功能:
|
||||
* <ul>
|
||||
* <li>封装 API 请求的上下文信息,如请求的本地信息、请求体、时间戳等。</li>
|
||||
* <li>支持用户认证相关字段,如 JWT、用户名和用户 ID。</li>
|
||||
* <li>易于扩展,便于在多种场景下使用。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 使用场景:
|
||||
* <ul>
|
||||
* <li>用于记录 API 请求的上下文信息,支持日志记录和调试。</li>
|
||||
* <li>支持后端服务间传递上下文信息,便于追踪和分析请求流向。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 示例:
|
||||
* <pre>
|
||||
* ApiContext context = ApiContext.builder()
|
||||
* .reqLocal("127.0.0.1")
|
||||
* .requestBody("{\"key\":\"value\"}")
|
||||
* .requestTime(System.currentTimeMillis())
|
||||
* .jwt("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...")
|
||||
* .username("user123")
|
||||
* .uid("1001")
|
||||
* .id(1L)
|
||||
* .build();
|
||||
* </pre>
|
||||
* </p>
|
||||
*
|
||||
* @author huangxin@cmhi.chinamobile.com
|
||||
* @version 1.0.0
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class ApiContext {
|
||||
|
||||
/**
|
||||
* 请求来源的本地信息。
|
||||
* <p>
|
||||
* 表示请求来源的 IP 地址或本地标识。例如,"127.0.0.1" 表示本机。
|
||||
* </p>
|
||||
*/
|
||||
private String reqLocal;
|
||||
|
||||
/**
|
||||
* 请求体内容。
|
||||
* <p>
|
||||
* 表示当前请求的具体内容,可以是 JSON 字符串或其他格式的请求数据。
|
||||
* </p>
|
||||
*/
|
||||
private String requestBody;
|
||||
|
||||
/**
|
||||
* 请求时间戳。
|
||||
* <p>
|
||||
* 表示请求的发起时间,采用毫秒级时间戳(例如通过 {@code System.currentTimeMillis()} 获取)。
|
||||
* </p>
|
||||
*/
|
||||
private Long requestTime;
|
||||
|
||||
/**
|
||||
* 用户的 JWT(JSON Web Token)。
|
||||
* <p>
|
||||
* 表示用户的认证信息,通过 JWT 进行身份验证和授权。
|
||||
* </p>
|
||||
*/
|
||||
private String jwt;
|
||||
|
||||
/**
|
||||
* 用户名。
|
||||
* <p>
|
||||
* 表示当前请求用户的用户名,用于标识用户的身份。
|
||||
* </p>
|
||||
*/
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* 用户唯一标识符。
|
||||
* <p>
|
||||
* 表示用户的唯一 ID,用于区分不同的用户。
|
||||
* </p>
|
||||
*/
|
||||
private String uid;
|
||||
|
||||
/**
|
||||
* 请求的唯一标识符。
|
||||
* <p>
|
||||
* 表示该请求的唯一 ID,可用于请求追踪或日志记录。
|
||||
* </p>
|
||||
*/
|
||||
private Long id;
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
package com.cmhi.magent.pojo.po;
|
||||
|
||||
import com.cmhi.magent.common.ErrorCode;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 通用响应状态封装类 {@code BaseRespStatus}。
|
||||
* <p>
|
||||
* 该类用于封装接口响应的基础状态信息,包括状态码 {@code status} 和消息 {@code message}。
|
||||
* 默认状态为成功(状态码为 {@code ErrorCode.ERR_OK.getCode()},消息为 {@code ErrorCode.ERR_OK.getDescription()})。
|
||||
* 具体接口可以通过继承该类扩展其他字段以适配业务需要。
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 核心功能:
|
||||
* <ul>
|
||||
* <li>提供统一的状态码字段 {@code status},便于接口响应的状态管理。</li>
|
||||
* <li>支持多条消息描述字段 {@code message},便于传递更详细的状态信息。</li>
|
||||
* <li>默认状态为成功,便于简化正常响应的构建。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 使用场景:
|
||||
* <ul>
|
||||
* <li>适用于所有需要返回状态码和状态描述的接口。</li>
|
||||
* <li>作为基础类,支持扩展以适配更多字段需求。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* JSON 输出示例:
|
||||
* <pre>
|
||||
* {
|
||||
* "status": 0,
|
||||
* "message": ["操作成功"]
|
||||
* }
|
||||
* </pre>
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 默认值说明:
|
||||
* <ul>
|
||||
* <li>{@code status} 默认值为 {@code ErrorCode.ERR_OK.getCode()},表示操作成功。</li>
|
||||
* <li>{@code message} 默认值为 {@code ErrorCode.ERR_OK.getDescription()},即 "操作成功"。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* @see ErrorCode
|
||||
* @author huangxin@cmhi.chinamobile.com
|
||||
* @version 1.0.0
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class BaseRespStatus {
|
||||
|
||||
/**
|
||||
* 响应状态码。
|
||||
* <p>
|
||||
* 表示接口调用的执行状态,默认值为 {@code ErrorCode.ERR_OK.getCode()},即操作成功。
|
||||
* 可以通过 {@link ErrorCode} 枚举类获取所有状态码。
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 示例:
|
||||
* <ul>
|
||||
* <li>0:操作成功。</li>
|
||||
* <li>1:操作失败。</li>
|
||||
* <li>400:请求参数错误。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* @see ErrorCode
|
||||
*/
|
||||
@Schema(description = "响应状态码。0表示成功,其他值表示失败,可参考ErrorCode类。", example = "0")
|
||||
private Integer status = ErrorCode.ERR_OK.getCode();
|
||||
|
||||
/**
|
||||
* 响应消息描述。
|
||||
* <p>
|
||||
* 表示接口调用的状态描述信息,支持多条消息内容。
|
||||
* 默认值为 {@code ErrorCode.ERR_OK.getDescription()},即 "操作成功"。
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 示例:
|
||||
* <ul>
|
||||
* <li>["操作成功"]</li>
|
||||
* <li>["参数错误", "字段name不能为空"]</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*/
|
||||
@Schema(description = "响应消息描述,支持多条消息内容。", example = "[\"操作成功\"]")
|
||||
private String[] message = new String[] {ErrorCode.ERR_OK.getDescription()};
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
package com.cmhi.magent.pojo.po;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 硬件固件信息类 {@code HwFirmware}。
|
||||
* <p>
|
||||
* 用于描述硬件固件的基本信息,包括名称、版本、描述、制造商以及发布日期等字段。
|
||||
* 该类可用于描述设备固件属性,便于在系统中进行固件信息的管理和展示。
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 核心功能:
|
||||
* <ul>
|
||||
* <li>封装固件的基本信息字段,如名称、版本、描述等。</li>
|
||||
* <li>通过 {@link JsonPropertyOrder} 注解定义 JSON 输出字段顺序。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 使用场景:
|
||||
* <ul>
|
||||
* <li>用于描述设备或硬件的固件详细信息。</li>
|
||||
* <li>可应用于系统固件管理模块。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* JSON 输出示例:
|
||||
* <pre>
|
||||
* {
|
||||
* "name": "固件A",
|
||||
* "version": "1.0.0",
|
||||
* "manufacturer": "公司XYZ",
|
||||
* "releaseDate": "2025-01-01",
|
||||
* "description": "这是固件的描述信息"
|
||||
* }
|
||||
* </pre>
|
||||
* </p>
|
||||
*
|
||||
* @author huangxin@cmhi.chinamobile.com
|
||||
* @version 1.0.0
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
@Data
|
||||
@JsonPropertyOrder({"name", "version", "manufacturer", "releaseDate", "description"})
|
||||
public class HwFirmware {
|
||||
|
||||
/**
|
||||
* 固件名称。
|
||||
* <p>
|
||||
* 表示固件的完整名称,例如 "CPU固件"。
|
||||
* </p>
|
||||
*/
|
||||
@Schema(description = "固件名称,用于标识具体固件。", example = "CPU固件")
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 固件版本。
|
||||
* <p>
|
||||
* 表示固件的版本号,例如 "1.0.0"。
|
||||
* </p>
|
||||
*/
|
||||
@Schema(description = "固件的版本号。", example = "1.0.0")
|
||||
private String version;
|
||||
|
||||
/**
|
||||
* 固件描述。
|
||||
* <p>
|
||||
* 描述固件的详细信息,例如用途或特性。
|
||||
* </p>
|
||||
*/
|
||||
@Schema(description = "固件的详细描述信息。", example = "这是固件的描述信息")
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 制造商名称。
|
||||
* <p>
|
||||
* 表示固件的制造商或开发公司,例如 "公司XYZ"。
|
||||
* </p>
|
||||
*/
|
||||
@Schema(description = "固件制造商的名称。", example = "公司XYZ")
|
||||
private String manufacturer;
|
||||
|
||||
/**
|
||||
* 固件发布日期。
|
||||
* <p>
|
||||
* 表示固件的发布时间,使用 ISO 8601 格式的日期字符串,例如 "2025-01-01"。
|
||||
* </p>
|
||||
*/
|
||||
@Schema(description = "固件的发布日期,格式为YYYY-MM-DD。", example = "2025-01-01")
|
||||
private String releaseDate;
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
package com.cmhi.magent.pojo.po;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 硬件信息类 {@code HwInfo}。
|
||||
* <p>
|
||||
* 用于描述设备硬件的基本信息,包括型号、制造商、唯一标识符、序列号、固件信息以及主板信息等。
|
||||
* 该类可以作为设备硬件管理模块的核心数据模型,用于记录和展示设备的详细硬件属性。
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 核心功能:
|
||||
* <ul>
|
||||
* <li>封装设备的基本硬件信息(如型号、制造商等)。</li>
|
||||
* <li>支持嵌套其他硬件相关类(如固件信息和主板信息)。</li>
|
||||
* <li>通过 {@link JsonPropertyOrder} 注解定义 JSON 输出字段顺序。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 使用场景:
|
||||
* <ul>
|
||||
* <li>用于描述设备的硬件信息。</li>
|
||||
* <li>可应用于硬件管理或设备配置展示模块。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* JSON 输出示例:
|
||||
* <pre>
|
||||
* {
|
||||
* "model": "设备型号A",
|
||||
* "manufacturer": "公司XYZ",
|
||||
* "uuid": "123e4567-e89b-12d3-a456-426655440000",
|
||||
* "serialNumber": "SN123456789",
|
||||
* "firmware": {
|
||||
* "name": "固件A",
|
||||
* "version": "1.0.0",
|
||||
* "manufacturer": "公司XYZ",
|
||||
* "releaseDate": "2025-01-01",
|
||||
* "description": "这是固件的描述信息"
|
||||
* },
|
||||
* "mb": {
|
||||
* "name": "主板型号B",
|
||||
* "chipset": "芯片组XYZ",
|
||||
* "manufacturer": "公司XYZ",
|
||||
* "serialNumber": "MB123456789"
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
* </p>
|
||||
*
|
||||
* @author huangxin@cmhi.chinamobile.com
|
||||
* @version 1.0.0
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
@Data
|
||||
@JsonPropertyOrder({"model", "manufacturer", "uuid", "serialNumber", "firmware", "mb"})
|
||||
public class HwInfo {
|
||||
|
||||
/**
|
||||
* 硬件型号。
|
||||
* <p>
|
||||
* 表示设备的型号名称,例如 "设备型号A"。
|
||||
* </p>
|
||||
*/
|
||||
@Schema(description = "硬件型号名称。", example = "设备型号A")
|
||||
private String model;
|
||||
|
||||
/**
|
||||
* 硬件制造商。
|
||||
* <p>
|
||||
* 表示设备的制造商名称,例如 "公司XYZ"。
|
||||
* </p>
|
||||
*/
|
||||
@Schema(description = "硬件制造商的名称。", example = "公司XYZ")
|
||||
private String manufacturer;
|
||||
|
||||
/**
|
||||
* 硬件唯一标识符。
|
||||
* <p>
|
||||
* 表示硬件的 UUID,例如 "123e4567-e89b-12d3-a456-426655440000"。
|
||||
* </p>
|
||||
*/
|
||||
@Schema(description = "硬件的唯一标识符 (UUID)。", example = "123e4567-e89b-12d3-a456-426655440000")
|
||||
private String uuid;
|
||||
|
||||
/**
|
||||
* 硬件序列号。
|
||||
* <p>
|
||||
* 表示设备的序列号,例如 "SN123456789"。
|
||||
* </p>
|
||||
*/
|
||||
@Schema(description = "硬件的序列号。", example = "SN123456789")
|
||||
private String serialNumber;
|
||||
|
||||
/**
|
||||
* 固件信息。
|
||||
* <p>
|
||||
* 使用 {@link HwFirmware} 类型,表示硬件的固件详细信息,包括名称、版本、制造商等。
|
||||
* </p>
|
||||
*/
|
||||
@Schema(description = "硬件的固件信息,包括固件名称、版本、制造商等。")
|
||||
private HwFirmware firmware;
|
||||
|
||||
/**
|
||||
* 主板信息。
|
||||
* <p>
|
||||
* 使用 {@link HwMotherBoard} 类型,表示硬件主板的详细信息,包括名称、芯片组、制造商等。
|
||||
* </p>
|
||||
*/
|
||||
@Schema(description = "硬件的主板信息,包括主板型号、芯片组、制造商等。")
|
||||
private HwMotherBoard mb;
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
package com.cmhi.magent.pojo.po;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 主板信息类 {@code HwMotherBoard}。
|
||||
* <p>
|
||||
* 用于描述硬件主板的基本信息,包括版本号、制造商、型号以及序列号等字段。
|
||||
* 该类可作为设备硬件管理模块中的主板属性描述,便于记录和展示主板的详细信息。
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 核心功能:
|
||||
* <ul>
|
||||
* <li>封装主板的基本属性字段(如版本号、制造商等)。</li>
|
||||
* <li>通过 {@link JsonPropertyOrder} 注解定义 JSON 输出字段顺序。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 使用场景:
|
||||
* <ul>
|
||||
* <li>用于描述设备主板的基本信息,例如系统硬件配置模块。</li>
|
||||
* <li>可用于设备主板信息的查询和展示。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* JSON 输出示例:
|
||||
* <pre>
|
||||
* {
|
||||
* "version": "1.0.0",
|
||||
* "manufacturer": "公司XYZ",
|
||||
* "model": "主板型号A",
|
||||
* "serialNumber": "MB123456789"
|
||||
* }
|
||||
* </pre>
|
||||
* </p>
|
||||
*
|
||||
* @author huangxin@cmhi.chinamobile.com
|
||||
* @version 1.0.0
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
@Data
|
||||
@JsonPropertyOrder({"version", "manufacturer", "model", "serialNumber"})
|
||||
public class HwMotherBoard {
|
||||
|
||||
/**
|
||||
* 主板版本号。
|
||||
* <p>
|
||||
* 表示主板的版本,例如 "1.0.0"。
|
||||
* </p>
|
||||
*/
|
||||
@Schema(description = "主板的版本号。", example = "1.0.0")
|
||||
private String version;
|
||||
|
||||
/**
|
||||
* 主板制造商。
|
||||
* <p>
|
||||
* 表示主板的制造商名称,例如 "公司XYZ"。
|
||||
* </p>
|
||||
*/
|
||||
@Schema(description = "主板的制造商名称。", example = "公司XYZ")
|
||||
private String manufacturer;
|
||||
|
||||
/**
|
||||
* 主板型号名称。
|
||||
* <p>
|
||||
* 表示主板的型号,例如 "主板型号A"。
|
||||
* </p>
|
||||
*/
|
||||
@Schema(description = "主板的型号名称。", example = "主板型号A")
|
||||
private String model;
|
||||
|
||||
/**
|
||||
* 主板序列号。
|
||||
* <p>
|
||||
* 表示主板的唯一序列号,例如 "MB123456789"。
|
||||
* </p>
|
||||
*/
|
||||
@Schema(description = "主板的序列号。", example = "MB123456789")
|
||||
private String serialNumber;
|
||||
}
|
|
@ -0,0 +1,188 @@
|
|||
package com.cmhi.magent.pojo.po;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
|
||||
/**
|
||||
* 操作日志类 {@code OperationLog}。
|
||||
* <p>
|
||||
* 用于记录系统中用户或服务的操作行为,包含模块名称、操作类型、操作状态、请求信息、
|
||||
* 执行耗时等详细字段,便于系统审计和问题排查。
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 核心功能:
|
||||
* <ul>
|
||||
* <li>记录操作的基本信息(如模块名称、操作类型等)。</li>
|
||||
* <li>包含请求和响应的详细信息(如 HTTP 方法、路径、头信息等)。</li>
|
||||
* <li>记录操作耗时和延迟,便于分析性能。</li>
|
||||
* <li>支持通过构造器或建造者模式创建对象。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 使用场景:
|
||||
* <ul>
|
||||
* <li>用于记录用户操作日志,例如管理系统中的关键行为。</li>
|
||||
* <li>应用于接口访问日志记录模块,用于审计和回溯。</li>
|
||||
* <li>便于监控和优化系统性能。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 示例:
|
||||
* <pre>
|
||||
* OperationLog log = OperationLog.builder()
|
||||
* .module("用户管理")
|
||||
* .operationType("查询")
|
||||
* .operationStatus("成功")
|
||||
* .description("查询用户列表")
|
||||
* .requestIp("192.168.0.1")
|
||||
* .httpMethod("GET")
|
||||
* .httpPath("/api/users")
|
||||
* .expendTime(120L)
|
||||
* .operationTime(new Timestamp(System.currentTimeMillis()))
|
||||
* .build();
|
||||
* </pre>
|
||||
* </p>
|
||||
*
|
||||
* @author huangxin@cmhi.chinamobile.com
|
||||
* @version 1.0.0
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class OperationLog {
|
||||
|
||||
/**
|
||||
* 模块名称。
|
||||
* <p>
|
||||
* 表示操作所属的业务模块,例如 "用户管理"。
|
||||
* </p>
|
||||
*/
|
||||
@Schema(description = "操作所属的业务模块。", example = "用户管理")
|
||||
private String module;
|
||||
|
||||
/**
|
||||
* 操作类型。
|
||||
* <p>
|
||||
* 表示操作的类别,例如 "查询"、"新增"、"删除"。
|
||||
* </p>
|
||||
*/
|
||||
@Schema(description = "操作的类型或类别。", example = "查询")
|
||||
private String operationType;
|
||||
|
||||
/**
|
||||
* 操作状态。
|
||||
* <p>
|
||||
* 表示操作的结果状态,例如 "成功" 或 "失败"。
|
||||
* </p>
|
||||
*/
|
||||
@Schema(description = "操作的执行状态。", example = "成功")
|
||||
private String operationStatus;
|
||||
|
||||
/**
|
||||
* 操作描述。
|
||||
* <p>
|
||||
* 对操作的简要说明,例如 "查询用户列表"。
|
||||
* </p>
|
||||
*/
|
||||
@Schema(description = "操作的简要描述。", example = "查询用户列表")
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 请求来源 IP。
|
||||
* <p>
|
||||
* 记录操作的来源 IP,例如 "192.168.0.1"。
|
||||
* </p>
|
||||
*/
|
||||
@Schema(description = "请求的来源 IP 地址。", example = "192.168.0.1")
|
||||
private String requestIp;
|
||||
|
||||
/**
|
||||
* 调用的函数或方法名称。
|
||||
* <p>
|
||||
* 表示执行操作时调用的方法名称。</p>
|
||||
*/
|
||||
@Schema(description = "执行操作时调用的函数或方法名称。", example = "getUserList")
|
||||
private String callFunction;
|
||||
|
||||
/**
|
||||
* HTTP 请求方法。
|
||||
* <p>
|
||||
* 记录操作对应的 HTTP 请求方法,例如 "GET"、"POST"。
|
||||
* </p>
|
||||
*/
|
||||
@Schema(description = "HTTP 请求的请求方法。", example = "GET")
|
||||
private String httpMethod;
|
||||
|
||||
/**
|
||||
* HTTP 请求路径。
|
||||
* <p>
|
||||
* 表示操作访问的具体接口路径,例如 "/api/users"。
|
||||
* </p>
|
||||
*/
|
||||
@Schema(description = "HTTP 请求的接口路径。", example = "/api/users")
|
||||
private String httpPath;
|
||||
|
||||
/**
|
||||
* 操作耗时(毫秒)。
|
||||
* <p>
|
||||
* 表示操作执行的时间消耗,例如 120 毫秒。
|
||||
* </p>
|
||||
*/
|
||||
@Schema(description = "操作执行的耗时(单位:毫秒)。", example = "120")
|
||||
private Long expendTime;
|
||||
|
||||
/**
|
||||
* 数据传输延迟(毫秒)。
|
||||
* <p>
|
||||
* 表示操作中数据传输的延迟时间。</p>
|
||||
*/
|
||||
@Schema(description = "数据传输的延迟时间(单位:毫秒)。", example = "15")
|
||||
private Long transmitDelay;
|
||||
|
||||
/**
|
||||
* 请求头。
|
||||
* <p>
|
||||
* 记录 HTTP 请求的头部信息,通常以 JSON 字符串形式存储。
|
||||
* </p>
|
||||
*/
|
||||
@Schema(description = "HTTP 请求头信息(以 JSON 格式存储)。",
|
||||
example = "{ 'Content-Type': 'application/json' }")
|
||||
private String requestHeaders;
|
||||
|
||||
/**
|
||||
* 请求内容。
|
||||
* <p>
|
||||
* 记录 HTTP 请求的具体内容,例如请求体。
|
||||
* </p>
|
||||
*/
|
||||
@Schema(description = "HTTP 请求的具体内容。", example = "{ 'username': 'admin' }")
|
||||
private String request;
|
||||
|
||||
/**
|
||||
* 响应结果。
|
||||
* <p>
|
||||
* 记录操作的响应结果,例如返回值或错误信息。
|
||||
* </p>
|
||||
*/
|
||||
@Schema(description = "操作的响应结果。", example = "{ 'status': 200, 'message': 'Success' }")
|
||||
private String result;
|
||||
|
||||
/**
|
||||
* 操作时间。
|
||||
* <p>
|
||||
* 表示记录操作的具体时间戳。
|
||||
* </p>
|
||||
*/
|
||||
@Schema(description = "操作发生的时间。", example = "2023-01-07T12:00:00.000+00:00")
|
||||
private Timestamp operationTime;
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
package com.cmhi.magent.pojo.po;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 分页结果类 {@code PageResults}。
|
||||
* <p>
|
||||
* 用于封装分页查询结果,包括当前页码、页大小、总页数、总行数以及数据项列表。
|
||||
* 支持通过泛型参数 {@code T} 指定数据项的类型,适用于常见的分页接口场景。
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 核心功能:
|
||||
* <ul>
|
||||
* <li>支持分页结果的数据封装(如页码、总行数等)。</li>
|
||||
* <li>通过泛型参数 {@code T} 表示数据项的类型,便于复用。</li>
|
||||
* <li>通过 {@link JsonPropertyOrder} 注解定义 JSON 输出字段顺序。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 使用场景:
|
||||
* <ul>
|
||||
* <li>用于 RESTful API 的分页响应结果封装。</li>
|
||||
* <li>适用于分页查询接口,例如列表数据查询等。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* JSON 输出示例:
|
||||
* <pre>
|
||||
* {
|
||||
* "pageNumber": 1,
|
||||
* "pageSize": 10,
|
||||
* "totalPage": 5,
|
||||
* "totalRow": 50,
|
||||
* "items": [
|
||||
* { "id": 1, "name": "Item 1" },
|
||||
* { "id": 2, "name": "Item 2" }
|
||||
* ]
|
||||
* }
|
||||
* </pre>
|
||||
* </p>
|
||||
*
|
||||
* @param <T> 数据项的类型。
|
||||
*
|
||||
* @author huangxin@cmhi.chinamobile.com
|
||||
* @version 1.0.0
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@JsonPropertyOrder({"pageNumber", "pageSize", "totalPage", "totalRow", "items"})
|
||||
public class PageResults<T> {
|
||||
|
||||
/**
|
||||
* 数据项列表。
|
||||
* <p>
|
||||
* 表示当前页返回的具体数据项,类型由泛型参数 {@code T} 决定。
|
||||
* </p>
|
||||
*/
|
||||
@Schema(description = "当前页的具体数据项列表。",
|
||||
example = "[{ 'id': 1, 'name': 'Item 1' }, { 'id': 2, 'name': 'Item 2' }]")
|
||||
private List<T> items;
|
||||
|
||||
/**
|
||||
* 当前页码。
|
||||
* <p>
|
||||
* 表示当前分页的页码,从 1 开始计数。
|
||||
* </p>
|
||||
*/
|
||||
@Schema(description = "当前分页的页码,从 1 开始。", example = "1")
|
||||
private long pageNumber;
|
||||
|
||||
/**
|
||||
* 每页大小。
|
||||
* <p>
|
||||
* 表示每页包含的数据项数量。
|
||||
* </p>
|
||||
*/
|
||||
@Schema(description = "每页包含的数据项数量。", example = "10")
|
||||
private long pageSize;
|
||||
|
||||
/**
|
||||
* 总页数。
|
||||
* <p>
|
||||
* 表示分页结果的总页数。
|
||||
* </p>
|
||||
*/
|
||||
@Schema(description = "分页结果的总页数。", example = "5")
|
||||
private long totalPage;
|
||||
|
||||
/**
|
||||
* 总行数。
|
||||
* <p>
|
||||
* 表示所有数据项的总行数。
|
||||
* </p>
|
||||
*/
|
||||
@Schema(description = "所有数据项的总行数。", example = "50")
|
||||
private long totalRow;
|
||||
}
|
|
@ -0,0 +1,147 @@
|
|||
package com.cmhi.magent.pojo.po;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 处理器信息类 {@code ProcessorInfo}。
|
||||
* <p>
|
||||
* 用于描述系统处理器(CPU)的详细信息,包括处理器的厂商、型号、架构等关键属性。
|
||||
* 该类可用于系统硬件管理模块,便于采集和展示处理器详细信息。
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 核心功能:
|
||||
* <ul>
|
||||
* <li>封装处理器的基本信息(如厂商、名称、架构等)。</li>
|
||||
* <li>支持通过 {@link JsonPropertyOrder} 注解定义 JSON 输出顺序。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 使用场景:
|
||||
* <ul>
|
||||
* <li>用于描述设备的处理器详细信息,例如服务器或个人计算机的处理器配置。</li>
|
||||
* <li>适用于硬件设备监控或系统配置展示模块。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* JSON 输出示例:
|
||||
* <pre>
|
||||
* {
|
||||
* "cpuVendor": "Intel",
|
||||
* "cpuName": "Intel(R) Core(TM) i7-10700K CPU @ 3.80GHz",
|
||||
* "processorId": "BFEBFBFF000306C3",
|
||||
* "cpuIdentifier": "x86_64 Family 6 Model 158 Stepping 10",
|
||||
* "microArchitecture": "Comet Lake",
|
||||
* "cpu64bit": true,
|
||||
* "cpuVendorFreq": 3800000000,
|
||||
* "physicalCpus": 1,
|
||||
* "physicalCores": 8,
|
||||
* "logicalCpu": 16
|
||||
* }
|
||||
* </pre>
|
||||
* </p>
|
||||
*
|
||||
* @author huangxin@cmhi.chinamobile.com
|
||||
* @version 1.0.0
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
@Data
|
||||
@JsonPropertyOrder({"cpuVendor", "cpuName", "processorId", "cpuIdentifier", "microArchitecture", "cpu64bit", "cpuVendorFreq",
|
||||
"physicalCpus", "physicalCores", "logicalCpu"})
|
||||
public class ProcessorInfo {
|
||||
|
||||
/**
|
||||
* CPU 厂商。
|
||||
* <p>
|
||||
* 表示处理器的制造商名称,例如 "Intel"、"AMD"。
|
||||
* </p>
|
||||
*/
|
||||
@Schema(description = "处理器的制造商名称。", example = "Intel")
|
||||
private String cpuVendor;
|
||||
|
||||
/**
|
||||
* CPU 名称。
|
||||
* <p>
|
||||
* 表示处理器的完整型号名称,例如 "Intel(R) Core(TM) i7-10700K CPU @ 3.80GHz"。
|
||||
* </p>
|
||||
*/
|
||||
@Schema(description = "处理器的完整型号名称。",
|
||||
example = "Intel(R) Core(TM) i7-10700K CPU @ 3.80GHz")
|
||||
private String cpuName;
|
||||
|
||||
/**
|
||||
* CPU 标识符(Processor ID)。
|
||||
* <p>
|
||||
* 表示处理器的唯一标识符,例如 "BFEBFBFF000306C3"。
|
||||
* </p>
|
||||
*/
|
||||
@Schema(description = "处理器的唯一标识符。", example = "BFEBFBFF000306C3")
|
||||
private String processorId;
|
||||
|
||||
/**
|
||||
* CPU 标识信息。
|
||||
* <p>
|
||||
* 提供额外的处理器标识信息,例如 "x86_64 Family 6 Model 158 Stepping 10"。
|
||||
* </p>
|
||||
*/
|
||||
@Schema(description = "处理器的标识信息。", example = "x86_64 Family 6 Model 158 Stepping 10")
|
||||
private String cpuIdentifier;
|
||||
|
||||
/**
|
||||
* 微架构(Micro-Architecture)。
|
||||
* <p>
|
||||
* 表示处理器的微架构名称,例如 "Comet Lake"。
|
||||
* </p>
|
||||
*/
|
||||
@Schema(description = "处理器的微架构名称。", example = "Comet Lake")
|
||||
private String microArchitecture;
|
||||
|
||||
/**
|
||||
* 是否为 64 位架构。
|
||||
* <p>
|
||||
* 表示处理器是否支持 64 位架构。
|
||||
* </p>
|
||||
*/
|
||||
@Schema(description = "处理器是否支持 64 位架构。", example = "true")
|
||||
private Boolean cpu64bit;
|
||||
|
||||
/**
|
||||
* CPU 主频(Hz)。
|
||||
* <p>
|
||||
* 表示处理器的厂商标称主频(单位:Hz),例如 3800000000 表示 3.80GHz。
|
||||
* </p>
|
||||
*/
|
||||
@Schema(description = "处理器的标称主频(单位:Hz)。", example = "3800000000")
|
||||
private Long cpuVendorFreq;
|
||||
|
||||
/**
|
||||
* 物理 CPU 数量。
|
||||
* <p>
|
||||
* 表示系统中物理处理器的数量。
|
||||
* </p>
|
||||
*/
|
||||
@Schema(description = "系统中物理处理器的数量。", example = "1")
|
||||
private Integer physicalCpus;
|
||||
|
||||
/**
|
||||
* 物理核心数量。
|
||||
* <p>
|
||||
* 表示处理器的物理核心数,例如 8 核。
|
||||
* </p>
|
||||
*/
|
||||
@Schema(description = "处理器的物理核心数。", example = "8")
|
||||
private Integer physicalCores;
|
||||
|
||||
/**
|
||||
* 逻辑核心数量。
|
||||
* <p>
|
||||
* 表示处理器的逻辑核心数(包括超线程),例如 16 核。
|
||||
* </p>
|
||||
*/
|
||||
@Schema(description = "处理器的逻辑核心数(包括超线程)。", example = "16")
|
||||
private Integer logicalCpu;
|
||||
}
|
|
@ -0,0 +1,109 @@
|
|||
package com.cmhi.magent.pojo.po;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 版本信息类 {@code VersionInfo}。
|
||||
* <p>
|
||||
* 用于描述系统版本的关键信息,包括代码提交记录、构建时间、分支信息等。
|
||||
* 该类可用于版本管理模块,便于展示或记录系统的版本状态。
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 核心功能:
|
||||
* <ul>
|
||||
* <li>封装系统版本的关键信息(如提交 ID、提交描述、构建时间等)。</li>
|
||||
* <li>支持通过建造者模式({@link Builder})快速构建对象。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 使用场景:
|
||||
* <ul>
|
||||
* <li>用于系统版本信息展示模块,例如前端关于页面或者系统管理模块。</li>
|
||||
* <li>适用于调试或诊断系统版本状态,便于问题排查。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* JSON 输出示例:
|
||||
* <pre>
|
||||
* {
|
||||
* "commitId": "abc12345",
|
||||
* "commitDescribe": "Fix bug in user login",
|
||||
* "commitTime": "2023-01-07T12:00:00Z",
|
||||
* "tagName": "v1.0.0",
|
||||
* "buildTime": "2023-01-07T14:00:00Z",
|
||||
* "gitBranch": "main"
|
||||
* }
|
||||
* </pre>
|
||||
* </p>
|
||||
*
|
||||
* @author huangxin@cmhi.chinamobile.com
|
||||
* @version 1.0.0
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class VersionInfo {
|
||||
|
||||
/**
|
||||
* 提交 ID。
|
||||
* <p>
|
||||
* 表示系统当前版本的代码提交记录的唯一标识,例如 "abc12345"。
|
||||
* </p>
|
||||
*/
|
||||
@Schema(description = "系统当前版本的代码提交记录的唯一标识。", example = "abc12345")
|
||||
private String commitId;
|
||||
|
||||
/**
|
||||
* 提交描述。
|
||||
* <p>
|
||||
* 表示系统当前版本的代码提交的具体描述信息,例如 "Fix bug in user login"。
|
||||
* </p>
|
||||
*/
|
||||
@Schema(description = "系统当前版本的代码提交的具体描述信息。", example = "Fix bug in user login")
|
||||
private String commitDescribe;
|
||||
|
||||
/**
|
||||
* 提交时间。
|
||||
* <p>
|
||||
* 表示代码提交的时间戳,例如 "2023-01-07T12:00:00Z"。
|
||||
* </p>
|
||||
*/
|
||||
@Schema(description = "代码提交的时间戳。", example = "2023-01-07T12:00:00Z")
|
||||
private String commitTime;
|
||||
|
||||
/**
|
||||
* 标签名称。
|
||||
* <p>
|
||||
* 表示系统当前版本的标签信息,例如 "v1.0.0"。
|
||||
* </p>
|
||||
*/
|
||||
@Schema(description = "系统当前版本的标签信息。", example = "v1.0.0")
|
||||
private String tagName;
|
||||
|
||||
/**
|
||||
* 构建时间。
|
||||
* <p>
|
||||
* 表示当前系统版本构建的时间戳,例如 "2023-01-07T14:00:00Z"。
|
||||
* </p>
|
||||
*/
|
||||
@Schema(description = "系统版本的构建时间戳。", example = "2023-01-07T14:00:00Z")
|
||||
private String buildTime;
|
||||
|
||||
/**
|
||||
* Git 分支。
|
||||
* <p>
|
||||
* 表示代码版本对应的 Git 分支名称,例如 "main"。
|
||||
* </p>
|
||||
*/
|
||||
@Schema(description = "代码版本对应的 Git 分支名称。", example = "main")
|
||||
private String gitBranch;
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
package com.cmhi.magent.pojo.vo;
|
||||
|
||||
import com.cmhi.magent.pojo.po.BaseRespStatus;
|
||||
import com.cmhi.magent.pojo.po.PageResults;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 基础分页结果响应类 {@code BasePageResultResp}。
|
||||
* <p>
|
||||
* 用于封装包含分页数据的统一响应结果,继承自 {@link BaseRespStatus}(包含状态码和消息),
|
||||
* 同时将分页数据封装在 {@link PageResults} 对象中。
|
||||
* 该类支持通过泛型 {@code T} 指定分页数据项的类型。
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 核心功能:
|
||||
* <ul>
|
||||
* <li>继承基础响应状态类,统一包含状态码和消息。</li>
|
||||
* <li>支持分页数据的封装,包含分页元信息和数据项列表。</li>
|
||||
* <li>通过 {@link JsonInclude} 注解对空字段进行排除,提高 JSON 响应的简洁性。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 使用场景:
|
||||
* <ul>
|
||||
* <li>用于 RESTful API 的分页响应结果封装,适合分页查询接口。 </li>
|
||||
* <li>适用于需要返回分页数据列表的场景,例如表格数据展示。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* JSON 输出示例:
|
||||
* <pre>
|
||||
* {
|
||||
* "status": "SUCCESS",
|
||||
* "message": "Query executed successfully",
|
||||
* "items": {
|
||||
* "pageNumber": 1,
|
||||
* "pageSize": 10,
|
||||
* "totalPage": 5,
|
||||
* "totalRow": 50,
|
||||
* "items": [
|
||||
* { "id": 1, "name": "Item 1" },
|
||||
* { "id": 2, "name": "Item 2" }
|
||||
* ]
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
* </p>
|
||||
*
|
||||
* @param <T> 分页数据项的类型。
|
||||
* @author huangxin@cmhi.chinamobile.com
|
||||
* @version 1.0.0
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
@JsonPropertyOrder({"items", "status", "message"})
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class BasePageResultResp<T> extends BaseRespStatus {
|
||||
|
||||
/**
|
||||
* 分页数据。
|
||||
* <p>
|
||||
* 包含分页元信息(如当前页码、总页数、总行数等)以及具体的数据项列表。
|
||||
* 类型由泛型 {@code T} 指定。
|
||||
* </p>
|
||||
*/
|
||||
@Schema(description = "分页数据,包含分页元信息(如当前页码、总页数、总行数等)以及具体数据项。",
|
||||
example = "{ \"pageNumber\": 1, \"pageSize\": 10, \"totalPage\": 5, \"totalRow\": 50, " +
|
||||
"\"items\": [ {\"id\": 1, \"name\": " + "\"Item 1\"}, {\"id\": 2, \"name\": " +
|
||||
"\"Item 2\"} ] }")
|
||||
private PageResults<T> items;
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
package com.cmhi.magent.pojo.vo;
|
||||
|
||||
import com.cmhi.magent.pojo.po.BaseRespStatus;
|
||||
import com.cmhi.magent.pojo.po.HwInfo;
|
||||
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 系统硬件信息响应类 {@code GetHwInfoResp}。
|
||||
* <p>
|
||||
* 用于封装系统硬件信息的响应结果,继承自 {@link BaseRespStatus},包含状态码、消息以及硬件信息内容。
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 核心功能:
|
||||
* <ul>
|
||||
* <li>继承基础响应状态类,统一包含状态码和消息。</li>
|
||||
* <li>封装硬件信息内容,通过 {@code hwInfo} 字段提供详细的硬件描述。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 使用场景:
|
||||
* <ul>
|
||||
* <li>用于 RESTful API 的硬件信息查询结果响应。</li>
|
||||
* <li>适用于系统监控模块,提供硬件配置信息。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* JSON 输出示例:
|
||||
* <pre>
|
||||
* {
|
||||
* "status": "SUCCESS",
|
||||
* "message": "System hardware information retrieved successfully",
|
||||
* "hwInfo": {
|
||||
* "cpu": "Intel(R) Core(TM) i7-10700K CPU @ 3.80GHz",
|
||||
* "ram": "16GB",
|
||||
* "storage": "512GB SSD",
|
||||
* "os": "Ubuntu 20.04"
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
* </p>
|
||||
*
|
||||
* @author huangxin@cmhi.chinamobile.com
|
||||
* @version 1.0.0
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
@Schema(name = "系统硬件信息")
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@Builder
|
||||
@JsonPropertyOrder({"hwInfo", "status", "message"})
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class GetHwInfoResp extends BaseRespStatus {
|
||||
|
||||
/**
|
||||
* 硬件信息内容。
|
||||
* <p>
|
||||
* 包含系统的硬件配置信息,例如 CPU 型号、内存大小、存储容量和操作系统版本等。
|
||||
* 类型为 {@link HwInfo}。
|
||||
* </p>
|
||||
*/
|
||||
@Schema(description = "硬件信息内容,例如 CPU 型号、内存大小、存储容量和操作系统版本")
|
||||
private HwInfo hwInfo;
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
package com.cmhi.magent.pojo.vo;
|
||||
|
||||
import com.cmhi.magent.pojo.po.BaseRespStatus;
|
||||
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
|
||||
/**
|
||||
* 操作系统信息响应类 {@code GetOsInfoResp}。
|
||||
* <p>
|
||||
* 用于封装操作系统信息的响应结果,继承自 {@link BaseRespStatus},包含状态码、消息、
|
||||
* 操作系统名称以及系统的启动时间。
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 核心功能:
|
||||
* <ul>
|
||||
* <li>继承基础响应状态类,统一包含状态码和消息。</li>
|
||||
* <li>封装操作系统的相关信息,包括操作系统名称和启动时间。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 使用场景:
|
||||
* <ul>
|
||||
* <li>适用于 RESTful API 的操作系统信息查询接口。</li>
|
||||
* <li>用于系统监控模块,提供操作系统的基础信息。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* JSON 输出示例:
|
||||
* <pre>
|
||||
* {
|
||||
* "status": "SUCCESS",
|
||||
* "message": "Operating system information retrieved successfully",
|
||||
* "os": "Ubuntu 20.04",
|
||||
* "bootTime": "2023-01-07T08:00:00"
|
||||
* }
|
||||
* </pre>
|
||||
* </p>
|
||||
*
|
||||
* @author huangxin@cmhi.chinamobile.com
|
||||
* @version 1.0.0
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
@Schema(name = "操作系统信息")
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@Builder
|
||||
@JsonPropertyOrder({"os", "bootTime", "status", "message"})
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class GetOsInfoResp extends BaseRespStatus {
|
||||
|
||||
/**
|
||||
* 操作系统名称。
|
||||
* <p>
|
||||
* 表示系统的操作系统名称,例如 "Ubuntu 20.04" 或 "Windows 10"。
|
||||
* </p>
|
||||
*/
|
||||
@Schema(description = "操作系统名称,例如 Ubuntu 20.04 或 Windows 10")
|
||||
private String os;
|
||||
|
||||
/**
|
||||
* 系统启动时间。
|
||||
* <p>
|
||||
* 表示系统最近一次启动的时间,格式为 {@link Timestamp}。
|
||||
* 例如 "2023-01-07T08:00:00"。
|
||||
* </p>
|
||||
*/
|
||||
@Schema(description = "系统启动时间,例如 2023-01-07T08:00:00")
|
||||
private Timestamp bootTime;
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
package com.cmhi.magent.pojo.vo;
|
||||
|
||||
import com.cmhi.magent.pojo.po.BaseRespStatus;
|
||||
import com.cmhi.magent.pojo.po.ProcessorInfo;
|
||||
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 处理器信息响应类 {@code GetProcessorInfoResp}。
|
||||
* <p>
|
||||
* 用于封装处理器信息的响应结果,继承自 {@link BaseRespStatus},包含状态码、消息以及处理器的详细信息。
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 核心功能:
|
||||
* <ul>
|
||||
* <li>继承基础响应状态类,统一包含状态码和消息。</li>
|
||||
* <li>封装处理器相关的详细信息,通过 {@code cpuInfo} 字段返回。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 使用场景:
|
||||
* <ul>
|
||||
* <li>用于 RESTful API 的处理器信息查询接口。</li>
|
||||
* <li>适用于系统监控模块,获取处理器的详细配置信息,例如核心数、线程数、架构等。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* JSON 输出示例:
|
||||
* <pre>
|
||||
* {
|
||||
* "status": "SUCCESS",
|
||||
* "message": "Processor information retrieved successfully",
|
||||
* "cpuInfo": {
|
||||
* "model": "Intel(R) Core(TM) i7-10700K",
|
||||
* "cores": 8,
|
||||
* "threads": 16,
|
||||
* "architecture": "x86_64"
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
* </p>
|
||||
*
|
||||
* @author huangxin@cmhi.chinamobile.com
|
||||
* @version 1.0.0
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
@Schema(name = "处理器信息")
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@Builder
|
||||
@JsonPropertyOrder({"cpuInfo", "status", "message"})
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class GetProcessorInfoResp extends BaseRespStatus {
|
||||
|
||||
/**
|
||||
* 处理器信息内容。
|
||||
* <p>
|
||||
* 包含处理器的详细信息,例如型号、核心数、线程数、架构类型等。
|
||||
* 类型为 {@link ProcessorInfo}。
|
||||
* </p>
|
||||
*/
|
||||
@Schema(description = "处理器信息内容,例如型号、核心数、线程数、架构类型等。")
|
||||
private ProcessorInfo cpuInfo;
|
||||
}
|
|
@ -0,0 +1,148 @@
|
|||
package com.cmhi.magent.pojo.vo;
|
||||
|
||||
import com.cmhi.magent.common.CommonEnumHandler;
|
||||
import com.cmhi.magent.common.ConstValue;
|
||||
import com.cmhi.magent.common.ErrorCode;
|
||||
import com.cmhi.magent.config.ProtocolConfigure;
|
||||
import com.cmhi.magent.pojo.base.BaseProtocol;
|
||||
import com.cmhi.magent.pojo.po.BaseRespStatus;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 通用协议响应类 {@code ProtocolResp<T>}。
|
||||
* <p>
|
||||
* 用于封装响应结果的通用协议格式,继承自 {@link BaseProtocol},提供统一的协议结构支持,
|
||||
* 包括版本号、加密类型、时间戳、状态码及消息内容。
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 核心功能:
|
||||
* <ul>
|
||||
* <li>封装协议的基础字段,包括版本号(`ver`)、加密类型(`cryptoType`)、时间戳(`timeStamp`)。</li>
|
||||
* <li>支持通过静态方法快速生成不同类型的响应对象。</li>
|
||||
* <li>可根据错误码和响应消息,构造不同的响应内容。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 使用场景:
|
||||
* <ul>
|
||||
* <li>适用于与前端或外部系统的接口通信,作为标准化的协议响应结构。</li>
|
||||
* <li>用于封装业务逻辑处理后的统一返回结果。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* JSON 输出示例:
|
||||
* <pre>
|
||||
* {
|
||||
* "ver": "1.0",
|
||||
* "cryptoType": "AES",
|
||||
* "timeStamp": 1673088000000,
|
||||
* "code": 200,
|
||||
* "msgContent": {
|
||||
* "status": "SUCCESS",
|
||||
* "message": "Operation completed successfully"
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
* </p>
|
||||
*
|
||||
* @param <T> 响应消息的泛型类型,通常为业务数据或错误消息。
|
||||
*
|
||||
* @author huangxin@cmhi.chinamobile.com
|
||||
* @version 1.0.0
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
@Slf4j
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@JsonPropertyOrder({"ver", "cryptoType", "timeStamp", "code", "msgContent"})
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public class ProtocolResp<T> extends BaseProtocol<T> {
|
||||
|
||||
/**
|
||||
* 状态码。
|
||||
* <p>
|
||||
* 表示协议的响应状态码,例如 HTTP 状态码(200 表示成功,400 表示请求错误等)。
|
||||
* </p>
|
||||
*/
|
||||
@Schema(description = "状态码,例如:200 表示成功,400 表示请求错误")
|
||||
private Integer code;
|
||||
|
||||
/**
|
||||
* 根据 HTTP 状态码和响应消息内容生成协议响应。
|
||||
*
|
||||
* @param httpCode HTTP 状态码
|
||||
* @param respMsg 响应消息内容
|
||||
* @param <T> 响应消息的泛型类型
|
||||
* @return 格式化的协议响应对象
|
||||
*/
|
||||
private static <T> ProtocolResp<T> result(Integer httpCode, T respMsg) {
|
||||
ProtocolResp<T> resp = new ProtocolResp<>();
|
||||
resp.setVer(ConstValue.Protocol.VERSION);
|
||||
resp.setCode(httpCode);
|
||||
resp.setCryptoType(ProtocolConfigure.SECURITY_PROTOCOL_TYPE);
|
||||
resp.setTimeStamp(System.currentTimeMillis());
|
||||
resp.setMsgContent(respMsg);
|
||||
return resp;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据错误码和响应消息生成协议响应。
|
||||
*
|
||||
* @param err 错误码
|
||||
* @param respMsg 响应消息内容
|
||||
* @param <T> 响应消息的泛型类型
|
||||
* @return 格式化的协议响应对象
|
||||
*/
|
||||
public static <T> ProtocolResp<T> result(ErrorCode err, T respMsg) {
|
||||
return result(err.getHttpCode(), respMsg);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据错误码生成协议响应,响应消息为 {@link BaseRespStatus} 类型。
|
||||
*
|
||||
* @param err 错误码
|
||||
* @return 格式化的协议响应对象
|
||||
*/
|
||||
public static ProtocolResp<BaseRespStatus> result(ErrorCode err) {
|
||||
BaseRespStatus rspMsg = new BaseRespStatus(err.getCode(), new String[] {err.getDescription()});
|
||||
return result(err.getHttpCode(), rspMsg);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据响应消息生成协议响应,响应消息需继承 {@link BaseRespStatus}。
|
||||
*
|
||||
* @param respMsg 响应消息内容
|
||||
* @param <T> 响应消息的具体类型,需继承 {@link BaseRespStatus}
|
||||
* @return 格式化的协议响应对象
|
||||
*/
|
||||
public static <T extends BaseRespStatus> ProtocolResp<T> result(T respMsg) {
|
||||
return result(Objects.requireNonNull(CommonEnumHandler.codeOf(ErrorCode.class, respMsg.getStatus())), respMsg);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据错误码、自定义 HTTP 状态码和响应消息生成协议响应。
|
||||
*
|
||||
* @param errCode 错误码
|
||||
* @param httpCode 自定义 HTTP 状态码
|
||||
* @param message 响应消息内容
|
||||
* @return 格式化的协议响应对象
|
||||
*/
|
||||
public static ProtocolResp<BaseRespStatus> result(ErrorCode errCode, Integer httpCode, String[] message) {
|
||||
BaseRespStatus rspMsg = new BaseRespStatus(errCode.getCode(), message);
|
||||
return result(httpCode, rspMsg);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
package com.cmhi.magent.pojo.vo;
|
||||
|
||||
import com.cmhi.magent.pojo.po.BaseRespStatus;
|
||||
import com.cmhi.magent.pojo.po.VersionInfo;
|
||||
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 版本信息响应类 {@code VersionResp}。
|
||||
*
|
||||
* 用于封装版本信息查询接口的响应结果,继承自 {@link BaseRespStatus},包含状态码、消息以及版本的详细信息。
|
||||
*
|
||||
* <p>
|
||||
* 核心功能:
|
||||
* <ul>
|
||||
* <li>提供统一的接口响应结构,继承自 {@link BaseRespStatus}。</li>
|
||||
* <li>封装版本的详细信息,通过 {@link VersionInfo} 对象表示。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 使用场景:
|
||||
* <ul>
|
||||
* <li>应用于系统版本查询接口,返回当前系统或模块的版本信息。</li>
|
||||
* <li>用于客户端与服务端保持一致性的版本校验场景。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* JSON 输出示例:
|
||||
* <pre>
|
||||
* {
|
||||
* "status": "SUCCESS",
|
||||
* "message": "Version information retrieved successfully",
|
||||
* "version": {
|
||||
* "versionNumber": "1.0.0",
|
||||
* "releaseDate": "2023-01-01",
|
||||
* "description": "Initial release of the system."
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
* </p>
|
||||
*
|
||||
* @author huangxin@cmhi.chinamobile.com
|
||||
* @version 1.0.0
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@JsonPropertyOrder({"version", "status", "message"})
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class VersionResp extends BaseRespStatus {
|
||||
|
||||
/**
|
||||
* 版本信息内容。
|
||||
*
|
||||
* 表示当前系统或模块的版本详情信息,包括版本号、发布日期以及描述信息。
|
||||
*/
|
||||
@Schema(description = "版本信息内容,包括版本号、发布日期以及描述信息")
|
||||
private VersionInfo version;
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
package com.cmhi.magent.service;
|
||||
|
||||
import com.cmhi.magent.annotation.OperationLogAnnotation;
|
||||
import com.cmhi.magent.common.ErrorCode;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* 操作日志服务接口。
|
||||
* <p>
|
||||
* 提供记录系统操作日志的方法,用于跟踪系统中执行的操作,支持基于注解或自定义参数的日志记录。
|
||||
* 该接口可与业务逻辑集成,以满足审计、问题排查或数据分析的需求。
|
||||
* </p>
|
||||
*
|
||||
* @author huangxin@cmhi.chinamobile.com
|
||||
* @version 1.0.0
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
public interface OperationLogService {
|
||||
|
||||
/**
|
||||
* 记录系统操作日志(基于注解方式)。
|
||||
* <p>
|
||||
* 使用 {@link OperationLogAnnotation} 注解中定义的元数据记录操作日志。
|
||||
* 通常用于通过切面(AOP)方式拦截调用而生成日志记录。
|
||||
* </p>
|
||||
*
|
||||
* @param call 调用的接口或方法名称
|
||||
* @param request 当前的 HTTP 请求对象,包含请求者的相关信息
|
||||
* @param result 操作的结果对象,用于记录操作的返回值
|
||||
* @param annotation 操作日志注解,包含元数据信息(如模块、操作类型等)
|
||||
* @param errorCode 错误码对象,记录操作中可能出现的错误信息
|
||||
*/
|
||||
void systemOperationLog(String call, HttpServletRequest request, Object result, OperationLogAnnotation annotation, ErrorCode errorCode);
|
||||
|
||||
/**
|
||||
* 记录系统操作日志(基于自由参数方式)。
|
||||
* <p>
|
||||
* 手动指定模块、操作类型及描述等信息来记录操作日志。
|
||||
* 通常用于需要更灵活的日志记录场景。
|
||||
* </p>
|
||||
*
|
||||
* @param call 调用的接口或方法名称
|
||||
* @param request 当前的 HTTP 请求对象,包含请求者的相关信息
|
||||
* @param result 操作的结果对象,用于记录操作的返回值
|
||||
* @param module 操作所属模块,用于分类日志记录(例如 "用户管理"、"系统设置")
|
||||
* @param optType 操作类型,标识操作的具体类别(例如 "新增"、"修改"、"删除")
|
||||
* @param desc 操作描述,详细说明本次操作的内容或目的
|
||||
* @param errorCode 错误码对象,记录操作中可能出现的错误信息
|
||||
*/
|
||||
void systemOperationLog(String call,
|
||||
HttpServletRequest request,
|
||||
Object result,
|
||||
String module,
|
||||
String optType,
|
||||
String desc,
|
||||
ErrorCode errorCode);
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
package com.cmhi.magent.service;
|
||||
|
||||
import com.cmhi.magent.crypto.DecryptRequestProtocol;
|
||||
import com.cmhi.magent.pojo.vo.ProtocolResp;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import org.springframework.http.HttpInputMessage;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* 协议安全服务接口。
|
||||
* <p>
|
||||
* 提供协议的加密与解密功能,适用于处理敏感数据的场景。
|
||||
* 该接口定义了加密和解密方法,支持字符串和对象的协议加密与解密。
|
||||
* </p>
|
||||
*
|
||||
* @author huangxin@cmhi.chinamobile.com
|
||||
* @version 1.0.0
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
public interface ProtocolSecurityService {
|
||||
|
||||
/**
|
||||
* 解密协议字符串。
|
||||
* <p>
|
||||
* 将密文协议字符串解密为明文字符串。
|
||||
* </p>
|
||||
*
|
||||
* @param ciphertext 需要解密的协议密文
|
||||
* @return 解密后的明文字符串
|
||||
* @throws JsonProcessingException 如果在解析过程中发生 JSON 处理错误
|
||||
*/
|
||||
String decryptProtocol(String ciphertext) throws JsonProcessingException;
|
||||
|
||||
/**
|
||||
* 从 HTTP 输入消息中解密协议。
|
||||
* <p>
|
||||
* 解析并解密传入的 HTTP 请求消息,返回对应的协议对象。
|
||||
* </p>
|
||||
*
|
||||
* @param httpInputMessage 需要解密的 HTTP 输入消息
|
||||
* @return 解密后的协议对象 {@link DecryptRequestProtocol}
|
||||
* @throws IOException 如果在读取输入流时发生 I/O 错误
|
||||
*/
|
||||
DecryptRequestProtocol decryptProtocol(HttpInputMessage httpInputMessage) throws IOException;
|
||||
|
||||
/**
|
||||
* 加密明文字符串协议。
|
||||
* <p>
|
||||
* 将明文字符串加密为密文,支持不同的加密类型。
|
||||
* </p>
|
||||
*
|
||||
* @param plainText 明文字符串
|
||||
* @param cryptoType 加密类型,用于指定加密算法或方式
|
||||
* @return 加密后的密文字符串
|
||||
*/
|
||||
String encryptProtocolString(String plainText, int cryptoType);
|
||||
|
||||
/**
|
||||
* 加密协议对象。
|
||||
* <p>
|
||||
* 将对象协议加密为密文字符串,并封装为 {@link ProtocolResp} 对象。
|
||||
* </p>
|
||||
*
|
||||
* @param orgProtocol 原始协议对象
|
||||
* @param cryptoType 加密类型,用于指定加密算法或方式
|
||||
* @return 加密后的协议响应对象,包含密文字符串
|
||||
*/
|
||||
ProtocolResp<String> encryptProtocol(Object orgProtocol, int cryptoType);
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
package com.cmhi.magent.service;
|
||||
|
||||
import com.cmhi.magent.pojo.po.HwInfo;
|
||||
import com.cmhi.magent.pojo.po.ProcessorInfo;
|
||||
|
||||
/**
|
||||
* 系统信息服务接口。
|
||||
* <p>
|
||||
* 提供获取操作系统信息、硬件信息和处理器信息的功能。
|
||||
* 该接口定义了系统信息的查询方法,便于实现类对相关数据进行封装和提供。
|
||||
* </p>
|
||||
*
|
||||
* @author huangxin@cmhi.chinamobile.com
|
||||
* @version 1.0.0
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
public interface SystemInfoService {
|
||||
|
||||
/**
|
||||
* 获取操作系统的名称。
|
||||
* <p>
|
||||
* 返回当前系统的操作系统类型或名称,例如 "Windows 10" 或 "Ubuntu 20.04"。
|
||||
* </p>
|
||||
*
|
||||
* @return 操作系统名称
|
||||
*/
|
||||
String getOsName();
|
||||
|
||||
/**
|
||||
* 获取操作系统的启动时间戳。
|
||||
* <p>
|
||||
* 返回自1970年1月1日00:00:00 UTC以来的毫秒数,表示当前操作系统的启动时间。
|
||||
* </p>
|
||||
*
|
||||
* @return 操作系统启动时间戳
|
||||
*/
|
||||
long getOsBootTimeStamp();
|
||||
|
||||
/**
|
||||
* 获取处理器信息。
|
||||
* <p>
|
||||
* 封装处理器的相关信息,如核心数量、线程数量、频率等。结果以 {@link ProcessorInfo} 对象形式返回。
|
||||
* </p>
|
||||
*
|
||||
* @return 处理器信息对象
|
||||
*/
|
||||
ProcessorInfo getProcessInfo();
|
||||
|
||||
/**
|
||||
* 获取硬件信息。
|
||||
* <p>
|
||||
* 包含硬件相关的详细信息,例如内存大小、磁盘容量等。结果以 {@link HwInfo} 对象形式返回。
|
||||
* </p>
|
||||
*
|
||||
* @return 硬件信息对象
|
||||
*/
|
||||
HwInfo getHardwareInfo();
|
||||
}
|
|
@ -0,0 +1,170 @@
|
|||
package com.cmhi.magent.service.impl;
|
||||
|
||||
import com.cmhi.magent.annotation.OperationLogAnnotation;
|
||||
import com.cmhi.magent.common.ErrorCode;
|
||||
import com.cmhi.magent.config.CommonConfigure;
|
||||
import com.cmhi.magent.misc.ApiContextUtils;
|
||||
import com.cmhi.magent.misc.HelperUtils;
|
||||
import com.cmhi.magent.pojo.po.ApiContext;
|
||||
import com.cmhi.magent.pojo.po.OperationLog;
|
||||
import com.cmhi.magent.service.OperationLogService;
|
||||
import com.jayway.jsonpath.JsonPath;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.autoconfigure.web.ServerProperties;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 实现了 {@link OperationLogService} 接口的操作日志服务实现类。
|
||||
* <p>
|
||||
* 该类提供了系统操作日志的记录功能,可以通过注解方式或手动指定参数的方式记录操作日志。
|
||||
* 日志内容包括调用的方法名称、操作所属模块、操作类型、HTTP 请求相关信息、返回结果、操作时间、延时等。
|
||||
* </p>
|
||||
* <p>
|
||||
* 此服务支持将操作日志记录到持久化存储或其他日志存储系统中,方便系统审计、问题排查或数据分析。
|
||||
* </p>
|
||||
* <p>
|
||||
* 如果在记录日志过程中出现异常,会被捕获并安全忽略,确保日志记录不会影响正常业务流程。
|
||||
* </p>
|
||||
*
|
||||
* @author huangxin@cmhi.chinamobile.com
|
||||
* @version 1.0.0
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class OperationLogServiceImpl implements OperationLogService {
|
||||
|
||||
/**
|
||||
* 数据库字段最大长度,用于截取过长的字符串,避免存储溢出。
|
||||
*/
|
||||
private static final int MAX_DB_STRING = 4090;
|
||||
|
||||
/**
|
||||
* 用于排除记录错误处理路径的请求日志。
|
||||
*/
|
||||
private static final String ERROR_URL = "/error";
|
||||
|
||||
/**
|
||||
* Spring Boot 提供的服务器属性配置,用于获取上下文路径等信息。
|
||||
*/
|
||||
@Resource
|
||||
private ServerProperties serverProperties;
|
||||
|
||||
/**
|
||||
* 基于注解记录操作日志。
|
||||
* <p>
|
||||
* 当注解不为空时,会从 {@link OperationLogAnnotation} 中提取模块、操作类型和描述信息,调用另一个重载方法进行日志记录。
|
||||
* 如果注解为空,则使用默认空值调用重载方法。
|
||||
* </p>
|
||||
*
|
||||
* @param call 调用的方法名称或接口名称
|
||||
* @param request 当前的 HTTP 请求对象,包含请求者的相关信息
|
||||
* @param result 操作结果对象,用于记录方法返回值
|
||||
* @param annotation 操作日志注解,包含模块、操作类型和描述信息
|
||||
* @param errorCode 错误码对象,记录操作中出现的错误信息
|
||||
*/
|
||||
@Override
|
||||
public void systemOperationLog(String call,
|
||||
HttpServletRequest request,
|
||||
Object result,
|
||||
OperationLogAnnotation annotation,
|
||||
ErrorCode errorCode) {
|
||||
if (annotation != null) {
|
||||
// 提取注解中的元数据并记录日志
|
||||
systemOperationLog(call, request, result, annotation.OperationModule(), annotation.OperationType(), annotation.OperationDesc(),
|
||||
errorCode);
|
||||
} else {
|
||||
// 如果注解为空,使用默认值记录日志
|
||||
systemOperationLog(call, request, result, "", "", "", errorCode);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 基于自由参数记录操作日志。
|
||||
* <p>
|
||||
* 手动指定操作所属模块、操作类型、描述及其他相关信息,生成操作日志并记录。
|
||||
* 日志内容包括:
|
||||
* <ul>
|
||||
* <li>模块、操作类型和描述信息</li>
|
||||
* <li>HTTP 请求的相关信息(方法、路径、请求头、IP 地址、请求体)</li>
|
||||
* <li>返回结果、操作时间和延迟信息</li>
|
||||
* </ul>
|
||||
* 方法包含异常安全处理,如果日志记录过程中出现异常,将会被捕获并安全忽略。
|
||||
* </p>
|
||||
*
|
||||
* @param call 调用的方法名称或接口名称
|
||||
* @param request 当前的 HTTP 请求对象,包含请求者的相关信息
|
||||
* @param result 操作结果对象,用于记录方法返回值
|
||||
* @param module 操作所属模块的名称(例如 "用户管理"、"系统设置" 等)
|
||||
* @param optType 操作类型(例如 "新增"、"修改"、"删除")
|
||||
* @param desc 操作描述,对操作的内容或目的的详细说明
|
||||
* @param errorCode 错误码对象,记录操作中出现的错误信息
|
||||
*/
|
||||
@Override
|
||||
public void systemOperationLog(String call,
|
||||
HttpServletRequest request,
|
||||
Object result,
|
||||
String module,
|
||||
String optType,
|
||||
String desc,
|
||||
ErrorCode errorCode) {
|
||||
try {
|
||||
// 获取当前请求的上下文信息
|
||||
ApiContext ctx = ApiContextUtils.get();
|
||||
OperationLog optLog = new OperationLog();
|
||||
|
||||
// 排除错误处理路径的日志记录
|
||||
if (request.getRequestURI().startsWith(CommonConfigure.PROJECT_PREFIX_URL + ERROR_URL)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 设置操作日志的基础信息
|
||||
optLog.setOperationStatus(errorCode.getStringValue());
|
||||
optLog.setCallFunction(call);
|
||||
optLog.setModule(module);
|
||||
optLog.setOperationType(optType);
|
||||
optLog.setDescription(desc);
|
||||
|
||||
// 设置操作时间为当前系统时间
|
||||
optLog.setOperationTime(new Timestamp(System.currentTimeMillis()));
|
||||
|
||||
// 设置 HTTP 请求信息
|
||||
optLog.setHttpMethod(request.getMethod());
|
||||
String prefixUrl = Optional.ofNullable(serverProperties.getServlet().getContextPath()).orElse("");
|
||||
String url = request.getRequestURI().replaceFirst("^" + prefixUrl, "");
|
||||
optLog.setHttpPath(url);
|
||||
|
||||
// 设置请求 IP 地址
|
||||
optLog.setRequestIp(HelperUtils.getHttpRequestSrcIp(request));
|
||||
|
||||
// 设置操作结果,截取过长字符串以避免数据库存储溢出
|
||||
optLog.setResult(HelperUtils.truncateString(HelperUtils.getJson(result), MAX_DB_STRING));
|
||||
|
||||
// 设置请求头信息
|
||||
optLog.setRequestHeaders(HelperUtils.truncateString(HelperUtils.getHttpRequestHeaders(request), MAX_DB_STRING));
|
||||
|
||||
// 设置请求体信息
|
||||
String reqJson = ctx.getRequestBody();
|
||||
optLog.setRequest(HelperUtils.truncateString(reqJson, MAX_DB_STRING));
|
||||
|
||||
// 记录操作响应时间
|
||||
optLog.setExpendTime(System.currentTimeMillis() - ctx.getRequestTime());
|
||||
|
||||
// 记录传输延迟,如果请求体中包含时间戳信息
|
||||
if (HelperUtils.stringNotEmptyOrNull(reqJson)) {
|
||||
Long reqTime = Optional.ofNullable((Long) JsonPath.read(reqJson, "$.timeStamp")).orElse(0L);
|
||||
optLog.setTransmitDelay(ctx.getRequestTime() - reqTime);
|
||||
}
|
||||
|
||||
// 保存日志(此处为打印到控制台)
|
||||
log.info("{}", HelperUtils.getJson(optLog));
|
||||
} catch (Exception ignored) {
|
||||
// 忽略所有异常,避免日志记录影响正常业务流程
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,232 @@
|
|||
package com.cmhi.magent.service.impl;
|
||||
|
||||
import com.cmhi.magent.common.ConstValue;
|
||||
import com.cmhi.magent.common.ErrorCode;
|
||||
import com.cmhi.magent.common.ProtoCryptoType;
|
||||
import com.cmhi.magent.config.ProtocolConfigure;
|
||||
import com.cmhi.magent.crypto.DecryptRequestProtocol;
|
||||
import com.cmhi.magent.crypto.arithmetic.CryptoHelper;
|
||||
import com.cmhi.magent.exception.SecurityProtocolException;
|
||||
import com.cmhi.magent.misc.HelperUtils;
|
||||
import com.cmhi.magent.pojo.dto.ProtocolReq;
|
||||
import com.cmhi.magent.pojo.vo.ProtocolResp;
|
||||
import com.cmhi.magent.service.ProtocolSecurityService;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.springframework.http.HttpInputMessage;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 协议安全服务实现类。
|
||||
* <p>
|
||||
* 提供对请求和响应协议的加密与解密功能,支持多种加密方式,如 BASE64、AES128、AES256 和 DES。
|
||||
* 主要用于处理安全协议,确保传输数据的安全性。
|
||||
* </p>
|
||||
* <p>
|
||||
* 该服务适用于客户端与服务端之间的加解密通信,支持动态选择加密类型。
|
||||
* </p>
|
||||
*
|
||||
* @author huangxin@cmhi.chinamobile.com
|
||||
* @version 1.0.0
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class ProtocolSecurityServiceImpl implements ProtocolSecurityService {
|
||||
|
||||
/**
|
||||
* 用于 JSON 解析和序列化/反序列化。
|
||||
*/
|
||||
@Resource
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
/**
|
||||
* 协议配置类,用于获取密钥等配置。
|
||||
*/
|
||||
@Resource
|
||||
private ProtocolConfigure protocolConfigure;
|
||||
|
||||
/**
|
||||
* 解密协议。
|
||||
* <p>
|
||||
* 根据加密类型(cryptoType)对协议内容进行解密。支持的加密类型包括:
|
||||
* <ul>
|
||||
* <li>CRYPTO_NONE (0):未加密,直接返回原始内容。</li>
|
||||
* <li>CRYPTO_BASE64 (1):Base64 解码。</li>
|
||||
* <li>CRYPTO_AES128 (2):AES128 解密。</li>
|
||||
* <li>CRYPTO_AES256 (3):AES256 解密。</li>
|
||||
* <li>CRYPTO_DES (4):DES 解密。</li>
|
||||
* </ul>
|
||||
* 如果加密类型超出支持范围,或解密失败,将抛出 {@link SecurityProtocolException}。
|
||||
* </p>
|
||||
*
|
||||
* @param ciphertext 加密的协议内容(JSON 格式字符串)
|
||||
* @return 解密后的协议内容(JSON 格式字符串)
|
||||
* @throws JsonProcessingException 如果解析 JSON 失败
|
||||
* @throws SecurityProtocolException 如果解密失败或加密类型不支持
|
||||
*/
|
||||
@Override
|
||||
public String decryptProtocol(String ciphertext) throws JsonProcessingException {
|
||||
JsonNode objRoot = objectMapper.readTree(ciphertext);
|
||||
|
||||
int cryptoType = objRoot.path("cryptoType").asInt();
|
||||
|
||||
// 协议未加密
|
||||
if (cryptoType == ProtoCryptoType.CRYPTO_NONE.getValue()) {
|
||||
return ciphertext;
|
||||
}
|
||||
|
||||
if (cryptoType > ProtoCryptoType.CRYPTO_AES256.getValue() || cryptoType < ProtoCryptoType.CRYPTO_NONE.getValue()) {
|
||||
throw new SecurityProtocolException(ErrorCode.ERR_PARAMS, "cryptoType 字段取值为 [0, 4]");
|
||||
}
|
||||
|
||||
ProtocolReq<String> proReq = objectMapper.readValue(ciphertext, new TypeReference<>() {
|
||||
});
|
||||
|
||||
byte[] base64Decode = CryptoHelper.base64Decryption(proReq.getMsgContent());
|
||||
|
||||
byte[] decryptContent;
|
||||
|
||||
if (Objects.equals(proReq.getCryptoType(), ProtoCryptoType.CRYPTO_BASE64.getValue())) {
|
||||
decryptContent = base64Decode;
|
||||
} else if (Objects.equals(proReq.getCryptoType(), ProtoCryptoType.CRYPTO_AES128.getValue())) {
|
||||
try {
|
||||
decryptContent = CryptoHelper.aes128Decryption(base64Decode, protocolConfigure.getCryptoKey());
|
||||
} catch (Exception e) {
|
||||
log.error("AES128 decode message error: {}", base64Decode);
|
||||
throw new SecurityProtocolException(ErrorCode.ERR_DECRYPT_AES128);
|
||||
}
|
||||
} else if (Objects.equals(proReq.getCryptoType(), ProtoCryptoType.CRYPTO_AES256.getValue())) {
|
||||
try {
|
||||
decryptContent = CryptoHelper.aes256Decryption(base64Decode, protocolConfigure.getCryptoKey());
|
||||
} catch (Exception e) {
|
||||
log.error("AES256 decode message error: {}", base64Decode);
|
||||
throw new SecurityProtocolException(ErrorCode.ERR_DECRYPT_AES256);
|
||||
}
|
||||
} else if (Objects.equals(proReq.getCryptoType(), ProtoCryptoType.CRYPTO_DES.getValue())) {
|
||||
try {
|
||||
decryptContent = CryptoHelper.desDecryption(base64Decode, protocolConfigure.getCryptoKey());
|
||||
} catch (Exception e) {
|
||||
log.error("DES decode message error: {}", base64Decode);
|
||||
throw new SecurityProtocolException(ErrorCode.ERR_DECRYPT_3DES);
|
||||
}
|
||||
} else {
|
||||
log.error("Unknown protocol security type: {}, {}", proReq.getCryptoType(), ciphertext);
|
||||
throw new SecurityProtocolException(ErrorCode.ERR_DECRYPT_UNKNOWN);
|
||||
}
|
||||
|
||||
String decodeMsg = new String(decryptContent, StandardCharsets.UTF_8);
|
||||
|
||||
return ciphertext.replace("\"" + proReq.getMsgContent() + "\"", decodeMsg);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密协议(重载方法)。
|
||||
* <p>
|
||||
* 从 {@link HttpInputMessage} 中提取协议内容并进行解密处理。
|
||||
* </p>
|
||||
*
|
||||
* @param httpInputMessage 包含协议内容的 HTTP 输入消息
|
||||
* @return 解密后的协议对象 {@link DecryptRequestProtocol}
|
||||
* @throws IOException 如果读取请求内容失败
|
||||
*/
|
||||
@Override
|
||||
public DecryptRequestProtocol decryptProtocol(HttpInputMessage httpInputMessage) throws IOException {
|
||||
String reqMessage = IOUtils.toString(httpInputMessage.getBody(), StandardCharsets.UTF_8);
|
||||
|
||||
return new DecryptRequestProtocol(httpInputMessage, decryptProtocol(reqMessage));
|
||||
}
|
||||
|
||||
/**
|
||||
* 加密协议内容。
|
||||
* <p>
|
||||
* 根据指定的加密类型对明文内容进行加密,支持的加密类型包括 Base64、AES128、AES256 和 DES。
|
||||
* </p>
|
||||
*
|
||||
* @param plainText 明文内容
|
||||
* @param cryptoType 加密类型
|
||||
* @return 加密后的内容
|
||||
* @throws SecurityProtocolException 如果加密失败或加密类型不支持
|
||||
*/
|
||||
@Override
|
||||
public String encryptProtocolString(String plainText, int cryptoType) {
|
||||
String cipherText;
|
||||
if (cryptoType == ProtoCryptoType.CRYPTO_BASE64.getValue()) {
|
||||
cipherText = CryptoHelper.base64Encryption(plainText.getBytes(StandardCharsets.UTF_8));
|
||||
} else if (cryptoType == ProtoCryptoType.CRYPTO_AES128.getValue()) {
|
||||
try {
|
||||
byte[] encode = CryptoHelper.aes128Encryption(plainText.getBytes(StandardCharsets.UTF_8), protocolConfigure.getCryptoKey());
|
||||
cipherText = CryptoHelper.base64Encryption(encode);
|
||||
} catch (Exception e) {
|
||||
log.error("AES128 encode message error({}): {}", e.getMessage(), plainText);
|
||||
throw new SecurityProtocolException(ErrorCode.ERR_ENCRYPT_AES128, e.getMessage());
|
||||
}
|
||||
} else if (cryptoType == ProtoCryptoType.CRYPTO_AES256.getValue()) {
|
||||
try {
|
||||
byte[] encode = CryptoHelper.aes256Encryption(plainText.getBytes(StandardCharsets.UTF_8), protocolConfigure.getCryptoKey());
|
||||
cipherText = CryptoHelper.base64Encryption(encode);
|
||||
} catch (Exception e) {
|
||||
log.error("AES256 encode message error({}): {}", e.getMessage(), plainText);
|
||||
throw new SecurityProtocolException(ErrorCode.ERR_ENCRYPT_AES256, e.getMessage());
|
||||
}
|
||||
} else if (cryptoType == ProtoCryptoType.CRYPTO_DES.getValue()) {
|
||||
try {
|
||||
byte[] encode = CryptoHelper.desEncryption(plainText.getBytes(StandardCharsets.UTF_8), protocolConfigure.getCryptoKey());
|
||||
cipherText = CryptoHelper.base64Encryption(encode);
|
||||
} catch (Exception e) {
|
||||
log.error("DES encode message error({}): {}", e.getMessage(), plainText);
|
||||
throw new SecurityProtocolException(ErrorCode.ERR_ENCRYPT_3DES, e.getMessage());
|
||||
}
|
||||
} else {
|
||||
log.error("Unknown protocol security type: {}, {}.", cryptoType, plainText);
|
||||
throw new SecurityProtocolException(ErrorCode.ERR_ENCRYPT_UNKNOWN);
|
||||
}
|
||||
|
||||
return cipherText;
|
||||
}
|
||||
|
||||
/**
|
||||
* 加密协议对象。
|
||||
* <p>
|
||||
* 将协议对象的内容加密后封装为 {@link ProtocolResp} 对象。
|
||||
* </p>
|
||||
*
|
||||
* @param orgProtocol 原始协议对象
|
||||
* @param cryptoType 加密类型
|
||||
* @return 加密后的协议对象
|
||||
* @throws SecurityProtocolException 如果加密失败
|
||||
*/
|
||||
@Override
|
||||
public ProtocolResp<String> encryptProtocol(Object orgProtocol, int cryptoType) {
|
||||
ProtocolResp<String> cryptoObject = new ProtocolResp<>();
|
||||
cryptoObject.setVer(ConstValue.Protocol.VERSION);
|
||||
cryptoObject.setCryptoType(ProtocolConfigure.SECURITY_PROTOCOL_TYPE);
|
||||
cryptoObject.setTimeStamp(System.currentTimeMillis());
|
||||
|
||||
String msgContentJsonString;
|
||||
|
||||
try {
|
||||
Method getMsgMethod = orgProtocol.getClass().getMethod("getMsgContent");
|
||||
Method getCode = orgProtocol.getClass().getMethod("getCode");
|
||||
msgContentJsonString = HelperUtils.getJson(getMsgMethod.invoke(orgProtocol));
|
||||
cryptoObject.setCode((Integer) getCode.invoke(orgProtocol));
|
||||
} catch (Exception e) {
|
||||
log.error("Json encode message error: {}", orgProtocol);
|
||||
throw new SecurityProtocolException(ErrorCode.ERR_SYSTEMEXCEPTION);
|
||||
}
|
||||
|
||||
cryptoObject.setMsgContent(encryptProtocolString(msgContentJsonString, cryptoType));
|
||||
|
||||
return cryptoObject;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
package com.cmhi.magent.service.impl;
|
||||
|
||||
import com.cmhi.magent.pojo.mapper.IObjectConvert;
|
||||
import com.cmhi.magent.pojo.po.HwInfo;
|
||||
import com.cmhi.magent.pojo.po.ProcessorInfo;
|
||||
import com.cmhi.magent.service.SystemInfoService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import oshi.SystemInfo;
|
||||
import oshi.hardware.CentralProcessor;
|
||||
import oshi.hardware.ComputerSystem;
|
||||
|
||||
/**
|
||||
* 系统信息服务实现类。
|
||||
* <p>
|
||||
* 该服务通过集成 OSHI(一个跨平台的硬件/操作系统信息库)来提供系统的相关信息,包括操作系统名称、系统启动时间、
|
||||
* 处理器信息以及硬件信息。
|
||||
* </p>
|
||||
* <p>
|
||||
* 主要功能包括:
|
||||
* <ul>
|
||||
* <li>获取操作系统名称。</li>
|
||||
* <li>获取系统启动时间(时间戳)。</li>
|
||||
* <li>获取处理器详细信息。</li>
|
||||
* <li>获取硬件系统信息(如主板、硬件厂商等)。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
* <p>
|
||||
* 该类实现了 {@link SystemInfoService} 接口,使用 OSHI 库实现底层系统信息的收集。
|
||||
* </p>
|
||||
*
|
||||
* @author huangxin@cmhi.chinamobile.com
|
||||
* @version 1.0.0
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
@Service
|
||||
public class SystemInfoServiceImpl implements SystemInfoService {
|
||||
|
||||
/**
|
||||
* OSHI 的 {@link SystemInfo} 类实例,用于获取操作系统和硬件相关信息。
|
||||
*/
|
||||
private final SystemInfo si = new SystemInfo();
|
||||
|
||||
/**
|
||||
* 获取操作系统名称。
|
||||
* <p>
|
||||
* 通过 OSHI 库获取操作系统的名称和其它描述信息。
|
||||
* </p>
|
||||
*
|
||||
* @return 操作系统的名称。
|
||||
*/
|
||||
@Override
|
||||
public String getOsName() {
|
||||
return String.valueOf(si.getOperatingSystem());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取系统启动时间的时间戳。
|
||||
* <p>
|
||||
* 通过 OSHI 库计算系统的启动时间(秒级时间戳转换为毫秒级时间戳)。
|
||||
* </p>
|
||||
*
|
||||
* @return 系统启动时间的时间戳(以毫秒为单位)。
|
||||
*/
|
||||
@Override
|
||||
public long getOsBootTimeStamp() {
|
||||
return si.getOperatingSystem().getSystemBootTime() * 1000L;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取处理器信息。
|
||||
* <p>
|
||||
* 通过 OSHI 库获取处理器的详细信息,包括逻辑处理器数、处理器标识符等,
|
||||
* 并通过 {@link IObjectConvert} 工具类将其转换为自定义的 {@link ProcessorInfo} 对象。
|
||||
* </p>
|
||||
*
|
||||
* @return 包含处理器详细信息的 {@link ProcessorInfo} 对象。
|
||||
*/
|
||||
@Override
|
||||
public ProcessorInfo getProcessInfo() {
|
||||
CentralProcessor proc = si.getHardware().getProcessor();
|
||||
return IObjectConvert.INSTANCE.toProcessorInfo(proc, proc.getProcessorIdentifier());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取硬件信息。
|
||||
* <p>
|
||||
* 通过 OSHI 库获取硬件系统的基本信息,包括主板型号、硬件厂商等,
|
||||
* 并通过 {@link IObjectConvert} 工具类将其转换为自定义的 {@link HwInfo} 对象。
|
||||
* </p>
|
||||
*
|
||||
* @return 包含硬件详细信息的 {@link HwInfo} 对象。
|
||||
*/
|
||||
@Override
|
||||
public HwInfo getHardwareInfo() {
|
||||
ComputerSystem cs = si.getHardware().getComputerSystem();
|
||||
return IObjectConvert.INSTANCE.toHwInfo(cs);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package com.cmhi.magent.setup;
|
||||
|
||||
import com.cmhi.magent.config.CommonConfigure;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 系统初始化类,用于在应用程序启动后执行必要的初始化逻辑。<p>
|
||||
* 该类实现了 {@link CommandLineRunner} 接口,可在 Spring Boot 应用程序启动完成后运行指定的代码。<p>
|
||||
* 当前实现的主要功能是打印在线 API 文档的访问地址。<p>
|
||||
* 使用 {@code @Slf4j} 注解简化日志记录功能。
|
||||
* @author huangxin@cmhi.chinamobile.com
|
||||
* @version 1.0.0
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class SystemInitial implements CommandLineRunner {
|
||||
|
||||
/**
|
||||
* 在应用程序启动后执行初始化逻辑。<p>
|
||||
* 该方法会被 Spring Boot 自动调用,用于执行自定义的启动后任务。<p>
|
||||
* 当前实现功能是调用 {@link #showOpenApiUrl()} 方法来打印 API 文档访问地址。
|
||||
* @param args 应用程序启动时传递的参数列表
|
||||
*/
|
||||
@Override
|
||||
public void run(String... args) {
|
||||
showOpenApiUrl();
|
||||
}
|
||||
|
||||
/**
|
||||
* 打印在线 API 文档的访问地址到日志。<p>
|
||||
* 该方法使用日志记录功能将 Swagger 文档的访问地址输出到日志中。地址格式为:<pre>{BASEURL}/swagger-ui/index.html</pre><p>
|
||||
* 其中,基础 URL 通过 {@link CommonConfigure#BASEURL} 变量获取。
|
||||
* @implNote 使用 {@code log.info()} 方法输出日志信息。
|
||||
*/
|
||||
private void showOpenApiUrl() {
|
||||
log.info("Access Online API Documents: {}/swagger-ui/index.html", CommonConfigure.BASEURL);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
package com.cmhi.magent.validation.group;
|
||||
|
||||
/**
|
||||
* 验证分组接口定义。
|
||||
* <p>
|
||||
* 该接口用于定义数据校验时的分组标识,结合 Bean Validation 的 {@code @Validated} 注解使用,
|
||||
* 可实现分组校验逻辑,便于在不同场景下复用校验规则。
|
||||
* </p>
|
||||
* <p>
|
||||
* 按照业务需求,分组校验规则从通用协议校验组 {@link ProtocolCommonValid} 开始,
|
||||
* 逐步扩展到更具体的场景,例如登录请求、登出请求、用户请求、操作日志请求等。
|
||||
* 接口的继承关系表示了校验规则的层次结构。
|
||||
* </p>
|
||||
*
|
||||
* @author huangxin@cmhi.chinamobile.com
|
||||
* @version 1.0.0
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
public interface ValidGroups {
|
||||
|
||||
/**
|
||||
* 通用协议校验分组。
|
||||
* <p>
|
||||
* 包含所有协议相关的基本校验规则,可作为其他分组校验规则的基础接口。
|
||||
* </p>
|
||||
*/
|
||||
interface ProtocolCommonValid {
|
||||
}
|
||||
|
||||
/**
|
||||
* 基础协议校验分组。
|
||||
* <p>
|
||||
* 扩展了通用协议校验规则,适用于需要基础协议校验的场景。
|
||||
* </p>
|
||||
*/
|
||||
interface BaseProtocolValid extends ProtocolCommonValid {
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页请求校验分组。
|
||||
* <p>
|
||||
* 适用于具有分页功能的请求校验,继承了基础协议校验规则。
|
||||
* </p>
|
||||
*/
|
||||
interface BasePagedReqValid extends BaseProtocolValid {
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录请求校验分组。
|
||||
* <p>
|
||||
* 适用于登录功能请求的校验规则,继承了基础协议校验规则。
|
||||
* </p>
|
||||
*/
|
||||
interface LoginReqValid extends BaseProtocolValid {
|
||||
}
|
||||
|
||||
/**
|
||||
* 登出请求校验分组。
|
||||
* <p>
|
||||
* 适用于登出功能请求的校验规则,继承了基础协议校验规则。
|
||||
* </p>
|
||||
*/
|
||||
interface LogoutReqValid extends BaseProtocolValid {
|
||||
}
|
||||
|
||||
/**
|
||||
* 操作日志请求校验分组。
|
||||
* <p>
|
||||
* 适用于操作日志功能请求的校验规则,继承了基础协议校验规则。
|
||||
* </p>
|
||||
*/
|
||||
interface OperationLogReqValid extends BaseProtocolValid {
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户 ID 请求校验分组。
|
||||
* <p>
|
||||
* 适用于用户 ID 请求的校验规则,继承了基础协议校验规则。
|
||||
* </p>
|
||||
*/
|
||||
interface UserIdReqValid extends BaseProtocolValid {
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户请求校验分组。
|
||||
* <p>
|
||||
* 适用于用户功能请求的校验规则,继承了基础协议校验规则。
|
||||
* </p>
|
||||
*/
|
||||
interface UserReqValid extends BaseProtocolValid {
|
||||
}
|
||||
|
||||
/**
|
||||
* 资源请求校验分组。
|
||||
* <p>
|
||||
* 适用于资源管理相关功能请求的校验规则,继承了基础协议校验规则。
|
||||
* </p>
|
||||
*/
|
||||
interface ResourceReqValid extends BaseProtocolValid {
|
||||
}
|
||||
|
||||
/**
|
||||
* 资源信息校验分组。
|
||||
* <p>
|
||||
* 适用于资源信息相关功能请求的校验规则,继承了基础协议校验规则。
|
||||
* </p>
|
||||
*/
|
||||
interface ResourceInfoValid extends BaseProtocolValid {
|
||||
}
|
||||
|
||||
/**
|
||||
* 字典请求校验分组。
|
||||
* <p>
|
||||
* 适用于字典功能请求的校验规则,继承了基础协议校验规则。
|
||||
* </p>
|
||||
*/
|
||||
interface DictReqValid extends BaseProtocolValid {
|
||||
}
|
||||
|
||||
/**
|
||||
* 字典请求内容校验分组。
|
||||
* <p>
|
||||
* 适用于字典请求内容的校验规则,继承了基础协议校验规则。
|
||||
* </p>
|
||||
*/
|
||||
interface DictReqContentValid extends BaseProtocolValid {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
package com.cmhi.magent.validation.valids;
|
||||
|
||||
import jakarta.validation.Constraint;
|
||||
import jakarta.validation.Payload;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 自定义校验注解,用于验证字段是否符合 UUID 格式。
|
||||
* <p>
|
||||
* 该注解基于正则表达式实现校验,支持标准的 UUID 格式(例如:8-4-4-4-12 的格式)。
|
||||
* </p>
|
||||
* <p>
|
||||
* 正则表达式规则:
|
||||
* <ul>
|
||||
* <li>32 个小写十六进制字符,按以下模式分隔:8-4-4-4-12。</li>
|
||||
* <li>第 3 段的第一个字符为 1-5(表示 UUID 版本)。</li>
|
||||
* <li>第 4 段的第一个字符为 8、9、a 或 b(表示 UUID 的变种)。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* @author huangxin@cmhi.chinamobile.com
|
||||
* @version 1.0.0
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
@Target(ElementType.FIELD)
|
||||
@Constraint(validatedBy = {})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Pattern(regexp = "^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$")
|
||||
public @interface UUID {
|
||||
|
||||
/**
|
||||
* 错误消息,当字段值不符合 UUID 格式时返回。
|
||||
*
|
||||
* @return 默认错误消息,支持自定义。
|
||||
*/
|
||||
String message() default "{invalid.uuid}";
|
||||
|
||||
/**
|
||||
* 指定校验分组。
|
||||
* <p>
|
||||
* 可用于分组校验场景,例如在不同的业务逻辑中使用不同的校验规则。
|
||||
* </p>
|
||||
*
|
||||
* @return 校验分组,默认为空。
|
||||
*/
|
||||
Class<?>[] groups() default {};
|
||||
|
||||
/**
|
||||
* 用于携带元数据信息的扩展。
|
||||
* <p>
|
||||
* 通常用于自定义校验逻辑时传递附加信息。
|
||||
* </p>
|
||||
*
|
||||
* @return 负载类型数组。
|
||||
*/
|
||||
Class<? extends Payload>[] payload() default {};
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
package com.cmhi.magent.validation.valids;
|
||||
|
||||
import com.cmhi.magent.validation.valids.impl.ValidHttpMethodImpl;
|
||||
import jakarta.validation.Constraint;
|
||||
import jakarta.validation.Payload;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 自定义校验注解,用于验证字段或参数是否为有效的 HTTP 方法。
|
||||
* <p>
|
||||
* 该注解适用于字段或方法参数,校验逻辑通过 {@link ValidHttpMethodImpl} 实现。
|
||||
* </p>
|
||||
* <p>
|
||||
* 可用于确保字段或参数值为标准的 HTTP 请求方法,例如:GET、POST、PUT、DELETE 等。
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 相关参数说明:
|
||||
* <ul>
|
||||
* <li><b>message</b>:校验失败时返回的错误消息,默认为空字符串,可自定义。</li>
|
||||
* <li><b>groups</b>:指定校验分组,用于分组校验逻辑。</li>
|
||||
* <li><b>payload</b>:扩展字段,用于携带元数据信息。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 默认的实现类:{@link ValidHttpMethodImpl}。
|
||||
* 该实现通常包含自定义逻辑,确保输入值属于预定义的 HTTP 方法集合。
|
||||
* </p>
|
||||
*
|
||||
* @author
|
||||
* @version 1.0.0
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
@Target({ElementType.FIELD, ElementType.PARAMETER})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Constraint(validatedBy = {ValidHttpMethodImpl.class})
|
||||
public @interface ValidHttpMethod {
|
||||
|
||||
/**
|
||||
* 错误消息,当字段或参数值不为有效的 HTTP 方法时返回。
|
||||
*
|
||||
* @return 默认错误消息,支持自定义。
|
||||
*/
|
||||
String message() default "";
|
||||
|
||||
/**
|
||||
* 指定校验分组。
|
||||
* <p>
|
||||
* 可用于分组校验场景,例如在不同的业务逻辑中使用不同的校验规则。
|
||||
* </p>
|
||||
*
|
||||
* @return 校验分组,默认为空。
|
||||
*/
|
||||
Class<?>[] groups() default {};
|
||||
|
||||
/**
|
||||
* 用于携带元数据信息的扩展字段。
|
||||
* <p>
|
||||
* 通常用于自定义校验逻辑时传递附加信息。
|
||||
* </p>
|
||||
*
|
||||
* @return 负载类型数组。
|
||||
*/
|
||||
Class<? extends Payload>[] payload() default {};
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
package com.cmhi.magent.validation.valids;
|
||||
|
||||
import com.cmhi.magent.validation.valids.impl.ValidPageSizeImpl;
|
||||
import jakarta.validation.Constraint;
|
||||
import jakarta.validation.Payload;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 自定义校验注解,用于验证分页查询时的页大小(Page Size)是否有效。
|
||||
* <p>
|
||||
* 该注解适用于字段或方法参数,校验逻辑由 {@link ValidPageSizeImpl} 实现。
|
||||
* </p>
|
||||
* <p>
|
||||
* 通常用于限制分页参数的范围,确保页大小符合系统的限制要求(例如:页大小应在 1 到某个最大值之间)。
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 相关参数说明:
|
||||
* <ul>
|
||||
* <li><b>message</b>:校验失败时返回的错误消息。</li>
|
||||
* <li><b>groups</b>:指定校验分组,适配不同场景的校验逻辑。</li>
|
||||
* <li><b>payload</b>:扩展字段,用于携带校验的元数据信息。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 默认的实现类:{@link ValidPageSizeImpl}。
|
||||
* 该实现类通常包含用于校验页大小范围的具体逻辑(比如最小页大小为 1,最大页大小为 100 或其他系统配置值)。
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 使用示例:
|
||||
* <pre>
|
||||
* @ValidPageSize(message = "Page size must be between 1 and 100")
|
||||
* private Integer pageSize;
|
||||
* </pre>
|
||||
* </p>
|
||||
*
|
||||
* @author
|
||||
* @version 1.0.0
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
@Target({ElementType.FIELD, ElementType.PARAMETER})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Constraint(validatedBy = {ValidPageSizeImpl.class})
|
||||
public @interface ValidPageSize {
|
||||
|
||||
/**
|
||||
* 错误消息,当页大小(Page Size)无效时返回。
|
||||
*
|
||||
* @return 默认错误消息,支持自定义。
|
||||
*/
|
||||
String message();
|
||||
|
||||
/**
|
||||
* 指定校验分组。
|
||||
* <p>
|
||||
* 可用于分组校验,例如在不同业务场景中应用不同的校验规则。
|
||||
* </p>
|
||||
*
|
||||
* @return 校验分组,默认为空。
|
||||
*/
|
||||
Class<?>[] groups() default {};
|
||||
|
||||
/**
|
||||
* 用于携带元数据信息的扩展字段。
|
||||
* <p>
|
||||
* 通常用于高级校验逻辑或特定场景下的附加信息传递。
|
||||
* </p>
|
||||
*
|
||||
* @return 负载类型数组。
|
||||
*/
|
||||
Class<? extends Payload>[] payload() default {};
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
package com.cmhi.magent.validation.valids;
|
||||
|
||||
import com.cmhi.magent.validation.valids.impl.ValidProtocolTimestampImpl;
|
||||
import jakarta.validation.Constraint;
|
||||
import jakarta.validation.Payload;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 自定义校验注解,用于验证协议中的时间戳(Protocol Timestamp)是否符合规定的格式和范围。
|
||||
* <p>
|
||||
* 该注解适用于字段或方法参数,校验逻辑由 {@link ValidProtocolTimestampImpl} 实现。
|
||||
* </p>
|
||||
* <p>
|
||||
* 通常应用于需要验证时间戳的场景,例如请求中的时间戳字段,确保时间戳符合业务规则(如格式正确,未过期等)。
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 相关参数说明:
|
||||
* <ul>
|
||||
* <li><b>message</b>:校验失败时返回的错误消息。</li>
|
||||
* <li><b>groups</b>:指定校验分组,适配不同场景的校验逻辑。</li>
|
||||
* <li><b>payload</b>:扩展字段,用于携带校验的元数据信息。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 默认的实现类:{@link ValidProtocolTimestampImpl}。
|
||||
* 该实现类通常包含以下校验逻辑:
|
||||
* <ul>
|
||||
* <li>验证时间戳的格式是否符合 ISO 8601 或其他指定格式。</li>
|
||||
* <li>校验时间戳是否在允许的时间范围内(例如,不能早于一定时间,也不能超出当前时间)。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
* @author huangxin@cmhi.chinamobile.com
|
||||
* @version 1.0.0
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
@Target({ElementType.FIELD, ElementType.PARAMETER})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Constraint(validatedBy = {ValidProtocolTimestampImpl.class})
|
||||
public @interface ValidProtocolTimestamp {
|
||||
|
||||
/**
|
||||
* 错误消息,当时间戳(Timestamp)无效时返回。
|
||||
*
|
||||
* @return 默认错误消息,支持自定义。
|
||||
*/
|
||||
String message();
|
||||
|
||||
/**
|
||||
* 指定校验分组。
|
||||
* <p>
|
||||
* 可用于分组校验,例如在不同业务场景中应用不同的校验规则。
|
||||
* </p>
|
||||
*
|
||||
* @return 校验分组,默认为空。
|
||||
*/
|
||||
Class<?>[] groups() default {};
|
||||
|
||||
/**
|
||||
* 用于携带元数据信息的扩展字段。
|
||||
* <p>
|
||||
* 通常用于高级校验逻辑或特定场景下的附加信息传递。
|
||||
* </p>
|
||||
*
|
||||
* @return 负载类型数组。
|
||||
*/
|
||||
Class<? extends Payload>[] payload() default {};
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
package com.cmhi.magent.validation.valids.impl;
|
||||
|
||||
import com.cmhi.magent.misc.ApiContextUtils;
|
||||
import com.cmhi.magent.misc.MessageUtil;
|
||||
import com.cmhi.magent.validation.valids.ValidHttpMethod;
|
||||
import jakarta.validation.ConstraintValidator;
|
||||
import jakarta.validation.ConstraintValidatorContext;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 实现类 `ValidHttpMethodImpl`,用于校验被 {@link ValidHttpMethod} 注解标注的字段中包含的 HTTP 方法是否合法。
|
||||
* <p>
|
||||
* 该类实现了 {@link ConstraintValidator} 接口,通过其 `isValid` 方法实现自定义校验逻辑。
|
||||
* 主要服务于验证 HTTP 请求方法,确保字段值(如 `GET`、`POST` 等)符合标准的 HTTP 方法规范。
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 核心功能:
|
||||
* <ul>
|
||||
* <li>验证传入的 HTTP 方法列表是否有效。</li>
|
||||
* <li>提供详细的校验错误消息,包括无效方法及原因。</li>
|
||||
* <li>支持多语言错误提示,提升用户体验。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 使用场景:
|
||||
* <ul>
|
||||
* <li>动态路由配置:校验配置中 HTTP 方法是否合法。</li>
|
||||
* <li>API 参数校验:确保客户端传入的 HTTP 方法符合约定。</li>
|
||||
* <li>安全性检查:防止非法或拼写错误的 HTTP 方法导致异常。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* @see ValidHttpMethod
|
||||
* @see ConstraintValidator
|
||||
* @author huangxin@cmhi.chinamobile.com
|
||||
* @version 1.0.0
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
public class ValidHttpMethodImpl implements ConstraintValidator<ValidHttpMethod, List<String>> {
|
||||
|
||||
/**
|
||||
* 初始化校验器的方法。
|
||||
* <p>
|
||||
* 默认实现中调用了父类的初始化逻辑,可根据需要进行扩展。
|
||||
* </p>
|
||||
*
|
||||
* @param constraintAnnotation {@code @ValidHttpMethod} 注解,包含相关参数信息。
|
||||
*/
|
||||
@Override
|
||||
public void initialize(ValidHttpMethod constraintAnnotation) {
|
||||
ConstraintValidator.super.initialize(constraintAnnotation);
|
||||
}
|
||||
|
||||
/**
|
||||
* 核心校验逻辑,判断传入的 HTTP 方法列表是否包含无效的方法。
|
||||
* <p>
|
||||
* 遍历列表中的每个方法,尝试通过 {@link RequestMethod#resolve(String)} 方法解析。
|
||||
* 如果解析结果为 {@code null},则判定该方法无效,并记录错误信息。
|
||||
* 如果存在无效方法,校验失败并返回详细的错误消息;若所有方法均有效,则校验通过。
|
||||
* </p>
|
||||
*
|
||||
* @param strings 需要校验的 HTTP 方法列表。
|
||||
* @param context 校验器上下文,用于构建错误消息。
|
||||
* @return {@code true} 表示校验通过,{@code false} 表示校验失败。
|
||||
*/
|
||||
@Override
|
||||
public boolean isValid(List<String> strings, ConstraintValidatorContext context) {
|
||||
StringBuilder errMsg = new StringBuilder();
|
||||
|
||||
// 遍历 HTTP 方法列表并校验
|
||||
strings.forEach(k -> {
|
||||
if (RequestMethod.resolve(k.toUpperCase()) == null) {
|
||||
errMsg.append("[")
|
||||
.append(k)
|
||||
.append("]: ")
|
||||
.append(MessageUtil.get("http.request_invalid", ApiContextUtils.getLanguage()));
|
||||
}
|
||||
});
|
||||
|
||||
// 如果存在错误消息,校验失败
|
||||
if (!errMsg.isEmpty()) {
|
||||
context.disableDefaultConstraintViolation();
|
||||
context.buildConstraintViolationWithTemplate(errMsg.toString()).addConstraintViolation();
|
||||
return false;
|
||||
}
|
||||
// 校验成功
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
package com.cmhi.magent.validation.valids.impl;
|
||||
|
||||
import com.cmhi.magent.validation.valids.ValidPageSize;
|
||||
import jakarta.validation.ConstraintValidator;
|
||||
import jakarta.validation.ConstraintValidatorContext;
|
||||
|
||||
/**
|
||||
* 实现类 {@code ValidPageSizeImpl},用于校验被 {@link ValidPageSize} 注解标注的字段值是否为有效的分页大小。
|
||||
* <p>
|
||||
* 该类实现了 {@link ConstraintValidator} 接口,通过其 `isValid` 方法实现自定义校验逻辑。
|
||||
* 核心功能是验证给定的分页大小是否符合业务规则(例如,是否为 5 的倍数)。
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 核心功能:
|
||||
* <ul>
|
||||
* <li>校验分页大小是否为合法值。</li>
|
||||
* <li>支持动态注解配置(如可扩展为配置化规则)。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 使用场景:
|
||||
* <ul>
|
||||
* <li>分页 API 的参数校验,确保客户端传递的分页大小符合规定。</li>
|
||||
* <li>限制分页大小为标准化值,避免前端传递异常分页参数。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* @see ValidPageSize
|
||||
* @see ConstraintValidator
|
||||
* @author huangxin@cmhi.chinamobile.com
|
||||
* @version 1.0.0
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
public class ValidPageSizeImpl implements ConstraintValidator<ValidPageSize, Long> {
|
||||
|
||||
/**
|
||||
* 初始化校验器的方法。
|
||||
* <p>
|
||||
* 默认实现中调用了父类的初始化逻辑,目前无需特殊初始化步骤。
|
||||
* 如果需要从注解中提取参数,可在此方法中实现。
|
||||
* </p>
|
||||
*
|
||||
* @param constraintAnnotation {@code @ValidPageSize} 注解,包含相关参数信息。
|
||||
*/
|
||||
@Override
|
||||
public void initialize(ValidPageSize constraintAnnotation) {
|
||||
ConstraintValidator.super.initialize(constraintAnnotation);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验分页大小是否为有效值。
|
||||
* <p>
|
||||
* 业务规则:分页大小必须为 5 的倍数。若值符合规则,返回 {@code true};否则返回 {@code false}。
|
||||
* </p>
|
||||
*
|
||||
* @param pageSize 分页大小({@code Long} 类型),由注解标注的字段传入。
|
||||
* @param constraintValidatorContext 校验器上下文,用于构建动态错误消息(当前未使用)。
|
||||
* @return {@code true} 表示校验通过,{@code false} 表示校验失败。
|
||||
*/
|
||||
@Override
|
||||
public boolean isValid(Long pageSize, ConstraintValidatorContext constraintValidatorContext) {
|
||||
// 校验分页大小是否为 5 的倍数
|
||||
return pageSize != null && pageSize % 5 == 0;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,119 @@
|
|||
package com.cmhi.magent.validation.valids.impl;
|
||||
|
||||
import com.cmhi.magent.common.ConstValue;
|
||||
import com.cmhi.magent.config.ProtocolConfigure;
|
||||
import com.cmhi.magent.misc.ApiContextUtils;
|
||||
import com.cmhi.magent.misc.MessageUtil;
|
||||
import com.cmhi.magent.validation.valids.ValidProtocolTimestamp;
|
||||
import jakarta.validation.ConstraintValidator;
|
||||
import jakarta.validation.ConstraintValidatorContext;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
|
||||
/**
|
||||
* 实现类 {@code ValidProtocolTimestampImpl},用于校验被 {@link ValidProtocolTimestamp} 注解标注的时间戳是否符合协议要求。
|
||||
* <p>
|
||||
* 该类实现了 {@link ConstraintValidator} 接口,通过其 `isValid` 方法实现自定义校验逻辑。
|
||||
* 核心功能是验证时间戳的合法性,包括以下规则:
|
||||
* <ul>
|
||||
* <li>时间戳不能晚于当前时间。</li>
|
||||
* <li>时间戳与当前时间的差距不能超过设置的超时时间。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 核心功能:
|
||||
* <ul>
|
||||
* <li>校验时间戳是否有效,避免无效的时间戳影响协议逻辑。</li>
|
||||
* <li>支持动态超时时间配置,灵活适应不同场景。</li>
|
||||
* <li>提供详细的错误消息,包括当前时间和无效时间戳的具体信息。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 使用场景:
|
||||
* <ul>
|
||||
* <li>协议消息校验:确保消息的时间戳在合法范围内,避免过期或未来的消息被处理。</li>
|
||||
* <li>安全性校验:防止因时间戳问题导致的潜在安全漏洞。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* @see ValidProtocolTimestamp
|
||||
* @see ConstraintValidator
|
||||
* @see ProtocolConfigure
|
||||
* @see ConstValue
|
||||
* @see MessageUtil
|
||||
* @see ApiContextUtils
|
||||
* @author huangxin@cmhi.chinamobile.com
|
||||
* @version 1.0.0
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
public class ValidProtocolTimestampImpl implements ConstraintValidator<ValidProtocolTimestamp, Long> {
|
||||
|
||||
/**
|
||||
* 初始化校验器的方法。
|
||||
* <p>
|
||||
* 默认实现中调用了父类的初始化逻辑,目前无需特殊初始化步骤。
|
||||
* 如果需要从注解中提取参数,可在此方法中实现。
|
||||
* </p>
|
||||
*
|
||||
* @param constraintAnnotation {@code @ValidProtocolTimestamp} 注解,包含相关参数信息。
|
||||
*/
|
||||
@Override
|
||||
public void initialize(ValidProtocolTimestamp constraintAnnotation) {
|
||||
ConstraintValidator.super.initialize(constraintAnnotation);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验时间戳是否合法。
|
||||
* <p>
|
||||
* 校验逻辑包括:
|
||||
* <ul>
|
||||
* <li>如果 {@code ProtocolConfigure.CHECK_TIMESTAMP} 配置为 {@code false},则跳过校验,直接返回 {@code true}。</li>
|
||||
* <li>如果时间戳晚于当前时间,校验失败。</li>
|
||||
* <li>如果时间戳比当前时间早的时间差超过 {@code ProtocolConfigure.TIMEOUT_OF_SECONDS} 配置的超时时间,校验失败。</li>
|
||||
* </ul>
|
||||
* 如果校验失败,会通过 {@link ConstraintValidatorContext} 提供详细的错误提示信息。
|
||||
* </p>
|
||||
*
|
||||
* @param timeStamp 时间戳,将被校验的目标值。
|
||||
* @param constraintValidatorContext 校验器上下文,用于构建动态错误消息。
|
||||
* @return {@code true} 表示时间戳合法,{@code false} 表示时间戳不合法。
|
||||
*/
|
||||
@Override
|
||||
public boolean isValid(Long timeStamp, ConstraintValidatorContext constraintValidatorContext) {
|
||||
|
||||
// 如果跳过时间戳校验,则直接通过
|
||||
if (!ProtocolConfigure.CHECK_TIMESTAMP) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 格式化时间戳和当前时间
|
||||
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
|
||||
String date = format.format(timeStamp);
|
||||
String current = format.format(System.currentTimeMillis());
|
||||
|
||||
// 校验时间戳是否晚于当前时间
|
||||
if (timeStamp > System.currentTimeMillis()) {
|
||||
String errMsg = MessageUtil.get("timeout.current.format", new String[] {date, current}, ApiContextUtils.getLanguage());
|
||||
// 禁用默认的错误消息并设置自定义错误提示
|
||||
constraintValidatorContext.disableDefaultConstraintViolation();
|
||||
constraintValidatorContext.buildConstraintViolationWithTemplate(errMsg).addConstraintViolation();
|
||||
return false;
|
||||
}
|
||||
|
||||
// 校验时间戳是否超出允许的超时时间
|
||||
if (System.currentTimeMillis() - timeStamp > ProtocolConfigure.TIMEOUT_OF_SECONDS * ConstValue.MS_OF_SECOND) {
|
||||
String errMsg = MessageUtil.get("timeout.current.format",
|
||||
new String[] {date, current, String.valueOf(ProtocolConfigure.TIMEOUT_OF_SECONDS)},
|
||||
ApiContextUtils.getLanguage());
|
||||
// 禁用默认的错误消息并设置自定义错误提示
|
||||
constraintValidatorContext.disableDefaultConstraintViolation();
|
||||
constraintValidatorContext.buildConstraintViolationWithTemplate(errMsg).addConstraintViolation();
|
||||
return false;
|
||||
}
|
||||
|
||||
// 时间戳合法
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
NORMAL=Normal
|
||||
LOCKED=Locked
|
||||
DISABLED=Disabled
|
||||
DELETED=Deleted
|
||||
CRYPTO_NONE=Unencrypted
|
||||
CRYPTO_BASE64=Base64 encoding
|
||||
CRYPTO_AES128=AES128 encryption
|
||||
CRYPTO_DES=DES symmetric encryption
|
||||
CRYPTO_AES256=AES256 encryption
|
|
@ -0,0 +1,9 @@
|
|||
NORMAL=\u6B63\u5E38
|
||||
LOCKED=\u5DF2\u9501\u5B9A
|
||||
DISABLED=\u5DF2\u7981\u7528
|
||||
DELETED=\u5DF2\u5220\u9664
|
||||
CRYPTO_NONE=\u4E0D\u52A0\u5BC6
|
||||
CRYPTO_BASE64=Base64\u7F16\u7801
|
||||
CRYPTO_AES128=AES128\u52A0\u5BC6
|
||||
CRYPTO_DES=DES\u5BF9\u79F0\u52A0\u5BC6
|
||||
CRYPTO_AES256=AES256\u52A0\u5BC6
|
|
@ -0,0 +1,58 @@
|
|||
ERR_OK=Successful
|
||||
ERR_PASSWORD=Incorrect password
|
||||
ERR_USERNOTFOUND=User does not exist
|
||||
ERR_PASSWORDMORE=The number of consecutive incorrect passwords reaches the upper limit
|
||||
ERR_USERLOCK=Password error reaches the upper limit, The user is locked
|
||||
ERR_PASSWORD_EXPIRED=The password has expired
|
||||
ERR_ACCOUNT=The user account is abnormal
|
||||
ERR_USEREXIST=The user already exists
|
||||
ERR_PASSWORDSIMPLE=The user password strength does not meet the requirement. ERR_INPUTFORMAT=The input information format is incorrect
|
||||
ERR_INPUTMISS=No necessary input information
|
||||
ERR_PERMISSION=Operator permission is insufficient
|
||||
ERR_REQTIMEOUT=Request timeout
|
||||
ERR_PARAMS=Parameter error
|
||||
ERR_SYSTEMEXCEPTION=System exception
|
||||
ERR_UNKNOWNCMD=Unknown command
|
||||
ERR_LOGOUT=User not logged in
|
||||
ERR_TOKENTIMEOUT=Token timeout
|
||||
ERR_TOKENNOTFOUND=Illegal Token
|
||||
ERR_TOKEN_KEY=Token key error
|
||||
ERR_MISSAUTHHEAD=Http request missing authentication header
|
||||
ERR_NOSUCHITEM=No such content
|
||||
ERR_ITEMEXISTS=The content already exists
|
||||
ERR_PARAMEXCEPTION=Parameter exception
|
||||
ERR_DEVICELOCKED=Device locked
|
||||
ERR_VERSION=Protocol version incompatible, Please upgrade the system
|
||||
ERR_NOSUCHTYPE=No device of this type exists
|
||||
ERR_REMOVEMORE=Disable deleting multiple devices at the same time
|
||||
ERR_TASKRUNNING=Similar tasks are running
|
||||
ERR_UNSUPPORT=Unsupported operations
|
||||
ERR_INTERRUPT=Operation interruption
|
||||
ERR_CALLDEVICE=Failed to call the device
|
||||
ERR_NOSUCHTASK=No task
|
||||
ERR_TASKNOTRUNNING=No task is running
|
||||
ERR_REQUESTTIMEOUT=Request timeout
|
||||
ERR_UNABLEDISPOSEIP=Unable to handle the IP
|
||||
ERR_DATABASE=Failed to operate the database
|
||||
ERR_UNTRUSTHOST=Unauthorized client
|
||||
ERR_UNTRUSTTOKEN=Unauthorized Token
|
||||
ERR_UNKNOWNINTERFACE=Interface not provided
|
||||
ERR_DECRYPT_BASE64=BASE64 decryption failure
|
||||
ERR_ENCRYPT_BASE64=BASE64 encryption failure
|
||||
ERR_DECRYPT_AES128=AES128 decryption failure
|
||||
ERR_ENCRYPT_AES128=AES128 encryption failure
|
||||
ERR_DECRYPT_3DES=3DES decryption failure
|
||||
ERR_ENCRYPT_3DES=3DES Encryption failure
|
||||
ERR_DECRYPT_UNKNOWN=Unsupported decryption algorithm
|
||||
ERR_ENCRYPT_UNKNOWN=unsupported encryption algorithm
|
||||
ERR_JSON_ENCODE=Json Sequence number error
|
||||
ERR_JSON_DECODE=Json deserialization error
|
||||
ERR_ENCRYPT_AES256=AES256 Encryption failure
|
||||
ERR_DECRYPT_AES256=AES256 decryption failure
|
||||
ERR_CRYPTO_KEY=Wrong secret key
|
||||
ERR_USER_ROLE_NOTEXISTS=The user role does not exist
|
||||
ERR_RESOURCE_USED=Resource used
|
||||
err.auth.key.convert=Key algorithm or key conversion error
|
||||
err.auth.key.verify=Key missing verification data
|
||||
err.auth.key.timeout=Key expired
|
||||
err.auth.key.prase=Key resolution error
|
|
@ -0,0 +1,59 @@
|
|||
ERR_OK=\u6210\u529F
|
||||
ERR_PASSWORD=\u5BC6\u7801\u9519\u8BEF
|
||||
ERR_USERNOTFOUND=\u7528\u6237\u4E0D\u5B58\u5728
|
||||
ERR_PASSWORDMORE=\u8FDE\u7EED\u5BC6\u7801\u9519\u8BEF\u8FBE\u4E0A\u9650\uFF0C\u518D\u6B21\u8F93\u5165\u9519\u8BEF\u5C06\u9501\u5B9A\u7528\u6237
|
||||
ERR_USERLOCK=\u5BC6\u7801\u9519\u8BEF\u8FBE\u4E0A\u9650\uFF0C\u7528\u6237\u88AB\u9501\u5B9A
|
||||
ERR_PASSWORD_EXPIRED=\u5BC6\u7801\u5DF2\u7ECF\u8FC7\u671F
|
||||
ERR_ACCOUNT=\u7528\u6237\u8D26\u6237\u5F02\u5E38
|
||||
ERR_USEREXIST=\u8BE5\u7528\u6237\u5DF2\u7ECF\u5B58\u5728
|
||||
ERR_PASSWORDSIMPLE=\u7528\u6237\u5BC6\u7801\u5F3A\u5EA6\u4E0D\u7B26\u5408\u8981\u6C42
|
||||
ERR_INPUTFORMAT=\u8F93\u5165\u4FE1\u606F\u683C\u5F0F\u6709\u8BEF
|
||||
ERR_INPUTMISS=\u7F3A\u5C11\u5FC5\u8981\u8F93\u5165\u4FE1\u606F
|
||||
ERR_PERMISSION=\u64CD\u4F5C\u5458\u6743\u9650\u4E0D\u8DB3
|
||||
ERR_REQTIMEOUT=\u8BF7\u6C42\u8D85\u65F6
|
||||
ERR_PARAMS=\u53C2\u6570\u9519\u8BEF
|
||||
ERR_SYSTEMEXCEPTION=\u7CFB\u7EDF\u5F02\u5E38
|
||||
ERR_UNKNOWNCMD=\u672A\u77E5\u547D\u4EE4
|
||||
ERR_LOGOUT=\u7528\u6237\u672A\u767B\u5F55
|
||||
ERR_TOKENTIMEOUT=Token\u8D85\u65F6
|
||||
ERR_TOKENNOTFOUND=\u975E\u6CD5Token
|
||||
ERR_TOKEN_KEY=Token \u79D8\u94A5\u9519\u8BEF
|
||||
ERR_MISSAUTHHEAD=Http \u8BF7\u6C42\u7F3A\u5C11\u8BA4\u8BC1\u5934\u90E8
|
||||
ERR_NOSUCHITEM=\u6CA1\u6709\u8BE5\u5185\u5BB9
|
||||
ERR_ITEMEXISTS=\u8BE5\u5185\u5BB9\u5DF2\u7ECF\u5B58\u5728
|
||||
ERR_PARAMEXCEPTION=\u53C2\u6570\u5F02\u5E38
|
||||
ERR_DEVICELOCKED=\u8BBE\u5907\u5DF2\u9501\u5B9A
|
||||
ERR_VERSION=\u534F\u8BAE\u7248\u672C\u4E0D\u517C\u5BB9\uFF0C\u8BF7\u5347\u7EA7\u7CFB\u7EDF
|
||||
ERR_NOSUCHTYPE=\u6CA1\u6709\u8FD9\u4E2A\u7C7B\u578B\u7684\u8BBE\u5907
|
||||
ERR_REMOVEMORE=\u7981\u6B62\u540C\u65F6\u5220\u9664\u591A\u4E2A\u8BBE\u5907
|
||||
ERR_TASKRUNNING=\u540C\u7C7B\u4EFB\u52A1\u6B63\u5728\u8FD0\u884C
|
||||
ERR_UNSUPPORT=\u4E0D\u652F\u6301\u7684\u64CD\u4F5C
|
||||
ERR_INTERRUPT=\u64CD\u4F5C\u4E2D\u65AD
|
||||
ERR_CALLDEVICE=\u8C03\u7528\u8BBE\u5907\u5931\u8D25
|
||||
ERR_NOSUCHTASK=\u6CA1\u6709\u8BE5\u4EFB\u52A1
|
||||
ERR_TASKNOTRUNNING=\u8BE5\u4EFB\u52A1\u6CA1\u6709\u8FD0\u884C
|
||||
ERR_REQUESTTIMEOUT=\u8BF7\u6C42\u8D85\u65F6
|
||||
ERR_UNABLEDISPOSEIP=\u65E0\u6CD5\u5904\u7F6E\u8BE5IP
|
||||
ERR_DATABASE=\u64CD\u4F5C\u6570\u636E\u5E93\u5931\u8D25
|
||||
ERR_UNTRUSTHOST=\u672A\u7ECF\u6388\u6743\u7684\u5BA2\u6237\u7AEF
|
||||
ERR_UNTRUSTTOKEN=\u672A\u7ECF\u6388\u6743\u7684Token
|
||||
ERR_UNKNOWNINTERFACE=\u672A\u63D0\u4F9B\u8BE5\u63A5\u53E3
|
||||
ERR_DECRYPT_BASE64=BASE64\u89E3\u5BC6\u5931\u8D25
|
||||
ERR_ENCRYPT_BASE64=BASE64\u52A0\u5BC6\u5931\u8D25
|
||||
ERR_DECRYPT_AES128=AES128\u89E3\u5BC6\u5931\u8D25
|
||||
ERR_ENCRYPT_AES128=AES128\u52A0\u5BC6\u5931\u8D25
|
||||
ERR_DECRYPT_3DES=3DES\u89E3\u5BC6\u5931\u8D25
|
||||
ERR_ENCRYPT_3DES=3DES\u52A0\u5BC6\u5931\u8D25
|
||||
ERR_DECRYPT_UNKNOWN=\u4E0D\u652F\u6301\u7684\u89E3\u5BC6\u7B97\u6CD5
|
||||
ERR_ENCRYPT_UNKNOWN=\u4E0D\u652F\u6301\u7684\u52A0\u5BC6\u7B97\u6CD5
|
||||
ERR_JSON_ENCODE=Json \u5E8F\u5217\u53F7\u9519\u8BEF
|
||||
ERR_JSON_DECODE=Json \u53CD\u5E8F\u5217\u5316\u9519\u8BEF
|
||||
ERR_ENCRYPT_AES256=AES256\u52A0\u5BC6\u5931\u8D25
|
||||
ERR_DECRYPT_AES256=AES256\u89E3\u5BC6\u5931\u8D25
|
||||
ERR_CRYPTO_KEY=\u9519\u8BEF\u7684\u79D8\u94A5
|
||||
ERR_USER_ROLE_NOTEXISTS=\u7528\u6237\u89D2\u8272\u4E0D\u5B58\u5728
|
||||
ERR_RESOURCE_USED=\u8D44\u6E90\u88AB\u5360\u7528
|
||||
err.auth.key.convert=\u5BC6\u94A5\u7B97\u6CD5\u6216\u8005\u5BC6\u94A5\u8F6C\u6362\u9519\u8BEF
|
||||
err.auth.key.verify=\u5BC6\u94A5\u7F3A\u5C11\u6821\u9A8C\u6570\u636E
|
||||
err.auth.key.timeout=\u5BC6\u94A5\u5DF2\u8FC7\u671F
|
||||
err.auth.key.prase=\u5BC6\u94A5\u89E3\u6790\u9519\u8BEF
|
|
@ -0,0 +1,37 @@
|
|||
SYSLOG_MOD_COMMON=Universal Module
|
||||
SYSLOG_MOD_SYSLOG=Operation Log Module
|
||||
SYSLOG_MOD_AUTH=Rights Management Module
|
||||
SYSLOG_MOD_DICT=Dictionary Module
|
||||
SYSLOG_MOD_SYSINFO=System Information Module
|
||||
SYSLOG_MOD_USER_MGR=User Management Module
|
||||
SYSLOG_MOD_SECURITY=System Safety Module
|
||||
SYSLOG_TYPE_AUTH=Authentication/Authentication
|
||||
SYSLOG_TYPE_READ=READ
|
||||
SYSLOG_TYPE_CREATE=CREATE
|
||||
SYSLOG_TYPE_DEL=DELETE
|
||||
SYSLOG_TYPE_MODIFY=MODIFY
|
||||
SYSLOG_DESC_GET_VERSION=Obtain the current system version information
|
||||
SYSLOG_DESC_GET_SYSLOG_SUMMARY=Obtain the brief information about operationlogs
|
||||
SYSLOG_DESC_GET_SYSLOG_DETAIL=Obtain operation logdetails
|
||||
SYSLOG_DESC_GET_CUR_USR_RES_RIGHT=Get the current user resource rights
|
||||
SYSLOG_DESC_GET_USR_RES_RIGHT=Obtain user resource rights
|
||||
SYSLOG_DESC_GET_ALL_USR_GROUP=Get all current user groups
|
||||
SYSLOG_DESC_CREATE_RESOURCE=Register a new resource
|
||||
SYSLOG_DESC_DEL_RESOURCE=Delete resource
|
||||
SYSLOG_DESC_GET_ALL_ENUM_DICT=Get all enumeration dictionary type information of the system
|
||||
SYSLOG_DESC_GET_ENUM_DETAIL=Gets the details of the system enumeration dictionary
|
||||
SYSLOG_DESC_CREATE_USER_DICT=Creating a user dictionary
|
||||
SYSLOG_DESC_MODIFY_USER_DICT=Modify the contents of the user dictionary
|
||||
SYSLOG_DESC_GET_ALL_USER_DICT=Get all user dictionaries
|
||||
SYSLOG_DESC_GET_USER_DETAIL=Get the user dictionary content item
|
||||
SYSLOG_DESC_CREATE_USER_DICT_ITEM=Add user dictionary content items
|
||||
SYSLOG_DESC_GET_OS_INFO=Obtain information about the current operating system
|
||||
SYSLOG_DESC_GET_CPU_INFO=Gets current processor information
|
||||
SYSLOG_DESC_GET_HW_INFO=Obtain system hardware information
|
||||
SYSLOG_DESC_GET_USER=Get user information
|
||||
SYSLOG_DESC_GET_USER_LIST=Get user list
|
||||
SYSLOG_DESC_GET_CUR_USER=get current user information
|
||||
SYSLOG_DESC_CREATE_USER=Create new user
|
||||
SYSLOG_DESC_DEL_USER=Delete user
|
||||
SYSLOG_DESC_LOGOUT=Logout
|
||||
SYSLOG_DESC_LOGIN=Login
|
|
@ -0,0 +1,37 @@
|
|||
SYSLOG_MOD_COMMON=\u901A\u7528\u6A21\u5757
|
||||
SYSLOG_MOD_SYSLOG=\u64CD\u4F5C\u65E5\u5FD7\u6A21\u5757
|
||||
SYSLOG_MOD_AUTH=\u6743\u9650\u7BA1\u7406\u6A21\u5757
|
||||
SYSLOG_MOD_DICT=\u5B57\u5178\u6A21\u5757
|
||||
SYSLOG_MOD_SYSINFO=\u7CFB\u7EDF\u4FE1\u606F\u6A21\u5757
|
||||
SYSLOG_MOD_USER_MGR=\u7528\u6237\u7BA1\u7406\u6A21\u5757
|
||||
SYSLOG_MOD_SECURITY=\u7CFB\u7EDF\u5B89\u5168\u6A21\u5757
|
||||
SYSLOG_TYPE_AUTH=\u8BA4\u8BC1/\u9274\u6743
|
||||
SYSLOG_TYPE_READ=\u8BFB\u53D6
|
||||
SYSLOG_TYPE_CREATE=\u521B\u5EFA
|
||||
SYSLOG_TYPE_DEL=\u5220\u9664
|
||||
SYSLOG_TYPE_MODIFY=\u4FEE\u6539
|
||||
SYSLOG_DESC_GET_VERSION=\u83B7\u53D6\u5F53\u524D\u7CFB\u7EDF\u7248\u672C\u4FE1\u606F
|
||||
SYSLOG_DESC_GET_SYSLOG_SUMMARY=\u83B7\u53D6\u64CD\u4F5C\u65E5\u5FD7\u7B80\u8981\u4FE1\u606F
|
||||
SYSLOG_DESC_GET_SYSLOG_DETAIL=\u83B7\u53D6\u64CD\u4F5C\u65E5\u5FD7\u8BE6\u7EC6\u4FE1\u606F
|
||||
SYSLOG_DESC_GET_CUR_USR_RES_RIGHT=\u83B7\u53D6\u5F53\u524D\u7528\u6237\u8D44\u6E90\u6743\u9650
|
||||
SYSLOG_DESC_GET_USR_RES_RIGHT=\u83B7\u53D6\u7528\u6237\u8D44\u6E90\u6743\u9650
|
||||
SYSLOG_DESC_GET_ALL_USR_GROUP=\u83B7\u53D6\u5F53\u524D\u6240\u6709\u7528\u6237\u7EC4
|
||||
SYSLOG_DESC_CREATE_RESOURCE=\u6CE8\u518C\u65B0\u7684\u8D44\u6E90
|
||||
SYSLOG_DESC_DEL_RESOURCE=\u5220\u9664\u8D44\u6E90
|
||||
SYSLOG_DESC_GET_ALL_ENUM_DICT=\u83B7\u53D6\u7CFB\u7EDF\u6240\u6709\u679A\u4E3E\u5B57\u5178\u7C7B\u578B\u4FE1\u606F
|
||||
SYSLOG_DESC_GET_ENUM_DETAIL=\u83B7\u53D6\u7CFB\u7EDF\u679A\u4E3E\u5B57\u5178\u8BE6\u7EC6\u5185\u5BB9
|
||||
SYSLOG_DESC_CREATE_USER_DICT=\u65B0\u5EFA\u7528\u6237\u5B57\u5178
|
||||
SYSLOG_DESC_MODIFY_USER_DICT=\u4FEE\u6539\u7528\u6237\u5B57\u5178\u5185\u5BB9
|
||||
SYSLOG_DESC_GET_ALL_USER_DICT=\u83B7\u53D6\u6240\u6709\u7528\u6237\u5B57\u5178
|
||||
SYSLOG_DESC_GET_USER_DETAIL=\u83B7\u53D6\u7528\u6237\u5B57\u5178\u5185\u5BB9\u9879
|
||||
SYSLOG_DESC_CREATE_USER_DICT_ITEM=\u65B0\u589E\u7528\u6237\u5B57\u5178\u5185\u5BB9\u9879
|
||||
SYSLOG_DESC_GET_OS_INFO=\u83B7\u53D6\u5F53\u524D\u64CD\u4F5C\u7CFB\u7EDF\u4FE1\u606F
|
||||
SYSLOG_DESC_GET_CPU_INFO=\u83B7\u53D6\u5F53\u524D\u5904\u7406\u5668\u4FE1\u606F
|
||||
SYSLOG_DESC_GET_HW_INFO=\u83B7\u53D6\u7CFB\u7EDF\u786C\u4EF6\u4FE1\u606F
|
||||
SYSLOG_DESC_GET_USER=\u83B7\u53D6\u7528\u6237\u4FE1\u606F
|
||||
SYSLOG_DESC_GET_USER_LIST=\u83B7\u53D6\u7528\u6237\u5217\u8868
|
||||
SYSLOG_DESC_GET_CUR_USER=\u83B7\u53D6\u5F53\u524D\u7528\u6237\u4FE1\u606F
|
||||
SYSLOG_DESC_CREATE_USER=\u521B\u5EFA\u65B0\u7528\u6237
|
||||
SYSLOG_DESC_DEL_USER=\u5220\u9664\u7528\u6237
|
||||
SYSLOG_DESC_LOGOUT=\u6CE8\u9500
|
||||
SYSLOG_DESC_LOGIN=\u767B\u5F55
|
|
@ -0,0 +1,14 @@
|
|||
item.not_null=field cannot be null
|
||||
item.not_empty=field cannot be empty string
|
||||
item.value_range=Field value range [{min}, {max}]
|
||||
item.value_min=Minimum value is {min}
|
||||
array.value_range=Array/List size range [{min}, {max}]
|
||||
array.not_empty=Arrays/List cannot be NULL and elements cannot be empty
|
||||
timestamp.timeout=The difference between the field value and the current time cannot be greater than the timeout configuration
|
||||
page.item_size=The value must be an integer multiple of 5
|
||||
uuid.format=UUID string must match format(xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)
|
||||
invalid.character=Invalid character strings exist
|
||||
password.length=The password size must be SHA256 length
|
||||
http.request_invalid=Not a legitimate HTTP request method
|
||||
timeout.current.format=Request time[{0}] greater than current server time[{1}]
|
||||
timeout.server.format=Request time [{0}] Delay exceeds current server time [{1}] Allowed range {2}(S)
|
|
@ -0,0 +1,14 @@
|
|||
item.not_null=\u5B57\u6BB5\u4E0D\u80FD\u4E3A NULL
|
||||
item.not_empty=\u5B57\u6BB5\u4E0D\u80FD\u4E3A\u7A7A\u5B57\u7B26\u4E32
|
||||
item.value_range=\u5B57\u6BB5\u53D6\u503C\u8303\u56F4 [{min}, {max}]
|
||||
item.value_min=\u6700\u5C0F\u503C\u4E3A {min}
|
||||
array.value_range=\u6570\u7EC4/\u94FE\u8868\u5143\u7D20\u4E2A\u6570\u53D6\u503C\u8303\u56F4 [{min}, {max}]
|
||||
array.not_empty=\u6570\u7EC4/\u94FE\u8868\u4E0D\u80FD\u4E3ANULL\u4E14\u5143\u7D20\u4E0D\u80FD\u4E3A\u7A7A
|
||||
timestamp.timeout=\u5B57\u6BB5\u503C\u4E0E\u5F53\u524D\u65F6\u95F4\u5DEE\u4E0D\u80FD\u5927\u4E8E\u8D85\u65F6\u914D\u7F6E
|
||||
page.item_size=\u53D6\u503C\u5FC5\u987B\u4E3A 5 \u7684\u6574\u6570\u500D
|
||||
uuid.format=\u5FC5\u987B\u4E3A\u7B26\u5408UUID\u89C4\u8303(xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)\u7684\u5B57\u7B26\u4E32
|
||||
invalid_character=\u5B57\u7B26\u4E32\u5B58\u5728\u975E\u6CD5\u5B57\u7B26
|
||||
password.length=\u5BC6\u7801\u957F\u5EA6\u5FC5\u987B\u4E3ASHA256\u7F16\u7801\u540E\u7684\u957F\u5EA6
|
||||
http.request_invalid=\u4E0D\u662F\u5408\u6CD5\u7684 HTTP \u8BF7\u6C42\u65B9\u6CD5
|
||||
timeout.current.format=\u8BF7\u6C42\u65F6\u95F4[{0}] \u5927\u4E8E\u5F53\u524D\u670D\u52A1\u5668\u65F6\u95F4[{1}]
|
||||
timeout.server.format=\u8BF7\u6C42\u65F6\u95F4[{0}] \u5EF6\u65F6\u8D85\u8FC7\u5F53\u524D\u670D\u52A1\u5668\u65F6\u95F4[{1}] \u5141\u8BB8\u8303\u56F4 {2}(S)
|
|
@ -0,0 +1,13 @@
|
|||
package com.cmhi.magent;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
@SpringBootTest
|
||||
class MiddlewareAgentApplicationTests {
|
||||
|
||||
@Test
|
||||
void contextLoads() {
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,133 @@
|
|||
package com.cmhi.magent.common;
|
||||
|
||||
|
||||
import com.cmhi.magent.MiddlewareAgentApplication;
|
||||
import com.cmhi.magent.config.ProtocolConfigure;
|
||||
import com.cmhi.magent.misc.HelperUtils;
|
||||
import com.cmhi.magent.misc.ProtocolJsonUtils;
|
||||
import com.cmhi.magent.pojo.dto.ProtocolReq;
|
||||
import com.cmhi.magent.pojo.po.BaseRespStatus;
|
||||
import com.cmhi.magent.pojo.vo.ProtocolResp;
|
||||
import com.cmhi.magent.service.ProtocolSecurityService;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.TestInstance;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
|
||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
|
||||
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT, classes = {MiddlewareAgentApplication.class})
|
||||
@AutoConfigureMockMvc
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
@ActiveProfiles({"common", "test", "user"})
|
||||
public abstract class TestBaseAuthentication {
|
||||
protected static final ThreadLocal<HttpHeaders> headersThreadLocal = new ThreadLocal<>();
|
||||
|
||||
@Autowired
|
||||
protected MockMvc mockMvc;
|
||||
|
||||
@Resource
|
||||
private ProtocolSecurityService securityService;
|
||||
|
||||
public HttpHeaders getHeaders() {
|
||||
return headersThreadLocal.get();
|
||||
}
|
||||
|
||||
@BeforeAll
|
||||
public void setUpClass() {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
MediaType type = MediaType.parseMediaType("application/json; charset=UTF-8");
|
||||
headers.setContentType(type);
|
||||
headers.add("Accept", MediaType.APPLICATION_JSON.toString());
|
||||
headers.add(HttpHeaders.ACCEPT_ENCODING, "gzip, deflate, br");
|
||||
headersThreadLocal.set(headers);
|
||||
}
|
||||
|
||||
public <T> Object performanceRestful(RequestMethod reqType, T reqObject, String urlPath, Class<?>[] subRespClass) throws Exception {
|
||||
MockHttpServletRequestBuilder build = createMockMvcBuilder(urlPath, reqType);
|
||||
|
||||
if (Objects.nonNull(reqObject)) {
|
||||
String sendMsgContent;
|
||||
ProtocolReq<T> reqInfo = new ProtocolReq<>();
|
||||
reqInfo.setVer(ConstValue.Protocol.VERSION);
|
||||
reqInfo.setCryptoType(ProtocolConfigure.SECURITY_PROTOCOL_TYPE);
|
||||
reqInfo.setTimeStamp(System.currentTimeMillis());
|
||||
reqInfo.setMsgContent(reqObject);
|
||||
|
||||
if (ProtocolConfigure.SECURITY_PROTOCOL_TYPE != ProtoCryptoType.CRYPTO_NONE.getValue()) {
|
||||
String cipherText = securityService.encryptProtocolString(HelperUtils.getJson(reqObject),
|
||||
ProtocolConfigure.SECURITY_PROTOCOL_TYPE);
|
||||
|
||||
ProtocolReq<String> cipherInfo = new ProtocolReq<>();
|
||||
cipherInfo.setVer(reqInfo.getVer());
|
||||
cipherInfo.setCryptoType(reqInfo.getCryptoType());
|
||||
cipherInfo.setTimeStamp(System.currentTimeMillis());
|
||||
cipherInfo.setMsgContent(cipherText);
|
||||
sendMsgContent = HelperUtils.getJson(cipherInfo);
|
||||
} else {
|
||||
sendMsgContent = HelperUtils.getJson(reqInfo);
|
||||
}
|
||||
|
||||
build.content(sendMsgContent);
|
||||
}
|
||||
|
||||
|
||||
String rspValue = mockMvc.perform(build)
|
||||
.andDo(print())
|
||||
.andReturn()
|
||||
.getResponse()
|
||||
.getContentAsString();
|
||||
|
||||
AssertValidString(rspValue);
|
||||
|
||||
rspValue = securityService.decryptProtocol(rspValue);
|
||||
AssertValidString(rspValue);
|
||||
|
||||
ProtocolResp<?> resp = ProtocolJsonUtils.jsonGetProtocolResp(rspValue, subRespClass);
|
||||
|
||||
Assertions.assertNotNull(resp);
|
||||
Assertions.assertNotNull(resp.getMsgContent());
|
||||
Assertions.assertEquals(resp.getCode(), HttpStatus.OK.value());
|
||||
|
||||
return resp.getMsgContent();
|
||||
}
|
||||
|
||||
private MockHttpServletRequestBuilder createMockMvcBuilder(String uri, RequestMethod reqType) {
|
||||
MockHttpServletRequestBuilder build = switch (reqType) {
|
||||
case PUT -> MockMvcRequestBuilders.put(uri);
|
||||
case GET -> MockMvcRequestBuilders.get(uri);
|
||||
case DELETE -> MockMvcRequestBuilders.delete(uri);
|
||||
default -> MockMvcRequestBuilders.post(uri);
|
||||
};
|
||||
|
||||
build.accept(MediaType.APPLICATION_JSON);
|
||||
build.headers(getHeaders());
|
||||
return build;
|
||||
}
|
||||
|
||||
public void AssertValidString(String str) {
|
||||
Assertions.assertNotNull(str);
|
||||
Assertions.assertFalse(str.isEmpty());
|
||||
}
|
||||
|
||||
public <T extends BaseRespStatus> void AssertValidCommonResp(T resp) {
|
||||
Assertions.assertNotNull(resp);
|
||||
Assertions.assertNotNull(resp.getStatus());
|
||||
Assertions.assertNotNull(resp.getMessage());
|
||||
Assertions.assertNotEquals(0, resp.getMessage().length);
|
||||
Assertions.assertEquals(resp.getStatus(), ErrorCode.ERR_OK.getCode());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
package com.cmhi.magent.controller;
|
||||
|
||||
import com.cmhi.magent.common.TestBaseAuthentication;
|
||||
import com.cmhi.magent.pojo.vo.VersionResp;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
|
||||
@ExtendWith({SpringExtension.class})
|
||||
@SuppressWarnings("java:S5786")
|
||||
public class CommonFrameworkApiTest extends TestBaseAuthentication {
|
||||
@Test
|
||||
@DisplayName("获取版本信息")
|
||||
void testGetVersion() throws Exception {
|
||||
VersionResp resp = (VersionResp) performanceRestful(RequestMethod.GET, null, "/api/version", new Class[] {VersionResp.class});
|
||||
AssertValidCommonResp(resp);
|
||||
Assertions.assertNotNull(resp.getVersion());
|
||||
|
||||
Assertions.assertNotNull(resp.getVersion().getTagName());
|
||||
AssertValidString(resp.getVersion().getCommitId());
|
||||
AssertValidString(resp.getVersion().getCommitDescribe());
|
||||
AssertValidString(resp.getVersion().getCommitTime());
|
||||
AssertValidString(resp.getVersion().getBuildTime());
|
||||
AssertValidString(resp.getVersion().getGitBranch());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("获取版本信息(POST)")
|
||||
void testGetVersionV2() throws Exception {
|
||||
VersionResp resp = (VersionResp) performanceRestful(RequestMethod.POST, null, "/api/version", new Class[] {VersionResp.class});
|
||||
AssertValidCommonResp(resp);
|
||||
Assertions.assertNotNull(resp.getVersion());
|
||||
|
||||
Assertions.assertNotNull(resp.getVersion().getTagName());
|
||||
AssertValidString(resp.getVersion().getCommitId());
|
||||
AssertValidString(resp.getVersion().getCommitDescribe());
|
||||
AssertValidString(resp.getVersion().getCommitTime());
|
||||
AssertValidString(resp.getVersion().getBuildTime());
|
||||
AssertValidString(resp.getVersion().getGitBranch());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
package com.cmhi.magent.service.impl;
|
||||
|
||||
import com.cmhi.magent.pojo.po.HwInfo;
|
||||
import com.cmhi.magent.pojo.po.ProcessorInfo;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class SystemInfoServiceImplTest {
|
||||
|
||||
private SystemInfoServiceImpl systemInfoServiceImplUnderTest;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
systemInfoServiceImplUnderTest = new SystemInfoServiceImpl();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetOsName() {
|
||||
// Setup
|
||||
// Run the test
|
||||
final String result = systemInfoServiceImplUnderTest.getOsName();
|
||||
|
||||
// Verify the results
|
||||
assertThat(result).isNotEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetOsBootTimeStamp() {
|
||||
// Setup
|
||||
// Run the test
|
||||
final long result = systemInfoServiceImplUnderTest.getOsBootTimeStamp();
|
||||
|
||||
// Verify the results
|
||||
assertThat(result).isPositive();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetProcessInfo() {
|
||||
// Run the test
|
||||
final ProcessorInfo result = systemInfoServiceImplUnderTest.getProcessInfo();
|
||||
|
||||
// Verify the results
|
||||
assertThat(result.getCpuIdentifier()).isNotEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetHardwareInfo() {
|
||||
// Run the test
|
||||
final HwInfo result = systemInfoServiceImplUnderTest.getHardwareInfo();
|
||||
|
||||
// Verify the results
|
||||
assertThat(result.getModel()).isNotEmpty();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue