瀏覽代碼

first commit

徐勤民 1 月之前
當前提交
7fce902044
共有 26 個文件被更改,包括 1507 次插入0 次删除
  1. 33 0
      .gitignore
  2. 19 0
      .mvn/wrapper/maven-wrapper.properties
  3. 259 0
      mvnw
  4. 149 0
      mvnw.cmd
  5. 85 0
      pom.xml
  6. 17 0
      src/main/java/com/xuqm/server/appmanager/AppManagerApplication.java
  7. 98 0
      src/main/java/com/xuqm/server/appmanager/common/SpringUtilsAuTo.java
  8. 81 0
      src/main/java/com/xuqm/server/appmanager/common/TimeHelper.java
  9. 54 0
      src/main/java/com/xuqm/server/appmanager/common/json/GsonImplHelp.java
  10. 29 0
      src/main/java/com/xuqm/server/appmanager/common/json/Json.java
  11. 18 0
      src/main/java/com/xuqm/server/appmanager/common/json/LocalDateAdapter.java
  12. 62 0
      src/main/java/com/xuqm/server/appmanager/controller/HelloController.java
  13. 69 0
      src/main/java/com/xuqm/server/appmanager/controller/sys/v1/TenantV1Controller.java
  14. 33 0
      src/main/java/com/xuqm/server/appmanager/entitys/converter/AbstractBaseTimeEntity.java
  15. 32 0
      src/main/java/com/xuqm/server/appmanager/entitys/converter/AbstractBaseTimeEntityNoKey.java
  16. 24 0
      src/main/java/com/xuqm/server/appmanager/entitys/converter/StringListConverter.java
  17. 16 0
      src/main/java/com/xuqm/server/appmanager/entitys/sys/v1/ApplicationEntity.java
  18. 18 0
      src/main/java/com/xuqm/server/appmanager/entitys/sys/v1/TenantEntity.java
  19. 280 0
      src/main/java/com/xuqm/server/appmanager/http/CurlUtil.java
  20. 32 0
      src/main/java/com/xuqm/server/appmanager/http/GlobalCorsConfig.java
  21. 43 0
      src/main/java/com/xuqm/server/appmanager/http/HttpResult.java
  22. 12 0
      src/main/java/com/xuqm/server/appmanager/repository/sys/v1/ApplicationRepository.java
  23. 11 0
      src/main/java/com/xuqm/server/appmanager/repository/sys/v1/TenantRepository.java
  24. 2 0
      src/main/java/lombok.config
  25. 18 0
      src/main/resources/application.properties
  26. 13 0
      src/test/java/com/xuqm/server/appmanager/AppManagerApplicationTests.java

+ 33 - 0
.gitignore

@@ -0,0 +1,33 @@
+HELP.md
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/

+ 19 - 0
.mvn/wrapper/maven-wrapper.properties

@@ -0,0 +1,19 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+wrapperVersion=3.3.2
+distributionType=only-script
+distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.7/apache-maven-3.9.7-bin.zip

+ 259 - 0
mvnw

@@ -0,0 +1,259 @@
+#!/bin/sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#    https://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 "$@"

+ 149 - 0
mvnw.cmd

@@ -0,0 +1,149 @@
+<# : batch portion
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements.  See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership.  The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License.  You may obtain a copy of the License at
+@REM
+@REM    https://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"

+ 85 - 0
pom.xml

@@ -0,0 +1,85 @@
+<?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.3.1</version>
+        <relativePath/> <!-- lookup parent from repository -->
+    </parent>
+    <groupId>com.xuqm.server</groupId>
+    <artifactId>AppManager</artifactId>
+    <version>0.0.1-SNAPSHOT</version>
+    <name>AppManager</name>
+    <description>AppManager</description>
+    <url/>
+    <licenses>
+        <license/>
+    </licenses>
+    <developers>
+        <developer/>
+    </developers>
+    <scm>
+        <connection/>
+        <developerConnection/>
+        <tag/>
+        <url/>
+    </scm>
+    <properties>
+        <java.version>17</java.version>
+    </properties>
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+        <!--引入热部署依赖-->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-devtools</artifactId>
+        </dependency>
+        <!--	数据库	-->
+        <!-- Spring Data JPA 依赖(重要) -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-jpa</artifactId>
+        </dependency>
+        <!-- MySQL 驱动(重要) -->
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+            <version>8.0.24</version>
+        </dependency>
+        <dependency>
+            <groupId>com.google.code.gson</groupId>
+            <artifactId>gson</artifactId>
+            <version>2.9.0</version>
+        </dependency>
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+            <version>29.0-jre</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

+ 17 - 0
src/main/java/com/xuqm/server/appmanager/AppManagerApplication.java

