init demo-restful
This commit is contained in:
parent
6d4d53054d
commit
295b022762
18
.gitignore
vendored
18
.gitignore
vendored
@ -1,8 +1,9 @@
|
||||
HELP.md
|
||||
target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
!**/src/main/**/target/
|
||||
!**/src/test/**/target/
|
||||
.gradle
|
||||
build/
|
||||
!gradle/wrapper/gradle-wrapper.jar
|
||||
!**/src/main/**/build/
|
||||
!**/src/test/**/build/
|
||||
|
||||
### STS ###
|
||||
.apt_generated
|
||||
@ -12,12 +13,18 @@ target/
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
bin/
|
||||
!**/src/main/**/bin/
|
||||
!**/src/test/**/bin/
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
out/
|
||||
!**/src/main/**/out/
|
||||
!**/src/test/**/out/
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
@ -25,9 +32,6 @@ target/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
build/
|
||||
!**/src/main/**/build/
|
||||
!**/src/test/**/build/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
|
||||
48
README.md
48
README.md
@ -1,34 +1,32 @@
|
||||
# Getting Started RestFul
|
||||
|
||||
### Reference Documentation
|
||||
|
||||
**项目名称:** 沐雪博客DDD版本
|
||||
For further reference, please consider the following sections:
|
||||
|
||||
**项目描述:**
|
||||
* [Official Gradle documentation](https://docs.gradle.org)
|
||||
* [Spring Boot Gradle Plugin Reference Guide](https://docs.spring.io/spring-boot/docs/2.4.5/gradle-plugin/reference/html/)
|
||||
* [Create an OCI image](https://docs.spring.io/spring-boot/docs/2.4.5/gradle-plugin/reference/html/#build-image)
|
||||
* [Rest Repositories](https://docs.spring.io/spring-boot/docs/2.4.5/reference/htmlsingle/#howto-use-exposing-spring-data-repositories-rest-endpoint)
|
||||
* [Spring Web](https://docs.spring.io/spring-boot/docs/2.4.5/reference/htmlsingle/#boot-features-developing-web-applications)
|
||||
* [Spring Data JPA](https://docs.spring.io/spring-boot/docs/2.4.5/reference/htmlsingle/#boot-features-jpa-and-spring-data)
|
||||
* [Spring Boot DevTools](https://docs.spring.io/spring-boot/docs/2.4.5/reference/htmlsingle/#using-boot-devtools)
|
||||
|
||||
主要用于博主分享学习经验,书写 技术文章,笔记以及日常生活的记录。
|
||||
### Guides
|
||||
|
||||
**涉及技术:**
|
||||
The following guides illustrate how to use some features concretely:
|
||||
|
||||
```java
|
||||
前端: Semantic-UI框架
|
||||
后端: JDK1.8+SpringBoot2.4.5
|
||||
```
|
||||
* [Accessing JPA Data with REST](https://spring.io/guides/gs/accessing-data-rest/)
|
||||
* [Accessing Neo4j Data with REST](https://spring.io/guides/gs/accessing-neo4j-data-rest/)
|
||||
* [Accessing MongoDB Data with REST](https://spring.io/guides/gs/accessing-mongodb-data-rest/)
|
||||
* [Building a RESTful Web Service](https://spring.io/guides/gs/rest-service/)
|
||||
* [Serving Web Content with Spring MVC](https://spring.io/guides/gs/serving-web-content/)
|
||||
* [Building REST services with Spring](https://spring.io/guides/tutorials/bookmarks/)
|
||||
* [Accessing Data with JPA](https://spring.io/guides/gs/accessing-data-jpa/)
|
||||
* [Accessing data with MySQL](https://spring.io/guides/gs/accessing-data-mysql/)
|
||||
|
||||
**项目介绍:**
|
||||
### See Links
|
||||
|
||||
整个项目前端采用 Semantic-UI,后端基于 SpringBoot,分为:
|
||||
* https://zyue.wiki/articles/2019/01/05/1546684795983.htmlhttps://scans.gradle.com#gradle)
|
||||
* https://github.com/lizhongyue248/spring-boot-restful-api
|
||||
|
||||
- 前台展示
|
||||
|
||||
首页、分类、标签、 归档、站长信息、搜索详情页、博客详情页
|
||||
|
||||
- 后台管理
|
||||
|
||||
登录、欢迎页、博客管理、新增/编辑博客、 分类管理、分类编辑/新增、标签管理
|
||||
|
||||
2、密码加密采用 MD5 进行加密,使得项目安全性有所提高。
|
||||
|
||||
3、博客编辑页面采用 Markdown 放行进行书写,集成了 Markdown 编辑器插件。期间使用 MarkdownUtil 工具将 Markdown 转化成 HTML 格式。
|
||||
|
||||
4、使用 CommentUtil 工具类以及 Semantic-UI 中自带的留言组件实现评论功能,让评论显示为层级
|
||||
|
||||
5、采用 SpringData JAP 连接数据库,简化 SQL 编写方式。
|
||||
40
build.gradle
Normal file
40
build.gradle
Normal file
@ -0,0 +1,40 @@
|
||||
plugins {
|
||||
id 'org.springframework.boot' version '2.4.5'
|
||||
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
|
||||
id 'java'
|
||||
}
|
||||
|
||||
group = 'com.example'
|
||||
version = '0.0.1-SNAPSHOT'
|
||||
sourceCompatibility = '1.8'
|
||||
|
||||
configurations {
|
||||
compileOnly {
|
||||
extendsFrom annotationProcessor
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
// 单个项目有效
|
||||
maven { url 'http://maven.aliyun.com/nexus/content/groups/public/'}
|
||||
maven { url 'http://maven.aliyun.com/nexus/content/repositories/jcenter'}
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
|
||||
//implementation 'org.springframework.boot:spring-boot-starter-data-rest'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-web'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-validation'
|
||||
// 显示完整sql
|
||||
implementation 'p6spy:p6spy:3.7.0'
|
||||
compileOnly 'org.projectlombok:lombok'
|
||||
developmentOnly 'org.springframework.boot:spring-boot-devtools'
|
||||
runtimeOnly 'mysql:mysql-connector-java'
|
||||
annotationProcessor 'org.projectlombok:lombok'
|
||||
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
||||
}
|
||||
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
5
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
5
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
185
gradlew
vendored
Normal file
185
gradlew
vendored
Normal file
@ -0,0 +1,185 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
#
|
||||
# Copyright 2015 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.
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
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
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
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
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=`expr $i + 1`
|
||||
done
|
||||
case $i in
|
||||
0) set -- ;;
|
||||
1) set -- "$args0" ;;
|
||||
2) set -- "$args0" "$args1" ;;
|
||||
3) set -- "$args0" "$args1" "$args2" ;;
|
||||
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=`save "$@"`
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
89
gradlew.bat
vendored
Normal file
89
gradlew.bat
vendored
Normal file
@ -0,0 +1,89 @@
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem 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, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
59
pom.xml
59
pom.xml
@ -1,59 +0,0 @@
|
||||
<?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.4.5</version>
|
||||
<relativePath/> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<groupId>cn.allms.community</groupId>
|
||||
<artifactId>mx-community</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<name>mx-community</name>
|
||||
<description>Demo project for Spring Boot</description>
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</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>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<excludes>
|
||||
<exclude>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
1
settings.gradle
Normal file
1
settings.gradle
Normal file
@ -0,0 +1 @@
|
||||
rootProject.name = 'demo-rest'
|
||||
@ -1,17 +0,0 @@
|
||||
package cn.allms.community;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
/**
|
||||
* 啓動入口
|
||||
* @author josxy
|
||||
*/
|
||||
@SpringBootApplication
|
||||
public class MxCommunityApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(MxCommunityApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,99 +0,0 @@
|
||||
package cn.allms.community.domain.model.post;
|
||||
|
||||
import cn.allms.community.domain.model.topic.TopicPost;
|
||||
import cn.allms.community.infrastructure.constant.CommonConstants;
|
||||
import cn.allms.community.infrastructure.constant.ExceptionCodeConstant;
|
||||
import cn.allms.community.infrastructure.exception.BusinessException;
|
||||
import lombok.Data;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 帖子对象
|
||||
* @author xieYj
|
||||
* @date 2021/5/11 11:50
|
||||
*/
|
||||
@Data
|
||||
public class Post {
|
||||
/**
|
||||
* 帖子ID
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 帖子作者ID
|
||||
*/
|
||||
private Long authorId;
|
||||
|
||||
/**
|
||||
* 帖子标题
|
||||
*/
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 帖子内容
|
||||
*/
|
||||
private String sourceContent;
|
||||
|
||||
/**
|
||||
* 发帖时间戳
|
||||
*/
|
||||
private Long postingTime;
|
||||
|
||||
/**
|
||||
* 最后修改时间
|
||||
*/
|
||||
private Long lastModifyTime;
|
||||
|
||||
/**
|
||||
* 加入的话题
|
||||
*/
|
||||
private Set<TopicPost> topics = new HashSet<>();
|
||||
|
||||
/**
|
||||
* 最多加入的主题数
|
||||
*/
|
||||
public static final Integer MAX_JOINED_TOPICS_NUM = 5;
|
||||
|
||||
/**
|
||||
* 自定义的setter,优先会调用此set方法,而不是lombok自带的
|
||||
* @param authorId 作者Id
|
||||
*/
|
||||
public void setAuthorId(Long authorId) {
|
||||
Assert.isTrue(authorId > 0, "帖子作者Id必须大于0");
|
||||
this.authorId = authorId;
|
||||
}
|
||||
|
||||
public void setSourceContent(String sourceContent) {
|
||||
Assert.isTrue(Objects.nonNull(sourceContent), "帖子内容不能为空");
|
||||
Assert.isTrue(sourceContent.length() >= 16, "帖子内容必须大于16");
|
||||
this.sourceContent = sourceContent;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将帖子关联话题 | 加入话题
|
||||
*
|
||||
* @param topicIds 话题集合
|
||||
*/
|
||||
public void joinTopics(String topicIds) throws BusinessException {
|
||||
if (Objects.isNull(topicIds)) {
|
||||
return;
|
||||
}
|
||||
String[] topicIdArray = topicIds.split(CommonConstants.TOPIC_SPLIT_COMMA);
|
||||
for (String s : topicIdArray) {
|
||||
TopicPost topicPost = new TopicPost(Long.valueOf(s), this.getId());
|
||||
this.topics.add(topicPost);
|
||||
if (topicSize() > MAX_JOINED_TOPICS_NUM) {
|
||||
throw new BusinessException(ExceptionCodeConstant.ONE_POST_MOST_JOIN_INTO_FIVE_TOPICS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int topicSize(){
|
||||
return this.topics.size();
|
||||
}
|
||||
}
|
||||
@ -1,52 +0,0 @@
|
||||
package cn.allms.community.domain.model.post;
|
||||
|
||||
import cn.allms.community.infrastructure.constant.ExceptionCodeConstant;
|
||||
import cn.allms.community.infrastructure.exception.BusinessException;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 帖子作者对象
|
||||
* @author xieYj
|
||||
* @date 2021/5/11 11:51
|
||||
*/
|
||||
@Data
|
||||
public class PostAuthor {
|
||||
/**
|
||||
* 帖子内容最小长度
|
||||
*/
|
||||
private final static int MIN_LENGTH_POST_SOURCE_CONTENT = 16;
|
||||
|
||||
/**
|
||||
* 作者ID
|
||||
*/
|
||||
private Long id;
|
||||
/**
|
||||
* 作者账号
|
||||
*/
|
||||
private String account;
|
||||
/**
|
||||
* 作者昵称
|
||||
*/
|
||||
private String nickname;
|
||||
|
||||
/**
|
||||
* 发布帖子
|
||||
*
|
||||
* @param title 帖子标题
|
||||
* @param sourceContent 帖子内容
|
||||
* @return
|
||||
*/
|
||||
public Post posting(String title, String sourceContent) throws BusinessException {
|
||||
long currentTimeMillis = System.currentTimeMillis();
|
||||
if (sourceContent.length() < MIN_LENGTH_POST_SOURCE_CONTENT) {
|
||||
throw new BusinessException(ExceptionCodeConstant.POST_SOURCE_CONTENT_AT_LEAST_SIXTEEN_WORDS);
|
||||
}
|
||||
Post post = new Post();
|
||||
post.setAuthorId(this.id);
|
||||
post.setSourceContent(sourceContent);
|
||||
post.setTitle(title);
|
||||
post.setPostingTime(currentTimeMillis);
|
||||
post.setLastModifyTime(currentTimeMillis);
|
||||
return post;
|
||||
}
|
||||
}
|
||||
@ -1,36 +0,0 @@
|
||||
package cn.allms.community.domain.model.topic;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 话题对象
|
||||
*
|
||||
* @author xieYj
|
||||
* @date 2021/5/11 14:31
|
||||
*/
|
||||
@Data
|
||||
public class Topic {
|
||||
/**
|
||||
* 话题id
|
||||
*/
|
||||
private Long id;
|
||||
/**
|
||||
* 话题标题
|
||||
*/
|
||||
private String title;
|
||||
/**
|
||||
* 话题描述
|
||||
*/
|
||||
private String description;
|
||||
/**
|
||||
* 话题创建时间
|
||||
*/
|
||||
private Timestamp createTime;
|
||||
/**
|
||||
* 话题下的帖子列表
|
||||
*/
|
||||
private Set<TopicPost> posts;
|
||||
}
|
||||
@ -1,17 +0,0 @@
|
||||
package cn.allms.community.domain.model.topic;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 话题文章对象
|
||||
*
|
||||
* @author xieYj
|
||||
* @date 2021/5/11 14:31
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class TopicPost {
|
||||
private Long topicId;
|
||||
private Long postId;
|
||||
}
|
||||
@ -1,25 +0,0 @@
|
||||
package cn.allms.community.domain.service.contentfilter;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* 内容过滤器
|
||||
* @author xieYj
|
||||
* @date 2021/5/11 15:09
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
public abstract class ContentFilter {
|
||||
/**
|
||||
* 下一个内容过滤器
|
||||
*/
|
||||
private ContentFilter nextContentFilter;
|
||||
|
||||
/**
|
||||
* 过滤内容
|
||||
* @param object 过滤对象
|
||||
* @return
|
||||
*/
|
||||
public abstract boolean filterContent(Object object);
|
||||
}
|
||||
@ -1,14 +0,0 @@
|
||||
package cn.allms.community.domain.service.contentfilter;
|
||||
|
||||
/**
|
||||
* 图片过滤器
|
||||
* @author xieYj
|
||||
* @date 2021/5/11 15:17
|
||||
*/
|
||||
public class ImageContentFilter extends ContentFilter{
|
||||
@Override
|
||||
public boolean filterContent(Object object) {
|
||||
// TODO 调用第三方接口实现图片过滤
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -1,35 +0,0 @@
|
||||
package cn.allms.community.domain.service.contentfilter;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 本地文本内容过滤器
|
||||
* @author xieYj
|
||||
* @date 2021/5/11 15:24
|
||||
*/
|
||||
public class LocalTextContentFilter extends TextContentFilter {
|
||||
/**
|
||||
* 本地敏感词
|
||||
*/
|
||||
private Set<String> sensitiveWords = new HashSet<>();
|
||||
|
||||
public LocalTextContentFilter() {
|
||||
sensitiveWords.add("NND");
|
||||
sensitiveWords.add("奶奶个熊");
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean filterContent(Object content) {
|
||||
Assert.isTrue(content instanceof String, "本地文本过滤对象必须为 String 类型");
|
||||
for (String sensitiveWord : sensitiveWords) {
|
||||
if (((String) content).contains(sensitiveWord)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -1,46 +0,0 @@
|
||||
package cn.allms.community.domain.service.contentfilter;
|
||||
|
||||
import cn.allms.community.domain.model.post.Post;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 帖子内容过滤:包含文本过滤,图片过滤
|
||||
* 责任链模式
|
||||
*
|
||||
* @author xieYj
|
||||
* @date 2021/5/11 15:33
|
||||
*/
|
||||
public class PostMainBodyContentFilterChain {
|
||||
/**
|
||||
* 内容过滤器
|
||||
*/
|
||||
private Set<ContentFilter> contentFilters;
|
||||
|
||||
public PostMainBodyContentFilterChain() {
|
||||
TextContentFilter localTextContentFilter = new LocalTextContentFilter();
|
||||
TextContentFilter remoteTextContentFilter = new RemoteTextContentFilter();
|
||||
ImageContentFilter imageContentFilter = new ImageContentFilter();
|
||||
// 优先校验本地的敏感词
|
||||
contentFilters.add(localTextContentFilter);
|
||||
// 第三方文本敏感词过滤
|
||||
contentFilters.add(remoteTextContentFilter);
|
||||
// 第三方图片过滤
|
||||
contentFilters.add(imageContentFilter);
|
||||
}
|
||||
|
||||
/**
|
||||
* 过滤标题
|
||||
*
|
||||
* @param post 帖子信息
|
||||
* @return true —— 通过, false —— 未通过
|
||||
*/
|
||||
public boolean filterMainBody(Post post) {
|
||||
for (ContentFilter contentFilter : contentFilters) {
|
||||
if (!contentFilter.filterContent(post.getSourceContent())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -1,44 +0,0 @@
|
||||
package cn.allms.community.domain.service.contentfilter;
|
||||
|
||||
import cn.allms.community.domain.model.post.Post;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 帖子标题过滤:文本过滤
|
||||
* 责任链模式
|
||||
*
|
||||
* @author xieYj
|
||||
* @date 2021/5/11 15:40
|
||||
*/
|
||||
public class PostTitleContentFilterChain {
|
||||
/**
|
||||
* 内容过滤器
|
||||
*/
|
||||
private Set<ContentFilter> contentFilters;
|
||||
|
||||
public PostTitleContentFilterChain() {
|
||||
LocalTextContentFilter localTextContentFilter = new LocalTextContentFilter();
|
||||
RemoteTextContentFilter remoteTextContentFilter = new RemoteTextContentFilter();
|
||||
// 本地文本敏感词校验
|
||||
contentFilters.add(localTextContentFilter);
|
||||
// 第三方文本敏感词校验
|
||||
contentFilters.add(remoteTextContentFilter);
|
||||
}
|
||||
|
||||
/**
|
||||
* 过滤标题
|
||||
*
|
||||
* @param post 帖子信息
|
||||
* @return true —— 通过,false —— 未通过
|
||||
*/
|
||||
public boolean filterTitle(Post post) {
|
||||
for (ContentFilter contentFilter : contentFilters) {
|
||||
if (!contentFilter.filterContent(post.getTitle())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -1,14 +0,0 @@
|
||||
package cn.allms.community.domain.service.contentfilter;
|
||||
|
||||
/**
|
||||
* 远程文本内容过滤器
|
||||
* @author xieYj
|
||||
* @date 2021/5/11 15:22
|
||||
*/
|
||||
public class RemoteTextContentFilter extends TextContentFilter{
|
||||
@Override
|
||||
public boolean filterContent(Object object) {
|
||||
// TODO 需要调用第三方服务才能实现
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -1,16 +0,0 @@
|
||||
package cn.allms.community.domain.service.contentfilter;
|
||||
|
||||
/**
|
||||
* 文本过滤器
|
||||
* @author xieYj
|
||||
* @date 2021/5/11 15:18
|
||||
*/
|
||||
public abstract class TextContentFilter extends ContentFilter{
|
||||
/**
|
||||
* 文本过滤器,抽象
|
||||
* @param object 过滤对象
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public abstract boolean filterContent(Object object);
|
||||
}
|
||||
@ -1,10 +0,0 @@
|
||||
package cn.allms.community.infrastructure.constant;
|
||||
|
||||
/**
|
||||
* 公共常量
|
||||
* @author xieYj
|
||||
* @date 2021/5/11 14:56
|
||||
*/
|
||||
public interface CommonConstants {
|
||||
String TOPIC_SPLIT_COMMA = ",";
|
||||
}
|
||||
@ -1,12 +0,0 @@
|
||||
package cn.allms.community.infrastructure.constant;
|
||||
|
||||
/**
|
||||
* 异常码状态常量
|
||||
*
|
||||
* @author xieYj
|
||||
* @date 2021/5/11 14:17
|
||||
*/
|
||||
public interface ExceptionCodeConstant {
|
||||
String POST_SOURCE_CONTENT_AT_LEAST_SIXTEEN_WORDS = "2001";
|
||||
String ONE_POST_MOST_JOIN_INTO_FIVE_TOPICS = "2002";
|
||||
}
|
||||
@ -1,84 +0,0 @@
|
||||
package cn.allms.community.infrastructure.exception;
|
||||
|
||||
/**
|
||||
* @author xieYj
|
||||
* @date 2021/5/11 13:59
|
||||
*/
|
||||
public class BusinessException extends Exception {
|
||||
/**
|
||||
* 业务异常码
|
||||
*/
|
||||
private String code;
|
||||
/**
|
||||
* 业务异常参数
|
||||
*/
|
||||
private Object[] arguments;
|
||||
|
||||
public BusinessException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public BusinessException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public BusinessException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public BusinessException(String code, String message) {
|
||||
super(message);
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public BusinessException(String message, Object[] arguments) {
|
||||
super(message);
|
||||
this.arguments = arguments;
|
||||
}
|
||||
|
||||
public BusinessException(String code, String message, Object[] arguments) {
|
||||
super(message);
|
||||
this.code = code;
|
||||
this.arguments = arguments;
|
||||
}
|
||||
|
||||
public BusinessException(String message, Object[] arguments, Throwable cause) {
|
||||
super(message, cause);
|
||||
this.arguments = arguments;
|
||||
}
|
||||
|
||||
public BusinessException(String code, String message, Object[] arguments, Throwable cause) {
|
||||
super(message, cause);
|
||||
this.code = code;
|
||||
this.arguments = arguments;
|
||||
}
|
||||
|
||||
public BusinessException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
|
||||
public BusinessException(String code, String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
|
||||
public BusinessException(String message,
|
||||
Throwable cause,
|
||||
boolean enableSuppression,
|
||||
boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
|
||||
|
||||
public BusinessException(String code,
|
||||
String message,
|
||||
Throwable cause,
|
||||
boolean enableSuppression,
|
||||
boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
}
|
||||
16
src/main/java/com/example/demorest/DemoRestApplication.java
Normal file
16
src/main/java/com/example/demorest/DemoRestApplication.java
Normal file
@ -0,0 +1,16 @@
|
||||
package com.example.demorest;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
/**
|
||||
* @author josxy
|
||||
*/
|
||||
@SpringBootApplication
|
||||
public class DemoRestApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(DemoRestApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,164 @@
|
||||
package com.example.demorest.controller;
|
||||
|
||||
import com.example.demorest.entity.Book;
|
||||
import com.example.demorest.exception.InvalidRequestException;
|
||||
import com.example.demorest.exception.ResourceNoFoundException;
|
||||
import com.example.demorest.repository.BookRepository;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.BeanWrapper;
|
||||
import org.springframework.beans.BeanWrapperImpl;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* rest 风格 api
|
||||
* <p>
|
||||
* GET /api/v1/books 所有书单
|
||||
* GET /api/v1/books/{id} 获取一条书单
|
||||
* POST /api/v1/books 新建一条书单
|
||||
* PUT /api/v1/books/{id} 更新一条书单,提供全部信息
|
||||
* PATCH /api/v1/books/{id} 更新一条书单,提供部分信息
|
||||
* DELETE /api/v1/books/{id} 删除一条书单
|
||||
* DELETE /API/v1/books 删除所有书单
|
||||
*
|
||||
* @author Echo
|
||||
* @version 1.0
|
||||
* @date 2019-01-05 21:59
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/v1")
|
||||
public class BookController {
|
||||
private final BookRepository bookRepository;
|
||||
|
||||
@Autowired
|
||||
public BookController(BookRepository bookRepository) {
|
||||
this.bookRepository = bookRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有书单
|
||||
* GET /api/v1/books 所有书单
|
||||
*
|
||||
* @return http 响应
|
||||
*/
|
||||
@GetMapping("/books")
|
||||
public HttpEntity<?> books() {
|
||||
return new ResponseEntity<>(bookRepository.findAll(), HttpStatus.OK);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取一个书单 * GET /api/v1/books/{id} 获取一条书单 * * @param id id
|
||||
*
|
||||
* @return http 响应
|
||||
*/
|
||||
@GetMapping("/books/{id}")
|
||||
public HttpEntity<?> booksOne(@PathVariable Long id) {
|
||||
return new ResponseEntity<>(bookRepository.findById(id).orElseThrow(() ->
|
||||
new ResourceNoFoundException(String.format("Book by id %s not found!", id))),
|
||||
HttpStatus.OK);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加一个书单
|
||||
* POST /api/v1/books 新建一条书单
|
||||
*
|
||||
* @param book 书单
|
||||
* @return http 响应
|
||||
*/
|
||||
@PostMapping("/books")
|
||||
public HttpEntity<?> booksAdd(@Valid @RequestBody Book book, BindingResult bindingResult) {
|
||||
if (bindingResult.hasErrors()) {
|
||||
throw new InvalidRequestException("Invalid parameter", bindingResult);
|
||||
}
|
||||
return new ResponseEntity<>(bookRepository.save(book), HttpStatus.CREATED);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新一个书单,提供一个书单的全部信息
|
||||
* PUT /api/v1/books/{id} 更新一条书单,提供全部信息
|
||||
*
|
||||
* @param id 更新的id
|
||||
* @param book 更新后的书单
|
||||
* @return http 响应
|
||||
*/
|
||||
@PutMapping("/books/{id}")
|
||||
public HttpEntity<?> booksPut(@Valid @PathVariable Long id, @RequestBody Book book, BindingResult bindingResult) {
|
||||
Book exist = bookRepository.findById(id).orElseThrow(() ->
|
||||
new ResourceNoFoundException(String.format("Book by id %s not found!", id)));
|
||||
if (bindingResult.hasErrors()) {
|
||||
throw new InvalidRequestException("Invalid parameter", bindingResult);
|
||||
}
|
||||
book.setId(exist.getId());
|
||||
return new ResponseEntity<>(bookRepository.save(book), HttpStatus.OK);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新一个书单,提供一个书单的部分信息
|
||||
* PATCH /api/v1/books/{id} 更新一条书单,提供部分信息
|
||||
*
|
||||
* @param id 更新的id
|
||||
* @param book 更新后的书单
|
||||
* @return http 响应
|
||||
*/
|
||||
@PatchMapping("/books/{id}")
|
||||
public HttpEntity<?> booksPatch(@PathVariable Long id, @RequestBody Book book) {
|
||||
Book exist = bookRepository.findById(id).orElseThrow(() ->
|
||||
new ResourceNoFoundException(String.format("Book by id %s not found!", id)));
|
||||
BeanWrapper beanWrapper = new BeanWrapperImpl(book);
|
||||
PropertyDescriptor[] propertyDescriptors = beanWrapper.getPropertyDescriptors();
|
||||
List<String> nullPropertyNames = new ArrayList<>();
|
||||
for (PropertyDescriptor pd :
|
||||
propertyDescriptors) {
|
||||
if (beanWrapper.getPropertyValue(pd.getName()) == null) {
|
||||
nullPropertyNames.add(pd.getName());
|
||||
}
|
||||
}
|
||||
BeanUtils.copyProperties(book, exist, nullPropertyNames.toArray(new String[nullPropertyNames.size()]));
|
||||
return new ResponseEntity<>(bookRepository.save(exist), HttpStatus.OK);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除一个书单
|
||||
* DELETE /api/v1/books/{id} 删除一条书单
|
||||
*
|
||||
* @param id id
|
||||
* @return http 响应
|
||||
*/
|
||||
@DeleteMapping("/books/{id}")
|
||||
public HttpEntity<?> booksDeleteOne(@PathVariable Long id) {
|
||||
Book exist = bookRepository.findById(id).orElseThrow(() ->
|
||||
new ResourceNoFoundException(String.format("Book by id %s not found!", id)));
|
||||
bookRepository.deleteById(exist.getId());
|
||||
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除所有书单
|
||||
* DELETE /API/v1/books 删除所有书单
|
||||
*
|
||||
* @return http 响应
|
||||
*/
|
||||
@DeleteMapping("/books")
|
||||
public HttpEntity<?> booksDeleteAll() {
|
||||
List<Book> books = bookRepository.findAll();
|
||||
if (books.isEmpty()) {
|
||||
throw new ResourceNoFoundException("Not found books!");
|
||||
}
|
||||
bookRepository.deleteAll();
|
||||
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
|
||||
}
|
||||
}
|
||||
47
src/main/java/com/example/demorest/entity/Book.java
Normal file
47
src/main/java/com/example/demorest/entity/Book.java
Normal file
@ -0,0 +1,47 @@
|
||||
package com.example.demorest.entity;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import lombok.Data;
|
||||
import org.hibernate.annotations.ColumnDefault;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.validation.constraints.Digits;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* 书单对象
|
||||
* @author xieYj
|
||||
* @date 2021/5/12 13:57
|
||||
*/
|
||||
@Entity
|
||||
@Data
|
||||
public class Book {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@Column(insertable = false, length = 20, nullable = false)
|
||||
private Long id;
|
||||
|
||||
@NotBlank(message = "书名不能为空")
|
||||
@Column(columnDefinition = "varchar(50) comment '书名'")
|
||||
private String name;
|
||||
|
||||
@NotBlank(message = "作者不能为空")
|
||||
@Column(columnDefinition = "varchar(25) comment '作者'")
|
||||
private String author;
|
||||
|
||||
@Column(columnDefinition = "varchar(255) comment '描述'")
|
||||
public String description;
|
||||
|
||||
/**
|
||||
* status 非空
|
||||
*/
|
||||
@NotNull(message = "status 不能为空")
|
||||
@ColumnDefault("1")
|
||||
@Column(columnDefinition = "tinyint(1) comment '是否存在,false|0不存在,true|1已存在'")
|
||||
public Boolean status;
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
package com.example.demorest.exception;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.springframework.validation.Errors;
|
||||
|
||||
/**
|
||||
* 无效的参数请求异常
|
||||
*
|
||||
* @author xieYj
|
||||
* @date 2021/5/12 16:22
|
||||
*/
|
||||
public class InvalidRequestException extends RuntimeException {
|
||||
@Getter
|
||||
private Errors errors;
|
||||
|
||||
public InvalidRequestException(String message, Errors errors) {
|
||||
super(message);
|
||||
this.errors = errors;
|
||||
}
|
||||
|
||||
public InvalidRequestException(Errors errors) {
|
||||
this.errors = errors;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
package com.example.demorest.exception;
|
||||
|
||||
/**
|
||||
* 资源找不到异常
|
||||
*
|
||||
* @author xieYj
|
||||
* @date 2021/5/12 16:24
|
||||
*/
|
||||
public class ResourceNoFoundException extends RuntimeException {
|
||||
public ResourceNoFoundException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,63 @@
|
||||
package com.example.demorest.handle;
|
||||
|
||||
import com.example.demorest.exception.InvalidRequestException;
|
||||
import com.example.demorest.exception.ResourceNoFoundException;
|
||||
import com.example.demorest.resource.ErrorResource;
|
||||
import com.example.demorest.resource.FieldResource;
|
||||
import com.example.demorest.resource.InvalidErrorResource;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.validation.Errors;
|
||||
import org.springframework.validation.FieldError;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 书单API全局异常处理
|
||||
*
|
||||
* @author xieYj
|
||||
* @date 2021/5/12 16:31
|
||||
*/
|
||||
@RestControllerAdvice(basePackages = "com.example.demorest.controller")
|
||||
public class ApiExceptionHandler {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||
|
||||
@ExceptionHandler(ResourceNoFoundException.class)
|
||||
public HttpEntity<?> handleNotFound(ResourceNoFoundException e) {
|
||||
ErrorResource errorResource = new ErrorResource(e.getMessage());
|
||||
logger.error(errorResource.toString());
|
||||
return new ResponseEntity<>(errorResource, HttpStatus.NOT_FOUND);
|
||||
}
|
||||
|
||||
@ExceptionHandler(InvalidRequestException.class)
|
||||
public HttpEntity<?> handleInvalidRequest(InvalidRequestException e) {
|
||||
Errors errors = e.getErrors();
|
||||
List<FieldResource> fieldResources = new ArrayList<>();
|
||||
List<FieldError> fieldErrors = errors.getFieldErrors();
|
||||
for (FieldError fieldError : fieldErrors) {
|
||||
fieldResources.add(
|
||||
new FieldResource(fieldError.getObjectName(),
|
||||
fieldError.getField(),
|
||||
fieldError.getCode(),
|
||||
fieldError.getDefaultMessage())
|
||||
);
|
||||
}
|
||||
InvalidErrorResource invalidErrorResource = new InvalidErrorResource(e.getMessage(), fieldResources);
|
||||
logger.error(invalidErrorResource.toString());
|
||||
return new ResponseEntity<>(invalidErrorResource, HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
|
||||
@ExceptionHandler(Exception.class)
|
||||
public HttpEntity<?> handleException(Exception e) {
|
||||
logger.error(e.getMessage());
|
||||
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
package com.example.demorest.repository;
|
||||
|
||||
import com.example.demorest.entity.Book;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
/**
|
||||
* 书单仓储层,jpa实现
|
||||
* @author xieYj
|
||||
* @date 2021/5/12 15:19
|
||||
*/
|
||||
@Repository
|
||||
public interface BookRepository extends JpaRepository<Book, Long> {
|
||||
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
package com.example.demorest.resource;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.ToString;
|
||||
|
||||
/**
|
||||
* 资源错误
|
||||
*
|
||||
* @author xieYj
|
||||
* @date 2021/5/12 16:27
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@ToString
|
||||
@Getter
|
||||
public class ErrorResource {
|
||||
private String message;
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
package com.example.demorest.resource;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.ToString;
|
||||
|
||||
/**
|
||||
* 字段错误
|
||||
*
|
||||
* @author josxy
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@ToString
|
||||
@Getter
|
||||
public class FieldResource {
|
||||
/**
|
||||
* 实体对象
|
||||
*/
|
||||
private String resource;
|
||||
/**
|
||||
* 字段
|
||||
*/
|
||||
private String field;
|
||||
/**
|
||||
* 代码
|
||||
*/
|
||||
private String code;
|
||||
/**
|
||||
* 具体信息
|
||||
*/
|
||||
private String message;
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
package com.example.demorest.resource;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.ToString;
|
||||
|
||||
/**
|
||||
* 资源错误信息封装
|
||||
* @author xieYj
|
||||
* @date 2021/5/12 16:29
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@ToString
|
||||
@Getter
|
||||
public class InvalidErrorResource {
|
||||
private String message;
|
||||
private Object errors;
|
||||
}
|
||||
@ -1,4 +1,21 @@
|
||||
spring:
|
||||
application:
|
||||
name: restful-api
|
||||
datasource:
|
||||
url: jdbc:p6spy:mysql://localhost:3306/spring?useUnicode=true&characterEncoding=utf-8&useSSL=false
|
||||
username: root
|
||||
password: 123456
|
||||
platform: mysql
|
||||
driver-class-name: com.p6spy.engine.spy.P6SpyDriver
|
||||
jpa:
|
||||
# 关闭jpa自带的show sql
|
||||
show-sql: false
|
||||
open-in-view: false
|
||||
#加上这句会执行import.sql
|
||||
generate-ddl: true
|
||||
hibernate:
|
||||
ddl-auto: create
|
||||
server:
|
||||
port: 9420
|
||||
servlet:
|
||||
context-path: /api
|
||||
port: 8001
|
||||
|
||||
19
src/main/resources/application-prod.yml
Normal file
19
src/main/resources/application-prod.yml
Normal file
@ -0,0 +1,19 @@
|
||||
spring:
|
||||
application:
|
||||
name: restful-api
|
||||
datasource:
|
||||
url: jdbc:mysql://localhost:3306/spring?useUnicode=true&characterEncoding=utf-8&useSSL=false
|
||||
username: root
|
||||
password: 123456
|
||||
platform: mysql
|
||||
jpa:
|
||||
show-sql: false
|
||||
open-in-view: false
|
||||
#加上这句会执行import.sql
|
||||
generate-ddl: false
|
||||
hibernate:
|
||||
ddl-auto: update
|
||||
server:
|
||||
servlet:
|
||||
context-path: /api
|
||||
port: 8001
|
||||
@ -1,4 +0,0 @@
|
||||
server:
|
||||
port: 9420
|
||||
servlet:
|
||||
context-path: /server
|
||||
24
src/main/resources/import.sql
Normal file
24
src/main/resources/import.sql
Normal file
@ -0,0 +1,24 @@
|
||||
INSERT INTO spring.book (id, author, description, name, status) VALUES (1, '孟宁', '本书从理解计算机硬件的核心工作机制(存储程序计算机和函数调用堆栈)和用户态程序如何通过系统调用陷入内核(中断异常)入手,通过上下两个方向双向夹击的策略,并利用实际可运行程序的反汇编代码从实践的角度理解操作系统内核,分析Linux内核源代码,从系统调用陷入内核、进程调度与进程切换开始,最后返回到用户态进程。', '庖丁解牛Linux内核分析', 1);
|
||||
INSERT INTO spring.book (id, author, description, name, status) VALUES (2, '孙亮', '大数据时代为机器学习的应用提供了广阔的空间,各行各业涉及数据分析的工作都需要使用机器学习算法。本书围绕实际数据分析的流程展开,着重介绍数据探索、数据预处理和常用的机器学习算法模型。本书从解决实际问题的角度出发,介绍回归算法、分类算法、推荐算法、排序算法和集成学习算法。在介绍每种机器学习算法模型时,书中不但阐述基本原理,而且讨论模型的评价与选择。为方便读者学习各种算法,本书介绍了R语言中相应的软件包并给出了示例程序。', '实用机器学习', 1);
|
||||
INSERT INTO spring.book (id, author, description, name, status) VALUES (3, '托马斯·哈斯尔万特', '本书以基础的统计学知识和假设检验为重点,简明扼要地讲述了Python在数据分析、可视化和统计建模中的应用。主要包括Python的简单介绍、研究设计、数据管理、概率分布、不同数据类型的假设检验、广义线性模型、生存分析和贝叶斯统计学等从入门到高级的内容。', 'Python统计分析', 1);
|
||||
INSERT INTO spring.book (id, author, description, name, status) VALUES (4, '甘迪文', '《Windows黑客编程技术详解》介绍的是黑客编程的基础技术,涉及用户层下的Windows编程和内核层下的Rootkit编程。本书分为用户篇和内核篇两部分,用户篇包括11章,配套49个示例程序源码;内核篇包括7章,配套28个示例程序源码。本书介绍的每个技术都有详细的实现原理,以及对应的示例代码(配套代码均支持32位和64位Windows 7、Windows 8.1及Windows 10系统),旨在帮助初学者建立起黑客编程技术的基础。', 'Windows黑客编程技术详解', 1);
|
||||
INSERT INTO spring.book (id, author, description, name, status) VALUES (5, '科里•奥尔索夫', '本书作者是一名自学成才的程序员,经过一年的自学,掌握了编程技能并在eBay找到了一份软件工程师的工作。本书是作者结合个人经验写作而成,旨在帮助读者从外行成长为一名专业的Python程序员。', 'Python编程无师自通——专业程序员的养成', 1);
|
||||
INSERT INTO spring.book (id, author, description, name, status) VALUES (6, '威廉·史密斯', '本书由浅入深地详细讲解了计算机存储使用的多种数据结构。本书首先讲解了初级的数据结构(如表、栈、队列和堆等),具体包括它们的工作原理、功能实现以及典型的应用程序等;然后讨论了数据结构,如泛型集合、排序、搜索和递归等;最后介绍了如何在日常应用中使用这些数据结构。', '程序员学数据结构', 1);
|
||||
INSERT INTO spring.book (id, author, description, name, status) VALUES (7, '张鑫旭', '本书从前端开发人员的需求出发,以“流”为线索,从结构、内容到美化装饰等方面,全面且深入地讲解前端开发人员必须了解和掌握的大量的CSS知识点。同时,作者结合多年的从业经验,通过大量的实战案例,详尽解析CSS的相关知识与常见问题。作者还为本书开发了专门的配套网站,进行实例展示、问题答疑。', 'CSS世界', 1);
|
||||
INSERT INTO spring.book (id, author, description, name, status) VALUES (8, '理查德·格里姆斯', '作为一门广为人知的编程语言,C++已经诞生30多年了,这期间也出现并流行过许多种编程语言,但是C++是经得起考验的。如此经典的编程语言,值得每一位编程领域的新人认真学习,也适合有经验的程序员细细品味。', 'C++编程自学宝典', 1);
|
||||
INSERT INTO spring.book (id, author, description, name, status) VALUES (9, '萨沙·戈德斯汀', '本书详细解释了影响应用程序性能的Windows、CLR和物理硬件的内部结构,并为读者提供了衡量代码如何独立于外部因素执行操作的知识和工具。书中提供了大量的C#代码示例和技巧,将帮助读者zui大限度地提高算法和应用程序的性能,提高个人竞争优势,使用更低的成本获取更多的用户。', '.NET性能优化', 1);
|
||||
INSERT INTO spring.book (id, author, description, name, status) VALUES (10, '李伟', '《C++模板元编程实战:一个深度学习框架的初步实现》以一个深度学习框架的初步实现为例,讨论如何在一个相对较大的项目中深入应用元编程,为系统性能优化提供更多的可能。', 'C++模板元编程实战:一个深度学习框架的初步实现', 1);
|
||||
INSERT INTO spring.book (id, author, description, name, status) VALUES (11, 'Ben Klemens 克莱蒙', '本书展现了传统C语言教科书所不具有相关技术。全书分', 'C程序设计新思维(第2版)', 1);
|
||||
INSERT INTO spring.book (id, author, description, name, status) VALUES (12, '王云', '本书遵循由浅入深、循序渐进的原则,讲解单片机开发经典案例。本书以YL51单片机开发板为平台,通过案例逐个讲解开发板上各个器件模块的使用及其编程方法,包括单片机最小系统、数码管显示原理、中断与定时器、数模\\模数转换工作原理、LCD液晶显示、串行口通信、步进电机驱动原理、PWM脉宽调制与直流电机等内容。', '51单片机C语言程序设计教程', 1);
|
||||
INSERT INTO spring.book (id, author, description, name, status) VALUES (13, '胡振波', '本书是一本介绍通用CPU设计的入门书,以通俗的语言系统介绍了CPU和RISC-V架构,力求为读者揭开CPU设计的神秘面纱,打开计算机体系结构的大门。', '手把手教你设计CPU——RISC-V处理器篇', 1);
|
||||
INSERT INTO spring.book (id, author, description, name, status) VALUES (14, '克劳斯·福勒', '本书旨在通过实际的Python 3.0代码示例展示Python与数学应用程序的紧密联系,介绍将Python中的各种概念用于科学计算的方法。', 'Python 3.0科学计算指南', 1);
|
||||
INSERT INTO spring.book (id, author, description, name, status) VALUES (15, '路彦雄', '《文本上的算法 深入浅出自然语言处理》结合-作者多年学习和从事自然语言处理相关工作的经验,力图用生动形象的方式深入浅出地介绍自然语言处理的理论、方法和技术。本书抛弃掉繁琐的证明,提取出算法的核心,帮助读者尽快地掌握自然语言处理所必需的知识和技能。', '文本上的算法——深入浅出自然语言处理', 1);
|
||||
INSERT INTO spring.book (id, author, description, name, status) VALUES (16, '胡世杰', '本书从云存储的需求出发讲述对象存储的原理,循序渐进地建立起一个分布式对象存储的架构,并且将软件实现出来。全书共8章,分别涉及对象存储简介、可扩展分布式系统、元数据服务、数据校验和去重、数据冗余处理、断点续传、数据压缩和数据维护等。本书选择用来实现分布式对象存储软件的编程语言是当前流行的Go语言。', '分布式对象存储——原理、架构及Go语言实现', 1);
|
||||
INSERT INTO spring.book (id, author, description, name, status) VALUES (17, '徐子珊', '《趣题学算法》适于作为程序员的参考书,高校各专业学生学习“数据结构”“算法设计分析”“程序设计”等课程的扩展读物,也可以作为上述课程的实验或课程设计的材料,还可以作为准备参加国内或国际程序设计赛事的读者的赛前训练材料。', '趣题学算法', 1);
|
||||
INSERT INTO spring.book (id, author, description, name, status) VALUES (18, '鲁什迪·夏姆斯', '现如今,数据科学已经成为一个热门的技术领域,它涵盖了人工智能的各个方面,例如数据处理、信息检索、机器学习、自然语言处理、数据可视化等。而Java作为一门经典的编程语言,在数据科学领域也有着杰出的表现。', 'Java数据科学指南', 1);
|
||||
INSERT INTO spring.book (id, author, description, name, status) VALUES (19, '罗炳森', '结构化查询语言(Structured Query Language,SQL)是一种功能强大的数据库语言。它基于关系代数运算,功能丰富、语言简洁、使用方便灵活,已成为关系数据库的标准语言。', 'SQL优化核心思想', 1);
|
||||
INSERT INTO spring.book (id, author, description, name, status) VALUES (20, '弗兰克·D.卢娜', 'Direct3D是微软公司DirectX SDK集成开发包中的重要组成部分,是编写高性能3D图形应用程序的渲染库,适用于多媒体、娱乐、即时3D动画等广泛和实用的3D图形计算领域。', 'DirectX 12 3D 游戏开发实战', 1);
|
||||
INSERT INTO spring.book (id, author, description, name, status) VALUES (21, '巴阿尔丁•阿扎米', 'Kibana是广泛地应用在数据检索和数据可视化领域的ELK中的一员。本书专门介绍Kibana,通过不同的用例场景,带领读者全面体验Kibana的可视化功能。', 'Kibana数据可视化', 1);
|
||||
INSERT INTO spring.book (id, author, description, name, status) VALUES (22, '郝佳', '《Spring源码深度解析(第2版)》从核心实现、企业应用和Spring Boot这3个方面,由浅入深、由易到难地对Spring源码展开了系统的讲解,包括Spring 整体架构和环境搭建、容器的基本实现、默认标签的解析、自定义标签的解析、bean的加载、容器的功能扩展、AOP、数据库连接JDBC、整合MyBatis、事务、SpringMVC、远程服务、Spring消息、Spring Boot体系原理等内容。', 'Spring源码深度解析(第2版)', 1);
|
||||
INSERT INTO spring.book (id, author, description, name, status) VALUES (23, 'Jon Bentley', '书的内容围绕程序设计人员面对的一系列实际问题展开。作者JonBentley以其独有的洞察力和创造力,引导读者理解这些问题并学会解决方法,而这些正是程序员实际编程生涯中到关重要的。', '编程珠玑(第2版•修订版)', 1);
|
||||
INSERT INTO spring.book (id, author, description, name, status) VALUES (24, 'Mickey W. Mantle', '这是一本系统阐述面对混乱而容易失控的技术开发团队时,如何管理、建设和强化团队,成功交付开发成果的大作。两位作者Mickey W. Mantle和Ron Lichty以合起来近70年的开发管理经验为基础,通过深刻的观察和分析,找到了软件开发管理的核心问题——人的管理,并围绕如何真正理解程序员、找到合适的程序员、与程序员沟通这几个核心话题,一步步展开,扩展到如何以人为本地进行团队建设、管理和项目管理。', '告别失控:软件开发团队管理必读', 1);
|
||||
10
src/main/resources/spy.properties
Normal file
10
src/main/resources/spy.properties
Normal file
@ -0,0 +1,10 @@
|
||||
driverlist=com.mysql.cj.jdbc.Driver
|
||||
module.log=com.p6spy.engine.logging.P6LogFactory
|
||||
appender=com.p6spy.engine.spy.appender.Slf4JLogger
|
||||
#自定义日志格式
|
||||
logMessageFormat=com.p6spy.engine.spy.appender.CustomLineFormat
|
||||
#格式:%(currentTime)|%(executionTime)|%(category)|connection%(connectionId)|%(sqlSingleLine)
|
||||
customLogMessageFormat=%(category)|conn%(connectionId)|%(sqlSingleLine)
|
||||
databaseDialectDateFormat=yyyy-MM-dd HH:mm:ss
|
||||
databaseDialectTimestampFormat=yyyy-MM-dd HH:mm:ss
|
||||
dateformat=yyyy-MM-dd HH:mm:ss
|
||||
@ -1,13 +1,13 @@
|
||||
package cn.allms.community;
|
||||
package com.example.demorest;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
@SpringBootTest
|
||||
class MxCommunityApplicationTests {
|
||||
class DemoRestApplicationTests {
|
||||
|
||||
@Test
|
||||
void contextLoads() {
|
||||
}
|
||||
@Test
|
||||
void contextLoads() {
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user