This commit is contained in:
Saturneric 2020-09-01 01:08:27 +08:00
parent 9908f7f7a0
commit b66b478dd4
194 changed files with 16219 additions and 0 deletions

32
.gitignore vendored Normal file
View File

@ -0,0 +1,32 @@
HELP.md
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**
!**/src/test/**
### 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/
### VS Code ###
.vscode/
/epaper.iml

118
.mvn/wrapper/MavenWrapperDownloader.java vendored Normal file
View File

@ -0,0 +1,118 @@
/*
* Copyright 2007-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.net.*;
import java.io.*;
import java.nio.channels.*;
import java.util.Properties;
public class MavenWrapperDownloader {
private static final String WRAPPER_VERSION = "0.5.6";
/**
* Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
*/
private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/"
+ WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar";
/**
* Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
* use instead of the default one.
*/
private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
".mvn/wrapper/maven-wrapper.properties";
/**
* Path where the maven-wrapper.jar will be saved to.
*/
private static final String MAVEN_WRAPPER_JAR_PATH =
".mvn/wrapper/maven-wrapper.jar";
/**
* Name of the property which should be used to override the default download url for the wrapper.
*/
private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
public static void main(String args[]) {
System.out.println("- Downloader started");
File baseDirectory = new File(args[0]);
System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());
// If the maven-wrapper.properties exists, read it and check if it contains a custom
// wrapperUrl parameter.
File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
String url = DEFAULT_DOWNLOAD_URL;
if (mavenWrapperPropertyFile.exists()) {
FileInputStream mavenWrapperPropertyFileInputStream = null;
try {
mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
Properties mavenWrapperProperties = new Properties();
mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
} catch (IOException e) {
System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
} finally {
try {
if (mavenWrapperPropertyFileInputStream != null) {
mavenWrapperPropertyFileInputStream.close();
}
} catch (IOException e) {
// Ignore ...
}
}
}
System.out.println("- Downloading from: " + url);
File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
if (!outputFile.getParentFile().exists()) {
if (!outputFile.getParentFile().mkdirs()) {
System.out.println(
"- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'");
}
}
System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
try {
downloadFileFromURL(url, outputFile);
System.out.println("Done");
System.exit(0);
} catch (Throwable e) {
System.out.println("- Error downloading");
e.printStackTrace();
System.exit(1);
}
}
private static void downloadFileFromURL(String urlString, File destination) throws Exception {
if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) {
String username = System.getenv("MVNW_USERNAME");
char[] password = System.getenv("MVNW_PASSWORD").toCharArray();
Authenticator.setDefault(new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, password);
}
});
}
URL website = new URL(urlString);
ReadableByteChannel rbc;
rbc = Channels.newChannel(website.openStream());
FileOutputStream fos = new FileOutputStream(destination);
fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
fos.close();
rbc.close();
}
}

BIN
.mvn/wrapper/maven-wrapper.jar vendored Normal file

Binary file not shown.

2
.mvn/wrapper/maven-wrapper.properties vendored Normal file
View File

@ -0,0 +1,2 @@
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip
wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar

322
mvnw vendored Normal file
View File

@ -0,0 +1,322 @@
#!/bin/sh
# ----------------------------------------------------------------------------
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
# ----------------------------------------------------------------------------
# ----------------------------------------------------------------------------
# Maven Start Up Batch script
#
# Required ENV vars:
# ------------------
# JAVA_HOME - location of a JDK home dir
#
# Optional ENV vars
# -----------------
# M2_HOME - location of maven2's installed home dir
# MAVEN_OPTS - parameters passed to the Java VM when running Maven
# e.g. to debug Maven itself, use
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
# ----------------------------------------------------------------------------
if [ -z "$MAVEN_SKIP_RC" ]; then
if [ -f /etc/mavenrc ]; then
. /etc/mavenrc
fi
if [ -f "$HOME/.mavenrc" ]; then
. "$HOME/.mavenrc"
fi
fi
# OS specific support. $var _must_ be set to either true or false.
cygwin=false
darwin=false
mingw=false
case "$(uname)" in
CYGWIN*) cygwin=true ;;
MINGW*) mingw=true ;;
Darwin*)
darwin=true
# Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
# See https://developer.apple.com/library/mac/qa/qa1170/_index.html
if [ -z "$JAVA_HOME" ]; then
if [ -x "/usr/libexec/java_home" ]; then
export JAVA_HOME="$(/usr/libexec/java_home)"
else
export JAVA_HOME="/Library/Java/Home"
fi
fi
;;
esac
if [ -z "$JAVA_HOME" ]; then
if [ -r /etc/gentoo-release ]; then
JAVA_HOME=$(java-config --jre-home)
fi
fi
if [ -z "$M2_HOME" ]; then
## resolve links - $0 may be a link to maven's home
PRG="$0"
# need this for relative symlinks
while [ -h "$PRG" ]; do
ls=$(ls -ld "$PRG")
link=$(expr "$ls" : '.*-> \(.*\)$')
if expr "$link" : '/.*' >/dev/null; then
PRG="$link"
else
PRG="$(dirname "$PRG")/$link"
fi
done
saveddir=$(pwd)
M2_HOME=$(dirname "$PRG")/..
# make it fully qualified
M2_HOME=$(cd "$M2_HOME" && pwd)
cd "$saveddir"
# echo Using m2 at $M2_HOME
fi
# For Cygwin, ensure paths are in UNIX format before anything is touched
if $cygwin; then
[ -n "$M2_HOME" ] &&
M2_HOME=$(cygpath --unix "$M2_HOME")
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=$(cygpath --unix "$JAVA_HOME")
[ -n "$CLASSPATH" ] &&
CLASSPATH=$(cygpath --path --unix "$CLASSPATH")
fi
# For Mingw, ensure paths are in UNIX format before anything is touched
if $mingw; then
[ -n "$M2_HOME" ] &&
M2_HOME="$( (
cd "$M2_HOME"
pwd
))"
[ -n "$JAVA_HOME" ] &&
JAVA_HOME="$( (
cd "$JAVA_HOME"
pwd
))"
fi
if [ -z "$JAVA_HOME" ]; then
javaExecutable="$(which javac)"
if [ -n "$javaExecutable" ] && ! [ "$(expr \"$javaExecutable\" : '\([^ ]*\)')" = "no" ]; then
# readlink(1) is not available as standard on Solaris 10.
readLink=$(which readlink)
if [ ! $(expr "$readLink" : '\([^ ]*\)') = "no" ]; then
if $darwin; then
javaHome="$(dirname \"$javaExecutable\")"
javaExecutable="$(cd \"$javaHome\" && pwd -P)/javac"
else
javaExecutable="$(readlink -f \"$javaExecutable\")"
fi
javaHome="$(dirname \"$javaExecutable\")"
javaHome=$(expr "$javaHome" : '\(.*\)/bin')
JAVA_HOME="$javaHome"
export JAVA_HOME
fi
fi
fi
if [ -z "$JAVACMD" ]; then
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"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
else
JAVACMD="$(which java)"
fi
fi
if [ ! -x "$JAVACMD" ]; then
echo "Error: JAVA_HOME is not defined correctly." >&2
echo " We cannot execute $JAVACMD" >&2
exit 1
fi
if [ -z "$JAVA_HOME" ]; then
echo "Warning: JAVA_HOME environment variable is not set."
fi
CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
# traverses directory structure from process work directory to filesystem root
# first directory with .mvn subdirectory is considered project base directory
find_maven_basedir() {
if [ -z "$1" ]; then
echo "Path not specified to find_maven_basedir"
return 1
fi
basedir="$1"
wdir="$1"
while [ "$wdir" != '/' ]; do
if [ -d "$wdir"/.mvn ]; then
basedir=$wdir
break
fi
# workaround for JBEAP-8937 (on Solaris 10/Sparc)
if [ -d "${wdir}" ]; then
wdir=$(
cd "$wdir/.."
pwd
)
fi
# end of workaround
done
echo "${basedir}"
}
# concatenates all lines of a file
concat_lines() {
if [ -f "$1" ]; then
echo "$(tr -s '\n' ' ' <"$1")"
fi
}
BASE_DIR=$(find_maven_basedir "$(pwd)")
if [ -z "$BASE_DIR" ]; then
exit 1
fi
##########################################################################################
# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
# This allows using the maven wrapper in projects that prohibit checking in binary data.
##########################################################################################
if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found .mvn/wrapper/maven-wrapper.jar"
fi
else
if [ "$MVNW_VERBOSE" = true ]; then
echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
fi
if [ -n "$MVNW_REPOURL" ]; then
jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
else
jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
fi
while IFS="=" read key value; do
case "$key" in wrapperUrl)
jarUrl="$value"
break
;;
esac
done <"$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
if [ "$MVNW_VERBOSE" = true ]; then
echo "Downloading from: $jarUrl"
fi
wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
if $cygwin; then
wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath")
fi
if command -v wget >/dev/null; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found wget ... using wget"
fi
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
wget "$jarUrl" -O "$wrapperJarPath"
else
wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath"
fi
elif command -v curl >/dev/null; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found curl ... using curl"
fi
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
curl -o "$wrapperJarPath" "$jarUrl" -f
else
curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
fi
else
if [ "$MVNW_VERBOSE" = true ]; then
echo "Falling back to using Java to download"
fi
javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
# For Cygwin, switch paths to Windows format before running javac
if $cygwin; then
javaClass=$(cygpath --path --windows "$javaClass")
fi
if [ -e "$javaClass" ]; then
if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
if [ "$MVNW_VERBOSE" = true ]; then
echo " - Compiling MavenWrapperDownloader.java ..."
fi
# Compiling the Java class
("$JAVA_HOME/bin/javac" "$javaClass")
fi
if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
# Running the downloader
if [ "$MVNW_VERBOSE" = true ]; then
echo " - Running MavenWrapperDownloader.java ..."
fi
("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
fi
fi
fi
fi
##########################################################################################
# End of extension
##########################################################################################
export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
if [ "$MVNW_VERBOSE" = true ]; then
echo $MAVEN_PROJECTBASEDIR
fi
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
# For Cygwin, switch paths to Windows format before running java
if $cygwin; then
[ -n "$M2_HOME" ] &&
M2_HOME=$(cygpath --path --windows "$M2_HOME")
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME")
[ -n "$CLASSPATH" ] &&
CLASSPATH=$(cygpath --path --windows "$CLASSPATH")
[ -n "$MAVEN_PROJECTBASEDIR" ] &&
MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR")
fi
# Provide a "standardized" way to retrieve the CLI args that will
# work with both Windows and non-Windows executions.
MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
export MAVEN_CMD_LINE_ARGS
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
exec "$JAVACMD" \
$MAVEN_OPTS \
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
"-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"

182
mvnw.cmd vendored Normal file
View File

@ -0,0 +1,182 @@
@REM ----------------------------------------------------------------------------
@REM Licensed to the Apache Software Foundation (ASF) under one
@REM or more contributor license agreements. See the NOTICE file
@REM distributed with this work for additional information
@REM regarding copyright ownership. The ASF licenses this file
@REM to you under the Apache License, Version 2.0 (the
@REM "License"); you may not use this file except in compliance
@REM with the License. You may obtain a copy of the License at
@REM
@REM https://www.apache.org/licenses/LICENSE-2.0
@REM
@REM Unless required by applicable law or agreed to in writing,
@REM software distributed under the License is distributed on an
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@REM KIND, either express or implied. See the License for the
@REM specific language governing permissions and limitations
@REM under the License.
@REM ----------------------------------------------------------------------------
@REM ----------------------------------------------------------------------------
@REM Maven Start Up Batch script
@REM
@REM Required ENV vars:
@REM JAVA_HOME - location of a JDK home dir
@REM
@REM Optional ENV vars
@REM M2_HOME - location of maven2's installed home dir
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
@REM e.g. to debug Maven itself, use
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
@REM ----------------------------------------------------------------------------
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
@echo off
@REM set title of command window
title %0
@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
@REM set %HOME% to equivalent of $HOME
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
@REM Execute a user defined script before this one
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
@REM check for pre script, once with legacy .bat ending and once with .cmd ending
if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
:skipRcPre
@setlocal
set ERROR_CODE=0
@REM To isolate internal variables from possible post scripts, we use another setlocal
@setlocal
@REM ==== START VALIDATION ====
if not "%JAVA_HOME%" == "" goto OkJHome
echo.
echo Error: JAVA_HOME not found in your environment. >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
:OkJHome
if exist "%JAVA_HOME%\bin\java.exe" goto init
echo.
echo Error: JAVA_HOME is set to an invalid directory. >&2
echo JAVA_HOME = "%JAVA_HOME%" >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
@REM ==== END VALIDATION ====
:init
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
@REM Fallback to current working directory if not found.
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
set EXEC_DIR=%CD%
set WDIR=%EXEC_DIR%
:findBaseDir
IF EXIST "%WDIR%"\.mvn goto baseDirFound
cd ..
IF "%WDIR%"=="%CD%" goto baseDirNotFound
set WDIR=%CD%
goto findBaseDir
:baseDirFound
set MAVEN_PROJECTBASEDIR=%WDIR%
cd "%EXEC_DIR%"
goto endDetectBaseDir
:baseDirNotFound
set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
cd "%EXEC_DIR%"
:endDetectBaseDir
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
@setlocal EnableExtensions EnableDelayedExpansion
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
:endReadAdditionalConfig
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
)
@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
if exist %WRAPPER_JAR% (
if "%MVNW_VERBOSE%" == "true" (
echo Found %WRAPPER_JAR%
)
) else (
if not "%MVNW_REPOURL%" == "" (
SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
)
if "%MVNW_VERBOSE%" == "true" (
echo Couldn't find %WRAPPER_JAR%, downloading it ...
echo Downloading from: %DOWNLOAD_URL%
)
powershell -Command "&{"^
"$webclient = new-object System.Net.WebClient;"^
"if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
"$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
"}"^
"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
"}"
if "%MVNW_VERBOSE%" == "true" (
echo Finished downloading %WRAPPER_JAR%
)
)
@REM End of extension
@REM Provide a "standardized" way to retrieve the CLI args that will
@REM work with both Windows and non-Windows executions.
set MAVEN_CMD_LINE_ARGS=%*
%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
if ERRORLEVEL 1 goto error
goto end
:error
set ERROR_CODE=1
:end
@endlocal & set ERROR_CODE=%ERROR_CODE%
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
@REM check for post script, once with legacy .bat ending and once with .cmd ending
if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
:skipRcPost
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
if "%MAVEN_BATCH_PAUSE%" == "on" pause
if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
exit /B %ERROR_CODE%

226
pom.xml Normal file
View File

@ -0,0 +1,226 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>org.codedream</groupId>
<artifactId>epaper</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>epaper</name>
<description>ePaper For CodeDream</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mariadb.jdbc</groupId>
<artifactId>mariadb-java-client</artifactId>
<version>2.5.4</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.61</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.1.71.android</version>
</dependency>
<dependency>
<groupId>com.github.java-json-tools</groupId>
<artifactId>json-patch</artifactId>
<version>1.12</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.11</version>
</dependency>
<!-- Restful API 文档可视化支持 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>com.baidu.aip</groupId>
<artifactId>java-sdk</artifactId>
<version>4.12.0</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-scratchpad</artifactId>
<version>4.1.2</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>4.1.2</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.1.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<!-- 缓存依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.python</groupId>
<artifactId>jython-standalone</artifactId>
<version>2.7.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.pdfbox/pdfbox -->
<dependency>
<groupId>org.apache.pdfbox</groupId>
<artifactId>pdfbox</artifactId>
<version>2.0.19</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.docx4j/docx4j -->
<dependency>
<groupId>args4j</groupId>
<artifactId>args4j</artifactId>
<version>2.32</version>
</dependency>
<dependency>
<groupId>org.docx4j</groupId>
<artifactId>docx4j</artifactId>
<version>3.2.1</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>fr.opensagres.xdocreport</groupId>
<artifactId>org.apache.poi.xwpf.converter.pdf</artifactId>
<version>1.0.6</version>
</dependency>
<dependency>
<groupId>fr.opensagres.xdocreport</groupId>
<artifactId>org.odftoolkit.odfdom.converter.pdf</artifactId>
<version>1.0.6</version>
</dependency>
<dependency>
<groupId>com.googlecode.jaxb-namespaceprefixmapper-interfaces</groupId>
<artifactId>JAXBNamespacePrefixMapper</artifactId>
<version>2.2.4</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>2.2.11</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-core</artifactId>
<version>2.2.11</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.freemarker/freemarker -->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.30</version>
</dependency>
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>AlibabaMaven</id>
<name>Maven Aliyun Mirror</name>
<url>http://maven.aliyun.com/nexus/content/repositories/central/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</project>

View File

@ -0,0 +1,21 @@
package org.codedream.epaper;
import org.codedream.epaper.configure.EPApplicationContextInitializer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication
@EnableScheduling
@EnableAsync
public class EpaperApplication {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(EpaperApplication.class);
// 添加启动检查
application.addInitializers(new EPApplicationContextInitializer());
application.run(args);
}
}

View File

@ -0,0 +1,20 @@
package org.codedream.epaper.component;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* 手动获得Bean的工具类
*/
@Component
public class EPSpringUtil {
@Resource
private ApplicationContext applicationContext;
public <T> T getBean(Class<T> tClass){
return applicationContext.getBean(tClass);
}
}

View File

@ -0,0 +1,218 @@
package org.codedream.epaper.component.api;
import org.codedream.epaper.component.datamanager.JSONParameter;
import org.codedream.epaper.component.json.respond.JSONBaseRespondObject;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* JSON返回快速构造组件类主要用于异常处理机制
*/
@Component
public class QuickJSONRespond {
@Resource
private JSONParameter jsonParameter;
/**
* 根据对象构造获得标准的JSON响应字符串
* @param status 状态码
* @param msg 信息
* @param info 附加信息
* @param dataObject 附带对象
* @return JSON字符串
*/
public String getJSONStandardRespond(Integer status, String msg, String info, Object dataObject){
JSONBaseRespondObject respondObject = new JSONBaseRespondObject(status, msg);
if(info != null) respondObject.setInfo(info);
else respondObject.setInfo(null);
respondObject.setData(dataObject);
return jsonParameter.getJSONString(respondObject);
}
/**
* 根据对象构造获得标准的JSON响应字符串
* @param status 状态码
* @param dataObject 附带对象
* @return JSON字符串
*/
public String getJSONStandardRespond(HttpStatus status, Object dataObject){
JSONBaseRespondObject respondObject = new JSONBaseRespondObject(status.value(), status.getReasonPhrase());
respondObject.setData(dataObject);
return jsonParameter.getJSONString(respondObject);
}
/**
* 根据对象构造获得标准的JSON响应字符串
* @param status 状态码
* @param info 附加信息
* @param dataObject 附带对象
* @return JSON字符串
*/
public String getJSONStandardRespond(HttpStatus status, String info, Object dataObject){
JSONBaseRespondObject respondObject = new JSONBaseRespondObject(status.value(), status.getReasonPhrase());
if(info != null) respondObject.setInfo(info);
else respondObject.setInfo(null);
respondObject.setData(dataObject);
return jsonParameter.getJSONString(respondObject);
}
/**
* 根据对象构造获得标准的JSON响应字符串
* @param status 状态码
* @param info 附加信息
* @return JSON字符串
*/
public String getJSONStandardRespond(HttpStatus status, String info){
JSONBaseRespondObject respondObject = new JSONBaseRespondObject(status.value(), status.getReasonPhrase());
if(info != null) respondObject.setInfo(info);
else respondObject.setInfo(null);
return jsonParameter.getJSONString(respondObject);
}
/**
* 获得标准的JSON响应字符串返回特定状态码的和解释息
* @param code 状态码
* @param msg 信息
* @param info 附加信息
* @return JSON字符串
*/
public String getJSONStandardRespond(Integer code, String msg, String info){
JSONBaseRespondObject respondObject = new JSONBaseRespondObject(code, msg);
if(info != null) respondObject.setInfo(info);
else respondObject.setInfo(null);
respondObject.setData(null);
return jsonParameter.getJSONString(respondObject);
}
/**
* 获得标准的JSON响应字符串返回(404状态)
* @param info 附加信息
* @return JSON字符串
*/
public String getRespond404(String info){
return getJSONStandardRespond(HttpStatus.NOT_FOUND, info);
}
/**
* 获得标准的JSON响应字符串返回(404状态)
* @param info 附加信息
* @param object 附带对象
* @return JSON字符串
*/
public String getRespond404(String info, Object object){
return getJSONStandardRespond(HttpStatus.NOT_FOUND, info, object);
}
/**
* 获得标准的JSON响应字符串返回(500状态)
* @param info 附加信息
* @return JSON字符串
*/
public String getRespond500(String info){
return getJSONStandardRespond(HttpStatus.INTERNAL_SERVER_ERROR, info);
}
/**
* 获得标准的JSON响应字符串返回(200状态)
* @param info 附加信息
* @return JSON字符串
*/
public String getRespond200(String info){
return getJSONStandardRespond(HttpStatus.OK, info);
}
/**
* 获得标准的JSON响应字符串返回(200状态)
* @param info 附加信息
* @param object 附带对象
* @return JSON字符串
*/
public String getRespond200(String info, Object object){
return getJSONStandardRespond(HttpStatus.OK, info, object);
}
/**
* 获得标准的JSON响应字符串返回(403状态)
* @param info 附加信息
* @return JSON字符串
*/
public String getRespond403(String info){
return getJSONStandardRespond(HttpStatus.FORBIDDEN, info);
}
/**
* 获得标准的JSON响应字符串返回(406状态)
* @param info 附加信息
* @return JSON字符串
*/
public String getRespond406(String info){
return getJSONStandardRespond(HttpStatus.NOT_ACCEPTABLE, info);
}
/**
* 获得标准的JSON响应字符串返回(406状态)
* @param info 附加信息
* @param object 附带对象
* @return JSON字符串
*/
public String getRespond406(String info, Object object){
return getJSONStandardRespond(HttpStatus.NOT_ACCEPTABLE, info, object);
}
/**
* 获得标准的JSON响应字符串返回(501状态)
* @param info 附加信息
* @return JSON字符串
*/
public String getRespond501(String info){
return getJSONStandardRespond(501, "Not Implemented", info) ;
}
/**
* 获得标准的JSON响应字符串返回(401状态)
* @param info 附加信息
* @return JSON字符串
*/
public String getRespond401(String info){
return getJSONStandardRespond(401, "Unauthorized", info);
}
/**
* 获得标准的JSON响应字符串返回(400状态)
* @param info 附加信息
* @return JSON字符串
*/
public String getRespond400(String info){
return getJSONStandardRespond(400, "Bad Request", info);
}
/**
* 获得标准的JSON响应字符串返回(404状态)
* @param info 附加信息
* @param object 附带对象
* @return JSON字符串
*/
public String getRespond400(String info, Object object){
return getJSONStandardRespond(400, "Bad Request", info, object);
}
/**
* 获得标准的JSON响应字符串返回(400状态)
* @param info 附加信息
* @return JSON字符串
*/
public String getRespond409(String info){
return getJSONStandardRespond(409, "Conflict", info);
}
}

