init the aiAudit project, commit local code

dev
houcj 3 weeks ago
commit d4042d490c

2
.gitattributes vendored

@ -0,0 +1,2 @@
/mvnw text eol=lf
*.cmd text eol=crlf

33
.gitignore vendored

@ -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/

@ -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

259
mvnw vendored

@ -0,0 +1,259 @@
#!/bin/sh
# ----------------------------------------------------------------------------
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
# ----------------------------------------------------------------------------
# ----------------------------------------------------------------------------
# Apache Maven Wrapper startup batch script, version 3.3.2
#
# Optional ENV vars
# -----------------
# JAVA_HOME - location of a JDK home dir, required when download maven via java source
# MVNW_REPOURL - repo url base for downloading maven distribution
# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output
# ----------------------------------------------------------------------------
set -euf
[ "${MVNW_VERBOSE-}" != debug ] || set -x
# OS specific support.
native_path() { printf %s\\n "$1"; }
case "$(uname)" in
CYGWIN* | MINGW*)
[ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")"
native_path() { cygpath --path --windows "$1"; }
;;
esac
# set JAVACMD and JAVACCMD
set_java_home() {
# For Cygwin and MinGW, ensure paths are in Unix format before anything is touched
if [ -n "${JAVA_HOME-}" ]; then
if [ -x "$JAVA_HOME/jre/sh/java" ]; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
JAVACCMD="$JAVA_HOME/jre/sh/javac"
else
JAVACMD="$JAVA_HOME/bin/java"
JAVACCMD="$JAVA_HOME/bin/javac"
if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then
echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2
echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2
return 1
fi
fi
else
JAVACMD="$(
'set' +e
'unset' -f command 2>/dev/null
'command' -v java
)" || :
JAVACCMD="$(
'set' +e
'unset' -f command 2>/dev/null
'command' -v javac
)" || :
if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then
echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2
return 1
fi
fi
}
# hash string like Java String::hashCode
hash_string() {
str="${1:-}" h=0
while [ -n "$str" ]; do
char="${str%"${str#?}"}"
h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296))
str="${str#?}"
done
printf %x\\n $h
}
verbose() { :; }
[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; }
die() {
printf %s\\n "$1" >&2
exit 1
}
trim() {
# MWRAPPER-139:
# Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds.
# Needed for removing poorly interpreted newline sequences when running in more
# exotic environments such as mingw bash on Windows.
printf "%s" "${1}" | tr -d '[:space:]'
}
# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties
while IFS="=" read -r key value; do
case "${key-}" in
distributionUrl) distributionUrl=$(trim "${value-}") ;;
distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;;
esac
done <"${0%/*}/.mvn/wrapper/maven-wrapper.properties"
[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in ${0%/*}/.mvn/wrapper/maven-wrapper.properties"
case "${distributionUrl##*/}" in
maven-mvnd-*bin.*)
MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/
case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in
*AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;;
:Darwin*x86_64) distributionPlatform=darwin-amd64 ;;
:Darwin*arm64) distributionPlatform=darwin-aarch64 ;;
:Linux*x86_64*) distributionPlatform=linux-amd64 ;;
*)
echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2
distributionPlatform=linux-amd64
;;
esac
distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip"
;;
maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;;
*) MVN_CMD="mvn${0##*/mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;;
esac
# apply MVNW_REPOURL and calculate MAVEN_HOME
# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash>
[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}"
distributionUrlName="${distributionUrl##*/}"
distributionUrlNameMain="${distributionUrlName%.*}"
distributionUrlNameMain="${distributionUrlNameMain%-bin}"
MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}"
MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")"
exec_maven() {
unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || :
exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD"
}
if [ -d "$MAVEN_HOME" ]; then
verbose "found existing MAVEN_HOME at $MAVEN_HOME"
exec_maven "$@"
fi
case "${distributionUrl-}" in
*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;;
*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;;
esac
# prepare tmp dir
if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then
clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; }
trap clean HUP INT TERM EXIT
else
die "cannot create temp dir"
fi
mkdir -p -- "${MAVEN_HOME%/*}"
# Download and Install Apache Maven
verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
verbose "Downloading from: $distributionUrl"
verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
# select .zip or .tar.gz
if ! command -v unzip >/dev/null; then
distributionUrl="${distributionUrl%.zip}.tar.gz"
distributionUrlName="${distributionUrl##*/}"
fi
# verbose opt
__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR=''
[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v
# normalize http auth
case "${MVNW_PASSWORD:+has-password}" in
'') MVNW_USERNAME='' MVNW_PASSWORD='' ;;
has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;;
esac
if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then
verbose "Found wget ... using wget"
wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl"
elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then
verbose "Found curl ... using curl"
curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl"
elif set_java_home; then
verbose "Falling back to use Java to download"
javaSource="$TMP_DOWNLOAD_DIR/Downloader.java"
targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName"
cat >"$javaSource" <<-END
public class Downloader extends java.net.Authenticator
{
protected java.net.PasswordAuthentication getPasswordAuthentication()
{
return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() );
}
public static void main( String[] args ) throws Exception
{
setDefault( new Downloader() );
java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() );
}
}
END
# For Cygwin/MinGW, switch paths to Windows format before running javac and java
verbose " - Compiling Downloader.java ..."
"$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java"
verbose " - Running Downloader.java ..."
"$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")"
fi
# If specified, validate the SHA-256 sum of the Maven distribution zip file
if [ -n "${distributionSha256Sum-}" ]; then
distributionSha256Result=false
if [ "$MVN_CMD" = mvnd.sh ]; then
echo "Checksum validation is not supported for maven-mvnd." >&2
echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
exit 1
elif command -v sha256sum >/dev/null; then
if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c >/dev/null 2>&1; then
distributionSha256Result=true
fi
elif command -v shasum >/dev/null; then
if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then
distributionSha256Result=true
fi
else
echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2
echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
exit 1
fi
if [ $distributionSha256Result = false ]; then
echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2
echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2
exit 1
fi
fi
# unzip and move
if command -v unzip >/dev/null; then
unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip"
else
tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar"
fi
printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/mvnw.url"
mv -- "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME"
clean || :
exec_maven "$@"