@@ -0,0 +1,17 @@
+package com.xuqm.server.appmanager;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
+import org.springframework.scheduling.annotation.EnableAsync;
+
+@EnableJpaAuditing
+@SpringBootApplication
+@EnableAsync
+public class AppManagerApplication {
+
+    public static void main(String[] args) {
+        SpringApplication.run(AppManagerApplication.class, args);
+    }
+
+}

+ 98 - 0
src/main/java/com/xuqm/server/appmanager/common/SpringUtilsAuTo.java

@@ -0,0 +1,98 @@
+package cn.org.bjca.trust.java.imserver.common;
+
+import org.springframework.aop.framework.AopContext;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.NoSuchBeanDefinitionException;
+import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.springframework.stereotype.Component;
+
+@Component
+public class SpringUtilsAuTo implements BeanFactoryPostProcessor {
+    /**
+     * Spring应用上下文环境
+     */
+    private static ConfigurableListableBeanFactory beanFactory;
+
+    @Override
+    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
+        SpringUtilsAuTo.beanFactory = beanFactory;
+    }
+
+    /**
+     * 获取对象
+     *
+     * @param name
+     * @return Object 一个以所给名字注册的bean的实例
+     * @throws BeansException
+     */
+    @SuppressWarnings("unchecked")
+    public static <T> T getBean(String name) throws BeansException {
+        return (T) beanFactory.getBean(name);
+    }
+
+    /**
+     * 获取类型为requiredType的对象
+     *
+     * @param clz
+     * @return
+     * @throws BeansException
+     */
+    public static <T> T getBean(Class<T> clz) throws BeansException {
+        T result = (T) beanFactory.getBean(clz);
+        return result;
+    }
+
+    /**
+     * 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true
+     *
+     * @param name
+     * @return boolean
+     */
+    public static boolean containsBean(String name) {
+        return beanFactory.containsBean(name);
+    }
+
+    /**
+     * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException)
+     *
+     * @param name
+     * @return boolean
+     * @throws NoSuchBeanDefinitionException
+     */
+    public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException {
+        return beanFactory.isSingleton(name);
+    }
+
+    /**
+     * @param name
+     * @return Class 注册对象的类型
+     * @throws NoSuchBeanDefinitionException
+     */
+    public static Class<?> getType(String name) throws NoSuchBeanDefinitionException {
+        return beanFactory.getType(name);
+    }
+
+    /**
+     * 如果给定的bean名字在bean定义中有别名,则返回这些别名
+     *
+     * @param name
+     * @return
+     * @throws NoSuchBeanDefinitionException
+     */
+    public static String[] getAliases(String name) throws NoSuchBeanDefinitionException {
+        return beanFactory.getAliases(name);
+    }
+
+    /**
+     * 获取aop代理对象
+     *
+     * @param invoker
+     * @return
+     */
+    @SuppressWarnings("unchecked")
+    public static <T> T getAopProxy(T invoker) {
+        return (T) AopContext.currentProxy();
+    }
+}
+

+ 81 - 0
src/main/java/com/xuqm/server/appmanager/common/TimeHelper.java

@@ -0,0 +1,81 @@
+package cn.org.bjca.trust.java.imserver.common;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+
+public class TimeHelper {
+
+    /**
+     * 获取当前时间戳
+     *
+     * @return 时间戳
+     */
+    public static long getTimeMillis() {
+        return System.currentTimeMillis();
+    }
+
+    /**
+     * 获取当前时间,指定返回样式
+     *
+     * @param formats 指定样式
+     * @return 时间字符串
+     */
+    public static String getTimeString(String formats) {
+        return getStringFormMillis(System.currentTimeMillis(), formats);
+    }
+
+    /**
+     * 根据给定时间戳和样式,返回字符串
+     *
+     * @param millis  时间戳
+     * @param formats 指定字符串格式
+     * @return 时间字符串
+     */
+    public static String getStringFormMillis(long millis, String formats) {
+        Date date = new Date(millis);
+        return new SimpleDateFormat(formats, Locale.getDefault()).format(date);
+    }
+
+    /**
+     * 根据Date 返回指定格式的时字符串
+     *
+     * @param date    数据
+     * @param formats 格式
+     * @return 指定格式的字符串
+     */
+    public static String getStringFromDate(Date date, String formats) {
+        SimpleDateFormat formatter = new SimpleDateFormat(formats, Locale.getDefault());
+        return formatter.format(date);
+    }
+
+    /**
+     * 根据给定字符串和格式,获取时间戳
+     *
+     * @param dateString 时间字符串
+     * @param formats    时间格式
+     * @return 时间戳
+     */
+    public static long getTimeMillisForType(String dateString, String formats) {
+        SimpleDateFormat format = new SimpleDateFormat(formats, Locale.getDefault());
+        Date date = null;
+        try {
+            date = format.parse(dateString);
+        } catch (ParseException e) {
+            e.printStackTrace();
+        }
+
+        return date != null ? date.getTime() : 0;
+
+    }
+
+    /**
+     * 获取以秒为单位的时间戳
+     *
+     * @return 秒
+     */
+    public static long getTimeFromSecond() {
+        return getTimeMillis() / 1000;
+    }
+}

