init demo-restful

This commit is contained in:
xieYj 2021-05-12 17:16:03 +08:00
parent 6d4d53054d
commit 295b022762
40 changed files with 846 additions and 622 deletions

18
.gitignore vendored
View File

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

View File

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

Binary file not shown.

View 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
View 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
View 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
View File

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

@ -0,0 +1 @@
rootProject.name = 'demo-rest'

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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

View File

@ -1,4 +0,0 @@
server:
port: 9420
servlet:
context-path: /server

View 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 LanguageSQL是一种功能强大的数据库语言。它基于关系代数运算功能丰富、语言简洁、使用方便灵活已成为关系数据库的标准语言。', '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);

View 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

View File

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