View File

@ -0,0 +1,31 @@
package org.codedream.epaper.component.article;
import org.codedream.epaper.component.json.model.JsonableSTN;
import org.codedream.epaper.model.article.Article;
import org.codedream.epaper.model.article.Paragraph;
import org.codedream.epaper.model.article.Sentence;
import org.codedream.epaper.model.task.Task;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
/**
* 获得章结构的句列表
*/
@Component
public class GetSentenceFromArticle {
/**
* 获得章结构的句列表
* @param article 章结构
* @return 句结构列表
*/
public List<Sentence> get(Article article){
List<Sentence> sentences = new ArrayList<>();
for(Paragraph paragraph : article.getParagraphs()){
sentences.addAll(paragraph.getSentences());
}
return sentences;
}
}

View File

@ -0,0 +1,22 @@
package org.codedream.epaper.component.auth;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.util.Optional;
/**
* 检查请求是否为Ajax的方式发起
*/
@Component
public class AJAXRequestChecker {
/**
* 检查请求是否为Ajax的方式发起
* @param request HTTP请求
* @return 布尔值
*/
public boolean checkAjaxPOSTRequest(HttpServletRequest request){
return Optional.ofNullable(request.getHeader("X-Requested-With")).isPresent();
}
}

View File

@ -0,0 +1,27 @@
package org.codedream.epaper.component.auth;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Date;
import java.util.UUID;
/**
* Token生成器
*/
@Component
public class AuthTokenGenerator {
@Resource
private SHA1Encoder encoder;
/**
* 生成Token
* @param username 用户名
* @return token字符串
*/
public String generateAuthToken(String username){
Date dateNow = new Date();
UUID uuid = UUID.randomUUID();
return encoder.encode(String.format("Token [%s][%d][%s]",username,dateNow.getTime(), uuid.toString()));
}
}

View File

@ -0,0 +1,63 @@
package org.codedream.epaper.component.auth;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Component;
import java.util.Collection;
/**
* 权限管理器
*/
@Component
public class EPAccessDecisionManager implements AccessDecisionManager {
/**
* 确定用户的权限
* @param authentication 认证柄
* @param object 传入对象
* @param configAttributes 角色信息对象
* @throws AccessDeniedException 访问禁止异常
* @throws InsufficientAuthenticationException 会话认证异常
*/
@Override
public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
if(null == configAttributes || configAttributes.size() <= 0) {
return;
}
for (ConfigAttribute c : configAttributes) {
String needRole = c.getAttribute();
for (GrantedAuthority ga : authentication.getAuthorities()) {
if (needRole.trim().equals(ga.getAuthority())) {
return;
}
}
}
throw new AccessDeniedException("Access Denied");
}
/**
* 是否支持该角色信息对象
* @param attribute 角色信息对象
* @return 布尔值
*/
@Override
public boolean supports(ConfigAttribute attribute) {
return true;
}
/**
* 是否支持该认证柄
* @param clazz 认证柄对象信息
* @return 布尔值
*/
@Override
public boolean supports(Class<?> clazz) {
return true;
}
}

View File

@ -0,0 +1,43 @@
package org.codedream.epaper.component.auth;
import lombok.extern.slf4j.Slf4j;
import org.codedream.epaper.component.api.QuickJSONRespond;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 已认证用户访问无权限资源状态处理
*/
@Slf4j
@Component
public class EPAccessDeniedHandler implements AccessDeniedHandler {
@Resource
private QuickJSONRespond quickJSONRespond;
/**
* 处理函数
* @param request HTTP请求
* @param response HTTP返回
* @param accessDeniedException 无权限访问异常
* @throws IOException I/O异常
*/
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException)
throws IOException {
log.info("ASEAccessDeniedHandler Found!");
// 对无权限操作返回403
response.getWriter().print(quickJSONRespond.getRespond403(null));
response.setStatus(403);
}
}

View File

@ -0,0 +1,38 @@
package org.codedream.epaper.component.auth;
import lombok.extern.slf4j.Slf4j;
import org.codedream.epaper.component.api.QuickJSONRespond;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 匿名用户访问无权限资源未得到认证的用户视为匿名用户
*/
@Slf4j
@Component
public class EPAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Resource
private QuickJSONRespond quickJSONRespond;
/**
* 处理函数
* @param request HTTP请求
* @param response HTTP返回
* @param authException 认证异常
* @throws IOException I/O异常
*/
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException)
throws IOException {
// 对匿名用户返回401
response.getWriter().print(quickJSONRespond.getRespond401(null));
response.setStatus(401);
}
}

View File

@ -0,0 +1,53 @@
package org.codedream.epaper.component.auth;
import lombok.extern.slf4j.Slf4j;
import org.codedream.epaper.component.api.QuickJSONRespond;
import org.codedream.epaper.component.json.respond.ErrorInfoJSONRespond;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;
/**
* 认证失败处理
*/
@Slf4j
@Component
public class EPAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
@Resource
private QuickJSONRespond quickJSONRespond;
/**
*
* @param request HTTP请求
* @param response HTTP返回
* @param exception 异常类型
* @throws IOException I/O异常
*/
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception)
throws IOException
{
log.info("ASEAuthenticationFailureHandler Login Fail!");
// 填写异常信息存储对象
ErrorInfoJSONRespond errorInfoJSONRespond = new ErrorInfoJSONRespond();
errorInfoJSONRespond.setDate(new Date());
errorInfoJSONRespond.setExceptionMessage(exception.getMessage());
errorInfoJSONRespond.setException(exception.getClass().getSimpleName());
// 认证失败返回406
response.getWriter().write(quickJSONRespond.getJSONStandardRespond(
406,
"Not Acceptable",
"Authentication Failure",
errorInfoJSONRespond));
response.setStatus(406);
}
}

View File

@ -0,0 +1,65 @@
package org.codedream.epaper.component.auth;
import lombok.extern.slf4j.Slf4j;
import org.codedream.epaper.component.api.QuickJSONRespond;
import org.codedream.epaper.component.json.respond.UserLoginCheckerJSONRespond;
import org.codedream.epaper.model.user.User;
import org.codedream.epaper.service.IAuthService;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Optional;
// 认证成功返回
@Slf4j
@Component
public class EPAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
@Resource
private QuickJSONRespond quickJSONRespond;
@Resource
private IAuthService authService;
/**
*
* @param request HTTP请求
* @param response HTTP返回
* @param authentication 认证柄
* @throws IOException I/O异常
* @throws ServletException Servlet异常
*/
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
throws IOException, ServletException
{
UserLoginCheckerJSONRespond respond = new UserLoginCheckerJSONRespond();
respond.setUserExist(authentication.isAuthenticated());
respond.setLoginStatus(authentication.isAuthenticated());
respond.setPvc(authService.preValidationCodeGetter());
// 获得 JSONTokenAuthenticationToken
JSONTokenAuthenticationToken authenticationToken = (JSONTokenAuthenticationToken) authentication;
User user = (User) authenticationToken.getPrincipal();
Optional<String> tokenOptional = authService.userNewTokenGetter(
user.getUsername(), authenticationToken.getClientCode());
if(tokenOptional.isPresent()){
respond.setToken(tokenOptional.get());
}
else respond.setToken("");
// 认证成功返回200
response.getWriter().write(quickJSONRespond.getRespond200("Authentication Success", respond));
response.setStatus(200);
}
}

View File

@ -0,0 +1,126 @@
package org.codedream.epaper.component.auth;
import lombok.extern.slf4j.Slf4j;
import org.codedream.epaper.model.auth.JSONToken;
import org.codedream.epaper.service.AuthService;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Collection;
import java.util.Date;
import java.util.Optional;
/**
* API请求验证服务
*/
@Slf4j
public class EPJSONTokenAuthenticationFilter extends OncePerRequestFilter {
@Resource
private JSONRandomCodeGenerator randomCodeGenerator;
@Resource
private AuthService authService;
@Resource
private JSONSignedGenerator signedGenerator;
@Resource
private UserDetailsService userDetailsService;
/**
*
* @param request HTTP请求
* @param response HTTP返回
* @param filterChain 过滤器链
* @throws ServletException Servlet异常
* @throws IOException I/O异常
*/
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
// 用户名
String openid = request.getHeader( "openid");
// 客户端签名
String signed = request.getHeader("signed");
// 时间戳
String timestamp = request.getHeader("timestamp");
// 服务端API测试豁免签名
if(signed != null && signed.equals("6d4923fca4dcb51f67b85e54a23a8d763d9e02af")){
//执行授权
doAuthentication("test", request);
}
// 正常认证
else if (signed != null && openid != null && timestamp != null) {
// 获得具体时间
Date date = new Date(Long.parseLong(timestamp));
Date now = new Date();
// 限制时间戳有效区间为60s
long dtTime = 60*1000;
Date maxDate = new Date(now.getTime() + dtTime);
// 检查时间戳是否合理
if(maxDate.after(date)) {
// 从服务器中查找token
Optional<JSONToken> optionalJSONToken = authService.findTokenByUserName(openid);
if (optionalJSONToken.isPresent()) {
JSONToken token = optionalJSONToken.get();
// 检查token是否过期
if (!authService.checkTokenIfExpired(token)) {
// 生成特征随机代码
String randomCode = randomCodeGenerator.generateRandomCode(openid,
date, token.getClientCode());
log.info(String.format("Determined Signed: %s",
signedGenerator.generateSigned(openid, randomCode, token.getToken())));
log.info(String.format("Get Signed: %s", signed));
// 检查签名是否正确
if (signed.equals(signedGenerator.generateSigned(openid, randomCode, token.getToken()))) {
// 执行授权操作
doAuthentication(openid, request);
}
}
}
}
}
filterChain.doFilter(request, response);
}
// 执行授权
private void doAuthentication(String username, HttpServletRequest request){
// 查询用户的相关信息
UserDetails user = userDetailsService.loadUserByUsername(username);
// 生成用户权限列表
Collection<? extends GrantedAuthority> authorities = user.getAuthorities();
// 生成授权柄 (储存上下文信息)
JSONTokenAuthenticationToken authentication =
new JSONTokenAuthenticationToken(user, null, authorities);
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
// 执行授权
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}

View File

@ -0,0 +1,32 @@
package org.codedream.epaper.component.auth;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
/**
* 密码编码器
*/
@Component
public class EPPasswordEncoder implements PasswordEncoder {
/**
* 密码编码
* @param charSequence
* @return 密文
*/
@Override
public String encode(CharSequence charSequence) {
return DigestUtils.sha256Hex(charSequence.toString());
}
/**
* 密码验证
* @param charSequence 字符队列
* @param s 密文
* @return 布尔值
*/
@Override
public boolean matches(CharSequence charSequence, String s) {
return s.equals(DigestUtils.sha256Hex(charSequence.toString()));
}
}

View File

@ -0,0 +1,70 @@
package org.codedream.epaper.component.auth;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.*;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Collection;
// 普通用户名密码验证, 用户获得Token
@Slf4j
@Component
public class EPSecurityAuthenticationProvider implements AuthenticationProvider {
@Resource
UserDetailsService userDetailsService;
@Resource
EPPasswordEncoder passwordEncoder;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
JSONTokenUsernamePasswordAuthenticationToken authenticationToken =
(JSONTokenUsernamePasswordAuthenticationToken) authentication;
// 获得JSON中的学号
String username = (String) authenticationToken.getPrincipal();
// 获得JSON中的加密密码
String encrypted_password = passwordEncoder.encode((String) authenticationToken.getCredentials());
// 获得客户端代码
String clientCode = authenticationToken.getClientCode();
// 判断用户是否存在并获取用户信息
UserDetails userInfo = userDetailsService.loadUserByUsername(username);
if (userInfo == null) {
throw new UsernameNotFoundException("User Not Exist");
}
// 判断密码是否正确
if(!userInfo.getPassword().equals(encrypted_password)){
throw new BadCredentialsException("Password IS INCORRECT");
}
// 判断账号是否停用/删除
if (!userInfo.isEnabled()) {
throw new DisabledException("User IS Disabled");
}
else if(!userInfo.isAccountNonLocked()) {
throw new LockedException("User IS Locked");
}
else if(!userInfo.isAccountNonExpired()) {
throw new AccountExpiredException("User IS Expired");
}
// 生成权限列表
Collection<? extends GrantedAuthority> authorities = userInfo.getAuthorities();
return new JSONTokenAuthenticationToken(userInfo, clientCode, authorities);
}
@Override
public boolean supports(Class<?> aClass) {
return aClass.equals(JSONTokenUsernamePasswordAuthenticationToken.class);
}
}

View File

@ -0,0 +1,29 @@
package org.codedream.epaper.component.auth;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.stereotype.Component;
/**
* 用户名编码器
*/
@Component
public class EPUsernameEncoder {
/**
* 编码
* @param charSequence 字符队列
* @return 密文
*/
public String encode(CharSequence charSequence){
return "openid_" + DigestUtils.sha256Hex(charSequence.toString());
}
/**
* 验证
* @param charSequence 字符队列
* @param s 密文
* @return 布尔值
*/
public boolean matches(CharSequence charSequence, String s){
return s.equals(encode(charSequence.toString()));
}
}

View File

@ -0,0 +1,87 @@
package org.codedream.epaper.component.auth;
import lombok.extern.slf4j.Slf4j;
import org.codedream.epaper.component.datamanager.JSONParameter;
import org.codedream.epaper.component.json.request.UserLoginChecker;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Optional;
/**
* 账号认证过滤器
*/
@Slf4j
public class EPUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
@Resource
private JSONParameter jsonParameter;
@Resource
private AJAXRequestChecker ajaxRequestChecker;
@Resource
private TimestampExpiredChecker timestampExpiredChecker;
/**
* 验证函数
* @param request HTTP请求
* @param response HTTP返回
* @return 认证柄
* @throws AuthenticationException 认证异常
*/
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException {
String timestamp = request.getHeader("timestamp");
// 检查时间戳是否合理(60秒内)
if(timestamp == null || !timestampExpiredChecker.checkTimestampBeforeMaxTime(timestamp, 60)){
throw new AuthenticationServiceException("Timestamp Expired.");
}
// 判断是否为AJAX请求格式的数据
if(!ajaxRequestChecker.checkAjaxPOSTRequest(request)) {
throw new AuthenticationServiceException("Authentication method not supported: NOT Ajax Method.");
}
Optional<UserLoginChecker> checkerOptional = jsonParameter.getJavaObjectByRequest(request, UserLoginChecker.class);
if(!checkerOptional.isPresent()) throw new BadCredentialsException("Invalid AJAX JSON Request");
UserLoginChecker checker = checkerOptional.get();
if(checker.getOpenid() == null
|| checker.getPassword() == null
|| checker.getClientCode() == null)
throw new AuthenticationServiceException("Request Data IS Incomplete");
// 获得相应的用户名密码
String openid = checker.getOpenid();
// 得到加密密码
String password = checker.getPassword();
String clientCode = checker.getClientCode();
if (openid == null) openid = "";
if (password == null) password = "";
// 去除首尾两端的空白字符
openid = openid.trim();
password = password.trim();
JSONTokenUsernamePasswordAuthenticationToken authRequest =
new JSONTokenUsernamePasswordAuthenticationToken(openid, password, clientCode);
authRequest.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
return this.getAuthenticationManager().authenticate(authRequest);
}
}

View File

@ -0,0 +1,28 @@
package org.codedream.epaper.component.auth;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Date;
/**
* 随机特征值生成器
*/
@Component
public class JSONRandomCodeGenerator {
@Resource
private SHA1Encoder encoder;
/**
* 生成随机特征值
* @param username 用户名
* @param date 时间
* @param clientCode 客户端代码
* @return 随机特征值字符串
*/
public String generateRandomCode(String username, Date date, String clientCode){
return encoder.encode(String.format("RandomCode [%s][%s][%s]",
username, Long.toString(date.getTime()), clientCode));
}
}

View File

@ -0,0 +1,25 @@
package org.codedream.epaper.component.auth;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* 客户端签名生成器
*/
@Component
public class JSONSignedGenerator {
@Resource
SHA1Encoder encoder;
/**
* 生成签名
* @param username 用户名
* @param randomCode 随机特征值
* @param token Token
* @return 客户端签名
*/
public String generateSigned(String username, String randomCode, String token){
return encoder.encode(String.format("SIGN [%s][%s][%s]",username, randomCode, token));
}
}

View File

@ -0,0 +1,59 @@
package org.codedream.epaper.component.auth;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
/**
* 关联Token与其他用户的相关数据的认证柄
*/
public class JSONTokenAuthenticationToken extends AbstractAuthenticationToken {
// 客户端签名
private String signed = null;
// 用户名
private Object principal = null;
// 客户端代码
private String clientCode = null;
public JSONTokenAuthenticationToken(UserDetails principal,
String clientCode,
Collection<? extends GrantedAuthority> authorities)
{
super(authorities);
this.principal = principal;
this.clientCode = clientCode;
this.signed = null;
setAuthenticated(true);
}
public JSONTokenAuthenticationToken(String principal, String clientCode, String signed) {
super(null);
this.principal = principal;
this.clientCode = clientCode;
this.signed = signed;
setAuthenticated(false);
}
@Override
public String getCredentials() {
return signed;
}
@Override
public Object getPrincipal() {
return principal;
}
public String getClientCode() {
return clientCode;
}
public void setClientCode(String clientCode) {
this.clientCode = clientCode;
}
}

View File