149
mvnw.cmd vendored

@ -0,0 +1,149 @@
<# : batch portion
@REM ----------------------------------------------------------------------------
@REM Licensed to the Apache Software Foundation (ASF) under one
@REM or more contributor license agreements. See the NOTICE file
@REM distributed with this work for additional information
@REM regarding copyright ownership. The ASF licenses this file
@REM to you under the Apache License, Version 2.0 (the
@REM "License"); you may not use this file except in compliance
@REM with the License. You may obtain a copy of the License at
@REM
@REM http://www.apache.org/licenses/LICENSE-2.0
@REM
@REM Unless required by applicable law or agreed to in writing,
@REM software distributed under the License is distributed on an
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@REM KIND, either express or implied. See the License for the
@REM specific language governing permissions and limitations
@REM under the License.
@REM ----------------------------------------------------------------------------
@REM ----------------------------------------------------------------------------
@REM Apache Maven Wrapper startup batch script, version 3.3.2
@REM
@REM Optional ENV vars
@REM MVNW_REPOURL - repo url base for downloading maven distribution
@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output
@REM ----------------------------------------------------------------------------
@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0)
@SET __MVNW_CMD__=
@SET __MVNW_ERROR__=
@SET __MVNW_PSMODULEP_SAVE=%PSModulePath%
@SET PSModulePath=
@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @(
IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B)
)
@SET PSModulePath=%__MVNW_PSMODULEP_SAVE%
@SET __MVNW_PSMODULEP_SAVE=
@SET __MVNW_ARG0_NAME__=
@SET MVNW_USERNAME=
@SET MVNW_PASSWORD=
@IF NOT "%__MVNW_CMD__%"=="" (%__MVNW_CMD__% %*)
@echo Cannot start maven from wrapper >&2 && exit /b 1
@GOTO :EOF
: end batch / begin powershell #>
$ErrorActionPreference = "Stop"
if ($env:MVNW_VERBOSE -eq "true") {
$VerbosePreference = "Continue"
}
# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties
$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl
if (!$distributionUrl) {
Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties"
}
switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) {
"maven-mvnd-*" {
$USE_MVND = $true
$distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip"
$MVN_CMD = "mvnd.cmd"
break
}
default {
$USE_MVND = $false
$MVN_CMD = $script -replace '^mvnw','mvn'
break
}
}
# apply MVNW_REPOURL and calculate MAVEN_HOME
# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash>
if ($env:MVNW_REPOURL) {
$MVNW_REPO_PATTERN = if ($USE_MVND) { "/org/apache/maven/" } else { "/maven/mvnd/" }
$distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace '^.*'+$MVNW_REPO_PATTERN,'')"
}
$distributionUrlName = $distributionUrl -replace '^.*/',''
$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$',''
$MAVEN_HOME_PARENT = "$HOME/.m2/wrapper/dists/$distributionUrlNameMain"
if ($env:MAVEN_USER_HOME) {
$MAVEN_HOME_PARENT = "$env:MAVEN_USER_HOME/wrapper/dists/$distributionUrlNameMain"
}
$MAVEN_HOME_NAME = ([System.Security.Cryptography.MD5]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join ''
$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME"
if (Test-Path -Path "$MAVEN_HOME" -PathType Container) {
Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME"
Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
exit $?
}
if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) {
Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl"
}
# prepare tmp dir
$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile
$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir"
$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null
trap {
if ($TMP_DOWNLOAD_DIR.Exists) {
try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
}
}
New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null
# Download and Install Apache Maven
Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
Write-Verbose "Downloading from: $distributionUrl"
Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
$webclient = New-Object System.Net.WebClient
if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) {
$webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD)
}
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null
# If specified, validate the SHA-256 sum of the Maven distribution zip file
$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum
if ($distributionSha256Sum) {
if ($USE_MVND) {
Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties."
}
Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash
if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) {
Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property."
}
}
# unzip and move
Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null
Rename-Item -Path "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" -NewName $MAVEN_HOME_NAME | Out-Null
try {
Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null
} catch {
if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) {
Write-Error "fail to move MAVEN_HOME"
}
} finally {
try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
}
Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"

