完成最基础的智能体切换
This commit is contained in:
parent
af3b777d07
commit
e5df564b8c
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/mvnw text eol=lf
|
||||
*.cmd text eol=crlf
|
||||
51
.gitignore
vendored
51
.gitignore
vendored
@ -1,26 +1,33 @@
|
||||
# ---> Java
|
||||
# Compiled class file
|
||||
*.class
|
||||
HELP.md
|
||||
target/
|
||||
.mvn/wrapper/maven-wrapper.jar
|
||||
!**/src/main/**/target/
|
||||
!**/src/test/**/target/
|
||||
|
||||
# Log file
|
||||
*.log
|
||||
### STS ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
|
||||
# BlueJ files
|
||||
*.ctxt
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
# Mobile Tools for Java (J2ME)
|
||||
.mtj.tmp/
|
||||
|
||||
# Package Files #
|
||||
*.jar
|
||||
*.war
|
||||
*.nar
|
||||
*.ear
|
||||
*.zip
|
||||
*.tar.gz
|
||||
*.rar
|
||||
|
||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||
hs_err_pid*
|
||||
replay_pid*
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
build/
|
||||
!**/src/main/**/build/
|
||||
!**/src/test/**/build/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
|
||||
3
.mvn/wrapper/maven-wrapper.properties
vendored
Normal file
3
.mvn/wrapper/maven-wrapper.properties
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
wrapperVersion=3.3.4
|
||||
distributionType=only-script
|
||||
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.11/apache-maven-3.9.11-bin.zip
|
||||
295
mvnw
vendored
Normal file
295
mvnw
vendored
Normal file
@ -0,0 +1,295 @@
|
||||
#!/bin/sh
|
||||
# ----------------------------------------------------------------------------
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Apache Maven Wrapper startup batch script, version 3.3.4
|
||||
#
|
||||
# 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:]'
|
||||
}
|
||||
|
||||
scriptDir="$(dirname "$0")"
|
||||
scriptName="$(basename "$0")"
|
||||
|
||||
# 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 <"$scriptDir/.mvn/wrapper/maven-wrapper.properties"
|
||||
[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in $scriptDir/.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${scriptName#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
|
||||
|
||||
# Find the actual extracted directory name (handles snapshots where filename != directory name)
|
||||
actualDistributionDir=""
|
||||
|
||||
# First try the expected directory name (for regular distributions)
|
||||
if [ -d "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" ]; then
|
||||
if [ -f "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/bin/$MVN_CMD" ]; then
|
||||
actualDistributionDir="$distributionUrlNameMain"
|
||||
fi
|
||||
fi
|
||||
|
||||
# If not found, search for any directory with the Maven executable (for snapshots)
|
||||
if [ -z "$actualDistributionDir" ]; then
|
||||
# enable globbing to iterate over items
|
||||
set +f
|
||||
for dir in "$TMP_DOWNLOAD_DIR"/*; do
|
||||
if [ -d "$dir" ]; then
|
||||
if [ -f "$dir/bin/$MVN_CMD" ]; then
|
||||
actualDistributionDir="$(basename "$dir")"
|
||||
break
|
||||
fi
|
||||
fi
|
||||
done
|
||||
set -f
|
||||
fi
|
||||
|
||||
if [ -z "$actualDistributionDir" ]; then
|
||||
verbose "Contents of $TMP_DOWNLOAD_DIR:"
|
||||
verbose "$(ls -la "$TMP_DOWNLOAD_DIR")"
|
||||
die "Could not find Maven distribution directory in extracted archive"
|
||||
fi
|
||||
|
||||
verbose "Found extracted Maven distribution directory: $actualDistributionDir"
|
||||
printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$actualDistributionDir/mvnw.url"
|
||||
mv -- "$TMP_DOWNLOAD_DIR/$actualDistributionDir" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME"
|
||||
|
||||
clean || :
|
||||
exec_maven "$@"
|
||||
189
mvnw.cmd
vendored
Normal file
189
mvnw.cmd
vendored
Normal file
@ -0,0 +1,189 @@
|
||||
<# : batch portion
|
||||
@REM ----------------------------------------------------------------------------
|
||||
@REM Licensed to the Apache Software Foundation (ASF) under one
|
||||
@REM or more contributor license agreements. See the NOTICE file
|
||||
@REM distributed with this work for additional information
|
||||
@REM regarding copyright ownership. The ASF licenses this file
|
||||
@REM to you under the Apache License, Version 2.0 (the
|
||||
@REM "License"); you may not use this file except in compliance
|
||||
@REM with the License. You may obtain a copy of the License at
|
||||
@REM
|
||||
@REM http://www.apache.org/licenses/LICENSE-2.0
|
||||
@REM
|
||||
@REM Unless required by applicable law or agreed to in writing,
|
||||
@REM software distributed under the License is distributed on an
|
||||
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
@REM KIND, either express or implied. See the License for the
|
||||
@REM specific language governing permissions and limitations
|
||||
@REM under the License.
|
||||
@REM ----------------------------------------------------------------------------
|
||||
|
||||
@REM ----------------------------------------------------------------------------
|
||||
@REM Apache Maven Wrapper startup batch script, version 3.3.4
|
||||
@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 -eq $False) { "/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_M2_PATH = "$HOME/.m2"
|
||||
if ($env:MAVEN_USER_HOME) {
|
||||
$MAVEN_M2_PATH = "$env:MAVEN_USER_HOME"
|
||||
}
|
||||
|
||||
if (-not (Test-Path -Path $MAVEN_M2_PATH)) {
|
||||
New-Item -Path $MAVEN_M2_PATH -ItemType Directory | Out-Null
|
||||
}
|
||||
|
||||
$MAVEN_WRAPPER_DISTS = $null
|
||||
if ((Get-Item $MAVEN_M2_PATH).Target[0] -eq $null) {
|
||||
$MAVEN_WRAPPER_DISTS = "$MAVEN_M2_PATH/wrapper/dists"
|
||||
} else {
|
||||
$MAVEN_WRAPPER_DISTS = (Get-Item $MAVEN_M2_PATH).Target[0] + "/wrapper/dists"
|
||||
}
|
||||
|
||||
$MAVEN_HOME_PARENT = "$MAVEN_WRAPPER_DISTS/$distributionUrlNameMain"
|
||||
$MAVEN_HOME_NAME = ([System.Security.Cryptography.SHA256]::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
|
||||
|
||||
# Find the actual extracted directory name (handles snapshots where filename != directory name)
|
||||
$actualDistributionDir = ""
|
||||
|
||||
# First try the expected directory name (for regular distributions)
|
||||
$expectedPath = Join-Path "$TMP_DOWNLOAD_DIR" "$distributionUrlNameMain"
|
||||
$expectedMvnPath = Join-Path "$expectedPath" "bin/$MVN_CMD"
|
||||
if ((Test-Path -Path $expectedPath -PathType Container) -and (Test-Path -Path $expectedMvnPath -PathType Leaf)) {
|
||||
$actualDistributionDir = $distributionUrlNameMain
|
||||
}
|
||||
|
||||
# If not found, search for any directory with the Maven executable (for snapshots)
|
||||
if (!$actualDistributionDir) {
|
||||
Get-ChildItem -Path "$TMP_DOWNLOAD_DIR" -Directory | ForEach-Object {
|
||||
$testPath = Join-Path $_.FullName "bin/$MVN_CMD"
|
||||
if (Test-Path -Path $testPath -PathType Leaf) {
|
||||
$actualDistributionDir = $_.Name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$actualDistributionDir) {
|
||||
Write-Error "Could not find Maven distribution directory in extracted archive"
|
||||
}
|
||||
|
||||
Write-Verbose "Found extracted Maven distribution directory: $actualDistributionDir"
|
||||
Rename-Item -Path "$TMP_DOWNLOAD_DIR/$actualDistributionDir" -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"
|
||||
153
pom.xml
Normal file
153
pom.xml
Normal file
@ -0,0 +1,153 @@
|
||||
<?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.5.7</version>
|
||||
<relativePath/> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<groupId>top.krcia</groupId>
|
||||
<artifactId>cosmos</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<name>cosmos</name>
|
||||
<description>cosmos</description>
|
||||
<url/>
|
||||
<licenses>
|
||||
<license/>
|
||||
</licenses>
|
||||
<developers>
|
||||
<developer/>
|
||||
</developers>
|
||||
<scm>
|
||||
<connection/>
|
||||
<developerConnection/>
|
||||
<tag/>
|
||||
<url/>
|
||||
</scm>
|
||||
<properties>
|
||||
<java.version>17</java.version>
|
||||
<mybatis-plus.version>3.5.5</mybatis-plus.version>
|
||||
<fastjson.version>2.0.60</fastjson.version>
|
||||
<math3.version>3.6.1</math3.version>
|
||||
<http3.version>4.12.0</http3.version>
|
||||
<smart-doc.version>3.0.9</smart-doc.version>
|
||||
<httpclient.version>4.5.6</httpclient.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-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-extension</artifactId>
|
||||
<version>${mybatis-plus.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
|
||||
<version>${mybatis-plus.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-core</artifactId>
|
||||
<version>${mybatis-plus.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-jdbc</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.mysql</groupId>
|
||||
<artifactId>mysql-connector-j</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.fastjson2</groupId>
|
||||
<artifactId>fastjson2</artifactId>
|
||||
<version>${fastjson.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-math3</artifactId>
|
||||
<version>${math3.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp -->
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
<version>${http3.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.microsoft.onnxruntime</groupId>
|
||||
<artifactId>onnxruntime</artifactId>
|
||||
<version>1.20.0</version> <!-- ✅ 最新版本 -->
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
<version>${httpclient.version}</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>com.ly.smart-doc</groupId>
|
||||
<artifactId>smart-doc-maven-plugin</artifactId>
|
||||
<version>${smart-doc.version}</version>
|
||||
<configuration>
|
||||
<configFile>${basedir}/src/main/resources/smart-doc.json</configFile>
|
||||
<projectName>多智体聊天</projectName>
|
||||
<includes>
|
||||
<!-- 使用了mybatis-plus的Page分页需要include所使用的源码包 -->
|
||||
<include>com.baomidou:mybatis-plus-extension</include>
|
||||
<!-- 使用了mybatis-plus的IPage分页需要include mybatis-plus-core-->
|
||||
<include>com.baomidou:mybatis-plus-core</include>
|
||||
</includes>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<!--如果不需要在执行编译时启动smart-doc,则将phase注释掉-->
|
||||
<phase>compile</phase>
|
||||
<goals>
|
||||
<!--smart-doc提供了html、openapi、markdown等goal,可按需配置-->
|
||||
<goal>html</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>central</id>
|
||||
<url>https://repo1.maven.org/maven2/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
</project>
|
||||
15
src/main/java/top/krcia/cosmos/CosmosApplication.java
Normal file
15
src/main/java/top/krcia/cosmos/CosmosApplication.java
Normal file
@ -0,0 +1,15 @@
|
||||
package top.krcia.cosmos;
|
||||
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
@MapperScan("top.krcia.cosmos.mapper")
|
||||
public class CosmosApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(CosmosApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
7
src/main/java/top/krcia/cosmos/agent/Agent.java
Normal file
7
src/main/java/top/krcia/cosmos/agent/Agent.java
Normal file
@ -0,0 +1,7 @@
|
||||
package top.krcia.cosmos.agent;
|
||||
|
||||
public interface Agent {
|
||||
AgentType getType();
|
||||
boolean canHandle(String message);
|
||||
AgentResponse process(AgentRequest request);
|
||||
}
|
||||
16
src/main/java/top/krcia/cosmos/agent/AgentRequest.java
Normal file
16
src/main/java/top/krcia/cosmos/agent/AgentRequest.java
Normal file
@ -0,0 +1,16 @@
|
||||
package top.krcia.cosmos.agent;
|
||||
|
||||
import lombok.Data;
|
||||
import top.krcia.cosmos.memory.ShortTermMemory;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
public class AgentRequest {
|
||||
private String userId;
|
||||
private String sessionId;
|
||||
private String message;
|
||||
private Map<String, Object> context;
|
||||
private List<ShortTermMemory.ConversationExchange> conversationHistory;
|
||||
}
|
||||
20
src/main/java/top/krcia/cosmos/agent/AgentResponse.java
Normal file
20
src/main/java/top/krcia/cosmos/agent/AgentResponse.java
Normal file
@ -0,0 +1,20 @@
|
||||
package top.krcia.cosmos.agent;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
public class AgentResponse {
|
||||
private String response;
|
||||
private AgentType agentType;
|
||||
private double confidence;
|
||||
private Map<String, Object> metadata;
|
||||
|
||||
public AgentResponse(String response, AgentType agentType, double confidence, Map<String, Object> metadata) {
|
||||
this.response = response;
|
||||
this.agentType = agentType;
|
||||
this.confidence = confidence;
|
||||
this.metadata = metadata;
|
||||
}
|
||||
}
|
||||
35
src/main/java/top/krcia/cosmos/agent/AgentType.java
Normal file
35
src/main/java/top/krcia/cosmos/agent/AgentType.java
Normal file
@ -0,0 +1,35 @@
|
||||
package top.krcia.cosmos.agent;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public enum AgentType {
|
||||
SCHEDULER("调度智能体"),
|
||||
EMOTIONAL("情感支持智能体"),
|
||||
KNOWLEDGE("知识问答智能体"),
|
||||
CREATIVE("创意生成智能体"),
|
||||
LOGICAL("逻辑推理智能体");
|
||||
|
||||
private final String description;
|
||||
|
||||
AgentType(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
public static String getAll() {
|
||||
return Arrays.stream(AgentType.values())
|
||||
.map(type -> type.name() + ":" + type.getDescription())
|
||||
.collect(Collectors.joining(","));
|
||||
}
|
||||
public static AgentType forName(String name) {
|
||||
if (name == null || name.isEmpty()) return null;
|
||||
return Arrays.stream(AgentType.values())
|
||||
.filter(type -> type.name().equalsIgnoreCase(name))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,63 @@
|
||||
package top.krcia.cosmos.agent.module;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
import top.krcia.cosmos.agent.Agent;
|
||||
import top.krcia.cosmos.agent.AgentRequest;
|
||||
import top.krcia.cosmos.agent.AgentResponse;
|
||||
import top.krcia.cosmos.agent.AgentType;
|
||||
import top.krcia.cosmos.client.DeepSeekClient;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class CreativeAgent implements Agent {
|
||||
private final DeepSeekClient deepSeekClient;
|
||||
|
||||
@Override
|
||||
public AgentType getType() {
|
||||
return AgentType.CREATIVE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canHandle(String message) {
|
||||
String lowerMsg = message.toLowerCase();
|
||||
return lowerMsg.contains("写诗") || lowerMsg.contains("故事") || lowerMsg.contains("创意") ||
|
||||
lowerMsg.contains("想象") || lowerMsg.contains("创作") || lowerMsg.contains("诗歌") ||
|
||||
lowerMsg.contains("小说") || lowerMsg.contains("文案") || lowerMsg.contains("头脑风暴");
|
||||
}
|
||||
|
||||
@Override
|
||||
public AgentResponse process(AgentRequest request) {
|
||||
try {
|
||||
String systemPrompt = """
|
||||
你是一个富有创造力的作家和艺术家,具有丰富的想象力和文学素养。
|
||||
|
||||
你的任务是:
|
||||
1. 根据用户要求创作诗歌、故事、文案等创意内容
|
||||
2. 充分发挥想象力,提供新颖独特的创意
|
||||
3. 保持文学性和艺术性,语言优美生动
|
||||
4. 适当时可以运用比喻、拟人等修辞手法
|
||||
5. 根据用户的具体要求调整创作风格
|
||||
6. 鼓励创新思维,提供多样化的创意方案
|
||||
|
||||
请展现你的创造力和文学才华!""";
|
||||
|
||||
String response = deepSeekClient.generateResponse(request.getMessage(), systemPrompt);
|
||||
|
||||
return new AgentResponse(response, getType(), 0.88, Map.of("creative", true));
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("创意生成智能体处理失败", e);
|
||||
return new AgentResponse(
|
||||
"我的创意灵感暂时枯竭了,请稍后再试或尝试更具体的创作要求。",
|
||||
getType(),
|
||||
0.2,
|
||||
Map.of("fallback", true)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,121 @@
|
||||
package top.krcia.cosmos.agent.module;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
import top.krcia.cosmos.agent.Agent;
|
||||
import top.krcia.cosmos.agent.AgentRequest;
|
||||
import top.krcia.cosmos.agent.AgentResponse;
|
||||
import top.krcia.cosmos.agent.AgentType;
|
||||
import top.krcia.cosmos.client.DeepSeekClient;
|
||||
import top.krcia.cosmos.memory.ShortTermMemory;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class EmotionalSupportAgent implements Agent {
|
||||
private final DeepSeekClient deepSeekClient;
|
||||
|
||||
@Override
|
||||
public AgentType getType() {
|
||||
return AgentType.EMOTIONAL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canHandle(String message) {
|
||||
String lowerMsg = message.toLowerCase();
|
||||
return lowerMsg.contains("难过") || lowerMsg.contains("开心") || lowerMsg.contains("生气") ||
|
||||
lowerMsg.contains("失望") || lowerMsg.contains("感觉") || lowerMsg.contains("心情") ||
|
||||
lowerMsg.contains("情绪") || lowerMsg.contains("安慰") || lowerMsg.contains("倾诉");
|
||||
}
|
||||
|
||||
@Override
|
||||
public AgentResponse process(AgentRequest request) {
|
||||
try {
|
||||
String systemPrompt = buildSystemPrompt(request);
|
||||
String userMessage = request.getMessage();
|
||||
|
||||
String response = deepSeekClient.generateResponse(userMessage, systemPrompt);
|
||||
|
||||
double confidence = calculateConfidence(request.getMessage());
|
||||
|
||||
return new AgentResponse(response, getType(), confidence,
|
||||
Map.of("emotionalTone", analyzeEmotionalTone(request.getMessage())));
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("情感支持智能体处理失败", e);
|
||||
return getFallbackResponse();
|
||||
}
|
||||
}
|
||||
private String buildSystemPrompt(AgentRequest request) {
|
||||
StringBuilder prompt = new StringBuilder();
|
||||
prompt.append("你是一个专业的情感支持助手,具有温暖、同理心和专业素养。\n");
|
||||
prompt.append("你的任务是:\n");
|
||||
prompt.append("1. 倾听用户的情绪表达,给予真诚的理解和共情\n");
|
||||
prompt.append("2. 提供情感支持和安慰,帮助用户缓解负面情绪\n");
|
||||
prompt.append("3. 引导用户积极看待问题,但不要强行说教\n");
|
||||
prompt.append("4. 保持温暖、支持性的语气,避免冷漠或机械的回复\n");
|
||||
prompt.append("5. 适当时可以分享一些缓解情绪的小建议\n\n");
|
||||
|
||||
if (request.getContext() != null) {
|
||||
List<ShortTermMemory.ConversationExchange> recentConversations =
|
||||
(List<ShortTermMemory.ConversationExchange>) request.getContext().get("recentConversations");
|
||||
|
||||
if (recentConversations != null && !recentConversations.isEmpty()) {
|
||||
prompt.append("最近的对话历史:\n");
|
||||
for (ShortTermMemory.ConversationExchange exchange : recentConversations) {
|
||||
prompt.append("用户: ").append(exchange.getUserMessage()).append("\n");
|
||||
prompt.append("助手: ").append(exchange.getAiResponse()).append("\n");
|
||||
}
|
||||
prompt.append("\n");
|
||||
}
|
||||
|
||||
// 添加当前智能体上下文
|
||||
AgentType currentAgent = (AgentType) request.getContext().get("currentAgent");
|
||||
if (currentAgent != null) {
|
||||
prompt.append("注意:用户之前在与").append(currentAgent.getDescription()).append("交流。\n");
|
||||
}
|
||||
}
|
||||
|
||||
prompt.append("请根据用户的当前情绪状态,提供恰当的情感支持回复。");
|
||||
|
||||
return prompt.toString();
|
||||
}
|
||||
|
||||
private double calculateConfidence(String message) {
|
||||
String lowerMsg = message.toLowerCase();
|
||||
if (lowerMsg.contains("难过") || lowerMsg.contains("伤心") || lowerMsg.contains("哭泣") ||
|
||||
lowerMsg.contains("生气") || lowerMsg.contains("愤怒") || lowerMsg.contains("失望")) {
|
||||
return 0.95;
|
||||
} else if (lowerMsg.contains("开心") || lowerMsg.contains("高兴") || lowerMsg.contains("快乐")) {
|
||||
return 0.90;
|
||||
} else if (lowerMsg.contains("感觉") || lowerMsg.contains("心情") || lowerMsg.contains("情绪")) {
|
||||
return 0.85;
|
||||
}
|
||||
return 0.70;
|
||||
}
|
||||
|
||||
private double analyzeEmotionalTone(String text) {
|
||||
String lowerText = text.toLowerCase();
|
||||
if (lowerText.contains("开心") || lowerText.contains("高兴") || lowerText.contains("喜欢")) {
|
||||
return 0.8;
|
||||
} else if (lowerText.contains("难过") || lowerText.contains("伤心") || lowerText.contains("失望")) {
|
||||
return -0.8;
|
||||
} else if (lowerText.contains("生气") || lowerText.contains("愤怒") || lowerText.contains("讨厌")) {
|
||||
return -0.9;
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
private AgentResponse getFallbackResponse() {
|
||||
return new AgentResponse(
|
||||
"我理解你现在可能需要情感支持,但暂时无法提供更深入的回应。请记住,你的感受是重要的,如果需要可以找信任的人聊聊。",
|
||||
getType(),
|
||||
0.3,
|
||||
Map.of("fallback", true)
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,64 @@
|
||||
package top.krcia.cosmos.agent.module;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
import top.krcia.cosmos.agent.Agent;
|
||||
import top.krcia.cosmos.agent.AgentRequest;
|
||||
import top.krcia.cosmos.agent.AgentResponse;
|
||||
import top.krcia.cosmos.agent.AgentType;
|
||||
import top.krcia.cosmos.client.DeepSeekClient;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class KnowledgeAgent implements Agent {
|
||||
private final DeepSeekClient deepSeekClient;
|
||||
|
||||
@Override
|
||||
public AgentType getType() {
|
||||
return AgentType.KNOWLEDGE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canHandle(String message) {
|
||||
String lowerMsg = message.toLowerCase();
|
||||
return lowerMsg.contains("是什么") || lowerMsg.contains("为什么") || lowerMsg.contains("怎么") ||
|
||||
lowerMsg.contains("何时") || lowerMsg.contains("哪里") || lowerMsg.contains("谁") ||
|
||||
lowerMsg.contains("解释") || lowerMsg.contains("定义") || lowerMsg.contains("介绍");
|
||||
}
|
||||
|
||||
@Override
|
||||
public AgentResponse process(AgentRequest request) {
|
||||
try {
|
||||
String systemPrompt = """
|
||||
你是一个专业的知识问答助手,具有准确、全面、客观的特点。
|
||||
|
||||
你的任务是:
|
||||
1. 准确回答用户提出的知识性问题
|
||||
2. 提供清晰、有条理的解释
|
||||
3. 如果信息不确定,要明确说明
|
||||
4. 避免主观臆断,基于事实回答
|
||||
5. 对于复杂概念,可以用通俗易懂的方式解释
|
||||
6. 适当时可以提供相关背景知识
|
||||
|
||||
请确保回答准确、专业且易于理解。""";
|
||||
|
||||
String response = deepSeekClient.generateResponse(request.getMessage(), systemPrompt);
|
||||
|
||||
return new AgentResponse(response, getType(), 0.85, Map.of("source", "deepseek_knowledge"));
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("知识问答智能体处理失败", e);
|
||||
return new AgentResponse(
|
||||
"我目前无法回答这个知识性问题,请稍后再试或咨询其他专业资源。",
|
||||
getType(),
|
||||
0.2,
|
||||
Map.of("fallback", true)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,63 @@
|
||||
package top.krcia.cosmos.agent.module;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
import top.krcia.cosmos.agent.Agent;
|
||||
import top.krcia.cosmos.agent.AgentRequest;
|
||||
import top.krcia.cosmos.agent.AgentResponse;
|
||||
import top.krcia.cosmos.agent.AgentType;
|
||||
import top.krcia.cosmos.client.DeepSeekClient;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class LogicalAgent implements Agent {
|
||||
private final DeepSeekClient deepSeekClient;
|
||||
|
||||
@Override
|
||||
public AgentType getType() {
|
||||
return AgentType.LOGICAL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canHandle(String message) {
|
||||
String lowerMsg = message.toLowerCase();
|
||||
return lowerMsg.contains("计划") || lowerMsg.contains("步骤") || lowerMsg.contains("解决") ||
|
||||
lowerMsg.contains("分析") || lowerMsg.contains("推理") || lowerMsg.contains("逻辑") ||
|
||||
lowerMsg.contains("方案") || lowerMsg.contains("策略") || lowerMsg.contains("规划");
|
||||
}
|
||||
|
||||
@Override
|
||||
public AgentResponse process(AgentRequest request) {
|
||||
try {
|
||||
String systemPrompt = """
|
||||
你是一个逻辑严谨的分析师和规划师,具有出色的分析和推理能力。
|
||||
|
||||
你的任务是:
|
||||
1. 帮助用户制定详细的计划和步骤
|
||||
2. 提供逻辑清晰的问题分析和解决方案
|
||||
3. 将复杂问题分解为可执行的子任务
|
||||
4. 确保思考过程严谨、有条理
|
||||
5. 提供结构化的建议和方案
|
||||
6. 考虑各种可能性和约束条件
|
||||
|
||||
请用清晰、有条理的方式提供逻辑分析和规划建议。""";
|
||||
|
||||
String response = deepSeekClient.generateResponse(request.getMessage(), systemPrompt);
|
||||
|
||||
return new AgentResponse(response, getType(), 0.90, Map.of("reasoning", "structured"));
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("逻辑推理智能体处理失败", e);
|
||||
return new AgentResponse(
|
||||
"我暂时无法提供详细的逻辑分析,请尝试更具体地描述问题或稍后再试。",
|
||||
getType(),
|
||||
0.2,
|
||||
Map.of("fallback", true)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,201 @@
|
||||
package top.krcia.cosmos.agent.scheduler;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import top.krcia.cosmos.agent.Agent;
|
||||
import top.krcia.cosmos.agent.AgentRequest;
|
||||
import top.krcia.cosmos.agent.AgentResponse;
|
||||
import top.krcia.cosmos.agent.AgentType;
|
||||
import top.krcia.cosmos.entity.UserProfile;
|
||||
import top.krcia.cosmos.entity.VectorMemory;
|
||||
import top.krcia.cosmos.memory.LongTermMemory;
|
||||
import top.krcia.cosmos.memory.ShortTermMemory;
|
||||
import top.krcia.cosmos.request.WebRequest;
|
||||
import top.krcia.cosmos.request.enums.RequestType;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.*;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class SchedulerAgent implements Agent {
|
||||
|
||||
private final List<Agent> specializedAgents;
|
||||
private final LongTermMemory longTermMemory;
|
||||
private final ShortTermMemory shortTermMemory;
|
||||
private static Map<String, String> header = new HashMap<>();
|
||||
private static final String model = "deepseek-chat";
|
||||
@Value("${app.deepseek.api-key}")
|
||||
private String apiKey;
|
||||
|
||||
@PostConstruct
|
||||
private void init() {
|
||||
header.put("Authorization", "Bearer " + apiKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AgentType getType() {
|
||||
return AgentType.SCHEDULER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canHandle(String message) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public AgentResponse process(AgentRequest request) {
|
||||
try {
|
||||
shortTermMemory.createOrUpdateUserSession(request.getUserId(), request.getSessionId());
|
||||
|
||||
Agent selectedAgent = selectBestAgent(request);
|
||||
log.info("用户: {}, 消息: {}, 路由到: {}", request.getUserId(), request.getMessage(), selectedAgent.getType().getDescription());
|
||||
|
||||
Map<String, Object> context = prepareContext(request);
|
||||
request.setContext(context);
|
||||
|
||||
AgentResponse response = selectedAgent.process(request);
|
||||
|
||||
shortTermMemory.storeCurrentAgent(request.getSessionId(), selectedAgent.getType());
|
||||
storeMemories(request, response, selectedAgent);
|
||||
|
||||
return response;
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("调度智能体处理失败", e);
|
||||
return getFallbackResponse();
|
||||
}
|
||||
}
|
||||
|
||||
private Agent selectBestAgent(AgentRequest request) {
|
||||
String message = request.getMessage();
|
||||
String sessionId = request.getSessionId();
|
||||
|
||||
// 1. 调用 API 获取消息最可能的智能体类型
|
||||
AgentType apiDetectedType = detectAgentTypeFromAPI(message);
|
||||
if (apiDetectedType != null) {
|
||||
AgentType lastAgentType = shortTermMemory.getCurrentAgent(sessionId);
|
||||
if (lastAgentType != apiDetectedType) {
|
||||
log.info("API检测到新智能体,从 {} 切换到 {}", lastAgentType, apiDetectedType.getDescription());
|
||||
}
|
||||
return findAgentByType(apiDetectedType)
|
||||
.orElse(specializedAgents.get(0));
|
||||
}
|
||||
|
||||
// 2. 如果 API 无结果,则使用会话记忆
|
||||
AgentType lastAgentType = shortTermMemory.getCurrentAgent(sessionId);
|
||||
if (lastAgentType != null) {
|
||||
Optional<Agent> lastAgent = findAgentByType(lastAgentType);
|
||||
if (lastAgent.isPresent()) {
|
||||
log.debug("继续使用上一个智能体: {}", lastAgentType);
|
||||
return lastAgent.get();
|
||||
}
|
||||
}
|
||||
return findAgentByType(AgentType.KNOWLEDGE)
|
||||
.orElse(specializedAgents.get(0));
|
||||
}
|
||||
|
||||
private AgentType detectAgentTypeFromAPI(String message) {
|
||||
try {
|
||||
JSONObject body = new JSONObject();
|
||||
body.put("model",model);
|
||||
body.put("messages",new ArrayList<>(){
|
||||
{
|
||||
add(
|
||||
new JSONObject(){
|
||||
{
|
||||
put("role","system");
|
||||
put("content","你是一个语句检测专家,我会输入一段话你帮我选择最符合的智能体英文,只输出英文,不要输出额外的任何内容:"+AgentType.getAll());
|
||||
}
|
||||
}
|
||||
);
|
||||
add(
|
||||
new JSONObject(){
|
||||
{
|
||||
put("role","user");
|
||||
put("content",message);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
String result = WebRequest.Create(new URI("https://api.deepseek.com/chat/completions"))
|
||||
.setBody(body)
|
||||
.setHeader(header)
|
||||
.setRequestType(RequestType.POST).execute();
|
||||
String content = JSON.parseObject(result).getJSONArray("choices").getJSONObject(0).getJSONObject("message").getString("content");
|
||||
return AgentType.forName(content);
|
||||
} catch (Exception e) {
|
||||
log.error("调用 API 检测智能体类型失败", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private Optional<Agent> findAgentByType(AgentType agentType) {
|
||||
return specializedAgents.stream()
|
||||
.filter(agent -> agent.getType() == agentType)
|
||||
.findFirst();
|
||||
}
|
||||
|
||||
private void storeMemories(AgentRequest request, AgentResponse response, Agent selectedAgent) {
|
||||
shortTermMemory.storeConversation(
|
||||
request.getSessionId(),
|
||||
request.getMessage(),
|
||||
response.getResponse(),
|
||||
selectedAgent.getType().name()
|
||||
);
|
||||
|
||||
longTermMemory.storeConversationMemory(
|
||||
request.getUserId(),
|
||||
request.getSessionId(),
|
||||
request.getMessage(),
|
||||
response.getResponse(),
|
||||
selectedAgent.getType().name()
|
||||
);
|
||||
}
|
||||
|
||||
private Map<String, Object> prepareContext(AgentRequest request) {
|
||||
Map<String, Object> context = new HashMap<>();
|
||||
|
||||
List<ShortTermMemory.ConversationExchange> recentConversations =
|
||||
shortTermMemory.getRecentConversations(request.getSessionId(), 5);
|
||||
context.put("recentConversations", recentConversations);
|
||||
|
||||
ShortTermMemory.UserSession userSession =
|
||||
shortTermMemory.getUserSession(request.getUserId());
|
||||
context.put("userSession", userSession);
|
||||
|
||||
UserProfile userProfile = longTermMemory.getUserProfile(request.getUserId());
|
||||
context.put("userProfile", userProfile);
|
||||
|
||||
List<VectorMemory> relatedMemories =
|
||||
longTermMemory.findRelatedMemories(request.getUserId(), request.getMessage(), 3);
|
||||
context.put("relatedMemories", relatedMemories);
|
||||
|
||||
AgentType currentAgent = shortTermMemory.getCurrentAgent(request.getSessionId());
|
||||
context.put("currentAgent", currentAgent);
|
||||
|
||||
context.put("conversationCount",
|
||||
shortTermMemory.getConversationSize(request.getSessionId()));
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
private AgentResponse getFallbackResponse() {
|
||||
return new AgentResponse(
|
||||
"抱歉,我现在遇到了一些技术问题,暂时无法处理您的请求。请稍后再试。",
|
||||
AgentType.SCHEDULER,
|
||||
0.1,
|
||||
Map.of("error", true)
|
||||
);
|
||||
}
|
||||
}
|
||||
163
src/main/java/top/krcia/cosmos/client/DeepSeekClient.java
Normal file
163
src/main/java/top/krcia/cosmos/client/DeepSeekClient.java
Normal file
@ -0,0 +1,163 @@
|
||||
package top.krcia.cosmos.client;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import okhttp3.*;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class DeepSeekClient {
|
||||
|
||||
private final OkHttpClient client;
|
||||
private final String apiKey;
|
||||
private final String baseUrl;
|
||||
private final String model;
|
||||
private final int maxTokens;
|
||||
private final double temperature;
|
||||
|
||||
public DeepSeekClient(
|
||||
@Value("${app.deepseek.api-key}") String apiKey,
|
||||
@Value("${app.deepseek.base-url:https://api.deepseek.com/v1}") String baseUrl,
|
||||
@Value("${app.deepseek.model:deepseek-chat}") String model,
|
||||
@Value("${app.deepseek.max-tokens:2000}") int maxTokens,
|
||||
@Value("${app.deepseek.temperature:0.7}") double temperature) {
|
||||
|
||||
this.apiKey = apiKey;
|
||||
this.baseUrl = baseUrl;
|
||||
this.model = model;
|
||||
this.maxTokens = maxTokens;
|
||||
this.temperature = temperature;
|
||||
|
||||
this.client = new OkHttpClient.Builder()
|
||||
.connectTimeout(30, TimeUnit.SECONDS)
|
||||
.readTimeout(60, TimeUnit.SECONDS)
|
||||
.writeTimeout(30, TimeUnit.SECONDS)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class ChatMessage {
|
||||
private String role; // "system", "user", "assistant"
|
||||
private String content;
|
||||
|
||||
public ChatMessage(String role, String content) {
|
||||
this.role = role;
|
||||
this.content = content;
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class ChatRequest {
|
||||
private String model;
|
||||
private List<ChatMessage> messages;
|
||||
private int max_tokens;
|
||||
private double temperature;
|
||||
private boolean stream = false;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class ChatResponse {
|
||||
private String id;
|
||||
private String object;
|
||||
private long created;
|
||||
private String model;
|
||||
private List<Choice> choices;
|
||||
private Usage usage;
|
||||
|
||||
@Data
|
||||
public static class Choice {
|
||||
private int index;
|
||||
private ChatMessage message;
|
||||
private String finish_reason;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class Usage {
|
||||
private int prompt_tokens;
|
||||
private int completion_tokens;
|
||||
private int total_tokens;
|
||||
}
|
||||
}
|
||||
|
||||
public String chatCompletion(List<ChatMessage> messages) throws IOException {
|
||||
ChatRequest request = new ChatRequest();
|
||||
request.setModel(model);
|
||||
request.setMessages(messages);
|
||||
request.setMax_tokens(maxTokens);
|
||||
request.setTemperature(temperature);
|
||||
|
||||
String json = JSON.toJSONString(request);
|
||||
|
||||
RequestBody body = RequestBody.create(
|
||||
json,
|
||||
MediaType.parse("application/json; charset=utf-8")
|
||||
);
|
||||
|
||||
Request httpRequest = new Request.Builder()
|
||||
.url(baseUrl + "/chat/completions")
|
||||
.post(body)
|
||||
.addHeader("Authorization", "Bearer " + apiKey)
|
||||
.addHeader("Content-Type", "application/json")
|
||||
.build();
|
||||
|
||||
try (Response response = client.newCall(httpRequest).execute()) {
|
||||
if (!response.isSuccessful()) {
|
||||
String errorBody = response.body() != null ? response.body().string() : "No error body";
|
||||
log.error("DeepSeek API请求失败: {} - {}", response.code(), errorBody);
|
||||
throw new IOException("DeepSeek API请求失败: " + response.code() + " - " + errorBody);
|
||||
}
|
||||
|
||||
String responseBody = response.body().string();
|
||||
ChatResponse chatResponse = JSON.parseObject(responseBody, ChatResponse.class);
|
||||
|
||||
if (chatResponse.getChoices() != null && !chatResponse.getChoices().isEmpty()) {
|
||||
return chatResponse.getChoices().get(0).getMessage().getContent();
|
||||
} else {
|
||||
throw new IOException("DeepSeek API返回空回复");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 简化调用方法 - 单条消息
|
||||
*/
|
||||
public String generateResponse(String userMessage, String systemPrompt) throws IOException {
|
||||
List<ChatMessage> messages = new ArrayList<>();
|
||||
|
||||
if (systemPrompt != null && !systemPrompt.trim().isEmpty()) {
|
||||
messages.add(new ChatMessage("system", systemPrompt));
|
||||
}
|
||||
|
||||
messages.add(new ChatMessage("user", userMessage));
|
||||
|
||||
return chatCompletion(messages);
|
||||
}
|
||||
|
||||
/**
|
||||
* 带上下文的对话
|
||||
*/
|
||||
public String generateResponseWithContext(String userMessage, String systemPrompt,
|
||||
List<ChatMessage> conversationHistory) throws IOException {
|
||||
List<ChatMessage> messages = new ArrayList<>();
|
||||
|
||||
if (systemPrompt != null && !systemPrompt.trim().isEmpty()) {
|
||||
messages.add(new ChatMessage("system", systemPrompt));
|
||||
}
|
||||
|
||||
if (conversationHistory != null) {
|
||||
messages.addAll(conversationHistory);
|
||||
}
|
||||
|
||||
messages.add(new ChatMessage("user", userMessage));
|
||||
|
||||
return chatCompletion(messages);
|
||||
}
|
||||
}
|
||||
18
src/main/java/top/krcia/cosmos/config/MyBatisPlusConfig.java
Normal file
18
src/main/java/top/krcia/cosmos/config/MyBatisPlusConfig.java
Normal file
@ -0,0 +1,18 @@
|
||||
package top.krcia.cosmos.config;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.DbType;
|
||||
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
public class MyBatisPlusConfig {
|
||||
|
||||
@Bean
|
||||
public MybatisPlusInterceptor mybatisPlusInterceptor() {
|
||||
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
|
||||
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
|
||||
return interceptor;
|
||||
}
|
||||
}
|
||||
89
src/main/java/top/krcia/cosmos/config/RedisConfig.java
Normal file
89
src/main/java/top/krcia/cosmos/config/RedisConfig.java
Normal file
@ -0,0 +1,89 @@
|
||||
package top.krcia.cosmos.config;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.PropertyAccessor;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
|
||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
|
||||
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
|
||||
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
|
||||
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
|
||||
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
|
||||
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
|
||||
import org.springframework.data.redis.serializer.RedisSerializer;
|
||||
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
@Configuration
|
||||
public class RedisConfig {
|
||||
|
||||
// 专用于 Redis 的 ObjectMapper
|
||||
@Bean("redisObjectMapper")
|
||||
public ObjectMapper redisObjectMapper() {
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
objectMapper.registerModule(new JavaTimeModule());
|
||||
// Redis 序列化可以保留时间戳(根据需求调整)
|
||||
objectMapper.enable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
|
||||
return objectMapper;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RedisTemplate<String, Object> redisTemplate(
|
||||
RedisConnectionFactory redisConnectionFactory,
|
||||
@Qualifier("redisObjectMapper") ObjectMapper redisObjectMapper) {
|
||||
RedisTemplate<String, Object> template = new RedisTemplate<>();
|
||||
template.setConnectionFactory(redisConnectionFactory);
|
||||
template.setKeySerializer(new StringRedisSerializer());
|
||||
template.setHashKeySerializer(new StringRedisSerializer());
|
||||
template.setValueSerializer(redisSerializer());
|
||||
template.setHashValueSerializer(redisSerializer());
|
||||
template.afterPropertiesSet();
|
||||
return template;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder) {
|
||||
return builder.build(); // 自动应用 application.yml 的配置
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RedisSerializer<Object> redisSerializer() {
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
|
||||
// 必须设置,否则无法将JSON转化为对象,会转化成Map类型
|
||||
objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
|
||||
|
||||
// 自定义ObjectMapper的时间处理模块
|
||||
JavaTimeModule javaTimeModule = new JavaTimeModule();
|
||||
|
||||
javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
|
||||
javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
|
||||
|
||||
javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
|
||||
javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
|
||||
|
||||
javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern("HH:mm:ss")));
|
||||
javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern("HH:mm:ss")));
|
||||
|
||||
objectMapper.registerModule(javaTimeModule);
|
||||
|
||||
// 禁用将日期序列化为时间戳的行为
|
||||
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
|
||||
|
||||
//创建JSON序列化器
|
||||
return new Jackson2JsonRedisSerializer<>(objectMapper, Object.class);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,51 @@
|
||||
package top.krcia.cosmos.controller;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import top.krcia.cosmos.agent.AgentRequest;
|
||||
import top.krcia.cosmos.agent.AgentResponse;
|
||||
import top.krcia.cosmos.agent.scheduler.SchedulerAgent;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/chat")
|
||||
public class ChatController {
|
||||
private final SchedulerAgent schedulerAgent;
|
||||
|
||||
public ChatController(SchedulerAgent schedulerAgent) {
|
||||
this.schedulerAgent = schedulerAgent;
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public ChatResponse chat(@RequestBody ChatRequest request) {
|
||||
AgentRequest agentRequest = new AgentRequest();
|
||||
agentRequest.setUserId(request.getUserId());
|
||||
agentRequest.setSessionId(request.getSessionId());
|
||||
agentRequest.setMessage(request.getMessage());
|
||||
|
||||
AgentResponse agentResponse = schedulerAgent.process(agentRequest);
|
||||
|
||||
return new ChatResponse(agentResponse.getResponse(),
|
||||
agentResponse.getAgentType().name(),
|
||||
agentResponse.getConfidence());
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class ChatRequest {
|
||||
private String userId;
|
||||
private String sessionId;
|
||||
private String message;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class ChatResponse {
|
||||
private String response;
|
||||
private String agentType;
|
||||
private double confidence;
|
||||
|
||||
public ChatResponse(String response, String agentType, double confidence) {
|
||||
this.response = response;
|
||||
this.agentType = agentType;
|
||||
this.confidence = confidence;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,28 @@
|
||||
package top.krcia.cosmos.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@TableName("conversation_memories")
|
||||
public class ConversationMemory {
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
private String userId;
|
||||
private String sessionId;
|
||||
|
||||
@TableField("user_message")
|
||||
private String userMessage;
|
||||
|
||||
@TableField("ai_response")
|
||||
private String aiResponse;
|
||||
|
||||
private String agentType;
|
||||
private Double emotionalTone;
|
||||
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private LocalDateTime timestamp;
|
||||
}
|
||||
33
src/main/java/top/krcia/cosmos/entity/UserProfile.java
Normal file
33
src/main/java/top/krcia/cosmos/entity/UserProfile.java
Normal file
@ -0,0 +1,33 @@
|
||||
package top.krcia.cosmos.entity;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
@Data
|
||||
@TableName("user_profiles")
|
||||
public class UserProfile {
|
||||
@TableId(type = IdType.INPUT)
|
||||
private String userId;
|
||||
|
||||
private String name;
|
||||
private Integer age;
|
||||
|
||||
@TableField(typeHandler = com.baomidou.mybatisplus.extension.handlers.Fastjson2TypeHandler.class)
|
||||
private JSONObject preferences;
|
||||
|
||||
@TableField(typeHandler = com.baomidou.mybatisplus.extension.handlers.Fastjson2TypeHandler.class)
|
||||
private Set<String> interests = new HashSet<>();
|
||||
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
private LocalDateTime updatedAt;
|
||||
}
|
||||
|
||||
27
src/main/java/top/krcia/cosmos/entity/VectorMemory.java
Normal file
27
src/main/java/top/krcia/cosmos/entity/VectorMemory.java
Normal file
@ -0,0 +1,27 @@
|
||||
package top.krcia.cosmos.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@TableName("vector_memories")
|
||||
public class VectorMemory {
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
private String userId;
|
||||
private String memoryType;
|
||||
|
||||
@TableField(typeHandler = com.baomidou.mybatisplus.extension.handlers.Fastjson2TypeHandler.class)
|
||||
private String content;
|
||||
|
||||
@TableField(typeHandler = com.baomidou.mybatisplus.extension.handlers.Fastjson2TypeHandler.class)
|
||||
private String embedding;
|
||||
|
||||
private Double importanceScore;
|
||||
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private LocalDateTime createdAt;
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
package top.krcia.cosmos.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
import top.krcia.cosmos.entity.ConversationMemory;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface ConversationMemoryMapper extends BaseMapper<ConversationMemory> {
|
||||
|
||||
@Select("SELECT * FROM conversation_memories WHERE user_id = #{userId} ORDER BY timestamp DESC LIMIT #{limit}")
|
||||
List<ConversationMemory> selectRecentByUserId(@Param("userId") String userId, @Param("limit") int limit);
|
||||
|
||||
@Select("SELECT * FROM conversation_memories WHERE user_id = #{userId} AND timestamp > #{since} ORDER BY timestamp DESC")
|
||||
List<ConversationMemory> selectByUserIdSince(@Param("userId") String userId, @Param("since") LocalDateTime since);
|
||||
|
||||
@Select("SELECT agent_type, COUNT(*) as count FROM conversation_memories WHERE user_id = #{userId} GROUP BY agent_type")
|
||||
List<AgentCount> countByAgentType(@Param("userId") String userId);
|
||||
|
||||
class AgentCount {
|
||||
private String agentType;
|
||||
private Long count;
|
||||
// getters and setters
|
||||
}
|
||||
}
|
||||
17
src/main/java/top/krcia/cosmos/mapper/UserProfileMapper.java
Normal file
17
src/main/java/top/krcia/cosmos/mapper/UserProfileMapper.java
Normal file
@ -0,0 +1,17 @@
|
||||
package top.krcia.cosmos.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
import top.krcia.cosmos.entity.ConversationMemory;
|
||||
import top.krcia.cosmos.entity.UserProfile;
|
||||
import top.krcia.cosmos.entity.VectorMemory;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface UserProfileMapper extends BaseMapper<UserProfile> {
|
||||
}
|
||||
|
||||
@ -0,0 +1,19 @@
|
||||
package top.krcia.cosmos.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
import top.krcia.cosmos.entity.VectorMemory;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface VectorMemoryMapper extends BaseMapper<VectorMemory> {
|
||||
|
||||
@Select("SELECT * FROM vector_memories WHERE user_id = #{userId} ORDER BY importance_score DESC")
|
||||
List<VectorMemory> selectByUserIdOrderByScore(@Param("userId") String userId);
|
||||
|
||||
@Select("SELECT * FROM vector_memories WHERE user_id = #{userId} AND memory_type = #{memoryType}")
|
||||
List<VectorMemory> selectByUserIdAndType(@Param("userId") String userId, @Param("memoryType") String memoryType);
|
||||
}
|
||||
191
src/main/java/top/krcia/cosmos/memory/LongTermMemory.java
Normal file
191
src/main/java/top/krcia/cosmos/memory/LongTermMemory.java
Normal file
@ -0,0 +1,191 @@
|
||||
package top.krcia.cosmos.memory;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Component;
|
||||
import top.krcia.cosmos.entity.ConversationMemory;
|
||||
import top.krcia.cosmos.entity.UserProfile;
|
||||
import top.krcia.cosmos.entity.VectorMemory;
|
||||
import top.krcia.cosmos.mapper.ConversationMemoryMapper;
|
||||
import top.krcia.cosmos.mapper.UserProfileMapper;
|
||||
import top.krcia.cosmos.mapper.VectorMemoryMapper;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class LongTermMemory {
|
||||
private final UserProfileMapper userProfileMapper;
|
||||
private final ConversationMemoryMapper conversationMemoryMapper;
|
||||
private final VectorMemoryMapper vectorMemoryMapper;
|
||||
private final ShortTermMemory shortTermMemory;
|
||||
private final VectorStore vectorStore;
|
||||
|
||||
public void storeConversationMemory(String userId, String sessionId, String userMessage,
|
||||
String aiResponse, String agentType) {
|
||||
double emotionalTone = analyzeEmotionalTone(userMessage);
|
||||
|
||||
ConversationMemory memory = new ConversationMemory();
|
||||
memory.setUserId(userId);
|
||||
memory.setSessionId(sessionId);
|
||||
memory.setUserMessage(userMessage);
|
||||
memory.setAiResponse(aiResponse);
|
||||
memory.setAgentType(agentType);
|
||||
memory.setEmotionalTone(emotionalTone);
|
||||
memory.setTimestamp(LocalDateTime.now());
|
||||
|
||||
conversationMemoryMapper.insert(memory);
|
||||
|
||||
if (isImportantMemory(userMessage, emotionalTone)) {
|
||||
CompletableFuture.runAsync(() -> {
|
||||
storeVectorMemory(userId, userMessage, emotionalTone);
|
||||
});
|
||||
}
|
||||
|
||||
shortTermMemory.updateSessionActivity(userId);
|
||||
}
|
||||
|
||||
public List<ConversationMemory> getConversationHistory(String userId, LocalDateTime since) {
|
||||
if (since != null) {
|
||||
return conversationMemoryMapper.selectByUserIdSince(userId, since);
|
||||
}
|
||||
return conversationMemoryMapper.selectRecentByUserId(userId, 20);
|
||||
}
|
||||
|
||||
public UserProfile getUserProfile(String userId) {
|
||||
UserProfile profile = userProfileMapper.selectById(userId);
|
||||
if (profile == null) {
|
||||
profile = createDefaultUserProfile(userId);
|
||||
}
|
||||
return profile;
|
||||
}
|
||||
|
||||
public void updateUserProfile(String userId, String name, Integer age, Set<String> interests) {
|
||||
UserProfile profile = getUserProfile(userId);
|
||||
if (name != null) profile.setName(name);
|
||||
if (age != null) profile.setAge(age);
|
||||
if (interests != null) profile.setInterests(interests);
|
||||
profile.setUpdatedAt(LocalDateTime.now());
|
||||
|
||||
userProfileMapper.updateById(profile);
|
||||
}
|
||||
|
||||
public void addUserInterest(String userId, String interest) {
|
||||
UserProfile profile = getUserProfile(userId);
|
||||
profile.getInterests().add(interest);
|
||||
profile.setUpdatedAt(LocalDateTime.now());
|
||||
userProfileMapper.updateById(profile);
|
||||
}
|
||||
|
||||
public List<VectorMemory> findRelatedMemories(String userId, String query, int limit) {
|
||||
double[] queryEmbedding = vectorStore.createEmbedding(query);
|
||||
List<VectorMemory> userMemories = vectorMemoryMapper.selectByUserIdOrderByScore(userId);
|
||||
|
||||
return userMemories.stream()
|
||||
.sorted((m1, m2) -> {
|
||||
try {
|
||||
double[] emb1 = parseEmbedding(m1.getEmbedding());
|
||||
double[] emb2 = parseEmbedding(m2.getEmbedding());
|
||||
double sim1 = vectorStore.cosineSimilarity(queryEmbedding, emb1);
|
||||
double sim2 = vectorStore.cosineSimilarity(queryEmbedding, emb2);
|
||||
return Double.compare(sim2, sim1);
|
||||
} catch (Exception e) {
|
||||
return 0;
|
||||
}
|
||||
})
|
||||
.limit(limit)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public void storeImportantMemory(String userId, String content, String memoryType, double importance) {
|
||||
double[] embedding = vectorStore.createEmbedding(content);
|
||||
|
||||
VectorMemory memory = new VectorMemory();
|
||||
memory.setUserId(userId);
|
||||
memory.setContent(content);
|
||||
memory.setEmbedding(JSON.toJSONString(embedding));
|
||||
memory.setImportanceScore(importance);
|
||||
memory.setMemoryType(memoryType);
|
||||
memory.setCreatedAt(LocalDateTime.now());
|
||||
|
||||
vectorMemoryMapper.insert(memory);
|
||||
}
|
||||
|
||||
private UserProfile createDefaultUserProfile(String userId) {
|
||||
UserProfile profile = new UserProfile();
|
||||
profile.setUserId(userId);
|
||||
profile.setName("用户_" + userId.substring(0, 8));
|
||||
profile.setInterests(new HashSet<>());
|
||||
profile.setPreferences(new JSONObject());
|
||||
profile.setCreatedAt(LocalDateTime.now());
|
||||
profile.setUpdatedAt(LocalDateTime.now());
|
||||
|
||||
userProfileMapper.insert(profile);
|
||||
return profile;
|
||||
}
|
||||
|
||||
private double analyzeEmotionalTone(String text) {
|
||||
String lowerText = text.toLowerCase();
|
||||
int positiveWords = countWords(lowerText,
|
||||
Arrays.asList("开心", "高兴", "喜欢", "爱", "美好", "幸福", "满意"));
|
||||
int negativeWords = countWords(lowerText,
|
||||
Arrays.asList("难过", "伤心", "失望", "生气", "愤怒", "讨厌", "糟糕", "痛苦"));
|
||||
|
||||
int totalWords = positiveWords + negativeWords;
|
||||
if (totalWords == 0) return 0.0;
|
||||
|
||||
return (double) (positiveWords - negativeWords) / totalWords;
|
||||
}
|
||||
|
||||
private int countWords(String text, List<String> words) {
|
||||
int count = 0;
|
||||
for (String word : words) {
|
||||
if (text.contains(word)) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
private boolean isImportantMemory(String message, double emotionalTone) {
|
||||
return Math.abs(emotionalTone) > 0.3 ||
|
||||
message.length() > 15 ||
|
||||
message.matches(".*[我我的].*") ||
|
||||
message.contains("喜欢") || message.contains("讨厌") ||
|
||||
message.contains("想要") || message.contains("希望");
|
||||
}
|
||||
|
||||
private void storeVectorMemory(String userId, String content, double emotionalTone) {
|
||||
String memoryType = determineMemoryType(content, emotionalTone);
|
||||
double importanceScore = calculateImportanceScore(content, emotionalTone);
|
||||
storeImportantMemory(userId, content, memoryType, importanceScore);
|
||||
}
|
||||
|
||||
private String determineMemoryType(String content, double emotionalTone) {
|
||||
if (emotionalTone > 0.5) return "positive_experience";
|
||||
if (emotionalTone < -0.5) return "negative_experience";
|
||||
if (content.matches(".*(喜欢|爱|享受).*")) return "preference";
|
||||
if (content.matches(".*(知道|了解|学习).*")) return "knowledge";
|
||||
return "general_experience";
|
||||
}
|
||||
|
||||
private double calculateImportanceScore(String content, double emotionalTone) {
|
||||
double lengthFactor = Math.min(content.length() / 50.0, 1.0);
|
||||
double emotionFactor = Math.abs(emotionalTone);
|
||||
double personalFactor = content.matches(".*[我我的].*") ? 0.8 : 0.2;
|
||||
|
||||
return (lengthFactor * 0.2 + emotionFactor * 0.5 + personalFactor * 0.3);
|
||||
}
|
||||
|
||||
private double[] parseEmbedding(String embeddingStr) {
|
||||
try {
|
||||
return JSON.parseObject(embeddingStr, double[].class);
|
||||
} catch (Exception e) {
|
||||
return new double[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
193
src/main/java/top/krcia/cosmos/memory/ShortTermMemory.java
Normal file
193
src/main/java/top/krcia/cosmos/memory/ShortTermMemory.java
Normal file
@ -0,0 +1,193 @@
|
||||
package top.krcia.cosmos.memory;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
import top.krcia.cosmos.agent.AgentType;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
public class ShortTermMemory {
|
||||
private final RedisTemplate<String, Object> redisTemplate;
|
||||
|
||||
private final String CONVERSATION_PREFIX = "cosmos:conversation:";
|
||||
private final String USER_SESSION_PREFIX = "cosmos:user_session:";
|
||||
private final String AGENT_MEMORY_PREFIX = "cosmos:agent_memory:";
|
||||
private final int MAX_CONTEXT_LENGTH = 10;
|
||||
|
||||
@Value("${app.redis.conversation-ttl:7200}")
|
||||
private long conversationTtl;
|
||||
|
||||
@Autowired
|
||||
public ShortTermMemory(RedisTemplate<String, Object> redisTemplate) {
|
||||
this.redisTemplate = redisTemplate;
|
||||
}
|
||||
|
||||
public void storeConversation(String sessionId, String userMessage,
|
||||
String aiResponse, String agentType) {
|
||||
String key = CONVERSATION_PREFIX + sessionId;
|
||||
|
||||
ConversationExchange exchange = new ConversationExchange(
|
||||
userMessage, aiResponse, agentType, LocalDateTime.now());
|
||||
|
||||
redisTemplate.opsForList().rightPush(key, exchange);
|
||||
|
||||
Long size = redisTemplate.opsForList().size(key);
|
||||
if (size != null && size > MAX_CONTEXT_LENGTH) {
|
||||
redisTemplate.opsForList().leftPop(key);
|
||||
}
|
||||
|
||||
redisTemplate.expire(key, Duration.ofSeconds(conversationTtl));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<ConversationExchange> getRecentConversations(String sessionId, int count) {
|
||||
String key = CONVERSATION_PREFIX + sessionId;
|
||||
|
||||
Long size = redisTemplate.opsForList().size(key);
|
||||
if (size == null || size == 0) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
int endIndex = Math.min(count, size.intValue()) - 1;
|
||||
List<Object> exchanges = redisTemplate.opsForList().range(key, 0, endIndex);
|
||||
|
||||
if (exchanges == null) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
return exchanges.stream()
|
||||
.filter(obj -> obj instanceof ConversationExchange)
|
||||
.map(obj -> (ConversationExchange) obj)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public void storeUserSession(String userId, UserSession session) {
|
||||
String key = USER_SESSION_PREFIX + userId;
|
||||
redisTemplate.opsForValue().set(key, session, conversationTtl, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
public UserSession getUserSession(String userId) {
|
||||
String key = USER_SESSION_PREFIX + userId;
|
||||
Object session = redisTemplate.opsForValue().get(key);
|
||||
if (session instanceof UserSession) {
|
||||
return (UserSession) session;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void updateSessionActivity(String userId) {
|
||||
String key = USER_SESSION_PREFIX + userId;
|
||||
UserSession session = getUserSession(userId);
|
||||
if (session != null) {
|
||||
session.setLastActivity(LocalDateTime.now());
|
||||
storeUserSession(userId, session);
|
||||
}
|
||||
}
|
||||
|
||||
public UserSession createOrUpdateUserSession(String userId, String sessionId) {
|
||||
UserSession session = getUserSession(userId);
|
||||
if (session == null) {
|
||||
session = new UserSession(userId, sessionId);
|
||||
} else {
|
||||
session.setCurrentSessionId(sessionId);
|
||||
session.setLastActivity(LocalDateTime.now());
|
||||
}
|
||||
storeUserSession(userId, session);
|
||||
return session;
|
||||
}
|
||||
|
||||
public long getConversationSize(String sessionId) {
|
||||
String key = CONVERSATION_PREFIX + sessionId;
|
||||
Long size = redisTemplate.opsForList().size(key);
|
||||
return size != null ? size : 0;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class UserSession {
|
||||
private String userId;
|
||||
private String currentSessionId;
|
||||
private LocalDateTime loginTime;
|
||||
private LocalDateTime lastActivity;
|
||||
private Map<String, Object> attributes = new HashMap<>();
|
||||
|
||||
public UserSession() {}
|
||||
|
||||
public UserSession(String userId, String sessionId) {
|
||||
this.userId = userId;
|
||||
this.currentSessionId = sessionId;
|
||||
this.loginTime = LocalDateTime.now();
|
||||
this.lastActivity = LocalDateTime.now();
|
||||
}
|
||||
|
||||
public void setAttribute(String key, Object value) {
|
||||
this.attributes.put(key, value);
|
||||
}
|
||||
|
||||
public Object getAttribute(String key) {
|
||||
return this.attributes.get(key);
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class ConversationExchange {
|
||||
private String userMessage;
|
||||
private String aiResponse;
|
||||
private String agentType;
|
||||
private LocalDateTime timestamp;
|
||||
|
||||
public ConversationExchange() {}
|
||||
|
||||
public ConversationExchange(String userMessage, String aiResponse, String agentType, LocalDateTime timestamp) {
|
||||
this.userMessage = userMessage;
|
||||
this.aiResponse = aiResponse;
|
||||
this.agentType = agentType;
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 存储当前会话的智能体类型
|
||||
*/
|
||||
public void storeCurrentAgent(String sessionId, AgentType agentType) {
|
||||
String key = AGENT_MEMORY_PREFIX + sessionId;
|
||||
redisTemplate.opsForValue().set(key, agentType.name(), Duration.ofSeconds(conversationTtl));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前会话的智能体类型
|
||||
*/
|
||||
public AgentType getCurrentAgent(String sessionId) {
|
||||
String key = AGENT_MEMORY_PREFIX + sessionId;
|
||||
Object agentTypeStr = redisTemplate.opsForValue().get(key);
|
||||
if (agentTypeStr instanceof String) {
|
||||
try {
|
||||
return AgentType.valueOf((String) agentTypeStr);
|
||||
} catch (IllegalArgumentException e) {
|
||||
log.warn("无效的智能体类型: {}", agentTypeStr);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除会话的智能体记忆
|
||||
*/
|
||||
public void clearAgentMemory(String sessionId) {
|
||||
String key = AGENT_MEMORY_PREFIX + sessionId;
|
||||
redisTemplate.delete(key);
|
||||
}
|
||||
}
|
||||
49
src/main/java/top/krcia/cosmos/memory/VectorStore.java
Normal file
49
src/main/java/top/krcia/cosmos/memory/VectorStore.java
Normal file
@ -0,0 +1,49 @@
|
||||
package top.krcia.cosmos.memory;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
@Component
|
||||
public class VectorStore {
|
||||
private final int EMBEDDING_SIZE = 1536;
|
||||
|
||||
public double[] createEmbedding(String text) {
|
||||
Random random = new Random(text.hashCode());
|
||||
double[] embedding = new double[EMBEDDING_SIZE];
|
||||
|
||||
for (int i = 0; i < EMBEDDING_SIZE; i++) {
|
||||
embedding[i] = random.nextGaussian();
|
||||
}
|
||||
|
||||
return normalize(embedding);
|
||||
}
|
||||
|
||||
public double cosineSimilarity(double[] vec1, double[] vec2) {
|
||||
double dotProduct = 0.0;
|
||||
double norm1 = 0.0;
|
||||
double norm2 = 0.0;
|
||||
|
||||
for (int i = 0; i < vec1.length; i++) {
|
||||
dotProduct += vec1[i] * vec2[i];
|
||||
norm1 += vec1[i] * vec1[i];
|
||||
norm2 += vec2[i] * vec2[i];
|
||||
}
|
||||
|
||||
return dotProduct / (Math.sqrt(norm1) * Math.sqrt(norm2));
|
||||
}
|
||||
|
||||
private double[] normalize(double[] vector) {
|
||||
double sum = 0.0;
|
||||
for (double v : vector) {
|
||||
sum += v * v;
|
||||
}
|
||||
double norm = Math.sqrt(sum);
|
||||
|
||||
double[] normalized = new double[vector.length];
|
||||
for (int i = 0; i < vector.length; i++) {
|
||||
normalized[i] = vector[i] / norm;
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
}
|
||||
427
src/main/java/top/krcia/cosmos/request/RequestHelper.java
Normal file
427
src/main/java/top/krcia/cosmos/request/RequestHelper.java
Normal file
@ -0,0 +1,427 @@
|
||||
package top.krcia.cosmos.request;
|
||||
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import io.micrometer.common.util.StringUtils;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.NameValuePair;
|
||||
import org.apache.http.client.CookieStore;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.config.AuthSchemes;
|
||||
import org.apache.http.client.config.CookieSpecs;
|
||||
import org.apache.http.client.config.RequestConfig;
|
||||
import org.apache.http.client.entity.UrlEncodedFormEntity;
|
||||
import org.apache.http.client.methods.HttpDelete;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.client.methods.HttpPut;
|
||||
import org.apache.http.config.Registry;
|
||||
import org.apache.http.config.RegistryBuilder;
|
||||
import org.apache.http.conn.socket.ConnectionSocketFactory;
|
||||
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
|
||||
import org.apache.http.conn.ssl.NoopHostnameVerifier;
|
||||
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
|
||||
import org.apache.http.message.BasicNameValuePair;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.springframework.http.MediaType;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class RequestHelper {
|
||||
|
||||
/**
|
||||
* get
|
||||
*
|
||||
* @param host
|
||||
* @param path
|
||||
* @param headers
|
||||
* @param queryMap
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public static String get(String host, String path, MediaType mediaType,
|
||||
Map<String, String> headers,
|
||||
Map<String, Object> queryMap, CookieStore cookies, int timeOut) throws IOException {
|
||||
HttpClient httpClient = wrapClient(host, path, cookies);
|
||||
HttpGet request = null;
|
||||
try {
|
||||
request = new HttpGet(buildUrl(host, path, queryMap));
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
request.setHeader("Content-Type", mediaType.getType() + "/" + mediaType.getSubtype());
|
||||
try {
|
||||
for (Map.Entry<String, String> e : headers.entrySet()) {
|
||||
request.addHeader(e.getKey(), e.getValue());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
|
||||
request.setConfig(setTimeOutConfig(request.getConfig()));
|
||||
HttpResponse response = null;
|
||||
response = httpClient.execute(request);
|
||||
|
||||
String json = null;
|
||||
try {
|
||||
json = EntityUtils.toString(response.getEntity());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
/**
|
||||
* post form
|
||||
*
|
||||
* @param host
|
||||
* @param path
|
||||
* @param headers
|
||||
* @param queryMap
|
||||
* @param bodyMap
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public static String post(String host, String path, MediaType mediaType,
|
||||
Map<String, String> headers,
|
||||
Map<String, Object> queryMap,
|
||||
Map<String, Object> bodyMap, CookieStore cookies, int timeOut) throws IOException {
|
||||
HttpClient httpClient = wrapClient(host, path, cookies);
|
||||
HttpPost request = null;
|
||||
try {
|
||||
request = new HttpPost(buildUrl(host, path, queryMap));
|
||||
request.setConfig(RequestConfig.custom().setConnectTimeout(timeOut).setConnectionRequestTimeout(timeOut).build());
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
request.setHeader("Content-Type", mediaType.getType() + "/" + mediaType.getSubtype());
|
||||
for (Map.Entry<String, String> e : headers.entrySet()) {
|
||||
request.addHeader(e.getKey(), e.getValue());
|
||||
}
|
||||
request.setConfig(setTimeOutConfig(request.getConfig()));
|
||||
if (bodyMap != null && bodyMap.size() > 0) {
|
||||
List<NameValuePair> nameValuePairList = new ArrayList<NameValuePair>();
|
||||
|
||||
for (String key : bodyMap.keySet()) {
|
||||
nameValuePairList.add(new BasicNameValuePair(key, bodyMap.get(key).toString()));
|
||||
}
|
||||
UrlEncodedFormEntity formEntity = null;
|
||||
try {
|
||||
formEntity = new UrlEncodedFormEntity(nameValuePairList, "utf-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
formEntity.setContentType("application/x-www-form-urlencoded; charset=UTF-8");
|
||||
request.setEntity(formEntity);
|
||||
}
|
||||
HttpResponse response = httpClient.execute(request);
|
||||
String json = EntityUtils.toString(response.getEntity());
|
||||
return json;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Post String
|
||||
*
|
||||
* @param host
|
||||
* @param path
|
||||
* @param headers
|
||||
* @param queryMap
|
||||
* @param body
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public static String post(String host, String path, MediaType mediaType,
|
||||
Map<String, String> headers,
|
||||
Map<String, Object> queryMap,
|
||||
String body, CookieStore cookies, int timeOut) throws IOException {
|
||||
HttpClient httpClient = wrapClient(host, path, cookies);
|
||||
HttpPost request = null;
|
||||
try {
|
||||
request = new HttpPost(buildUrl(host, path, queryMap));
|
||||
|
||||
request.setConfig(RequestConfig.custom().setConnectTimeout(timeOut).setConnectionRequestTimeout(timeOut).build());
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
request.setHeader("Content-Type", mediaType.getType() + "/" + mediaType.getSubtype());
|
||||
try {
|
||||
for (Map.Entry<String, String> e : headers.entrySet()) {
|
||||
request.addHeader(e.getKey(), e.getValue());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
if (StringUtils.isNotBlank(body)) {
|
||||
request.setEntity(new StringEntity(body, "utf-8"));
|
||||
}
|
||||
request.setConfig(setTimeOutConfig(request.getConfig()));
|
||||
HttpResponse response = httpClient.execute(request);
|
||||
String json = EntityUtils.toString(response.getEntity());
|
||||
return json;
|
||||
}
|
||||
|
||||
/**
|
||||
* Post String
|
||||
*
|
||||
* @param host
|
||||
* @param path
|
||||
* @param headers
|
||||
* @param queryMap
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public static String post(String host, String path, MediaType mediaType,
|
||||
Map<String, String> headers,
|
||||
Map<String, Object> queryMap, CookieStore cookies, int timeOut) throws IOException {
|
||||
HttpClient httpClient = wrapClient(host, path, cookies);
|
||||
HttpPost request = null;
|
||||
try {
|
||||
request = new HttpPost(buildUrl(host, path, queryMap));
|
||||
|
||||
request.setConfig(RequestConfig.custom().setConnectTimeout(timeOut).setConnectionRequestTimeout(timeOut).build());
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
request.setHeader("Content-Type", mediaType.getType() + "/" + mediaType.getSubtype());
|
||||
try {
|
||||
for (Map.Entry<String, String> e : headers.entrySet()) {
|
||||
request.addHeader(e.getKey(), e.getValue());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
request.setConfig(setTimeOutConfig(request.getConfig()));
|
||||
HttpResponse response = httpClient.execute(request);
|
||||
String json = EntityUtils.toString(response.getEntity());
|
||||
return json;
|
||||
}
|
||||
|
||||
/**
|
||||
* Put String
|
||||
*
|
||||
* @param host
|
||||
* @param path
|
||||
* @param headers
|
||||
* @param queryMap
|
||||
* @param body
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public static String put(String host, String path, MediaType mediaType,
|
||||
Map<String, String> headers,
|
||||
Map<String, Object> queryMap,
|
||||
String body, CookieStore cookies, int timeOut)
|
||||
throws Exception {
|
||||
HttpClient httpClient = wrapClient(host, path, cookies);
|
||||
HttpPut request = new HttpPut(buildUrl(host, path, queryMap));
|
||||
request.setConfig(RequestConfig.custom().setConnectTimeout(timeOut).setConnectionRequestTimeout(timeOut).build());
|
||||
request.setHeader("Content-Type", mediaType.getType() + "/" + mediaType.getSubtype());
|
||||
try {
|
||||
for (Map.Entry<String, String> e : headers.entrySet()) {
|
||||
request.addHeader(e.getKey(), e.getValue());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
;
|
||||
if (StringUtils.isNotBlank(body)) {
|
||||
request.setEntity(new StringEntity(body, "utf-8"));
|
||||
}
|
||||
request.setConfig(setTimeOutConfig(request.getConfig()));
|
||||
HttpResponse response = httpClient.execute(request);
|
||||
String json = EntityUtils.toString(response.getEntity());
|
||||
return json;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete
|
||||
*
|
||||
* @param host
|
||||
* @param path
|
||||
* @param headers
|
||||
* @param queryMap
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public static String delete(String host, String path, MediaType mediaType,
|
||||
Map<String, String> headers,
|
||||
Map<String, Object> queryMap, CookieStore cookies, int timeOut)
|
||||
throws Exception {
|
||||
HttpClient httpClient = wrapClient(host, path, cookies);
|
||||
HttpDelete request = new HttpDelete(buildUrl(host, path, queryMap));
|
||||
request.setConfig(RequestConfig.custom().setConnectTimeout(timeOut).setConnectionRequestTimeout(timeOut).build());
|
||||
request.setHeader("Content-Type", mediaType.getType() + "/" + mediaType.getSubtype());
|
||||
for (Map.Entry<String, String> e : headers.entrySet()) {
|
||||
request.addHeader(e.getKey(), e.getValue());
|
||||
}
|
||||
request.setConfig(setTimeOutConfig(request.getConfig()));
|
||||
HttpResponse response = httpClient.execute(request);
|
||||
String json = EntityUtils.toString(response.getEntity());
|
||||
return json;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建请求的 url
|
||||
*
|
||||
* @param host
|
||||
* @param path
|
||||
* @param queryMap
|
||||
* @return
|
||||
* @throws UnsupportedEncodingException
|
||||
*/
|
||||
private static String buildUrl(String host, String path, Map<String, Object> queryMap) throws UnsupportedEncodingException {
|
||||
StringBuilder sbUrl = new StringBuilder();
|
||||
if (!StringUtils.isBlank(host)) {
|
||||
sbUrl.append(host);
|
||||
}
|
||||
if (!StringUtils.isBlank(path)) {
|
||||
sbUrl.append(path);
|
||||
}
|
||||
if (null != queryMap) {
|
||||
StringBuilder sbQuery = new StringBuilder();
|
||||
for (Map.Entry<String, Object> query : queryMap.entrySet()) {
|
||||
if (0 < sbQuery.length()) {
|
||||
sbQuery.append("&");
|
||||
}
|
||||
if (StringUtils.isBlank(query.getKey()) && !StringUtils.isBlank(query.getValue().toString())) {
|
||||
sbQuery.append(query.getValue());
|
||||
}
|
||||
if (!StringUtils.isBlank(query.getKey())) {
|
||||
sbQuery.append(query.getKey());
|
||||
if (!StringUtils.isBlank(query.getValue().toString())) {
|
||||
sbQuery.append("=");
|
||||
sbQuery.append(URLEncoder.encode(query.getValue().toString(), "utf-8"));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (0 < sbQuery.length()) {
|
||||
sbUrl.append("?").append(sbQuery);
|
||||
}
|
||||
}
|
||||
return sbUrl.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 HttpClient
|
||||
*
|
||||
* @param host
|
||||
* @param path
|
||||
* @return
|
||||
*/
|
||||
private static HttpClient wrapClient(String host, String path, CookieStore cookies) {
|
||||
HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
|
||||
if (cookies != null) {
|
||||
httpClientBuilder.setDefaultCookieStore(cookies);
|
||||
}
|
||||
HttpClient httpClient = httpClientBuilder.build();
|
||||
if (host != null && host.startsWith("https://")) {
|
||||
return sslClient();
|
||||
} else if (StringUtils.isBlank(host) && path != null && path.startsWith("https://")) {
|
||||
return sslClient();
|
||||
}
|
||||
return httpClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* 在调用SSL之前需要重写验证方法,取消检测SSL
|
||||
* 创建ConnectionManager,添加Connection配置信息
|
||||
*
|
||||
* @return HttpClient 支持https
|
||||
*/
|
||||
private static HttpClient sslClient() {
|
||||
try {
|
||||
// 在调用SSL之前需要重写验证方法,取消检测SSL
|
||||
X509TrustManager trustManager = new X509TrustManager() {
|
||||
@Override
|
||||
public X509Certificate[] getAcceptedIssuers() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkClientTrusted(X509Certificate[] xcs, String str) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkServerTrusted(X509Certificate[] xcs, String str) {
|
||||
}
|
||||
};
|
||||
SSLContext ctx = SSLContext.getInstance(SSLConnectionSocketFactory.TLS);
|
||||
ctx.init(null, new TrustManager[]{trustManager}, null);
|
||||
SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(ctx, NoopHostnameVerifier.INSTANCE);
|
||||
// 创建Registry
|
||||
RequestConfig requestConfig = RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD_STRICT)
|
||||
.setExpectContinueEnabled(Boolean.TRUE).setTargetPreferredAuthSchemes(Arrays.asList(AuthSchemes.NTLM, AuthSchemes.DIGEST))
|
||||
.setProxyPreferredAuthSchemes(Arrays.asList(AuthSchemes.BASIC)).build();
|
||||
Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
|
||||
.register("http", PlainConnectionSocketFactory.INSTANCE)
|
||||
.register("https", socketFactory).build();
|
||||
// 创建ConnectionManager,添加Connection配置信息
|
||||
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
|
||||
CloseableHttpClient closeableHttpClient = HttpClients.custom().setConnectionManager(connectionManager)
|
||||
.setDefaultRequestConfig(requestConfig).build();
|
||||
return closeableHttpClient;
|
||||
} catch (KeyManagementException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
} catch (NoSuchAlgorithmException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 连接超时、 请求超时 、 读取超时 毫秒
|
||||
*
|
||||
* @param requestConfig
|
||||
* @return
|
||||
*/
|
||||
private static RequestConfig setTimeOutConfig(RequestConfig requestConfig) {
|
||||
if (requestConfig == null) {
|
||||
requestConfig = RequestConfig.DEFAULT;
|
||||
}
|
||||
return RequestConfig.copy(requestConfig)
|
||||
.setConnectionRequestTimeout(30000)
|
||||
.setConnectTimeout(30000)
|
||||
.setSocketTimeout(10000)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将结果转换成JSONObject
|
||||
*
|
||||
* @param httpResponse
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
public static JSONObject parseJson(HttpResponse httpResponse) throws IOException {
|
||||
return JSON.parseObject(parseString(httpResponse));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将结果转换成 String
|
||||
*
|
||||
* @param httpResponse
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
public static String parseString(HttpResponse httpResponse) throws IOException {
|
||||
HttpEntity entity = httpResponse.getEntity();
|
||||
String resp = EntityUtils.toString(entity, "UTF-8");
|
||||
EntityUtils.consume(entity);
|
||||
return resp;
|
||||
}
|
||||
}
|
||||
111
src/main/java/top/krcia/cosmos/request/WebRequest.java
Normal file
111
src/main/java/top/krcia/cosmos/request/WebRequest.java
Normal file
@ -0,0 +1,111 @@
|
||||
package top.krcia.cosmos.request;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import org.apache.http.client.CookieStore;
|
||||
import org.apache.http.impl.client.BasicCookieStore;
|
||||
import org.apache.http.impl.cookie.BasicClientCookie;
|
||||
import org.springframework.http.MediaType;
|
||||
import top.krcia.cosmos.request.enums.RequestType;
|
||||
import top.krcia.cosmos.request.interfaces.WebRequestOption;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Map;
|
||||
|
||||
public class WebRequest implements WebRequestOption {
|
||||
private Map<String, String> headers;
|
||||
private Map<String, Object> params;
|
||||
private String body;
|
||||
private Map<String, Object> bodyForm;
|
||||
private RequestType requestType = RequestType.GET;
|
||||
private MediaType mediaType = MediaType.APPLICATION_JSON;
|
||||
private int timeout=-1;
|
||||
|
||||
private CookieStore cookies = new BasicCookieStore();
|
||||
|
||||
private URI url;
|
||||
|
||||
@Override
|
||||
public WebRequest setHeader(Map<String, String> headers) {
|
||||
this.headers = headers;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebRequest setParam(Map<String, Object> params) {
|
||||
this.params = params;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebRequest setBody(JSONObject body) {
|
||||
this.body = body.toJSONString();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebRequest setBodyForm(Map<String, Object> bodyForm) {
|
||||
this.bodyForm=bodyForm;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebRequest setCookie(Map<String, String> cookies) {
|
||||
for (var item: cookies.keySet()){
|
||||
BasicClientCookie cookie = new BasicClientCookie(item, cookies.get(item));
|
||||
cookie.setDomain(url.getHost());
|
||||
cookie.setPath("/");
|
||||
this.cookies.addCookie(cookie);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebRequest setRequestType(RequestType requestType) {
|
||||
this.requestType = requestType;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebRequest setTimeOut(int timeout) {
|
||||
this.timeout=timeout;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebRequest setContentType(MediaType mediaType) {
|
||||
this.mediaType = mediaType;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String execute() throws Exception {
|
||||
switch (requestType){
|
||||
case GET -> {
|
||||
return RequestHelper.get(url.getScheme()+"://"+url.getHost()+":"+url.getPort(),url.getPath(),mediaType,headers,params,cookies,timeout);
|
||||
}
|
||||
case POST -> {
|
||||
if (bodyForm!=null){
|
||||
return RequestHelper.post(url.getScheme()+"://"+url.getHost()+":"+url.getPort(),url.getPath(),mediaType,headers,params,bodyForm,cookies,timeout);
|
||||
}else if (body!=null){
|
||||
return RequestHelper.post(url.getScheme()+"://"+url.getHost()+":"+url.getPort(),url.getPath(),mediaType,headers,params,body,cookies,timeout);
|
||||
}else{
|
||||
return RequestHelper.post(url.getScheme()+"://"+url.getHost()+":"+url.getPort(),url.getPath(),mediaType,headers,params,cookies,timeout);
|
||||
}
|
||||
}
|
||||
case PUT -> {
|
||||
return RequestHelper.put(url.getScheme()+"://"+url.getHost()+":"+url.getPort(),url.getPath(),mediaType,headers,params,body,cookies,timeout);
|
||||
}
|
||||
case DELETE -> {
|
||||
return RequestHelper.delete(url.getScheme()+"://"+url.getHost()+":"+url.getPort(),url.getPath(),mediaType,headers,params,cookies,timeout);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public WebRequest(URI url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public static WebRequest Create(URI url){
|
||||
return new WebRequest(url);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,5 @@
|
||||
package top.krcia.cosmos.request.enums;
|
||||
|
||||
public enum RequestType {
|
||||
GET,POST,PUT,DELETE
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
package top.krcia.cosmos.request.interfaces;
|
||||
|
||||
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import org.springframework.http.MediaType;
|
||||
import top.krcia.cosmos.request.WebRequest;
|
||||
import top.krcia.cosmos.request.enums.RequestType;
|
||||
|
||||
import java.util.Map;
|
||||
public interface WebRequestOption {
|
||||
WebRequest setHeader(Map<String, String> headers);
|
||||
WebRequest setParam(Map<String, Object> params);
|
||||
WebRequest setBody(JSONObject body);
|
||||
WebRequest setBodyForm(Map<String, Object> bodyForm);
|
||||
WebRequest setCookie(Map<String, String> cookie);
|
||||
WebRequest setRequestType(RequestType requestType);
|
||||
WebRequest setTimeOut(int timeout);
|
||||
WebRequest setContentType(MediaType mediaType);
|
||||
String execute() throws Exception;
|
||||
}
|
||||
52
src/main/resources/application.yml
Normal file
52
src/main/resources/application.yml
Normal file
@ -0,0 +1,52 @@
|
||||
spring:
|
||||
datasource:
|
||||
url: jdbc:mysql://krcia.top:3306/cosmos_ai?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8
|
||||
username: root
|
||||
password: xiong990416.
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
hikari:
|
||||
maximum-pool-size: 20
|
||||
minimum-idle: 5
|
||||
|
||||
data:
|
||||
redis:
|
||||
host: krcia.top
|
||||
port: 6379
|
||||
password: xiong990416.
|
||||
database: 0
|
||||
timeout: 3000ms
|
||||
jedis:
|
||||
pool:
|
||||
max-active: 20
|
||||
max-idle: 10
|
||||
min-idle: 5
|
||||
|
||||
jackson:
|
||||
property-naming-strategy: LOWER_CAMEL_CASE
|
||||
date-format: yyyy-MM-dd HH:mm:ss
|
||||
time-zone: Asia/Shanghai
|
||||
|
||||
mybatis-plus:
|
||||
configuration:
|
||||
map-underscore-to-camel-case: true
|
||||
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
||||
global-config:
|
||||
db-config:
|
||||
id-type: auto
|
||||
logic-delete-field: deleted
|
||||
logic-delete-value: 1
|
||||
logic-not-delete-value: 0
|
||||
|
||||
app:
|
||||
redis:
|
||||
conversation-ttl: 7200
|
||||
session-ttl: 86400
|
||||
deepseek:
|
||||
api-key: sk-44a95df146f945c981594710026ced7e
|
||||
base-url: "https://api.deepseek.com/v1"
|
||||
model: "deepseek-chat"
|
||||
max-tokens: 2000
|
||||
temperature: 1.2
|
||||
|
||||
server:
|
||||
port: 1212
|
||||
10
src/main/resources/smart-doc.json
Normal file
10
src/main/resources/smart-doc.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"outPath": "./src/main/resources/static/doc",
|
||||
"serverUrl": "/",
|
||||
"serverEnv": "http://{{server}}",
|
||||
"allInOne": true,
|
||||
"coverOld": true,
|
||||
"createDebugPage": true,
|
||||
"showAuthor": "true",
|
||||
"allInOneDocFileName": "index.html"
|
||||
}
|
||||
13
src/main/resources/static/doc/AllInOne.css
Normal file
13
src/main/resources/static/doc/AllInOne.css
Normal file
@ -0,0 +1,13 @@
|
||||
article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,video{display:inline-block}audio:not([controls]){display:none;height:0}[hidden],template{display:none}script{display:none!important}html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}a{background:transparent}a:focus{outline:thin dotted}a:active,a:hover{outline:0}h1{font-size:2em;margin:.67em 0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}dfn{font-style:italic}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}mark{background:#ff0;color:#000}code,kbd,pre,samp{font-family:monospace;font-size:1em}pre{white-space:pre-wrap}q{quotes:"\201C" "\201D" "\2018" "\2019"}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:0}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}button,input,select,textarea{font-family:inherit;font-size:100%;margin:0}button,input{line-height:normal}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}textarea{overflow:auto;vertical-align:top}table{border-collapse:collapse;border-spacing:0}*,*:before,*:after{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}html,body{font-size:100%}body{background:#fff;color:rgba(0,0,0,.8);padding:0;margin:0;font-family:"Noto Serif","DejaVu Serif",serif;font-weight:400;font-style:normal;line-height:1;position:relative;cursor:auto;tab-size:4;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased}a:hover{cursor:pointer}img,object,embed{max-width:100%;height:auto}object,embed{height:100%}img{-ms-interpolation-mode:bicubic}.left{float:left!important}.right{float:right!important}.text-left{text-align:left!important}.text-right{text-align:right!important}.text-center{text-align:center!important}.text-justify{text-align:justify!important}.hide{display:none}img,object,svg{display:inline-block;vertical-align:middle}textarea{height:auto;min-height:50px}select{width:100%}.center{margin-left:auto;margin-right:auto}.spread{width:100%}p.lead,.paragraph.lead>p,#preamble>.sectionbody>.paragraph:first-of-type p{font-size:1.21875em;line-height:1.6}.subheader,.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{line-height:1.45;color:#7a2518;font-weight:400;margin-top:0;margin-bottom:.25em}div,dl,dt,dd,ul,ol,li,h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6,pre,form,p,blockquote,th,td{margin:0;padding:0;direction:ltr}a{color:#364149;text-decoration:underline;line-height:inherit}a:hover,a:focus{color:#364149}a img{border:0}p{font-family:inherit;font-weight:400;font-size:1em;line-height:1.6;margin-bottom:1.25em;text-rendering:optimizeLegibility}p aside{font-size:.875em;line-height:1.35;font-style:italic}h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{font-family:"Open Sans","DejaVu Sans",sans-serif;font-weight:300;font-style:normal;color:#ba3925;text-rendering:optimizeLegibility;margin-top:1em;margin-bottom:.5em;line-height:1.0125em}h1 small,h2 small,h3 small,#toctitle small,.sidebarblock>.content>.title small,h4 small,h5 small,h6 small{font-size:60%;color:#e99b8f;line-height:0}h1{font-size:2.125em}h2{font-size:1.6875em}h3,#toctitle,.sidebarblock>.content>.title{font-size:1.375em}h4,h5{font-size:1.125em}h6{font-size:1em}hr{border:solid #ddddd8;border-width:1px 0 0;clear:both;margin:1.25em 0 1.1875em;height:0}em,i{font-style:italic;line-height:inherit}strong,b{font-weight:bold;line-height:inherit}small{font-size:60%;line-height:inherit}code{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;font-weight:400;color:rgba(0,0,0,.9)}
|
||||
ul,ol,dl{font-size:1em;line-height:1.6;margin-bottom:1.25em;list-style-position:outside;font-family:inherit}ul,ol{margin-left:1.5em}ul li ul,ul li ol{margin-left:1.25em;margin-bottom:0;font-size:1em}ul.square li ul,ul.circle li ul,ul.disc li ul{list-style:inherit}ul.square{list-style-type:square}ul.circle{list-style-type:circle}ul.disc{list-style-type:disc}ol li ul,ol li ol{margin-left:1.25em;margin-bottom:0}dl dt{margin-bottom:.3125em;font-weight:bold}dl dd{margin-bottom:1.25em}abbr,acronym{text-transform:uppercase;font-size:90%;color:rgba(0,0,0,.8);border-bottom:1px dotted #ddd;cursor:help}abbr{text-transform:none}blockquote{margin:0 0 1.25em;padding:.5625em 1.25em 0 1.1875em;border-left:1px solid #ddd}blockquote cite{display:block;font-size:.9375em;color:rgba(0,0,0,.6)}blockquote cite:before{content:"\2014 \0020"}blockquote cite a,blockquote cite a:visited{color:rgba(0,0,0,.6)}blockquote,blockquote p{line-height:1.6;color:rgba(0,0,0,.85)}@media only screen and (min-width:768px){h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2}h1{font-size:2.75em}h2{font-size:2.3125em}h3,#toctitle,.sidebarblock>.content>.title{font-size:1.6875em}h4{font-size:1.4375em}}table{background:#fff;margin-bottom:1.25em;border:solid 1px #dedede}table thead,table tfoot{background:#f7f8f7;font-weight:bold}table thead tr th,table thead tr td,table tfoot tr th,table tfoot tr td{padding:.5em .625em .625em;font-size:inherit;color:rgba(0,0,0,.8);text-align:left}table tr th,table tr td{padding:.5625em .625em;font-size:inherit;color:rgba(0,0,0,.8)}table tr.even,table tr.alt,table tr:nth-of-type(even){background:#f8f8f7}table thead tr th,table tfoot tr th,table tbody tr td,table tr td,table tfoot tr td{display:table-cell;line-height:1.6}h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2;word-spacing:-.05em}h1 strong,h2 strong,h3 strong,#toctitle strong,.sidebarblock>.content>.title strong,h4 strong,h5 strong,h6 strong{font-weight:400}.clearfix:before,.clearfix:after,.float-group:before,.float-group:after{content:" ";display:table}.clearfix:after,.float-group:after{clear:both}*:not(pre)>code{font-size:.9375em;font-style:normal!important;letter-spacing:0;padding:.1em .5ex;word-spacing:-.15em;background-color:#f7f7f8;-webkit-border-radius:4px;border-radius:4px;line-height:1.45;text-rendering:optimizeSpeed;word-wrap:break-word}*:not(pre)>code.nobreak{word-wrap:normal}*:not(pre)>code.nowrap{white-space:nowrap}pre,pre>code{line-height:1.45;color:rgba(0,0,0,.9);font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;font-weight:400;text-rendering:optimizeSpeed}em em{font-style:normal}strong strong{font-weight:400}.keyseq{color:rgba(51,51,51,.8)}kbd{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;display:inline-block;color:rgba(0,0,0,.8);font-size:.65em;line-height:1.45;background-color:#f7f7f7;border:1px solid #ccc;-webkit-border-radius:3px;border-radius:3px;-webkit-box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em white inset;box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em #fff inset;margin:0 .15em;padding:.2em .5em;vertical-align:middle;position:relative;top:-.1em;white-space:nowrap}.keyseq kbd:first-child{margin-left:0}.keyseq kbd:last-child{margin-right:0}.menuseq,.menuref{color:#000}.menuseq b:not(.caret),.menuref{font-weight:inherit}.menuseq{word-spacing:-.02em}.menuseq b.caret{font-size:1.25em;line-height:.8}.menuseq i.caret{font-weight:bold;text-align:center;width:.45em}b.button:before,b.button:after{position:relative;top:-1px;font-weight:400}b.button:before{content:"[";padding:0 3px 0 2px}b.button:after{content:"]";padding:0 2px 0 3px}p a>code:hover{color:rgba(0,0,0,.9)}#header,#content,#footnotes,#footer{width:100%;margin-left:auto;margin-right:auto;margin-top:0;margin-bottom:0;max-width:62.5em;*zoom:1;position:relative;padding-left:.9375em;padding-right:.9375em}#header:before,#header:after,#content:before,#content:after,#footnotes:before,#footnotes:after,#footer:before,#footer:after{content:" ";display:table}#header:after,#content:after,#footnotes:after,#footer:after{clear:both}#content{margin-top:1.25em}#content:before{content:none}#header>h1:first-child{color:rgba(0,0,0,.85);margin-top:2.25rem;margin-bottom:0}#header>h1:first-child+#toc{margin-top:8px;border-top:1px solid #ddddd8}#header>h1:only-child,body.toc2 #header>h1:nth-last-child(2){border-bottom:1px solid #ddddd8;padding-bottom:8px}#header .details{border-bottom:1px solid #ddddd8;line-height:1.45;padding-top:.25em;padding-bottom:.25em;padding-left:.25em;color:rgba(0,0,0,.6);display:-ms-flexbox;display:-webkit-flex;display:flex;-ms-flex-flow:row wrap;-webkit-flex-flow:row wrap;flex-flow:row wrap}#header .details span:first-child{margin-left:-.125em}#header .details span.email a{color:rgba(0,0,0,.85)}#header .details br{display:none}
|
||||
#header .details br+span:before{content:"\00a0\2013\00a0"}#header .details br+span.author:before{content:"\00a0\22c5\00a0";color:rgba(0,0,0,.85)}#header .details br+span#revremark:before{content:"\00a0|\00a0"}#header #revnumber{text-transform:capitalize}#header #revnumber:after{content:"\00a0"}#content>h1:first-child:not([class]){color:rgba(0,0,0,.85);border-bottom:1px solid #ddddd8;padding-bottom:8px;margin-top:0;padding-top:1rem;margin-bottom:1.25rem}#toc{border-bottom:1px solid #efefed;padding-bottom:.5em}#toc>ul{margin-left:.125em;padding-left:1.25em}#toc ul.sectlevel0>li>a{font-style:italic}#toc ul.sectlevel0 ul.sectlevel1{margin:.5em 0}#toc ul{font-family:"Open Sans","DejaVu Sans",sans-serif;list-style-type:none}#toc li{line-height:1.3334;margin-top:.3334em;padding-bottom:4px;padding-top:4px}#toc a{text-decoration:none}#toc a:active{text-decoration:underline}#toctitle{color:#7a2518;font-size:1.2em}@media only screen and (min-width:768px){#toctitle{font-size:1.375em}body.toc2{padding-left:15em;padding-right:0}#toc.toc2{margin-top:0!important;background-color:#f8f8f7;position:fixed;width:15em;left:0;top:0;border-right:1px solid #efefed;border-top-width:0!important;border-bottom-width:0!important;z-index:1000;#padding:1.25em 1em;height:100%;overflow:auto}#toc.toc2 #toctitle{margin-top:0;margin-bottom:.8rem;font-size:1.2em}#toc.toc2>ul{font-size:.9em;margin-bottom:0}#toc.toc2 ul ul{margin-left:0;padding-left:1em}#toc.toc2 ul.sectlevel0 ul.sectlevel1{padding-left:0;margin-top:.5em;margin-bottom:.5em}body.toc2.toc-right{padding-left:0;padding-right:15em}body.toc2.toc-right #toc.toc2{border-right-width:0;border-left:1px solid #efefed;left:auto;right:0}}@media only screen and (min-width:1280px){body.toc2{padding-left:20em;padding-right:0}#toc.toc2{width:20em}#toc.toc2 #toctitle{border-bottom:1px solid rgba(0,0,0,.07);padding-top:20px;padding-bottom:15px}#toc.toc2 #toctitle span{padding-left:1.25em;padding-bottom:15px}#toc.toc2>ul{font-size:.95em}#toc.toc2 ul ul{padding-left:1.25em}body.toc2.toc-right{padding-left:0;padding-right:20em}}#content #toc{border-style:solid;border-width:1px;border-color:#e0e0dc;margin-bottom:1.25em;padding:1.25em;background:#f8f8f7;-webkit-border-radius:4px;border-radius:4px}#content #toc>:first-child{margin-top:0}#content #toc>:last-child{margin-bottom:0}#footer{max-width:100%;background-color:rgba(0,0,0,.8);padding:1.25em}#footer-text{color:rgba(255,255,255,.8);line-height:1.44}.sect1{padding-bottom:.625em}@media only screen and (min-width:768px){.sect1{padding-bottom:1.25em}}.sect1+.sect1{border-top:1px solid #efefed}#content h1>a.anchor,h2>a.anchor,h3>a.anchor,#toctitle>a.anchor,.sidebarblock>.content>.title>a.anchor,h4>a.anchor,h5>a.anchor,h6>a.anchor{position:absolute;z-index:1001;width:1.5ex;margin-left:-1.5ex;display:block;text-decoration:none!important;visibility:hidden;text-align:center;font-weight:400}#content h1>a.anchor:before,h2>a.anchor:before,h3>a.anchor:before,#toctitle>a.anchor:before,.sidebarblock>.content>.title>a.anchor:before,h4>a.anchor:before,h5>a.anchor:before,h6>a.anchor:before{content:"\00A7";font-size:.85em;display:block;padding-top:.1em}#content h1:hover>a.anchor,#content h1>a.anchor:hover,h2:hover>a.anchor,h2>a.anchor:hover,h3:hover>a.anchor,#toctitle:hover>a.anchor,.sidebarblock>.content>.title:hover>a.anchor,h3>a.anchor:hover,#toctitle>a.anchor:hover,.sidebarblock>.content>.title>a.anchor:hover,h4:hover>a.anchor,h4>a.anchor:hover,h5:hover>a.anchor,h5>a.anchor:hover,h6:hover>a.anchor,h6>a.anchor:hover{visibility:visible}#content h1>a.link,h2>a.link,h3>a.link,#toctitle>a.link,.sidebarblock>.content>.title>a.link,h4>a.link,h5>a.link,h6>a.link{color:#ba3925;text-decoration:none}#content h1>a.link:hover,h2>a.link:hover,h3>a.link:hover,#toctitle>a.link:hover,.sidebarblock>.content>.title>a.link:hover,h4>a.link:hover,h5>a.link:hover,h6>a.link:hover{color:#a53221}.audioblock,.imageblock,.literalblock,.listingblock,.stemblock,.videoblock{margin-bottom:1.25em}.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{text-rendering:optimizeLegibility;text-align:left;font-family:"Noto Serif","DejaVu Serif",serif;font-size:1rem;font-style:italic}table.tableblock>caption.title{white-space:nowrap;overflow:visible;max-width:0}.paragraph.lead>p,#preamble>.sectionbody>.paragraph:first-of-type p{color:rgba(0,0,0,.85)}table.tableblock #preamble>.sectionbody>.paragraph:first-of-type p{font-size:inherit}.admonitionblock>table{border-collapse:separate;border:0;background:0;width:100%}.admonitionblock>table td.icon{text-align:center;width:80px}
|
||||
.admonitionblock>table td.icon img{max-width:initial}.admonitionblock>table td.icon .title{font-weight:bold;font-family:"Open Sans","DejaVu Sans",sans-serif;text-transform:uppercase}.admonitionblock>table td.content{padding-left:1.125em;padding-right:1.25em;border-left:1px solid #ddddd8;color:rgba(0,0,0,.6)}.admonitionblock>table td.content>:last-child>:last-child{margin-bottom:0}.exampleblock>.content{border-style:solid;border-width:1px;border-color:#e6e6e6;margin-bottom:1.25em;padding:1.25em;background:#fff;-webkit-border-radius:4px;border-radius:4px}.exampleblock>.content>:first-child{margin-top:0}.exampleblock>.content>:last-child{margin-bottom:0}.sidebarblock{border-style:solid;border-width:1px;border-color:#e0e0dc;margin-bottom:1.25em;padding:1.25em;background:#f8f8f7;-webkit-border-radius:4px;border-radius:4px}.sidebarblock>:first-child{margin-top:0}.sidebarblock>:last-child{margin-bottom:0}.sidebarblock>.content>.title{color:#7a2518;margin-top:0;text-align:center}.exampleblock>.content>:last-child>:last-child,.exampleblock>.content .olist>ol>li:last-child>:last-child,.exampleblock>.content .ulist>ul>li:last-child>:last-child,.exampleblock>.content .qlist>ol>li:last-child>:last-child,.sidebarblock>.content>:last-child>:last-child,.sidebarblock>.content .olist>ol>li:last-child>:last-child,.sidebarblock>.content .ulist>ul>li:last-child>:last-child,.sidebarblock>.content .qlist>ol>li:last-child>:last-child{margin-bottom:0}.literalblock pre,.listingblock pre:not(.highlight),.listingblock pre[class="highlight"],.listingblock pre[class^="highlight "],.listingblock pre.CodeRay,.listingblock pre.prettyprint{background:#f7f7f8}.sidebarblock .literalblock pre,.sidebarblock .listingblock pre:not(.highlight),.sidebarblock .listingblock pre[class="highlight"],.sidebarblock .listingblock pre[class^="highlight "],.sidebarblock .listingblock pre.CodeRay,.sidebarblock .listingblock pre.prettyprint{background:#f2f1f1}.literalblock pre,.literalblock pre[class],.listingblock pre,.listingblock pre[class]{-webkit-border-radius:4px;border-radius:4px;word-wrap:break-word;padding:1em;font-size:.8125em}.literalblock pre.nowrap,.literalblock pre[class].nowrap,.listingblock pre.nowrap,.listingblock pre[class].nowrap{overflow-x:auto;white-space:pre;word-wrap:normal}@media only screen and (min-width:768px){.literalblock pre,.literalblock pre[class],.listingblock pre,.listingblock pre[class]{font-size:.90625em}}@media only screen and (min-width:1280px){.literalblock pre,.literalblock pre[class],.listingblock pre,.listingblock pre[class]{font-size:1em}}.literalblock.output pre{color:#f7f7f8;background-color:rgba(0,0,0,.9)}.listingblock pre.highlightjs{padding:0}.listingblock pre.highlightjs>code{padding:1em;-webkit-border-radius:4px;border-radius:4px}.listingblock pre.prettyprint{border-width:0}.listingblock>.content{position:relative}.listingblock code[data-lang]:before{display:none;content:attr(data-lang);position:absolute;font-size:.75em;top:.425rem;right:.5rem;line-height:1;text-transform:uppercase;color:#999}.listingblock:hover code[data-lang]:before{display:block}.listingblock.terminal pre .command:before{content:attr(data-prompt);padding-right:.5em;color:#999}.listingblock.terminal pre .command:not([data-prompt]):before{content:"$"}table.pyhltable{border-collapse:separate;border:0;margin-bottom:0;background:0}table.pyhltable td{vertical-align:top;padding-top:0;padding-bottom:0;line-height:1.45}table.pyhltable td.code{padding-left:.75em;padding-right:0}pre.pygments .lineno,table.pyhltable td:not(.code){color:#999;padding-left:0;padding-right:.5em;border-right:1px solid #ddddd8}pre.pygments .lineno{display:inline-block;margin-right:.25em}table.pyhltable .linenodiv{background:none!important;padding-right:0!important}.quoteblock{margin:0 1em 1.25em 1.5em;display:table}.quoteblock>.title{margin-left:-1.5em;margin-bottom:.75em}.quoteblock blockquote,.quoteblock blockquote p{color:rgba(0,0,0,.85);font-size:1.15rem;line-height:1.75;word-spacing:.1em;letter-spacing:0;font-style:italic;text-align:justify}.quoteblock blockquote{margin:0;padding:0;border:0}.quoteblock blockquote:before{content:"\201c";float:left;font-size:2.75em;font-weight:bold;line-height:.6em;margin-left:-.6em;color:#7a2518;text-shadow:0 1px 2px rgba(0,0,0,.1)}.quoteblock blockquote>.paragraph:last-child p{margin-bottom:0}.quoteblock .attribution{margin-top:.5em;margin-right:.5ex;text-align:right}.quoteblock .quoteblock{margin-left:0;margin-right:0;padding:.5em 0;border-left:3px solid rgba(0,0,0,.6)}.quoteblock .quoteblock blockquote{padding:0 0 0 .75em}.quoteblock .quoteblock blockquote:before{display:none}.verseblock{margin:0 1em 1.25em 1em}.verseblock pre{font-family:"Open Sans","DejaVu Sans",sans;font-size:1.15rem;color:rgba(0,0,0,.85);font-weight:300;text-rendering:optimizeLegibility}
|
||||
.verseblock pre strong{font-weight:400}.verseblock .attribution{margin-top:1.25rem;margin-left:.5ex}.quoteblock .attribution,.verseblock .attribution{font-size:.9375em;line-height:1.45;font-style:italic}.quoteblock .attribution br,.verseblock .attribution br{display:none}.quoteblock .attribution cite,.verseblock .attribution cite{display:block;letter-spacing:-.025em;color:rgba(0,0,0,.6)}.quoteblock.abstract{margin:0 0 1.25em 0;display:block}.quoteblock.abstract blockquote,.quoteblock.abstract blockquote p{text-align:left;word-spacing:0}.quoteblock.abstract blockquote:before,.quoteblock.abstract blockquote p:first-of-type:before{display:none}table.tableblock{max-width:100%;border-collapse:separate}table.tableblock td>.paragraph:last-child p>p:last-child,table.tableblock th>p:last-child,table.tableblock td>p:last-child{margin-bottom:0}table.tableblock,th.tableblock,td.tableblock{border:0 solid #dedede}table.grid-all>thead>tr>.tableblock,table.grid-all>tbody>tr>.tableblock{border-width:0 1px 1px 0}table.grid-all>tfoot>tr>.tableblock{border-width:1px 1px 0 0}table.grid-cols>*>tr>.tableblock{border-width:0 1px 0 0}table.grid-rows>thead>tr>.tableblock,table.grid-rows>tbody>tr>.tableblock{border-width:0 0 1px 0}table.grid-rows>tfoot>tr>.tableblock{border-width:1px 0 0 0}table.grid-all>*>tr>.tableblock:last-child,table.grid-cols>*>tr>.tableblock:last-child{border-right-width:0}table.grid-all>tbody>tr:last-child>.tableblock,table.grid-all>thead:last-child>tr>.tableblock,table.grid-rows>tbody>tr:last-child>.tableblock,table.grid-rows>thead:last-child>tr>.tableblock{border-bottom-width:0}table.frame-all{border-width:1px}table.frame-sides{border-width:0 1px}table.frame-topbot{border-width:1px 0}th.halign-left,td.halign-left{text-align:left}th.halign-right,td.halign-right{text-align:right}th.halign-center,td.halign-center{text-align:center}th.valign-top,td.valign-top{vertical-align:top}th.valign-bottom,td.valign-bottom{vertical-align:bottom}th.valign-middle,td.valign-middle{vertical-align:middle}table thead th,table tfoot th{font-weight:bold}tbody tr th{display:table-cell;line-height:1.6;background:#f7f8f7}tbody tr th,tbody tr th p,tfoot tr th,tfoot tr th p{color:rgba(0,0,0,.8);font-weight:bold}p.tableblock>code:only-child{background:0;padding:0}p.tableblock{font-size:1em}td>div.verse{white-space:pre}ol{margin-left:1.75em}ul li ol{margin-left:1.5em}dl dd{margin-left:1.125em}dl dd:last-child,dl dd:last-child>:last-child{margin-bottom:0}ol>li p,ul>li p,ul dd,ol dd,.olist .olist,.ulist .ulist,.ulist .olist,.olist .ulist{margin-bottom:.625em}ul.checklist,ul.none,ol.none,ul.no-bullet,ol.no-bullet,ol.unnumbered,ul.unstyled,ol.unstyled{list-style-type:none}ul.no-bullet,ol.no-bullet,ol.unnumbered{margin-left:.625em}ul.unstyled,ol.unstyled{margin-left:0}ul.checklist{margin-left:.625em}ul.checklist li>p:first-child>.fa-square-o:first-child,ul.checklist li>p:first-child>.fa-check-square-o:first-child{width:1.25em;font-size:.8em;position:relative;bottom:.125em}ul.checklist li>p:first-child>input[type="checkbox"]:first-child{margin-right:.25em}ul.inline{margin:0 auto .625em auto;margin-left:-1.375em;margin-right:0;padding:0;list-style:none;overflow:hidden}ul.inline>li{list-style:none;float:left;margin-left:1.375em;display:block}ul.inline>li>*{display:block}.unstyled dl dt{font-weight:400;font-style:normal}ol.arabic{list-style-type:decimal}ol.decimal{list-style-type:decimal-leading-zero}ol.loweralpha{list-style-type:lower-alpha}ol.upperalpha{list-style-type:upper-alpha}ol.lowerroman{list-style-type:lower-roman}ol.upperroman{list-style-type:upper-roman}ol.lowergreek{list-style-type:lower-greek}.hdlist>table,.colist>table{border:0;background:0}.hdlist>table>tbody>tr,.colist>table>tbody>tr{background:0}td.hdlist1,td.hdlist2{vertical-align:top;padding:0 .625em}td.hdlist1{font-weight:bold;padding-bottom:1.25em}.literalblock+.colist,.listingblock+.colist{margin-top:-.5em}.colist>table tr>td:first-of-type{padding:.4em .75em 0 .75em;line-height:1;vertical-align:top}.colist>table tr>td:first-of-type img{max-width:initial}.colist>table tr>td:last-of-type{padding:.25em 0}.thumb,.th{line-height:0;display:inline-block;border:solid 4px #fff;-webkit-box-shadow:0 0 0 1px #ddd;box-shadow:0 0 0 1px #ddd}.imageblock.left,.imageblock[style*="float:left"]{margin:.25em .625em 1.25em 0}.imageblock.right,.imageblock[style*="float:right"]{margin:.25em 0 1.25em .625em}.imageblock>.title{margin-bottom:0}.imageblock.thumb,.imageblock.th{border-width:6px}.imageblock.thumb>.title,.imageblock.th>.title{padding:0 .125em}.image.left,.image.right{margin-top:.25em;margin-bottom:.25em;display:inline-block;line-height:0}.image.left{margin-right:.625em}.image.right{margin-left:.625em}a.image{text-decoration:none;display:inline-block}a.image object{pointer-events:none}sup.footnote,sup.footnoteref{font-size:.875em;position:static;vertical-align:super}sup.footnote a,sup.footnoteref a{text-decoration:none}
|
||||
sup.footnote a:active,sup.footnoteref a:active{text-decoration:underline}#footnotes{padding-top:.75em;padding-bottom:.75em;margin-bottom:.625em}#footnotes hr{width:20%;min-width:6.25em;margin:-.25em 0 .75em 0;border-width:1px 0 0 0}#footnotes .footnote{padding:0 .375em 0 .225em;line-height:1.3334;font-size:.875em;margin-left:1.2em;text-indent:-1.05em;margin-bottom:.2em}#footnotes .footnote a:first-of-type{font-weight:bold;text-decoration:none}#footnotes .footnote:last-of-type{margin-bottom:0}#content #footnotes{margin-top:-.625em;margin-bottom:0;padding:.75em 0}.gist .file-data>table{border:0;background:#fff;width:100%;margin-bottom:0}.gist .file-data>table td.line-data{width:99%}div.unbreakable{page-break-inside:avoid}.big{font-size:larger}.small{font-size:smaller}.underline{text-decoration:underline}.overline{text-decoration:overline}.line-through{text-decoration:line-through}.aqua{color:#00bfbf}.aqua-background{background-color:#00fafa}.black{color:#000}.black-background{background-color:#000}.blue{color:#0000bf}.blue-background{background-color:#0000fa}.fuchsia{color:#bf00bf}.fuchsia-background{background-color:#fa00fa}.gray{color:#606060}.gray-background{background-color:#7d7d7d}.green{color:#006000}.green-background{background-color:#007d00}.lime{color:#00bf00}.lime-background{background-color:#00fa00}.maroon{color:#600000}.maroon-background{background-color:#7d0000}.navy{color:#000060}.navy-background{background-color:#00007d}.olive{color:#606000}.olive-background{background-color:#7d7d00}.purple{color:#600060}.purple-background{background-color:#7d007d}.red{color:#bf0000}.red-background{background-color:#fa0000}.silver{color:#909090}.silver-background{background-color:#bcbcbc}.teal{color:#006060}.teal-background{background-color:#007d7d}.white{color:#bfbfbf}.white-background{background-color:#fafafa}.yellow{color:#bfbf00}.yellow-background{background-color:#fafa00}span.icon>.fa{cursor:default}a span.icon>.fa{cursor:inherit}.admonitionblock td.icon [class^="fa icon-"]{font-size:2.5em;text-shadow:1px 1px 2px rgba(0,0,0,.5);cursor:default}.admonitionblock td.icon .icon-note:before{content:"\f05a";color:#19407c}.admonitionblock td.icon .icon-tip:before{content:"\f0eb";text-shadow:1px 1px 2px rgba(155,155,0,.8);color:#111}.admonitionblock td.icon .icon-warning:before{content:"\f071";color:#bf6900}.admonitionblock td.icon .icon-caution:before{content:"\f06d";color:#bf3400}.admonitionblock td.icon .icon-important:before{content:"\f06a";color:#bf0000}.conum[data-value]{display:inline-block;color:#fff!important;background-color:rgba(0,0,0,.8);-webkit-border-radius:100px;border-radius:100px;text-align:center;font-size:.75em;width:1.67em;height:1.67em;line-height:1.67em;font-family:"Open Sans","DejaVu Sans",sans-serif;font-style:normal;font-weight:bold}.conum[data-value] *{color:#fff!important}.conum[data-value]+b{display:none}.conum[data-value]:after{content:attr(data-value)}pre .conum[data-value]{position:relative;top:-.125em}b.conum *{color:inherit!important}.conum:not([data-value]):empty{display:none}dt,th.tableblock,td.content,div.footnote{text-rendering:optimizeLegibility}h1,h2,p,td.content,span.alt{letter-spacing:-.01em}p strong,td.content strong,div.footnote strong{letter-spacing:-.005em}p,blockquote,dt,td.content,span.alt{font-size:1.0625rem}p{margin-bottom:1.25rem}.sidebarblock p,.sidebarblock dt,.sidebarblock td.content,p.tableblock{font-size:1em}.exampleblock>.content{background-color:#fffef7;border-color:#e0e0dc;-webkit-box-shadow:0 1px 4px #e0e0dc;box-shadow:0 1px 4px #e0e0dc}.print-only{display:none!important}@media print{@page{margin:1.25cm .75cm}*{-webkit-box-shadow:none!important;box-shadow:none!important;text-shadow:none!important}a{color:inherit!important;text-decoration:underline!important}a.bare,a[href^="#"],a[href^="mailto:"]{text-decoration:none!important}a[href^="http:"]:not(.bare):after,a[href^="https:"]:not(.bare):after{content:"(" attr(href) ")";display:inline-block;font-size:.875em;padding-left:.25em}abbr[title]:after{content:" (" attr(title) ")"}pre,blockquote,tr,img,object,svg{page-break-inside:avoid}thead{display:table-header-group}svg{max-width:100%}p,blockquote,dt,td.content{font-size:1em;orphans:3;widows:3}h2,h3,#toctitle,.sidebarblock>.content>.title{page-break-after:avoid}#toc,.sidebarblock,.exampleblock>.content{background:none!important}#toc{border-bottom:1px solid #ddddd8!important;padding-bottom:0!important}.sect1{padding-bottom:0!important}.sect1+.sect1{border:0!important}#header>h1:first-child{margin-top:1.25rem}
|
||||
body.book #header{text-align:center}body.book #header>h1:first-child{border:0!important;margin:2.5em 0 1em 0}body.book #header .details{border:0!important;display:block;padding:0!important}body.book #header .details span:first-child{margin-left:0!important}body.book #header .details br{display:block}body.book #header .details br+span:before{content:none!important}body.book #toc{border:0!important;text-align:left!important;padding:0!important;margin:0!important}body.book #toc,body.book #preamble,body.book h1.sect0,body.book .sect1>h2{page-break-before:always}.listingblock code[data-lang]:before{display:block}#footer{background:none!important;padding:0 .9375em}#footer-text{color:rgba(0,0,0,.6)!important;font-size:.9em}.hide-on-print{display:none!important}.print-only{display:block!important}.hide-for-print{display:none!important}.show-for-print{display:inherit!important}}#content .page-footer{height:100px;border-top:1px solid #ccc;overflow:hidden;padding:10px 0;font-size:14px;color:gray}#content .footer-modification{float:right}#content .footer-modification a{text-decoration:none}.sectlevel2{display:none}.submenu{background:#e7e7e6}.submenu li{border:0}.submenu a{color:#555}.checkbox{position:relative;height:30px}.checkbox input[type='checkbox']{left:0;top:0;width:20px;height:20px;opacity:0;border-radius:4px}.checkbox label{position:absolute;left:30px;top:0;height:20px;line-height:20px}.checkbox label:before{content:'';position:absolute;left:-30px;top:2px;width:20px;height:20px;border:1px solid #ddd;border-radius:4px;transition:all .3s ease;-webkit-transition:all .3s ease;-moz-transition:all .3s ease}.checkbox label:after{content:'';position:absolute;left:-22px;top:3px;width:6px;height:12px;border:0;border-right:1px solid #fff;border-bottom:1px solid #fff;border-radius:2px;transform:rotate(45deg);-webkit-transform:rotate(45deg);-moz-transform:rotate(45deg);-ms-transform:rotate(45deg);transition:all .3s ease;-webkit-transition:all .3s ease;-moz-transition:all .3s ease}.checkbox input[type='checkbox']:checked+label:before{background:#4cd764;border-color:#4cd764}.checkbox input[type='checkbox']:checked+label:after{background:#4cd764}.send-button{color:#fff;background-color:#5cb85c;display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent;border-radius:4px;outline-color:transparent}textarea{width:100%;background-color:#f7f7f8;border:1px solid #f7f7f8;border-radius:4px;font-size:1em;padding:1em;font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;outline-color:#dedede}input{border:0;background-color:transparent;outline-color:transparent;outline-style:dotted;max-width:100%}#book-search-input{padding:13px;background:0;transition:top .5s ease;border-bottom:1px solid rgba(0,0,0,.07);border-top:1px solid rgba(0,0,0,.07);margin-top:-1px}#book-search-input input,#book-search-input input:focus,#book-search-input input:hover{width:100%;background:0;border:1px solid transparent;box-shadow:none;outline:0;line-height:22px;padding:7px 7px;color:inherit}[contenteditable="plaintext-only"]:focus{border:0;outline:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,video{display:inline-block}audio:not([controls]){display:none;height:0}[hidden],template{display:none}script{display:none!important}html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}a{background:transparent}a:focus{outline:thin dotted}a:active,a:hover{outline:0}h1{font-size:2em;margin:.67em 0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}dfn{font-style:italic}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}mark{background:#ff0;color:#000}code,kbd,pre,samp{font-family:monospace;font-size:1em}pre{white-space:pre-wrap}q{quotes:"\201C" "\201D" "\2018" "\2019"}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:0}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}button,input,select,textarea{font-family:inherit;font-size:100%;margin:0}button,input{line-height:normal}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}
|
||||
button[disabled],html input[disabled]{cursor:default}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}textarea{overflow:auto;vertical-align:top}table{border-collapse:collapse;border-spacing:0}*,*:before,*:after{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}html,body{font-size:100%}body{background:#fff;color:rgba(0,0,0,.8);padding:0;margin:0;font-family:"Noto Serif","DejaVu Serif",serif;font-weight:400;font-style:normal;line-height:1;position:relative;cursor:auto;tab-size:4;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased}a:hover{cursor:pointer}img,object,embed{max-width:100%;height:auto}object,embed{height:100%}img{-ms-interpolation-mode:bicubic}.left{float:left!important}.right{float:right!important}.text-left{text-align:left!important}.text-right{text-align:right!important}.text-center{text-align:center!important}.text-justify{text-align:justify!important}.hide{display:none}img,object,svg{display:inline-block;vertical-align:middle}textarea{height:auto;min-height:50px}select{width:100%}.center{margin-left:auto;margin-right:auto}.spread{width:100%}p.lead,.paragraph.lead>p,#preamble>.sectionbody>.paragraph:first-of-type p{font-size:1.21875em;line-height:1.6}.subheader,.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{line-height:1.45;color:#7a2518;font-weight:400;margin-top:0;margin-bottom:.25em}div,dl,dt,dd,ul,ol,li,h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6,pre,form,p,blockquote,th,td{margin:0;padding:0;direction:ltr}a{color:#364149;text-decoration:underline;line-height:inherit}a:hover,a:focus{color:#364149}a img{border:0}p{font-family:inherit;font-weight:400;font-size:1em;line-height:1.6;margin-bottom:1.25em;text-rendering:optimizeLegibility}p aside{font-size:.875em;line-height:1.35;font-style:italic}h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{font-family:"Open Sans","DejaVu Sans",sans-serif;font-weight:300;font-style:normal;color:#ba3925;text-rendering:optimizeLegibility;margin-top:1em;margin-bottom:.5em;line-height:1.0125em}h1 small,h2 small,h3 small,#toctitle small,.sidebarblock>.content>.title small,h4 small,h5 small,h6 small{font-size:60%;color:#e99b8f;line-height:0}h1{font-size:2.125em}h2{font-size:1.6875em}h3,#toctitle,.sidebarblock>.content>.title{font-size:1.375em}h4,h5{font-size:1.125em}h6{font-size:1em}hr{border:solid #ddddd8;border-width:1px 0 0;clear:both;margin:1.25em 0 1.1875em;height:0}em,i{font-style:italic;line-height:inherit}strong,b{font-weight:bold;line-height:inherit}small{font-size:60%;line-height:inherit}code{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;font-weight:400;color:rgba(0,0,0,.9)}ul,ol,dl{font-size:1em;line-height:1.6;margin-bottom:1.25em;list-style-position:outside;font-family:inherit}ul,ol{margin-left:1.5em}ul li ul,ul li ol{margin-left:1.25em;margin-bottom:0;font-size:1em}ul.square li ul,ul.circle li ul,ul.disc li ul{list-style:inherit}ul.square{list-style-type:square}ul.circle{list-style-type:circle}ul.disc{list-style-type:disc}ol li ul,ol li ol{margin-left:1.25em;margin-bottom:0}dl dt{margin-bottom:.3125em;font-weight:bold}dl dd{margin-bottom:1.25em}abbr,acronym{text-transform:uppercase;font-size:90%;color:rgba(0,0,0,.8);border-bottom:1px dotted #ddd;cursor:help}abbr{text-transform:none}blockquote{margin:0 0 1.25em;padding:.5625em 1.25em 0 1.1875em;border-left:1px solid #ddd}blockquote cite{display:block;font-size:.9375em;color:rgba(0,0,0,.6)}blockquote cite:before{content:"\2014 \0020"}blockquote cite a,blockquote cite a:visited{color:rgba(0,0,0,.6)}blockquote,blockquote p{line-height:1.6;color:rgba(0,0,0,.85)}@media only screen and (min-width:768px){h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2}h1{font-size:2.75em}h2{font-size:2.3125em}h3,#toctitle,.sidebarblock>.content>.title{font-size:1.6875em}h4{font-size:1.4375em}}table{background:#fff;margin-bottom:1.25em;border:solid 1px #dedede}table thead,table tfoot{background:#f7f8f7;font-weight:bold}table thead tr th,table thead tr td,table tfoot tr th,table tfoot tr td{padding:.5em .625em .625em;font-size:inherit;color:rgba(0,0,0,.8);text-align:left}
|
||||
table tr th,table tr td{padding:.5625em .625em;font-size:inherit;color:rgba(0,0,0,.8)}table tr.even,table tr.alt,table tr:nth-of-type(even){background:#f8f8f7}table thead tr th,table tfoot tr th,table tbody tr td,table tr td,table tfoot tr td{display:table-cell;line-height:1.6}h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2;word-spacing:-.05em}h1 strong,h2 strong,h3 strong,#toctitle strong,.sidebarblock>.content>.title strong,h4 strong,h5 strong,h6 strong{font-weight:400}.clearfix:before,.clearfix:after,.float-group:before,.float-group:after{content:" ";display:table}.clearfix:after,.float-group:after{clear:both}*:not(pre)>code{font-size:.9375em;font-style:normal!important;letter-spacing:0;padding:.1em .5ex;word-spacing:-.15em;background-color:#f7f7f8;-webkit-border-radius:4px;border-radius:4px;line-height:1.45;text-rendering:optimizeSpeed;word-wrap:break-word}*:not(pre)>code.nobreak{word-wrap:normal}*:not(pre)>code.nowrap{white-space:nowrap}pre,pre>code{line-height:1.45;color:rgba(0,0,0,.9);font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;font-weight:400;text-rendering:optimizeSpeed}em em{font-style:normal}strong strong{font-weight:400}.keyseq{color:rgba(51,51,51,.8)}kbd{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;display:inline-block;color:rgba(0,0,0,.8);font-size:.65em;line-height:1.45;background-color:#f7f7f7;border:1px solid #ccc;-webkit-border-radius:3px;border-radius:3px;-webkit-box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em white inset;box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em #fff inset;margin:0 .15em;padding:.2em .5em;vertical-align:middle;position:relative;top:-.1em;white-space:nowrap}.keyseq kbd:first-child{margin-left:0}.keyseq kbd:last-child{margin-right:0}.menuseq,.menuref{color:#000}.menuseq b:not(.caret),.menuref{font-weight:inherit}.menuseq{word-spacing:-.02em}.menuseq b.caret{font-size:1.25em;line-height:.8}.menuseq i.caret{font-weight:bold;text-align:center;width:.45em}b.button:before,b.button:after{position:relative;top:-1px;font-weight:400}b.button:before{content:"[";padding:0 3px 0 2px}b.button:after{content:"]";padding:0 2px 0 3px}p a>code:hover{color:rgba(0,0,0,.9)}#header,#content,#footnotes,#footer{width:100%;margin-left:auto;margin-right:auto;margin-top:0;margin-bottom:0;max-width:62.5em;*zoom:1;position:relative;padding-left:.9375em;padding-right:.9375em}#header:before,#header:after,#content:before,#content:after,#footnotes:before,#footnotes:after,#footer:before,#footer:after{content:" ";display:table}#header:after,#content:after,#footnotes:after,#footer:after{clear:both}#content{margin-top:1.25em}#content:before{content:none}#header>h1:first-child{color:rgba(0,0,0,.85);margin-top:2.25rem;margin-bottom:0}#header>h1:first-child+#toc{margin-top:8px;border-top:1px solid #ddddd8}#header>h1:only-child,body.toc2 #header>h1:nth-last-child(2){border-bottom:1px solid #ddddd8;padding-bottom:8px}#header .details{border-bottom:1px solid #ddddd8;line-height:1.45;padding-top:.25em;padding-bottom:.25em;padding-left:.25em;color:rgba(0,0,0,.6);display:-ms-flexbox;display:-webkit-flex;display:flex;-ms-flex-flow:row wrap;-webkit-flex-flow:row wrap;flex-flow:row wrap}#header .details span:first-child{margin-left:-.125em}#header .details span.email a{color:rgba(0,0,0,.85)}#header .details br{display:none}#header .details br+span:before{content:"\00a0\2013\00a0"}#header .details br+span.author:before{content:"\00a0\22c5\00a0";color:rgba(0,0,0,.85)}#header .details br+span#revremark:before{content:"\00a0|\00a0"}#header #revnumber{text-transform:capitalize}#header #revnumber:after{content:"\00a0"}#content>h1:first-child:not([class]){color:rgba(0,0,0,.85);border-bottom:1px solid #ddddd8;padding-bottom:8px;margin-top:0;padding-top:1rem;margin-bottom:1.25rem}#toc{border-bottom:1px solid #efefed;padding-bottom:.5em}#toc>ul{margin-left:.125em;padding-left:1.25em}#toc ul.sectlevel0>li>a{font-style:italic}#toc ul.sectlevel0 ul.sectlevel1{margin:.5em 0}#toc ul{font-family:"Open Sans","DejaVu Sans",sans-serif;list-style-type:none}#toc li{line-height:1.3334;margin-top:.3334em;padding-bottom:4px;padding-top:4px}#toc a{text-decoration:none}#toc a:active{text-decoration:underline}#toctitle{color:#7a2518;font-size:1.2em}@media only screen and (min-width:768px){#toctitle{font-size:1.375em}body.toc2{padding-left:15em;padding-right:0}#toc.toc2{margin-top:0!important;background-color:#f8f8f7;position:fixed;width:15em;left:0;top:0;border-right:1px solid #efefed;border-top-width:0!important;border-bottom-width:0!important;z-index:1000;#padding:1.25em 1em;height:100%;overflow:auto}#toc.toc2 #toctitle{margin-top:0;margin-bottom:.8rem;font-size:1.2em}
|
||||
#toc.toc2>ul{font-size:.9em;margin-bottom:0}#toc.toc2 ul ul{margin-left:0;padding-left:1em}#toc.toc2 ul.sectlevel0 ul.sectlevel1{padding-left:0;margin-top:.5em;margin-bottom:.5em}body.toc2.toc-right{padding-left:0;padding-right:15em}body.toc2.toc-right #toc.toc2{border-right-width:0;border-left:1px solid #efefed;left:auto;right:0}}@media only screen and (min-width:1280px){body.toc2{padding-left:20em;padding-right:0}#toc.toc2{width:20em}#toc.toc2 #toctitle{font-size:1.375em;border-bottom:1px solid rgba(0,0,0,.07);padding-top:20px;padding-bottom:15px}#toc.toc2 #toctitle span{padding-left:1.25em;padding-bottom:15px}#toc.toc2>ul{font-size:.95em}#toc.toc2 ul ul{padding-left:1.25em}body.toc2.toc-right{padding-left:0;padding-right:20em}}#content #toc{border-style:solid;border-width:1px;border-color:#e0e0dc;margin-bottom:1.25em;padding:1.25em;background:#f8f8f7;-webkit-border-radius:4px;border-radius:4px}#content #toc>:first-child{margin-top:0}#content #toc>:last-child{margin-bottom:0}#footer{max-width:100%;background-color:rgba(0,0,0,.8);padding:1.25em}#footer-text{color:rgba(255,255,255,.8);line-height:1.44}.sect1{padding-bottom:.625em}@media only screen and (min-width:768px){.sect1{padding-bottom:1.25em}}.sect1+.sect1{border-top:1px solid #efefed}#content h1>a.anchor,h2>a.anchor,h3>a.anchor,#toctitle>a.anchor,.sidebarblock>.content>.title>a.anchor,h4>a.anchor,h5>a.anchor,h6>a.anchor{position:absolute;z-index:1001;width:1.5ex;margin-left:-1.5ex;display:block;text-decoration:none!important;visibility:hidden;text-align:center;font-weight:400}#content h1>a.anchor:before,h2>a.anchor:before,h3>a.anchor:before,#toctitle>a.anchor:before,.sidebarblock>.content>.title>a.anchor:before,h4>a.anchor:before,h5>a.anchor:before,h6>a.anchor:before{content:"\00A7";font-size:.85em;display:block;padding-top:.1em}#content h1:hover>a.anchor,#content h1>a.anchor:hover,h2:hover>a.anchor,h2>a.anchor:hover,h3:hover>a.anchor,#toctitle:hover>a.anchor,.sidebarblock>.content>.title:hover>a.anchor,h3>a.anchor:hover,#toctitle>a.anchor:hover,.sidebarblock>.content>.title>a.anchor:hover,h4:hover>a.anchor,h4>a.anchor:hover,h5:hover>a.anchor,h5>a.anchor:hover,h6:hover>a.anchor,h6>a.anchor:hover{visibility:visible}#content h1>a.link,h2>a.link,h3>a.link,#toctitle>a.link,.sidebarblock>.content>.title>a.link,h4>a.link,h5>a.link,h6>a.link{color:#ba3925;text-decoration:none}#content h1>a.link:hover,h2>a.link:hover,h3>a.link:hover,#toctitle>a.link:hover,.sidebarblock>.content>.title>a.link:hover,h4>a.link:hover,h5>a.link:hover,h6>a.link:hover{color:#a53221}.audioblock,.imageblock,.literalblock,.listingblock,.stemblock,.videoblock{margin-bottom:1.25em}.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{text-rendering:optimizeLegibility;text-align:left;font-family:"Noto Serif","DejaVu Serif",serif;font-size:1rem;font-style:italic}table.tableblock>caption.title{white-space:nowrap;overflow:visible;max-width:0}.paragraph.lead>p,#preamble>.sectionbody>.paragraph:first-of-type p{color:rgba(0,0,0,.85)}table.tableblock #preamble>.sectionbody>.paragraph:first-of-type p{font-size:inherit}.admonitionblock>table{border-collapse:separate;border:0;background:0;width:100%}.admonitionblock>table td.icon{text-align:center;width:80px}.admonitionblock>table td.icon img{max-width:initial}.admonitionblock>table td.icon .title{font-weight:bold;font-family:"Open Sans","DejaVu Sans",sans-serif;text-transform:uppercase}.admonitionblock>table td.content{padding-left:1.125em;padding-right:1.25em;border-left:1px solid #ddddd8;color:rgba(0,0,0,.6)}.admonitionblock>table td.content>:last-child>:last-child{margin-bottom:0}.exampleblock>.content{border-style:solid;border-width:1px;border-color:#e6e6e6;margin-bottom:1.25em;padding:1.25em;background:#fff;-webkit-border-radius:4px;border-radius:4px}.exampleblock>.content>:first-child{margin-top:0}.exampleblock>.content>:last-child{margin-bottom:0}.sidebarblock{border-style:solid;border-width:1px;border-color:#e0e0dc;margin-bottom:1.25em;padding:1.25em;background:#f8f8f7;-webkit-border-radius:4px;border-radius:4px}.sidebarblock>:first-child{margin-top:0}.sidebarblock>:last-child{margin-bottom:0}.sidebarblock>.content>.title{color:#7a2518;margin-top:0;text-align:center}.exampleblock>.content>:last-child>:last-child,.exampleblock>.content .olist>ol>li:last-child>:last-child,.exampleblock>.content .ulist>ul>li:last-child>:last-child,.exampleblock>.content .qlist>ol>li:last-child>:last-child,.sidebarblock>.content>:last-child>:last-child,.sidebarblock>.content .olist>ol>li:last-child>:last-child,.sidebarblock>.content .ulist>ul>li:last-child>:last-child,.sidebarblock>.content .qlist>ol>li:last-child>:last-child{margin-bottom:0}
|
||||
.literalblock pre,.listingblock pre:not(.highlight),.listingblock pre[class="highlight"],.listingblock pre[class^="highlight "],.listingblock pre.CodeRay,.listingblock pre.prettyprint{background:#f7f7f8}.sidebarblock .literalblock pre,.sidebarblock .listingblock pre:not(.highlight),.sidebarblock .listingblock pre[class="highlight"],.sidebarblock .listingblock pre[class^="highlight "],.sidebarblock .listingblock pre.CodeRay,.sidebarblock .listingblock pre.prettyprint{background:#f2f1f1}.literalblock pre,.literalblock pre[class],.listingblock pre,.listingblock pre[class]{-webkit-border-radius:4px;border-radius:4px;word-wrap:break-word;padding:1em;font-size:.8125em}.literalblock pre.nowrap,.literalblock pre[class].nowrap,.listingblock pre.nowrap,.listingblock pre[class].nowrap{overflow-x:auto;white-space:pre;word-wrap:normal}@media only screen and (min-width:768px){.literalblock pre,.literalblock pre[class],.listingblock pre,.listingblock pre[class]{font-size:.90625em}}@media only screen and (min-width:1280px){.literalblock pre,.literalblock pre[class],.listingblock pre,.listingblock pre[class]{font-size:1em}}.literalblock.output pre{color:#f7f7f8;background-color:rgba(0,0,0,.9)}.listingblock pre.highlightjs{padding:0}.listingblock pre.highlightjs>code{padding:1em;-webkit-border-radius:4px;border-radius:4px}.listingblock pre.prettyprint{border-width:0}.listingblock>.content{position:relative}.listingblock code[data-lang]:before{display:none;content:attr(data-lang);position:absolute;font-size:.75em;top:.425rem;right:.5rem;line-height:1;text-transform:uppercase;color:#999}.listingblock:hover code[data-lang]:before{display:block}.listingblock.terminal pre .command:before{content:attr(data-prompt);padding-right:.5em;color:#999}.listingblock.terminal pre .command:not([data-prompt]):before{content:"$"}table.pyhltable{border-collapse:separate;border:0;margin-bottom:0;background:0}table.pyhltable td{vertical-align:top;padding-top:0;padding-bottom:0;line-height:1.45}table.pyhltable td.code{padding-left:.75em;padding-right:0}pre.pygments .lineno,table.pyhltable td:not(.code){color:#999;padding-left:0;padding-right:.5em;border-right:1px solid #ddddd8}pre.pygments .lineno{display:inline-block;margin-right:.25em}table.pyhltable .linenodiv{background:none!important;padding-right:0!important}.quoteblock{margin:0 1em 1.25em 1.5em;display:table}.quoteblock>.title{margin-left:-1.5em;margin-bottom:.75em}.quoteblock blockquote,.quoteblock blockquote p{color:rgba(0,0,0,.85);font-size:1.15rem;line-height:1.75;word-spacing:.1em;letter-spacing:0;font-style:italic;text-align:justify}.quoteblock blockquote{margin:0;padding:0;border:0}.quoteblock blockquote:before{content:"\201c";float:left;font-size:2.75em;font-weight:bold;line-height:.6em;margin-left:-.6em;color:#7a2518;text-shadow:0 1px 2px rgba(0,0,0,.1)}.quoteblock blockquote>.paragraph:last-child p{margin-bottom:0}.quoteblock .attribution{margin-top:.5em;margin-right:.5ex;text-align:right}.quoteblock .quoteblock{margin-left:0;margin-right:0;padding:.5em 0;border-left:3px solid rgba(0,0,0,.6)}.quoteblock .quoteblock blockquote{padding:0 0 0 .75em}.quoteblock .quoteblock blockquote:before{display:none}.verseblock{margin:0 1em 1.25em 1em}.verseblock pre{font-family:"Open Sans","DejaVu Sans",sans;font-size:1.15rem;color:rgba(0,0,0,.85);font-weight:300;text-rendering:optimizeLegibility}.verseblock pre strong{font-weight:400}.verseblock .attribution{margin-top:1.25rem;margin-left:.5ex}.quoteblock .attribution,.verseblock .attribution{font-size:.9375em;line-height:1.45;font-style:italic}.quoteblock .attribution br,.verseblock .attribution br{display:none}.quoteblock .attribution cite,.verseblock .attribution cite{display:block;letter-spacing:-.025em;color:rgba(0,0,0,.6)}.quoteblock.abstract{margin:0 0 1.25em 0;display:block}.quoteblock.abstract blockquote,.quoteblock.abstract blockquote p{text-align:left;word-spacing:0}.quoteblock.abstract blockquote:before,.quoteblock.abstract blockquote p:first-of-type:before{display:none}table.tableblock{max-width:100%;border-collapse:separate}table.tableblock td>.paragraph:last-child p>p:last-child,table.tableblock th>p:last-child,table.tableblock td>p:last-child{margin-bottom:0}table.tableblock,th.tableblock,td.tableblock{border:0 solid #dedede}table.grid-all>thead>tr>.tableblock,table.grid-all>tbody>tr>.tableblock{border-width:0 1px 1px 0}table.grid-all>tfoot>tr>.tableblock{border-width:1px 1px 0 0}table.grid-cols>*>tr>.tableblock{border-width:0 1px 0 0}table.grid-rows>thead>tr>.tableblock,table.grid-rows>tbody>tr>.tableblock{border-width:0 0 1px 0}table.grid-rows>tfoot>tr>.tableblock{border-width:1px 0 0 0}table.grid-all>*>tr>.tableblock:last-child,table.grid-cols>*>tr>.tableblock:last-child{border-right-width:0}table.grid-all>tbody>tr:last-child>.tableblock,table.grid-all>thead:last-child>tr>.tableblock,table.grid-rows>tbody>tr:last-child>.tableblock,table.grid-rows>thead:last-child>tr>.tableblock{border-bottom-width:0}
|
||||
table.frame-all{border-width:1px}table.frame-sides{border-width:0 1px}table.frame-topbot{border-width:1px 0}th.halign-left,td.halign-left{text-align:left}th.halign-right,td.halign-right{text-align:right}th.halign-center,td.halign-center{text-align:center}th.valign-top,td.valign-top{vertical-align:top}th.valign-bottom,td.valign-bottom{vertical-align:bottom}th.valign-middle,td.valign-middle{vertical-align:middle}table thead th,table tfoot th{font-weight:bold}tbody tr th{display:table-cell;line-height:1.6;background:#f7f8f7}tbody tr th,tbody tr th p,tfoot tr th,tfoot tr th p{color:rgba(0,0,0,.8);font-weight:bold}p.tableblock>code:only-child{background:0;padding:0}p.tableblock{font-size:1em}td>div.verse{white-space:pre}ol{margin-left:1.75em}ul li ol{margin-left:1.5em}dl dd{margin-left:1.125em}dl dd:last-child,dl dd:last-child>:last-child{margin-bottom:0}ol>li p,ul>li p,ul dd,ol dd,.olist .olist,.ulist .ulist,.ulist .olist,.olist .ulist{margin-bottom:.625em}ul.checklist,ul.none,ol.none,ul.no-bullet,ol.no-bullet,ol.unnumbered,ul.unstyled,ol.unstyled{list-style-type:none}ul.no-bullet,ol.no-bullet,ol.unnumbered{margin-left:.625em}ul.unstyled,ol.unstyled{margin-left:0}ul.checklist{margin-left:.625em}ul.checklist li>p:first-child>.fa-square-o:first-child,ul.checklist li>p:first-child>.fa-check-square-o:first-child{width:1.25em;font-size:.8em;position:relative;bottom:.125em}ul.checklist li>p:first-child>input[type="checkbox"]:first-child{margin-right:.25em}ul.inline{margin:0 auto .625em auto;margin-left:-1.375em;margin-right:0;padding:0;list-style:none;overflow:hidden}ul.inline>li{list-style:none;float:left;margin-left:1.375em;display:block}ul.inline>li>*{display:block}.unstyled dl dt{font-weight:400;font-style:normal}ol.arabic{list-style-type:decimal}ol.decimal{list-style-type:decimal-leading-zero}ol.loweralpha{list-style-type:lower-alpha}ol.upperalpha{list-style-type:upper-alpha}ol.lowerroman{list-style-type:lower-roman}ol.upperroman{list-style-type:upper-roman}ol.lowergreek{list-style-type:lower-greek}.hdlist>table,.colist>table{border:0;background:0}.hdlist>table>tbody>tr,.colist>table>tbody>tr{background:0}td.hdlist1,td.hdlist2{vertical-align:top;padding:0 .625em}td.hdlist1{font-weight:bold;padding-bottom:1.25em}.literalblock+.colist,.listingblock+.colist{margin-top:-.5em}.colist>table tr>td:first-of-type{padding:.4em .75em 0 .75em;line-height:1;vertical-align:top}.colist>table tr>td:first-of-type img{max-width:initial}.colist>table tr>td:last-of-type{padding:.25em 0}.thumb,.th{line-height:0;display:inline-block;border:solid 4px #fff;-webkit-box-shadow:0 0 0 1px #ddd;box-shadow:0 0 0 1px #ddd}.imageblock.left,.imageblock[style*="float:left"]{margin:.25em .625em 1.25em 0}.imageblock.right,.imageblock[style*="float:right"]{margin:.25em 0 1.25em .625em}.imageblock>.title{margin-bottom:0}.imageblock.thumb,.imageblock.th{border-width:6px}.imageblock.thumb>.title,.imageblock.th>.title{padding:0 .125em}.image.left,.image.right{margin-top:.25em;margin-bottom:.25em;display:inline-block;line-height:0}.image.left{margin-right:.625em}.image.right{margin-left:.625em}a.image{text-decoration:none;display:inline-block}a.image object{pointer-events:none}sup.footnote,sup.footnoteref{font-size:.875em;position:static;vertical-align:super}sup.footnote a,sup.footnoteref a{text-decoration:none}sup.footnote a:active,sup.footnoteref a:active{text-decoration:underline}#footnotes{padding-top:.75em;padding-bottom:.75em;margin-bottom:.625em}#footnotes hr{width:20%;min-width:6.25em;margin:-.25em 0 .75em 0;border-width:1px 0 0 0}#footnotes .footnote{padding:0 .375em 0 .225em;line-height:1.3334;font-size:.875em;margin-left:1.2em;text-indent:-1.05em;margin-bottom:.2em}#footnotes .footnote a:first-of-type{font-weight:bold;text-decoration:none}#footnotes .footnote:last-of-type{margin-bottom:0}#content #footnotes{margin-top:-.625em;margin-bottom:0;padding:.75em 0}.gist .file-data>table{border:0;background:#fff;width:100%;margin-bottom:0}.gist .file-data>table td.line-data{width:99%}div.unbreakable{page-break-inside:avoid}.big{font-size:larger}.small{font-size:smaller}.underline{text-decoration:underline}.overline{text-decoration:overline}.line-through{text-decoration:line-through}.aqua{color:#00bfbf}.aqua-background{background-color:#00fafa}.black{color:#000}.black-background{background-color:#000}.blue{color:#0000bf}.blue-background{background-color:#0000fa}.fuchsia{color:#bf00bf}.fuchsia-background{background-color:#fa00fa}.gray{color:#606060}.gray-background{background-color:#7d7d7d}.green{color:#006000}.green-background{background-color:#007d00}.lime{color:#00bf00}.lime-background{background-color:#00fa00}.maroon{color:#600000}.maroon-background{background-color:#7d0000}.navy{color:#000060}.navy-background{background-color:#00007d}.olive{color:#606000}.olive-background{background-color:#7d7d00}.purple{color:#600060}.purple-background{background-color:#7d007d}.red{color:#bf0000}
|
||||
.red-background{background-color:#fa0000}.silver{color:#909090}.silver-background{background-color:#bcbcbc}.teal{color:#006060}.teal-background{background-color:#007d7d}.white{color:#bfbfbf}.white-background{background-color:#fafafa}.yellow{color:#bfbf00}.yellow-background{background-color:#fafa00}span.icon>.fa{cursor:default}a span.icon>.fa{cursor:inherit}.admonitionblock td.icon [class^="fa icon-"]{font-size:2.5em;text-shadow:1px 1px 2px rgba(0,0,0,.5);cursor:default}.admonitionblock td.icon .icon-note:before{content:"\f05a";color:#19407c}.admonitionblock td.icon .icon-tip:before{content:"\f0eb";text-shadow:1px 1px 2px rgba(155,155,0,.8);color:#111}.admonitionblock td.icon .icon-warning:before{content:"\f071";color:#bf6900}.admonitionblock td.icon .icon-caution:before{content:"\f06d";color:#bf3400}.admonitionblock td.icon .icon-important:before{content:"\f06a";color:#bf0000}.conum[data-value]{display:inline-block;color:#fff!important;background-color:rgba(0,0,0,.8);-webkit-border-radius:100px;border-radius:100px;text-align:center;font-size:.75em;width:1.67em;height:1.67em;line-height:1.67em;font-family:"Open Sans","DejaVu Sans",sans-serif;font-style:normal;font-weight:bold}.conum[data-value] *{color:#fff!important}.conum[data-value]+b{display:none}.conum[data-value]:after{content:attr(data-value)}pre .conum[data-value]{position:relative;top:-.125em}b.conum *{color:inherit!important}.conum:not([data-value]):empty{display:none}dt,th.tableblock,td.content,div.footnote{text-rendering:optimizeLegibility}h1,h2,p,td.content,span.alt{letter-spacing:-.01em}p strong,td.content strong,div.footnote strong{letter-spacing:-.005em}p,blockquote,dt,td.content,span.alt{font-size:1.0625rem}p{margin-bottom:1.25rem}.sidebarblock p,.sidebarblock dt,.sidebarblock td.content,p.tableblock{font-size:1em}.exampleblock>.content{background-color:#fffef7;border-color:#e0e0dc;-webkit-box-shadow:0 1px 4px #e0e0dc;box-shadow:0 1px 4px #e0e0dc}.print-only{display:none!important}@media print{@page{margin:1.25cm .75cm}*{-webkit-box-shadow:none!important;box-shadow:none!important;text-shadow:none!important}a{color:inherit!important;text-decoration:underline!important}a.bare,a[href^="#"],a[href^="mailto:"]{text-decoration:none!important}a[href^="http:"]:not(.bare):after,a[href^="https:"]:not(.bare):after{content:"(" attr(href) ")";display:inline-block;font-size:.875em;padding-left:.25em}abbr[title]:after{content:" (" attr(title) ")"}pre,blockquote,tr,img,object,svg{page-break-inside:avoid}thead{display:table-header-group}svg{max-width:100%}p,blockquote,dt,td.content{font-size:1em;orphans:3;widows:3}h2,h3,#toctitle,.sidebarblock>.content>.title{page-break-after:avoid}#toc,.sidebarblock,.exampleblock>.content{background:none!important}#toc{border-bottom:1px solid #ddddd8!important;padding-bottom:0!important}.sect1{padding-bottom:0!important}.sect1+.sect1{border:0!important}#header>h1:first-child{margin-top:1.25rem}body.book #header{text-align:center}body.book #header>h1:first-child{border:0!important;margin:2.5em 0 1em 0}body.book #header .details{border:0!important;display:block;padding:0!important}body.book #header .details span:first-child{margin-left:0!important}body.book #header .details br{display:block}body.book #header .details br+span:before{content:none!important}body.book #toc{border:0!important;text-align:left!important;padding:0!important;margin:0!important}body.book #toc,body.book #preamble,body.book h1.sect0,body.book .sect1>h2{page-break-before:always}.listingblock code[data-lang]:before{display:block}#footer{background:none!important;padding:0 .9375em}#footer-text{color:rgba(0,0,0,.6)!important;font-size:.9em}.hide-on-print{display:none!important}.print-only{display:block!important}.hide-for-print{display:none!important}.show-for-print{display:inherit!important}}#content .page-footer{height:100px;border-top:1px solid #ccc;overflow:hidden;padding:10px 0;font-size:14px;color:gray}#content .footer-modification{float:right}#content .footer-modification a{text-decoration:none}.sectlevel2{display:none}.submenu{background:#e7e7e6}.submenu li{border:0}.submenu a{color:#555}.copyright{text-align:right;padding-top:1.25em}#toTop{display:none;position:fixed;bottom:10px;right:0;width:44px;height:44px;border-radius:50%;background-color:#ced4ce;cursor:pointer;text-align:center}#upArrow{position:absolute;left:24%;right:0;bottom:19%;transition:.3s ease-in-out;display:block}#upText{position:absolute;left:0;right:0;bottom:0;font-size:16px;font-weight: 600;line-height:45px;display:none;transition:.3s ease-in-out;-webkit-box-align:center}
|
||||
407
src/main/resources/static/doc/debug.js
Normal file
407
src/main/resources/static/doc/debug.js
Normal file
@ -0,0 +1,407 @@
|
||||
$(function () {
|
||||
const Accordion = function (el, multiple) {
|
||||
this.el = el || {};
|
||||
this.multiple = multiple || false;
|
||||
const links = this.el.find('.dd');
|
||||
links.on('click', {el: this.el, multiple: this.multiple}, this.dropdown);
|
||||
};
|
||||
Accordion.prototype.dropdown = function (e) {
|
||||
const $el = e.data.el;
|
||||
const $this = $(this), $next = $this.next();
|
||||
$next.slideToggle();
|
||||
$this.parent().toggleClass('open');
|
||||
if (!e.data.multiple) {
|
||||
$el.find('.submenu').not($next).slideUp("20").parent().removeClass(
|
||||
'open');
|
||||
}
|
||||
};
|
||||
new Accordion($('#accordion'), false);
|
||||
hljs.highlightAll();
|
||||
});
|
||||
|
||||
$("[contenteditable=plaintext-only]").on('blur', function (e) {
|
||||
e.preventDefault();
|
||||
const $this = $(this);
|
||||
const data = $this.text();
|
||||
let content;
|
||||
if (undefined === e.originalEvent.clipboardData) {
|
||||
content = data;
|
||||
} else {
|
||||
content = e.originalEvent.clipboardData.getData('text/plain')
|
||||
}
|
||||
content = JSON.stringify(JSON.parse(content), null, 4);
|
||||
const highlightedCode = hljs.highlight('json', content).value;
|
||||
$this.html(highlightedCode);
|
||||
})
|
||||
$("button").on("click", function () {
|
||||
const $this = $(this);
|
||||
const id = $this.data("id");
|
||||
console.log("method-id=>" + id);
|
||||
|
||||
let body = $("#" + id + "-body").text();
|
||||
|
||||
// header
|
||||
const $headerElement = $("#" + id + "-header");
|
||||
const headersData = getInputData($headerElement);
|
||||
|
||||
// body param
|
||||
const $paramElement = $("#" + id + "-param");
|
||||
let bodyParamData = getInputData($paramElement)
|
||||
|
||||
// path param
|
||||
const $pathElement = $("#" + id + "-path-params")
|
||||
const pathParamData = getInputData($pathElement)
|
||||
|
||||
// query param
|
||||
const $queryElement = $("#" + id + "-query-params")
|
||||
let $urlDataElement = $("#" + id + "-url");
|
||||
const url = $urlDataElement.data("url");
|
||||
const isDownload = $urlDataElement.data("download");
|
||||
const page = $urlDataElement.data("page");
|
||||
const method = $("#" + id + "-method").data("method");
|
||||
const contentType = $("#" + id + "-content-type").data("content-type");
|
||||
console.log("request-headers=>" + JSON.stringify(headersData))
|
||||
console.log("path-params=>" + JSON.stringify(pathParamData))
|
||||
|
||||
console.log("body-params=>" + JSON.stringify(bodyParamData))
|
||||
console.log("json-body=>" + body);
|
||||
let finalUrl = "";
|
||||
let queryParamData;
|
||||
if (!isEmpty(page)) {
|
||||
queryParamData = getInputData($queryElement)
|
||||
finalUrl = castToGetUri(page, pathParamData, queryParamData)
|
||||
window.open(finalUrl, "_blank");
|
||||
return;
|
||||
}
|
||||
if (isDownload) {
|
||||
queryParamData = getInputData($queryElement);
|
||||
download(url, headersData, pathParamData, queryParamData, bodyParamData,
|
||||
method, contentType);
|
||||
return;
|
||||
}
|
||||
const ajaxOptions = {};
|
||||
|
||||
if ("multipart/form-data" === contentType) {
|
||||
finalUrl = castToGetUri(url, pathParamData);
|
||||
queryParamData = getInputData($queryElement, true, true)
|
||||
body = queryParamData;
|
||||
ajaxOptions.processData = false;
|
||||
ajaxOptions.contentType = false;
|
||||
} else if ("POST" === method && contentType !== "multipart/form-data"
|
||||
&& contentType !== "application/json") {
|
||||
finalUrl = castToGetUri(url, pathParamData);
|
||||
queryParamData = getInputData($queryElement, true)
|
||||
body = queryParamData;
|
||||
} else {
|
||||
queryParamData = getInputData($queryElement)
|
||||
finalUrl = castToGetUri(url, pathParamData, queryParamData)
|
||||
ajaxOptions.contentType = contentType;
|
||||
}
|
||||
console.log("query-params=>" + JSON.stringify(queryParamData));
|
||||
console.log("url=>" + finalUrl)
|
||||
ajaxOptions.headers = headersData
|
||||
ajaxOptions.url = finalUrl
|
||||
ajaxOptions.type = method
|
||||
ajaxOptions.data = body;
|
||||
|
||||
const $responseEle = $("#" + id + "-response").find("pre code");
|
||||
const ajaxTime = new Date().getTime();
|
||||
$.ajax(ajaxOptions).done(function (result, textStatus, jqXHR) {
|
||||
const totalTime = new Date().getTime() - ajaxTime;
|
||||
$this.css("background", "#5cb85c");
|
||||
$("#" + id + "-resp-status").html(
|
||||
" Status: " + jqXHR.status + " " + jqXHR.statusText
|
||||
+ " Time: " + totalTime + " ms");
|
||||
const highlightedCode = hljs.highlight('json',
|
||||
JSON.stringify(result, null, 4)).value;
|
||||
$responseEle.html(highlightedCode);
|
||||
}).fail(function (jqXHR) {
|
||||
const totalTime = new Date().getTime() - ajaxTime;
|
||||
$this.css("background", "#D44B47");
|
||||
if (jqXHR.status === 0 && jqXHR.readyState === 0) {
|
||||
$("#" + id + "-resp-status").html(
|
||||
"Connection refused, please check the server.");
|
||||
} else {
|
||||
$("#" + id + "-resp-status").html(
|
||||
" Status: " + jqXHR.status + " "
|
||||
+ jqXHR.statusText + " Time: " + totalTime
|
||||
+ " ms");
|
||||
}
|
||||
if (undefined !== jqXHR.responseJSON) {
|
||||
const highlightedCode = hljs.highlight('json',
|
||||
JSON.stringify(jqXHR.responseJSON, null, 4)).value;
|
||||
$responseEle.html(highlightedCode);
|
||||
}
|
||||
}).always(function () {
|
||||
|
||||
});
|
||||
const curlCmd = toCurl(ajaxOptions);
|
||||
const highlightedCode = hljs.highlight('bash', curlCmd).value;
|
||||
$("#" + id + "-curl").find("pre code").html(highlightedCode);
|
||||
})
|
||||
$(".check-all").on("click", function () {
|
||||
const checkboxName = $(this).prop("name");
|
||||
const checked = $(this).is(':checked');
|
||||
if (!checked) {
|
||||
$(this).removeAttr("checked");
|
||||
} else {
|
||||
$(this).prop("checked", true);
|
||||
}
|
||||
$('input[name="' + checkboxName + '"]').each(function () {
|
||||
if (!checked) {
|
||||
$(this).prop("checked", false);
|
||||
} else {
|
||||
$(this).prop("checked", true);
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
function castToGetUri(url, pathParams, params) {
|
||||
if (undefined !== url) {
|
||||
let urls = url.split(";")
|
||||
url = urls[0];
|
||||
}
|
||||
if (pathParams instanceof Object && !(pathParams instanceof Array)) {
|
||||
url = url.format(pathParams)
|
||||
}
|
||||
if (params instanceof Object && !(params instanceof Array)) {
|
||||
const pm = params || {};
|
||||
const arr = [];
|
||||
arr.push(url);
|
||||
let j = 0;
|
||||
for (const i in pm) {
|
||||
if (j === 0) {
|
||||
arr.push("?");
|
||||
arr.push(i + "=" + pm[i]);
|
||||
} else {
|
||||
arr.push("&" + i + "=" + pm[i]);
|
||||
}
|
||||
j++;
|
||||
}
|
||||
return arr.join("");
|
||||
} else {
|
||||
return url;
|
||||
}
|
||||
}
|
||||
|
||||
function getInputData(element, isPost, returnFormDate) {
|
||||
const formData = new FormData();
|
||||
$(element).find("tr").each(function (i) {
|
||||
const checked = $(this).find('td:eq(0)').children(".checkbox").children(
|
||||
"input").is(':checked');
|
||||
if (checked) {
|
||||
const input = $(this).find('td:eq(2) input');
|
||||
let attr = $(input).attr("type");
|
||||
let multiple = $(input).attr("multiple");
|
||||
console.log("input type:" + attr)
|
||||
const name = $(input).attr("name");
|
||||
if (attr === "file") {
|
||||
let files = $(input)[0].files;
|
||||
if (multiple) {
|
||||
$.each(files, function (i, file) {
|
||||
formData.append(name, file);
|
||||
})
|
||||
} else {
|
||||
formData.append(name, files[0]);
|
||||
}
|
||||
} else {
|
||||
const val = $(input).val();
|
||||
if (isValidUrl(val)) {
|
||||
formData.append(name, encodeURI(val));
|
||||
} else {
|
||||
// support chinese
|
||||
if (hasChinese(val) && !isPost) {
|
||||
formData.append(name, encodeURIComponent(val));
|
||||
} else {
|
||||
formData.append(name, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
if (returnFormDate) {
|
||||
return formData;
|
||||
}
|
||||
const headersData = {};
|
||||
formData.forEach((value, key) => headersData[key] = value);
|
||||
return headersData;
|
||||
}
|
||||
|
||||
String.prototype.format = function (args) {
|
||||
let reg;
|
||||
if (arguments.length > 0) {
|
||||
let result = this;
|
||||
if (arguments.length === 1 && typeof (args) == "object") {
|
||||
for (const key in args) {
|
||||
reg = new RegExp("({" + key + "})", "g");
|
||||
result = result.replace(reg, args[key]);
|
||||
}
|
||||
} else {
|
||||
for (let i = 0; i < arguments.length; i++) {
|
||||
if (arguments[i] === undefined) {
|
||||
return "";
|
||||
} else {
|
||||
reg = new RegExp("({[" + i + "]})", "g");
|
||||
result = result.replace(reg, arguments[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
} else {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
function download(url, headersData, pathParamData, queryParamData,
|
||||
bodyParamData, method, contentType) {
|
||||
url = castToGetUri(url, pathParamData, queryParamData)
|
||||
const xmlRequest = new XMLHttpRequest();
|
||||
xmlRequest.open(method, url, true);
|
||||
xmlRequest.setRequestHeader("Content-type", contentType);
|
||||
for (let key in headersData) {
|
||||
xmlRequest.setRequestHeader(key, headersData[key])
|
||||
}
|
||||
xmlRequest.responseType = "blob";
|
||||
xmlRequest.onload = function () {
|
||||
if (this.status === 200) {
|
||||
let fileName = "";
|
||||
const disposition = xmlRequest.getResponseHeader('Content-Disposition');
|
||||
if (disposition && disposition.indexOf('attachment') !== -1) {
|
||||
const filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
|
||||
const matches = filenameRegex.exec(disposition);
|
||||
if (matches != null && matches[1]) {
|
||||
fileName = decodeURI(matches[1].replace(/['"]/g, ''));
|
||||
}
|
||||
}
|
||||
console.log("download filename:" + fileName);
|
||||
const blob = this.response;
|
||||
if (navigator.msSaveBlob) {
|
||||
// IE10 can't do a[download], only Blobs:
|
||||
window.navigator.msSaveBlob(blob, fileName);
|
||||
return;
|
||||
}
|
||||
if (window.URL) { // simple fast and modern way using Blob and URL:
|
||||
const a = document.createElement("a");
|
||||
document.body.appendChild(a);
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
a.href = url;
|
||||
a.download = fileName;
|
||||
a.click();
|
||||
window.URL.revokeObjectURL(url);
|
||||
}
|
||||
console.log(fileName);
|
||||
} else {
|
||||
console.log("download failed");
|
||||
}
|
||||
};
|
||||
try {
|
||||
xmlRequest.send(bodyParamData);
|
||||
} catch (e) {
|
||||
console.error("Failed to send data", e);
|
||||
}
|
||||
}
|
||||
|
||||
function toCurl(request) {
|
||||
if (typeof request !== 'object') {
|
||||
throw "Request is not an object";
|
||||
}
|
||||
// default is a GET request
|
||||
const cmd = ["curl", "-X", request.type || "GET"];
|
||||
|
||||
if (request.url.indexOf('https') === 0) {
|
||||
cmd.push("-k");
|
||||
}
|
||||
|
||||
// append Content-Type
|
||||
if (request.contentType) {
|
||||
cmd.push("-H");
|
||||
cmd.push("'Content-Type:");
|
||||
cmd.push(request.contentType + "'");
|
||||
}
|
||||
// append request headers
|
||||
let headerValue;
|
||||
if (typeof request.headers == 'object') {
|
||||
for (let key in request.headers) {
|
||||
if (Object.prototype.hasOwnProperty.call(request.headers, key)) {
|
||||
cmd.push("-H");
|
||||
headerValue = request.headers[key];
|
||||
if (headerValue.value === '') {
|
||||
cmd.push("'" + key + "'");
|
||||
} else {
|
||||
cmd.push("'" + key + ':' + headerValue + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// display the response headers
|
||||
cmd.push("-i");
|
||||
// append request url
|
||||
let url = request.url;
|
||||
if (!url.startsWith("http")) {
|
||||
const protocol = window.document.location.protocol;
|
||||
const domain = window.document.location.hostname;
|
||||
const port = window.document.location.port;
|
||||
url = protocol + "//" + domain + ":" + port + url;
|
||||
}
|
||||
cmd.push(url);
|
||||
// append data
|
||||
|
||||
if (typeof request.data == 'object') {
|
||||
let index = 0;
|
||||
const bodyData = [];
|
||||
bodyData.push("\"")
|
||||
for (let key in request.data) {
|
||||
if (Object.prototype.hasOwnProperty.call(request.data, key)) {
|
||||
if (index === 0) {
|
||||
bodyData.push(key);
|
||||
bodyData.push("=");
|
||||
bodyData.push(request.data[key]);
|
||||
} else {
|
||||
bodyData.push("&")
|
||||
bodyData.push(key);
|
||||
bodyData.push("=");
|
||||
bodyData.push(request.data[key]);
|
||||
}
|
||||
}
|
||||
index++;
|
||||
}
|
||||
bodyData.push("\"");
|
||||
let bodyStr = ""
|
||||
bodyData.forEach(function (item) {
|
||||
bodyStr += item;
|
||||
});
|
||||
cmd.push("--data");
|
||||
cmd.push(bodyStr);
|
||||
} else if (request.data && request.data.length > 0) {
|
||||
// append json data
|
||||
cmd.push("--data");
|
||||
cmd.push("'" + request.data + "'");
|
||||
}
|
||||
|
||||
let curlCmd = "";
|
||||
cmd.forEach(function (item) {
|
||||
curlCmd += item + " ";
|
||||
});
|
||||
console.log(curlCmd);
|
||||
return curlCmd;
|
||||
}
|
||||
|
||||
function isEmpty(obj) {
|
||||
return obj === undefined || obj === null || String(obj).trim() === '';
|
||||
}
|
||||
|
||||
function hasChinese(_string) {
|
||||
const chineseReg = /[\u4e00-\u9fa5]/g
|
||||
return chineseReg.test(_string);
|
||||
}
|
||||
|
||||
function isValidUrl(_string) {
|
||||
let urlString;
|
||||
try {
|
||||
urlString = new URL(_string);
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
return urlString.protocol === "http:" || urlString.protocol === "https:";
|
||||
}
|
||||
6
src/main/resources/static/doc/font.css
Normal file
6
src/main/resources/static/doc/font.css
Normal file
@ -0,0 +1,6 @@
|
||||
@font-face{font-family:'Droid Sans Mono';font-style:normal;font-weight:400;src:url(https://fonts.gstatic.com/s/droidsansmono/v19/6NUO8FuJNQ2MbkrZ5-J8lKFrp7pRef2r.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:'Noto Serif';font-style:italic;font-weight:400;src:url(https://fonts.gstatic.com/s/notoserif/v20/ga6Kaw1J5X9T9RW6j9bNfFImZzC7TMQ.woff2) format('woff2');unicode-range:U+0460-052F,U+1C80-1C88,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:'Noto Serif';font-style:italic;font-weight:400;src:url(https://fonts.gstatic.com/s/notoserif/v20/ga6Kaw1J5X9T9RW6j9bNfFImbjC7TMQ.woff2) format('woff2');unicode-range:U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:'Noto Serif';font-style:italic;font-weight:400;src:url(https://fonts.gstatic.com/s/notoserif/v20/ga6Kaw1J5X9T9RW6j9bNfFImZjC7TMQ.woff2) format('woff2');unicode-range:U+1F00-1FFF}@font-face{font-family:'Noto Serif';font-style:italic;font-weight:400;src:url(https://fonts.gstatic.com/s/notoserif/v20/ga6Kaw1J5X9T9RW6j9bNfFImaTC7TMQ.woff2) format('woff2');unicode-range:U+0370-03FF}@font-face{font-family:'Noto Serif';font-style:italic;font-weight:400;src:url(https://fonts.gstatic.com/s/notoserif/v20/ga6Kaw1J5X9T9RW6j9bNfFImZTC7TMQ.woff2) format('woff2');unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+1EA0-1EF9,U+20AB}@font-face{font-family:'Noto Serif';font-style:italic;font-weight:400;src:url(https://fonts.gstatic.com/s/notoserif/v20/ga6Kaw1J5X9T9RW6j9bNfFImZDC7TMQ.woff2) format('woff2');unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:'Noto Serif';font-style:italic;font-weight:400;src:url(https://fonts.gstatic.com/s/notoserif/v20/ga6Kaw1J5X9T9RW6j9bNfFImajC7.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:'Noto Serif';font-style:italic;font-weight:700;src:url(https://fonts.gstatic.com/s/notoserif/v20/ga6Vaw1J5X9T9RW6j9bNfFIu0RWufuVMCoY.woff2) format('woff2');unicode-range:U+0460-052F,U+1C80-1C88,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:'Noto Serif';font-style:italic;font-weight:700;src:url(https://fonts.gstatic.com/s/notoserif/v20/ga6Vaw1J5X9T9RW6j9bNfFIu0RWud-VMCoY.woff2) format('woff2');unicode-range:U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:'Noto Serif';font-style:italic;font-weight:700;src:url(https://fonts.gstatic.com/s/notoserif/v20/ga6Vaw1J5X9T9RW6j9bNfFIu0RWuf-VMCoY.woff2) format('woff2');unicode-range:U+1F00-1FFF}@font-face{font-family:'Noto Serif';font-style:italic;font-weight:700;src:url(https://fonts.gstatic.com/s/notoserif/v20/ga6Vaw1J5X9T9RW6j9bNfFIu0RWucOVMCoY.woff2) format('woff2');unicode-range:U+0370-03FF}@font-face{font-family:'Noto Serif';font-style:italic;font-weight:700;src:url(https://fonts.gstatic.com/s/notoserif/v20/ga6Vaw1J5X9T9RW6j9bNfFIu0RWufOVMCoY.woff2) format('woff2');unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+1EA0-1EF9,U+20AB}@font-face{font-family:'Noto Serif';font-style:italic;font-weight:700;src:url(https://fonts.gstatic.com/s/notoserif/v20/ga6Vaw1J5X9T9RW6j9bNfFIu0RWufeVMCoY.woff2) format('woff2');unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:'Noto Serif';font-style:italic;font-weight:700;src:url(https://fonts.gstatic.com/s/notoserif/v20/ga6Vaw1J5X9T9RW6j9bNfFIu0RWuc-VM.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:'Noto Serif';font-style:normal;font-weight:400;src:url(https://fonts.gstatic.com/s/notoserif/v20/ga6Iaw1J5X9T9RW6j9bNfFoWaCi_.woff2) format('woff2');unicode-range:U+0460-052F,U+1C80-1C88,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}
|
||||
@font-face{font-family:'Noto Serif';font-style:normal;font-weight:400;src:url(https://fonts.gstatic.com/s/notoserif/v20/ga6Iaw1J5X9T9RW6j9bNfFMWaCi_.woff2) format('woff2');unicode-range:U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:'Noto Serif';font-style:normal;font-weight:400;src:url(https://fonts.gstatic.com/s/notoserif/v20/ga6Iaw1J5X9T9RW6j9bNfFsWaCi_.woff2) format('woff2');unicode-range:U+1F00-1FFF}@font-face{font-family:'Noto Serif';font-style:normal;font-weight:400;src:url(https://fonts.gstatic.com/s/notoserif/v20/ga6Iaw1J5X9T9RW6j9bNfFQWaCi_.woff2) format('woff2');unicode-range:U+0370-03FF}@font-face{font-family:'Noto Serif';font-style:normal;font-weight:400;src:url(https://fonts.gstatic.com/s/notoserif/v20/ga6Iaw1J5X9T9RW6j9bNfFgWaCi_.woff2) format('woff2');unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+1EA0-1EF9,U+20AB}@font-face{font-family:'Noto Serif';font-style:normal;font-weight:400;src:url(https://fonts.gstatic.com/s/notoserif/v20/ga6Iaw1J5X9T9RW6j9bNfFkWaCi_.woff2) format('woff2');unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:'Noto Serif';font-style:normal;font-weight:400;src:url(https://fonts.gstatic.com/s/notoserif/v20/ga6Iaw1J5X9T9RW6j9bNfFcWaA.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:'Noto Serif';font-style:normal;font-weight:700;src:url(https://fonts.gstatic.com/s/notoserif/v20/ga6Law1J5X9T9RW6j9bNdOwzfRqecf1I.woff2) format('woff2');unicode-range:U+0460-052F,U+1C80-1C88,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:'Noto Serif';font-style:normal;font-weight:700;src:url(https://fonts.gstatic.com/s/notoserif/v20/ga6Law1J5X9T9RW6j9bNdOwzfROecf1I.woff2) format('woff2');unicode-range:U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:'Noto Serif';font-style:normal;font-weight:700;src:url(https://fonts.gstatic.com/s/notoserif/v20/ga6Law1J5X9T9RW6j9bNdOwzfRuecf1I.woff2) format('woff2');unicode-range:U+1F00-1FFF}@font-face{font-family:'Noto Serif';font-style:normal;font-weight:700;src:url(https://fonts.gstatic.com/s/notoserif/v20/ga6Law1J5X9T9RW6j9bNdOwzfRSecf1I.woff2) format('woff2');unicode-range:U+0370-03FF}@font-face{font-family:'Noto Serif';font-style:normal;font-weight:700;src:url(https://fonts.gstatic.com/s/notoserif/v20/ga6Law1J5X9T9RW6j9bNdOwzfRiecf1I.woff2) format('woff2');unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+1EA0-1EF9,U+20AB}@font-face{font-family:'Noto Serif';font-style:normal;font-weight:700;src:url(https://fonts.gstatic.com/s/notoserif/v20/ga6Law1J5X9T9RW6j9bNdOwzfRmecf1I.woff2) format('woff2');unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:'Noto Serif';font-style:normal;font-weight:700;src:url(https://fonts.gstatic.com/s/notoserif/v20/ga6Law1J5X9T9RW6j9bNdOwzfReecQ.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:'Open Sans';font-style:italic;font-weight:300;font-stretch:normal;src:url(https://fonts.gstatic.com/s/opensans/v28/memQYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWq8tWZ0Pw86hd0Rk5hkWV0ewJER.woff2) format('woff2');unicode-range:U+0460-052F,U+1C80-1C88,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:'Open Sans';font-style:italic;font-weight:300;font-stretch:normal;src:url(https://fonts.gstatic.com/s/opensans/v28/memQYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWq8tWZ0Pw86hd0Rk5hkWVQewJER.woff2) format('woff2');unicode-range:U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:'Open Sans';font-style:italic;font-weight:300;font-stretch:normal;src:url(https://fonts.gstatic.com/s/opensans/v28/memQYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWq8tWZ0Pw86hd0Rk5hkWVwewJER.woff2) format('woff2');unicode-range:U+1F00-1FFF}@font-face{font-family:'Open Sans';font-style:italic;font-weight:300;font-stretch:normal;src:url(https://fonts.gstatic.com/s/opensans/v28/memQYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWq8tWZ0Pw86hd0Rk5hkWVMewJER.woff2) format('woff2');unicode-range:U+0370-03FF}
|
||||
@font-face{font-family:'Open Sans';font-style:italic;font-weight:300;font-stretch:normal;src:url(https://fonts.gstatic.com/s/opensans/v28/memQYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWq8tWZ0Pw86hd0Rk5hkWVIewJER.woff2) format('woff2');unicode-range:U+0590-05FF,U+200C-2010,U+20AA,U+25CC,U+FB1D-FB4F}@font-face{font-family:'Open Sans';font-style:italic;font-weight:300;font-stretch:normal;src:url(https://fonts.gstatic.com/s/opensans/v28/memQYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWq8tWZ0Pw86hd0Rk5hkWV8ewJER.woff2) format('woff2');unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+1EA0-1EF9,U+20AB}@font-face{font-family:'Open Sans';font-style:italic;font-weight:300;font-stretch:normal;src:url(https://fonts.gstatic.com/s/opensans/v28/memQYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWq8tWZ0Pw86hd0Rk5hkWV4ewJER.woff2) format('woff2');unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:'Open Sans';font-style:italic;font-weight:300;font-stretch:normal;src:url(https://fonts.gstatic.com/s/opensans/v28/memQYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWq8tWZ0Pw86hd0Rk5hkWVAewA.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:'Open Sans';font-style:italic;font-weight:400;font-stretch:normal;src:url(https://fonts.gstatic.com/s/opensans/v28/memQYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWq8tWZ0Pw86hd0Rk8ZkWV0ewJER.woff2) format('woff2');unicode-range:U+0460-052F,U+1C80-1C88,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:'Open Sans';font-style:italic;font-weight:400;font-stretch:normal;src:url(https://fonts.gstatic.com/s/opensans/v28/memQYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWq8tWZ0Pw86hd0Rk8ZkWVQewJER.woff2) format('woff2');unicode-range:U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:'Open Sans';font-style:italic;font-weight:400;font-stretch:normal;src:url(https://fonts.gstatic.com/s/opensans/v28/memQYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWq8tWZ0Pw86hd0Rk8ZkWVwewJER.woff2) format('woff2');unicode-range:U+1F00-1FFF}@font-face{font-family:'Open Sans';font-style:italic;font-weight:400;font-stretch:normal;src:url(https://fonts.gstatic.com/s/opensans/v28/memQYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWq8tWZ0Pw86hd0Rk8ZkWVMewJER.woff2) format('woff2');unicode-range:U+0370-03FF}@font-face{font-family:'Open Sans';font-style:italic;font-weight:400;font-stretch:normal;src:url(https://fonts.gstatic.com/s/opensans/v28/memQYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWq8tWZ0Pw86hd0Rk8ZkWVIewJER.woff2) format('woff2');unicode-range:U+0590-05FF,U+200C-2010,U+20AA,U+25CC,U+FB1D-FB4F}@font-face{font-family:'Open Sans';font-style:italic;font-weight:400;font-stretch:normal;src:url(https://fonts.gstatic.com/s/opensans/v28/memQYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWq8tWZ0Pw86hd0Rk8ZkWV8ewJER.woff2) format('woff2');unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+1EA0-1EF9,U+20AB}@font-face{font-family:'Open Sans';font-style:italic;font-weight:400;font-stretch:normal;src:url(https://fonts.gstatic.com/s/opensans/v28/memQYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWq8tWZ0Pw86hd0Rk8ZkWV4ewJER.woff2) format('woff2');unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:'Open Sans';font-style:italic;font-weight:400;font-stretch:normal;src:url(https://fonts.gstatic.com/s/opensans/v28/memQYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWq8tWZ0Pw86hd0Rk8ZkWVAewA.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:'Open Sans';font-style:italic;font-weight:600;font-stretch:normal;src:url(https://fonts.gstatic.com/s/opensans/v28/memQYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWq8tWZ0Pw86hd0RkxhjWV0ewJER.woff2) format('woff2');unicode-range:U+0460-052F,U+1C80-1C88,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:'Open Sans';font-style:italic;font-weight:600;font-stretch:normal;src:url(https://fonts.gstatic.com/s/opensans/v28/memQYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWq8tWZ0Pw86hd0RkxhjWVQewJER.woff2) format('woff2');unicode-range:U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}
|
||||
@font-face{font-family:'Open Sans';font-style:italic;font-weight:600;font-stretch:normal;src:url(https://fonts.gstatic.com/s/opensans/v28/memQYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWq8tWZ0Pw86hd0RkxhjWVwewJER.woff2) format('woff2');unicode-range:U+1F00-1FFF}@font-face{font-family:'Open Sans';font-style:italic;font-weight:600;font-stretch:normal;src:url(https://fonts.gstatic.com/s/opensans/v28/memQYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWq8tWZ0Pw86hd0RkxhjWVMewJER.woff2) format('woff2');unicode-range:U+0370-03FF}@font-face{font-family:'Open Sans';font-style:italic;font-weight:600;font-stretch:normal;src:url(https://fonts.gstatic.com/s/opensans/v28/memQYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWq8tWZ0Pw86hd0RkxhjWVIewJER.woff2) format('woff2');unicode-range:U+0590-05FF,U+200C-2010,U+20AA,U+25CC,U+FB1D-FB4F}@font-face{font-family:'Open Sans';font-style:italic;font-weight:600;font-stretch:normal;src:url(https://fonts.gstatic.com/s/opensans/v28/memQYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWq8tWZ0Pw86hd0RkxhjWV8ewJER.woff2) format('woff2');unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+1EA0-1EF9,U+20AB}@font-face{font-family:'Open Sans';font-style:italic;font-weight:600;font-stretch:normal;src:url(https://fonts.gstatic.com/s/opensans/v28/memQYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWq8tWZ0Pw86hd0RkxhjWV4ewJER.woff2) format('woff2');unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:'Open Sans';font-style:italic;font-weight:600;font-stretch:normal;src:url(https://fonts.gstatic.com/s/opensans/v28/memQYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWq8tWZ0Pw86hd0RkxhjWVAewA.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:'Open Sans';font-style:normal;font-weight:300;font-stretch:normal;src:url(https://fonts.gstatic.com/s/opensans/v28/memSYaGs126MiZpBA-UvWbX2vVnXBbObj2OVZyOOSr4dVJWUgsiH0B4taVIGxA.woff2) format('woff2');unicode-range:U+0460-052F,U+1C80-1C88,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:'Open Sans';font-style:normal;font-weight:300;font-stretch:normal;src:url(https://fonts.gstatic.com/s/opensans/v28/memSYaGs126MiZpBA-UvWbX2vVnXBbObj2OVZyOOSr4dVJWUgsiH0B4kaVIGxA.woff2) format('woff2');unicode-range:U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:'Open Sans';font-style:normal;font-weight:300;font-stretch:normal;src:url(https://fonts.gstatic.com/s/opensans/v28/memSYaGs126MiZpBA-UvWbX2vVnXBbObj2OVZyOOSr4dVJWUgsiH0B4saVIGxA.woff2) format('woff2');unicode-range:U+1F00-1FFF}@font-face{font-family:'Open Sans';font-style:normal;font-weight:300;font-stretch:normal;src:url(https://fonts.gstatic.com/s/opensans/v28/memSYaGs126MiZpBA-UvWbX2vVnXBbObj2OVZyOOSr4dVJWUgsiH0B4jaVIGxA.woff2) format('woff2');unicode-range:U+0370-03FF}@font-face{font-family:'Open Sans';font-style:normal;font-weight:300;font-stretch:normal;src:url(https://fonts.gstatic.com/s/opensans/v28/memSYaGs126MiZpBA-UvWbX2vVnXBbObj2OVZyOOSr4dVJWUgsiH0B4iaVIGxA.woff2) format('woff2');unicode-range:U+0590-05FF,U+200C-2010,U+20AA,U+25CC,U+FB1D-FB4F}@font-face{font-family:'Open Sans';font-style:normal;font-weight:300;font-stretch:normal;src:url(https://fonts.gstatic.com/s/opensans/v28/memSYaGs126MiZpBA-UvWbX2vVnXBbObj2OVZyOOSr4dVJWUgsiH0B4vaVIGxA.woff2) format('woff2');unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+1EA0-1EF9,U+20AB}@font-face{font-family:'Open Sans';font-style:normal;font-weight:300;font-stretch:normal;src:url(https://fonts.gstatic.com/s/opensans/v28/memSYaGs126MiZpBA-UvWbX2vVnXBbObj2OVZyOOSr4dVJWUgsiH0B4uaVIGxA.woff2) format('woff2');unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:'Open Sans';font-style:normal;font-weight:300;font-stretch:normal;src:url(https://fonts.gstatic.com/s/opensans/v28/memSYaGs126MiZpBA-UvWbX2vVnXBbObj2OVZyOOSr4dVJWUgsiH0B4gaVI.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
|
||||
@font-face{font-family:'Open Sans';font-style:normal;font-weight:400;font-stretch:normal;src:url(https://fonts.gstatic.com/s/opensans/v28/memSYaGs126MiZpBA-UvWbX2vVnXBbObj2OVZyOOSr4dVJWUgsjZ0B4taVIGxA.woff2) format('woff2');unicode-range:U+0460-052F,U+1C80-1C88,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:'Open Sans';font-style:normal;font-weight:400;font-stretch:normal;src:url(https://fonts.gstatic.com/s/opensans/v28/memSYaGs126MiZpBA-UvWbX2vVnXBbObj2OVZyOOSr4dVJWUgsjZ0B4kaVIGxA.woff2) format('woff2');unicode-range:U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:'Open Sans';font-style:normal;font-weight:400;font-stretch:normal;src:url(https://fonts.gstatic.com/s/opensans/v28/memSYaGs126MiZpBA-UvWbX2vVnXBbObj2OVZyOOSr4dVJWUgsjZ0B4saVIGxA.woff2) format('woff2');unicode-range:U+1F00-1FFF}@font-face{font-family:'Open Sans';font-style:normal;font-weight:400;font-stretch:normal;src:url(https://fonts.gstatic.com/s/opensans/v28/memSYaGs126MiZpBA-UvWbX2vVnXBbObj2OVZyOOSr4dVJWUgsjZ0B4jaVIGxA.woff2) format('woff2');unicode-range:U+0370-03FF}@font-face{font-family:'Open Sans';font-style:normal;font-weight:400;font-stretch:normal;src:url(https://fonts.gstatic.com/s/opensans/v28/memSYaGs126MiZpBA-UvWbX2vVnXBbObj2OVZyOOSr4dVJWUgsjZ0B4iaVIGxA.woff2) format('woff2');unicode-range:U+0590-05FF,U+200C-2010,U+20AA,U+25CC,U+FB1D-FB4F}@font-face{font-family:'Open Sans';font-style:normal;font-weight:400;font-stretch:normal;src:url(https://fonts.gstatic.com/s/opensans/v28/memSYaGs126MiZpBA-UvWbX2vVnXBbObj2OVZyOOSr4dVJWUgsjZ0B4vaVIGxA.woff2) format('woff2');unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+1EA0-1EF9,U+20AB}@font-face{font-family:'Open Sans';font-style:normal;font-weight:400;font-stretch:normal;src:url(https://fonts.gstatic.com/s/opensans/v28/memSYaGs126MiZpBA-UvWbX2vVnXBbObj2OVZyOOSr4dVJWUgsjZ0B4uaVIGxA.woff2) format('woff2');unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:'Open Sans';font-style:normal;font-weight:400;font-stretch:normal;src:url(https://fonts.gstatic.com/s/opensans/v28/memSYaGs126MiZpBA-UvWbX2vVnXBbObj2OVZyOOSr4dVJWUgsjZ0B4gaVI.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:'Open Sans';font-style:normal;font-weight:600;font-stretch:normal;src:url(https://fonts.gstatic.com/s/opensans/v28/memSYaGs126MiZpBA-UvWbX2vVnXBbObj2OVZyOOSr4dVJWUgsgH1x4taVIGxA.woff2) format('woff2');unicode-range:U+0460-052F,U+1C80-1C88,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:'Open Sans';font-style:normal;font-weight:600;font-stretch:normal;src:url(https://fonts.gstatic.com/s/opensans/v28/memSYaGs126MiZpBA-UvWbX2vVnXBbObj2OVZyOOSr4dVJWUgsgH1x4kaVIGxA.woff2) format('woff2');unicode-range:U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:'Open Sans';font-style:normal;font-weight:600;font-stretch:normal;src:url(https://fonts.gstatic.com/s/opensans/v28/memSYaGs126MiZpBA-UvWbX2vVnXBbObj2OVZyOOSr4dVJWUgsgH1x4saVIGxA.woff2) format('woff2');unicode-range:U+1F00-1FFF}@font-face{font-family:'Open Sans';font-style:normal;font-weight:600;font-stretch:normal;src:url(https://fonts.gstatic.com/s/opensans/v28/memSYaGs126MiZpBA-UvWbX2vVnXBbObj2OVZyOOSr4dVJWUgsgH1x4jaVIGxA.woff2) format('woff2');unicode-range:U+0370-03FF}@font-face{font-family:'Open Sans';font-style:normal;font-weight:600;font-stretch:normal;src:url(https://fonts.gstatic.com/s/opensans/v28/memSYaGs126MiZpBA-UvWbX2vVnXBbObj2OVZyOOSr4dVJWUgsgH1x4iaVIGxA.woff2) format('woff2');unicode-range:U+0590-05FF,U+200C-2010,U+20AA,U+25CC,U+FB1D-FB4F}@font-face{font-family:'Open Sans';font-style:normal;font-weight:600;font-stretch:normal;src:url(https://fonts.gstatic.com/s/opensans/v28/memSYaGs126MiZpBA-UvWbX2vVnXBbObj2OVZyOOSr4dVJWUgsgH1x4vaVIGxA.woff2) format('woff2');unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+1EA0-1EF9,U+20AB}@font-face{font-family:'Open Sans';font-style:normal;font-weight:600;font-stretch:normal;src:url(https://fonts.gstatic.com/s/opensans/v28/memSYaGs126MiZpBA-UvWbX2vVnXBbObj2OVZyOOSr4dVJWUgsgH1x4uaVIGxA.woff2) format('woff2');unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}
|
||||
@font-face{font-family:'Open Sans';font-style:normal;font-weight:600;font-stretch:normal;src:url(https://fonts.gstatic.com/s/opensans/v28/memSYaGs126MiZpBA-UvWbX2vVnXBbObj2OVZyOOSr4dVJWUgsgH1x4gaVI.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
|
||||
1213
src/main/resources/static/doc/highlight.min.js
vendored
Normal file
1213
src/main/resources/static/doc/highlight.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
13
src/main/resources/static/doc/index.html
Normal file
13
src/main/resources/static/doc/index.html
Normal file
@ -0,0 +1,13 @@
|
||||
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="smart-doc"><title>多智体聊天</title><link rel="stylesheet" href="font.css"><link rel="stylesheet" href="AllInOne.css?v=1762843844040"/><style>.literalblock pre, .listingblock pre:not(.highlight), .listingblock pre[class="highlight"], .listingblock pre[class^="highlight "], .listingblock pre.CodeRay, .listingblock pre.prettyprint {background: #f7f7f8} .hljs {padding: 0}</style><script src="highlight.min.js"></script><script src="jquery.min.js"></script></head><body class="book toc2 toc-left"><div id="header"><h1>多智体聊天</h1><div id="toc" class="toc2"><div id="book-search-input"><input id="search" type="text" placeholder="Type to search"></div><div id="toctitle"><span>API Reference</span></div><ul id="accordion" class="sectlevel1"><li class="open"><a class="dd" href="#ChatController">1. </a><ul class="sectlevel2"><li><a href="#ab3df8e0a34a981c94521c2ded5fd656">1.1. </a></li></ul></li></ul></div></div><div id="content"><div id="preamble"><div class="sectionbody"><table class="tableblock frame-all grid-all spread"><colgroup><col style="width: 20%;"><col style="width: 20%;"><col style="width: 20%;"><col style="width: 20%;"><col style="width: 20%;"></colgroup><thead><tr><th class="tableblock halign-left valign-top">Version</th><th class="tableblock halign-left valign-top">Update Time</th><th class="tableblock halign-left valign-top">Status</th><th class="tableblock halign-left valign-top">Author</th><th class="tableblock halign-left valign-top">Description</th></tr></thead><tbody><tr><td class="tableblock halign-left valign-top"><p class="tableblock">v2025-11-11 14:50:44</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">2025-11-11 14:50:44</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">auto</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">@Krcia</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">Created by smart-doc</p></td></tr></tbody></table></div></div><div class="sect1"><h2 id="#ChatController"><a class="anchor" href="#ChatController"></a><a class="link" href="#ChatController">1. </a></h2><div class="sectionbody"><div class="sect2" id="ab3df8e0a34a981c94521c2ded5fd656"><h3 id="_1_1_1_"><a class="anchor" href="#ab3df8e0a34a981c94521c2ded5fd656"></a><a class="link" href="#ab3df8e0a34a981c94521c2ded5fd656">1.1. </a></h3><div class="paragraph" id="ab3df8e0a34a981c94521c2ded5fd656-url" data-url="/api/chat" data-download="false" data-page=""><p><strong>URL:</strong><a href="/api/chat" class="bare"> /api/chat</a></p></div><div class="paragraph" id="ab3df8e0a34a981c94521c2ded5fd656-method" data-method="POST"><p><strong>Type:</strong> POST</p></div><div class="paragraph" id="ab3df8e0a34a981c94521c2ded5fd656-content-type" data-content-type="application/json"><p><strong>Content-Type:</strong> application/json</p></div><div class="paragraph"><p><strong>Description:</strong> </p></div><div class="paragraph"><p><strong>Body-parameters:</strong></p></div><table class="tableblock frame-all grid-all spread"><colgroup><col style="width: 20%;"><col style="width: 20%;"><col style="width: 20%;"><col style="width: 20%;"></colgroup><thead><tr><th class="tableblock halign-left valign-top">Parameter</th><th class="tableblock halign-left valign-top">Type</th><th class="tableblock halign-left valign-top">Required</th><th class="tableblock halign-left valign-top">Description</th></tr></thead><tbody><tr><td class="tableblock halign-left valign-top"><p class="tableblock">userId</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">No comments found.</p></td></tr><tr><td class="tableblock halign-left valign-top"><p class="tableblock">sessionId</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">No comments found.</p></td></tr><tr><td class="tableblock halign-left valign-top"><p class="tableblock">message</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">No comments found.</p></td></tr></tbody></table><div class="paragraph"><p><strong>Request-body:</strong></p></div><div class="listingblock"><div class="content"><pre><code contenteditable="plaintext-only" id="ab3df8e0a34a981c94521c2ded5fd656-body">{
|
||||
"userId": "",
|
||||
"sessionId": "",
|
||||
"message": ""
|
||||
}</code></pre></div></div><div class="paragraph"><p><strong>Response-fields:</strong></p></div><table class="tableblock frame-all grid-all spread"><colgroup><col style="width: 25%;"><col style="width: 25%;"><col style="width: 25%;"><col style="width: 25%;"></colgroup><thead><tr><th class="tableblock halign-left valign-top">Field</th><th class="tableblock halign-left valign-top">Type</th><th class="tableblock halign-left valign-top">Description</th><th class="tableblock halign-left valign-top">Since</th></tr></thead><tbody><tr><td class="tableblock halign-left valign-top"><p class="tableblock">response</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">No comments found.</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">-</p></td></tr><tr><td class="tableblock halign-left valign-top"><p class="tableblock">agentType</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">No comments found.</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">-</p></td></tr><tr><td class="tableblock halign-left valign-top"><p class="tableblock">confidence</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">double</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">No comments found.</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">-</p></td></tr></tbody></table><div class="paragraph"><p><strong><button class="send-button" data-id="ab3df8e0a34a981c94521c2ded5fd656">Send Request</button></strong><span id="ab3df8e0a34a981c94521c2ded5fd656-resp-status"></span></p></div><div class="paragraph"><p><strong>Response-example:</strong></p></div><div class="listingblock"><div class="content" id="ab3df8e0a34a981c94521c2ded5fd656-response"><pre><code class="json">{
|
||||
"response": "",
|
||||
"agentType": "",
|
||||
"confidence": 0.0
|
||||
}</code></pre></div></div><div class="paragraph"><p><strong>Curl-example:</strong></p></div><div class="listingblock"><div class="content" id="ab3df8e0a34a981c94521c2ded5fd656-curl"><pre><code class="bash">curl -X POST -H "Content-Type: application/json" -i '/api/chat' --data '{
|
||||
"userId": "",
|
||||
"sessionId": "",
|
||||
"message": ""
|
||||
}'</code></pre></div></div></div></div></div><footer class="page-footer"><span class="copyright">Generated by smart-doc at 2025-11-11 14:50:44</span><span class="footer-modification">Suggestions,contact,support and error reporting on<a href="https://gitee.com/smart-doc-team/smart-doc" target="_blank"> Gitee</a> or<a href="https://github.com/smart-doc-group/smart-doc.git" target="_blank"> Github</a></span></footer><div href="javascript:void(0)" id="toTop"><img id="upArrow" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAABlUlEQVRIS+2UvUvDQBiH398Rly4udnARwUXs4qAIOigI4iL30dTZ2T9AcNPVvUsXF7uYttdScNDFRRAnB11cFFwKxcXBJTQnJ6lEbRI/CIiY9e6e5/e+9+ZAGX/ImE9/QKCU2jfGbGTQqq4xZgtSyisiKmQgIAAVCCFWAGxnIOhqrdd/xyUrpRZsP40xSwA6AI57vd5eq9W6T6s8tQIppSKi+gDQNREprfVNkiRRwDlfY4xZ+FAIuSOi8Qjw0nEc5XnebZwkViClXA2T5+xhY8xus9ncEUJMAziITN5FEARuXLsGCoQQywBs8uEovJ+Scz7FGDuMSM4cx3E9z+u8r+SDQEq5SEQ1IhoZBE+QnBKRq7V+iEreCDjn84wxCx9NgidITnK5nFutVh/7e14FSqnZIAhqAMY+A4+TADjyfb/Ubref7J4XQXhxNvnEV+AJlbTy+XypUqn4KBaLBZuciCa/A0+opN5oNFz7FpUBbP4EHicxxsyAcz7HGDvvz3nar5+2Ho5wOQwsU5+KNGDa+r8grUP0DBLjtRtNKEliAAAAAElFTkSuQmCC"><span id="upText">Top</span></div></div><script src="debug.js?v=1762843844040"></script><script src="search.js?v=1762843844040"></script><script>$(function () {$(window).scroll(function () {if ($(window).scrollTop() > 100) {let $toTop = $("#toTop");$toTop.fadeIn(1500);$toTop.hover(function () {$("#upArrow").hide();$("#upText").show()}, function () {$("#upArrow").show();$("#upText").hide()})} else {$("#toTop").fadeOut(1500)}});$("#toTop").click(function () { $("body,html").animate({scrollTop: 0}, 1000);return false})});</script></body></html>
|
||||
2
src/main/resources/static/doc/jquery.min.js
vendored
Normal file
2
src/main/resources/static/doc/jquery.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
173
src/main/resources/static/doc/search.js
Normal file
173
src/main/resources/static/doc/search.js
Normal file
@ -0,0 +1,173 @@
|
||||
let api = [];
|
||||
const apiDocListSize = 1
|
||||
api.push({
|
||||
name: 'default',
|
||||
order: '1',
|
||||
list: []
|
||||
})
|
||||
api[0].list.push({
|
||||
alias: 'ChatController',
|
||||
order: '1',
|
||||
link: '',
|
||||
desc: '',
|
||||
list: []
|
||||
})
|
||||
api[0].list[0].list.push({
|
||||
order: '1',
|
||||
deprecated: 'false',
|
||||
url: '/api/chat',
|
||||
methodId: 'ab3df8e0a34a981c94521c2ded5fd656',
|
||||
desc: '',
|
||||
});
|
||||
document.onkeydown = keyDownSearch;
|
||||
function keyDownSearch(e) {
|
||||
const theEvent = e;
|
||||
const code = theEvent.keyCode || theEvent.which || theEvent.charCode;
|
||||
if (code === 13) {
|
||||
const search = document.getElementById('search');
|
||||
const searchValue = search.value.toLocaleLowerCase();
|
||||
|
||||
let searchGroup = [];
|
||||
for (let i = 0; i < api.length; i++) {
|
||||
|
||||
let apiGroup = api[i];
|
||||
|
||||
let searchArr = [];
|
||||
for (let i = 0; i < apiGroup.list.length; i++) {
|
||||
let apiData = apiGroup.list[i];
|
||||
const desc = apiData.desc;
|
||||
if (desc.toLocaleLowerCase().indexOf(searchValue) > -1) {
|
||||
searchArr.push({
|
||||
order: apiData.order,
|
||||
desc: apiData.desc,
|
||||
link: apiData.link,
|
||||
alias: apiData.alias,
|
||||
list: apiData.list
|
||||
});
|
||||
} else {
|
||||
let methodList = apiData.list || [];
|
||||
let methodListTemp = [];
|
||||
for (let j = 0; j < methodList.length; j++) {
|
||||
const methodData = methodList[j];
|
||||
const methodDesc = methodData.desc;
|
||||
if (methodDesc.toLocaleLowerCase().indexOf(searchValue) > -1) {
|
||||
methodListTemp.push(methodData);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (methodListTemp.length > 0) {
|
||||
const data = {
|
||||
order: apiData.order,
|
||||
desc: apiData.desc,
|
||||
link: apiData.link,
|
||||
alias: apiData.alias,
|
||||
list: methodListTemp
|
||||
};
|
||||
searchArr.push(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (apiGroup.name.toLocaleLowerCase().indexOf(searchValue) > -1) {
|
||||
searchGroup.push({
|
||||
name: apiGroup.name,
|
||||
order: apiGroup.order,
|
||||
list: searchArr
|
||||
});
|
||||
continue;
|
||||
}
|
||||
if (searchArr.length === 0) {
|
||||
continue;
|
||||
}
|
||||
searchGroup.push({
|
||||
name: apiGroup.name,
|
||||
order: apiGroup.order,
|
||||
list: searchArr
|
||||
});
|
||||
}
|
||||
let html;
|
||||
if (searchValue === '') {
|
||||
const liClass = "";
|
||||
const display = "display: none";
|
||||
html = buildAccordion(api,liClass,display);
|
||||
document.getElementById('accordion').innerHTML = html;
|
||||
} else {
|
||||
const liClass = "open";
|
||||
const display = "display: block";
|
||||
html = buildAccordion(searchGroup,liClass,display);
|
||||
document.getElementById('accordion').innerHTML = html;
|
||||
}
|
||||
const Accordion = function (el, multiple) {
|
||||
this.el = el || {};
|
||||
this.multiple = multiple || false;
|
||||
const links = this.el.find('.dd');
|
||||
links.on('click', {el: this.el, multiple: this.multiple}, this.dropdown);
|
||||
};
|
||||
Accordion.prototype.dropdown = function (e) {
|
||||
const $el = e.data.el;
|
||||
let $this = $(this), $next = $this.next();
|
||||
$next.slideToggle();
|
||||
$this.parent().toggleClass('open');
|
||||
if (!e.data.multiple) {
|
||||
$el.find('.submenu').not($next).slideUp("20").parent().removeClass('open');
|
||||
}
|
||||
};
|
||||
new Accordion($('#accordion'), false);
|
||||
}
|
||||
}
|
||||
|
||||
function buildAccordion(apiGroups, liClass, display) {
|
||||
let html = "";
|
||||
if (apiGroups.length > 0) {
|
||||
if (apiDocListSize === 1) {
|
||||
let apiData = apiGroups[0].list;
|
||||
let order = apiGroups[0].order;
|
||||
for (let j = 0; j < apiData.length; j++) {
|
||||
html += '<li class="'+liClass+'">';
|
||||
html += '<a class="dd" href="#' + apiData[j].alias + '">' + apiData[j].order + '. ' + apiData[j].desc + '</a>';
|
||||
html += '<ul class="sectlevel2" style="'+display+'">';
|
||||
let doc = apiData[j].list;
|
||||
for (let m = 0; m < doc.length; m++) {
|
||||
let spanString;
|
||||
if (doc[m].deprecated === 'true') {
|
||||
spanString='<span class="line-through">';
|
||||
} else {
|
||||
spanString='<span>';
|
||||
}
|
||||
html += '<li><a href="#' + doc[m].methodId + '">' + apiData[j].order + '.' + doc[m].order + '. ' + spanString + doc[m].desc + '<span></a> </li>';
|
||||
}
|
||||
html += '</ul>';
|
||||
html += '</li>';
|
||||
}
|
||||
} else {
|
||||
for (let i = 0; i < apiGroups.length; i++) {
|
||||
let apiGroup = apiGroups[i];
|
||||
html += '<li class="'+liClass+'">';
|
||||
html += '<a class="dd" href="#_'+apiGroup.order+'_' + apiGroup.name + '">' + apiGroup.order + '. ' + apiGroup.name + '</a>';
|
||||
html += '<ul class="sectlevel1">';
|
||||
|
||||
let apiData = apiGroup.list;
|
||||
for (let j = 0; j < apiData.length; j++) {
|
||||
html += '<li class="'+liClass+'">';
|
||||
html += '<a class="dd" href="#' + apiData[j].alias + '">' +apiGroup.order+'.'+ apiData[j].order + '. ' + apiData[j].desc + '</a>';
|
||||
html += '<ul class="sectlevel2" style="'+display+'">';
|
||||
let doc = apiData[j].list;
|
||||
for (let m = 0; m < doc.length; m++) {
|
||||
let spanString;
|
||||
if (doc[m].deprecated === 'true') {
|
||||
spanString='<span class="line-through">';
|
||||
} else {
|
||||
spanString='<span>';
|
||||
}
|
||||
html += '<li><a href="#' + doc[m].methodId + '">'+apiGroup.order+'.' + apiData[j].order + '.' + doc[m].order + '. ' + spanString + doc[m].desc + '<span></a> </li>';
|
||||
}
|
||||
html += '</ul>';
|
||||
html += '</li>';
|
||||
}
|
||||
|
||||
html += '</ul>';
|
||||
html += '</li>';
|
||||
}
|
||||
}
|
||||
}
|
||||
return html;
|
||||
}
|
||||
13
src/test/java/top/krcia/cosmos/CosmosApplicationTests.java
Normal file
13
src/test/java/top/krcia/cosmos/CosmosApplicationTests.java
Normal file
@ -0,0 +1,13 @@
|
||||
package top.krcia.cosmos;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
@SpringBootTest
|
||||
class CosmosApplicationTests {
|
||||
|
||||
@Test
|
||||
void contextLoads() {
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user