+ 54 - 0
src/main/java/com/xuqm/server/appmanager/common/json/GsonImplHelp.java

@@ -0,0 +1,54 @@
+package com.xuqm.server.appmanager.common.json;
+
+import com.google.gson.*;
+
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Created by xuqm on 2016/6/3.
+ */
+public class GsonImplHelp extends Json {
+    private final Gson gson = new GsonBuilder()
+            .setPrettyPrinting()
+            .registerTypeAdapter(LocalDateTime.class, new LocalDateAdapter()).create();
+
+    @Override
+    public String toJson(Object src) {
+        return gson.toJson(src);
+
+    }
+
+    @Override
+    public <T> T toObject(String json, Class<T> claxx) {
+        return gson.fromJson(json, claxx);
+
+    }
+
+    @Override
+    public <T> T toObject(byte[] bytes, Class<T> claxx) {
+        return gson.fromJson(new String(bytes), claxx);
+
+    }
+
+    public <T> List<T> toList(String json, Class<T> clazz) {
+        JsonArray jsonArray = JsonParser.parseString(json).getAsJsonArray();
+
+        List<T> list = new ArrayList<>();
+        for (JsonElement jsonElement : jsonArray) {
+            list.add(gson.fromJson(jsonElement, clazz)); //cls
+        }
+
+        return list;
+
+
+    }
+
+    public static <T> List<T> stringToArray(String s, Class<T[]> cls) {
+        T[] array = new Gson().fromJson(s, cls);
+        return Arrays.asList(array);
+    }
+
+}

+ 29 - 0
src/main/java/com/xuqm/server/appmanager/common/json/Json.java

@@ -0,0 +1,29 @@
+package com.xuqm.server.appmanager.common.json;
+
+import java.util.List;
+
+/**
+ * Created by xuqm on 2016/6/3.
+ */
+public abstract class Json {
+    private static Json json;
+
+    Json() {
+    }
+
+    public static Json get() {
+        if (json == null) {
+            json = new GsonImplHelp();
+        }
+        return json;
+    }
+
+    public abstract String toJson(Object src);
+
+    public abstract <T> T toObject(String json, Class<T> claxx);
+
+    public abstract <T> T toObject(byte[] bytes, Class<T> claxx);
+
+    public abstract <T> List<T> toList(String json, Class<T> claxx);
+
+}

+ 18 - 0
src/main/java/com/xuqm/server/appmanager/common/json/LocalDateAdapter.java

@@ -0,0 +1,18 @@
+package com.xuqm.server.appmanager.common.json;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonPrimitive;
+import com.google.gson.JsonSerializationContext;
+import com.google.gson.JsonSerializer;
+
+import java.lang.reflect.Type;
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+
+public class LocalDateAdapter implements JsonSerializer<LocalDateTime> {
+    @Override
+    public JsonElement serialize(LocalDateTime localDateTime, Type type, JsonSerializationContext jsonSerializationContext) {
+        return new JsonPrimitive(localDateTime.toInstant(ZoneOffset.ofHours(8)).toEpochMilli());
+    }
+}
+

+ 62 - 0
src/main/java/com/xuqm/server/appmanager/controller/HelloController.java

@@ -0,0 +1,62 @@
+package com.xuqm.server.appmanager.controller;
+
+
+import jakarta.servlet.http.HttpServletRequest;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.File;
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Random;
+import java.util.UUID;
+import java.util.concurrent.*;
+
+@RestController
+@RequestMapping("hello")
+public class HelloController {
+
+    @GetMapping("/{id}")
+    public String getById(@PathVariable String id) throws Exception {
+        System.out.println("id ==> " + id);
+        return "{\"msg\":\"qdxorigin\",\"code\":200,\"data\":\"Sbfuiaefhaikufhcsauik\"}";
+    }
+
+    SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd/");
+
+    @PostMapping("/upload")
+    public String upload(MultipartFile file, HttpServletRequest req) {
+
+        System.out.println("-------");
+        System.out.println(req.toString());
+        System.out.println("-------");
+        if (null == file) return """
+                {
+                  "code": 500,
+                  "message": "系统异常"
+                }""";
+        String realPath =
+                req.getSession().getServletContext().getRealPath("/uploadFile/");
+        String format = sdf.format(new Date());
+        File folder = new File(realPath + format);
+        String filePath = "";
+        if (!folder.isDirectory()) {
+            folder.mkdirs();
+            String oldName = file.getOriginalFilename();
+            String newName = UUID.randomUUID().toString() +
+                    oldName.substring(oldName.lastIndexOf("."), oldName.length());
+            try {
+                file.transferTo(new File(folder, newName));
+                filePath = req.getScheme() + "://" + req.getServerName() + ":" +
+                        req.getServerPort() + "/uploadFile/" + format + newName;
+
+            } catch (IOException e) {
+                e.printStackTrace();
+                return "上传失败! ";
+            }
+        }
+        return filePath;
+    }
+
+}