@ -0,0 +1,114 @@
<?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>com.philisense.www.parent</groupId>
<artifactId>zngf_server_business_parent</artifactId>
<version>0.0.1-RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.flx.techcenter</groupId>
<artifactId>aidoc</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>aidoc</name>
<description>aidoc</description>
<url/>
<licenses>
<license/>
</licenses>
<developers>
<developer/>
</developers>
<scm>
<connection/>
<developerConnection/>
<tag/>
<url/>
</scm>
<properties>
<java.version>18</java.version>
</properties>
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.27</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.11</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!-- 排除默认的Logback引入Log4j2 -->
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.9</version>
<type>pom</type>
</dependency>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version> <!-- 使用最新版本,请检查最新版本号 -->
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.1.2</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version> <!-- Use the latest version -->
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>11</source>
<target>11</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

@ -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);
}
}

@ -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<String, String> replace = new HashMap<>();
replace.put("今天早上。", "南京");
return wordService.replaceTextInDocument(inputFile, outputFile, replace);
}
@PostMapping("/upload/word")
public ResponseEntity<String> 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;
}
}

@ -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();
}
}

@ -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<String, Object>
{
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;
}
}

@ -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;
}
}

@ -0,0 +1,16 @@
package com.flx.techcenter.aidoc.httpbo.apply;
import java.util.List;
public class ReqApplyModify {
public List<ModifiedItem> content;
public List<ModifiedItem> getContent() {
return content;
}
public void setContent(List<ModifiedItem> itemList) {
this.content = itemList;
}
}

@ -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;
}
}

@ -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;
}
}

@ -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;
}
}

@ -0,0 +1,15 @@
package com.flx.techcenter.aidoc.httpbo.checkdoc;
import java.util.List;
public class RspOutput {
private List<ResultContent> result;
public List<ResultContent> getResult() {
return result;
}
public void setResult(List<ResultContent> result) {
this.result = result;
}
}

@ -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;
}
}

@ -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();
}
}