@ -0,0 +1,43 @@
package org.codedream.epaper.component.auth;
import org.springframework.security.authentication.AbstractAuthenticationToken;
/**
* 明文用户名密码验证认证柄
*/
public class JSONTokenUsernamePasswordAuthenticationToken extends AbstractAuthenticationToken {
// 用户名
private String username = null;
// 明文密码
private String password = null;
// 授权柄
private String clientCode = null;
public JSONTokenUsernamePasswordAuthenticationToken(String username, String password, String clientCode) {
super(null);
this.username = username;
this.password = password;
this.clientCode = clientCode;
setAuthenticated(false);
}
@Override
public Object getCredentials() {
return password;
}
@Override
public Object getPrincipal() {
return username;
}
/**
* 扩展接口 获得客户端代码
* @return 客户端代码
*/
public String getClientCode() {
return clientCode;
}
}

View File

@ -0,0 +1,29 @@
package org.codedream.epaper.component.auth;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.stereotype.Component;
/**
* SHA1编码器
*/
@Component
public class SHA1Encoder {
/**
* 编码
* @param charSequence 字符队列
* @return 密文
*/
public String encode(CharSequence charSequence){
return DigestUtils.sha1Hex(charSequence.toString());
}
/**
* 验证
* @param charSequence 字符队列
* @param s 密文
* @return 布尔值
*/
public boolean match (CharSequence charSequence, String s){
return s.equals(encode(charSequence));
}
}

View File

@ -0,0 +1,50 @@
package org.codedream.epaper.component.auth;
import org.springframework.stereotype.Component;
import java.util.Date;
/**
* 验证时间戳验证器
*/
@Component
public class TimestampExpiredChecker {
/**
* 检查时间戳是否在某个最大有效时间前
* @param timestamp 时间戳字符串
* @param seconds 最大有效时间
* @return 布尔值
*/
public boolean checkTimestampBeforeMaxTime(String timestamp, int seconds){
Date timestampDate = new Date(Long.parseLong(timestamp));
long currentTime = System.currentTimeMillis();
Date maxDate = new Date(currentTime + seconds * 1000);
return timestampDate.before(maxDate);
}
/**
* 检查时间戳是否在某个最大有效时间前
* @param date 时间对象
* @param seconds 最大做大有效时间
* @return 布尔值
*/
public boolean checkDateBeforeMaxTime(Date date, int seconds){
long currentTime = System.currentTimeMillis();
Date maxDate = new Date(currentTime + seconds * 1000);
return date.before(maxDate);
}
/**
* 检查时间戳是否在某个设置的时间前
* @param date 时间对象
* @param seconds 设置时间
* @return 布尔值
*/
public boolean checkDateBeforeDeterminedTime(Date date, int seconds){
long currentTime = System.currentTimeMillis();
Date maxDate = new Date(currentTime - seconds * 1000);
return date.before(maxDate);
}
}

View File

@ -0,0 +1,33 @@
package org.codedream.epaper.component.batchthread;
import org.codedream.epaper.model.task.BatchProcessingTask;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
public class ComparableFutureTask extends FutureTask implements Comparable<ComparableFutureTask> {
private BatchProcessingTask taskToAppoint = new BatchProcessingTask();
public ComparableFutureTask(Callable callable) {
super(callable);
}
@Override
public int compareTo(ComparableFutureTask o) {
if (this.getTaskToAppoint().getPriority() > o.getTaskToAppoint().getPriority()) {
return 1;
} else if (this.getTaskToAppoint().getPriority() < o.getTaskToAppoint().getPriority()) {
return -1;
}
return 0;
}
public BatchProcessingTask getTaskToAppoint() {
return taskToAppoint;
}
}

View File

@ -0,0 +1,53 @@
package org.codedream.epaper.component.batchthread;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.codedream.epaper.model.task.BatchProcessingTask;
import javax.annotation.Resource;
import java.util.Comparator;
import java.util.concurrent.Callable;
@Data
@Slf4j
public class TaskAppointer implements Callable<BatchProcessingTask>, Comparable<TaskAppointer> {
Comparator<BatchProcessingTask> comparator = new Comparator<BatchProcessingTask>() {
@Override
public int compare(BatchProcessingTask o1, BatchProcessingTask o2) {
return o1.getPriority() - o2.getPriority() < 0 ? -1 : 1;
}
};
@Resource
private BatchProcessingTask taskToAppoint = new BatchProcessingTask();
public TaskAppointer(BatchProcessingTask bpt) {
this.taskToAppoint = bpt;
}
@Override
public int compareTo(TaskAppointer o) {
if (this.getTaskToAppoint().getPriority() > o.getTaskToAppoint().getPriority()) {
return 1;
} else if (this.getTaskToAppoint().getPriority() < o.getTaskToAppoint().getPriority()) {
return -1;
}
return 0;
}
@Override
public BatchProcessingTask call() throws Exception {
log.info("Thead " + Thread.currentThread().getName() + "bpt" + this.getTaskToAppoint().getId());
try {
Thread.sleep(10);
} catch (Exception e) {
e.printStackTrace();
}
log.info("Thread " + Thread.currentThread().getName() + " executing");
return getTaskToAppoint();
}
}

View File