+ 69 - 0
src/main/java/com/xuqm/server/appmanager/controller/sys/v1/TenantV1Controller.java

@@ -0,0 +1,69 @@
+package com.xuqm.server.appmanager.controller.sys.v1;
+
+import cn.org.bjca.trust.java.imserver.common.TimeHelper;
+import com.xuqm.server.appmanager.entitys.sys.v1.ApplicationEntity;
+import com.xuqm.server.appmanager.entitys.sys.v1.TenantEntity;
+import com.xuqm.server.appmanager.http.HttpResult;
+import com.xuqm.server.appmanager.repository.sys.v1.ApplicationRepository;
+import com.xuqm.server.appmanager.repository.sys.v1.TenantRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.Random;
+
+@RestController
+@RequestMapping("tenant/v1")
+public class TenantV1Controller {
+
+    @Autowired
+    private TenantRepository tenantRepository;
+    @Autowired
+    private ApplicationRepository applicationRepository;
+
+    @PostMapping("/tenant/create")
+    public HttpResult<String> tenantCreate(@RequestBody TenantEntity tenant) throws Exception {
+        if (null == tenant.getTenantName() ||
+                null == tenant.getUserPhone() ||
+                null == tenant.getUserEmail() ||
+                null == tenant.getUserName()) {
+            return new HttpResult<>(201, "参数错误", "");
+        } else {
+            TenantEntity t = tenantRepository.findFirstByUserEmailOrUserPhone(tenant.getUserEmail(), tenant.getUserPhone());
+            if (null != t) return new HttpResult<>(201, "当前联系人已注册", "");
+            t = new TenantEntity();
+            t.setTenantName(tenant.getTenantName());
+            t.setUserEmail(tenant.getUserEmail());
+            t.setUserName(tenant.getUserName());
+            t.setUserPhone(tenant.getUserPhone());
+            t.setTenantNo(TimeHelper.getTimeString("yyyyMMddHHmm") + (new Random().nextInt(899999999) + 100000000));
+            tenantRepository.save(t);
+
+//            RabbitMQHelper.requestByGetAndParams("http://114.115.203.60:15672/api/vhosts/" + appid, "");
+            return new HttpResult<>(200, "创建成功", "创建成功");
+        }
+
+    }
+
+    @PostMapping("/app/create")
+    public HttpResult<String> appCreate(@RequestBody ApplicationEntity application) throws Exception {
+        if (null == application.getAppName() ||
+                null == application.getTenantNo()) {
+            return new HttpResult<>(201, "参数错误", "");
+        } else {
+            ApplicationEntity app = applicationRepository.findFirstByAppNameAndTenantNo(application.getAppName(), application.getTenantNo());
+            if (null != app) return new HttpResult<>(201, "已创建相关类型的同名应用", "");
+            app = new ApplicationEntity();
+            app.setTenantNo(application.getTenantNo());
+            app.setAppName(application.getAppName());
+            app.setAppId(TimeHelper.getTimeString("yyyyMMddHHmm") + (new Random().nextInt(899999999) + 100000000));
+            applicationRepository.save(app);
+
+            return new HttpResult<>(200, "创建成功", "创建成功");
+        }
+
+    }
+
+}

+ 33 - 0
src/main/java/com/xuqm/server/appmanager/entitys/converter/AbstractBaseTimeEntity.java

@@ -0,0 +1,33 @@
+package com.xuqm.server.appmanager.entitys.converter;
+
+import jakarta.persistence.*;
+import lombok.Getter;
+import lombok.Setter;
+import org.springframework.data.annotation.CreatedDate;
+import org.springframework.data.annotation.LastModifiedDate;
+import org.springframework.data.jpa.domain.support.AuditingEntityListener;
+
+import java.time.LocalDateTime;
+
+/**
+ * 所有类的超类
+ * 自动更新创建时间和更新时间
+ *
+ * @author peter
+ **/
+@MappedSuperclass
+@EntityListeners(value = AuditingEntityListener.class)
+@Getter
+@Setter
+public abstract class AbstractBaseTimeEntity {
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long _id;
+
+    @CreatedDate
+    @Column(nullable = false, updatable = false)
+    private LocalDateTime createTime;
+    @LastModifiedDate
+    @Column()
+    private LocalDateTime updateTime;
+}