@ -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<String, String> 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<String, Object> body = new LinkedMultiValueMap<>();
body.add("user", user); // 添加user参数
body.add("file", fileResource); // 添加文件参数
// 创建请求实体
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);
try {
// 发送POST请求:cite[1]
ResponseEntity<String> 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<String, Object> body = buildRequestBody(resId, promp);
// 创建请求实体
HttpEntity<HashMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);
try {
// 发送POST请求:cite[1]
ResponseEntity<String> 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<String, Object> buildRequestBody(String resId, String promp) {
HashMap<String, Object> requestMap = new HashMap<String, Object>();
String fileId = resId.isEmpty() ? "3bf74eca-1e66-4b98-978a-cc47213e87ea" : resId;
HashMap<String, Object> inputMap = new HashMap<String, Object>();
inputMap.put("prompt", promp);
HashMap<String, Object> wordMap = new HashMap<String, Object>();
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;
}
/**
* 使JSONmarkdown
*/
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<String> 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();
}
}

@ -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);
}

@ -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<String> handleWordFileUpload(@RequestParam("file") MultipartFile file);
Resource loadFileAsResource(String fileName);
long getFileSize(String fileName);
}

@ -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);
}

@ -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<String, String> replacements) throws IOException;
}

@ -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<String, String> map = new HashMap<String, String>();
List<ModifiedItem> 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();
}
}

@ -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<String>() {
@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<String,String> 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<String, Object> 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<String, Object> 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<String, Object> inputMap = new HashMap<String, Object>();
inputMap.put("query", prompt);
Map<String, Object> 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;
}
/**
* <think></think>,answer
*/
private Map<String, String> getAnswer(String answers){
Pattern patern = Pattern.compile("<think>([\\s\\S]*?)</think>");
Matcher matcher = patern.matcher(answers);
String think = "";
String answer = answers;
Map<String, String> 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;
}
}

@ -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<String> 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<String> 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());
}
}
}

@ -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<String, String> 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<XWPFParagraph> paragraphs, Map<String, String> replacements) {
for (XWPFParagraph paragraph : paragraphs) {
replaceInParagraph(paragraph, replacements);
}
}
/**
* run
*/
/**
* run
*/
private void replaceInParagraph(XWPFParagraph paragraph, Map<String, String> replacements) {
StringBuilder paragraphText = new StringBuilder();
List<XWPFRun> runs = paragraph.getRuns();
// 拼接整个段落的文字
for (XWPFRun run : runs) {
paragraphText.append(run.toString());
}
String fullText = paragraphText.toString();
for (Map.Entry<String, String> 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<String, String> replacements) {
for (Map.Entry<String, String> 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<XWPFTable> tables, Map<String, String> replacements) {
for (XWPFTable table : tables) {
for (XWPFTableRow row : table.getRows()) {
for (XWPFTableCell cell : row.getTableCells()) {
modifyParagraphs(cell.getParagraphs(), replacements);
}
}
}
}
}

@ -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";
}

@ -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);
}
}
}

@ -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

@ -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/

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
<File name="File" fileName="logs/application.log">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} [%t] %-5level %logger{36} - %msg%n"/>
</File>
</Appenders>
<Loggers>
<Root level="info">
<AppenderRef ref="Console"/>
<AppenderRef ref="File"/>
</Root>
<Logger name="com.flx.techcenter.aidoc" level="debug" additivity="false">
<AppenderRef ref="Console"/>
<AppenderRef ref="File"/>
</Logger>
</Loggers>
</Configuration>

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="cacheEnabled" value="true" /> <!-- 全局映射器启用缓存 -->
<setting name="useGeneratedKeys" value="true" /> <!-- 允许 JDBC 支持自动生成主键 -->
<setting name="defaultExecutorType" value="REUSE" /> <!-- 配置默认的执行器 -->
<setting name="logImpl" value="SLF4J" /> <!-- 指定 MyBatis 所用日志的具体实现 -->
<setting name="callSettersOnNulls" value="true" /> <!--解决查询返回结果含null没有对应字段值问题-->
<!-- <setting name="mapUnderscoreToCamelCase" value="true"/> 驼峰式命名 -->
</settings>
</configuration>

@ -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() {
}
}
Loading…
Cancel
Save