@ -0,0 +1,31 @@
package org.codedream.epaper.component.cache;
import lombok.extern.slf4j.Slf4j;
import org.codedream.epaper.repository.article.CacheRepository;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.time.LocalDateTime;
@EnableScheduling
@Component
@Slf4j
public class ScheduleCacheTask {
@Resource
CacheRepository cacheRepository;
@Scheduled(cron = "0 0 0 1/1 * ? ")
public void deleteCache() {
LocalDateTime deleteTime = LocalDateTime.now().minusDays(7);
try {
cacheRepository.deleteByRecordTime(deleteTime);
} catch (Exception e) {
log.error("Failed to delete cache.");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,101 @@
package org.codedream.epaper.component.datamanager;
import org.codedream.epaper.exception.innerservererror.RuntimeIOException;
import org.codedream.epaper.model.file.File;
import org.codedream.epaper.repository.file.FileRepository;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
/**
* 文件解析器
*/
@Component
public class FileParser {
@Resource
private FileRepository fileRepository;
@Resource
private SHA512Encoder encoder;
/**
* 查找缓存
* @param hash 哈希值
* @return 文件信息对象
*/
public Optional<File> find(String hash){
Iterable<File> files = fileRepository.findAllByHash(hash);
Iterator<File> fileIterator = files.iterator();
if(!fileIterator.hasNext()) return Optional.empty();
return Optional.of(fileIterator.next());
}
/**
* 数据散列值计算
* @param bytesData 文件数据
* @return 散列值
*/
public String encode(byte[] bytesData){
return encoder.encode(Base64.getEncoder().encodeToString(bytesData));
}
/**
* 读取文件数据
* @param stream 输入流
* @return 文件数据字节数组
*/
public Optional<byte[]> read(InputStream stream){
ByteArrayOutputStream arrayOutputStream = new ByteArrayOutputStream();
try {
// 双重数据写出
int readBits = 0;
byte[] rawBytes = new byte[1024];
while ((readBits = stream.read(rawBytes)) != -1) {
arrayOutputStream.write(rawBytes, 0, readBits);
}
return Optional.of(arrayOutputStream.toByteArray());
} catch (IOException e){
return Optional.empty();
}
}
/**
* 将文件数据写入到文件系统
* @param file 文件信息对象
* @param bytesData 文件数据
* @return 文件信息对象更新后
*/
public File writeOut(File file, byte[] bytesData){
String storageName = UUID.randomUUID().toString();
Path path = Paths.get(file.getPath(), storageName);
try {
Files.createFile(path);
OutputStream outputStream = Files.newOutputStream(path);
ByteArrayInputStream stream = new ByteArrayInputStream(bytesData);
// 数据写出文件系统
int readBits = 0;
byte[] rawBytes = new byte[1024];
while ((readBits = stream.read(rawBytes)) != -1) {
outputStream.write(rawBytes, 0, readBits);
}
outputStream.close();
stream.close();
file.setStorageName(storageName);
file.setSize(bytesData.length);
return file;
} catch (IOException e){
throw new RuntimeIOException(e.getMessage());
}
}
}

View File

@ -0,0 +1,120 @@
package org.codedream.epaper.component.datamanager;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.fge.jsonpatch.JsonPatch;
import com.github.fge.jsonpatch.JsonPatchException;
import org.codedream.epaper.exception.innerservererror.HandlingErrorsException;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.util.Optional;
/**
* JSON请求参数处理器
*/
@Component
public class JSONParameter {
/**
* 获得HTTP请求内容
* @param request HTTP请求
* @return 内容字符串
*/
public String getRequestBody(HttpServletRequest request){
try {
StringBuilder stringBuilder = new StringBuilder();
BufferedReader reader = request.getReader();
reader.reset();
String line;
while ((line = reader.readLine()) != null)
stringBuilder.append(line);
return stringBuilder.toString();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 提取Request内容并解析为JSON对象
* @param request HTTP请求
* @return JSON对象
*/
public Optional<JSONObject> getJSONByRequest(HttpServletRequest request){
try {
JSONObject jsonParam = null;
String content = getRequestBody(request);
jsonParam = JSONObject.parseObject(content);
return Optional.ofNullable(jsonParam);
} catch (Exception e) {
e.printStackTrace();
return Optional.empty();
}
}
/**
* 根据JSON对象构造JSON字符串用于返回
* @param json JSON对象
* @return JSON字符串
*/
public String getJSONString(JSONObject json){
return json.toJSONString();
}
/**
* 根据Java对象构造JSON字符串用于返回
* @param object Java对象
* @return JSON字符串
*/
public String getJSONString(Object object){
return JSON.toJSONString(object);
}
/**
* 由JSON对象获得对应的Java对象
* @param json JSON对象
* @param type 对应的Java对象类型
* @param <T> 对应的Java对象类型
* @return 指定的Java对象
*/
public <T> T getJavaObject(JSONObject json, Class<T> type){
return json.toJavaObject(type);
}
/**
* 由HTTP请求获得对应的Java对象(一般用于Post请求中)
* @param request HTTP请求
* @param type 对应的Java对象类型
* @param <T> 对应的Java对象类型
* @return 指定的Java对象
*/
public <T> Optional<T> getJavaObjectByRequest(HttpServletRequest request, Class<T> type){
Optional<JSONObject> json = getJSONByRequest(request);
return json.map(jsonObject -> getJavaObject(jsonObject, type));
}
/**
* 将JsonPath对象转换成Java对象Restful API的Path动词
* @param patch JsonPath对象
* @param object Java对象
* @param <T> 对应的Java对象类型
* @return 指定的Java对象更新后
*/
public <T> T parsePathToObject(JsonPatch patch, T object){
try {
ObjectMapper mapper = new ObjectMapper();
JsonNode patched = patch.apply(mapper.convertValue(object, JsonNode.class));
return (T) mapper.treeToValue(patched, object.getClass());
} catch (JsonPatchException | JsonProcessingException e) {
throw new HandlingErrorsException(e.getMessage());
}
}
}

View File

@ -0,0 +1,50 @@
package org.codedream.epaper.component.datamanager;
import org.codedream.epaper.configure.PunctuationConfiguration;
import org.codedream.epaper.model.article.Paragraph;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 将一个段落分为几个句子
*/
@Component
public class ParagraphDivider {
/**
* 将一个段落划分为若干句并进行相应的持久化
*
* @param text 需要划分的段落文本
* @return 段落中的所有被划分好的句子
*/
public List<String> divideParagraph(String text) {
if (text.isEmpty()) return null;
Paragraph paragraph = new Paragraph();
paragraph.setText(text);
List<String> back = PunctuationConfiguration.getBackPunctuations();
String[] arr = text.split("。|||……|\\?|");
List<String> sentenceTexts = Arrays.asList(arr);
List<String> sentences = new ArrayList<>();
Pattern p = Pattern.compile("[\u4e00-\u9fa5]");
for (int i = 0; i < sentenceTexts.size(); i++) {
String sentenceText = sentenceTexts.get(i);
Matcher m = p.matcher(sentenceText);
int len = sentenceTexts.get(i).length();
if (!m.find() || len < 15 || len > 510) continue;
if (back.contains(sentenceTexts.get(i).charAt(0)) && sentences.size() > 1) {
sentences.set(i - 1, sentences.get(i - 1) + sentenceTexts.get(i).charAt(0));
}
sentences.add(sentenceTexts.get(i));
}
return sentences;
}
}

View File

@ -0,0 +1,94 @@
package org.codedream.epaper.component.datamanager;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.text.PDFTextStripper;
import org.codedream.epaper.exception.innerservererror.HandlingErrorsException;
import org.codedream.epaper.exception.innerservererror.RuntimeIOException;
import org.codedream.epaper.model.article.Article;
import org.codedream.epaper.model.article.Paragraph;
import org.codedream.epaper.model.file.File;
import org.codedream.epaper.repository.article.ParagraphRepository;
import org.codedream.epaper.service.IArticleService;
import org.codedream.epaper.service.IFileService;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.io.IOException;
import java.io.InputStream;
import java.util.Optional;
@Component
public class PdfParser {
@Resource
private IFileService fileService;
@Resource
private IArticleService articleService;
@Resource
private ParagraphRepository paragraphRepository;
@Resource
private SHA512Encoder encoder;
public Article parse(InputStream stream){
try {
Article article = articleService.createArticle(null);
PDDocument doc = PDDocument.load(stream);
PDFTextStripper textStripper = new PDFTextStripper();
String content = textStripper.getText(doc);
String regA = "^[\\u4e00-\\u9fa5a-zA-Z0-9。!;、:()“”]";
String regB = "[\\s|.,/?\"%$#@*^~`()+=\\-{}<>\\///_]";
content = content.replaceAll(regA, "");
content = content.replaceAll(regB, "");
saveParagraph(article, content);
doc.close();
return article;
} catch (IOException e) {
throw new RuntimeIOException(e.getMessage());
}
}
public Integer parse(Integer fileId){
File file = fileService.getFileInfo(fileId);
if(file.getType().equals("pdf")){
Article article = parse(fileService.getFile(fileId));
article.setFileId(fileId);
article = articleService.save(article);
return article.getId();
}
else throw new HandlingErrorsException(file.getType());
}
// 储存段结构并考虑缓存情况
private void saveParagraph(Article article, String text){
String hash = encoder.encode(text);
Paragraph paragraph;
Optional<Paragraph> paragraphOptional = paragraphRepository.findBySha512Hash(hash);
if(!paragraphOptional.isPresent()){
paragraph = articleService.createParagraph(text);
paragraph.setSha512Hash(hash);
paragraph = articleService.save(paragraph);
}
else{
paragraph = paragraphOptional.get();
}
articleService.addParagraph(article, paragraph);
}
}

View File

@ -0,0 +1,235 @@
package org.codedream.epaper.component.datamanager;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import lombok.extern.slf4j.Slf4j;
import org.apache.log4j.helpers.Loader;
import org.codedream.epaper.component.article.GetSentenceFromArticle;
import org.codedream.epaper.component.json.model.JsonableSTNResult;
import org.codedream.epaper.component.json.model.JsonableTaskResult;
import org.codedream.epaper.component.task.JsonableTaskResultGenerator;
import org.codedream.epaper.exception.innerservererror.HandlingErrorsException;
import org.codedream.epaper.exception.innerservererror.InnerDataTransmissionException;
import org.codedream.epaper.exception.innerservererror.RuntimeIOException;
import org.codedream.epaper.model.article.Sentence;
import org.codedream.epaper.model.file.File;
import org.codedream.epaper.model.task.Task;
import org.codedream.epaper.service.FileService;
import org.codedream.epaper.service.IFileService;
import org.codedream.epaper.service.ITaskService;
import org.docx4j.Docx4J;
import org.docx4j.fonts.IdentityPlusMapper;
import org.docx4j.fonts.Mapper;
import org.docx4j.fonts.PhysicalFont;
import org.docx4j.fonts.PhysicalFonts;
import org.docx4j.openpackaging.exceptions.Docx4JException;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.springframework.stereotype.Component;
import org.springframework.util.ResourceUtils;
import javax.annotation.Resource;
import java.io.*;
import java.net.URISyntaxException;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.*;
@Slf4j
@Component
public class ReportGenerator {
@Resource
private ITaskService taskService;
@Resource
private IFileService fileService;
@Resource
private JsonableTaskResultGenerator taskResultGenerator;
@Resource
private GetSentenceFromArticle getSentenceFromArticle;
Mapper fontMapper = null;
public byte[] generate(Integer taskId){
Optional<Task> taskOptional =taskService.getTaskInfo(taskId);
if(!taskOptional.isPresent()) throw new InnerDataTransmissionException(taskId.toString());
Task task = taskOptional.get();
// 获得数据列表
Map<String, Object> dataMap = processDataMap(task);
StringWriter stringWriter = new StringWriter();
BufferedWriter writer = new BufferedWriter(stringWriter);
Template template = getTemplate("report.ftl");
try {
template.process(dataMap, writer);
// 转换为Word
String xmlStr = stringWriter.toString();
ByteArrayInputStream in = new ByteArrayInputStream(xmlStr.getBytes());
WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.load(in);
wordMLPackage.save(new java.io.File("./Report.docx"));
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
wordMLPackage.setFontMapper(generateFrontMapper());
// 转换为PDF
Docx4J.toPDF(wordMLPackage, outputStream);
return outputStream.toByteArray();
} catch (IOException | TemplateException | Docx4JException e){
e.printStackTrace();
throw new HandlingErrorsException(e.getMessage());
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private synchronized Mapper generateFrontMapper() throws Exception {
if (this.fontMapper == null) {
// URL simSumUrl = ResourceUtils.getURL("classpath:fonts/simsun.ttc");
PhysicalFonts.discoverPhysicalFonts();
URL simSumUrl = ResourceUtils.getURL("./fonts/simsun.ttc");
PhysicalFonts.addPhysicalFont("SimSun", simSumUrl);
this.fontMapper = new IdentityPlusMapper();
fontMapper.put("隶书", PhysicalFonts.get("LiSu"));
fontMapper.put("宋体", PhysicalFonts.get("SimSun"));
fontMapper.put("微软雅黑", PhysicalFonts.get("Microsoft Yahei"));
fontMapper.put("黑体", PhysicalFonts.get("SimHei"));
fontMapper.put("楷体", PhysicalFonts.get("KaiTi"));
fontMapper.put("新宋体", PhysicalFonts.get("NSimSun"));
fontMapper.put("华文行楷", PhysicalFonts.get("STXingkai"));
fontMapper.put("华文仿宋", PhysicalFonts.get("STFangsong"));
fontMapper.put("仿宋", PhysicalFonts.get("FangSong"));
fontMapper.put("幼圆", PhysicalFonts.get("YouYuan"));
fontMapper.put("华文宋体", PhysicalFonts.get("STSong"));
fontMapper.put("华文中宋", PhysicalFonts.get("STZhongsong"));
fontMapper.put("等线", PhysicalFonts.get("SimSun"));
fontMapper.put("等线 Light", PhysicalFonts.get("SimSun"));
fontMapper.put("华文琥珀", PhysicalFonts.get("STHupo"));
fontMapper.put("华文隶书", PhysicalFonts.get("STLiti"));
fontMapper.put("华文新魏", PhysicalFonts.get("STXinwei"));
fontMapper.put("华文彩云", PhysicalFonts.get("STCaiyun"));
fontMapper.put("方正姚体", PhysicalFonts.get("FZYaoti"));
fontMapper.put("方正舒体", PhysicalFonts.get("FZShuTi"));
fontMapper.put("华文细黑", PhysicalFonts.get("STXihei"));
fontMapper.put("宋体扩展", PhysicalFonts.get("simsun-extB"));
fontMapper.put("仿宋_GB2312", PhysicalFonts.get("FangSong_GB2312"));
fontMapper.put("新細明體", PhysicalFonts.get("SimSun"));
//解决宋体正文和宋体标题的乱码问题
PhysicalFonts.put("PMingLiU", PhysicalFonts.get("SimSun"));
PhysicalFonts.put("新細明體", PhysicalFonts.get("SimSun"));
return this.fontMapper;
}
return this.fontMapper;
}
public void saveByFile(String path, byte[] bytes) throws IOException {
FileOutputStream outputStream = new FileOutputStream(path);
outputStream.write(bytes);
outputStream.close();
}
public Integer saveByFileService(byte[] byteArray){
ByteArrayInputStream inputStream = new ByteArrayInputStream(byteArray);
return fileService.saveFile(UUID.randomUUID().toString() + ".pdf", "pdf", inputStream);
}
private Map<String, Object> processDataMap(Task task){
File file = task.getFile();
if(file == null) throw new InnerDataTransmissionException(task.toString());
JsonableTaskResult taskResult = taskResultGenerator.getJsonableTaskResult(task.getId());
Map<String, Object> dataMap = new HashMap<>();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm");
dataMap.put("articleTitle", file.getName());
dataMap.put("datetime", sdf.format(new Date()));
if(taskResult.getScore() > 90)
dataMap.put("grade", "A");
else if(taskResult.getScore() > 80)
dataMap.put("grade", "B");
else if(taskResult.getScore() > 60)
dataMap.put("grade", "C");
else
dataMap.put("grade", "D");
dataMap.put("wdScore", taskResult.getCorrectionScore());
dataMap.put("fqScore", taskResult.getDnnScore());
dataMap.put("flScore", taskResult.getEmotionScore());
List<Sentence> sentences = getSentenceFromArticle.get(task.getArticle());
int totalLen = 0;
for(Sentence sentence : sentences)
totalLen += sentence.getText().length();
dataMap.put("svgLen", totalLen / sentences.size());
dataMap.put("stnNum", sentences.size());
dataMap.put("userId", task.getUser().getId());
dataMap.put("errStatus", "正态分布");
dataMap.put("wEum", taskResult.getWrongTextCount());
dataMap.put("pcsTime", task.getEndDate().getTime() - task.getCreateDate().getTime());
dataMap.put("fqRum", taskResult.getBrokenSentencesCount());
dataMap.put("flEum", taskResult.getOralCount());
dataMap.put("status", "完成");
Map<Integer, STNResult> stnResults = new HashMap<>();
Map<Integer, Sentence> sentenceMap = new HashMap<>();
for(Sentence sentence : sentences){
sentenceMap.put(sentence.getId(), sentence);
}
for(JsonableSTNResult jsonableSTNResult : taskResult.getStnResults()){
if(jsonableSTNResult.getErrorList().size() == 0) continue;
STNResult stnResult = new STNResult();
stnResult.setId(jsonableSTNResult.getStnId());
stnResult.setText(sentenceMap.get(jsonableSTNResult.getStnId()).getText());
stnResults.put(stnResult.getId(), stnResult);
stnResult.setStnResultList(jsonableSTNResult.getErrorList());
stnResults.put(stnResult.getId(), stnResult);
}
List<STNResult> stnResultList = new ArrayList<>(stnResults.values());
dataMap.put("errorStnList", stnResultList);
return dataMap;
}
private Template getTemplate(String name){
try {
Configuration conf = new Configuration();
// conf.setDirectoryForTemplateLoading(ResourceUtils.getFile("classpath:templates"));
conf.setDirectoryForTemplateLoading(new java.io.File("./templates/"));
return conf.getTemplate(name);
} catch (IOException e){
throw new RuntimeIOException(e.getMessage());
}
}
}

View File

@ -0,0 +1,29 @@
package org.codedream.epaper.component.datamanager;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.stereotype.Component;
/**
* SHA256编码器
*/
@Component
public class SHA256Encoder {
/**
* 编码
* @param charSequence 字符队列
* @return 密文
*/
public String encode(CharSequence charSequence){
return DigestUtils.sha256Hex(charSequence.toString());
}
/**
* 验证
* @param charSequence 字符队列
* @param s 密文
* @return 布尔值
*/
public boolean match (CharSequence charSequence, String s){
return s.equals(encode(charSequence));
}
}

View File

@ -0,0 +1,29 @@
package org.codedream.epaper.component.datamanager;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.stereotype.Component;
/**
* SHA256编码器
*/
@Component
public class SHA512Encoder {
/**
* 编码
* @param charSequence 字符队列
* @return 密文
*/
public String encode(CharSequence charSequence){
return DigestUtils.sha512Hex(charSequence.toString());
}
/**
* 验证
* @param charSequence 字符队列
* @param s 密文
* @return 布尔值
*/
public boolean match (CharSequence charSequence, String s){
return s.equals(encode(charSequence));
}
}

View File

@ -0,0 +1,15 @@
package org.codedream.epaper.component.datamanager;
import lombok.Data;
import org.codedream.epaper.component.json.model.JsonableSTNError;
import org.codedream.epaper.component.json.model.JsonableSTNResult;
import java.util.ArrayList;
import java.util.List;
@Data
public class STNResult {
private Integer id;
private String text;
private List<JsonableSTNError> stnResultList = new ArrayList<>();
}

View File

@ -0,0 +1,122 @@
package org.codedream.epaper.component.datamanager;
import com.baidu.aip.nlp.AipNlp;
import com.sun.org.apache.xpath.internal.operations.Bool;
import javafx.util.Pair;
import lombok.extern.slf4j.Slf4j;
import org.codedream.epaper.configure.SingletonAipNlp;
import org.codedream.epaper.model.article.Phrase;
import org.codedream.epaper.model.article.Sentence;
import org.codedream.epaper.service.ArticleService;
import org.json.JSONArray;
import org.json.JSONObject;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.*;
/**
* 提供句子分词方法并通过{@link ArticleService}进行相应持久化操作
*/
@Slf4j
@Component
public class SentenceDivider {
@Resource
ArticleService articleService;
/**
* 将句子分词
*
* @param text 要进行分词操作的句子
* @return 一个已经持久化的分完词的句子
*/
public List<Phrase> divideSentence(String text) {
if (text.isEmpty()) return null;
AipNlp client = SingletonAipNlp.getInstance();
HashMap<String, Object> hashMap = new HashMap<>();
List<Phrase> phrases = new ArrayList<>();
String f = "";
try {
JSONObject res = client.lexer(text, hashMap);
f = "1";
boolean lexerFlag = true;
Iterator<String> iterator = res.keys();
while (iterator.hasNext()) {
if (iterator.next().equals("error_msg")) {
lexerFlag = false;
break;
}
}
if (lexerFlag) {
f = "2";
JSONArray items = (JSONArray) res.get("items");
f = "After lexer";
for (int i = 0; i < items.length(); i++) {
JSONObject item = (JSONObject) items.get(i);
List<Phrase> basic = new ArrayList<>();
f = "Before basicArray";
JSONArray basicArray = (JSONArray) item.get("basic_words");
for (int j = 0; j < basicArray.length(); j++) {
f = "In basicArray";
Pair<Boolean, Phrase> phrasePair = articleService.savePhrase(basicArray.get(j).toString());
basic.add(phrasePair.getValue());
}
f = "Before vec";
JSONObject vecRes = new JSONObject();
Pair<Boolean, Phrase> phrasePair = articleService.savePhrase(item.get("item").toString());
Phrase phrase = phrasePair.getValue();
if(!phrasePair.getKey()){
phrase.setText(item.get("item").toString());
phrase.getBasicPhrase().addAll(basic);
phrase.setPos(item.get("pos").toString());
phrase = articleService.save(phrase);
}
Iterator<String> keys = vecRes.keys();
boolean flag = true;
while (keys.hasNext()) {
if (keys.next().equals("error_msg")) {
flag = false;
break;
}
}
flag = false;
if (flag) {
f = "Before vecArray";
JSONArray vec = vecRes.getJSONArray("vec");
List<Float> floatList = new ArrayList<>();
for (int j = 0; j < vec.length(); j++) {
f = "In vecArray";
String str = vec.getString(i);
f = "Before parsing to float";
float vecUnit = Float.parseFloat(str);
floatList.add(vecUnit);
}
phrase.setVec(floatList);
}
phrases.add(phrase);
}
}
} catch (Exception e) {
log.error(e.toString() + ": f");
}
return phrases;
}
}

View File

@ -0,0 +1,38 @@
package org.codedream.epaper.component.datamanager;
import com.baidu.aip.nlp.AipNlp;
import lombok.extern.slf4j.Slf4j;
import org.codedream.epaper.configure.SingletonAipNlp;
import org.json.JSONObject;
import org.springframework.stereotype.Component;
import java.util.HashMap;
/**
* 提供句子通顺度获取方法
*/
@Slf4j
@Component
public class SentenceSmoothnessGetter {
/**
* 通过调用百度接口获取句子通顺度
*
* @param text 待处理的文本
* @return 句子通顺度的值
*/
public float getSentenceSmoothness(String text) {
AipNlp client = SingletonAipNlp.getInstance();
HashMap<String, Object> options = new HashMap<>();
JSONObject jsonObject = client.dnnlmCn(text, options);
try {
Thread.sleep(500);
float dnn = Float.parseFloat(jsonObject.get("ppl").toString());
return dnn;
} catch (Exception e) {
log.error(e.toString() + "In dnn: " + jsonObject.toString());
return (float) 0;
}
}
}

View File

@ -0,0 +1,25 @@
package org.codedream.epaper.component.datamanager;
import lombok.Data;
/**
* 储存字符串标识的文件可以将文件直接转换为Json进行传输
*/
@Data
public class StringFile {
// 文件内容Base64编码Gzip算法压缩
private String strData = null;
// 散列值
private String sha256 = null;
// 文件大小
private Integer size = null;
// 文件类型
private String type = "none";
// 文件名
private String name = null;
}

View File

@ -0,0 +1,131 @@
package org.codedream.epaper.component.datamanager;
import org.codedream.epaper.exception.innerservererror.StringFileConvertException;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Base64;
import java.util.Optional;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
/**
* 字符串文件生成器
*/
@Component
public class StringFileGenerator {
@Resource
private SHA256Encoder encoder;
/**
* 用过读入流创建一个字符串文件
* @param name 文件名
* @param type 文件类型
* @param stream 输入流
* @return 字符串文件对象
*/
public Optional<StringFile> generateStringFile(String name, String type,InputStream stream){
StringFile file = new StringFile();
file.setName(name);
file.setType(type);
// 字符串内容计算
file.setStrData(generateFile2String(stream));
if(file.getStrData() == null) return Optional.empty();
// 相关校验值计算
file.setSha256(generateSHA256Checker(file.getStrData()));
file.setSize(file.getStrData().length());
return Optional.of(file);
}
/**
* 一次性读取输入流中的文件数据
* @param stream 输入流
* @return 文件数据
*/
private byte[] readSteamAll(InputStream stream) {
try {
byte[] bytes = new byte[stream.available()];
//检查文件书否完全读取
if (stream.read(bytes) != bytes.length) return null;
else return bytes;
} catch (IOException e){
return null;
}
}
/**
* 将文件数据压缩Gzip然后用Base64编码为字符串
* @param stream 输入流
* @return 文件数据编码
*/
private String generateFile2String(InputStream stream){
ByteArrayOutputStream zipDataStream = new ByteArrayOutputStream();
try {
// 解压缩
GZIPOutputStream gzipOutputStream = new GZIPOutputStream(zipDataStream);
byte[] bytes = readSteamAll(stream);
if(bytes == null) return null;
gzipOutputStream.write(bytes);
gzipOutputStream.close();
// 编码转换
return Base64.getEncoder().encodeToString(zipDataStream.toByteArray());
} catch (IOException e) {
return null;
}
}
/**
* 生成字符串文件的校验码
* @param str 文件散列值检查
* @return
*/
private String generateSHA256Checker(String str){
return encoder.encode(str);
}
/**
* 检查文件内容是否正确包括大小与校验码
* @param file 字符串文件对象
* @return 布尔值
*/
public boolean checkStringFile(StringFile file){
return file.getStrData().length() == file.getSize()
&& encoder.match(file.getStrData(), file.getSha256());
}
/**
* 从字符串文件中读取真实的文件数据
* @param file 字符串文件对象
* @return 输入流
*/
public InputStream readFileString(StringFile file){
try {
// 字符串转换为二进制数据
byte[] bytes = Base64.getDecoder().decode(file.getStrData());
GZIPInputStream stream = new GZIPInputStream(new ByteArrayInputStream(bytes), bytes.length);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
// 数据解压缩
int readBits = 0;
byte[] rawBytes = new byte[1024];
while ((readBits = stream.read(rawBytes)) != -1) {
outputStream.write(rawBytes, 0, readBits);
}
stream.close();
return new ByteArrayInputStream(outputStream.toByteArray());
} catch (IOException e) {
throw new StringFileConvertException("Read FileString Failed");
}
}
}

View File

@ -0,0 +1,70 @@
package org.codedream.epaper.component.datamanager;
import com.baidu.aip.nlp.AipNlp;
import lombok.extern.slf4j.Slf4j;
import org.codedream.epaper.configure.SingletonAipNlp;
import org.codedream.epaper.model.task.CorrectionResult;
import org.json.JSONObject;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
/**
* 用于文本纠错
*/
@Slf4j
@Component
public class TextCorrector {
/**
* 文本纠错
*
* @param text 需要纠错的文本
* @return 一个纠正类的列表保存句子需要修改的信息
* 值的位置corr_text存放纠错后文本org_text存放原文本
*/
public List<CorrectionResult> correctText(String text) {
if (text.isEmpty()) return new ArrayList<>();
AipNlp client = SingletonAipNlp.getInstance();
HashMap<String, Object> options = new HashMap<>();
List<CorrectionResult> correctionResults = new ArrayList<>();
String correction;
try {
JSONObject res = (JSONObject) client.ecnet(text, options).get("item");
if (res.get("vec_fragment") == null) {
return correctionResults;
}
correction = (String) res.get("correct_query");
DiffMatchPatch dmp = new DiffMatchPatch();
List<DiffMatchPatch.Diff> diffs = dmp.diff_main(text, correction);
int p = 0;
int size = diffs.size();
for (int i = 0; i < size; i++) {
DiffMatchPatch.Diff diff = diffs.get(i);
if (diff.operation.equals(DiffMatchPatch.Operation.EQUAL) ||
diff.operation.equals(DiffMatchPatch.Operation.INSERT)) {
p += diff.text.length();
continue;
}
CorrectionResult correctionResult = new CorrectionResult();
correctionResult.setStartPos(p);
correctionResult.setLength(diff.text.length());
if (i == size - 1 || !diffs.get(i + 1).operation.equals(DiffMatchPatch.Operation.INSERT)) {
correctionResult.setCorrectionText("");
} else {
DiffMatchPatch.Diff nextDiff = diffs.get(i + 1);
correctionResult.setCorrectionText(nextDiff.text);
}
correctionResults.add(correctionResult);
}
} catch (Exception e) {
log.error(e.toString() + ": Failed to analyze \"" + text + "\"");
}
return correctionResults;
}
}

View File

@ -0,0 +1,82 @@
package org.codedream.epaper.component.datamanager;
import javassist.bytecode.ByteArray;
import org.codedream.epaper.exception.innerservererror.RuntimeIOException;
import org.codedream.epaper.model.article.Article;
import org.codedream.epaper.model.article.Paragraph;
import org.codedream.epaper.model.file.File;
import org.codedream.epaper.repository.article.ParagraphRepository;
import org.codedream.epaper.service.IArticleService;
import org.codedream.epaper.service.IFileService;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Optional;
@Component
public class TextParser {
@Resource
private IFileService fileService;
@Resource
private IArticleService articleService;
@Resource
private ParagraphRepository paragraphRepository;
@Resource
private SHA512Encoder encoder;
public Integer parse(Integer fileId) {
File file = fileService.getFileInfo(fileId);
if(file.getType().equals("plain")){
Article article = articleService.createArticle(null);
InputStream stream = fileService.getFile(fileId);
ByteArrayOutputStream arrayOutputStream = new ByteArrayOutputStream();
try {
// 数据内存写出
int readBits = 0;
byte[] rawBytes = new byte[1024];
while ((readBits = stream.read(rawBytes)) != -1) {
arrayOutputStream.write(rawBytes, 0, readBits);
}
saveParagraph(article, new String(arrayOutputStream.toByteArray()));
article.setFileId(fileId);
article = articleService.save(article);
return article.getId();
}
catch (IOException e){
throw new RuntimeIOException(e.getMessage());
}
}
return null;
}
// 储存段结构并考虑缓存情况
private void saveParagraph(Article article, String text){
String hash = encoder.encode(text);
Paragraph paragraph;
Optional<Paragraph> paragraphOptional = paragraphRepository.findBySha512Hash(hash);
if(!paragraphOptional.isPresent()){
paragraph = articleService.createParagraph(text);
paragraph.setSha512Hash(hash);
paragraph = articleService.save(paragraph);
}
else{
paragraph = paragraphOptional.get();
}
articleService.addParagraph(article, paragraph);
}
}

View File

@ -0,0 +1,132 @@
package org.codedream.epaper.component.datamanager;
import org.apache.poi.hwpf.extractor.WordExtractor;
import org.apache.poi.xwpf.extractor.XWPFWordExtractor;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.codedream.epaper.configure.AppConfigure;
import org.codedream.epaper.exception.innerservererror.HandlingErrorsException;
import org.codedream.epaper.exception.innerservererror.RuntimeIOException;
import org.codedream.epaper.model.article.Article;
import org.codedream.epaper.model.article.Paragraph;
import org.codedream.epaper.model.file.File;
import org.codedream.epaper.repository.article.ParagraphRepository;
import org.codedream.epaper.service.IArticleService;
import org.codedream.epaper.service.IFileService;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
import java.util.Optional;
/**
* Word文档解析
*/
@Component
public class WordParser {
@Resource
private IFileService fileService;
@Resource
private IArticleService articleService;
@Resource
private ParagraphRepository paragraphRepository;
@Resource
private SHA512Encoder encoder;
@Resource
private AppConfigure configure;
public Article parse(InputStream stream, String type){
if(type.equals("doc")){
return parseDoc(stream);
}
else{
return parseDocx(stream);
}
}
public Integer parse(Integer fileId){
File file = fileService.getFileInfo(fileId);
if(file.getType().equals("doc") || file.getType().equals("docx")){
Article article = parse(fileService.getFile(fileId), file.getType());
article.setFileId(fileId);
article = articleService.save(article);
return article.getId();
}
else throw new HandlingErrorsException(file.getType());
}
public Article parseDoc(InputStream stream) {
try {
WordExtractor extractor = new WordExtractor(stream);
Article article = articleService.createArticle(null);
for (String text : extractor.getParagraphText()) {
if (text.length() > configure.getParagraphMinSize()
&& text.length() < configure.getParagraphMaxSize()) {
// 储存段结构
saveParagraph(article ,text);
}
}
// 保存章
return articleService.save(article);
} catch (IOException e){
throw new RuntimeIOException("Doc Parse Error");
}
}
public Article parseDocx(InputStream stream){
try{
XWPFDocument doc = new XWPFDocument(stream);
XWPFWordExtractor extractor = new XWPFWordExtractor(doc);
Article article = articleService.createArticle(null);
// 遍历段落
Iterator<XWPFParagraph> iterator = doc.getParagraphsIterator();
while (iterator.hasNext()){
XWPFParagraph para = iterator.next();
String text = para.getText();
if(text.length() > configure.getParagraphMinSize()
&& text.length() < configure.getParagraphMaxSize()){
System.out.println("Paragraph: " + text);
// 储存段结构
saveParagraph(article ,text);
}
}
// 保存章
return articleService.save(article);
} catch (IOException e){
throw new RuntimeIOException("Docx Parse Error");
}
}
// 储存段结构并考虑缓存情况
private void saveParagraph(Article article, String text){
String hash = encoder.encode(text);
Paragraph paragraph;
Optional<Paragraph> paragraphOptional = paragraphRepository.findBySha512Hash(hash);
if(!paragraphOptional.isPresent()){
paragraph = articleService.createParagraph(text);
paragraph.setSha512Hash(hash);
paragraph = articleService.save(paragraph);
}
else{
paragraph = paragraphOptional.get();
}
articleService.addParagraph(article, paragraph);
}
}

View File

@ -0,0 +1,14 @@
package org.codedream.epaper.component.json;
import lombok.extern.slf4j.Slf4j;
import java.util.Date;
/**
* 所有有效的JSON对象模板
*/
@Slf4j
public class JSONBaseObject {
Date time = new Date();
}

View File

@ -0,0 +1,43 @@
package org.codedream.epaper.component.json.model;
import io.swagger.annotations.ApiModel;
import io.swagger.models.auth.In;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.codedream.epaper.model.article.Article;
import org.codedream.epaper.model.article.Paragraph;
import org.codedream.epaper.model.article.Sentence;
import org.codedream.epaper.model.task.BatchProcessingTask;
import org.codedream.epaper.model.task.Task;
import java.util.ArrayList;
import java.util.List;
@Data
@ApiModel("批处理任务结构")
@NoArgsConstructor
public class JsonableBPT {
private Integer id;
private Integer stnNumber;
private List<JsonableSTN> stns = new ArrayList<>();
public JsonableBPT(BatchProcessingTask bpt){
this.id = bpt.getId();
this.stnNumber = bpt.getSentencesNumber();
for (Task task : bpt.getTasks()){
for(Paragraph paragraph : task.getArticle().getParagraphs()){
for(Sentence sentence : paragraph.getSentences()){
// 检查是否已经深处理完毕
if(sentence.isDeepProcess()) continue;
JsonableSTN stn = new JsonableSTN();
stn.setStnId(sentence.getId());
stn.setText(sentence.getText());
this.stns.add(stn);
}
}
}
}
}

View File

@ -0,0 +1,18 @@
package org.codedream.epaper.component.json.model;
import io.swagger.annotations.ApiModel;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.ArrayList;
import java.util.List;
@Data
@ApiModel("批处理任务结果返回结构")
@NoArgsConstructor
public class JsonableBPTResult {
// 句子Id
private Integer stnid;
// 标签的预测值
private List<Float> tagPossible = new ArrayList<>();
}

View File

@ -0,0 +1,29 @@
package org.codedream.epaper.component.json.model;
import io.swagger.annotations.ApiModel;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.codedream.epaper.model.server.ChildServerPassport;
import java.util.Date;
@Data
@ApiModel("子服务器护照")
@NoArgsConstructor
public class JsonableCSP {
// 身份认证码
private String identityCode;
// 最后一次签证日期
private Date lastUpdateTime;
// 护照是否过期
private boolean expired;
public JsonableCSP(ChildServerPassport csp){
this.identityCode = csp.getIdentityCode();
this.lastUpdateTime = csp.getLastUpdateTime();
this.expired = csp.isExpired();
}
}

View File

@ -0,0 +1,20 @@
package org.codedream.epaper.component.json.model;
import io.swagger.annotations.ApiModel;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@ApiModel("文件信息结构")
@NoArgsConstructor
public class JsonableFile {
// 文件ID号
private Integer fileId;
// 文件名
private String filename;
// 文件类型
private String type;
}

View File

@ -0,0 +1,17 @@
package org.codedream.epaper.component.json.model;
import io.swagger.annotations.ApiModel;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@ApiModel("句原文列表结构")
@NoArgsConstructor
public class JsonableSTN {
// 句子ID号
private Integer stnId;
// 句子文本内容
private String text;
}

View File

@ -0,0 +1,23 @@
package org.codedream.epaper.component.json.model;
import io.swagger.annotations.ApiModel;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@ApiModel("句错误结构")
@NoArgsConstructor
public class JsonableSTNError {
// 序列号index
private Integer wordIdx;
// 词长
private Integer wordLen;
// 错误类型
private Integer type;
// 错误内容
private String content;
}

View File

@ -0,0 +1,21 @@
package org.codedream.epaper.component.json.model;
import io.swagger.annotations.ApiModel;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Data
@ApiModel("句分页结构")
@NoArgsConstructor
public class JsonableSTNPage {
// 页序号
Integer page;
// 页数
Integer all;
// 句列表
List<JsonableSTN> stns;
}

View File

@ -0,0 +1,28 @@
package org.codedream.epaper.component.json.model;
import io.swagger.annotations.ApiModel;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.ArrayList;
import java.util.List;
@Data
@ApiModel("句处理结果")
@NoArgsConstructor
public class JsonableSTNResult {
// 句ID号
Integer stnId;
// 错误类型
Integer appear;
// 分数
Float score;
// 是否为书面文本
boolean isNeutral;
// 错误列表
List<JsonableSTNError> errorList = new ArrayList<>();
}

View File

@ -0,0 +1,42 @@
package org.codedream.epaper.component.json.model;
import io.swagger.annotations.ApiModel;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.codedream.epaper.model.task.Task;
@Data
@ApiModel("子任务结果")
@NoArgsConstructor
public class JsonableTask {
// 任务ID号
private Integer taskId;
// 用户openid
private String openid;
// 文件ID号
private Integer fileId;
// 文本
private String text;
// 任务是否完成
private boolean finished;
// 任务进度
private Float progress;
// 描述
private String description;
public JsonableTask(Task task){
this.taskId = task.getId();
this.openid = task.getUser().getUsername();
this.fileId = task.getFile().getId();
this.finished = task.isFinished();
this.progress = task.getProgressRate() / 5.0f;
}
}

View File

@ -0,0 +1,43 @@
package org.codedream.epaper.component.json.model;
import io.swagger.annotations.ApiModel;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.ArrayList;
import java.util.List;
@Data
@ApiModel("任务处理结果")
@NoArgsConstructor
public class JsonableTaskResult {
// 任务ID号
private Integer taskId;
// 任务是否成功
private boolean success;
// 文章错误位置数量
private Integer wrongTextCount = 0;
// 不通顺的句子数量
private Integer brokenSentencesCount = 0;
// 口语化的句子数量
private Integer oralCount = 0;
// 文章得分
private Double score = (double) 0;
// 通顺度得分
private Double dnnScore = (double) 0;
// 感情倾向得分
private Double emotionScore = (double) 0;
// 文本纠错得分
private Double correctionScore = (double) 0;
private List<JsonableSTNResult> stnResults = new ArrayList<>();
}

View File

@ -0,0 +1,33 @@
package org.codedream.epaper.component.json.model;
import io.swagger.annotations.ApiModel;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.codedream.epaper.model.user.User;
@Data
@ApiModel("用户验证信息")
@NoArgsConstructor
public class JsonableUser {
// 用户的ID号数据库
private Integer id;
// 用户openid
private String openid;
// 密码哈希值
private String password;
public JsonableUser(User user){
this.openid = user.getUsername();
this.password = user.getPassword();
this.id = user.getId();
}
public User parseObject(User user){
user.setUsername(this.getOpenid());
user.setPassword(this.getPassword());
return user;
}
}

View File

@ -0,0 +1,22 @@
package org.codedream.epaper.component.json.request;
import lombok.Data;
/**
* 用户登录请求对象
*/
@Data
public class UserLoginChecker {
// 请求类型
private String checkType;
// openid
private String openid;
// 密码
private String password;
// 客户端代码
private String clientCode;
}

View File

@ -0,0 +1,8 @@
package org.codedream.epaper.component.json.respond;
/**
* 空应答
*/
public class EmptyDataObjectRespond {
}

View File

@ -0,0 +1,15 @@
package org.codedream.epaper.component.json.respond;
import lombok.Data;
import java.util.Date;
/**
* 错误信息对象
*/
@Data
public class ErrorInfoJSONRespond {
String exception = null;
String exceptionMessage = null;
Date date = null;
}

View File

@ -0,0 +1,61 @@
package org.codedream.epaper.component.json.respond;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.codedream.epaper.component.json.JSONBaseObject;
/**
* 服务端返回的JSON对象标准模板
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class JSONBaseRespondObject extends JSONBaseObject {
// 存放返回内容
private Object data = new EmptyDataObjectRespond();
// 存放响应信息提示
private String msg = "";
// 额外信息
private String info = null;
// 状态
private Integer status = 200;
public JSONBaseRespondObject(String msg){
super();
this.status = 200;
this.msg = msg;
}
public JSONBaseRespondObject(Integer status, String msg){
super();
this.status = status;
this.msg = msg;
}
public void setStatusNotFound(){
this.status = 404;
}
public void setStatusBadRequest(){
this.status = 400;
}
public void setStatusUnauthorized(){
this.status = 401;
}
public void setStatusForbidden(){
this.status = 403;
}
public void setStatusInternalServerError(){
this.status = 500;
}
public void setStatusOK(){
this.status = 200;
}
}

View File

@ -0,0 +1,32 @@
package org.codedream.epaper.component.json.respond;
import lombok.Data;
/**
* 用户登录请求应答
*/
@Data
public class UserLoginCheckerJSONRespond {
// 用户是否存在
Boolean userExist = null;
// 用户是否被封禁
Boolean userBanned = null;
// 登录状态
Boolean loginStatus = null;
// 返回附加信息
String respondInformation = null;
// Token
String token = null;
// 用户ID号数据库
String uid = null;
// 预验证码
String pvc = null;
}

View File

@ -0,0 +1,69 @@
package org.codedream.epaper.component.server;
import lombok.extern.slf4j.Slf4j;
import org.codedream.epaper.component.auth.TimestampExpiredChecker;
import org.codedream.epaper.configure.AppConfigure;
import org.codedream.epaper.exception.innerservererror.InnerDataTransmissionException;
import org.codedream.epaper.model.server.ChildServerPassport;
import org.codedream.epaper.model.task.BatchProcessingTask;
import org.codedream.epaper.repository.server.ChildServerPassportRepository;
import org.codedream.epaper.repository.task.BatchProcessingTaskRepository;
import org.codedream.epaper.service.INeuralNetworkModelService;
import org.codedream.epaper.service.ITaskService;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Optional;
/**
* 子服务器护照管理器
*/
@Slf4j
@Component
public class CSPUpdater {
@Resource
private ChildServerPassportRepository cspRepository;
@Resource
private BatchProcessingTaskRepository bptRepository;
@Resource
private INeuralNetworkModelService neuralNetworkModelService;
@Resource
private TimestampExpiredChecker expiredChecker;
@Resource
private AppConfigure configure;
/**
* 检查护照状态
*/
@Scheduled(cron = "0/60 * * * * ?")
public void update(){
log.info("CSP Updater Started");
Iterable<ChildServerPassport> childServerPassports = cspRepository.findByExpired(false);
for(ChildServerPassport csp : childServerPassports){
if(expiredChecker
.checkDateBeforeDeterminedTime(csp.getLastUpdateTime(), configure.gerChildServerRegisterTimeout())){
if(csp.getBptId() != null){
// 释放其占用的批处理任务
Optional<BatchProcessingTask> bpt = bptRepository.findById(csp.getBptId());
if(!bpt.isPresent()) throw new InnerDataTransmissionException();
neuralNetworkModelService.markBPTFailed(bpt.get());
log.info(String.format("Unlock BPT: bptId %s", csp.getBptId()));
csp.setBptId(null);
}
csp.setExpired(true);
csp = cspRepository.save(csp);
log.info(String.format("CSP Expired: idcode %s", csp.getIdentityCode()));
}
}
}
}

View File

@ -0,0 +1,89 @@
package org.codedream.epaper.component.task;
import lombok.extern.slf4j.Slf4j;
import org.codedream.epaper.component.datamanager.PdfParser;
import org.codedream.epaper.component.datamanager.TextParser;
import org.codedream.epaper.component.datamanager.WordParser;
import org.codedream.epaper.exception.innerservererror.HandlingErrorsException;
import org.codedream.epaper.exception.innerservererror.InnerDataTransmissionException;
import org.codedream.epaper.model.article.Article;
import org.codedream.epaper.model.file.File;
import org.codedream.epaper.model.task.Task;
import org.codedream.epaper.repository.article.ArticleRepository;
import org.codedream.epaper.repository.task.TaskRepository;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Optional;
/**
* 用于对文章的预处理
* 包括对word文档的解析{@link WordParser}
*/
@Slf4j
@Component
public class ArticlePreprocessor {
@Resource
private TaskRepository taskRepository;
@Resource
private ArticleRepository articleRepository;
@Resource
private WordParser wordParser;
@Resource
private TextParser textParser;
@Resource
private PdfParser pdfParser;
/**
* 预处理任务中存储的文章并将其持久化
*
* @param taskId 任务id
*/
public void parse(Integer taskId) {
// 查找子任务
Optional<Task> taskOptional = taskRepository.findById(taskId);
if (!taskOptional.isPresent()) throw new InnerDataTransmissionException(taskId.toString());
Task task = taskOptional.get();
File file = task.getFile();
if (file == null) throw new InnerDataTransmissionException();
task.setFile(file);
Integer articleId = null;
// 章分段处理
switch (file.getType()) {
case "doc":
case "docx":
articleId = wordParser.parse(file.getId());
break;
case "plain":
articleId = textParser.parse(file.getId());
break;
case "pdf":
articleId = pdfParser.parse(file.getId());
break;
default:
throw new HandlingErrorsException(file.getType());
}
if(articleId == null) throw new HandlingErrorsException(file.getId().toString());
Optional<Article> optionalArticle = articleRepository.findById(articleId);
if (!optionalArticle.isPresent()) throw new InnerDataTransmissionException();
task.setArticle(optionalArticle.get());
task.setProgressRate(task.getProgressRate() + 1);
log.info(String.format("Article preprocess finished, task progress for now is: %d", task.getProgressRate()));
taskRepository.save(task);
}
}

View File

@ -0,0 +1,63 @@
package org.codedream.epaper.component.task;
import org.codedream.epaper.component.article.GetSentenceFromArticle;
import org.codedream.epaper.model.article.Sentence;
import org.codedream.epaper.model.task.BatchProcessingTask;
import org.codedream.epaper.model.task.Task;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* 把一个超过limit的BPT按照task数量等分
*/
@Component
public class BPTDivider {
@Resource
private GetSentenceFromArticle getSentenceFromArticle;
/**
* 将一个批处理任务等分
* <p>
* 此方法会将原有的已经持久化的批处理任务从数据库中删除并换成两个
* 已经等分了的批处理任务
*
* @param bpt 一个批处理任务
* @return 两个封装在列表中的批处理任务
*/
public List<BatchProcessingTask> divideBPT(BatchProcessingTask bpt) {
List<BatchProcessingTask> batchProcessingTasks = new ArrayList<>();
BatchProcessingTask bpt1 = new BatchProcessingTask(bpt);
BatchProcessingTask bpt2 = new BatchProcessingTask(bpt);
List<Task> tasks = bpt.getTasks();
List<Sentence> sentenceList = new ArrayList<>();
bpt1.setTasks(tasks.subList(0, tasks.size() / 2));
bpt2.setTasks(tasks.subList(tasks.size() / 2, tasks.size()));
Integer sentenceNum = 0;
divide(batchProcessingTasks, bpt1, sentenceList, sentenceNum);
sentenceList.clear();
sentenceNum = 0;
divide(batchProcessingTasks, bpt2, sentenceList, sentenceNum);
return batchProcessingTasks;
}
private void divide(List<BatchProcessingTask> batchProcessingTasks, BatchProcessingTask bpt, List<Sentence> sentenceList, Integer sentenceNum) {
for (int i = 0; i < bpt.getTasks().size(); i++) {
Task task = bpt.getTasks().get(i);
sentenceList.addAll(getSentenceFromArticle.get(task.getArticle()));
sentenceNum += task.getArticle().getSentencesNumber();
}
bpt.setSentences(sentenceList);
bpt.setSentencesNumber(sentenceNum);
bpt.setPriority(sentenceNum);
bpt.setCreateDate(new Date());
batchProcessingTasks.add(bpt);
}
}

View File

@ -0,0 +1,83 @@
package org.codedream.epaper.component.task;
import lombok.extern.slf4j.Slf4j;
import org.codedream.epaper.component.auth.TimestampExpiredChecker;
import org.codedream.epaper.configure.AppConfigure;
import org.codedream.epaper.exception.innerservererror.InnerDataTransmissionException;
import org.codedream.epaper.model.task.BatchProcessingTask;
import org.codedream.epaper.repository.task.BatchProcessingTaskRepository;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Iterator;
import java.util.Optional;
/**
* 用于提供对批处理任务的监控方法
* <p>
* 批处理任务在处理时可能会出现分配的GPU服务端突发故障网络通信故障等问题从而造成
* 当前批处理任务在结果等待队列中的无限制等待为了避免这种问题的发生我们设计了一个
* 批处理任务监听器用于监控其计算等待时间超时后会放入原就绪队列进行重新分配
*/
@Slf4j
@Component
public class BPTMonitor {
@Resource
private LockedBPTs lockedBPTs;
@Resource
private BPTQueue bptQueue;
@Resource
private BatchProcessingTaskRepository bptRepository;
@Resource
private TimestampExpiredChecker timestampExpiredChecker;
@Resource
private AppConfigure configure;
private boolean initStatus = true;
/**
* 一个定时任务每15秒检测一次批处理任务的等待情况
*/
@Scheduled(cron = "0/5 * * ? * *")
public void monitorBPTs() {
// 启动自检
if (initStatus) {
// 查找未完成的批处理任务
Iterable<BatchProcessingTask> batchProcessingTasks = bptRepository.findAllByFinished(false);
for (BatchProcessingTask bpt : batchProcessingTasks) {
bptQueue.addBPT(bpt.getId());
}
initStatus = false;
}
log.info("BPT Monitor Started");
log.info(String.format("Lined BPTs Number For Now: %s", bptQueue.size()));
log.info(String.format("Locked BPTs Number For Now: %s", lockedBPTs.size()));
if (lockedBPTs.isEmpty()) {
return;
}
Iterator<Integer> bptIterator = lockedBPTs.iterator();
while (bptIterator.hasNext()) {
Integer bptId = bptIterator.next();
Optional<BatchProcessingTask> oBpt = bptRepository.findById(bptId);
if (!oBpt.isPresent()) throw new InnerDataTransmissionException();
BatchProcessingTask bpt = oBpt.get();
if (timestampExpiredChecker.checkDateBeforeDeterminedTime(
bpt.getJoinDate(), configure.gerMaxBPTProcessDelayTime())) {
bptQueue.addBPT(bpt.getId());
bptIterator.remove();
}
}
}
}

View File

@ -0,0 +1,59 @@
package org.codedream.epaper.component.task;
import org.codedream.epaper.model.task.BatchProcessingTask;
import org.codedream.epaper.repository.task.BatchProcessingTaskRepository;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Comparator;
import java.util.concurrent.PriorityBlockingQueue;
/**
* 批处理任务优先级队列
* <p>
* 根据批处理任务待处理的句子数量设置优先级
* 参见{@link org.codedream.epaper.model.task.BatchProcessingTask#compareTo(BatchProcessingTask)}
*/
@Component
public class BPTQueue {
@Resource
private BatchProcessingTaskRepository bptRepository;
// 优先阻塞队列
private PriorityBlockingQueue<Integer> batchProcessingTasks = new PriorityBlockingQueue<>(1,
new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
BatchProcessingTask bpt1 = bptRepository.findById(o1).get();
BatchProcessingTask bpt2 = bptRepository.findById(o2).get();
return bpt1.getPriority() - bpt2.getPriority();
}
});
// 添加批处理任务
public void addBPT(Integer bptId) {
batchProcessingTasks.offer(bptId);
}
//移除批处理任务
public void removeBPT(Integer bptId) {
batchProcessingTasks.remove(bptId);
}
// 检查队列是否为空
public boolean checkEmpty(){
return batchProcessingTasks.isEmpty();
}
// 从队列中获得一个批处理任务
public Integer getBptId() throws InterruptedException {
return batchProcessingTasks.take();
}
public Integer size(){
return batchProcessingTasks.size();
}
}

View File

@ -0,0 +1,180 @@
package org.codedream.epaper.component.task;
import lombok.extern.slf4j.Slf4j;
import org.codedream.epaper.component.json.model.JsonableSTNError;
import org.codedream.epaper.component.json.model.JsonableSTNResult;
import org.codedream.epaper.component.json.model.JsonableTaskResult;
import org.codedream.epaper.exception.notfound.NotFoundException;
import org.codedream.epaper.model.task.CorrectionResult;
import org.codedream.epaper.model.task.SentenceResult;
import org.codedream.epaper.model.task.TaskResult;
import org.codedream.epaper.repository.task.TaskResultRepository;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
/**
* 提供用于返回前端的结果构造方法
*
* 原生的结果数据比较混杂不适合前端后端JSON格式的数据通信设立此类以整合结果
*/
@Slf4j
@Component
public class JsonableTaskResultGenerator {
@Resource
TaskResultRepository taskResultRepository;
/**
* 根据任务结果的原生数据构造用于返回前端的任务结果
* @param taskId 任务id
* @return 一个JsonableTaskResult对象包含构建好的任务结果
* @see JsonableTaskResult
*/
public JsonableTaskResult getJsonableTaskResult(Integer taskId) {
log.info(String.format("Generating jsonable result of task %d", taskId));
Optional<TaskResult> OTaskResult = taskResultRepository.findByTaskId(taskId);
double dnnScore = 0;
double correctScore = 0;
double emotionScore = 0;
double e = Math.E;
int dnnCnt = 0;
int wrongCnt = 0;
int oralCnt = 0;
if (OTaskResult.isPresent()) {
TaskResult taskResult = OTaskResult.get();
JsonableTaskResult jsonableTaskResult = new JsonableTaskResult();
List<JsonableSTNResult> jsonableSTNResults = new ArrayList<>();
jsonableTaskResult.setTaskId(taskResult.getTaskId());
taskResult.setSuccess(true);
taskResult = taskResultRepository.save(taskResult);
jsonableTaskResult.setSuccess(true);
Map<Integer, SentenceResult> sentenceResultMap = taskResult.getSentenceResultMap();
for (SentenceResult result : taskResult.getSentenceResultMap().values()) {
JsonableSTNResult jsonableSTNResult = new JsonableSTNResult();
jsonableSTNResult.setStnId(result.getSentenceId());
jsonableSTNResult.setAppear(0);
SentenceResult sentenceResult = sentenceResultMap.get(result.getSentenceId());
// 中立值设置
jsonableSTNResult.setNeutral(sentenceResult.isNeutral());
List<JsonableSTNError> jsonableSTNErrorList = new ArrayList<>();
List<CorrectionResult> correctionResultList = result.getCorrectionResults();
correctScore += correctionResultList.size();
log.info("Generating correction result……");
log.info(String.format("Correction result of sentence %d: %s", sentenceResult.getSentenceId(),
correctionResultList.toString()));
// 寻找该sentence被修改了的位置
for (CorrectionResult correctionResult : correctionResultList) {
JsonableSTNError jsonableSTNError = new JsonableSTNError();
jsonableSTNError.setWordIdx(correctionResult.getStartPos());
jsonableSTNError.setWordLen(correctionResult.getLength());
jsonableSTNError.setType(1);
wrongCnt++;
if (correctionResult.getCorrectionText().isEmpty()) {
jsonableSTNError.setContent("文本存在错误,建议删除");
} else {
jsonableSTNError.setContent(String.format("文本存在错误,建议修改为:%s",
correctionResult.getCorrectionText()));
}
jsonableSTNErrorList.add(jsonableSTNError);
}
// 判断句子情感倾向
String content;
JsonableSTNError stnError = new JsonableSTNError();
if (sentenceResult.isNegative() || sentenceResult.isPositive()) {
stnError.setType(2);
stnError.setWordIdx(0);
stnError.setWordLen(0x7fffffff);
if (sentenceResult.isNegative()) {
taskResult.getNegativeEmotionsCount().incrementAndGet();
float possibility = sentenceResult.getPossibilities().get(0);
emotionScore += Math.pow(e, possibility);
if (possibility < 0.9) {
content = "文本语言较强烈口语化特征,建议修改为书面语。";
} else {
content = "文本语言极为强烈的口语化特征,建议修改为书面语。";
}
} else {
taskResult.getPositiveEmotionsCount().incrementAndGet();
float possibility = sentenceResult.getPossibilities().get(1);
emotionScore += Math.pow(2 * e, possibility);
if (possibility < 0.99) {
content = "文本语言较强烈的口语化特征,建议修改为书面语。";
} else {
content = "文本语言有极为强烈的口语化特征,建议修改为书面语。";
}
}
oralCnt++;
stnError.setContent(content);
jsonableSTNErrorList.add(stnError);
} else if (sentenceResult.getPossibilities().get(1) < 0.99) {
stnError.setType(2);
stnError.setWordIdx(0);
stnError.setWordLen(0x7fffffff);
content = "文本疑似存在口语化问题,请注意审查";
stnError.setContent(content);
jsonableSTNErrorList.add(stnError);
oralCnt++;
}
if (sentenceResult.getDnn() > 4000) {
dnnScore += sentenceResult.getDnn();
taskResult.getBrokenSentencesCount().incrementAndGet();
JsonableSTNError jsonableSTNError = new JsonableSTNError();
jsonableSTNError.setType(3);
jsonableSTNError.setContent("句子通顺度存在问题,建议修改");
jsonableSTNError.setWordIdx(0);
jsonableSTNError.setWordLen(0x7fffffff);
jsonableSTNErrorList.add(jsonableSTNError);
dnnCnt++;
} else if (sentenceResult.getDnn() > 2000) {
dnnScore += sentenceResult.getDnn();
dnnCnt++;
}
jsonableSTNResult.setErrorList(jsonableSTNErrorList);
jsonableSTNResults.add(jsonableSTNResult);
}
taskResultRepository.save(taskResult);
jsonableTaskResult.setWrongTextCount(wrongCnt);
jsonableTaskResult.setBrokenSentencesCount(dnnCnt);
jsonableTaskResult.setOralCount(oralCnt);
jsonableTaskResult.setStnResults(jsonableSTNResults);
wrongCnt += 4;
correctScore = Math.PI * Math.log(Math.atan(1.1 * wrongCnt - 10) + 2) / Math.log(e) + 10;
correctScore = correctScore * 6.3489125794718040652210611515526 + 12.305831203372289317158476382609;
dnnCnt += 1;
dnnScore = Math.PI * Math.log(-Math.atan(2 * dnnCnt - 20) + 2) / Math.log(e) + 10;
dnnScore = dnnScore * 5.8066270155791913101469156905197 + 19.001197895698319575318511547571;
oralCnt += 1;
emotionScore = Math.PI * Math.log(-Math.atan(0.9 * oralCnt - 12) + 2) / Math.log(e) + 10;
emotionScore = emotionScore * 6.4684248579558173553422908337857 + 9.969074019579274789063012724827;
jsonableTaskResult.setDnnScore(dnnScore);
jsonableTaskResult.setEmotionScore(emotionScore);
jsonableTaskResult.setCorrectionScore(correctScore);
dnnScore *= 0.34482758620689655172413793103448;
correctScore *= 0.17241379310344827586206896551724;
emotionScore *= 0.48275862068965517241379310344828;
jsonableTaskResult.setScore(dnnScore + emotionScore + correctScore);
return jsonableTaskResult;
} else {
throw new NotFoundException("This task has no result yet.");
}
}
}

View File

@ -0,0 +1,35 @@
package org.codedream.epaper.component.task;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* 用于提供{@link org.codedream.epaper.model.task.BatchProcessingTask}的计算等待队列以及相关操作
*/
@Component
public class LockedBPTs {
private List<Integer> bptIdList = new ArrayList<>();
public void add(Integer bptId){
bptIdList.add(bptId);
}
public Iterator<Integer> iterator(){
return bptIdList.iterator();
}
public boolean isEmpty(){
return bptIdList.isEmpty();
}
public boolean contains(Integer integer){
return bptIdList.contains(integer);
}
public Integer size(){
return bptIdList.size();
}
}

View File

@ -0,0 +1,116 @@
package org.codedream.epaper.component.task;
import lombok.extern.slf4j.Slf4j;
import org.codedream.epaper.component.datamanager.ParagraphDivider;
import org.codedream.epaper.component.datamanager.SHA512Encoder;
import org.codedream.epaper.exception.innerservererror.InnerDataTransmissionException;
import org.codedream.epaper.model.article.Article;
import org.codedream.epaper.model.article.Paragraph;
import org.codedream.epaper.model.article.Sentence;
import org.codedream.epaper.model.task.Task;
import org.codedream.epaper.repository.article.ArticleRepository;
import org.codedream.epaper.repository.article.ParagraphRepository;
import org.codedream.epaper.repository.article.SentenceRepository;
import org.codedream.epaper.repository.task.TaskRepository;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.*;
/**
* 提供对段落的预处理方法
*/
@Slf4j
@Component
public class ParagraphProcessor {
@Resource
private TaskRepository taskRepository;
@Resource
private ParagraphRepository paragraphRepository;
@Resource
private ParagraphDivider paragraphDivider;
@Resource
private SentenceRepository sentenceRepository;
@Resource
private ArticleRepository articleRepository;
@Resource
private SHA512Encoder encoder;
/**
* 对段落进行分句处理并进行SHA512编码以便缓存识别
*
* @param taskId 任务id用于获取待处理的段落
* @see ParagraphDivider
* @see SHA512Encoder
*/
public void parse(Integer taskId) {
// 查找子任务
Optional<Task> taskOptional = taskRepository.findById(taskId);
if (!taskOptional.isPresent()) throw new InnerDataTransmissionException(taskId.toString());
Article article = taskOptional.get().getArticle();
if (article == null) throw new InnerDataTransmissionException(taskId.toString());
int stnNum = 0;
// 段分句及段结构更新
for (Paragraph paragraph : article.getParagraphs()) {
// 跳过预处理过的段落
if (paragraph.isPreprocess()) continue;
// 段分句处理
List<String> sentenceTexts = paragraphDivider.divideParagraph(paragraph.getText());
List<Sentence> sentences = new ArrayList<>();
for (String text : sentenceTexts) {
Sentence sentence;
String hash = encoder.encode(text);
// 查找句数据库缓存
Optional<Sentence> sentenceOptional = sentenceRepository.findBySha512Hash(hash);
if (!sentenceOptional.isPresent()) {
// 创建新的句
sentence = new Sentence();
sentence.setText(text);
sentence.setSha512Hash(hash);
sentence = sentenceRepository.save(sentence);
} else {
sentence = sentenceOptional.get();
}
sentences.add(sentence);
}
stnNum += sentences.size();
// 设置句集合
Set<Sentence> sentenceSet = new HashSet<>(sentences);
paragraph.setSentences(sentenceSet);
// 设置句列表
for (Sentence sentence : sentences) {
paragraph.getSentenceList().add(sentence.getId());
}
// 设置预处理状态
paragraph.setPreprocess(true);
// 更新段落信息
paragraphRepository.save(paragraph);
}
// 更新文章总句数
taskOptional.get().getArticle().setSentencesNumber(stnNum);
taskOptional.get().setProgressRate(taskOptional.get().getProgressRate() + 1);
log.info(String.format("Paragraph preprocess finished, task progress for now is: %d",
taskOptional.get().getProgressRate()));
articleRepository.save(taskOptional.get().getArticle());
taskRepository.save(taskOptional.get());
}
}

View File

@ -0,0 +1,62 @@
package org.codedream.epaper.component.task;
import org.codedream.epaper.component.datamanager.SentenceSmoothnessGetter;
import org.codedream.epaper.component.datamanager.TextCorrector;
import org.codedream.epaper.model.article.Sentence;
import org.codedream.epaper.model.task.CorrectionResult;
import org.codedream.epaper.model.task.SentenceResult;
import org.codedream.epaper.repository.task.SentenceResultRepository;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.List;
/**
* 句分析器提供句子分析相关方法以及持久化方法
*
* @see TextCorrector
* @see SentenceSmoothnessGetter
*/
@Component
public class SentenceAnalyser {
@Resource
private TextCorrector textCorrector;
@Resource
private SentenceSmoothnessGetter dnnGetter;
@Resource
private SentenceResultRepository sentenceResultRepository;
public SentenceResult analyse(Sentence sentence) {
// 创建与预填写句子与处理结果结构
SentenceResult sentenceResult = new SentenceResult();
sentenceResult.setSentenceId(sentence.getId());
sentenceResult.setDnn(dnnParse(sentence));
correct(sentenceResult, sentence);
// 储存句分析结果
return sentenceResultRepository.save(sentenceResult);
}
/**
* 获取句子的文本纠错结果
*
* @param sentence 句子
*/
private void correct(SentenceResult sentenceResult, Sentence sentence) {
List<CorrectionResult> correctionResultList = textCorrector.correctText(sentence.getText());
sentenceResult.setCorrectionResults(correctionResultList);
}
/**
* 获取DNN处理结果
*
* @param sentence 待处理句子
* @return DNN处理结果
* @see SentenceSmoothnessGetter#getSentenceSmoothness(String)
*/
private synchronized float dnnParse(Sentence sentence) {
return dnnGetter.getSentenceSmoothness(sentence.getText());
}
}

View File

@ -0,0 +1,78 @@
package org.codedream.epaper.component.task;
import lombok.extern.slf4j.Slf4j;
import org.codedream.epaper.component.article.GetSentenceFromArticle;
import org.codedream.epaper.component.datamanager.SentenceDivider;
import org.codedream.epaper.exception.innerservererror.InnerDataTransmissionException;
import org.codedream.epaper.model.article.Article;
import org.codedream.epaper.model.article.Phrase;
import org.codedream.epaper.model.article.Sentence;
import org.codedream.epaper.model.task.Task;
import org.codedream.epaper.repository.article.SentenceRepository;
import org.codedream.epaper.repository.task.TaskRepository;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.List;
import java.util.Optional;
/**
* 用于提供句子预处理相关方法以及对应的持久化方法
*/
@Slf4j
@Component
public class SentencePreprocessor {
@Resource
private TaskRepository taskRepository;
@Resource
private SentenceDivider sentenceDivider;
@Resource
private SentenceRepository sentenceRepository;
@Resource
private GetSentenceFromArticle getSentenceFromArticle;
/**
* 取出任务中的句子并进行预处理
*
* @param taskId 任务id
* @see SentenceDivider#divideSentence(String)
* @see GetSentenceFromArticle#get(Article)
*/
public void parse(Integer taskId) {
// 查找子任务
Optional<Task> taskOptional = taskRepository.findById(taskId);
if (!taskOptional.isPresent()) throw new InnerDataTransmissionException(taskId.toString());
Task task = taskOptional.get();
Article article = task.getArticle();
if (article == null) throw new InnerDataTransmissionException(taskId.toString());
List<Sentence> sentences = getSentenceFromArticle.get(article);
for (Sentence sentence : sentences) {
// 跳过缓存句
if (sentence.isPreprocess()) continue;
List<Phrase> phrases = sentenceDivider.divideSentence(sentence.getText());
sentence.getPhrases().addAll(phrases);
sentence.setPreprocess(true);
for (Phrase phrase : phrases) {
sentence.getPhraseList().add(phrase.getId());
}
sentenceRepository.save(sentence);
}
task.setProgressRate(task.getProgressRate() + 1);
log.info(String.format("Sentence preprocess finished, task progress for now is: %d", task.getProgressRate()));
taskRepository.save(task);
}
}

View File

@ -0,0 +1,110 @@
package org.codedream.epaper.component.task;
import lombok.extern.slf4j.Slf4j;
import org.codedream.epaper.component.article.GetSentenceFromArticle;
import org.codedream.epaper.exception.innerservererror.InnerDataTransmissionException;
import org.codedream.epaper.model.article.Sentence;
import org.codedream.epaper.model.task.SentenceResult;
import org.codedream.epaper.model.task.Task;
import org.codedream.epaper.model.task.TaskResult;
import org.codedream.epaper.repository.task.SentenceResultRepository;
import org.codedream.epaper.repository.task.TaskRepository;
import org.codedream.epaper.repository.task.TaskResultRepository;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
/**
* 子任务预分析器用于精确到句子的任务分析以及持久化子任务结果
*
* @see SentenceAnalyser
*/
@Slf4j
@Component
public class TaskAnalyser {
@Resource
private TaskResultRepository taskResultRepository;
@Resource
private TaskRepository taskRepository;
@Resource
private GetSentenceFromArticle getSentenceFromArticle;
@Resource
private SentenceAnalyser sentenceAnalyser;
@Resource
private SentenceResultRepository sentenceResultRepository;
/**
* 分析一个task并持久化其结果
* 持久化结果的时候会先查找缓存
*
* @param taskId 任务id
* @return 一个TaskResult对象
*/
public TaskResult analyse(Integer taskId) {
log.info(String.format("Analysing task: %d", taskId));
// 查找子任务
Optional<Task> taskOptional = taskRepository.findById(taskId);
if (!taskOptional.isPresent()) throw new InnerDataTransmissionException(taskId.toString());
Task task = taskOptional.get();
// 构建预处理的任务结果存储结构
TaskResult taskResult = new TaskResult();
taskResult.setTaskId(task.getId());
Map<Integer, Float> dnnRes = new HashMap<>();
Map<Integer, SentenceResult> sentenceMap = new HashMap<>();
// 句分析
for(Sentence sentence : getSentenceFromArticle.get(task.getArticle())){
SentenceResult sentenceResult;
log.info(String.format("Analysing sentence: %d", sentence.getId()));
// 查找缓存
Optional<SentenceResult> sentenceResultOptional =
sentenceResultRepository.findBySentenceId(sentence.getId());
if (sentenceResultOptional.isPresent()) {
sentenceResult = sentenceResultOptional.get();
dnnRes.put(sentence.getId(), sentenceResult.getDnn());
sentenceMap.put(sentence.getId(), sentenceResult);
log.info(String.format("DNN result of sentence %d: %f", sentence.getId(), sentenceResult.getDnn()));
log.info(String.format("Correction result of sentence %d: %s", sentence.getId(),
sentenceResult.getCorrectionResults().toString()));
continue;
} else {
sentenceResult = sentenceAnalyser.analyse(sentence);
}
log.info(String.format("DNN result of sentence %d: %f", sentence.getId(), sentenceResult.getDnn()));
log.info(String.format("Correction result of sentence %d: %s", sentence.getId(),
sentenceResult.getCorrectionResults().toString()));
dnnRes.put(sentence.getId(), sentenceResult.getDnn());
sentenceMap.put(sentence.getId(), sentenceResult);
}
taskResult.setDnnMap(dnnRes);
taskResult.setSentenceResultMap(sentenceMap);
taskResult = taskResultRepository.save(taskResult);
// 更新任务
taskOptional.get().setProgressRate(taskOptional.get().getProgressRate()+1);
task.setResult(taskResult);
taskRepository.save(task);
log.info(String.format("Analyse preprocess finished, task progress for now is: %d", task.getProgressRate()));
log.info(String.format("Analysing result of task %d: %s", taskId, task.getResult()));
return taskResult;
}
}

View File

@ -0,0 +1,83 @@
package org.codedream.epaper.component.task;
import lombok.extern.slf4j.Slf4j;
import org.codedream.epaper.component.auth.TimestampExpiredChecker;
import org.codedream.epaper.configure.AppConfigure;
import org.codedream.epaper.model.task.BatchProcessingTask;
import org.codedream.epaper.model.task.Task;
import org.codedream.epaper.repository.task.BatchProcessingTaskRepository;
import org.codedream.epaper.service.TaskService;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
/**
* 子任务监视器
*/
@Slf4j
@Component
public class TaskMonitor {
@Resource
private TaskQueue taskQueue;
@Resource
private TaskService taskService;
@Resource
private TimestampExpiredChecker timestampExpiredChecker;
@Resource
private BatchProcessingTaskRepository bptRepository;
@Resource
private AppConfigure configure;
@Scheduled(cron = "0/3 * * ? * *")
public void monitorTasks() {
log.info("Tasks Monitor Started.");
log.info(String.format("Tasks Number For Now: %s", taskQueue.size()));
if (taskQueue.checkEmpty()) {
return;
}
List<Task> tasks = new ArrayList<>();
Integer sentenceNum = 0;
Iterator<Task> iterator = taskQueue.getIterator();
while (iterator.hasNext()) {
Task task = iterator.next();
if (null == task) {
log.error("Task null value.");
continue;
}
//超时MaxTaskDelayTime秒即添加到一个批处理队列中
if (timestampExpiredChecker.checkDateBeforeDeterminedTime(
task.getCreateDate(), configure.gerMaxTaskDelayTime())) {
tasks.add(task);
iterator.remove();
sentenceNum += task.getArticle().getSentencesNumber();
}
}
if (tasks.isEmpty()) return;
// 对等待超时的task立即新建一个bpt将之放入就绪队列等待调用
BatchProcessingTask bpt = new BatchProcessingTask();
bpt.setTasks(tasks);
bpt.setCreateDate(new Date());
bpt.setPriority(sentenceNum);
bpt.setSentencesNumber(sentenceNum);
bpt = bptRepository.save(bpt);
// 注册批处理任务
taskService.registerBPTTask(bpt);
}
}

View File

@ -0,0 +1,55 @@
package org.codedream.epaper.component.task;
import lombok.Data;
import org.codedream.epaper.model.task.Task;
import org.springframework.stereotype.Component;
import java.util.*;
/**
* 任务优先队列用于给BPT中的任务进行优先级排序
*/
@Data
@Component
public class TaskQueue {
Comparator<Task> comparator = Comparator.comparingInt(Task::getPriority);
private Queue<Task> tasks = new PriorityQueue<>(comparator);
private Integer sentenceNumber = 0;
public boolean addTask(Task task) {
if (this.tasks.offer(task)) {
sentenceNumber += task.getArticle().getSentencesNumber();
return true;
} else return false;
}
public Integer size(){
return tasks.size();
}
public void removeTask(Task task){
tasks.remove(task);
}
public Iterator<Task> getIterator(){
return tasks.iterator();
}
public List<Task> getTaskAsList() {
List<Task> taskList = new ArrayList<>(this.tasks);
return taskList;
}
public void clear() {
this.tasks.clear();
this.setSentenceNumber(0);
}
public boolean checkEmpty() {
return this.tasks.isEmpty();
}
}

View File

@ -0,0 +1,125 @@
package org.codedream.epaper.configure;
import org.springframework.stereotype.Component;
/**
* 应用程序常用配置信息
* 用于常见的应用程序本身的相关信息的引用
*/
@Component
public class AppConfigure {
/**
* 获得应用程序的中文名
* @return 返回包含完整内容的字符串
*/
public String getName() {
return "智慧学术论文行文指导服务端";
}
/**
* 获得应用程序的版本号
* @return 返回版本号内容的字符串
*/
public String getVersion() {
return "0.0.1_200204";
}
/**
* 获得应用程序的英文名
* @return 返回包含完整内容的字符串
*/
public String getEnglishName() {
return "ePaper";
}
/**
* 获得开发小组的名称
* @return 包含完整内容的字符串
*/
public String getOrganization() {
return "码梦工坊";
}
/**
* 文件服务储存路径
* @return 字符串
*/
public String getFilePath(){
return "./FILES/";
}
/**
* 上传的文件的大小限制字节
* 预设值16MB
* @return 数值
*/
public Integer getFileMaxSize(){
return 16000000;
}
/**
* 单段的最小字数阈值
* @return 数值
*/
public Integer getParagraphMinSize(){
return 16;
}
/**
* 单段的最大字数阈值
* @return 数值
*/
public Integer getParagraphMaxSize(){
return 2048;
}
/**
* 批处理任务最大句数目阈值
* @return 数值
*/
public Long getBPTMaxSentenceNumber() {
return 32768L;
}
/**
* 批处理任务最少句数目阈值
* @return 数值
*/
public Long getBPTMinSentenceNumber(){
return 1024L;
}
/**
* 子服务器失联等待时间
* @return 数值
*/
public Integer gerChildServerRegisterTimeout(){
return 300;
}
/**
* 子任务等待被加入批处理任务最长时间
* @return 数值
*/
public Integer gerMaxTaskDelayTime(){
return 30;
}
/**
* 批处理任务处理最长时间
* @return 数值
*/
public Integer gerMaxBPTProcessDelayTime(){
return 300;
}
/**
* 单页句数
* @return 数值
*/
public Integer getSentencePrePage(){
return 10;
}
}

View File

@ -0,0 +1,46 @@
package org.codedream.epaper.configure;
import lombok.Data;
import org.springframework.context.annotation.Configuration;
/**
* 批处理任务配置用于配置线程相关参数
*/
@Configuration
@Data
public class BatchTaskConfiguration {
//保留的线程池大小
private static int corePoolSize = 15;
//线程池最大大小
private static int maxPoolSize = 30;
//线程最大空闲时间
private static int keepAliveTime = 1000;
//阻塞队列大小
private static int workQueueSize = 200;
private static Long limit = 20000L;
public static int getCorePoolSize() {
return corePoolSize;
}
public static int getMaxPoolSize() {
return maxPoolSize;
}
public static int getKeepAliveTime() {
return keepAliveTime;
}
public static int getWorkQueueSize() {
return workQueueSize;
}
public static Long getLimit() {
return limit;
}
}

View File

@ -0,0 +1,9 @@
package org.codedream.epaper.configure;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ComponentsConfigure {
}

View File

@ -0,0 +1,162 @@
package org.codedream.epaper.configure;
import org.codedream.epaper.component.auth.*;
import org.codedream.epaper.service.EPUserDetailsService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.session.ConcurrentSessionControlAuthenticationStrategy;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import javax.annotation.Resource;
/**
* Spring Security 配置类
* 用于Spring Security相关参数的配置
*/
@Configuration
@EnableWebSecurity
public class CustomWebSecurityConfig extends WebSecurityConfigurerAdapter {
@Resource
EPUserDetailsService aseUserDetailService;
@Resource
EPPasswordEncoder EPPasswordEncoder;
@Resource
EPSecurityAuthenticationProvider EPSecurityAuthenticationProvider;
@Resource
EPAuthenticationSuccessHandler successHandler;
@Resource
EPAuthenticationFailureHandler failureHandler;
@Resource
EPAuthenticationEntryPoint authenticationEntryPoint;
@Resource
EPAccessDeniedHandler accessDeniedHandler;
/**
* HTTP服务器安全配置
* @param http HTTP服务器安全对象
* @throws Exception 异常
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.csrf().disable()
.logout().permitAll();
http.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint)
.accessDeniedHandler(accessDeniedHandler);
// 替换掉原有的UsernamePasswordAuthenticationFilter
http.addFilterAt(epUsernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(jsonTokenAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
/**
* 认证器设置
* @param auth 认证对象
* @throws Exception 异常
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(EPSecurityAuthenticationProvider)
.userDetailsService(aseUserDetailService)
.passwordEncoder(EPPasswordEncoder);
}
/**
* 安全配置
* @param web web对象
* @throws Exception 异常
*/
@Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers(
"/assets/**",
"/forget/**",
"/not_found/**",
"/error/**",
"/user/**",
"/swagger-ui.html",
"/webjars/**",
"/swagger-resources/**",
"/v2/api-docs",
"/configuration/ui",
"/configuration/security");
}
/**
* 注册自定义的UsernamePasswordAuthenticationFilter
* @return UsernamePasswordAuthenticationFilter
* @throws Exception 异常
*/
@Bean
EPJSONTokenAuthenticationFilter jsonTokenAuthenticationFilter() throws Exception {
return new EPJSONTokenAuthenticationFilter();
}
/**
* 注册自定义的UsernamePasswordAuthenticationFilter
* @return UsernamePasswordAuthenticationFilter
* @throws Exception 异常
*/
@Bean
EPUsernamePasswordAuthenticationFilter epUsernamePasswordAuthenticationFilter() throws Exception {
EPUsernamePasswordAuthenticationFilter filter = new EPUsernamePasswordAuthenticationFilter();
filter.setAuthenticationSuccessHandler(successHandler);
filter.setAuthenticationFailureHandler(failureHandler);
filter.setSessionAuthenticationStrategy(sessionAuthenticationStrategy(sessionRegistry()));
filter.setAllowSessionCreation(true);
filter.setRequiresAuthenticationRequestMatcher(
new AntPathRequestMatcher("/user/login", "POST"));
filter.setAuthenticationManager(authenticationManagerBean());
return filter;
}
/**
* 注册Session会话储存器
* @return SessionRegistry
*/
@Bean
public SessionRegistry sessionRegistry() {
return new SessionRegistryImpl();
}
/**
* 登记sessionAuthenticationStrategy
* @param sessionRegistry sessionRegistry
* @return SessionAuthenticationStrategy
*/
@Bean
public SessionAuthenticationStrategy sessionAuthenticationStrategy(SessionRegistry sessionRegistry){
return new ConcurrentSessionControlAuthenticationStrategy(sessionRegistry){{
setMaximumSessions(1);
}};
}
}

View File

@ -0,0 +1,24 @@
package org.codedream.epaper.configure;
import lombok.extern.slf4j.Slf4j;
import org.codedream.epaper.component.task.BPTQueue;
import org.codedream.epaper.model.task.BatchProcessingTask;
import org.codedream.epaper.repository.task.BatchProcessingTaskRepository;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Configuration;
import javax.annotation.Resource;
/**
* 服务端程序初始化检查
*/
@Slf4j
public class EPApplicationContextInitializer implements ApplicationContextInitializer {
@Override
public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
log.info("EPApplicationContextInitializer Started");
}
}

View File

@ -0,0 +1,39 @@
package org.codedream.epaper.configure;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
/**
* 线程池配置
*/
@Slf4j
@Configuration
public class EPExecutorConfigure {
@Bean(value = "PaPoolExecutor")
public Executor asyncServiceExecutor() {
ThreadPoolTaskExecutor executor =new ThreadPoolTaskExecutor();
// 核心线程数
executor.setCorePoolSize(4);
// 最大线程数
executor.setMaxPoolSize(8);
// 队列大小
executor.setQueueCapacity(100);
// 空闲线程等待工作的超时时间
executor.setKeepAliveSeconds(60);
// 线程池中的线程的名称前缀
executor.setThreadNamePrefix("pa-pool");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 线程池初始化
executor.initialize();
return executor;
}
}

View File

@ -0,0 +1,59 @@
package org.codedream.epaper.configure;
import com.google.common.collect.Sets;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Parameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* Swagger2配置类
*/
@Configuration
@EnableSwagger2
public class EPSwaggerConfigure {
@Bean
public Docket createRestApi() {
List<Parameter> pars = new ArrayList<Parameter>();
pars.add(new ParameterBuilder().name("openid").description("账号openid").hidden(true).order(1)
.modelRef(new ModelRef("string")).parameterType("header")
.required(false).defaultValue("24310760d8bb8b6542e5a3f16a0d67253214e01ee7ab0e96a1").build());
pars.add(new ParameterBuilder().name("signed").description("客户端签名").hidden(true).order(2)
.modelRef(new ModelRef("string")).parameterType("header")
.required(false).defaultValue("6d4923fca4dcb51f67b85e54a23a8d763d9e02af").build());
pars.add(new ParameterBuilder().name("timestamp").description("时间戳").hidden(true).order(3)
.modelRef(new ModelRef("string")).parameterType("header")
.required(false).defaultValue(Long.toString(new Date().getTime())).build());
return new Docket(DocumentationType.SWAGGER_2)
.protocols(Sets.newHashSet("http"))
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("org.codedream.epaper.controller"))
.paths(PathSelectors.any())
.build()
.globalOperationParameters(pars);
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("智慧学术论文行文指导服务端接口定义")
.version("0.0.1")
.description("用于对服务端接口进行说明")
.build();
}
}

View File

@ -0,0 +1,15 @@
package org.codedream.epaper.configure;
import org.springframework.context.annotation.Configuration;
/**
* Spring 框架全局配置类
* 主要用于注册或者管理Bean
*/
@Configuration
public class GlobalConfigure {
}

View File

@ -0,0 +1,26 @@
package org.codedream.epaper.configure;
import org.springframework.context.annotation.Configuration;
/**
* 百度API接口所需信息
*/
@Configuration
public class NLPConfigure {
private static final String APP_ID = "18006539";
private static final String APP_KEY = "5sdgAnjElUhfuzH1eHFDnWpz";
private static final String SECRET_KEY = "7DYgD3j0KEO3h0Lxrwq16QUWWShvsvKV";
public static String getAPPId() {
return APP_ID;
}
public static String getAppKey() {
return APP_KEY;
}
public static String getSecretKey() {
return SECRET_KEY;
}
}

View File

@ -0,0 +1,75 @@
package org.codedream.epaper.configure;
import org.springframework.context.annotation.Configuration;
import java.util.ArrayList;
import java.util.List;
/**
* 句末标点引用标点等配置
*/
@Configuration
public class PunctuationConfiguration {
private static final List<String> expPunctuations = new ArrayList<String>() {{
add("(");
add(")");
add("");
add("");
}};
private static final List<String> endOfSentencePunctuations = new ArrayList<String>() {{
add("");
add("");
add("");
}};
private static final List<String> warningQuotes = new ArrayList<String>() {{
add("\"");
add("\'");
}};
private static final List<String> frontPunctuations = new ArrayList<String>() {{
add("");
add("");
}};
private static final List<String> backPunctuations = new ArrayList<String>() {{
add("");
add("");
add(")");
add("");
}};
private static final List<String> quotesPunctuations = new ArrayList<String>() {{
add("");
add("");
add("");
add("");
}};
static public List<String> getExpPunctuations() {
return expPunctuations;
}
public static List<String> getEndOfSentencePunctuations() {
return endOfSentencePunctuations;
}
public static List<String> getWarningQuotes() {
return warningQuotes;
}
public static List<String> getFrontPunctuations() {
return frontPunctuations;
}
public static List<String> getBackPunctuations() {
return backPunctuations;
}
public static List<String> getQuotesPunctuations() {
return quotesPunctuations;
}
}

View File

@ -0,0 +1,32 @@
package org.codedream.epaper.configure;
import com.baidu.aip.nlp.AipNlp;
import org.codedream.epaper.configure.NLPConfigure;
import org.springframework.stereotype.Component;
/**
* 百度{@link AipNlp}的接口单例设计提供线程安全的单例接口调用
*/
@Component
public class SingletonAipNlp {
private static AipNlp aipNlp;
public SingletonAipNlp() {
}
/**
* 调用一个单例{@link AipNlp}
*
* @return 一个单例的AipNlp对象
*/
public static AipNlp getInstance() {
if (null == aipNlp) {
synchronized (AipNlp.class) {
if (null == aipNlp) {
aipNlp = new AipNlp(NLPConfigure.getAPPId(), NLPConfigure.getAppKey(), NLPConfigure.getSecretKey());
}
}
}
return aipNlp;
}
}

View File

@ -0,0 +1,116 @@
package org.codedream.epaper.controller;
import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j;
import org.codedream.epaper.component.api.QuickJSONRespond;
import org.codedream.epaper.component.json.respond.ErrorInfoJSONRespond;
import org.codedream.epaper.exception.badrequest.AlreadyExistException;
import org.codedream.epaper.exception.badrequest.IllegalException;
import org.codedream.epaper.exception.conflict.RelatedObjectsExistException;
import org.codedream.epaper.exception.innerservererror.FormatException;
import org.codedream.epaper.exception.innerservererror.HandlingErrorsException;
import org.codedream.epaper.exception.innerservererror.RuntimeIOException;
import org.codedream.epaper.exception.notfound.NotFoundException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import javax.annotation.Resource;
import java.util.Date;
/**
* 错误/异常管理机制控制
*/
@Slf4j
@RestControllerAdvice
@Api(hidden = true)
public class EPControllerAdvice {
@Resource
private QuickJSONRespond quickJSONRespond;
/**
* 非法请求类异常处理
* @param ex 异常
* @return 返回对象
*/
@ExceptionHandler(value = {
NullPointerException.class,
AlreadyExistException.class,
IllegalException.class
})
public ResponseEntity<Object> handleBadRequest(Exception ex) {
ex.printStackTrace();
return getResponse(HttpStatus.BAD_REQUEST, ex);
}
/**
* 未找到类异常处理
* @param ex 异常
* @return 返回对象
*/
@ExceptionHandler(value = {NotFoundException.class})
public ResponseEntity<Object> handleNotFound(Exception ex) {
return getResponse(HttpStatus.NOT_FOUND, ex);
}
/**
* 不可接受类异常处理
* @param ex 异常
* @return 返回对象
*/
@ExceptionHandler(value = {})
public ResponseEntity<Object> handleNotAcceptable(Exception ex) {
return getResponse(HttpStatus.NOT_ACCEPTABLE, ex);
}
/**
* 冲突类异常处理
* @param ex 异常
* @return 返回对象
*/
@ExceptionHandler(value = {RelatedObjectsExistException.class})
public ResponseEntity<Object> handleConflict(Exception ex) {
return getResponse(HttpStatus.CONFLICT, ex);
}
/**
* 内部错误类异常处理
* @param ex 异常
* @return 返回对象
*/
@ExceptionHandler(value = {
HandlingErrorsException.class,
FormatException.class,
RuntimeIOException.class})
public ResponseEntity<Object> handleInnerServerError(Exception ex){
return getResponse(HttpStatus.INTERNAL_SERVER_ERROR, ex);
}
/**
* 构造JSON填充的返回对象
* @param status HTTP状态码
* @param ex 异常
* @return 返回对象
*/
private ResponseEntity<Object> getResponse(HttpStatus status, Exception ex){
return ResponseEntity.status(status).body(getJSON(status, ex));
}
private String getJSON(HttpStatus status, Exception ex){
return quickJSONRespond.getJSONStandardRespond(status, getJSONRespondObject(ex));
}
private Object getJSONRespondObject(Exception ex){
ErrorInfoJSONRespond errorInfoJSONRespond = new ErrorInfoJSONRespond();
errorInfoJSONRespond.setException(ex.getClass().getName());
errorInfoJSONRespond.setExceptionMessage(ex.getMessage());
errorInfoJSONRespond.setDate(new Date());
return errorInfoJSONRespond;
}
}

View File

@ -0,0 +1,122 @@
package org.codedream.epaper.controller;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.hdgf.streams.StringsStream;
import org.codedream.epaper.component.json.model.JsonableFile;
import org.codedream.epaper.configure.AppConfigure;
import org.codedream.epaper.exception.badrequest.IllegalException;
import org.codedream.epaper.exception.innerservererror.HandlingErrorsException;
import org.codedream.epaper.exception.innerservererror.RuntimeIOException;
import org.codedream.epaper.exception.notfound.NotFoundException;
import org.codedream.epaper.model.file.File;
import org.codedream.epaper.service.FileService;
import org.codedream.epaper.service.IFileService;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.util.Optional;
@Slf4j
@RestController
@Api("文件服务类接口")
@RequestMapping("file")
public class FileController {
@Resource
private IFileService fileService;
@Resource
private AppConfigure configure;
@PostMapping("text")
@ResponseStatus(HttpStatus.CREATED)
@ApiOperation("文本上传接口")
public JsonableFile uploadText(@RequestBody String text) {
if(text == null || text.length() < 300) throw new IllegalArgumentException();
ByteArrayInputStream stream = new ByteArrayInputStream(text.getBytes());
Integer fileId = fileService.saveFile(text.substring(0, 12), "plain", stream);
JsonableFile jsonableFile = new JsonableFile();
jsonableFile.setFileId(fileId);
jsonableFile.setFilename(text.substring(0, 12));
jsonableFile.setType("plain");
return jsonableFile;
}
@PostMapping("")
@ResponseStatus(HttpStatus.CREATED)
@ApiOperation("文件上传接口")
public JsonableFile uploadFile(@RequestParam("file") MultipartFile file){
String filename = file.getOriginalFilename();
String[] strArray = filename.split("\\.");
int suffixIndex = strArray.length -1;
String fileType = strArray[suffixIndex];
log.info(String.format("File Upload filename %s", filename));
log.info(String.format("File Upload fileType %s", fileType));
// 检查文件大小
if(file.getSize() > configure.getFileMaxSize()) throw new IllegalException(Long.toString(file.getSize()));
if(fileType.equals("doc") || fileType.equals("docx") || fileType.equals("pdf")){
try {
byte[] fileData = file.getBytes();
ByteArrayInputStream stream = new ByteArrayInputStream(fileData);
Integer fileId = fileService.saveFile(filename, fileType, stream);
// 填写返回JSON
JsonableFile jsonableFile = new JsonableFile();
jsonableFile.setFileId(fileId);
jsonableFile.setFilename(filename);
jsonableFile.setType(fileType);
return jsonableFile;
} catch (IOException e){
throw new RuntimeIOException(filename);
}
}
else throw new IllegalException(fileType);
}
@GetMapping("download")
public void downloadFile(@RequestParam("fileId") Integer fileId, HttpServletResponse response){
File file = fileService.getFileInfo(fileId);
if(file == null) throw new NotFoundException(fileId.toString());
try {
response.setHeader("content-type", "application/octet-stream");
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(file.getName(), "UTF-8"));
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e.getMessage());
}
InputStream stream = fileService.getFile(fileId);
try{
OutputStream outputStream = response.getOutputStream();
int readBits;
byte[] rawBytes = new byte[1024];
while ((readBits = stream.read(rawBytes)) != -1) {
outputStream.write(rawBytes, 0, readBits);
}
outputStream.close();
stream.close();
} catch (Exception e){
throw new HandlingErrorsException(e.getMessage());
}
}
}