+ 32 - 0
src/main/java/com/xuqm/server/appmanager/entitys/converter/AbstractBaseTimeEntityNoKey.java

@@ -0,0 +1,32 @@
+package com.xuqm.server.appmanager.entitys.converter;
+
+import jakarta.persistence.Column;
+import jakarta.persistence.EntityListeners;
+import jakarta.persistence.MappedSuperclass;
+import lombok.Getter;
+import lombok.Setter;
+import org.springframework.data.annotation.CreatedDate;
+import org.springframework.data.annotation.LastModifiedDate;
+import org.springframework.data.jpa.domain.support.AuditingEntityListener;
+
+import java.time.LocalDateTime;
+
+/**
+ * 所有类的超类
+ * 自动更新创建时间和更新时间
+ *
+ * @author peter
+ **/
+@MappedSuperclass
+@EntityListeners(value = AuditingEntityListener.class)
+@Getter
+@Setter
+public abstract class AbstractBaseTimeEntityNoKey {
+
+    @CreatedDate
+    @Column(nullable = false, updatable = false)
+    private LocalDateTime createTime;
+    @LastModifiedDate
+    @Column()
+    private LocalDateTime updateTime;
+}

+ 24 - 0
src/main/java/com/xuqm/server/appmanager/entitys/converter/StringListConverter.java

@@ -0,0 +1,24 @@
+package com.xuqm.server.appmanager.entitys.converter;
+
+import jakarta.persistence.AttributeConverter;
+import jakarta.persistence.Converter;
+
+import java.util.Arrays;
+import java.util.List;
+
+import static java.util.Collections.emptyList;
+
+@Converter
+public class StringListConverter implements AttributeConverter<List<String>, String> {
+    private static final String SPLIT_CHAR = "-><-";
+
+    @Override
+    public String convertToDatabaseColumn(List<String> stringList) {
+        return stringList != null ? String.join(SPLIT_CHAR, stringList) : "";
+    }
+
+    @Override
+    public List<String> convertToEntityAttribute(String string) {
+        return string != null ? Arrays.asList(string.split(SPLIT_CHAR)) : emptyList();
+    }
+}

+ 16 - 0
src/main/java/com/xuqm/server/appmanager/entitys/sys/v1/ApplicationEntity.java

@@ -0,0 +1,16 @@
+package com.xuqm.server.appmanager.entitys.sys.v1;
+
+import com.xuqm.server.appmanager.entitys.converter.AbstractBaseTimeEntity;
+import jakarta.persistence.Entity;
+import jakarta.persistence.Table;
+import lombok.Data;
+
+@Entity
+@Table(name = "applications")
+@Data
+public class ApplicationEntity extends AbstractBaseTimeEntity {
+    private String appId;
+    private String appName;
+    private String tenantNo;
+    private String userId;
+}

+ 18 - 0
src/main/java/com/xuqm/server/appmanager/entitys/sys/v1/TenantEntity.java

@@ -0,0 +1,18 @@
+package com.xuqm.server.appmanager.entitys.sys.v1;
+
+import com.xuqm.server.appmanager.entitys.converter.AbstractBaseTimeEntity;
+import jakarta.persistence.Entity;
+import jakarta.persistence.Table;
+import lombok.Data;
+
+@Entity
+@Table(name = "tenant")
+@Data
+public class TenantEntity extends AbstractBaseTimeEntity {
+    private String tenantNo;
+    private String tenantName;
+    private String userId;
+    private String userName;
+    private String userPhone;
+    private String userEmail;
+}

+ 280 - 0
src/main/java/com/xuqm/server/appmanager/http/CurlUtil.java

