From d4042d490c4518687d04a8489dda8a5d40c63e55 Mon Sep 17 00:00:00 2001 From: houcj Date: Wed, 19 Nov 2025 16:10:17 +0800 Subject: [PATCH] init the aiAudit project, commit local code --- .gitattributes | 2 + .gitignore | 33 ++ .mvn/wrapper/maven-wrapper.properties | 19 + mvnw | 259 ++++++++++++ mvnw.cmd | 149 +++++++ pom.xml | 114 +++++ .../techcenter/aidoc/AidocApplication.java | 13 + .../aidoc/controller/DocController.java | 151 +++++++ .../aidoc/controller/UserController.java | 18 + .../techcenter/aidoc/entity/HttpResult.java | 237 +++++++++++ .../aidoc/httpbo/apply/ModifiedItem.java | 26 ++ .../aidoc/httpbo/apply/ReqApplyModify.java | 16 + .../aidoc/httpbo/checkdoc/ResultContent.java | 31 ++ .../aidoc/httpbo/checkdoc/RspCheckResult.java | 36 ++ .../aidoc/httpbo/checkdoc/RspDataContent.java | 96 +++++ .../aidoc/httpbo/checkdoc/RspOutput.java | 15 + .../aidoc/httpbo/uploaddoc/RspUploadDoc.java | 68 +++ .../aidoc/rest/RestTemplateConfig.java | 20 + .../aidoc/rest/RestTemplateService.java | 398 ++++++++++++++++++ .../aidoc/service/AiDocService.java | 15 + .../aidoc/service/FileStorageService.java | 15 + .../techcenter/aidoc/service/LlmService.java | 7 + .../aidoc/service/WordDocumentService.java | 13 + .../aidoc/service/impl/AiDocServiceImpl.java | 140 ++++++ .../service/impl/CozeLlmServiceImpl.java | 256 +++++++++++ .../service/impl/FileStorageServiceImpl.java | 176 ++++++++ .../service/impl/WordDocumentServiceImpl.java | 129 ++++++ .../techcenter/aidoc/utils/AiDocConst.java | 16 + .../techcenter/aidoc/utils/LoggerUtil.java | 79 ++++ src/main/resources/application-dev.yml | 239 +++++++++++ src/main/resources/application.yml | 17 + src/main/resources/log4j2.xml | 21 + src/main/resources/mybatis/mybatis-config.xml | 16 + .../aidoc/AidocApplicationTests.java | 13 + 34 files changed, 2853 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 .mvn/wrapper/maven-wrapper.properties create mode 100644 mvnw create mode 100644 mvnw.cmd create mode 100644 pom.xml create mode 100644 src/main/java/com/flx/techcenter/aidoc/AidocApplication.java create mode 100644 src/main/java/com/flx/techcenter/aidoc/controller/DocController.java create mode 100644 src/main/java/com/flx/techcenter/aidoc/controller/UserController.java create mode 100644 src/main/java/com/flx/techcenter/aidoc/entity/HttpResult.java create mode 100644 src/main/java/com/flx/techcenter/aidoc/httpbo/apply/ModifiedItem.java create mode 100644 src/main/java/com/flx/techcenter/aidoc/httpbo/apply/ReqApplyModify.java create mode 100644 src/main/java/com/flx/techcenter/aidoc/httpbo/checkdoc/ResultContent.java create mode 100644 src/main/java/com/flx/techcenter/aidoc/httpbo/checkdoc/RspCheckResult.java create mode 100644 src/main/java/com/flx/techcenter/aidoc/httpbo/checkdoc/RspDataContent.java create mode 100644 src/main/java/com/flx/techcenter/aidoc/httpbo/checkdoc/RspOutput.java create mode 100644 src/main/java/com/flx/techcenter/aidoc/httpbo/uploaddoc/RspUploadDoc.java create mode 100644 src/main/java/com/flx/techcenter/aidoc/rest/RestTemplateConfig.java create mode 100644 src/main/java/com/flx/techcenter/aidoc/rest/RestTemplateService.java create mode 100644 src/main/java/com/flx/techcenter/aidoc/service/AiDocService.java create mode 100644 src/main/java/com/flx/techcenter/aidoc/service/FileStorageService.java create mode 100644 src/main/java/com/flx/techcenter/aidoc/service/LlmService.java create mode 100644 src/main/java/com/flx/techcenter/aidoc/service/WordDocumentService.java create mode 100644 src/main/java/com/flx/techcenter/aidoc/service/impl/AiDocServiceImpl.java create mode 100644 src/main/java/com/flx/techcenter/aidoc/service/impl/CozeLlmServiceImpl.java create mode 100644 src/main/java/com/flx/techcenter/aidoc/service/impl/FileStorageServiceImpl.java create mode 100644 src/main/java/com/flx/techcenter/aidoc/service/impl/WordDocumentServiceImpl.java create mode 100644 src/main/java/com/flx/techcenter/aidoc/utils/AiDocConst.java create mode 100644 src/main/java/com/flx/techcenter/aidoc/utils/LoggerUtil.java create mode 100644 src/main/resources/application-dev.yml create mode 100644 src/main/resources/application.yml create mode 100644 src/main/resources/log4j2.xml create mode 100644 src/main/resources/mybatis/mybatis-config.xml create mode 100644 src/test/java/com/flx/techcenter/aidoc/AidocApplicationTests.java diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..3b41682 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +/mvnw text eol=lf +*.cmd text eol=crlf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..667aaef --- /dev/null +++ b/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..12fbe1e --- /dev/null +++ b/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,19 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +wrapperVersion=3.3.2 +distributionType=only-script +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.11/apache-maven-3.9.11-bin.zip diff --git a/mvnw b/mvnw new file mode 100644 index 0000000..19529dd --- /dev/null +++ b/mvnw @@ -0,0 +1,259 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Apache Maven Wrapper startup batch script, version 3.3.2 +# +# Optional ENV vars +# ----------------- +# JAVA_HOME - location of a JDK home dir, required when download maven via java source +# MVNW_REPOURL - repo url base for downloading maven distribution +# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output +# ---------------------------------------------------------------------------- + +set -euf +[ "${MVNW_VERBOSE-}" != debug ] || set -x + +# OS specific support. +native_path() { printf %s\\n "$1"; } +case "$(uname)" in +CYGWIN* | MINGW*) + [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")" + native_path() { cygpath --path --windows "$1"; } + ;; +esac + +# set JAVACMD and JAVACCMD +set_java_home() { + # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched + if [ -n "${JAVA_HOME-}" ]; then + if [ -x "$JAVA_HOME/jre/sh/java" ]; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACCMD="$JAVA_HOME/jre/sh/javac" + else + JAVACMD="$JAVA_HOME/bin/java" + JAVACCMD="$JAVA_HOME/bin/javac" + + if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then + echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2 + echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2 + return 1 + fi + fi + else + JAVACMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v java + )" || : + JAVACCMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v javac + )" || : + + if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then + echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2 + return 1 + fi + fi +} + +# hash string like Java String::hashCode +hash_string() { + str="${1:-}" h=0 + while [ -n "$str" ]; do + char="${str%"${str#?}"}" + h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296)) + str="${str#?}" + done + printf %x\\n $h +} + +verbose() { :; } +[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; } + +die() { + printf %s\\n "$1" >&2 + exit 1 +} + +trim() { + # MWRAPPER-139: + # Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds. + # Needed for removing poorly interpreted newline sequences when running in more + # exotic environments such as mingw bash on Windows. + printf "%s" "${1}" | tr -d '[:space:]' +} + +# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties +while IFS="=" read -r key value; do + case "${key-}" in + distributionUrl) distributionUrl=$(trim "${value-}") ;; + distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;; + esac +done <"${0%/*}/.mvn/wrapper/maven-wrapper.properties" +[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in ${0%/*}/.mvn/wrapper/maven-wrapper.properties" + +case "${distributionUrl##*/}" in +maven-mvnd-*bin.*) + MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ + case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in + *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;; + :Darwin*x86_64) distributionPlatform=darwin-amd64 ;; + :Darwin*arm64) distributionPlatform=darwin-aarch64 ;; + :Linux*x86_64*) distributionPlatform=linux-amd64 ;; + *) + echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2 + distributionPlatform=linux-amd64 + ;; + esac + distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip" + ;; +maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;; +*) MVN_CMD="mvn${0##*/mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;; +esac + +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}" +distributionUrlName="${distributionUrl##*/}" +distributionUrlNameMain="${distributionUrlName%.*}" +distributionUrlNameMain="${distributionUrlNameMain%-bin}" +MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}" +MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")" + +exec_maven() { + unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || : + exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD" +} + +if [ -d "$MAVEN_HOME" ]; then + verbose "found existing MAVEN_HOME at $MAVEN_HOME" + exec_maven "$@" +fi + +case "${distributionUrl-}" in +*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;; +*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;; +esac + +# prepare tmp dir +if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then + clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; } + trap clean HUP INT TERM EXIT +else + die "cannot create temp dir" +fi + +mkdir -p -- "${MAVEN_HOME%/*}" + +# Download and Install Apache Maven +verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +verbose "Downloading from: $distributionUrl" +verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +# select .zip or .tar.gz +if ! command -v unzip >/dev/null; then + distributionUrl="${distributionUrl%.zip}.tar.gz" + distributionUrlName="${distributionUrl##*/}" +fi + +# verbose opt +__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR='' +[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v + +# normalize http auth +case "${MVNW_PASSWORD:+has-password}" in +'') MVNW_USERNAME='' MVNW_PASSWORD='' ;; +has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;; +esac + +if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then + verbose "Found wget ... using wget" + wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl" +elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then + verbose "Found curl ... using curl" + curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl" +elif set_java_home; then + verbose "Falling back to use Java to download" + javaSource="$TMP_DOWNLOAD_DIR/Downloader.java" + targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName" + cat >"$javaSource" <<-END + public class Downloader extends java.net.Authenticator + { + protected java.net.PasswordAuthentication getPasswordAuthentication() + { + return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() ); + } + public static void main( String[] args ) throws Exception + { + setDefault( new Downloader() ); + java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() ); + } + } + END + # For Cygwin/MinGW, switch paths to Windows format before running javac and java + verbose " - Compiling Downloader.java ..." + "$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java" + verbose " - Running Downloader.java ..." + "$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")" +fi + +# If specified, validate the SHA-256 sum of the Maven distribution zip file +if [ -n "${distributionSha256Sum-}" ]; then + distributionSha256Result=false + if [ "$MVN_CMD" = mvnd.sh ]; then + echo "Checksum validation is not supported for maven-mvnd." >&2 + echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + elif command -v sha256sum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c >/dev/null 2>&1; then + distributionSha256Result=true + fi + elif command -v shasum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then + distributionSha256Result=true + fi + else + echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2 + echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + fi + if [ $distributionSha256Result = false ]; then + echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2 + echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2 + exit 1 + fi +fi + +# unzip and move +if command -v unzip >/dev/null; then + unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip" +else + tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar" +fi +printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/mvnw.url" +mv -- "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME" + +clean || : +exec_maven "$@" diff --git a/mvnw.cmd b/mvnw.cmd new file mode 100644 index 0000000..249bdf3 --- /dev/null +++ b/mvnw.cmd @@ -0,0 +1,149 @@ +<# : batch portion +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Apache Maven Wrapper startup batch script, version 3.3.2 +@REM +@REM Optional ENV vars +@REM MVNW_REPOURL - repo url base for downloading maven distribution +@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output +@REM ---------------------------------------------------------------------------- + +@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0) +@SET __MVNW_CMD__= +@SET __MVNW_ERROR__= +@SET __MVNW_PSMODULEP_SAVE=%PSModulePath% +@SET PSModulePath= +@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @( + IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B) +) +@SET PSModulePath=%__MVNW_PSMODULEP_SAVE% +@SET __MVNW_PSMODULEP_SAVE= +@SET __MVNW_ARG0_NAME__= +@SET MVNW_USERNAME= +@SET MVNW_PASSWORD= +@IF NOT "%__MVNW_CMD__%"=="" (%__MVNW_CMD__% %*) +@echo Cannot start maven from wrapper >&2 && exit /b 1 +@GOTO :EOF +: end batch / begin powershell #> + +$ErrorActionPreference = "Stop" +if ($env:MVNW_VERBOSE -eq "true") { + $VerbosePreference = "Continue" +} + +# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties +$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl +if (!$distributionUrl) { + Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" +} + +switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) { + "maven-mvnd-*" { + $USE_MVND = $true + $distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip" + $MVN_CMD = "mvnd.cmd" + break + } + default { + $USE_MVND = $false + $MVN_CMD = $script -replace '^mvnw','mvn' + break + } +} + +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +if ($env:MVNW_REPOURL) { + $MVNW_REPO_PATTERN = if ($USE_MVND) { "/org/apache/maven/" } else { "/maven/mvnd/" } + $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace '^.*'+$MVNW_REPO_PATTERN,'')" +} +$distributionUrlName = $distributionUrl -replace '^.*/','' +$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$','' +$MAVEN_HOME_PARENT = "$HOME/.m2/wrapper/dists/$distributionUrlNameMain" +if ($env:MAVEN_USER_HOME) { + $MAVEN_HOME_PARENT = "$env:MAVEN_USER_HOME/wrapper/dists/$distributionUrlNameMain" +} +$MAVEN_HOME_NAME = ([System.Security.Cryptography.MD5]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join '' +$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME" + +if (Test-Path -Path "$MAVEN_HOME" -PathType Container) { + Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME" + Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" + exit $? +} + +if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) { + Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl" +} + +# prepare tmp dir +$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile +$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir" +$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null +trap { + if ($TMP_DOWNLOAD_DIR.Exists) { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } + } +} + +New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null + +# Download and Install Apache Maven +Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +Write-Verbose "Downloading from: $distributionUrl" +Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +$webclient = New-Object System.Net.WebClient +if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) { + $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD) +} +[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 +$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null + +# If specified, validate the SHA-256 sum of the Maven distribution zip file +$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum +if ($distributionSha256Sum) { + if ($USE_MVND) { + Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." + } + Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash + if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) { + Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property." + } +} + +# unzip and move +Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null +Rename-Item -Path "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" -NewName $MAVEN_HOME_NAME | Out-Null +try { + Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null +} catch { + if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) { + Write-Error "fail to move MAVEN_HOME" + } +} finally { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } +} + +Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..425baf3 --- /dev/null +++ b/pom.xml @@ -0,0 +1,114 @@ + + + 4.0.0 + + + com.philisense.www.parent + zngf_server_business_parent + 0.0.1-RELEASE + + + + com.flx.techcenter + aidoc + 0.0.1-SNAPSHOT + aidoc + aidoc + + + + + + + + + + + + + + + 18 + + + + + com.alibaba + druid-spring-boot-starter + 1.2.27 + + + + mysql + mysql-connector-java + 8.0.11 + + + + org.springframework.boot + spring-boot-starter + + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-logging + + + + + + com.alibaba + fastjson + 2.0.9 + pom + + + javax.annotation + javax.annotation-api + 1.3.2 + + + + org.apache.poi + poi-ooxml + 4.1.2 + + + + org.apache.commons + commons-lang3 + 3.12.0 + + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + org.apache.maven.plugins + maven-compiler-plugin + + 11 + 11 + + + + + + diff --git a/src/main/java/com/flx/techcenter/aidoc/AidocApplication.java b/src/main/java/com/flx/techcenter/aidoc/AidocApplication.java new file mode 100644 index 0000000..df39d40 --- /dev/null +++ b/src/main/java/com/flx/techcenter/aidoc/AidocApplication.java @@ -0,0 +1,13 @@ +package com.flx.techcenter.aidoc; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class AidocApplication { + + public static void main(String[] args) { + SpringApplication.run(AidocApplication.class, args); + } + +} diff --git a/src/main/java/com/flx/techcenter/aidoc/controller/DocController.java b/src/main/java/com/flx/techcenter/aidoc/controller/DocController.java new file mode 100644 index 0000000..1eec23b --- /dev/null +++ b/src/main/java/com/flx/techcenter/aidoc/controller/DocController.java @@ -0,0 +1,151 @@ +package com.flx.techcenter.aidoc.controller; + +import com.alibaba.fastjson2.JSON; +import com.flx.techcenter.aidoc.entity.HttpResult; +import com.flx.techcenter.aidoc.httpbo.apply.ReqApplyModify; +import com.flx.techcenter.aidoc.httpbo.checkdoc.RspCheckResult; +import com.flx.techcenter.aidoc.rest.RestTemplateService; +import com.flx.techcenter.aidoc.service.AiDocService; +import com.flx.techcenter.aidoc.service.FileStorageService; +import com.flx.techcenter.aidoc.service.LlmService; +import com.flx.techcenter.aidoc.service.WordDocumentService; +import com.flx.techcenter.aidoc.utils.AiDocConst; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + +import javax.servlet.http.HttpServletResponse; +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +@RestController() +@RequestMapping("api") +public class DocController { + + @Autowired + private AiDocService aiDocService; + @Autowired + FileStorageService fileStorageServiceImpl; + @Autowired + RestTemplateService restTemplate; + + @Autowired + private WordDocumentService wordService; + + private static final Logger logger = LogManager.getLogger(DocController.class); + + @GetMapping("/test") + public String test(){ + System.out.println("test"); + return "test"; + } + + + @Autowired + @Qualifier("cozeLlmServiceImpl") + private LlmService llmService; + // 前端通过这个接口获取流式响应 + @GetMapping("/chat/stream") + public SseEmitter streamChat(@RequestParam String prompt) { + + System.out.println("chat/stream"); + return llmService.chat(prompt); + } + + @GetMapping("/modifydoc")//for test + public HttpResult processDocument() throws IOException { + File inputFile = new File("d:/tem2/input.docx"); + File outputFile = new File("d:/tem2/output.docx"); + + Map replace = new HashMap<>(); + replace.put("今天早上。", "南京"); + + return wordService.replaceTextInDocument(inputFile, outputFile, replace); + + } + + @PostMapping("/upload/word") + public ResponseEntity handleWordFileUpload(@RequestParam("file") MultipartFile file) { + return fileStorageServiceImpl.handleWordFileUpload(file); + } + + @PostMapping("/upload/runflow") + public HttpResult runFlow(@RequestParam("file") MultipartFile file, String prompt) { + return restTemplate.runFlow(file, prompt); + } + + @PostMapping("/runcheckdoc") + public HttpResult handleDocCheck(){ + HttpResult result = HttpResult.success(); + String msg = "处理完成"; + result.setMsg(msg); + String res = restTemplate.runCheckingDoc("", "帮我检查里面的标点符号错误");// + System.out.println(res); + + RspCheckResult rspResult = JSON.parseObject(res, RspCheckResult.class); + String rspJsonStr = JSON.toJSONString(rspResult.getData().getOutputs().getResult()); + System.out.println("response: " + rspJsonStr); + result.setData(rspJsonStr); + return result; + } + + @GetMapping("/upload") + public String getUploadResponse(){ + System.out.println("test upload begin"); + String res = restTemplate.uploadFile("abc-123","d:/tem2/test.docx/", AiDocConst.UPLOAD_TOKEN); + return res; + } + + @GetMapping("/runcheck") + public String getCheckResponse(){ + System.out.println("test run check begin"); + String res = restTemplate.runCheckingDoc("", "帮我检查里面的标点符号错误"); + System.out.println(res); + + //RspCheckResult result = JSON.parseObject(res, RspCheckResult.class); + //RspOutput rspOutput = result.getData().getOutputs(); + return res + " end"; + } + + + @PostMapping("/upload/runcheck") + public HttpResult handleUploadAndRun(@RequestParam("file") MultipartFile file, @RequestParam("promp") String promp ) { + System.out.println("handleWordFileUpload begin: *********"); + HttpResult result = aiDocService.runCheckDoc(file, promp); + System.out.println(result); + return result; + } + + /** + * 下载Word文档 + */ + @GetMapping("/download") + public void downloadWordFile(HttpServletResponse response) { + try { + System.out.println("downloadWordFile begin"); + restTemplate.downloadFile("", response); + } catch (Exception e) { + logger.error("下载文件出错: {}", e.getMessage()); + } + + } + + @PostMapping("/doc/applymodify") + public HttpResult applyModify(@RequestBody ReqApplyModify reqApplyModify ) { + String fileName = restTemplate.getCurrentFileName(); + System.out.println("handle modify apply begin: *********"); + HttpResult result = aiDocService.applyModify(reqApplyModify, fileName); + //restTemplate.cleanMap(); + logger.info("apply modify: {}", result); + return result; + } + + +} diff --git a/src/main/java/com/flx/techcenter/aidoc/controller/UserController.java b/src/main/java/com/flx/techcenter/aidoc/controller/UserController.java new file mode 100644 index 0000000..328d4d7 --- /dev/null +++ b/src/main/java/com/flx/techcenter/aidoc/controller/UserController.java @@ -0,0 +1,18 @@ +package com.flx.techcenter.aidoc.controller; + +import com.flx.techcenter.aidoc.entity.HttpResult; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping ("user") +public class UserController { + @GetMapping("/ailogin") + public HttpResult login(String username, String password) { + return HttpResult.success(); + } + + + +} diff --git a/src/main/java/com/flx/techcenter/aidoc/entity/HttpResult.java b/src/main/java/com/flx/techcenter/aidoc/entity/HttpResult.java new file mode 100644 index 0000000..4731911 --- /dev/null +++ b/src/main/java/com/flx/techcenter/aidoc/entity/HttpResult.java @@ -0,0 +1,237 @@ +package com.flx.techcenter.aidoc.entity; + + +import org.apache.poi.openxml4j.exceptions.OpenXML4JException; + +import java.util.HashMap; +import java.util.Objects; + +/** + * 操作消息提醒 + * + * @author ruoyi + */ +public class HttpResult extends HashMap +{ + private static final long serialVersionUID = 1L; + + /** 状态码 */ + public static final String CODE_TAG = "code"; + + /** 返回内容 */ + public static final String MSG_TAG = "msg"; + + /** 数据对象 */ + public static final String DATA_TAG = "data"; + + /** + * 状态类型 + */ + public enum Type + { + /** 成功 */ + SUCCESS(200), + /** 警告 */ + WARN(301), + /** 错误 */ + ERROR(500); + private final int value; + + Type(int value) + { + this.value = value; + } + + public int value() + { + return this.value; + } + } + + /** + * 初始化一个新创建的 AjaxResult 对象,使其表示一个空消息。 + */ + public HttpResult() + { + } + + /** + * 初始化一个新创建的 AjaxResult 对象 + * + * @param type 状态类型 + * @param msg 返回内容 + */ + public HttpResult(Type type, String msg) + { + super.put(CODE_TAG, type.value); + super.put(MSG_TAG, msg); + } + + /** + * 初始化一个新创建的 AjaxResult 对象 + * + * @param type 状态类型 + * @param msg 返回内容 + * @param data 数据对象 + */ + public HttpResult(Type type, String msg, Object data) + { + super.put(CODE_TAG, type.value); + super.put(MSG_TAG, msg); + if (data != null) + { + super.put(DATA_TAG, data); + } + } + + /** + * 返回成功消息 + * + * @return 成功消息 + */ + public static HttpResult success() + { + return HttpResult.success("操作成功"); + } + + /** + * 返回成功数据 + * + * @return 成功消息 + */ + public static HttpResult success(Object data) + { + return HttpResult.success("操作成功", data); + } + + /** + * 返回成功消息 + * + * @param msg 返回内容 + * @return 成功消息 + */ + public static HttpResult success(String msg) + { + return HttpResult.success(msg, null); + } + + public void setData(Object data){ + this.put(DATA_TAG, data); + } + + public void setMsg(String msg){ + this.put(MSG_TAG, msg); + } + + /** + * 返回成功消息 + * + * @param msg 返回内容 + * @param data 数据对象 + * @return 成功消息 + */ + public static HttpResult success(String msg, Object data) + { + return new HttpResult(Type.SUCCESS, msg, data); + } + + /** + * 返回警告消息 + * + * @param msg 返回内容 + * @return 警告消息 + */ + public static HttpResult warn(String msg) + { + return HttpResult.warn(msg, null); + } + + /** + * 返回警告消息 + * + * @param msg 返回内容 + * @param data 数据对象 + * @return 警告消息 + */ + public static HttpResult warn(String msg, Object data) + { + return new HttpResult(Type.WARN, msg, data); + } + + /** + * 返回错误消息 + * + * @return + */ + public static HttpResult error() + { + return HttpResult.error("操作失败"); + } + + /** + * 返回错误消息 + * + * @param msg 返回内容 + * @return 警告消息 + */ + public static HttpResult error(String msg) + { + return HttpResult.error(msg, null); + } + + /** + * 返回错误消息 + * + * @param msg 返回内容 + * @param data 数据对象 + * @return 警告消息 + */ + public static HttpResult error(String msg, Object data) + { + return new HttpResult(Type.ERROR, msg, data); + } + + /** + * 是否为成功消息 + * + * @return 结果 + */ + public boolean isSuccess() + { + return Objects.equals(Type.SUCCESS.value, this.get(CODE_TAG)); + } + + /** + * 是否为警告消息 + * + * @return 结果 + */ + public boolean isWarn() + { + return Objects.equals(Type.WARN.value, this.get(CODE_TAG)); + } + + /** + * 是否为错误消息 + * + * @return 结果 + */ + public boolean isError() + { + return Objects.equals(Type.ERROR.value, this.get(CODE_TAG)); + } + + /** + * 方便链式调用 + * + * @param key 键 + * @param value 值 + * @return 数据对象 + */ + @Override + public HttpResult put(String key, Object value) + { + super.put(key, value); + return this; + } +} diff --git a/src/main/java/com/flx/techcenter/aidoc/httpbo/apply/ModifiedItem.java b/src/main/java/com/flx/techcenter/aidoc/httpbo/apply/ModifiedItem.java new file mode 100644 index 0000000..15f8b27 --- /dev/null +++ b/src/main/java/com/flx/techcenter/aidoc/httpbo/apply/ModifiedItem.java @@ -0,0 +1,26 @@ +package com.flx.techcenter.aidoc.httpbo.apply; + +/** + * 客户端选择的修改内容 + */ +public class ModifiedItem { + + private String oldContent; + private String newContent; + + public String getOldContent() { + return oldContent; + } + + public void setOldContent(String oldContent) { + this.oldContent = oldContent; + } + + public String getNewContent() { + return newContent; + } + + public void setNewContent(String newContent) { + this.newContent = newContent; + } +} diff --git a/src/main/java/com/flx/techcenter/aidoc/httpbo/apply/ReqApplyModify.java b/src/main/java/com/flx/techcenter/aidoc/httpbo/apply/ReqApplyModify.java new file mode 100644 index 0000000..3baf8b3 --- /dev/null +++ b/src/main/java/com/flx/techcenter/aidoc/httpbo/apply/ReqApplyModify.java @@ -0,0 +1,16 @@ +package com.flx.techcenter.aidoc.httpbo.apply; + +import java.util.List; + +public class ReqApplyModify { + + public List content; + + public List getContent() { + return content; + } + + public void setContent(List itemList) { + this.content = itemList; + } +} diff --git a/src/main/java/com/flx/techcenter/aidoc/httpbo/checkdoc/ResultContent.java b/src/main/java/com/flx/techcenter/aidoc/httpbo/checkdoc/ResultContent.java new file mode 100644 index 0000000..09c0f54 --- /dev/null +++ b/src/main/java/com/flx/techcenter/aidoc/httpbo/checkdoc/ResultContent.java @@ -0,0 +1,31 @@ +package com.flx.techcenter.aidoc.httpbo.checkdoc; + +public class ResultContent { + private String error; + private String context; + private String update; + + public String getError() { + return error; + } + + public void setError(String error) { + this.error = error; + } + + public String getContext() { + return context; + } + + public void setContext(String context) { + this.context = context; + } + + public String getUpdate() { + return update; + } + + public void setUpdate(String update) { + this.update = update; + } +} diff --git a/src/main/java/com/flx/techcenter/aidoc/httpbo/checkdoc/RspCheckResult.java b/src/main/java/com/flx/techcenter/aidoc/httpbo/checkdoc/RspCheckResult.java new file mode 100644 index 0000000..f680e39 --- /dev/null +++ b/src/main/java/com/flx/techcenter/aidoc/httpbo/checkdoc/RspCheckResult.java @@ -0,0 +1,36 @@ +package com.flx.techcenter.aidoc.httpbo.checkdoc; + +public class RspCheckResult { + + private String task_id; + private String workflow_run_id; + private RspDataContent data; + + + + public String getTask_id() { + return task_id; + } + + public void setTask_id(String task_id) { + this.task_id = task_id; + } + + public String getWorkflow_run_id() { + return workflow_run_id; + } + + public void setWorkflow_run_id(String workflow_run_id) { + this.workflow_run_id = workflow_run_id; + } + + public RspDataContent getData() { + return data; + } + + public void setData(RspDataContent data) { + this.data = data; + } + + +} diff --git a/src/main/java/com/flx/techcenter/aidoc/httpbo/checkdoc/RspDataContent.java b/src/main/java/com/flx/techcenter/aidoc/httpbo/checkdoc/RspDataContent.java new file mode 100644 index 0000000..ca71452 --- /dev/null +++ b/src/main/java/com/flx/techcenter/aidoc/httpbo/checkdoc/RspDataContent.java @@ -0,0 +1,96 @@ +package com.flx.techcenter.aidoc.httpbo.checkdoc; + +public class RspDataContent { + + + private String id; + private String workflow_id; + private String status; + private RspOutput outputs; + private String error; + private String elapsed_time; + private String total_tokens; + private String total_steps; + private String created_at; + private String finished_at; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getWorkflow_id() { + return workflow_id; + } + + public void setWorkflow_id(String workflow_id) { + this.workflow_id = workflow_id; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public RspOutput getOutputs() { + return outputs; + } + + public void setOutputs(RspOutput outputs) { + this.outputs = outputs; + } + + public String getError() { + return error; + } + + public void setError(String error) { + this.error = error; + } + + public String getElapsed_time() { + return elapsed_time; + } + + public void setElapsed_time(String elapsed_time) { + this.elapsed_time = elapsed_time; + } + + public String getTotal_tokens() { + return total_tokens; + } + + public void setTotal_tokens(String total_tokens) { + this.total_tokens = total_tokens; + } + + public String getTotal_steps() { + return total_steps; + } + + public void setTotal_steps(String total_steps) { + this.total_steps = total_steps; + } + + public String getCreated_at() { + return created_at; + } + + public void setCreated_at(String created_at) { + this.created_at = created_at; + } + + public String getFinished_at() { + return finished_at; + } + + public void setFinished_at(String finished_at) { + this.finished_at = finished_at; + } +} diff --git a/src/main/java/com/flx/techcenter/aidoc/httpbo/checkdoc/RspOutput.java b/src/main/java/com/flx/techcenter/aidoc/httpbo/checkdoc/RspOutput.java new file mode 100644 index 0000000..754f9c4 --- /dev/null +++ b/src/main/java/com/flx/techcenter/aidoc/httpbo/checkdoc/RspOutput.java @@ -0,0 +1,15 @@ +package com.flx.techcenter.aidoc.httpbo.checkdoc; + +import java.util.List; + +public class RspOutput { + + private List result; + public List getResult() { + return result; + } + public void setResult(List result) { + this.result = result; + } + +} diff --git a/src/main/java/com/flx/techcenter/aidoc/httpbo/uploaddoc/RspUploadDoc.java b/src/main/java/com/flx/techcenter/aidoc/httpbo/uploaddoc/RspUploadDoc.java new file mode 100644 index 0000000..22f2c46 --- /dev/null +++ b/src/main/java/com/flx/techcenter/aidoc/httpbo/uploaddoc/RspUploadDoc.java @@ -0,0 +1,68 @@ +package com.flx.techcenter.aidoc.httpbo.uploaddoc; + +public class RspUploadDoc { + + private String id; + private String name; + private String size; + private String extension; + private String mime_type; + private String created_by; + private String created_at; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getSize() { + return size; + } + + public void setSize(String size) { + this.size = size; + } + + public String getExtension() { + return extension; + } + + public void setExtension(String extension) { + this.extension = extension; + } + + public String getMime_type() { + return mime_type; + } + + public void setMime_type(String mime_type) { + this.mime_type = mime_type; + } + + public String getCreated_by() { + return created_by; + } + + public void setCreated_by(String created_by) { + this.created_by = created_by; + } + + public String getCreated_at() { + return created_at; + } + + public void setCreated_at(String created_at) { + this.created_at = created_at; + } +} diff --git a/src/main/java/com/flx/techcenter/aidoc/rest/RestTemplateConfig.java b/src/main/java/com/flx/techcenter/aidoc/rest/RestTemplateConfig.java new file mode 100644 index 0000000..75a9d44 --- /dev/null +++ b/src/main/java/com/flx/techcenter/aidoc/rest/RestTemplateConfig.java @@ -0,0 +1,20 @@ +package com.flx.techcenter.aidoc.rest; + +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; + +import java.time.Duration; + +@Configuration +public class RestTemplateConfig { + + @Bean + public RestTemplate restTemplate(RestTemplateBuilder builder) { + return builder + .setConnectTimeout(Duration.ofSeconds(1000)) + .setReadTimeout(Duration.ofSeconds(300)) + .build(); + } +} diff --git a/src/main/java/com/flx/techcenter/aidoc/rest/RestTemplateService.java b/src/main/java/com/flx/techcenter/aidoc/rest/RestTemplateService.java new file mode 100644 index 0000000..f188c6a --- /dev/null +++ b/src/main/java/com/flx/techcenter/aidoc/rest/RestTemplateService.java @@ -0,0 +1,398 @@ +package com.flx.techcenter.aidoc.rest; + +import com.alibaba.fastjson2.JSON; +import com.flx.techcenter.aidoc.entity.HttpResult; +import com.flx.techcenter.aidoc.httpbo.checkdoc.RspCheckResult; +import com.flx.techcenter.aidoc.httpbo.uploaddoc.RspUploadDoc; +import com.flx.techcenter.aidoc.service.FileStorageService; +import com.flx.techcenter.aidoc.utils.AiDocConst; +import org.apache.commons.lang3.time.StopWatch; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.FileSystemResource; +import org.springframework.core.io.Resource; +import org.springframework.http.*; +import org.springframework.stereotype.Service; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.http.HttpServletResponse; +import java.io.File; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +@Service +public class RestTemplateService { + + @Autowired + private RestTemplate restTemplate; + + private static final Logger logger = LogManager.getLogger(RestTemplateService.class); + + private static HashMap currentFileName = new HashMap<>(); + + /** + * 上传文件到指定接口 + * @param user 用户参数 + * @param filePath 文件路径 + * @param authToken 认证token + * @return 接口响应结果 + */ + public String uploadFile(String user, String filePath, String authToken) { + // 接口URL + String url = "http://lingshu-lzf-test.philisensedev.com/v1/files/upload"; + + // 设置请求头:cite[1]:cite[2]:cite[4] + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.MULTIPART_FORM_DATA); + headers.set("Authorization", "Bearer " + authToken); + headers.set("Accept", "*/*"); + headers.set("Accept-Language", "zh-CN,zh;q=0.9"); + headers.set("Cache-Control", "no-cache"); + headers.set("Pragma", "no-cache"); + headers.set("Connection", "keep-alive"); + headers.set("Sec-Fetch-Dest", "empty"); + headers.set("Sec-Fetch-Mode", "cors"); + headers.set("Sec-Fetch-Site", "same-origin"); + headers.set("sec-ch-ua", "\"Not)A;Brand\";v=\"8\", \"Chromium\";v=\"138\", \"Google Chrome\";v=\"138\""); + + // 创建文件资源:cite[2]:cite[10] + FileSystemResource fileResource = new FileSystemResource(new File(filePath)); + + // 构建请求体:cite[1]:cite[2]:cite[4] + MultiValueMap body = new LinkedMultiValueMap<>(); + body.add("user", user); // 添加user参数 + body.add("file", fileResource); // 添加文件参数 + + // 创建请求实体 + HttpEntity> requestEntity = new HttpEntity<>(body, headers); + + try { + // 发送POST请求:cite[1] + ResponseEntity response = restTemplate.exchange( + url, + HttpMethod.POST, + requestEntity, + String.class + ); + + // 返回上传后生成的文件ID + if (response.getStatusCode() == HttpStatus.CREATED) { + String responseBody = response.getBody(); + RspUploadDoc rsp = JSON.parseObject(responseBody, RspUploadDoc.class); + if(null != rsp){ + return rsp.getId(); + } else { + return "上传给大模型失败"; + } + } else { + throw new RuntimeException("上传失败,状态码: " + response.getStatusCode()); + } + } catch (Exception e) { + throw new RuntimeException("调用上传接口失败: " + e.getMessage(), e); + } + } + + + /** + * 使用blocking模式调用大模型 + */ + + public String runCheckingDoc(String resId, String promp) { + + String url = "http://lingshu-lzf-test.philisensedev.com/v1/workflows/run"; + + // 设置请求头:cite[1]:cite[2]:cite[4] + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.valueOf(MediaType.APPLICATION_JSON_VALUE)); + headers.set("Authorization", "Bearer " + "app-1DZ9zBTDSuc3zUlzjQDFWKLZ"); + + + StopWatch stopWatch = new StopWatch(); + stopWatch.start(); + // 构建请求体:cite[1]:cite[2]:cite[4] + HashMap body = buildRequestBody(resId, promp); + + + // 创建请求实体 + HttpEntity> requestEntity = new HttpEntity<>(body, headers); + + try { + // 发送POST请求:cite[1] + ResponseEntity response = restTemplate.exchange( + url, + HttpMethod.POST, + requestEntity, + String.class + ); + System.out.println("response: " + response); + stopWatch.stop(); + System.out.println("Execution time: " + stopWatch.getTime() + " ms"); + + // 返回响应结果 + if (response.getStatusCode() == HttpStatus.OK) { + String responseBody = response.getBody().replace("\\n", ""); + String res = decodeUnicode(responseBody); + System.out.println(res); + logger.info(res); + //RspCheckResult rspOutput = JSON.parseObject(res, RspCheckResult.class); + return decodeUnicode(res); + } else { + logger.error("上传失败,状态码: {} ", response.getStatusCode()); + throw new RuntimeException("上传失败,状态码: " + response.getStatusCode()); + } + } catch (Exception e) { + throw new RuntimeException("调用上传接口失败: " + e.getMessage(), e); + } + + } + + private HashMap buildRequestBody(String resId, String promp) { + HashMap requestMap = new HashMap(); + String fileId = resId.isEmpty() ? "3bf74eca-1e66-4b98-978a-cc47213e87ea" : resId; + + HashMap inputMap = new HashMap(); + inputMap.put("prompt", promp); + HashMap wordMap = new HashMap(); + wordMap.put("type", "document"); + wordMap.put("transfer_method", "local_file"); + wordMap.put("upload_file_id", fileId); + inputMap.put("word", wordMap); + requestMap.put("user", "abc-123"); + requestMap.put("inputs", inputMap); + requestMap.put("response_mode", "blocking"); + return requestMap; + } + + public static String decodeUnicode(String unicodeStr) { + Pattern pattern = Pattern.compile("\\\\u([0-9a-fA-F]{4})"); + Matcher matcher = pattern.matcher(unicodeStr); + StringBuffer sb = new StringBuffer(); + + while (matcher.find()) { + // 把16进制转成char + char ch = (char) Integer.parseInt(matcher.group(1), 16); + matcher.appendReplacement(sb, String.valueOf(ch)); + } + matcher.appendTail(sb); + String result = sb.toString(); + System.out.println(result); + String cleanedStr = cleanAllMarkdownVariants(result); + + String processedStr = cleanJsonStringPrecise(cleanedStr); + if(JSON.isValid(processedStr)){ + return processedStr; + } else { + return removeMalformedStr(processedStr); + } + } + + /** + * 更精确的清理方法,使用正则表达式匹配并替换 + */ + public static String cleanJsonStringPrecise(String dirtyJson) { + if (dirtyJson == null || dirtyJson.isEmpty()) { + return dirtyJson; + } + + // 匹配被引号包围的JSON对象字符串(包含转义字符) + Pattern pattern = Pattern.compile("\"\\s*\\{([^}]*)\\}\"\\s*"); + Matcher matcher = pattern.matcher(dirtyJson); + StringBuffer result = new StringBuffer(); + + while (matcher.find()) { + // 获取匹配到的内容并移除转义字符 + String matchedContent = matcher.group(1); + String cleanedContent = matchedContent.replaceAll("\\\\\"", "\"") + .replaceAll("\\\\\\\\", "\\\\"); + + // 重新构建JSON对象(不加引号) + matcher.appendReplacement(result, "{" + cleanedContent + "}"); + } + matcher.appendTail(result); + + // 处理markdown代码块 + String finalResult = result.toString() + .replaceAll("```json\\\\n", "") + .replaceAll("\\\\n```", ""); + + return finalResult; + } + + /** + * 使用正则表达式移除JSON字符串中的markdown代码块标记 + */ + public static String removeJsonMarkdownBlocks(String input) { + if (input == null || input.isEmpty()) { + return input; + } + + // 正则表达式匹配 ```json\n{...}\n``` 模式 + // 模式解释: + // - ```json\\n : 匹配开始的markdown代码块标记 + // - (.*?) : 非贪婪匹配中间的JSON内容 + // - \\n``` : 匹配结束的markdown代码块标记 + Pattern pattern = Pattern.compile("```json(.*?)```"); + Matcher matcher = pattern.matcher(input); + + StringBuffer result = new StringBuffer(); + while (matcher.find()) { + // 获取匹配到的JSON内容(不包含markdown标记) + String jsonContent = matcher.group(1); + // 替换为纯JSON内容 + matcher.appendReplacement(result, jsonContent); + } + matcher.appendTail(result); + + return result.toString(); + } + + /** + * 更全面的清理方法,处理多种markdown变体 + */ + public static String cleanAllMarkdownVariants(String input) { + if (input == null || input.isEmpty()) { + return input; + } + + // 处理 ```json\n{...}\n``` 模式 + String cleaned = removeJsonMarkdownBlocks(input); + + // 处理其他可能的markdown变体 + cleaned = cleaned.replaceAll("```json\\\\n", "") + .replaceAll("\\\\n```", "") + .replaceAll("```javascript\\\\n", "") + .replaceAll("```\\\\n", ""); + + return cleaned; + } + + /** + * 处理大模型幻觉输出的不规范json串 + * @param input + * @return + */ + private static String removeMalformedStr(String input) { + int begin = input.indexOf("\"result\": [") + ("\"result\": [").length(); + int end = input.indexOf("]}"); + String toHandledStr = input.substring(begin, end); + StringBuilder sb = new StringBuilder(); + if(toHandledStr.contains("},")) { + String[] ar = toHandledStr.split("},"); + for (int i = 0; i < ar.length; i++) { + ar[i] = ar[i] + "},"; + if (JSON.isValid(ar[i])) { + sb.append(ar[i]); + } + } + sb.deleteCharAt(sb.length() - 1); + } else if(!toHandledStr.contains("[{")) { + sb.append("\"result\":[\"{\\\"error\\\":false,\\\"context\\\":null,\\\"update\\\":null}\"]}"); + } + return input.substring(0, begin) + sb.toString() + input.substring(end); + } + + /** + * 下载Word文档 - 带中文文件名支持 + */ + @Autowired + FileStorageService fileStorageServiceImpl; + public void downloadFile(String filename, HttpServletResponse response) { + try { + filename = getOutFileName(); + if(filename == null || filename.isEmpty()){ + filename = "d:/tem2/测试文档1.docx"; + } + // 加载文件资源 + Resource resource = fileStorageServiceImpl.loadFileAsResource(filename); + + // 设置响应头 +// String contentType = fileStorageService.getFileContentType(filename); +// response.setContentType(contentType); + response.setContentLengthLong(fileStorageServiceImpl.getFileSize(filename)); + if(filename.contains("out_")){ + filename = filename.substring(filename.indexOf("out_")); + } + response.setHeader(HttpHeaders.CONTENT_DISPOSITION, + "attachment; filename=\"" + encodeFilename(filename) + "\""); + + + // 写入响应流 + try (var inputStream = resource.getInputStream(); + var outputStream = response.getOutputStream()) { + + byte[] buffer = new byte[4096]; + int bytesRead; + while ((bytesRead = inputStream.read(buffer)) != -1) { + outputStream.write(buffer, 0, bytesRead); + } + outputStream.flush(); + } + + } catch (Exception e) { + response.setStatus(HttpServletResponse.SC_NOT_FOUND); + } + } + + /** + * URL编码文件名(支持中文) + */ + private String encodeFilename(String filename) { + try { + return URLEncoder.encode(filename, StandardCharsets.UTF_8.toString()) + .replace("+", "%20"); + } catch (UnsupportedEncodingException e) { + return filename; + } + } + + public HttpResult runFlow(MultipartFile file, String prompt) { + + ResponseEntity response = fileStorageServiceImpl.handleWordFileUpload(file);//客户端上传文件 + String fileName = AiDocConst.UPLOAD_DIR + response.getBody(); + currentFileName.put("current", fileName); + String resId = uploadFile("abc-123",fileName, AiDocConst.UPLOAD_TOKEN); + String runRes = runCheckingDoc(resId, prompt); + + HttpResult result = HttpResult.success(); + String msg = "处理完成"; + result.setMsg(msg); + + System.out.println("runRes: " + runRes); + if(JSON.isValid(runRes)) { + RspCheckResult rspResult = JSON.parseObject(runRes, RspCheckResult.class); + if (rspResult.getData().getOutputs() == null) { + result.setMsg("处理出错"); + } else { + String rspJsonStr = JSON.toJSONString(rspResult.getData().getOutputs().getResult()); + System.out.println("response: " + rspJsonStr); + result.setData(rspJsonStr); + } + } + return result; + + } + + public String getCurrentFileName() { + return currentFileName.get("current"); + } + + public void setOutFileName(String name) { + currentFileName.put("outFile", name); + } + public String getOutFileName() { + return currentFileName.get("outFile"); + } + public void cleanMap() { + currentFileName.clear(); + } + +} + diff --git a/src/main/java/com/flx/techcenter/aidoc/service/AiDocService.java b/src/main/java/com/flx/techcenter/aidoc/service/AiDocService.java new file mode 100644 index 0000000..b4b4074 --- /dev/null +++ b/src/main/java/com/flx/techcenter/aidoc/service/AiDocService.java @@ -0,0 +1,15 @@ +package com.flx.techcenter.aidoc.service; + +import com.flx.techcenter.aidoc.entity.HttpResult; +import com.flx.techcenter.aidoc.httpbo.apply.ReqApplyModify; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.multipart.MultipartFile; + +public interface AiDocService { + + HttpResult runCheckDoc(MultipartFile file, String promp); + + HttpResult applyModify(ReqApplyModify reqApplyModify, String fileName); + +} diff --git a/src/main/java/com/flx/techcenter/aidoc/service/FileStorageService.java b/src/main/java/com/flx/techcenter/aidoc/service/FileStorageService.java new file mode 100644 index 0000000..77e8aca --- /dev/null +++ b/src/main/java/com/flx/techcenter/aidoc/service/FileStorageService.java @@ -0,0 +1,15 @@ +package com.flx.techcenter.aidoc.service; + +import org.springframework.core.io.Resource; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.multipart.MultipartFile; + + +public interface FileStorageService { + + ResponseEntity handleWordFileUpload(@RequestParam("file") MultipartFile file); + Resource loadFileAsResource(String fileName); + long getFileSize(String fileName); + +} diff --git a/src/main/java/com/flx/techcenter/aidoc/service/LlmService.java b/src/main/java/com/flx/techcenter/aidoc/service/LlmService.java new file mode 100644 index 0000000..e516526 --- /dev/null +++ b/src/main/java/com/flx/techcenter/aidoc/service/LlmService.java @@ -0,0 +1,7 @@ +package com.flx.techcenter.aidoc.service; + +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + +public interface LlmService { + SseEmitter chat(String prompt); +} diff --git a/src/main/java/com/flx/techcenter/aidoc/service/WordDocumentService.java b/src/main/java/com/flx/techcenter/aidoc/service/WordDocumentService.java new file mode 100644 index 0000000..597f0e5 --- /dev/null +++ b/src/main/java/com/flx/techcenter/aidoc/service/WordDocumentService.java @@ -0,0 +1,13 @@ +package com.flx.techcenter.aidoc.service; + +import com.flx.techcenter.aidoc.entity.HttpResult; + +import java.io.File; +import java.io.IOException; +import java.util.Map; + +public interface WordDocumentService { + + HttpResult replaceTextInDocument(File inputFile, File outputFile, Map replacements) throws IOException; + +} diff --git a/src/main/java/com/flx/techcenter/aidoc/service/impl/AiDocServiceImpl.java b/src/main/java/com/flx/techcenter/aidoc/service/impl/AiDocServiceImpl.java new file mode 100644 index 0000000..5fabe99 --- /dev/null +++ b/src/main/java/com/flx/techcenter/aidoc/service/impl/AiDocServiceImpl.java @@ -0,0 +1,140 @@ +package com.flx.techcenter.aidoc.service.impl; + +import com.alibaba.fastjson2.JSON; +import com.flx.techcenter.aidoc.entity.HttpResult; +import com.flx.techcenter.aidoc.httpbo.apply.ModifiedItem; +import com.flx.techcenter.aidoc.httpbo.apply.ReqApplyModify; +import com.flx.techcenter.aidoc.httpbo.checkdoc.RspCheckResult; +import com.flx.techcenter.aidoc.rest.RestTemplateService; +import com.flx.techcenter.aidoc.service.AiDocService; +import com.flx.techcenter.aidoc.service.WordDocumentService; +import com.flx.techcenter.aidoc.utils.AiDocConst; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.List; +import java.util.UUID; + +@Service +public class AiDocServiceImpl implements AiDocService { + + @Autowired + RestTemplateService restTemplate; + + @Autowired + private WordDocumentService wordService; + + private static final Logger logger = LogManager.getLogger(AiDocServiceImpl.class); + + @Override + public HttpResult runCheckDoc(MultipartFile file, String promp){ + //保存客户端上传的文件 + String fileName = handleWordFileUpload(file); + //上传文件给大模型 + String resId = restTemplate.uploadFile("abc-123", fileName, AiDocConst.UPLOAD_TOKEN); + //执行审阅 + HttpResult result = handleDocCheck(resId, promp); + System.out.println(result); + + return result; + } + + + + public String handleWordFileUpload(MultipartFile file) { + System.out.println("handleWordFileUpload begin: *********"); + String fileName; + // 检查文件是否为空 + if (file.isEmpty()) { + return "请选择一个文件上传"; + } + + // 检查文件类型(可选但推荐) + String originalFilename = file.getOriginalFilename(); + if (originalFilename != null && + !(originalFilename.endsWith(".doc") || originalFilename.endsWith(".docx"))) { + return "仅支持 Word 文档 (.doc, .docx) 格式"; + } + + try { + // 生成唯一的文件名,防止覆盖 + fileName = UUID.randomUUID().toString() + "_" + originalFilename; + // 构建文件存储路径 + Path path = Paths.get(AiDocConst.UPLOAD_DIR + fileName); + + // 确保上传目录存在 + Files.createDirectories(path.getParent()); + // 将文件保存到指定路径 + Files.write(path, file.getBytes()); + + // 这里可以添加进一步处理 Word 文档的逻辑,例如使用 Apache POI 读取内容 + // String content = readWordContent(path.toString()); + // System.out.println("读取到的Word内容: " + content); + + // 返回成功响应,包含文件存储信息(实际应用中可能返回访问URL) + return AiDocConst.UPLOAD_DIR + fileName; + } catch (IOException e) { + e.printStackTrace(); + return "处理上传出错"; + } + } + + public HttpResult handleDocCheck(String resId, String promp) { + HttpResult result = HttpResult.success(); + String msg = "处理完成"; + result.setMsg(msg); + String res = restTemplate.runCheckingDoc(resId, promp); + System.out.println(res); + + RspCheckResult rspResult = JSON.parseObject(res, RspCheckResult.class); + String rspJsonStr = JSON.toJSONString(rspResult.getData().getOutputs().getResult()); + System.out.println("response: " + rspJsonStr); + result.setData(rspJsonStr); + return result; + } + + @Override + public HttpResult applyModify(ReqApplyModify reqApplyModify, String fileName) { + logger.info(JSON.toJSONString(reqApplyModify)); + HashMap map = new HashMap(); + List content = reqApplyModify.getContent(); + for (ModifiedItem item : content) { + map.put(item.getOldContent(), item.getNewContent()); + } + + File inputFile = new File(fileName); + String outputFileName = fileName; + if(fileName.contains("_")){ + outputFileName = AiDocConst.OUT_DIR + "out" + fileName.substring(fileName.lastIndexOf("_") ); + } + File dir = new File(AiDocConst.OUT_DIR); + if(!dir.exists()){ + dir.mkdirs(); + } + restTemplate.setOutFileName(outputFileName); + File outputFile = new File(outputFileName); + //对于多次修改的场景 + if(outputFile.exists()){ + inputFile = outputFile; + } + try { + wordService.replaceTextInDocument(inputFile, outputFile, map); + }catch (IOException e){ + logger.error("修改文档出错: {}", e.getMessage()); + return HttpResult.error(); + } + + return HttpResult.success(); + } + +} diff --git a/src/main/java/com/flx/techcenter/aidoc/service/impl/CozeLlmServiceImpl.java b/src/main/java/com/flx/techcenter/aidoc/service/impl/CozeLlmServiceImpl.java new file mode 100644 index 0000000..74492ba --- /dev/null +++ b/src/main/java/com/flx/techcenter/aidoc/service/impl/CozeLlmServiceImpl.java @@ -0,0 +1,256 @@ +package com.flx.techcenter.aidoc.service.impl; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.flx.techcenter.aidoc.rest.RestTemplateService; +import com.flx.techcenter.aidoc.service.LlmService; +import javax.annotation.PostConstruct; + +import com.flx.techcenter.aidoc.utils.AiDocConst; +import com.flx.techcenter.aidoc.utils.LoggerUtil; +import org.apache.commons.lang3.StringUtils; +import org.springframework.boot.logging.LogLevel; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Service; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + +import java.io.IOException; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.time.Duration; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.Flow; +import java.util.concurrent.TimeUnit; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +@Service +public class CozeLlmServiceImpl implements LlmService { + //https://api.coze.cn/v3/chat"; + //private static final String API_URL = "http://lingshu-lzf-test.philisensedev.com/v1/workflows/run"; + private static final String API_URL = "http://lingshu-lzf-test.philisensedev.com/v1/chat-messages"; + + //private static final String API_KEY = "app-1DZ9zBTDSuc3zUlzjQDFWKLZ"; + private static final String API_KEY = "app-ThHAgw7HlMqsIEoCYzUyZWER";//"app-21oTMFKSeyCh0LXe9iGyhlx7" + //"cztei_qQbrtrcK5vDvICeXoCCsu3jVlRg8jdo9HNhRz5Za0v0wBkDij0WbIHPRvP1Q5p4nG"; // 实际使用时从配置中心获取 + private HttpClient httpClient; + + @PostConstruct + public void init() { + this.httpClient = HttpClient.newBuilder() + .version(HttpClient.Version.HTTP_2) + .connectTimeout(Duration.ofSeconds(10)) + .build(); + } + + @Override + public SseEmitter chat(String prompt) { + // 设置超时时间(30分钟,适应长对话) + SseEmitter emitter = new SseEmitter(TimeUnit.MINUTES.toMillis(30)); + + // 处理客户端断开连接的情况 + emitter.onCompletion(() -> System.out.println("SSE连接完成")); + emitter.onTimeout(() -> emitter.completeWithError(new RuntimeException("SSE连接超时"))); + emitter.onError(e -> System.err.println("SSE错误: " + e.getMessage())); + MediaType utf8Plain = new MediaType("text", "plain", java.nio.charset.StandardCharsets.UTF_8); + + // 异步调用大模型API + new Thread(() -> { + try { + String requestBody = buildRequestBody(prompt); + + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(API_URL)) + .header("Content-Type", "application/json") + .header("Authorization", "Bearer " + API_KEY) + .header("Accept", "text/event-stream") + .header("Accept-Language", "zh-CN,zh;q=0.9") + .header("Cache-Control", "no-cache") + //.header("Connection", "keep-alive") + .header("Pragma", "no-cache") + .header("Sec-Fetch-Dest", "empty") + .header("Sec-Fetch-Mode", "cors") + .header("Sec-Fetch-Site","same-origin") + .header("sec-ch-ua", "\"Not)A;Brand\";v=\"8\", \"Chromium\";v=\"138\", \"Google Chrome\";v=\"138\"") + + .timeout(Duration.ofMinutes(15)) + .POST(HttpRequest.BodyPublishers.ofString(requestBody)) + .build(); + + // 处理流式响应 + httpClient.send(request, HttpResponse.BodyHandlers.fromLineSubscriber( + new Flow.Subscriber() { + @Override + public void onSubscribe(Flow.Subscription subscription) { + subscription.request(Long.MAX_VALUE); // 请求所有数据 + } + + @Override + public void onNext(String line) { + try { + String content = parseSseData(line); + if (content != null && !content.isEmpty()) { + System.out.println("content: --- " + content); + LocalDateTime now = LocalDateTime.now(); + DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + System.out.println(now.format(dtf)); + JSONObject object = JSON.parseObject(content); + if(object.containsKey(AiDocConst.AI_RESPONSE_ANSWER)){ + + String answers = object.getString(AiDocConst.AI_RESPONSE_ANSWER); + + Map map = getAnswer(answers); + String answer = ""; + String thought = ""; + if(map.containsKey(AiDocConst.AI_RESPONSE_ANSWER)){ + answer = map.get(AiDocConst.AI_RESPONSE_ANSWER); + } + if(map.containsKey("think")){ + thought = map.get("think"); + } + if(StringUtils.isNotBlank(answer)){ + // 通过SSE发送数据(事件类型为"message") + emitter.send(SseEmitter.event() + .name("message") + .data(answer, utf8Plain)); + } + if(StringUtils.isNotBlank(thought)){ + emitter.send(SseEmitter.event() + .name("thought") + .data(thought, utf8Plain)); + } + } + + + } + } catch (IOException e) { + emitter.completeWithError(e); + } + } + + @Override + public void onError(Throwable throwable) { + System.out.println("error----------"); + emitter.completeWithError(throwable); + } + + @Override + public void onComplete() { + // 发送结束事件 + try { + emitter.send(SseEmitter.event().name("complete").data("done")); + } catch (IOException e) { + // 忽略结束时的发送错误 + } + emitter.complete(); + } + } + )); + + } catch (Exception e) { + emitter.completeWithError(e); + } + }).start(); + + return emitter; + } + + // 构建请求体(启用流式返回) +// private String buildRequestBody(String prompt) throws Exception { +// Map requestMap = Map.of( +// "bot_id", "7533046249135145003", +// "user_id", "123456789", +// "additional_messages", new Object[]{ +// Map.of("role", "user", "content", prompt, "type", "question", "content_type","text") +// }, +// "stream", true // 关键参数:启用流式返回 +// ); +// return JSON.toJSONString(requestMap); +// } + + private String buildRequestBody(String prompt) throws Exception { + Map requestMap = Map.of( + "query", "1+1=?", + "user", "abc-123", + "response_mode", "streaming", + "inputs", "{}", + "conversation_id", "" + ); + //String requestBody = "{\"inputs\":{},\"query\":\"某单位未公布采购流程,是否违规?\",\"response_mode\":\"streaming\",\"conversation_id\":\"\",\"user\":\"abc-123\"}"; + + HashMap inputMap = new HashMap(); + inputMap.put("query", prompt); + Map inputsMap = new HashMap<>(); + inputMap.put("inputs", inputsMap); + inputMap.put("user", "abc-123"); + inputMap.put("conversation_id", ""); + inputMap.put("response_mode", "streaming"); + String requestBody = JSON.toJSONString(inputMap); + System.out.println("convert result: " + requestBody); + + return requestBody;//JSON.toJSONString(requestMap); + } + + // 解析大模型返回的SSE数据 + private String parseSseData(String line) { + LoggerUtil.log(LogLevel.DEBUG, "in parseSSEData " + line); + if (line == null || line.isEmpty() || line.startsWith("event:")) { + return null; + } + + // 移除SSE的data前缀 + String data = line.startsWith("data:") ? line.substring(6) : line; + + // 检查是否为结束标记 + if (data.equals("[DONE]")) { + return null; + } + + try { + if(JSON.isValid(data)){ + // 解析JSON获取内容(根据实际API返回格式调整) +// JSONObject d = JSON.parseObject(data); +// LoggerUtil.log(LogLevel.DEBUG, "return: " + d.getString("answer")); +// String answer = d.getString("answer"); + //String content = RestTemplateService.decodeUnicode(answer); + //System.out.println("return data: " + data); + return data; + } else { + return null; + } + } catch (Exception e) { + // 忽略中间格式错误(可能是不完整的JSON) + LoggerUtil.log(LogLevel.ERROR, data, e); + } + return null; + } + + /** + * 提取标签中的内容(思考过程),标签外是answer + */ + private Map getAnswer(String answers){ + + Pattern patern = Pattern.compile("([\\s\\S]*?)"); + Matcher matcher = patern.matcher(answers); + + String think = ""; + String answer = answers; + Map map = new HashMap<>(); + if(matcher.find()){ + think = matcher.group(1).trim(); + answer = answers.replace(matcher.group(0), "").trim(); + map.put("think", think); + map.put("answer", answer); + } else { + map.put("answer", answer); + } + return map; + + } + +} diff --git a/src/main/java/com/flx/techcenter/aidoc/service/impl/FileStorageServiceImpl.java b/src/main/java/com/flx/techcenter/aidoc/service/impl/FileStorageServiceImpl.java new file mode 100644 index 0000000..0b343fe --- /dev/null +++ b/src/main/java/com/flx/techcenter/aidoc/service/impl/FileStorageServiceImpl.java @@ -0,0 +1,176 @@ +package com.flx.techcenter.aidoc.service.impl; + +import com.flx.techcenter.aidoc.service.FileStorageService; +import com.flx.techcenter.aidoc.utils.AiDocConst; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.io.Resource; +import org.springframework.core.io.UrlResource; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.List; +import java.util.UUID; + +@Service +public class FileStorageServiceImpl implements FileStorageService { + + private final Path fileStorageLocation; + private final List allowedExtensions; + + @Autowired + + public FileStorageServiceImpl( + @Value("${file.storage.path}") String fileStoragePath, + @Value("${file.allowed.extensions}") String allowedExtensions) { + + this.fileStorageLocation = Paths.get(fileStoragePath).toAbsolutePath().normalize(); + this.allowedExtensions = Arrays.asList(allowedExtensions.split(",")); + + try { + Files.createDirectories(this.fileStorageLocation); + } catch (IOException ex) { + throw new RuntimeException("无法创建文件存储目录", ex); + } + } + + /** + * 加载文件作为Resource + */ + public Resource loadFileAsResource(String fileName) { + try { + // 安全检查:防止目录遍历攻击 + if (fileName.contains("..")) { + throw new RuntimeException("文件名包含非法字符: " + fileName); + } + + // 检查文件扩展名 + if (!isAllowedExtension(fileName)) { + throw new RuntimeException("不支持的文件类型: " + fileName); + } + + Path filePath = this.fileStorageLocation.resolve(fileName).normalize(); + Resource resource = new UrlResource(filePath.toUri()); + + if (resource.exists()) { + return resource; + } else { + throw new RuntimeException("文件未找到: " + fileName); + } + } catch (MalformedURLException ex) { + throw new RuntimeException("文件路径格式错误: " + fileName, ex); + } + } + + /** + * 检查文件扩展名是否允许 + */ + private boolean isAllowedExtension(String fileName) { + String extension = getFileExtension(fileName).toLowerCase(); + return allowedExtensions.contains(extension); + } + + /** + * 获取文件扩展名 + */ + private String getFileExtension(String fileName) { + int lastDotIndex = fileName.lastIndexOf("."); + if (lastDotIndex > 0) { + return fileName.substring(lastDotIndex); + } + return ""; + } + + /** + * 检查文件是否存在 + */ + public boolean fileExists(String fileName) { + if (fileName.contains("..")) { + return false; + } + Path filePath = this.fileStorageLocation.resolve(fileName).normalize(); + return Files.exists(filePath) && Files.isRegularFile(filePath); + } + + /** + * 获取文件大小 + */ + public long getFileSize(String fileName) { + try { + Path filePath = this.fileStorageLocation.resolve(fileName).normalize(); + return Files.size(filePath); + } catch (IOException e) { + throw new RuntimeException("无法获取文件大小: " + fileName, e); + } + } + + /** + * 获取文件的MIME类型 + */ + public String getFileContentType(String fileName) { + try { + Path filePath = this.fileStorageLocation.resolve(fileName).normalize(); + String contentType = Files.probeContentType(filePath); + + // 如果无法自动探测,根据扩展名判断 + if (contentType == null) { + String extension = getFileExtension(fileName).toLowerCase(); + if (extension.equals(".docx")) { + contentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document"; + } else if (extension.equals(".doc")) { + contentType = "application/msword"; + } + } + + return contentType; + } catch (IOException e) { + return "application/octet-stream"; + } + } + + public ResponseEntity handleWordFileUpload(@RequestParam("file") MultipartFile file) { + System.out.println("handleWordFileUpload begin"); + // 检查文件是否为空 + if (file.isEmpty()) { + return ResponseEntity.badRequest().body("请选择一个文件上传"); + } + + // 检查文件类型(可选但推荐) + String originalFilename = file.getOriginalFilename(); + if (originalFilename != null && + !(originalFilename.endsWith(".doc") || originalFilename.endsWith(".docx"))) { + return ResponseEntity.badRequest().body("仅支持 Word 文档 (.doc, .docx) 格式"); + } + + try { + // 生成唯一的文件名,防止覆盖 + String fileName = UUID.randomUUID().toString() + "_" + originalFilename; + // 构建文件存储路径 + Path path = Paths.get(AiDocConst.UPLOAD_DIR + fileName); + + // 确保上传目录存在 + Files.createDirectories(path.getParent()); + // 将文件保存到指定路径 + Files.write(path, file.getBytes()); + + // 这里可以添加进一步处理 Word 文档的逻辑,例如使用 Apache POI 读取内容 + // String content = readWordContent(path.toString()); + // System.out.println("读取到的Word内容: " + content); + + // 返回成功响应,包含文件存储信息(实际应用中可能返回访问URL) + return ResponseEntity.ok().body(fileName); + } catch (IOException e) { + e.printStackTrace(); + return ResponseEntity.internalServerError().body("文件上传失败: " + e.getMessage()); + } + } + +} diff --git a/src/main/java/com/flx/techcenter/aidoc/service/impl/WordDocumentServiceImpl.java b/src/main/java/com/flx/techcenter/aidoc/service/impl/WordDocumentServiceImpl.java new file mode 100644 index 0000000..2479ee9 --- /dev/null +++ b/src/main/java/com/flx/techcenter/aidoc/service/impl/WordDocumentServiceImpl.java @@ -0,0 +1,129 @@ +package com.flx.techcenter.aidoc.service.impl; + +import com.flx.techcenter.aidoc.entity.HttpResult; +import com.flx.techcenter.aidoc.service.WordDocumentService; +import org.apache.poi.xwpf.usermodel.*; +import org.springframework.stereotype.Service; + +import java.io.*; +import java.util.List; +import java.util.Map; + +@Service +public class WordDocumentServiceImpl implements WordDocumentService { + + /** + * 替换Word文档中的文本内容 + */ + @Override + public HttpResult replaceTextInDocument(File inputFile, File outputFile, Map replacements) throws IOException { + try (FileInputStream fis = new FileInputStream(inputFile); + XWPFDocument document = new XWPFDocument(fis); + FileOutputStream fos = new FileOutputStream(outputFile)) { + + // 替换段落中的文本 + modifyParagraphs(document.getParagraphs(), replacements); + + // 替换表格中的文本 + replaceInTables(document.getTables(), replacements); + + document.write(fos); + } + return HttpResult.success(); + } + + /** + * 修改word文件的内容 + * @param paragraphs + * @param replacements + */ + private void modifyParagraphs(List paragraphs, Map replacements) { + for (XWPFParagraph paragraph : paragraphs) { + replaceInParagraph(paragraph, replacements); + } + } + + /** + * 跨run替换内容(保留格式) + */ + /** + * 在段落中支持跨 run 的文本替换,保留格式 + */ + private void replaceInParagraph(XWPFParagraph paragraph, Map replacements) { + StringBuilder paragraphText = new StringBuilder(); + List runs = paragraph.getRuns(); + + // 拼接整个段落的文字 + for (XWPFRun run : runs) { + paragraphText.append(run.toString()); + } + + String fullText = paragraphText.toString(); + + for (Map.Entry entry : replacements.entrySet()){ + String replacement; + if(fullText.contains(entry.getKey())){ + fullText = fullText.replace(entry.getKey(), entry.getValue()); + replacements.remove(entry.getKey()); + // 清除原有 runs + for (int i = runs.size() - 1; i >= 0; i--) { + paragraph.removeRun(i); + } + + // 新建一个 run(继承第一个 run 的样式,保证格式) + XWPFRun newRun = paragraph.createRun(); + if (!runs.isEmpty()) { + XWPFRun oldRun = runs.get(0); + copyStyle(oldRun, newRun); + } + newRun.setText(fullText, 0); + } + } + + } + + /** + * 复制 run 样式(字体、大小、颜色等) + */ + private void copyStyle(XWPFRun source, XWPFRun target) { + target.setBold(source.isBold()); + target.setItalic(source.isItalic()); + target.setUnderline(source.getUnderline()); + target.setColor(source.getColor()); + target.setFontFamily(source.getFontFamily()); + if (source.getFontSize() != -1) { + target.setFontSize(source.getFontSize()); + } + } + + + /** + * 仅替换文字(保留原格式) + * @param run + * @param text + * @param replacements + */ + private void replaceText(XWPFRun run, String text, Map replacements) { + for (Map.Entry entry : replacements.entrySet()){ + String replacement; + if(text.contains(entry.getKey())){ + replacement = text.replace(entry.getKey(), entry.getValue()); + run.setText(replacement, 0); + } + } + } + + /** + * 替换表格中的文本 + */ + private void replaceInTables(List tables, Map replacements) { + for (XWPFTable table : tables) { + for (XWPFTableRow row : table.getRows()) { + for (XWPFTableCell cell : row.getTableCells()) { + modifyParagraphs(cell.getParagraphs(), replacements); + } + } + } + } + +} diff --git a/src/main/java/com/flx/techcenter/aidoc/utils/AiDocConst.java b/src/main/java/com/flx/techcenter/aidoc/utils/AiDocConst.java new file mode 100644 index 0000000..51f28de --- /dev/null +++ b/src/main/java/com/flx/techcenter/aidoc/utils/AiDocConst.java @@ -0,0 +1,16 @@ +package com.flx.techcenter.aidoc.utils; + +public class AiDocConst { + + // 指定文件存储目录,例如在系统中创建一个 'upload' 文件夹 + public static final String UPLOAD_DIR = "d:/tem2/upload/"; + //修改后文件的临时存放目录 + public static final String OUT_DIR = "d:/tem2/upload/out/"; + //上传文件给大模型的token + public static final String UPLOAD_TOKEN = "app-1DZ9zBTDSuc3zUlzjQDFWKLZ"; + + public static final String AI_RESPONSE_ANSWER = "answer"; + + public static final String AI_RESPONSE_THOUGHT = "thought"; + +} diff --git a/src/main/java/com/flx/techcenter/aidoc/utils/LoggerUtil.java b/src/main/java/com/flx/techcenter/aidoc/utils/LoggerUtil.java new file mode 100644 index 0000000..2cf09f9 --- /dev/null +++ b/src/main/java/com/flx/techcenter/aidoc/utils/LoggerUtil.java @@ -0,0 +1,79 @@ +package com.flx.techcenter.aidoc.utils; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.logging.LogLevel; + +public class LoggerUtil { + /** + * 静态日志方法,自动获取调用者类名 + * @param level 日志级别 + * @param message 日志内容 + */ + public static void log(LogLevel level, String message) { + // 获取调用栈信息,找到实际调用者的类 + StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); + + // 栈索引说明: + // 0: Thread.getStackTrace() + // 1: LoggerUtil.log() + // 2: 实际调用者 + StackTraceElement caller = stackTrace[2]; + String className = caller.getClassName(); + + // 获取对应类的Logger实例 + Logger logger = LoggerFactory.getLogger(className); + + // 根据日志级别输出日志 + switch (level) { + case DEBUG: + if (logger.isDebugEnabled()) { + logger.debug(message); + } + break; + case INFO: + if (logger.isInfoEnabled()) { + logger.info(message); + } + break; + case WARN: + logger.warn(message); + break; + case ERROR: + logger.error(message); + break; + default: + throw new IllegalArgumentException("不支持的日志级别: " + level); + } + } + + /** + * 重载方法,支持异常信息 + * @param level 日志级别 + * @param message 日志内容 + * @param throwable 异常对象 + */ + public static void log(LogLevel level, String message, Throwable throwable) { + StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); + StackTraceElement caller = stackTrace[2]; + String className = caller.getClassName(); + Logger logger = LoggerFactory.getLogger(className); + + switch (level) { + case DEBUG: + logger.debug(message, throwable); + break; + case INFO: + logger.info(message, throwable); + break; + case WARN: + logger.warn(message, throwable); + break; + case ERROR: + logger.error(message, throwable); + break; + default: + throw new IllegalArgumentException("不支持的日志级别: " + level); + } + } +} diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml new file mode 100644 index 0000000..947e41c --- /dev/null +++ b/src/main/resources/application-dev.yml @@ -0,0 +1,239 @@ +server: + servlet: + context-path: /platform_system + port: 9091 + domain: http://localhost + sesseion: + timeout: 600000 + +http: + port: 80 + +mybatis: + typeAliasesPackage: com.philisense.www.pojos.entity + mapper-locations: classpath*:/mapper/mysql/*.xml + configLocation: classpath:mybatis/mybatis-config.xml + +# 数据源配置 +spring: + datasource: + type: com.alibaba.druid.pool.DruidDataSource + driverClassName: com.mysql.cj.jdbc.Driver + druid: + # 主库数据源 jdbc:mysql://127.0.0.1:3306/gfcn?useSSL=false&characterEncoding=UTF-8&serverTimezone=UTC&nullCatalogMeansCurrent=true&allowMultiQueries=true&allowPublicKeyRetrieval=true + master: + url: jdbc:mysql://192.168.13.152:3306/gfcntest?useSSL=false&characterEncoding=UTF-8&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true&allowMultiQueries=true&allowPublicKeyRetrieval=true + username: root + password: 12345678 + # 从库数据源 + slave1: + # 从数据源开关/默认关闭 + enabled: false + url: + username: + password: + # 从库数据源 + slave2: + # 从数据源开关/默认关闭 + enabled: false + url: + username: + password: + # 从库数据源 + slave3: + # 从数据源开关/默认关闭 + enabled: false + url: + username: + password: + # 从库数据源 + slave4: + # 从数据源开关/默认关闭 + enabled: false + url: + username: + password: + # 初始连接数 + initialSize: 5 + # 最小连接池数量 + minIdle: 10 + # 最大连接池数量 + maxActive: 20 + # 配置获取连接等待超时的时间 + maxWait: 60000 + # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 + timeBetweenEvictionRunsMillis: 60000 + # 配置一个连接在池中最小生存的时间,单位是毫秒 + minEvictableIdleTimeMillis: 300000 + # 配置一个连接在池中最大生存的时间,单位是毫秒 + maxEvictableIdleTimeMillis: 900000 + # 配置检测连接是否有效 + validationQuery: SELECT 1 FROM DUAL + testWhileIdle: true + testOnBorrow: false + testOnReturn: false + webStatFilter: + enabled: true + statViewServlet: + enabled: true + # 设置白名单,不填则允许所有访问 + allow: + url-pattern: /druid/* + # 控制台管理用户名和密码 + login-username: + login-password: + filter: + stat: + enabled: true + # 慢SQL记录 + log-slow-sql: true + slow-sql-millis: 1000 + merge-sql: true + wall: + config: + multi-statement-allow: true + output: + ansi: + enabled: ALWAYS + + redis: + database: 1 + host: 127.0.0.1 + port: 6379 + password: + session: + store-type: redis + + servlet: + multipart: + max-file-size: 104857600 + max-request-size: 104857600 + + freemarker: + checkTemplateLocation: false + charset: UTF-8 + template-loader-path: classpath:/templates + suffix: .ftl + request-context-attribute: request + + mvc: + throw-exception-if-no-handler-found: true + pathmatch: + matching-strategy: ant_path_matcher # SpringBoot2.6.0和swagger3.0.0兼容性 + + jackson: + # 全局设置时区 + time-zone: GMT+8 + # 全局设置@JsonFormat的格式pattern + date-format: yyyy-MM-dd HH:mm:ss + # 属性为 NULL 都不序列化,则返回的json是没有这个字段的 + default-property-inclusion: NON_NULL + + web: + resources: + add-mappings: true + +# 是否开启swagger +swagger: + enabled: true + +httpclient: + # 最大连接数 + maxtotal: 64 + # 每个route默认的最大连接数 + defaultMaxPerRoute: 64 + # 连接上服务器(握手成功)的时间,超出抛出connect timeout + connectTimeout: 10000 + # 从连接池中获取连接的超时时间,超时间未拿到可用连接,会抛出org.apache.http.conn.ConnectionPoolTimeoutException: Timeout waiting for connection from pool + connectionRequestTimeout: 10000 + # 服务器返回数据(response)的时间,超过抛出read timeout + socketTimeout: 10000 + # 存活时间 + validateAfterInactivity: 10000 + +#jwt +jwt: + header: Authorization + tokenHead: PhilisenseOfficeCenter- + secret: mySecret + # token 过期时间 2个小时 + expiration: 7200000 + # appToken 过期时间 2个小时 + appExpiration: 7200000 + auth: + # 授权路径 + path: /login + # 退出授权路径 + logout: /logout + # 获取用户信息 + account: /currentUser + +platform: + # 本地存储:0 阿里云:1 七牛云:2 又拍云:3 Minio:4 GoFastDFS:5 + oss: + ossType: 0 #存储类型 + diskName: system + + # 本地文件上传 + local: + endpoint: http://localhost:9091/platform_system + path: D:/Software/nginx-1.16.1/html/upload + bucketName: dl-oa + + # Aliyun配置 + aliyun: + endpoint: https://dl-app.oss-cn-beijing.aliyuncs.com + accessKey: LTAI5tD9EAarmoirH42pTYF7 + accessSecret: oO5hTSdRi6B0hW9XvNyb2c6FGecmVy + regionId: oss-cn-beijing + bucketName: dl-app + + # Qiniu配置 + qiniu: + endpoint: http://dlweapp.jdouzi.net + accessKey: lxMZuaGNs6LhP61X_ncbu9RVF6Vuhcj-D7C7wRyF + accessSecret: 5j_OuAAx9-hlgw3GFbPK0am25UYKaU0maX111Atc + regionId: huabei + bucketName: dl-oa + + # Upyun配置 + upyun: + endpoint: http://flx-oss.test.upcdn.net + userName: flxoss + password: SSqypgNBdU472QSVchHRy9LsVi2o32qB + bucketName: flx-oss + + # Minio配置 + minio: + endpoint: http://115.233.144.21:9000 + accessKey: minioadmin + secretKey: minioadmin@2022 + bucketName: internally-piloting + + # GoFastDFS配置 + goFastDFS: + endpoint: http://115.233.144.21:28080 + connectTimeout: 200000 + socketTimeout: 2000000 + bucketName: group1 + + # 验证码 + vcode: + code-length: 6 + code-expiration: 300 + +logging: + level: + root: INFO + org.springframework.security: INFO + com.philisense.www.mapper: DEBUG + org: + springframework: + scheduling:DEBUG: +task: + logdirwin: d:/logs + logdirlinux: /home/flx/program/zngf/logs + + + + diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 0000000..e350e67 --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,17 @@ +spring: + application: + name: aidoc + profiles: #指定执行环境 + active: dev + datasource: + driverClassName: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://192.168.13.152:3306/gfcntest?useSSL=false&characterEncoding=UTF-8&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true&allowMultiQueries=true&allowPublicKeyRetrieval=true + username: root + password: 12345678 + +file: + allowed: + extensions: .docx + max-size: 10485760 + storage: + path: D:/tem2/ \ No newline at end of file diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml new file mode 100644 index 0000000..9f2bb2f --- /dev/null +++ b/src/main/resources/log4j2.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/mybatis/mybatis-config.xml b/src/main/resources/mybatis/mybatis-config.xml new file mode 100644 index 0000000..7cd5e3f --- /dev/null +++ b/src/main/resources/mybatis/mybatis-config.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + diff --git a/src/test/java/com/flx/techcenter/aidoc/AidocApplicationTests.java b/src/test/java/com/flx/techcenter/aidoc/AidocApplicationTests.java new file mode 100644 index 0000000..f3e15b8 --- /dev/null +++ b/src/test/java/com/flx/techcenter/aidoc/AidocApplicationTests.java @@ -0,0 +1,13 @@ +package com.flx.techcenter.aidoc; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class AidocApplicationTests { + + @Test + void contextLoads() { + } + +}