View File

@ -0,0 +1,202 @@
package org.codedream.epaper.controller;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.models.auth.In;
import lombok.extern.slf4j.Slf4j;
import org.codedream.epaper.component.json.model.JsonableBPT;
import org.codedream.epaper.component.json.model.JsonableBPTResult;
import org.codedream.epaper.component.json.model.JsonableCSP;
import org.codedream.epaper.component.json.model.JsonableSTN;
import org.codedream.epaper.configure.AppConfigure;
import org.codedream.epaper.exception.badrequest.AuthExpiredException;
import org.codedream.epaper.exception.badrequest.IllegalException;
import org.codedream.epaper.exception.innerservererror.HandlingErrorsException;
import org.codedream.epaper.exception.notfound.NotFoundException;
import org.codedream.epaper.model.article.Sentence;
import org.codedream.epaper.model.server.ChildServerPassport;
import org.codedream.epaper.model.task.BatchProcessingTask;
import org.codedream.epaper.model.user.User;
import org.codedream.epaper.repository.task.BatchProcessingTaskRepository;
import org.codedream.epaper.service.IChildServerService;
import org.codedream.epaper.service.INeuralNetworkModelService;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.*;
@Slf4j
@RestController
@Api("GPU服务器分布式计算接口")
@RequestMapping("cs")
public class GPUServerController {
@Resource
private IChildServerService childServerService;
@Resource
private INeuralNetworkModelService modelService;
@Resource
private BatchProcessingTaskRepository bptRepository;
@Resource
private AppConfigure configure;
@PostMapping("")
@ApiOperation("获得子服务器护照")
@ResponseStatus(HttpStatus.OK)
public JsonableCSP registerCSP(Authentication authentication){
User user = (User) authentication.getPrincipal();
if(!user.getUserAuth().getRole().equals("ChildServer"))
throw new IllegalException(authentication.getName());
return new JsonableCSP(childServerService.createCSP(user));
}
@PutMapping("")
@ApiOperation("更新子服务器签证")
public JsonableCSP updateCSP(@RequestParam(value = "idcode") String idcode){
if(!childServerService.checkCSP(idcode)) throw new IllegalException(idcode);
ChildServerPassport csp = childServerService.updateCSP(idcode);
// 签证过期
if(csp == null) {
JsonableCSP jsonableCSP = new JsonableCSP();
jsonableCSP.setExpired(true);
jsonableCSP.setIdentityCode(idcode);
return jsonableCSP;
}
return new JsonableCSP(csp);
}
@GetMapping("bpt")
@ApiOperation("获得一个合适的批处理任务")
@ResponseStatus(HttpStatus.OK)
public JsonableBPT getBPT(@RequestParam(value = "idcode") String idcode,
@RequestParam(value = "maxStnNum") String maxSTNNumberStr){
log.info(String.format("Get BPT Request From %s Max Sentence Number %s.", idcode, maxSTNNumberStr));
if(childServerService.checkCSPExpired(idcode)){
log.info(String.format("Found CSP %s Expired.", idcode));
throw new AuthExpiredException(idcode);
}
float maxSTNNumber = Float.parseFloat(maxSTNNumberStr);
if(configure.getBPTMinSentenceNumber() > maxSTNNumber){
log.info(String.format("Found maxSTNNumber %f is Too Few.", maxSTNNumber));
throw new IllegalException ("Too Few maxSTNNumber");
}
log.info("PreCheck Succeed.");
// 标记签证地点
ChildServerPassport csp = childServerService.getCSPInfo(idcode);
// 存在被该CSP锁住的BPT
if(csp.getBptId() != null){
log.info(String.format("Locked BPT Found %d.", csp.getBptId()));
Optional<BatchProcessingTask> bpt = bptRepository.findById(csp.getBptId());
if(!bpt.isPresent()) throw new IllegalException();
modelService.markBPTFailed(bpt.get());
csp.setBptId(null);
log.info(String.format("Marked Locked BPT Failed %d.", csp.getBptId()));
}
try {
log.info("Trying To Get New BPT...");
Optional<BatchProcessingTask> bpt = modelService.getBPTTaskAndLock((int) maxSTNNumber);
if (!bpt.isPresent()) {
log.info("Available BPT Not Found.");
return new JsonableBPT();
}
log.info(String.format("New BPT Got %d.", bpt.get().getId()));
List<Sentence> sentences = modelService.calculateSentenceList(bpt.get());
// 所有句已处理
if(sentences.size() == 0){
log.info("None Sentence Must Be Processing");
// 标记任务已完成
modelService.markBPTSuccess(bpt.get(), new ArrayList<>());
log.info("Marked BPT Successful.");
log.info("Available BPT Not Found.");
return new JsonableBPT();
}
log.info(String.format("Record BPT %d To CSP %s.", bpt.get().getId(), csp.getIdentityCode()));
csp.setBptId(bpt.get().getId());
// 更新数据库
childServerService.update(csp);
return modelService.getJsonableBPT(bpt.get(), sentences);
} catch(Exception e){
log.error(e.getMessage());
e.printStackTrace();
throw new HandlingErrorsException(e.getMessage());
}
}
@PutMapping("bpt")
@ApiOperation("更新BPT状态为已完成")
@ResponseStatus(HttpStatus.CREATED)
public void setBPTFinished(@RequestParam(value = "idcode") String idcode,
@RequestParam(value = "bptId") Integer bptId,
@RequestParam(value = "status") boolean status,
@RequestBody List<JsonableBPTResult> results){
log.info(String.format("Get BPT Result Upload Request From %s For BPT %d", idcode, bptId));
if(childServerService.checkCSPExpired(idcode)){
log.info(String.format("Found CSP %s Expired.", idcode));
throw new AuthExpiredException(idcode);
}
ChildServerPassport csp = childServerService.getCSPInfo(idcode);
if(csp.getBptId() == null || !csp.getBptId().equals(bptId)){
log.info(String.format("Found CSP Status %s Illegal.", idcode));
throw new IllegalException(bptId.toString());
}
log.info("PreCheck Succeed.");
Optional<BatchProcessingTask> bpt = bptRepository.findById(bptId);
if(!bpt.isPresent()){
log.info(String.format("Found BPT ID %d Illegal.", bptId));
throw new NotFoundException(bptId.toString());
}
if(status) {
log.info("Child Server Process Status Successful.");
modelService.markBPTSuccess(bpt.get(), results);
}
else{
log.info("Child Server Process Status Failed.");
modelService.markBPTFailed(bpt.get());
}
csp.setBptId(null);
// 更新数据库
childServerService.update(csp);
log.info(String.format("BPT %d Result Processing Succeed For CSP %s", bptId, idcode));
}
}