@@ -0,0 +1,280 @@
+package com.xuqm.server.appmanager.http;
+
+import com.xuqm.server.appmanager.common.json.GsonImplHelp;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.*;
+import org.springframework.http.client.ClientHttpResponse;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.client.ResponseErrorHandler;
+import org.springframework.web.client.RestTemplate;
+
+import java.io.*;
+import java.net.URLEncoder;
+import java.util.*;
+import java.util.zip.GZIPInputStream;
+
+import static org.springframework.http.HttpMethod.GET;
+import static org.springframework.http.HttpMethod.POST;
+
+@Slf4j
+public class CurlUtil {
+    // 请求结果
+    private String result = "";
+
+    /*** RestTemplate 请求构造内部类*/
+    public static class Builder {
+        // 请求URL
+        private String url;
+        // basic auth用户名
+        private String userName;
+        // basic auth密码
+        private String passWord;
+        // 请求方式 默认为GET
+        private HttpMethod methodType = GET;
+        // 请求参数
+        private HashMap<String, String> paramMap;
+        // 请求header头
+        private HashMap<String, String> headerMap;
+        // RestTemplate 实例
+        private RestTemplate client;
+        // header头实例
+        private HttpHeaders headers;
+        // 请求结果
+        private String result;
+
+        /*** 基础请求* @param url*/
+        public Builder(String url) {
+            this.url = url;
+        }
+
+
+        /*** basic auth 认证类型请求* @param url* @param userName* @param passWord*/
+        public Builder(String url, String userName, String passWord) {
+            this.url = url;
+            this.userName = userName;
+            this.passWord = passWord;
+        }
+
+        /*** 添加参数* @param key 参数key* @param value 参数内容* @return Builder*/
+        public Builder addParam(String key, String value) {
+            if (paramMap == null) {
+                paramMap = new LinkedHashMap<>(16);
+            }
+            paramMap.put(key, value);
+            return this;
+        }
+
+        /*** 添加header参数* @param key  参数key* @param value  参数内容* @return  Builder*/
+        public Builder addHeader(String key, String value) {
+            if (headerMap == null) {
+                headerMap = new LinkedHashMap<>(16);
+            }
+            headerMap.put(key, value);
+            return this;
+        }
+
+        /*** GET 请求* @return Curl*/
+        public CurlUtil get() {
+            this.methodType = GET;
+            this.getResponse(MediaType.APPLICATION_FORM_URLENCODED);
+            return new CurlUtil(this);
+        }
+
+        /*** post 请求* @return Curl*/
+        public CurlUtil post() {
+            this.methodType = POST;
+            this.getResponse(MediaType.APPLICATION_FORM_URLENCODED);
+            return new CurlUtil(this);
+        }
+
+        /*** raw 方式提交json 请求* @return Curl*/
+        public CurlUtil postRaw() {
+            HashMap<String, Object> paramRawMap = new HashMap<>();
+            return this.raw(paramRawMap);
+        }
+
+        /*** raw 方式提交json 请求* @param paramRawMap Map数据* @return Curl*/
+        public CurlUtil postRaw(HashMap<String, Object> paramRawMap) {
+            return this.raw(paramRawMap);
+        }
+
+        /*** RAW 请求* @param paramRawMap 参数* @return*/
+        private CurlUtil raw(HashMap<String, Object> paramRawMap) {
+            client = new RestTemplate();
+            client.setErrorHandler(new RestErrorHandler());
+            headers = new HttpHeaders();
+            headers.setContentType(MediaType.APPLICATION_JSON);
+            this.setHeadersMapData();
+            this.responseRawExchange(GsonImplHelp.get().toJson(paramRawMap));
+            return new CurlUtil(this);
+        }
+
+        /*** 初始化请求体* @param type 请求类型*/
+        private void getResponse(MediaType type) {
+            client = new RestTemplate();
+            client.setErrorHandler(new RestErrorHandler());
+            headers = new HttpHeaders();
+            headers.setContentType(type);
+            this.setHeadersMapData();
+            MultiValueMap<String, String> params = this.setHttpMethodParamsMap();
+            this.responseExchange(params);
+        }
+
+        /*** 设置header头数据*/
+        private void setHeadersMapData() {
+            if (headerMap != null) {
+                headerMap.forEach((k, v) -> {
+                    this.headers.set(k, v);
+                });
+            }
+            // 设置basic auth请求方式认证信息
+            if (this.userName != null && this.passWord != null) {
+                String secretKey = this.userName + ":" + this.passWord;
+                String authValue = "Basic " + Base64.getEncoder().encodeToString(secretKey.getBytes());
+                this.headers.set("Authorization", authValue);
+            }
+        }
+
+        /*** 组装请求体参数* @return MultiValueMap<String, String>*/
+        private MultiValueMap<String, String> setHttpMethodParamsMap() {
+            MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
+            if (this.methodType.equals(GET)) {
+                if (this.paramMap != null) {
+                    String _UrlParams = getUrlParamsByMap(this.paramMap);
+                    this.url = this.url + "?" + _UrlParams;
+                }
+
+                if (this.paramMap != null) {
+                    this.paramMap.forEach((k, v) -> {
+                        params.put(k, Collections.singletonList(v));
+                    });
+                }
+            } else if (this.methodType.equals(POST)) {
+                if (this.paramMap != null) {
+                    this.paramMap.forEach((k, v) -> {
+                        params.put(k, Collections.singletonList(v));
+                    });
+                }
+            }
+            return params;
+        }
+
+        /*** 执行Curl请求操作* @param params 请求体参数*/
+        private void responseExchange(MultiValueMap<String, String> params) {
+            HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(params, this.headers);
+            // 执行HTTP请求,将返回的结构使用spring ResponseEntity处理http响应
+            ResponseEntity<byte[]> responseEntity = this.client.exchange(this.url, this.methodType, requestEntity, byte[].class);
+            String contentEncoding = responseEntity.getHeaders().getFirst(HttpHeaders.CONTENT_ENCODING);
+            int httpCode = responseEntity.getStatusCodeValue();
+            String httpCodeValue = responseEntity.getStatusCode().toString();
+            log.info("状态码:{}", httpCodeValue);
+            try {
+                if ("gzip".equals(contentEncoding)) {
+                    // gzip解压服务器的响应体
+                    byte[] data = unGZip(new ByteArrayInputStream(responseEntity.getBody()));
+                    // log.info(new String(data, StandardCharsets.UTF_8));
+                    this.result = new String(data);
+                } else {
+                    // 其他编码暂时不做处理(如果需要处理其他编码请自行扩展)
+                    this.result = new String(responseEntity.getBody());
+                }
+            } catch (NullPointerException e) {
+                log.error("请求错误: {}", e.getMessage());
+                this.result = httpCodeValue;
+            }
+        }
+
+        /*** 执行Curl Raw JSON请求操作* @param params 请求体参数*/
+        private void responseRawExchange(String params) {
+            HttpEntity<String> requestEntity = new HttpEntity<>(params, this.headers);
+            // 执行HTTP请求,将返回的结构使用spring ResponseEntity处理http响应
+            ResponseEntity<byte[]> responseEntity = this.client.postForEntity(this.url, requestEntity, byte[].class);
+            String contentEncoding = responseEntity.getHeaders().getFirst(HttpHeaders.CONTENT_ENCODING);
+            if ("gzip".equals(contentEncoding)) {
+                // gzip解压服务器的响应体
+                byte[] data = unGZip(new ByteArrayInputStream(responseEntity.getBody()));
+                this.result = new String(data);
+            } else {
+                // 其他编码暂时不做处理(如果需要处理其他编码请自行扩展)
+                this.result = new String(responseEntity.getBody());
+            }
+        }
+
+        /*** Gzip解压缩* @param inputStream 数据流* @return byte[]*/
+        private byte[] unGZip(InputStream inputStream) {
+            byte[] result = null;
+            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+            try {
+                try (GZIPInputStream gzipInputStream = new GZIPInputStream(inputStream)) {
+                    byte[] buf = new byte[4096];
+                    int len = -1;
+                    while ((len = gzipInputStream.read(buf, 0, buf.length)) != -1) {
+                        byteArrayOutputStream.write(buf, 0, len);
+                    }
+                    result = byteArrayOutputStream.toByteArray();
+                } finally {
+                    byteArrayOutputStream.close();
+                }
+            } catch (IOException e) {
+                log.error("unGZip error :", e);
+            }
+            return result;
+        }
+
+        /*** 组装GET参数* @param params 参数* @return String*/
+        private String getUrlParamsByMap(Map<String, String> params) {
+            List<String> keys = new ArrayList<String>(params.keySet());
+            Collections.sort(keys);
+            String prestr = "";
+            try {
+                for (int i = 0; i < keys.size(); i++) {
+                    String key = keys.get(i);
+                    String value = params.get(key);
+                    value = URLEncoder.encode(value, "UTF-8");
+                    if (i == keys.size() - 1) {
+                        //拼接时,不包括最后一个&字符
+                        prestr = prestr + key + "=" + value;
+                    } else {
+                        prestr = prestr + key + "=" + value + "&";
+                    }
+                }
+            } catch (UnsupportedEncodingException e) {
+                log.error("GET params error: {}", e);
+            }
+            return prestr;
+        }
+    }
+
+    /*** RestTemplate 异常处理*/
+    public static class RestErrorHandler implements ResponseErrorHandler {
+        /*** 判断返回结果response是否是异常结果* 主要是去检查response 的HTTP Status* 仿造DefaultResponseErrorHandler实现即可*/
+        @Override
+        public boolean hasError(ClientHttpResponse response) throws IOException {
+            int rawStatusCode = response.getStatusCode().value();
+            HttpStatus statusCode = HttpStatus.resolve(rawStatusCode);
+            return (statusCode != null ? statusCode.isError() : hasError(rawStatusCode));
+        }
+
+        protected boolean hasError(int unknownStatusCode) {
+            HttpStatus.Series series = HttpStatus.Series.resolve(unknownStatusCode);
+            return (series == HttpStatus.Series.CLIENT_ERROR || series == HttpStatus.Series.SERVER_ERROR);
+        }
+
+        @Override
+        public void handleError(ClientHttpResponse response) throws IOException {
+            log.error("handleError:", response);
+        }
+
+    }
+
+    /*** CurlUtil 实例化* @param builder*/
+    public CurlUtil(Builder builder) {
+        this.result = builder.result;
+    }
+
+    /*** 结果* @return*/
+    public String build() {
+        return this.result;
+    }
+}

