init demo-restful

This commit is contained in:
xieYj 2021-05-12 17:16:03 +08:00
parent 6d4d53054d
commit e1c2edb92b
21 changed files with 52 additions and 630 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 编写方式。

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>

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

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

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

View File

@ -1,13 +0,0 @@
package cn.allms.community;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class MxCommunityApplicationTests {
@Test
void contextLoads() {
}
}