View File

@ -0,0 +1,46 @@
package org.codedream.epaper.controller;
import lombok.extern.slf4j.Slf4j;
import org.codedream.epaper.component.datamanager.ReportGenerator;
import org.codedream.epaper.exception.badrequest.IllegalException;
import org.codedream.epaper.exception.notfound.NotFoundException;
import org.codedream.epaper.model.file.File;
import org.codedream.epaper.model.task.Task;
import org.codedream.epaper.model.user.User;
import org.codedream.epaper.service.ITaskService;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.Optional;
@Slf4j
@RestController
@RequestMapping("report")
public class ReportController {
@Resource
private ITaskService taskService;
@Resource
private ReportGenerator reportGenerator;
@GetMapping("generate")
Integer generateReport(Authentication authentication, @RequestParam("taskId") Integer taskId){
User user = (User) authentication.getPrincipal();
Optional<Task> taskOptional = taskService.getTaskInfo(taskId);
if(!taskOptional.isPresent()) throw new NotFoundException(taskId.toString());
if (taskOptional.get().getUser().getId() != user.getId()) throw new IllegalException(taskId.toString());
log.info(String.format("Start Generate Report For TaskID %d ...", taskId));
Integer fileId = reportGenerator.saveByFileService(reportGenerator.generate(taskId));
log.info(String.format("Generate Report For TaskID %d Done.", taskId));
return fileId;
}
}