+ 32 - 0
src/main/java/com/xuqm/server/appmanager/http/GlobalCorsConfig.java

@@ -0,0 +1,32 @@
+package com.xuqm.server.appmanager.http;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.CorsRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+@Configuration
+public class GlobalCorsConfig {
+    @Bean
+    public WebMvcConfigurer corsConfigurer() {
+        return new WebMvcConfigurer() {
+            @Override
+            //重写父类提供的跨域请求处理的接口
+            public void addCorsMappings(CorsRegistry registry) {
+                //添加映射路径
+                registry.addMapping("/**")
+                        //放行哪些原始域
+                        .allowedOrigins("*")
+                        //是否发送Cookie信息
+                        .allowCredentials(false)
+                        //放行哪些原始域(请求方式)
+                        .allowedMethods("GET", "POST", "PUT", "DELETE")
+                        //放行哪些原始域(头部信息)
+                        .allowedHeaders("*")
+                        //暴露哪些头部信息(因为跨域访问默认不能获取全部头部信息)
+                        .exposedHeaders("Header1", "Header2");
+            }
+        };
+    }
+}
+

+ 43 - 0
src/main/java/com/xuqm/server/appmanager/http/HttpResult.java

@@ -0,0 +1,43 @@
+package com.xuqm.server.appmanager.http;
+
+public class HttpResult<T> {
+    /**
+     * "code": 200
+     * "message": "success"
+     * data :
+     */
+
+    private int code;
+    private String msg;
+    private T data;
+
+    public HttpResult(int code, String msg, T data) {
+        this.code = code;
+        this.msg = msg;
+        this.data = data;
+    }
+
+    public int getCode() {
+        return code;
+    }
+
+    public void setCode(int code) {
+        this.code = code;
+    }
+
+    public String getMsg() {
+        return msg;
+    }
+
+    public void setMsg(String msg) {
+        this.msg = msg;
+    }
+
+    public T getData() {
+        return data;
+    }
+
+    public void setData(T data) {
+        this.data = data;
+    }
+}

