From bc7389af9b2841ab2e5ab44c595b4a3bde195abf Mon Sep 17 00:00:00 2001 From: zhouwei <zhouwei@ssish.com> Date: Mon, 14 Apr 2025 13:26:56 +0800 Subject: [PATCH] init --- .gitignore | 33 ++ .mvn/wrapper/maven-wrapper.properties | 18 ++ Dockerfile-prd | 5 + Dockerfile-qa | 5 + README.md | 3 +- deployment.yaml | 42 +++ mvnw | 250 ++++++++++++++++ mvnw.cmd | 146 +++++++++ pom.xml | 131 ++++++++ service.yaml | 35 +++ .../securitylink/GatewayApplication.java | 11 + .../nanyan/securitylink/common/Constant.java | 39 +++ .../nanyan/securitylink/common/MsgCode.java | 38 +++ .../config/FailoverProperties.java | 29 ++ .../securitylink/config/GatewayConfig.java | 26 ++ .../config/RestTemplateConfig.java | 13 + .../securitylink/config/SchedulerConfig.java | 17 ++ .../controller/HealthController.java | 13 + .../nanyan/securitylink/dto/TranslateDTO.java | 10 + .../securitylink/dto/TranslateInputDTO.java | 9 + .../entity/ChatCompletionResponse.java | 16 + .../securitylink/entity/ChatInputData.java | 12 + .../nanyan/securitylink/entity/Choice.java | 11 + .../nanyan/securitylink/entity/Message.java | 10 + .../entity/PromptTokensDetails.java | 8 + .../com/nanyan/securitylink/entity/Usage.java | 13 + .../securitylink/entity/UserHeader.java | 40 +++ .../securitylink/entity/UserThreadLocal.java | 14 + .../securitylink/execption/BaseException.java | 20 ++ .../execption/HeaderException.java | 9 + .../filter/FailoverGatewayFilterFactory.java | 208 +++++++++++++ .../schedule/RefreshConfigSync.java | 39 +++ .../schedule/ScheduledTasksService.java | 30 ++ .../service/TokenRouteMappingService.java | 34 +++ .../nanyan/securitylink/utils/DateUtils.java | 283 ++++++++++++++++++ .../nanyan/securitylink/vo/AIResponse.java | 15 + .../com/nanyan/securitylink/vo/CodeVO.java | 9 + .../com/nanyan/securitylink/vo/Response.java | 61 ++++ .../com/nanyan/securitylink/vo/ResultVO.java | 10 + src/main/resources/application-prd.properties | 6 + src/main/resources/application-qa.properties | 26 ++ src/main/resources/application.properties | 7 + src/main/resources/log4j2.xml | 87 ++++++ 43 files changed, 1840 insertions(+), 1 deletion(-) create mode 100644 .gitignore create mode 100644 .mvn/wrapper/maven-wrapper.properties create mode 100644 Dockerfile-prd create mode 100644 Dockerfile-qa create mode 100644 deployment.yaml create mode 100644 mvnw create mode 100644 mvnw.cmd create mode 100644 pom.xml create mode 100644 service.yaml create mode 100644 src/main/java/com/nanyan/securitylink/GatewayApplication.java create mode 100644 src/main/java/com/nanyan/securitylink/common/Constant.java create mode 100644 src/main/java/com/nanyan/securitylink/common/MsgCode.java create mode 100644 src/main/java/com/nanyan/securitylink/config/FailoverProperties.java create mode 100644 src/main/java/com/nanyan/securitylink/config/GatewayConfig.java create mode 100644 src/main/java/com/nanyan/securitylink/config/RestTemplateConfig.java create mode 100644 src/main/java/com/nanyan/securitylink/config/SchedulerConfig.java create mode 100644 src/main/java/com/nanyan/securitylink/controller/HealthController.java create mode 100644 src/main/java/com/nanyan/securitylink/dto/TranslateDTO.java create mode 100644 src/main/java/com/nanyan/securitylink/dto/TranslateInputDTO.java create mode 100644 src/main/java/com/nanyan/securitylink/entity/ChatCompletionResponse.java create mode 100644 src/main/java/com/nanyan/securitylink/entity/ChatInputData.java create mode 100644 src/main/java/com/nanyan/securitylink/entity/Choice.java create mode 100644 src/main/java/com/nanyan/securitylink/entity/Message.java create mode 100644 src/main/java/com/nanyan/securitylink/entity/PromptTokensDetails.java create mode 100644 src/main/java/com/nanyan/securitylink/entity/Usage.java create mode 100644 src/main/java/com/nanyan/securitylink/entity/UserHeader.java create mode 100644 src/main/java/com/nanyan/securitylink/entity/UserThreadLocal.java create mode 100644 src/main/java/com/nanyan/securitylink/execption/BaseException.java create mode 100644 src/main/java/com/nanyan/securitylink/execption/HeaderException.java create mode 100644 src/main/java/com/nanyan/securitylink/filter/FailoverGatewayFilterFactory.java create mode 100644 src/main/java/com/nanyan/securitylink/schedule/RefreshConfigSync.java create mode 100644 src/main/java/com/nanyan/securitylink/schedule/ScheduledTasksService.java create mode 100644 src/main/java/com/nanyan/securitylink/service/TokenRouteMappingService.java create mode 100644 src/main/java/com/nanyan/securitylink/utils/DateUtils.java create mode 100644 src/main/java/com/nanyan/securitylink/vo/AIResponse.java create mode 100644 src/main/java/com/nanyan/securitylink/vo/CodeVO.java create mode 100644 src/main/java/com/nanyan/securitylink/vo/Response.java create mode 100644 src/main/java/com/nanyan/securitylink/vo/ResultVO.java create mode 100644 src/main/resources/application-prd.properties create mode 100644 src/main/resources/application-qa.properties create mode 100644 src/main/resources/application.properties create mode 100644 src/main/resources/log4j2.xml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..549e00a --- /dev/null +++ b/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..aeccdfd --- /dev/null +++ b/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,18 @@ +# 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.1 +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.6/apache-maven-3.9.6-bin.zip diff --git a/Dockerfile-prd b/Dockerfile-prd new file mode 100644 index 0000000..4db625e --- /dev/null +++ b/Dockerfile-prd @@ -0,0 +1,5 @@ +FROM openjdk:8 +ARG JAR_FILE=target/*.jar +COPY ${JAR_FILE} app.jar +ENV SPRING_PROFILES_ACTIVE=prd +ENTRYPOINT ["java","-jar","/app.jar"] \ No newline at end of file diff --git a/Dockerfile-qa b/Dockerfile-qa new file mode 100644 index 0000000..ac81655 --- /dev/null +++ b/Dockerfile-qa @@ -0,0 +1,5 @@ +FROM openjdk:8 +ARG JAR_FILE=target/*.jar +COPY ${JAR_FILE} app.jar +ENV SPRING_PROFILES_ACTIVE=qa +ENTRYPOINT ["java","-jar","/app.jar"] \ No newline at end of file diff --git a/README.md b/README.md index f930bb1..0f55acc 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,3 @@ -# sl-gateway +# sl-ai +调用大模型的APIæœåŠ¡ \ No newline at end of file diff --git a/deployment.yaml b/deployment.yaml new file mode 100644 index 0000000..b24fea2 --- /dev/null +++ b/deployment.yaml @@ -0,0 +1,42 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: sl-gateway + namespace: securitylink +spec: + replicas: 1 + selector: + matchLabels: + app: sl-gateway + template: + metadata: + labels: + app: sl-gateway + spec: + containers: + - name: sl-gateway + image: 381492067916.dkr.ecr.ap-east-1.amazonaws.com/sl-gateway:latest + resources: + limits: + memory: "2Gi" + cpu: "1000m" + requests: + memory: "500Mi" + cpu: "500m" + ports: + - name: httpport + containerPort: 8080 + livenessProbe: + tcpSocket: + port: 8080 + initialDelaySeconds: 90 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 5 + readinessProbe: + tcpSocket: + port: 8080 + initialDelaySeconds: 60 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 \ No newline at end of file diff --git a/mvnw b/mvnw new file mode 100644 index 0000000..ba9212a --- /dev/null +++ b/mvnw @@ -0,0 +1,250 @@ +#!/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.1 +# +# 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 +} + +# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties +while IFS="=" read -r key value; do + case "${key-}" in + distributionUrl) distributionUrl="${value-}" ;; + distributionSha256Sum) distributionSha256Sum="${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_HOME="$HOME/.m2/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")" + +exec_maven() { + unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || : + exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD" +} + +if [ -d "$MAVEN_HOME" ]; then + verbose "found existing MAVEN_HOME at $MAVEN_HOME" + exec_maven "$@" +fi + +case "${distributionUrl-}" in +*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;; +*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;; +esac + +# prepare tmp dir +if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then + clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; } + trap clean HUP INT TERM EXIT +else + die "cannot create temp dir" +fi + +mkdir -p -- "${MAVEN_HOME%/*}" + +# Download and Install Apache Maven +verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +verbose "Downloading from: $distributionUrl" +verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +# select .zip or .tar.gz +if ! command -v unzip >/dev/null; then + distributionUrl="${distributionUrl%.zip}.tar.gz" + distributionUrlName="${distributionUrl##*/}" +fi + +# verbose opt +__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR='' +[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v + +# normalize http auth +case "${MVNW_PASSWORD:+has-password}" in +'') MVNW_USERNAME='' MVNW_PASSWORD='' ;; +has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;; +esac + +if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then + verbose "Found wget ... using wget" + wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl" +elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then + verbose "Found curl ... using curl" + curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl" +elif set_java_home; then + verbose "Falling back to use Java to download" + javaSource="$TMP_DOWNLOAD_DIR/Downloader.java" + targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName" + cat >"$javaSource" <<-END + public class Downloader extends java.net.Authenticator + { + protected java.net.PasswordAuthentication getPasswordAuthentication() + { + return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() ); + } + public static void main( String[] args ) throws Exception + { + setDefault( new Downloader() ); + java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() ); + } + } + END + # For Cygwin/MinGW, switch paths to Windows format before running javac and java + verbose " - Compiling Downloader.java ..." + "$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java" + verbose " - Running Downloader.java ..." + "$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")" +fi + +# If specified, validate the SHA-256 sum of the Maven distribution zip file +if [ -n "${distributionSha256Sum-}" ]; then + distributionSha256Result=false + if [ "$MVN_CMD" = mvnd.sh ]; then + echo "Checksum validation is not supported for maven-mvnd." >&2 + echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + elif command -v sha256sum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c >/dev/null 2>&1; then + distributionSha256Result=true + fi + elif command -v shasum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then + distributionSha256Result=true + fi + else + echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2 + echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + fi + if [ $distributionSha256Result = false ]; then + echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2 + echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2 + exit 1 + fi +fi + +# unzip and move +if command -v unzip >/dev/null; then + unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip" +else + tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar" +fi +printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/mvnw.url" +mv -- "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME" + +clean || : +exec_maven "$@" diff --git a/mvnw.cmd b/mvnw.cmd new file mode 100644 index 0000000..d446e4d --- /dev/null +++ b/mvnw.cmd @@ -0,0 +1,146 @@ +<# : 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.1 +@REM +@REM Optional ENV vars +@REM MVNW_REPOURL - repo url base for downloading maven distribution +@REM MVNW_USERNAME/MVNW_PASSWORD - jwtUser 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" +$MAVEN_HOME_NAME = ([System.Security.Cryptography.MD5]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join '' +$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME" + +if (Test-Path -Path "$MAVEN_HOME" -PathType Container) { + Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME" + Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" + exit $? +} + +if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) { + Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl" +} + +# prepare tmp dir +$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile +$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir" +$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null +trap { + if ($TMP_DOWNLOAD_DIR.Exists) { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } + } +} + +New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null + +# Download and Install Apache Maven +Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +Write-Verbose "Downloading from: $distributionUrl" +Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +$webclient = New-Object System.Net.WebClient +if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) { + $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD) +} +[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 +$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null + +# If specified, validate the SHA-256 sum of the Maven distribution zip file +$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum +if ($distributionSha256Sum) { + if ($USE_MVND) { + Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." + } + Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash + if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) { + Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property." + } +} + +# unzip and move +Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null +Rename-Item -Path "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" -NewName $MAVEN_HOME_NAME | Out-Null +try { + Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null +} catch { + if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) { + Write-Error "fail to move MAVEN_HOME" + } +} finally { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } +} + +Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..7ad80fc --- /dev/null +++ b/pom.xml @@ -0,0 +1,131 @@ +<?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>2.7.1</version> + <relativePath/> + </parent> + + <groupId>com.nanyan.securitylink</groupId> + <artifactId>sl-gateway</artifactId> + <version>0.0.1-SNAPSHOT</version> + <name>sl-gateway</name> + <description>Security Link Gateway Project</description> + + <properties> + <java.version>1.8</java.version> + <spring-cloud.version>2021.0.5</spring-cloud.version> + </properties> + + <dependencies> + <!-- Spring Cloud Gateway --> + <dependency> + <groupId>org.springframework.cloud</groupId> + <artifactId>spring-cloud-starter-gateway</artifactId> + </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-actuator-autoconfigure</artifactId> + </dependency> + <dependency> + <groupId>com.alibaba</groupId> + <artifactId>fastjson</artifactId> + <version>1.2.83</version> + </dependency> + + <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-config --> + <dependency> + <groupId>org.springframework.cloud</groupId> + <artifactId>spring-cloud-starter-config</artifactId> + </dependency> + + <!-- Spring Cloud LoadBalancer --> + <dependency> + <groupId>org.springframework.cloud</groupId> + <artifactId>spring-cloud-starter-loadbalancer</artifactId> + </dependency> + + <!-- Spring Boot Actuator --> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-actuator</artifactId> + </dependency> + + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-collections4</artifactId> + <version>4.4</version> + </dependency> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-api</artifactId> + <version>2.17.0</version> + </dependency> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-core</artifactId> + <version>2.17.0</version> + </dependency> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-slf4j-impl</artifactId> + <version>2.17.0</version> + </dependency> + + <!-- Lombok --> + <dependency> + <groupId>org.projectlombok</groupId> + <artifactId>lombok</artifactId> + <optional>true</optional> + </dependency> + + <!-- Test Dependencies --> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-test</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + + <dependencyManagement> + <dependencies> + <dependency> + <groupId>org.springframework.cloud</groupId> + <artifactId>spring-cloud-dependencies</artifactId> + <version>${spring-cloud.version}</version> + <type>pom</type> + <scope>import</scope> + </dependency> + </dependencies> + </dependencyManagement> + + <build> + <!-- <pluginManagement>--> + <plugins> + <plugin> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-maven-plugin</artifactId> + <version>2.7.1</version> + </plugin> + <plugin> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-maven-plugin</artifactId> + <configuration> + <mainClass>com.nanyan.securitylink.GatewayApplication</mainClass> + </configuration> + <executions> + <execution> + <goals> + <goal>repackage</goal> + </goals> + </execution> + </executions> + </plugin> + </plugins> + <!-- </pluginManagement>--> + + </build> +</project> \ No newline at end of file diff --git a/service.yaml b/service.yaml new file mode 100644 index 0000000..a839ebc --- /dev/null +++ b/service.yaml @@ -0,0 +1,35 @@ +--- +apiVersion: v1 +kind: Service +metadata: + namespace: securitylink + name: sl-gateway +spec: + ports: + - port: 8080 + targetPort: 8080 + protocol: TCP + type: NodePort + selector: + app: sl-gateway +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + namespace: securitylink + name: ingress-sl-gateway + annotations: + alb.ingress.kubernetes.io/scheme: internet-facing + alb.ingress.kubernetes.io/target-type: ip +spec: + ingressClassName: alb + rules: + - http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: sl-gateway + port: + number: 8080 \ No newline at end of file diff --git a/src/main/java/com/nanyan/securitylink/GatewayApplication.java b/src/main/java/com/nanyan/securitylink/GatewayApplication.java new file mode 100644 index 0000000..b60414c --- /dev/null +++ b/src/main/java/com/nanyan/securitylink/GatewayApplication.java @@ -0,0 +1,11 @@ +package com.nanyan.securitylink; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class GatewayApplication { + public static void main(String[] args) { + SpringApplication.run(GatewayApplication.class, args); + } +} \ No newline at end of file diff --git a/src/main/java/com/nanyan/securitylink/common/Constant.java b/src/main/java/com/nanyan/securitylink/common/Constant.java new file mode 100644 index 0000000..db84f13 --- /dev/null +++ b/src/main/java/com/nanyan/securitylink/common/Constant.java @@ -0,0 +1,39 @@ +package com.nanyan.securitylink.common; + +public interface Constant { +// String USER_ID = "userId"; + int picType = 1; + int textType = 0; + String ACCOUNT = "account"; + int SUCCESS_STATUS = 200; + String WEATHER_GROUP = "weatherType"; + String ADMIN_ROLE = "admin"; + String MSG_LABEL_NAME = "MesTag"; + int INVALID = 1; + int EFFECTIVE = 0; + + int NOT_SENT = 0; + int SENT = 1; + + String COUNTRY_CODE = "country"; + String CRONTAB_CODE = "crontab"; + String CITY_CODE = "city"; + String UTC_ZONE_ID = "UTC"; + String LAT = "lat"; + String LON = "lon"; + String LANGUAGE = "language"; + String CN = "䏿–‡"; + String CN_CODE = "zh_cn"; + String EN = "英文"; + String EN_CODE = "en"; + String indexEmergencyCode="indexEmergency"; + String indexCitySafeCode="indexCitySafe"; + String indexFrendlinessCode="indexFrendliness"; + String indexManmadeHazardsCode="indexManmadeHazards"; + String indexNaturalHazardsCode="indexNaturalHazards"; + String indexPublicHealthCode="indexPublicHealth"; + Integer DISABLED = 1; + Integer DELETE = 3; + Integer NON_VIP = 1; + Integer VIP = 0; +} diff --git a/src/main/java/com/nanyan/securitylink/common/MsgCode.java b/src/main/java/com/nanyan/securitylink/common/MsgCode.java new file mode 100644 index 0000000..8fcf958 --- /dev/null +++ b/src/main/java/com/nanyan/securitylink/common/MsgCode.java @@ -0,0 +1,38 @@ +package com.nanyan.securitylink.common; + +public enum MsgCode { + SUCCESS(200, "success"), + FAILED(1001, "access failed"), + NOT_LOGIN(401, "not login"), + LOGIN_FAILED(402, "login failed"), + LOGIN_EXPIRE(403, "token expire") + //翻译异常 + , + TRANSLATE_ERROR(500, "translate error"), + //JSONå—符串异常 + JSON_ERROR(501, "json error") + ; + int code; + String msg; + + MsgCode(int code, String msg) { + this.code = code; + this.msg = msg; + } + + 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; + } +} diff --git a/src/main/java/com/nanyan/securitylink/config/FailoverProperties.java b/src/main/java/com/nanyan/securitylink/config/FailoverProperties.java new file mode 100644 index 0000000..66b27c4 --- /dev/null +++ b/src/main/java/com/nanyan/securitylink/config/FailoverProperties.java @@ -0,0 +1,29 @@ +package com.nanyan.securitylink.config; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; +import lombok.Data; +import java.util.*; + +@Data +@Configuration +@ConfigurationProperties(prefix = "gateway.failover") +public class FailoverProperties { + private List<TokenUriMapping> tokenMappings; + + @Data + public static class TokenUriMapping { + private List<String> tokens; + private List<UriConfig> uriConfigs; + } + + @Data + public static class UriConfig { + private String sourceUri; + private String targetUri; + private String primaryHost; + private String primaryUrl; + private String fallbackHost; + private String fallbackUrl; + } +} \ No newline at end of file diff --git a/src/main/java/com/nanyan/securitylink/config/GatewayConfig.java b/src/main/java/com/nanyan/securitylink/config/GatewayConfig.java new file mode 100644 index 0000000..a2a2694 --- /dev/null +++ b/src/main/java/com/nanyan/securitylink/config/GatewayConfig.java @@ -0,0 +1,26 @@ +package com.nanyan.securitylink.config; + +import org.springframework.cloud.gateway.route.RouteLocator; +import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import lombok.RequiredArgsConstructor; +import com.nanyan.securitylink.filter.FailoverGatewayFilterFactory; + +@Configuration +@RequiredArgsConstructor +public class GatewayConfig { + + private final FailoverGatewayFilterFactory failoverFilter; + + @Bean + public RouteLocator customRouteLocator(RouteLocatorBuilder builder) { + return builder.routes() + .route("token_based_route", r -> r + .path("/**") + .filters(f -> f + .filter(failoverFilter.apply(new FailoverGatewayFilterFactory.Config()))) + .uri("no://op")) + .build(); + } +} \ No newline at end of file diff --git a/src/main/java/com/nanyan/securitylink/config/RestTemplateConfig.java b/src/main/java/com/nanyan/securitylink/config/RestTemplateConfig.java new file mode 100644 index 0000000..f5582b5 --- /dev/null +++ b/src/main/java/com/nanyan/securitylink/config/RestTemplateConfig.java @@ -0,0 +1,13 @@ +package com.nanyan.securitylink.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; + +@Configuration +public class RestTemplateConfig { + @Bean + public RestTemplate restTemplate() { + return new RestTemplate(); + } +} diff --git a/src/main/java/com/nanyan/securitylink/config/SchedulerConfig.java b/src/main/java/com/nanyan/securitylink/config/SchedulerConfig.java new file mode 100644 index 0000000..c394038 --- /dev/null +++ b/src/main/java/com/nanyan/securitylink/config/SchedulerConfig.java @@ -0,0 +1,17 @@ +package com.nanyan.securitylink.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; + +@Configuration +public class SchedulerConfig { + @Bean + public ThreadPoolTaskScheduler taskScheduler() { + ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); + scheduler.setPoolSize(10); // è®¾ç½®çº¿ç¨‹æ± å¤§å° + scheduler.setThreadNamePrefix("task-scheduler-"); // 设置线程åå‰ç¼€ + scheduler.initialize(); + return scheduler; + } +} diff --git a/src/main/java/com/nanyan/securitylink/controller/HealthController.java b/src/main/java/com/nanyan/securitylink/controller/HealthController.java new file mode 100644 index 0000000..60e69d9 --- /dev/null +++ b/src/main/java/com/nanyan/securitylink/controller/HealthController.java @@ -0,0 +1,13 @@ +package com.nanyan.securitylink.controller; + +import com.nanyan.securitylink.vo.Response; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class HealthController { + @GetMapping(value = "/health") + public Response<Boolean> checkHealth(){ + return Response.SUCCESS(true); + } +} diff --git a/src/main/java/com/nanyan/securitylink/dto/TranslateDTO.java b/src/main/java/com/nanyan/securitylink/dto/TranslateDTO.java new file mode 100644 index 0000000..23cef11 --- /dev/null +++ b/src/main/java/com/nanyan/securitylink/dto/TranslateDTO.java @@ -0,0 +1,10 @@ +package com.nanyan.securitylink.dto; + +import lombok.Data; + +@Data +public class TranslateDTO { + TranslateInputDTO inputs; + String response_mode; + String user; +} diff --git a/src/main/java/com/nanyan/securitylink/dto/TranslateInputDTO.java b/src/main/java/com/nanyan/securitylink/dto/TranslateInputDTO.java new file mode 100644 index 0000000..26e7576 --- /dev/null +++ b/src/main/java/com/nanyan/securitylink/dto/TranslateInputDTO.java @@ -0,0 +1,9 @@ +package com.nanyan.securitylink.dto; + +import lombok.Data; + +@Data +public class TranslateInputDTO { + String record_json; + String language; +} diff --git a/src/main/java/com/nanyan/securitylink/entity/ChatCompletionResponse.java b/src/main/java/com/nanyan/securitylink/entity/ChatCompletionResponse.java new file mode 100644 index 0000000..4498140 --- /dev/null +++ b/src/main/java/com/nanyan/securitylink/entity/ChatCompletionResponse.java @@ -0,0 +1,16 @@ +package com.nanyan.securitylink.entity; + +import lombok.Data; + +import java.util.List; + +@Data +public class ChatCompletionResponse { + private String id; + private String object; + private long created; + private String model; + private List<Choice> choices; + private Usage usage; + private String systemFingerprint; +} diff --git a/src/main/java/com/nanyan/securitylink/entity/ChatInputData.java b/src/main/java/com/nanyan/securitylink/entity/ChatInputData.java new file mode 100644 index 0000000..8888990 --- /dev/null +++ b/src/main/java/com/nanyan/securitylink/entity/ChatInputData.java @@ -0,0 +1,12 @@ +package com.nanyan.securitylink.entity; + +import lombok.Data; + +import java.util.List; + +@Data +public class ChatInputData { + private String model; + private Boolean stream = false; + private List<Message> messages; +} diff --git a/src/main/java/com/nanyan/securitylink/entity/Choice.java b/src/main/java/com/nanyan/securitylink/entity/Choice.java new file mode 100644 index 0000000..f244bdc --- /dev/null +++ b/src/main/java/com/nanyan/securitylink/entity/Choice.java @@ -0,0 +1,11 @@ +package com.nanyan.securitylink.entity; + +import lombok.Data; + +@Data +public class Choice { + private int index; + private Message message; + private Object logprobs; // logprobs is null in the JSON + private String finishReason; +} diff --git a/src/main/java/com/nanyan/securitylink/entity/Message.java b/src/main/java/com/nanyan/securitylink/entity/Message.java new file mode 100644 index 0000000..52ac45b --- /dev/null +++ b/src/main/java/com/nanyan/securitylink/entity/Message.java @@ -0,0 +1,10 @@ +package com.nanyan.securitylink.entity; + +import lombok.Data; + +@Data +public class Message { + private String role; + private String content; + +} diff --git a/src/main/java/com/nanyan/securitylink/entity/PromptTokensDetails.java b/src/main/java/com/nanyan/securitylink/entity/PromptTokensDetails.java new file mode 100644 index 0000000..b9ac908 --- /dev/null +++ b/src/main/java/com/nanyan/securitylink/entity/PromptTokensDetails.java @@ -0,0 +1,8 @@ +package com.nanyan.securitylink.entity; + +import lombok.Data; + +@Data +public class PromptTokensDetails { + private int cachedTokens; +} diff --git a/src/main/java/com/nanyan/securitylink/entity/Usage.java b/src/main/java/com/nanyan/securitylink/entity/Usage.java new file mode 100644 index 0000000..bac02f3 --- /dev/null +++ b/src/main/java/com/nanyan/securitylink/entity/Usage.java @@ -0,0 +1,13 @@ +package com.nanyan.securitylink.entity; + +import lombok.Data; + +@Data +public class Usage { + private int promptTokens; + private int completionTokens; + private int totalTokens; + private PromptTokensDetails promptTokensDetails; + private int promptCacheHitTokens; + private int promptCacheMissTokens; +} diff --git a/src/main/java/com/nanyan/securitylink/entity/UserHeader.java b/src/main/java/com/nanyan/securitylink/entity/UserHeader.java new file mode 100644 index 0000000..aebdc94 --- /dev/null +++ b/src/main/java/com/nanyan/securitylink/entity/UserHeader.java @@ -0,0 +1,40 @@ +package com.nanyan.securitylink.entity; + +public class UserHeader { + String userId; + String accountName; + String role; + String token; + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public String getAccountName() { + return accountName; + } + + public void setAccountName(String accountName) { + this.accountName = accountName; + } + + public String getRole() { + return role; + } + + public void setRole(String role) { + this.role = role; + } +} diff --git a/src/main/java/com/nanyan/securitylink/entity/UserThreadLocal.java b/src/main/java/com/nanyan/securitylink/entity/UserThreadLocal.java new file mode 100644 index 0000000..165af52 --- /dev/null +++ b/src/main/java/com/nanyan/securitylink/entity/UserThreadLocal.java @@ -0,0 +1,14 @@ +package com.nanyan.securitylink.entity; + +public class UserThreadLocal { + private static final ThreadLocal<UserHeader> userHeaderThreadLocal = new ThreadLocal<>(); + public static void set(UserHeader userHeader){ + userHeaderThreadLocal.set(userHeader); + } + public static UserHeader get(){ + return userHeaderThreadLocal.get(); + } + public static void remove(){ + userHeaderThreadLocal.remove(); + } +} diff --git a/src/main/java/com/nanyan/securitylink/execption/BaseException.java b/src/main/java/com/nanyan/securitylink/execption/BaseException.java new file mode 100644 index 0000000..4910eaa --- /dev/null +++ b/src/main/java/com/nanyan/securitylink/execption/BaseException.java @@ -0,0 +1,20 @@ +package com.nanyan.securitylink.execption; + +import com.nanyan.securitylink.common.MsgCode; + +public class BaseException extends RuntimeException { + private int code; + + public BaseException(MsgCode msgCode) { + super(msgCode.getMsg()); + this.code = msgCode.getCode(); + } + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } +} diff --git a/src/main/java/com/nanyan/securitylink/execption/HeaderException.java b/src/main/java/com/nanyan/securitylink/execption/HeaderException.java new file mode 100644 index 0000000..2dbb83d --- /dev/null +++ b/src/main/java/com/nanyan/securitylink/execption/HeaderException.java @@ -0,0 +1,9 @@ +package com.nanyan.securitylink.execption; + +import com.nanyan.securitylink.common.MsgCode; + +public class HeaderException extends BaseException { + public HeaderException(MsgCode msgCode) { + super(msgCode); + } +} diff --git a/src/main/java/com/nanyan/securitylink/filter/FailoverGatewayFilterFactory.java b/src/main/java/com/nanyan/securitylink/filter/FailoverGatewayFilterFactory.java new file mode 100644 index 0000000..85347ed --- /dev/null +++ b/src/main/java/com/nanyan/securitylink/filter/FailoverGatewayFilterFactory.java @@ -0,0 +1,208 @@ +package com.nanyan.securitylink.filter; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.nanyan.securitylink.service.TokenRouteMappingService; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import org.springframework.cloud.gateway.filter.GatewayFilter; +import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory; +import org.springframework.core.io.buffer.DataBuffer; +import org.springframework.core.io.buffer.DataBufferUtils; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.BodyInserters; +import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; +import java.nio.charset.StandardCharsets; + +@Slf4j +@Component +public class FailoverGatewayFilterFactory extends AbstractGatewayFilterFactory<FailoverGatewayFilterFactory.Config> { + + private static final ThreadLocal<String> REQUEST_BODY_CACHE = new ThreadLocal<>(); + private final WebClient.Builder webClientBuilder; + private final TokenRouteMappingService tokenRouteMappingService; + + public FailoverGatewayFilterFactory(WebClient.Builder webClientBuilder, + TokenRouteMappingService tokenRouteMappingService) { + super(Config.class); + this.webClientBuilder = webClientBuilder; + this.tokenRouteMappingService = tokenRouteMappingService; + } + + @Override + public GatewayFilter apply(Config config) { + return (exchange, chain) -> { + try { + String authHeader = exchange.getRequest().getHeaders().getFirst(HttpHeaders.AUTHORIZATION); + String token = authHeader != null && authHeader.startsWith("Bearer ") + ? authHeader.substring(7) + : null; + String sourceUri = exchange.getRequest().getPath().value(); + + if (token != null) { + return tokenRouteMappingService.findMatchingUriConfig(token, sourceUri) + .map(uriConfig -> { + return cacheRequestBody(exchange).flatMap(cachedBody -> { + log.info("Found matching URI config for token: {}, sourceUri: {}", token, sourceUri); + return tryRequest( + uriConfig.getPrimaryUrl(), + sourceUri, + uriConfig.getPrimaryHost(), + exchange, + true + ).onErrorResume(primaryError -> { + log.error("Primary endpoint failed: {}, error: {}", + uriConfig.getPrimaryUrl() + sourceUri, + primaryError.getMessage()); + + String targetUri = tokenRouteMappingService.resolveTargetUri(sourceUri, uriConfig); + return tryRequest( + uriConfig.getFallbackUrl(), + targetUri, + uriConfig.getFallbackHost(), + exchange, + false + ); + }); + }); + }) + .orElse(chain.filter(exchange)); + } + + return chain.filter(exchange); + } finally { + // ç¡®ä¿åœ¨è¯·æ±‚处ç†å®ŒæˆåŽæ¸…ç† ThreadLocal + REQUEST_BODY_CACHE.remove(); + } + }; + } + + private Mono<String> cacheRequestBody(ServerWebExchange exchange) { + + ServerHttpRequest request = exchange.getRequest(); + if (!requiresRequestBody(request.getMethod())) { + return Mono.just(""); + } + + // 读å–并缓å˜è¯·æ±‚体 + return DataBufferUtils.join(request.getBody()) + .map(dataBuffer -> { + try { + byte[] bytes = new byte[dataBuffer.readableByteCount()]; + dataBuffer.read(bytes); + String body = new String(bytes, StandardCharsets.UTF_8); + // 将请求体å˜å‚¨åœ¨ ThreadLocal ä¸ + REQUEST_BODY_CACHE.set(body); + return body; + } finally { + DataBufferUtils.release(dataBuffer); + } + }) + .defaultIfEmpty(""); + } + + private Mono<Void> tryRequest(String baseUrl, String uri, String host, + ServerWebExchange exchange, boolean isSourceRequest) { + String cachedBody = REQUEST_BODY_CACHE.get(); + ServerHttpRequest request = exchange.getRequest(); + String fullUrl = baseUrl + (baseUrl.endsWith("/") ? uri.substring(1) : uri); + + log.info("Trying request to: {}", fullUrl); + log.info("Using cached request body: {}", cachedBody); + + WebClient.RequestBodySpec requestBodySpec = webClientBuilder.build() + .method(request.getMethod()) + .uri(fullUrl + (request.getQueryParams().isEmpty() ? "" : + "?" + request.getQueryParams().toString().substring(1))); + + // å¤åˆ¶è¯·æ±‚头 + requestBodySpec.headers(headers -> { + headers.addAll(request.getHeaders()); + headers.set(HttpHeaders.HOST, host); + }); + + // 设置请求体 + if (requiresRequestBody(request.getMethod()) && cachedBody != null && !cachedBody.isEmpty()) { + MediaType contentType = request.getHeaders().getContentType(); + if (contentType != null) { + requestBodySpec.contentType(contentType); + } + requestBodySpec.body(BodyInserters.fromValue(cachedBody)); + } + + return requestBodySpec + .exchange() + .flatMap(clientResponse -> handleResponse(exchange, clientResponse, isSourceRequest)); + } + + private Mono<Void> handleResponse(ServerWebExchange exchange, + org.springframework.web.reactive.function.client.ClientResponse clientResponse, + boolean isSourceRequest) { + HttpStatus responseStatus = clientResponse.statusCode(); + + if (isSourceRequest) { + return clientResponse.bodyToMono(String.class) + .flatMap(body -> { + log.info("Source request response: status={}, body={}", responseStatus, body); + + if (responseStatus != HttpStatus.OK) { + return Mono.error(new RuntimeException( + String.format("Source request HTTP status not 200: %s, body: %s", + responseStatus.value(), body))); + } + + try { + JSONObject jsonBody = JSON.parseObject(body); + Integer bodyStatus = jsonBody.getInteger("status"); + if (bodyStatus == null || bodyStatus != 200) { + return Mono.error(new RuntimeException( + String.format("Source request body status not 200: %s, body: %s", + bodyStatus, body))); + } + + exchange.getResponse().setStatusCode(responseStatus); + exchange.getResponse().getHeaders().putAll(clientResponse.headers().asHttpHeaders()); + DataBuffer buffer = exchange.getResponse().bufferFactory() + .wrap(body.getBytes(StandardCharsets.UTF_8)); + return exchange.getResponse().writeWith(Mono.just(buffer)); + + } catch (Exception e) { + log.error("Error parsing response body: {}", e.getMessage()); + return Mono.error(new RuntimeException( + String.format("Failed to parse response body as JSON: %s", body))); + } + }); + } + + exchange.getResponse().setStatusCode(responseStatus); + exchange.getResponse().getHeaders().putAll(clientResponse.headers().asHttpHeaders()); + + return clientResponse.bodyToMono(DataBuffer.class) + .flatMap(buffer -> exchange.getResponse().writeWith(Mono.just(buffer))) + .onErrorResume(throwable -> { + log.info("Error handling response: {}", throwable.getMessage()); + exchange.getResponse().setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR); + String errorMessage = throwable.getMessage(); + DataBuffer buffer = exchange.getResponse().bufferFactory() + .wrap(errorMessage.getBytes(StandardCharsets.UTF_8)); + return exchange.getResponse().writeWith(Mono.just(buffer)); + }); + } + + private boolean requiresRequestBody(HttpMethod method) { + return method == HttpMethod.POST || + method == HttpMethod.PUT || + method == HttpMethod.PATCH; + } + + @Data + public static class Config { + } +} \ No newline at end of file diff --git a/src/main/java/com/nanyan/securitylink/schedule/RefreshConfigSync.java b/src/main/java/com/nanyan/securitylink/schedule/RefreshConfigSync.java new file mode 100644 index 0000000..1a742d6 --- /dev/null +++ b/src/main/java/com/nanyan/securitylink/schedule/RefreshConfigSync.java @@ -0,0 +1,39 @@ +package com.nanyan.securitylink.schedule; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestTemplate; + +@Slf4j +@Component +public class RefreshConfigSync { + @Autowired + RestTemplate restTemplate; + public void autoRefreshConfig(){ + try{ + String url = "http://127.0.0.1:8080/actuator/refresh"; + HttpEntity header = getHeader(); + ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, header, String.class); + if(response.getStatusCode().is2xxSuccessful()){ + log.info("é…置刷新æˆåŠŸ:{}", response.getBody()); + } + }catch (Exception e){ + log.error("", e); + } + + } + + private HttpEntity getHeader(){ + HttpHeaders httpHeaders = new HttpHeaders(); + httpHeaders.set("Content-Type","application/json"); + return new HttpEntity<>(httpHeaders); + } + + + +} diff --git a/src/main/java/com/nanyan/securitylink/schedule/ScheduledTasksService.java b/src/main/java/com/nanyan/securitylink/schedule/ScheduledTasksService.java new file mode 100644 index 0000000..6dd7598 --- /dev/null +++ b/src/main/java/com/nanyan/securitylink/schedule/ScheduledTasksService.java @@ -0,0 +1,30 @@ +package com.nanyan.securitylink.schedule; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.TaskScheduler; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.stereotype.Service; + +import javax.annotation.PostConstruct; + +@Service +@EnableScheduling +public class ScheduledTasksService { + private final TaskScheduler taskScheduler; + + @Autowired + RefreshConfigSync refreshConfigSync; + + public ScheduledTasksService(TaskScheduler taskScheduler) { + this.taskScheduler = taskScheduler; + } + + @PostConstruct + public void scheduleTask() { + + //定时刷新é…ç½® + taskScheduler.scheduleWithFixedDelay(() -> { + refreshConfigSync.autoRefreshConfig(); + }, 60000); + } +} diff --git a/src/main/java/com/nanyan/securitylink/service/TokenRouteMappingService.java b/src/main/java/com/nanyan/securitylink/service/TokenRouteMappingService.java new file mode 100644 index 0000000..bea9b5b --- /dev/null +++ b/src/main/java/com/nanyan/securitylink/service/TokenRouteMappingService.java @@ -0,0 +1,34 @@ +package com.nanyan.securitylink.service; + +import com.nanyan.securitylink.config.FailoverProperties; +import com.nanyan.securitylink.config.FailoverProperties.UriConfig; +import org.springframework.stereotype.Service; +import lombok.RequiredArgsConstructor; +import org.springframework.util.AntPathMatcher; +import java.util.Optional; + +@Service +@RequiredArgsConstructor +public class TokenRouteMappingService { + + private final FailoverProperties failoverProperties; + private final AntPathMatcher pathMatcher = new AntPathMatcher(); + + public Optional<UriConfig> findMatchingUriConfig(String token, String sourceUri) { + Optional<UriConfig> first = failoverProperties.getTokenMappings().stream() + .filter(mapping -> mapping.getTokens().contains(token)) + .flatMap(mapping -> mapping.getUriConfigs().stream()) + .filter(uriConfig -> pathMatcher.match(uriConfig.getSourceUri(), sourceUri)) + .findFirst(); + return first; + } + + public String resolveTargetUri(String sourceUri, UriConfig uriConfig) { + if (uriConfig.getSourceUri().contains("*")) { + String remaining = pathMatcher.extractPathWithinPattern( + uriConfig.getSourceUri(), sourceUri); + return uriConfig.getTargetUri().replace("{remaining}", remaining); + } + return uriConfig.getTargetUri(); + } +} \ No newline at end of file diff --git a/src/main/java/com/nanyan/securitylink/utils/DateUtils.java b/src/main/java/com/nanyan/securitylink/utils/DateUtils.java new file mode 100644 index 0000000..342c2ec --- /dev/null +++ b/src/main/java/com/nanyan/securitylink/utils/DateUtils.java @@ -0,0 +1,283 @@ +package com.nanyan.securitylink.utils; + + +import lombok.extern.slf4j.Slf4j; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.*; +import java.time.temporal.ChronoUnit; +import java.time.temporal.TemporalAdjusters; +import java.util.*; + +@Slf4j +public class DateUtils { + public static long dayTimeStamp(Date date) throws ParseException { + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); + sdf.setTimeZone(TimeZone.getTimeZone("UTC")); + String formattedDate = sdf.format(date); + return sdf.parse(formattedDate).getTime(); + } + public static boolean isValidDate(Date date) { + if (date == null) { + return false; // Date 为 nullï¼Œéžæ³• + } + + // 检查 Date 是å¦åœ¨åˆç†èŒƒå›´å†… + // 例如,检查是å¦å¤§äºŽæŸä¸ªæœ€å°æ—¥æœŸï¼ˆå¦‚ 1970 å¹´ 1 月 1 日) + Date minDate = new Date(0); // 1970-01-01 00:00:00 UTC + if (date.before(minDate)) { + return false; // Date 早于 1970 å¹´ 1 月 1 æ—¥ï¼Œéžæ³• + } + + // 检查是å¦å°äºŽæŸä¸ªæœ€å¤§æ—¥æœŸï¼ˆå¦‚ 2100 å¹´ 1 月 1 日) + Date maxDate = new Date(4102444800000L); // 2100-01-01 00:00:00 UTC + if (date.after(maxDate)) { + return false; // Date 晚于 2100 å¹´ 1 月 1 æ—¥ï¼Œéžæ³• + } + + return true; // Date åˆæ³• + } + + public static long LocalDateTimeToMillis(LocalDateTime localDateTime, String zoneId) { + // ç»“åˆæ—¶åŒºï¼Œè¿™é‡Œä½¿ç”¨ç³»ç»Ÿé»˜è®¤æ—¶åŒº + ZonedDateTime zonedDateTime = localDateTime.atZone(ZoneId.of(zoneId)); + // è½¬æ¢æˆInstant + Instant instant = zonedDateTime.toInstant(); + // èŽ·å–æ¯«ç§’值 + return instant.toEpochMilli(); + } + + public static Long currentUTCMilli(){ + // 获å–当å‰UTCæ—¶é—´çš„Instant对象 + Instant now = Instant.now(); + + // 获å–秒级时间戳 + long epochMilli = now.toEpochMilli(); + return epochMilli; + } + + public static long currentMonthlyStart(Date start) { + LocalDateTime now = start.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime(); + LocalDateTime firstDayOfMonth = now.with(TemporalAdjusters.firstDayOfMonth()); + // å°†å¼€å§‹å’Œç»“æŸæ—¶é—´è½¬æ¢ä¸ºæ¯«ç§’级时间戳 + return firstDayOfMonth.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); + } + public static long currentMonthlyEnd(Date start){ + LocalDateTime now = start.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime(); + LocalDateTime firstDayOfNextMonth = now.with(TemporalAdjusters.firstDayOfNextMonth()); + // å°†å¼€å§‹å’Œç»“æŸæ—¶é—´è½¬æ¢ä¸ºæ¯«ç§’级时间戳 + return firstDayOfNextMonth.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); + } + + public static long yearTimeStamp(Date date) { + SimpleDateFormat sdf = new SimpleDateFormat("yyyy"); + String year = sdf.format(date); + return toYMD(String.format("%s-01-01",year)).toInstant().toEpochMilli(); + } + + public static Long monthTimeStamp(Date date) { + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM"); + String yyyyMM = sdf.format(date); + return toYMD(String.format("%s-01",yyyyMM)).toInstant().toEpochMilli(); + } + public static long quarterTimeStamp(Date date) { + int month = getMonth(date); + String monthStr = null; + int year = getYear(date); + if (month <= 3) { + monthStr = "01"; + } else if (month <= 6) { + monthStr = "03"; + } else if (month <= 9) { + monthStr = "06"; + } else { + monthStr = "09"; + } + String format = String.format("%d-%s-01", year, monthStr); + return toYMD(format).getTime(); + } + + public static String dateYYYYMMDD(Date date){ + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); + String formattedDate = sdf.format(date); + return formattedDate; + } + + public static String getHourStr(Long dt ){ + Date date = new Date(dt * 1000); + SimpleDateFormat sdf = new SimpleDateFormat("HH:mm"); + return sdf.format(date); + } + + public static Integer getHour(Date dt ){ + Calendar calendar = Calendar.getInstance(); + calendar.setTime(dt); + + // 获å–å°æ—¶æ•° + return calendar.get(Calendar.HOUR_OF_DAY); + } + + public static Date toYMD(String dateStr) { + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); + try{ + sdf.setTimeZone(TimeZone.getTimeZone("UTC")); + return sdf.parse(dateStr); + }catch (Exception e){ + e.printStackTrace(); + } + return null; + } + + public static Date toYMD(Date date) { + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); + try{ + sdf.setTimeZone(TimeZone.getTimeZone("UTC")); + String format = sdf.format(date); + return sdf.parse(format); + }catch (Exception e){ + e.printStackTrace(); + } + return null; + } + + public static Date lastDate(Date date, Integer day){ + Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC")); + calendar.setTime(date); + calendar.add(Calendar.DAY_OF_MONTH, day); + return calendar.getTime(); + } + + public static int getMonth(Date date){ + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + int month = calendar.get(Calendar.MONTH) + 1; // 由于月份从0开始,所以需è¦+1 + return month; + } + public static int getYear(Date date){ + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + int year = calendar.get(Calendar.YEAR); + return year; + } + public static String getDaily(Date date){ + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + int month = calendar.get(Calendar.MONTH) + 1; // 由于月份从0开始,所以需è¦+1 + int day = calendar.get(Calendar.DAY_OF_MONTH) + 1; // 由于月份从0开始,所以需è¦+1 + return String.format("%d-%d", month, day); + } + + public static String calculateTimeDifference(Date date) { + if(date == null){ + return ""; + } + Date currentDate = new Date(); + long differenceInMillis = currentDate.getTime() - date.getTime(); + long seconds = differenceInMillis / 1000; + + if (seconds < 60) { + return seconds + "秒之å‰"; + } else if (seconds < 3600) { + long minutes = seconds / 60; + return minutes + "分钟之å‰"; + } else if(seconds < 3600 * 24){ + long hours = seconds / 3600; + return hours + "å°æ—¶ä¹‹å‰"; + }else { + long hours = seconds / (3600*24); + return hours + "天之å‰"; + } + } + /** + * 将毫秒时间长度转æ¢ä¸º N 天 N å°æ—¶ N 分 N ç§’ æ ¼å¼ + * @param millis 毫秒数 + * @return æ ¼å¼åŒ–åŽçš„æ—¶é—´å—符串 + */ + public static String formatMillis(long millis) { + // 计算总秒数 + long seconds = millis / 1000; + // 计算天数 + long days = seconds / (24 * 60 * 60); + seconds %= (24 * 60 * 60); + // è®¡ç®—å°æ—¶æ•° + long hours = seconds / (60 * 60); + seconds %= (60 * 60); + // 计算分钟数 + long minutes = seconds / 60; + // 计算剩余秒数 + seconds %= 60; + + StringBuilder result = new StringBuilder(); + if (days > 0) { + result.append(days).append("天 "); + } + if (hours > 0) { + result.append(hours).append("å°æ—¶ "); + } + if (minutes > 0) { + result.append(minutes).append("分 "); + } + if (seconds > 0) { + result.append(seconds).append("ç§’"); + } + // 如果结果为空,说明时间为 0,返回 "0 ç§’" + if (result.length() == 0) { + result.append("0 ç§’"); + } + + return result.toString(); + } + + public static String getWeekFormat(Date date) { + SimpleDateFormat sdf = new SimpleDateFormat("EEEE", Locale.SIMPLIFIED_CHINESE); + String format = sdf.format(date); + if("星期一".equals(format)){ + return "周一"; + }else if("星期二".equals(format)){ + return "周二"; + }else if("星期三".equals(format)){ + return "周三"; + }else if("星期四".equals(format)){ + return "周四"; + }else if("星期五".equals(format)){ + return "周五"; + }else if("星期å…".equals(format)){ + return "周å…"; + }else if("星期日".equals(format)){ + return "周日"; + } + return format; + } + + + public static String getYYDDFormat(Date date) { + SimpleDateFormat sdf = new SimpleDateFormat("M月dæ—¥"); + return sdf.format(date); + } + public static long getYesterday0H(){ + // 获å–当剿—¶é—´ + LocalDateTime now = LocalDateTime.now(); + // 计算昨天的时间 + LocalDateTime yesterday = now.minusDays(1); + // 设置为昨天 0 点 + LocalDateTime yesterdayNoon = yesterday.withHour(0).withMinute(0).withSecond(0).withNano(0); + return yesterdayNoon.toInstant(ZoneOffset.UTC).getEpochSecond(); + } + + public static long getYesterday12H(){ + // 获å–当剿—¶é—´ + LocalDateTime now = LocalDateTime.now(); + // 计算昨天的时间 + LocalDateTime yesterday = now.minusDays(1); + // 设置为昨天 12 点 + LocalDateTime yesterdayNoon = yesterday.withHour(12).withMinute(0).withSecond(0).withNano(0); + return yesterdayNoon.toInstant(ZoneOffset.UTC).getEpochSecond(); + } + + public static int dateDifference(Date start, Date end) { + LocalDate s = start.toInstant().atZone(ZoneId.of("UTC")).toLocalDate(); + LocalDate e = end.toInstant().atZone(ZoneId.of("UTC")).toLocalDate(); + return (int)ChronoUnit.DAYS.between(s, e); + } +} diff --git a/src/main/java/com/nanyan/securitylink/vo/AIResponse.java b/src/main/java/com/nanyan/securitylink/vo/AIResponse.java new file mode 100644 index 0000000..f388e6e --- /dev/null +++ b/src/main/java/com/nanyan/securitylink/vo/AIResponse.java @@ -0,0 +1,15 @@ +package com.nanyan.securitylink.vo; + +import lombok.Data; + +@Data +public class AIResponse { + private String event; + private String task_id; + private String id; + private String message_id; + private String mode; + private String answer; + private ResultVO outputs; + private long created_at; +} diff --git a/src/main/java/com/nanyan/securitylink/vo/CodeVO.java b/src/main/java/com/nanyan/securitylink/vo/CodeVO.java new file mode 100644 index 0000000..370ba89 --- /dev/null +++ b/src/main/java/com/nanyan/securitylink/vo/CodeVO.java @@ -0,0 +1,9 @@ +package com.nanyan.securitylink.vo; + +import lombok.Data; + +@Data +public class CodeVO { + String code; + String name; +} diff --git a/src/main/java/com/nanyan/securitylink/vo/Response.java b/src/main/java/com/nanyan/securitylink/vo/Response.java new file mode 100644 index 0000000..eeb7a60 --- /dev/null +++ b/src/main/java/com/nanyan/securitylink/vo/Response.java @@ -0,0 +1,61 @@ +package com.nanyan.securitylink.vo; + +import com.nanyan.securitylink.common.MsgCode; + +public class Response<T> { + String msg; + int code; + T data; + + public static Response getResponse(MsgCode msgCode){ + Response response = new Response(); + response.setCode(msgCode.getCode()); + response.setMsg(msgCode.getMsg()); + return response; + } + + public static Response getResponse(int code, String msg){ + Response response = new Response(); + response.setCode(code); + response.setMsg(msg); + return response; + } + + public static<T> Response<T> SUCCESS(T data){ + Response response = new Response<T>(); + response.setCode(MsgCode.SUCCESS.getCode()); + response.setMsg(MsgCode.SUCCESS.getMsg()); + response.setData(data); + return response; + } + public static<T> Response<T> FAILED(T data){ + Response response = new Response(); + response.setCode(MsgCode.FAILED.getCode()); + response.setMsg(MsgCode.FAILED.getMsg()); + response.setData(data); + return response; + } + public String getMsg() { + return msg; + } + + public void setMsg(String msg) { + this.msg = msg; + } + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public T getData() { + return data; + } + + public void setData(T data) { + this.data = data; + } +} diff --git a/src/main/java/com/nanyan/securitylink/vo/ResultVO.java b/src/main/java/com/nanyan/securitylink/vo/ResultVO.java new file mode 100644 index 0000000..179cd82 --- /dev/null +++ b/src/main/java/com/nanyan/securitylink/vo/ResultVO.java @@ -0,0 +1,10 @@ +package com.nanyan.securitylink.vo; + +import lombok.Data; + +import java.util.List; + +@Data +public class ResultVO { + List<CodeVO> result; +} diff --git a/src/main/resources/application-prd.properties b/src/main/resources/application-prd.properties new file mode 100644 index 0000000..123755f --- /dev/null +++ b/src/main/resources/application-prd.properties @@ -0,0 +1,6 @@ +spring.cloud.config.name=sl-gateway +spring.cloud.config.profile=prd +spring.cloud.config.label=master +spring.cloud.config.enabled=true +#spring.config.import=optional:configserver:http://sl-config-center:8080 +#spring.config.import=optional:configserver:http://43.199.200.152:32594 \ No newline at end of file diff --git a/src/main/resources/application-qa.properties b/src/main/resources/application-qa.properties new file mode 100644 index 0000000..dddfbca --- /dev/null +++ b/src/main/resources/application-qa.properties @@ -0,0 +1,26 @@ +spring.cloud.config.name=sl-gateway +spring.cloud.config.profile=qa +spring.cloud.config.label=master +spring.cloud.config.enabled=true +#spring.config.import=optional:configserver:http://sl-config-center:8080 +spring.config.import=optional:configserver:http://43.199.200.152:32594 +#crime.mapping.event={"Arson":"\u7EB5\u706B\u7F6A","Assault":"\u88AD\u51FB/\u653B\u51FB","Burglary":"\u5165\u5BA4\u76D7\u7A83","Disturbing the Peace":"\u6270\u4E71\u516C\u5171\u79E9\u5E8F","Drugs / Alcohol Violations":"\u6BD2\u54C1/\u9152\u7CBE\u8FDD\u89C4","DUI":"\u9189\u9A7E","Fraud":"\u8BC8\u9A97","Homicide":"\u6740\u4EBA\u7F6A","Motor Vehicle Theft":"\u673A\u52A8\u8F66\u76D7\u7A83","Robbery":"\u62A2\u52AB","Sex Crimes":"\u6027\u72AF\u7F6A","Theft / Larceny":"\u76D7\u7A83","Vandalism":"\u6076\u610F\u7834\u574F\u8D22\u7269","Vehicle Break-In / Theft":"\u8F66\u8F86\u95EF\u5165/\u76D7\u7A83","Weapons":"\u6D89\u62A2\u6D89\u68B0\u72AF\u7F6A"} +# Gateway Configuration +#spring.cloud.gateway.httpclient.connect-timeout=5000 +#spring.cloud.gateway.httpclient.response-timeout=5000 +# +## Gateway Failover Configuration +#gateway.failover.token-mappings[0].tokens[0]=app-KNq0O8kENP4ITqSmqHQ0IzAt1 +#gateway.failover.token-mappings[0].uri-configs[0].source-uri=/v1/workflows/run +#gateway.failover.token-mappings[0].uri-configs[0].target-uri=/api/v1/tag +#gateway.failover.token-mappings[0].uri-configs[0].primary-host=18.163.46.22 +#gateway.failover.token-mappings[0].uri-configs[0].primary-url=http://18.163.46.22 +#gateway.failover.token-mappings[0].uri-configs[0].fallback-host=127.0.0.1 +#gateway.failover.token-mappings[0].uri-configs[0].fallback-url=http://127.0.0.1:8081 + +#gateway.failover.token-mappings[0].uri-configs[1].source-uri=/v1/workflows/status/* +#gateway.failover.token-mappings[0].uri-configs[1].target-uri=/api/v1/status/{remaining} +#gateway.failover.token-mappings[0].uri-configs[1].primary-host=18.163.46.22 +#gateway.failover.token-mappings[0].uri-configs[1].primary-url=http://18.163.46.22 +#gateway.failover.token-mappings[0].uri-configs[1].fallback-host=k8s-security-ingresss-2004545575-1912502751.ap-east-1.elb.amazonaws.com +#gateway.failover.token-mappings[0].uri-configs[1].fallback-url=http://k8s-security-ingresss-2004545575-1912502751.ap-east-1.elb.amazonaws.com \ No newline at end of file diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties new file mode 100644 index 0000000..f0fea53 --- /dev/null +++ b/src/main/resources/application.properties @@ -0,0 +1,7 @@ +spring.application.name=sl-gateway +spring.profiles.active=qa +#server.servlet.context-path=/api/v1 +pagehelper.reasonable=true +pagehelper.support-methods-arguments=true +management.endpoints.web.exposure.include=refresh +#logging.config=classpath:log4j2.xml diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml new file mode 100644 index 0000000..b4a14ef --- /dev/null +++ b/src/main/resources/log4j2.xml @@ -0,0 +1,87 @@ +<Configuration status="WARN" monitorInterval="1800"> + + <!--å˜é‡é…ç½®--> + <Properties> + <!--应用åç§°--> + <property name="APP_NAME">sl-gateway</property> + <!--æ—¥å¿—å˜æ”¾è·¯å¾„--> + <property name="LOG_PATH">./logs/${APP_NAME}</property> + <!--日志备份路径--> + <property name="LOG_BACKUP_PATH">${LOG_PATH}/backup</property> + <!--æ—¥å¿—è¾“å‡ºæ ¼å¼-控制å°--> + <property name="PATTERN_CONSOLE">%d{yyyy-MM-dd HH:mm:ss.SSS} | %blue{%traceId} | %highlight{%-5p} | %magenta{${sys:PID}} | %yellow{%t} | %cyan{%l} : %msg%n</property> + <!--æ—¥å¿—è¾“å‡ºæ ¼å¼-文件--> + <property name="PATTERN_FILE">%d{yyyy-MM-dd HH:mm:ss.SSS} | %traceId | %-5p | ${sys:PID} | %t | %l : %msg%n</property> + </Properties> + + <!--å®šä¹‰æ—¥å¿—è¾“å‡ºç›®çš„åœ°ï¼Œå†…å®¹å’Œæ ¼å¼ç‰--> + <Appenders> + <Console name="Console" target="SYSTEM_OUT"> + <PatternLayout pattern="${PATTERN_CONSOLE}"/> + </Console> + + <!--å¯å½’档文件 + 1. fileName: 日志å˜å‚¨è·¯å¾„ + 2. filePattern: åŽ†å²æ—¥å¿—å°å˜è·¯å¾„。其ä¸%d{yyyy-MM-dd}表示了日志的时间å•使˜¯å¤©,log4j2自动识别zipç‰åŽç¼€ï¼Œè¡¨ç¤ºåކ岿—¥å¿—需è¦åŽ‹ç¼© + --> + <RollingFile name="RollingFile" fileName="${LOG_PATH}/${APP_NAME}.log" filePattern="${LOG_BACKUP_PATH}/$${date:yyyy-MM}/${APP_NAME}-%d{yyyy-MM-dd}_%i.log.zip"> + <!--è¾“å‡ºæ—¥å¿—çš„æ ¼å¼, ä¸è®¾ç½®é»˜è®¤ä¸º:%m%n--> + <PatternLayout pattern="${PATTERN_FILE}"/> + <!--åªè¾“出levelåŠä»¥ä¸Šçº§åˆ«çš„ä¿¡æ¯ï¼ˆonMatch),其他的直接拒ç»ï¼ˆonMismatch)--> + <ThresholdFilter level="DEBUG" onMatch="ACCEPT" onMismatch="DENY"/> + + <!--归档设置--> + <Policies> + <!--按时间间隔归档: + 1. interval=æ—¶é—´é—´éš”, å•ä½ç”±filePatternçš„%dæ—¥æœŸæ ¼å¼æŒ‡å®š, æ¤å¤„é…置代表æ¯ä¸€å¤©å½’档一次 + 2. modulate="true" 是å¦å¯¹intervalå–æ¨¡ï¼Œå†³å®šäº†ä¸‹ä¸€æ¬¡è§¦å‘的时间点 + --> + <TimeBasedTriggeringPolicy interval="1" modulate="true" /> + <!-- 按照日志文件的大å°: sizeè¡¨ç¤ºå½“å‰æ—¥å¿—文件的最大size,支æŒå•ä½ï¼šKB/MB/GB--> + <SizeBasedTriggeringPolicy size="50MB"/> + </Policies> + <!-- åŽ†å²æ—¥å¿—é…ç½®: 该属性如ä¸è®¾ç½®ï¼Œåˆ™é»˜è®¤ä¸ºæœ€å¤šåŒä¸€æ–‡ä»¶å¤¹ä¸‹7个文件开始覆盖--> + <DefaultRolloverStrategy max="30"/> + </RollingFile> + + <!--错误信æ¯å•独归档--> + <RollingFile name="RollingFileError" fileName="${LOG_PATH}/${APP_NAME}-error.log" filePattern="${LOG_BACKUP_PATH}/$${date:yyyy-MM}/${APP_NAME}-error-%d{yyyy-MM-dd}_%i.log.zip"> + <PatternLayout pattern="${PATTERN_FILE}"/> + <ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY"/> + <Policies> + <TimeBasedTriggeringPolicy/> + <SizeBasedTriggeringPolicy size="50MB"/> + </Policies> + </RollingFile> + </Appenders> + + + <!--Loggersé…ç½®--> + <Loggers> + + <!-- + 注æ„点: + 1. logger节点用æ¥å•独指定日志的形å¼ï¼Œæ¯”如è¦ä¸ºæŒ‡å®šåŒ…下的class指定ä¸åŒçš„æ—¥å¿—级别ç‰: + (1). name: ç”¨æ¥æŒ‡å®šè¯¥logger所适用的类或者类所在的包全路径,继承自Root节点. + (2). AppenderRef:关è”çš„Appender, åªæœ‰å®šä¹‰äº†logger并引入的appender,appenderæ‰ä¼šç”Ÿæ•ˆ + (3). additivity: logEventçš„ä¼ é€’æ€§ã€‚true LogEvent处ç†åŽä¼ 递给父Logger打å°ã€‚false LogEvent处ç†åŽä¸å†å‘ä¸Šä¼ é€’ç»™çˆ¶Logger(解决日志é‡å¤è¾“出问题) + (4). loggeré…置的level必须高于或ç‰äºŽAppendersä¸ThresholdFilteré…置的过滤level, å¦åˆ™ä¼šé€ æˆä¿¡æ¯ä¸¢å¤± + 2. rooté…ç½®æ—¥å¿—çš„æ ¹èŠ‚ç‚¹ + --> + + <!-- åŒæ¥æ—¥å¿—é…ç½®--> + <logger name="com.sky.hello.mapper" level="debug" additivity="false"> + <AppenderRef ref="Console"/> + <AppenderRef ref="RollingFile"/> + <AppenderRef ref="RollingFileError"/> + </logger> + + <root level="info"> + <AppenderRef ref="Console"/> + <AppenderRef ref="RollingFile"/> + <AppenderRef ref="RollingFileError"/> + </root> + + </Loggers> + +</Configuration> \ No newline at end of file -- 2.22.0