View File

@ -0,0 +1,249 @@
package org.codedream.epaper.controller;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.codedream.epaper.component.article.GetSentenceFromArticle;
import org.codedream.epaper.component.datamanager.DiffMatchPatch;
import org.codedream.epaper.component.json.model.JsonableSTN;
import org.codedream.epaper.component.json.model.JsonableSTNPage;
import org.codedream.epaper.component.json.model.JsonableTask;
import org.codedream.epaper.component.json.model.JsonableTaskResult;
import org.codedream.epaper.configure.AppConfigure;
import org.codedream.epaper.exception.badrequest.IllegalException;
import org.codedream.epaper.exception.innerservererror.InnerDataTransmissionException;
import org.codedream.epaper.exception.notfound.NotFoundException;
import org.codedream.epaper.model.article.Article;
import org.codedream.epaper.model.article.Paragraph;
import org.codedream.epaper.model.article.Sentence;
import org.codedream.epaper.model.file.File;
import org.codedream.epaper.model.task.Task;
import org.codedream.epaper.model.task.TaskResult;
import org.codedream.epaper.model.user.User;
import org.codedream.epaper.repository.task.TaskResultRepository;
import org.codedream.epaper.service.IFileService;
import org.codedream.epaper.service.ITaskService;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.*;
@RestController
@Api("任务类接口")
@RequestMapping("task")
public class TaskController {
@Resource
private ITaskService taskService;
@Resource
private IFileService fileService;
@Resource
private GetSentenceFromArticle getSentenceFromArticle;
@Resource
private AppConfigure configure;
@Resource
private TaskResultRepository taskResultRepository;
@PostMapping("")
@ApiOperation("创建任务")
@ResponseStatus(HttpStatus.CREATED)
public JsonableTask createTask(@RequestBody JsonableTask jsonableTask, Authentication authentication) {
// 获得当前认证用户的身份
User user = (User) authentication.getPrincipal();
// 文件序号检查
File file = fileService.getFileInfo(jsonableTask.getFileId());
if(file == null) throw new NotFoundException(jsonableTask.getFileId().toString());
// 注册子任务
Integer taskId = taskService.registerTask(user.getId(), file.getId(), "normal");
jsonableTask.setTaskId(taskId);
return jsonableTask;
}
@GetMapping("history")
@ApiOperation("查询用户历史记录")
@ResponseStatus(HttpStatus.OK)
public List<Integer> taskHistory(Authentication authentication){
User user = (User) authentication.getPrincipal();
Iterable<Task> tasks = taskService.findHistoryTaskId(user.getId());
List<Integer> taskIds = new ArrayList<>();
for(Task task : tasks){
taskIds.add(task.getId());
}
return taskIds;
}
@GetMapping("")
@ApiOperation("查询任务状态接口")
@ResponseStatus(HttpStatus.OK)
public JsonableTask checkTask(@RequestParam(value = "taskId") Integer taskId){
Optional<Task> task = taskService.getTaskInfo(taskId);
if(!task.isPresent()) throw new NotFoundException(taskId.toString());
JsonableTask jsonableTask = new JsonableTask(task.get());
if (task.get().isFinished()) {
Article article = task.get().getArticle();
Iterator<Paragraph> paragraphIterator = article.getParagraphs().iterator();
StringBuilder builder = new StringBuilder();
while (builder.length() < 30 && paragraphIterator.hasNext()) {
builder.append(paragraphIterator.next().getText());
}
String str = builder.toString().trim();
if(str.length() > 30) str = str.substring(0, 30);
jsonableTask.setDescription(str);
}
return jsonableTask;
}
@GetMapping("result")
@ApiOperation("获得任务结果")
@ResponseStatus(HttpStatus.OK)
public JsonableTaskResult getTaskResult(@RequestParam(value = "taskId") Integer taskId){
// 验证子任务Id
Optional<Task> task = taskService.getTaskInfo(taskId);
if (!task.isPresent()) throw new NotFoundException(taskId.toString());
if (!task.get().isFinished())
throw new NotFoundException(String.format("Task Not Finished : Task Id %d", taskId));
Optional<TaskResult> taskResult = taskService.getTaskResult(taskId);
if(!taskResult.isPresent()) throw new NotFoundException(String.format("Result Not Found : Task Id %d",taskId));
return taskService.getJsonableTaskResult(taskId);
}
@GetMapping("stnlist")
@ApiOperation("获取句子原文接口")
@ResponseStatus(HttpStatus.OK)
public JsonableSTNPage getSTNList(@RequestParam(value = "taskId") Integer taskId,
@RequestParam(value = "page") Integer page){
// 验证子任务Id
Optional<Task> task = taskService.getTaskInfo(taskId);
if (!task.isPresent()) throw new NotFoundException(taskId.toString());
if (!task.get().isFinished())
throw new NotFoundException(String.format("Task Not Finished : Task Id %d", taskId));
if (page == null) page = 1;
if(page < 1) throw new IllegalException();
Optional<TaskResult> taskResult = taskService.getTaskResult(taskId);
if(!taskResult.isPresent()) throw new InnerDataTransmissionException();
if(!taskResult.get().isSuccess()){
throw new IllegalException("Task Failed");
}
List<Sentence> sentences = getSentenceFromArticle.get(task.get().getArticle());
int stnPrePage = configure.getSentencePrePage();
// 求所有分页数
int pageAll = new Double(Math.ceil(sentences.size() / 10.0)).intValue();
if(page > pageAll) page = pageAll;
// 按照ID 排序
sentences.sort(Comparator.comparing(Sentence::getId));
List<Sentence> sentencePage = new ArrayList<>();
// 获取分页数据
for(int i = (page-1) * stnPrePage ; i < page * stnPrePage && i < sentences.size(); i++){
sentencePage.add(sentences.get(i));
}
List<JsonableSTN> jsonableSTNList = new ArrayList<>();
for(Sentence sentence : sentencePage){
JsonableSTN jsonableSTN = new JsonableSTN();
jsonableSTN.setStnId(sentence.getId());
jsonableSTN.setText(sentence.getText());
jsonableSTNList.add(jsonableSTN);
}
JsonableSTNPage stnPage = new JsonableSTNPage();
stnPage.setAll(pageAll);
stnPage.setPage(page);
stnPage.setStns(jsonableSTNList);
return stnPage;
}
@GetMapping("test")
@ResponseStatus(HttpStatus.OK)
public String test() {
DiffMatchPatch dmp = new DiffMatchPatch();
String text1 = "我们预备了缓存数据库功能,将论文种常见的行文错误进行特征编码后缓存。";
String text2 = "我们预备了缓存数据库功能,将论文中常见的行文错误进行特征编码后缓存。";
LinkedList<DiffMatchPatch.Diff> diff = dmp.diff_main(text1, text2);
List<Integer> show = new ArrayList<>();
System.out.println(diff.toString());
for (DiffMatchPatch.Diff diff1 : diff) {
DiffMatchPatch.Operation operation = diff1.operation;
System.out.println(operation.compareTo(DiffMatchPatch.Operation.INSERT));
show.add(operation.compareTo(DiffMatchPatch.Operation.INSERT));
}
return show.toString() + "\n" + diff.toString();
}
@GetMapping("test2")
@ResponseStatus(HttpStatus.OK)
public String test2(@RequestParam("str") String test) {
System.out.println("aha?");
List<TaskResult> taskResults = taskResultRepository.findAll();
/*for (TaskResult taskResult : taskResults) {
System.out.println(taskResult.getTaskId() + ": " + taskResult.getBrokenSentencesCount() +
" " + taskResult.getWrongTextCount() + " " +
taskResult.getPositiveEmotionsCount().addAndGet(taskResult.getNegativeEmotionsCount().intValue()));
}*/
StringTokenizer stringTokenizer = new StringTokenizer(test, ",");
int cnt = 0;
int[] args = new int[4];
while (stringTokenizer.hasMoreTokens()) {
args[cnt++] = Integer.parseInt(stringTokenizer.nextToken());
}
args[1] += 4;
double e = Math.E;
double res1 = Math.PI * Math.log(-Math.atan(1.3 * args[1] - 10) + 2) / Math.log(e) + 10;
res1 *= 6.3489125794718040652210611515526;
res1 += 12.305831203372289317158476382609;
//0: 13.949372309773254
//10: 9.643946813538529
// times: 5.8066270155791913101469156905197
/// tmp: 80.998802104301680424681488452429
// delta: 19.001197895698319575318511547571
args[0] += 1;
double res0 = Math.PI * Math.log(-Math.atan(2 * args[0] - 20) + 2) / Math.log(e) + 10;
res0 *= 5.8066270155791913101469156905197;
res0 += 19.001197895698319575318511547571;
//0 :13.918523899939487
//14:10.053595335568621
//times:6.4684248579558173553422908337857
// tmp:90.030925980420725210936987275173
// delta:9.969074019579274789063012724827
args[2] += 1;
double res2 = Math.PI * Math.log(-Math.atan(0.9 * args[2] - 12) + 2) / Math.log(e) + 10;
res2 *= 6.4684248579558173553422908337857;
res2 += 9.969074019579274789063012724827;
return res0 + " " + res1 + " " + res2;
}
}