+ 12 - 0
src/main/java/com/xuqm/server/appmanager/repository/sys/v1/ApplicationRepository.java

@@ -0,0 +1,12 @@
+package com.xuqm.server.appmanager.repository.sys.v1;
+
+import com.xuqm.server.appmanager.entitys.sys.v1.ApplicationEntity;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface ApplicationRepository extends JpaRepository<ApplicationEntity, Long> {
+    ApplicationEntity findFirstByAppNameAndTenantNo(String appName, String tenantNo);
+    ApplicationEntity findFirstByAppId(String appId);
+    boolean existsByAppId(String appId);
+}

+ 11 - 0
src/main/java/com/xuqm/server/appmanager/repository/sys/v1/TenantRepository.java

@@ -0,0 +1,11 @@
+package com.xuqm.server.appmanager.repository.sys.v1;
+
+import com.xuqm.server.appmanager.entitys.sys.v1.TenantEntity;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface TenantRepository extends JpaRepository<TenantEntity, Long> {
+    TenantEntity findFirstByUserEmailOrUserPhone(String userEmail, String userPhone);
+
+}

+ 2 - 0
src/main/java/lombok.config

@@ -0,0 +1,2 @@
+config.stopBubbling=true
+lombok.equalsAndHashCode.callSuper=call

+ 18 - 0
src/main/resources/application.properties

@@ -0,0 +1,18 @@
+spring.application.name=AppManager
+server.port=4562
+
+#????????????
+spring.jpa.hibernate.ddl-auto=update
+#mysql?????????????
+spring.datasource.url = jdbc:mysql://114.115.203.60:3306/app-manager?serverTimezone=Asia/Shanghai
+#??????
+spring.datasource.username = app-manager
+#?????
+spring.datasource.password = hjCcSd43pkxbdCHX
+#mysql???????????
+spring.datasource.driver-class-name = com.mysql.cj.jdbc.Driver
+#jpa?????????Hibernate?sql(??)
+spring.jpa.show-sql = true
+#???????Thymeleaf ???
+spring.thymeleaf.cache = false
+

+ 13 - 0
src/test/java/com/xuqm/server/appmanager/AppManagerApplicationTests.java

@@ -0,0 +1,13 @@
+package com.xuqm.server.appmanager;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+
+@SpringBootTest
+class AppManagerApplicationTests {
+
+    @Test
+    void contextLoads() {
+    }
+
+}