View File

@ -0,0 +1,70 @@
package org.codedream.epaper.controller;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.codedream.epaper.component.json.model.JsonableUser;
import org.codedream.epaper.exception.badrequest.AlreadyExistException;
import org.codedream.epaper.exception.badrequest.IllegalException;
import org.codedream.epaper.exception.notfound.NotFoundException;
import org.codedream.epaper.model.user.User;
import org.codedream.epaper.service.IUserService;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import springfox.documentation.spring.web.json.Json;
import javax.annotation.Resource;
import java.util.Optional;
@RestController
@RequestMapping("user")
@Api("用户验证类接口")
public class UserController {
@Resource
private IUserService userService;
@PostMapping("")
@ResponseStatus(HttpStatus.CREATED)
@ApiOperation("用户注册接口")
public JsonableUser createUser(@RequestBody JsonableUser jsonableUser){
if(jsonableUser.getOpenid() == null) throw new IllegalAccessError("Null Value Openid");
if(userService.findUserByOpenid(jsonableUser.getOpenid()).isPresent())
throw new AlreadyExistException(jsonableUser.getOpenid());
User user = userService.getDefaultUser();
return new JsonableUser(userService.save(jsonableUser.parseObject(user)));
}
@PostMapping("cs")
@ResponseStatus(HttpStatus.CREATED)
@ApiOperation("子服务器注册接口")
public JsonableUser createChildServerUser(@RequestParam(value = "clientCode") String clientCode,
@RequestBody JsonableUser jsonableUser){
if(jsonableUser.getOpenid() == null) throw new IllegalAccessError("Null Value Openid");
if(userService.findUserByOpenid(jsonableUser.getOpenid()).isPresent())
throw new AlreadyExistException(jsonableUser.getOpenid());
if(!clientCode.equals("dc9fbb4f4f0b84fa903058991af60e73556494af8a02ef69fb6a93217729f04b"))
throw new IllegalException("Illegal Child Server");
User user = userService.getDefaultUser();
user.getUserAuth().setRole("ChildServer");
return new JsonableUser(userService.save(jsonableUser.parseObject(user)));
}
@GetMapping("")
@ResponseStatus(HttpStatus.OK)
@ApiOperation("检查用户是否存在接口")
public JsonableUser getUser(@RequestParam(value = "openid") String openid){
Optional<User> user = userService.findUserByOpenid(openid);
if(!user.isPresent())
throw new NotFoundException(openid);
JsonableUser jsonableUser = new JsonableUser();
jsonableUser.setId(user.get().getId());
jsonableUser.setOpenid(user.get().getUsername());
return jsonableUser;
}
}

View File

@ -0,0 +1,14 @@
package org.codedream.epaper.exception.badrequest;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
@EqualsAndHashCode(callSuper = true)
@Data
@NoArgsConstructor
public class AlreadyExistException extends RuntimeException {
public AlreadyExistException(String msg){
super(msg);
}
}

View File

@ -0,0 +1,15 @@
package org.codedream.epaper.exception.badrequest;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 认证信息过期
*/
@Data
@NoArgsConstructor
public class AuthExpiredException extends IllegalException {
public AuthExpiredException(String msg){
super(msg);
}
}

View File

@ -0,0 +1,17 @@
package org.codedream.epaper.exception.badrequest;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Data
public class BaseInformationAlreadyExistException extends AlreadyExistException {
private String className;
private String value;
public BaseInformationAlreadyExistException(Class<?> aClass, String value){
super(String.format("%s: %s", aClass.getName(), value));
this.className = aClass.getName();
this.value = value;
}
}

View File

@ -0,0 +1,17 @@
package org.codedream.epaper.exception.badrequest;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Data
public class BaseInformationIllegalException extends IllegalException {
String type;
String value;
public BaseInformationIllegalException(Class<?> aClass, String value){
super();
this.type = aClass.getName();
this.value = value;
}
}

View File

@ -0,0 +1,10 @@
package org.codedream.epaper.exception.badrequest;
import lombok.NoArgsConstructor;
@NoArgsConstructor
public class IllegalException extends RuntimeException {
public IllegalException(String msg){
super(msg);
}
}

Some files were not shown because too many files have changed in this diff Show More