Compare commits

..

11 Commits

Author SHA1 Message Date
5db789dffd 更新 '.drone.yaml'
Some checks reported errors
continuous-integration/drone/push Build encountered an error
2021-07-31 17:11:45 +08:00
56aea528ca 更新 '.drone.yaml'
Some checks reported errors
continuous-integration/drone/push Build encountered an error
2021-07-31 17:11:27 +08:00
76fabdb461 更新 '.drone.yaml'
Some checks reported errors
continuous-integration/drone/push Build encountered an error
2021-07-31 17:11:11 +08:00
6c225308c3 更新 '.drone.yaml'
Some checks reported errors
continuous-integration/drone/push Build encountered an error
2021-07-31 14:12:23 +08:00
33de72c9df 更新 '.drone.yaml'
Some checks reported errors
continuous-integration/drone/push Build encountered an error
2021-07-31 14:11:20 +08:00
a08e17ad9c 更新 '.drone.yaml'
All checks were successful
continuous-integration/drone/push Build is passing
2021-07-31 13:47:30 +08:00
eda3f11ca2 更新 '.drone.yaml'
Some checks failed
continuous-integration/drone/push Build is failing
2021-07-31 13:35:29 +08:00
35de2ad9ca 更新 '.drone.yml'
Some checks failed
continuous-integration/drone/push Build is failing
2021-07-31 01:39:45 +08:00
c94bee58ae 添加 '.drone.yaml' 2021-07-31 00:41:34 +08:00
josxy
28e7e8d97c 项目END 2021-07-04 03:39:13 +08:00
josxy
a14767d48b add: 切换线上数据库 2021-06-10 00:18:46 +08:00
504 changed files with 88763 additions and 606 deletions

21
.drone.yaml Normal file
View File

@ -0,0 +1,21 @@
kind: pipeline
type: docker
name: mx-community
steps:
- name: build
image: maven:3-jdk-8
pull: if-not-exists
volumes:
- name: maven
path: /root/.m2
commands:
- mvn clean package -DskipTests
- ls -h
when:
event:
- push
volumes:
- name: maven
host:
path: /opt/ci/maven/

14
.gitignore vendored
View File

@ -1,10 +1,12 @@
HELP.md
target/
log/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
!**/src/main/**
!**/src/test/**
*.class
### STS ###
*.log
.apt_generated
.classpath
.factorypath
@ -18,10 +20,6 @@ target/
*.iws
*.iml
*.ipr
.gradle
gradlew
gradlew.bat
gradle
### NetBeans ###
/nbproject/private/
@ -30,8 +28,6 @@ gradle
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/
### VS Code ###
.vscode/

4
Dockerfile Normal file
View File

@ -0,0 +1,4 @@
FROM hub.c.163.com/library/java:latest
VOLUME /tmp
ADD target/blog-1.0.1-SNAPSHOT.jar blog.jar
ENTRYPOINT ["java","-jar","/blog.jar"]

View File

@ -1,6 +1,8 @@
**项目名称:** 沐雪博客DDD版本
## 浅枫沐雪:
**项目名称:** 浅枫沐雪开源版
**项目描述:**
@ -10,12 +12,12 @@
```java
前端: Semantic-UI框架
后端: JDK1.8+SpringBoot2.4.5
后端: JDK1.8+SpringBoot+ Spring Data JPA +MySQL5.7
```
**项目介绍:**
整个项目前端采用 Semantic-UI后端基于 SpringBoot分为
1、整个项目前端采用 Semantic-UI后端基于 SpringBoot分为
- 前台展示
@ -31,4 +33,16 @@
4、使用 CommentUtil 工具类以及 Semantic-UI 中自带的留言组件实现评论功能,让评论显示为层级
5、采用 SpringData JAP 连接数据库,简化 SQL 编写方式。
5、采用 SpringData JAP 连接数据库,简化 SQL 编写方式。
**项目收获:**
整个项目部署和测试阶段的调试 通过该项目的开发,让我对项目开发的整体流程有了一个全面的了解,加深了我对前后
端的技术栈的 掌握以及使我能够独立部署项目Linux 服务器相关操作更佳熟练。对自己的代码有了更佳严格的要求、 有了不同的
代码和业务思想,巩固了后端技术栈的知识,对代程序编写有更加深刻的认识!
## 感谢
原作者Githubhttps://github.com/yerenping/blog

203
pom.xml
View File

@ -1,59 +1,156 @@
<?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>
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<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>
<groupId>cn.allms</groupId>
<artifactId>blog</artifactId>
<version>1.0.0-RELEASE</version>
<packaging>jar</packaging>
<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>
<name>blog</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<repositories>
<repository>
<id>public</id>
<name>aliyun nexus</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<releases>
<enabled>true</enabled>
</releases>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>public</id>
<name>aliyun nexus</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!--jpa-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!--thymeleaf-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!--web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--devtools-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!--test-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--aop-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!--引入Markdown转HTML的插件-->
<dependency>
<groupId>com.atlassian.commonmark</groupId>
<artifactId>commonmark</artifactId>
<version>0.10.0</version>
</dependency>
<!--扩展 标题-->
<dependency>
<groupId>com.atlassian.commonmark</groupId>
<artifactId>commonmark-ext-heading-anchor</artifactId>
<version>0.10.0</version>
</dependency>
<!--扩展 表格-->
<dependency>
<groupId>com.atlassian.commonmark</groupId>
<artifactId>commonmark-ext-gfm-tables</artifactId>
<version>0.10.0</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.3.5.Final</version>
</dependency>
<!--配置文件:注解-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
<!--服务监控-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--fastjson-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.35</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.3.0.RELEASE</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,15 @@
package cn.allms;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* springboot启动器
*/
@SpringBootApplication
public class MxBlogApplication {
public static void main(String[] args) {
SpringApplication.run(MxBlogApplication.class, args);
}
}

View File

@ -0,0 +1,98 @@
package cn.allms.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
/**
* @author josxy
*/
@Component
public class LogAspectOld {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
/**
* 切点cn.allms.controller
*/
//@Pointcut("execution(* cn.allms.controller.*.*(..))")
public void log() {
}
// @Before("log()")
public void doBefore(JoinPoint joinPoint) {
// 获取request对象
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
assert attributes != null;
HttpServletRequest request = attributes.getRequest();
// URL
String url = request.getRequestURL().toString();
// IP
String ip = request.getRemoteAddr();
// 方法全名 = 类名.方法名
String classMethod = joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName();
// 请求对象
Object[] args = joinPoint.getArgs();
RequestLog requestLog = new RequestLog(url, ip, classMethod, args);
logger.info("Request : {}", requestLog);
}
//@After("log()")
public void doAfter() {
// logger.info("-------doafter-----------");
}
public void doAfterReturn(Object result) {
logger.info("控制器方法名称Result : {}", result);
}
/**
* 内部类:用于存放请求信息,用日志的方式将其记录下来
*/
private class RequestLog {
/**
* 请求地址
*/
private String url;
/**
* 请求ip
*/
private String ip;
/**
* 请求方法
*/
private String classMethod;
/**
* 请求参数集合
*/
private Object[] args;
public RequestLog(String url, String ip, String classMethod, Object[] args) {
this.url = url;
this.ip = ip;
this.classMethod = classMethod;
this.args = args;
}
@Override
public String toString() {
return "RequestLog{" +
"url='" + url + '\'' +
", ip='" + ip + '\'' +
", classMethod='" + classMethod + '\'' +
", args=" + Arrays.toString(args) +
'}';
}
}
}

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,62 @@
package cn.allms.config;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
/**
* 日志
*
* @author rainerosion
* @date 2021/4/9 19:11
*/
@Slf4j
@Component
@Aspect
public class LogAspect {
@Pointcut("execution(* cn.allms.controller.*.*(..))")
public void webLog() {
}
@Before("webLog()")
public void doBefore(JoinPoint joinPoint) {
// 开始打印请求日志
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
// 打印请求相关参数
log.info("========================================== Start ==========================================");
// 打印请求 url
log.info("[请求URL] : {}", request.getRequestURL().toString());
// 打印 Http method
log.info("[请求方法] : {}", request.getMethod());
// 打印调用 controller 的全路径以及执行方法
log.info("[请求类名] : {}.{}", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName());
// 打印请求的 IP
log.info("[请求IP] : {}", request.getRemoteAddr());
// 打印请求入参
log.info("[请求参数] : {}", JSONObject.toJSONString(joinPoint.getArgs()));
}
@Around("webLog()")
public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
Object result = proceedingJoinPoint.proceed();
// 打印出参
log.info("[响应结果] : {}", JSONObject.toJSONString(result));
// 执行耗时
log.info("[请求耗时] : {} ms", System.currentTimeMillis() - startTime);
log.info("=========================================== End ===========================================");
return result;
}
}

View File

@ -0,0 +1,22 @@
package cn.allms.config;
import cn.allms.interceptor.LoginInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
/**
* 配置类
*/
@Configuration
public class MyConfig extends WebMvcConfigurerAdapter {
//重写拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns("/admin/**")
.excludePathPatterns("/admin")
.excludePathPatterns("/admin/login");
}
}

View File

@ -0,0 +1,32 @@
package cn.allms.config;
import lombok.Data;
import java.util.List;
import java.util.Random;
/**
* 随机图片配置类
*
* @author josxy
*/
@Data
public class RandomPicturesConfig {
private List<String> pictures;
private final static String DEFAULT = "https://tva1.sinaimg.cn/large/0072Vf1pgy1foxk3rxvthj31kw0w0x3c";
/**
* TODO
* 随机获取一种图片
*
* @return
*/
public String getRandomPicAddr() {
Random random = new Random();
System.out.println(this.pictures);
System.out.println(this.pictures.get(random.nextInt(2)) + System.currentTimeMillis());
return this.pictures.size() > 0 ?
this.pictures.get(random.nextInt(2)) + System.currentTimeMillis() : DEFAULT;
}
}

View File

@ -0,0 +1,18 @@
package cn.allms.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
/**
* @Auther: 南迪叶先生:https://www.cnblogs.com/ye888/
* @Date: 2019/10/12
* @Description: com.yrp.controller
* @version: 1.0
*/
@Controller
public class AboutShowController {
@GetMapping("/about")
public String about() {
return "about";
}
}

View File

@ -0,0 +1,27 @@
package cn.allms.controller;
import cn.allms.service.BlogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
/**
* @Auther: 南迪叶先生:https://www.cnblogs.com/ye888/
* @Date: 2019/10/15
* @Description: com.yrp.controller.admin
* @version: 1.0
*/
@Controller
public class ArchiveShowController {
@Autowired
private BlogService blogServiceImpl;
@GetMapping("/archives")
public String archives(Model model) {
model.addAttribute("archiveMap", blogServiceImpl.archiveBlog());
model.addAttribute("blogCount", blogServiceImpl.countBlog());
return "archives";
}
}

View File

@ -0,0 +1,76 @@
package cn.allms.controller;
import cn.allms.po.User;
import cn.allms.po.Comment;
import cn.allms.service.BlogService;
import cn.allms.service.CommentService;
import cn.allms.util.MD5Utils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import javax.servlet.http.HttpSession;
/**
* 评论
*/
@Controller
public class CommentController {
@Autowired
private CommentService commentServiceImpl;
@Autowired
private BlogService blogServiceImpl;
@Value("${comment.avatar}")
private String avatar;
@Value("${comment.v2exUrl}")
private String v2exUrl;
/**
* 显示评论列表
*
* @param blogId
* @param model
* @return
*/
@GetMapping("/comments/{blogId}")
public String comments(@PathVariable Long blogId, Model model) {
model.addAttribute("comments", commentServiceImpl.listCommentByBlogId(blogId));
return "blog :: commentList";
}
/**
* 提交评论
*
* @param comment
* @return
*/
@PostMapping("/comments")
public String post(Comment comment, HttpSession session) {
Long blogId = comment.getBlog().getId();
//处理评论和博客之间的关系
comment.setBlog(blogServiceImpl.getBlog(blogId));
User user = (User) session.getAttribute("user");
//管理员发
if (user != null) {
comment.setAvatar(user.getAvatar());
comment.setAdminComment(true);
comment.setNickname(user.getNickname());
comment.setEmail(user.getEmail());
//普通用户
} else {
comment.setAvatar(v2exUrl + MD5Utils.code(comment.getEmail()));
}
commentServiceImpl.saveComment(comment);
return "redirect:/comments/" + blogId;
}
}

View File

@ -0,0 +1,80 @@
package cn.allms.controller;
import cn.allms.service.BlogService;
import cn.allms.service.TagService;
import cn.allms.service.TypeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.web.PageableDefault;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
/**
* 首页index.html
*/
@Controller
public class IndexController {
@Autowired
private BlogService blogServiceImpl;
@Autowired
private TypeService typeServiceImpl;
@Autowired
private TagService tagServiceImpl;
/**
* 首页面显示
*
* @param model
* @param pageable
* @return
*/
@GetMapping("/")
public String index(Model model, @PageableDefault(size = 8, sort = {"updateTime"}, direction = Sort.Direction.DESC)
Pageable pageable) {
//1. 获取分页的博客列表
model.addAttribute("page", blogServiceImpl.ListBlog(pageable));
//2. 获取分类的内容(显示6条)
model.addAttribute("types", typeServiceImpl.listTypeTop(6));
//3. 获取标签的内容
model.addAttribute("tags", tagServiceImpl.ListTagTop(10));
//4. 显示推荐博客列表
model.addAttribute("recommendBlogs", blogServiceImpl.listRecommendBlogTop(8));
return "index";
}
/**
* 导航栏中的搜索功能实现
*
* @param pageable
* @param model
* @return
*/
@PostMapping("/search")
public String search(@PageableDefault(size = 8, sort = {"updateTime"}, direction = Sort.Direction.DESC) Pageable pageable,
@RequestParam String query, Model model) {
model.addAttribute("page", blogServiceImpl.ListBlog(pageable, "%" + query + "%"));
model.addAttribute("query", query);
return "search";
}
@GetMapping("/blog/{id}")
public String blog(@PathVariable("id") Long id, Model model) {
model.addAttribute("blog", blogServiceImpl.getAadConvertBlog(id));
return "blog";
}
@GetMapping("/footer/newblog")
public String newblos(Model model) {
model.addAttribute("newblogs", blogServiceImpl.listRecommendBlogTop(3));
return "_fragments :: newblogList";
}
}

View File

@ -0,0 +1,50 @@
package cn.allms.controller;
import cn.allms.po.Tag;
import cn.allms.service.TagService;
import cn.allms.vo.BlogQuery;
import cn.allms.service.BlogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.web.PageableDefault;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import java.util.List;
/**
* @Auther: 南迪叶先生:https://www.cnblogs.com/ye888/
* @Date: 2019/10/12
* @Description: com.yrp.controller
* @version: 1.0
*/
@Controller
public class TagShowController {
@Autowired
private TagService tagServiceImpl;
@Autowired
private BlogService blogServiceImpl;
@GetMapping("/tags/{id}")
public String tags(Model model, @PageableDefault(size = 8, sort = {"updateTime"}, direction = Sort.Direction.DESC)
@PathVariable Long id, Pageable pageable) {
List<Tag> tags = tagServiceImpl.ListTagTop(10000);
if (id == -1 && tags.size() > 0) {
id = tags.get(0).getId();
}
BlogQuery blogQuery = new BlogQuery();
model.addAttribute("tags", tags);
model.addAttribute("page", blogServiceImpl.ListBlog(id, pageable));
model.addAttribute("avtiveTagId", id);
return "tags";
}
}

View File

@ -0,0 +1,50 @@
package cn.allms.controller;
import cn.allms.po.Type;
import cn.allms.service.TypeService;
import cn.allms.vo.BlogQuery;
import cn.allms.service.BlogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.web.PageableDefault;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import java.util.List;
/**
* @Auther: 南迪叶先生:https://www.cnblogs.com/ye888/
* @Date: 2019/10/12
* @Description: com.yrp.controller
* @version: 1.0
*/
@Controller
public class TypeShowController {
@Autowired
private TypeService typeServiceImpl;
@Autowired
private BlogService blogServiceImpl;
@GetMapping("/types/{id}")
public String types(Model model, @PageableDefault(size = 8, sort = {"updateTime"}, direction = Sort.Direction.DESC)
@PathVariable Long id, Pageable pageable) {
List<Type> types = typeServiceImpl.listTypeTop(10000);
if (id == -1 && types.size() > 0) {
id = types.get(0).getId();
}
BlogQuery blogQuery = new BlogQuery();
blogQuery.setTypeId(id);
model.addAttribute("types", types);
model.addAttribute("page", blogServiceImpl.ListBlog(pageable, blogQuery));
model.addAttribute("avtiveTypeId", id);
return "types";
}
}

View File

@ -0,0 +1,175 @@
package cn.allms.controller.admin;
import cn.allms.config.RandomPicturesConfig;
import cn.allms.po.Blog;
import cn.allms.po.User;
import cn.allms.vo.BlogQuery;
import cn.allms.service.BlogService;
import cn.allms.service.TagService;
import cn.allms.service.TypeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.web.PageableDefault;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import javax.servlet.http.HttpSession;
import java.util.Date;
/**
* @Auther: 南迪叶先生:https://www.cnblogs.com/ye888/
* @Date: 2019/10/3
* @Description: com.yrp.controller.admin
* @version: 1.0
*/
@Controller
@RequestMapping("admin")
public class BlogController {
/**
* 博客发布页面url
*/
private static final String INPUT = "admin/blogs-input";
private static final String HEAD_URL = "https://api.rainss.cn/random.php?t=";
private static final String HEAD_URL_1 = "https://api.rainss.cn/acgimgurl/acgurl.php?t=";
/**
* 显示分页查询到的博客列表页面url
*/
private static final String LIST = "admin/blogs";
/**
* 局部刷新表格查询到的数据url
*/
private static final String REDIRECT_LIST = "redirect:/admin/blogs";
@Autowired
private BlogService blogServiceImpl;
@Autowired
private TypeService typeServiceImpl;
@Autowired
private TagService tagServiceImpl;
/**
* 分页显示博客列表
*
* @param model
* @param pageable
* @param blog
* @return
*/
@GetMapping("/blogs")
public String showBlogs(Model model, @PageableDefault(size = 8, sort = {"updateTime"}, direction = Sort.Direction.DESC) Pageable pageable, BlogQuery blog) {
model.addAttribute("page", blogServiceImpl.ListBlog(pageable, blog));
model.addAttribute("types", typeServiceImpl.listType());
return LIST;
}
/**
* 联合查询博客列表
*
* @param model
* @param pageable
* @param blog
* @return
*/
@PostMapping("/blogs/search")
public String searchBlogs(Model model, @PageableDefault(size = 8, sort = {"updateTime"}, direction = Sort.Direction.DESC) Pageable pageable,
BlogQuery blog) {
model.addAttribute("page", blogServiceImpl.ListBlog(pageable, blog));
return "admin/blogs :: blogList";
}
/**
* 新增博客页面
*
* @param model
* @return
*/
@GetMapping("/blogs/input")
public String inputBlog(Model model) {
model.addAttribute("firstPicture", HEAD_URL_1 + System.currentTimeMillis());
setTypeAadTag(model);
return INPUT;
}
/**
* 提交博客
*
* @param blog
* @param attributes
* @param session
* @return
*/
@PostMapping("/blogs")
public String post(Blog blog, RedirectAttributes attributes, HttpSession session) {
blog.setUser((User) session.getAttribute("user"));
blog.setType(typeServiceImpl.getType(blog.getType().getId()));
blog.setTags(tagServiceImpl.ListTag(blog.getTagIds()));
Blog b;
Date date = new Date();
//新增博客
if (blog.getId() == null) {
blog.setUpdateTime(date);
blog.setCreateTime(date);
blog.setViews(0);
b = blogServiceImpl.saveBlog(blog);
} else {//更新博客
b = blogServiceImpl.updateBlog(blog.getId(), blog);
}
if (b == null) {
attributes.addFlashAttribute("message", "操作失败");
} else {
attributes.addFlashAttribute("message", "操作成功");
}
return REDIRECT_LIST;
}
/**
* 删除博客
*
* @param id
* @return
*/
@GetMapping("/blogs/{id}/delete")
public String deleteBlog(@PathVariable("id") Long id) {
blogServiceImpl.deleteBlog(id);
return REDIRECT_LIST;
}
/**
* 修改博客页面
*
* @param model
* @return
*/
@GetMapping("/blogs/{id}/input")
public String editBlog(@PathVariable("id") Long id, Model model) {
setTypeAadTag(model);
Blog blog = blogServiceImpl.getBlog(id);
//处理tagIds
blog.init();
model.addAttribute("firstPicture", HEAD_URL_1 + System.currentTimeMillis());
model.addAttribute("blog", blog);
return INPUT;
}
private void setTypeAadTag(Model model) {
/*博客查询,为后面的编辑博客做准备*/
model.addAttribute("blog", new Blog());
/*分类查询*/
model.addAttribute("types", typeServiceImpl.listType());
/*标签查询*/
model.addAttribute("tags", tagServiceImpl.ListTag());
}
}

View File

@ -0,0 +1,75 @@
package cn.allms.controller.admin;
import cn.allms.po.User;
import cn.allms.service.Userservice;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import javax.servlet.http.HttpSession;
/**
* @Auther: 南迪叶先生:https://www.cnblogs.com/ye888/
* @Date: 2019/9/28
* @Description: com.yrp.controller.admin
* @version: 1.0
*/
@Controller
@RequestMapping("/admin")
public class LoginController {
@Autowired
private Userservice userservice;
/**
* 显示用户登录页面
*
* @return
*/
@GetMapping
public String loginPage() {
return "admin/login";
}
/**
* 用户登录
*
* @param username
* @param password
* @param session
* @param attributes
* @return
*/
@PostMapping("/login")
public String login(@RequestParam String username,
@RequestParam String password,
HttpSession session,
RedirectAttributes attributes) {
User user = userservice.checkUser(username, password);
if (user != null) {
user.setPassword(null);
session.setAttribute("user", user);
return "admin/index";
} else {
attributes.addFlashAttribute("message", "用户名和密码错误");
return "redirect:/admin";
}
}
/**
* 用户注销
*
* @param session
* @return
*/
@GetMapping("/logout")
public String logout(HttpSession session) {
session.removeAttribute("user");
return "redirect:/admin";
}
}

View File

@ -0,0 +1,134 @@
package cn.allms.controller.admin;
import cn.allms.po.Tag;
import cn.allms.service.TagService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.web.PageableDefault;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import javax.validation.Valid;
/**
* 标签
*/
@Controller
@RequestMapping("/admin")
public class TagController {
@Autowired
private TagService tagServiceImpl;
/**
* 分页查询标签列表
*
* @return
*/
@GetMapping("/tags")
public String types(@PageableDefault(size = 8, sort = {"id"}, direction = Sort.Direction.DESC)
Pageable pageable, Model model) {
model.addAttribute("page", tagServiceImpl.ListTag(pageable));
return "admin/tags";
}
/**
* 显示添加标签页面
*
* @param model
* @return
*/
@GetMapping("/tags/input")
public String showInput(Model model) {
model.addAttribute("tag", new Tag());
return "admin/tags-input";
}
/**
* 新增标签
*
* @param tag
* @param result
* @param attributes
* @return
*/
@PostMapping("/tags")
public String post(@Valid Tag tag, BindingResult result, RedirectAttributes attributes) {
Tag tag1 = tagServiceImpl.getTagByName(tag.getName());
if (tag1 != null) {
result.rejectValue("name", "nameError", "不能添加重复的标签");
}
if (result.hasErrors()) {
return "admin/tags-input";
}
Tag t = tagServiceImpl.saveTag(tag);
if (t == null) {
attributes.addFlashAttribute("新增失败");
} else {
attributes.addFlashAttribute("新增成功");
}
return "redirect:/admin/tags";
}
/**
* 显示添加标签页面
*
* @param model
* @return
*/
@GetMapping("/tags/{id}/input")
public String editInput(@PathVariable("id") Long id, Model model) {
model.addAttribute("tag", tagServiceImpl.getTag(id));
return "admin/tags-input";
}
/**
* 修改标签名
*
* @param tag
* @param result
* @param id
* @param attributes
* @return
*/
@PostMapping("/tags/{id}")
public String editPost(@Valid Tag tag, BindingResult result, @PathVariable("id") Long id, RedirectAttributes attributes) {
Tag tag1 = tagServiceImpl.getTagByName(tag.getName());
if (tag1 != null) {
result.rejectValue("name", "nameError", "不能添加重复的分类");
}
if (result.hasErrors()) {
return "admin/tags-input";
}
Tag t = tagServiceImpl.updateTag(id, tag);
if (t == null) {
attributes.addFlashAttribute("message", "更新失败");
} else {
attributes.addFlashAttribute("message", "更新成功");
}
return "redirect:/admin/tags";
}
/**
* 删除功能
*
* @param id
* @param attributes
* @return
*/
@GetMapping("/tags/{id}/delete")
public String deleteById(@PathVariable("id") Long id, RedirectAttributes attributes) {
tagServiceImpl.deleteTag(id);
attributes.addFlashAttribute("message", "删除成功");
return "redirect:/admin/tags";
}
}

View File

@ -0,0 +1,127 @@
package cn.allms.controller.admin;
import cn.allms.po.Type;
import cn.allms.service.TypeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.web.PageableDefault;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import javax.validation.Valid;
/**
* @Auther: 南迪叶先生:https://www.cnblogs.com/ye888/
* @Date: 2019/9/29
* @Description: com.yrp.controller.admin
* @version: 1.0
*/
@Controller
@RequestMapping("/admin")
public class TypeController {
@Autowired
private TypeService typeServiceImpl;
/**
* 分页查询分类列表
*
* @return
*/
@GetMapping("/types")
public String types(@PageableDefault(size = 8, sort = {"id"}, direction = Sort.Direction.DESC)
Pageable pageable, Model model) {
model.addAttribute("page", typeServiceImpl.ListType(pageable));
return "admin/types";
}
/**
* 显示新增页面
*
* @return
*/
@GetMapping("/types/input")
public String showInput(Model model) {
model.addAttribute("type", new Type());
return "admin/types-input";
}
/**
* 新增分类
*
* @return
*/
@PostMapping("/types")
public String post(@Valid Type type, BindingResult result, RedirectAttributes attributes) {
//1.判断用户名是有重复
Type type1 = typeServiceImpl.getTypeByName(type.getName());
if (type1 != null) {
result.rejectValue("name", "nameError", "不能添加重复的分类");
}
//2. 后端校验用户名是否为空
if (result.hasErrors()) {
return "admin/types-input";
}
Type t = typeServiceImpl.saveType(type);
if (t == null) {
attributes.addFlashAttribute("message", "新增失败");
} else {
attributes.addFlashAttribute("message", "新增成功");
}
return "redirect:/admin/types";
}
/**
* 跳转到修改分类页面
*
* @param id
* @param model
* @return
*/
@GetMapping("/types/{id}/input")
public String editInput(@PathVariable("id") Long id, Model model) {
model.addAttribute("type", typeServiceImpl.getType(id));
return "admin/types-input";
}
/**
* 修改分类
*
* @return
*/
@PostMapping("/types/{id}")
public String editPost(@Valid Type type, BindingResult result, @PathVariable Long id, RedirectAttributes attributes) {
Type type1 = typeServiceImpl.getTypeByName(type.getName());
if (type1 != null) {
result.rejectValue("name", "nameError", "不能添加重复的分类");
}
if (result.hasErrors()) {
return "admin/types-input";
}
Type t = typeServiceImpl.updateType(id, type);
if (t == null) {
attributes.addFlashAttribute("message", "更新失败");
} else {
attributes.addFlashAttribute("message", "更新成功");
}
return "redirect:/admin/types";
}
@GetMapping("/types/{id}/delete")
public String deleteTypeById(@PathVariable("id") Long id, RedirectAttributes attributes) {
typeServiceImpl.deleteType(id);
attributes.addFlashAttribute("message", "删除成功");
return "redirect:/admin/types";
}
}

View File

@ -0,0 +1,39 @@
package cn.allms.dao;
import cn.allms.po.Blog;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
/**
* @Auther: 南迪叶先生:https://www.cnblogs.com/ye888/
* @Date: 2019/10/3
* @Description: com.yrp.dao
* @version: 1.0
*/
public interface BlogRepository extends JpaRepository<Blog, Long>, JpaSpecificationExecutor<Blog> {
@Query("select b from Blog b where b.recommend = true")
List<Blog> findTop(Pageable pageable);
@Query("select b from Blog b where b.title like ?1 or b.content like ?1")
Page<Blog> findByQuery(String query, Pageable pageable);
@Transactional
@Modifying
@Query("update Blog b set b.views= b.views+1 where b.id = ?1")
int updateViews(Long id);
@Query("select function('date_format',b.updateTime,'%Y') as year from Blog b group by function('date_format',b.updateTime,'%Y') order by year desc ")
List<String> findGroupYear();
@Query("select b from Blog b where function('date_format',b.updateTime,'%Y') = ?1")
List<Blog> findByYear(String year);
}

View File

@ -0,0 +1,20 @@
package cn.allms.dao;
import cn.allms.po.Comment;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
/**
* @Auther: 南迪叶先生:https://www.cnblogs.com/ye888/
* @Date: 2019/9/28
* @Description: com.yrp.dao
* @version: 1.0
*/
public interface CommentRepository extends JpaRepository<Comment, Long> {
List<Comment> findByBlogIdAndParentCommentNull(Long blogId, Sort sort);
}

View File

@ -0,0 +1,20 @@
package cn.allms.dao;
import cn.allms.po.Tag;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import java.util.List;
/**
* 标签
*/
public interface TagRepository extends JpaRepository<Tag, Long> {
Tag findByName(String name);
@Query("select t from Tag t")
List<Tag> findTop(Pageable pageable);
}

View File

@ -0,0 +1,30 @@
package cn.allms.dao;
import cn.allms.po.Type;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import java.util.List;
/**
* @Auther: 南迪叶先生:https://www.cnblogs.com/ye888/
* @Date: 2019/9/29
* @Description: com.yrp.dao
* @version: 1.0
*/
public interface TypeRepository extends JpaRepository<Type, Long> {
Type findByName(String name);
/**
* 查询type按照分页的方式
*
* @param pageable
* @return
*/
@Query("select t from Type t")
List<Type> findTop(Pageable pageable);
}

View File

@ -0,0 +1,21 @@
package cn.allms.dao;
import cn.allms.po.User;
import org.springframework.data.jpa.repository.JpaRepository;
/**
* @Auther: 南迪叶先生:https://www.cnblogs.com/ye888/
* @Date: 2019/9/28
* @Description: com.yrp.dao
* @version: 1.0
*/
public interface UserRepository extends JpaRepository<User, Long> {
/**
* 通过用户名和密码查询用户
*
* @param username
* @param password
* @return
*/
User findByUsernameAndPassword(String username, String password);
}

View File

@ -0,0 +1,22 @@
package cn.allms.exception;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
/**
* 博客为空的异常
*/
@ResponseStatus(HttpStatus.NOT_FOUND)
public class NotFoundException extends RuntimeException {
public NotFoundException() {
super();
}
public NotFoundException(String message) {
super(message);
}
public NotFoundException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@ -0,0 +1,40 @@
package cn.allms.handler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
@ControllerAdvice
public class ControllerExceptionHandler {
private final Logger logger = LoggerFactory.getLogger(ControllerExceptionHandler.class);
/**
* 异常处理
*
* @param request
* @param e
* @return
*/
@ExceptionHandler({Exception.class})
public ModelAndView handleException(HttpServletRequest request, Exception e) throws Exception {
logger.error("Request URL : {} , Exception : {}", request.getRequestURL(), e);
if (AnnotationUtils.findAnnotation(e.getClass(), ResponseStatus.class) != null) {
throw e;
}
ModelAndView mav = new ModelAndView();
mav.addObject("url", request.getRequestURL());
mav.addObject("exception", e);
mav.setViewName("error/error");
return mav;
}
}

View File

@ -0,0 +1,23 @@
package cn.allms.interceptor;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 登录拦截器
*/
public class LoginInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
//1. 用户未登录
if (request.getSession().getAttribute("user") == null) {
response.sendRedirect("/admin");
return false;
}
return true;
}
}

View File

@ -0,0 +1,250 @@
package cn.allms.po;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@Entity
@Table(name = "t_blog")
public class Blog {
@Id
@GeneratedValue
private Long id;
private String title;
@Basic(fetch = FetchType.LAZY)
@Lob
private String content;
private String firstPicture;
private String flag;
private Integer views;
private boolean appreciation;
private boolean shareStatement;
private boolean commentabled;
private boolean published;
private boolean recommend;
private String description;
@Transient
private String tagIds;
@Temporal(TemporalType.TIMESTAMP)
private Date createTime;
@Temporal(TemporalType.TIMESTAMP)
private Date updateTime;
@ManyToOne
private Type type;
@ManyToMany(cascade = {CascadeType.PERSIST})
private List<Tag> tags = new ArrayList<>();
@ManyToOne
private User user;
@OneToMany(mappedBy = "blog")
private List<Comment> comments = new ArrayList<>();
public void init() {
this.tagIds = tagsToIds(this.getTags());
}
//1,2,3
private String tagsToIds(List<Tag> tags) {
if (!tags.isEmpty()) {
StringBuffer ids = new StringBuffer();
boolean flag = false;
for (Tag tag : tags) {
if (flag) {
ids.append(",");
} else {
flag = true;
}
ids.append(tag.getId());
}
return ids.toString();
} else {
return tagIds;
}
}
public Blog() {
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getFirstPicture() {
return firstPicture;
}
public void setFirstPicture(String firstPicture) {
this.firstPicture = firstPicture;
}
public String getFlag() {
return flag;
}
public void setFlag(String flag) {
this.flag = flag;
}
public Integer getViews() {
return views;
}
public void setViews(Integer views) {
this.views = views;
}
public boolean isAppreciation() {
return appreciation;
}
public void setAppreciation(boolean appreciation) {
this.appreciation = appreciation;
}
public boolean isShareStatement() {
return shareStatement;
}
public void setShareStatement(boolean shareStatement) {
this.shareStatement = shareStatement;
}
public boolean isCommentabled() {
return commentabled;
}
public void setCommentabled(boolean commentabled) {
this.commentabled = commentabled;
}
public boolean isPublished() {
return published;
}
public void setPublished(boolean published) {
this.published = published;
}
public boolean isRecommend() {
return recommend;
}
public void setRecommend(boolean recommend) {
this.recommend = recommend;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public Date getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Date updateTime) {
this.updateTime = updateTime;
}
public Type getType() {
return type;
}
public void setType(Type type) {
this.type = type;
}
public List<Tag> getTags() {
return tags;
}
public void setTags(List<Tag> tags) {
this.tags = tags;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public List<Comment> getComments() {
return comments;
}
public void setComments(List<Comment> comments) {
this.comments = comments;
}
public String getTagIds() {
return tagIds;
}
public void setTagIds(String tagIds) {
this.tagIds = tagIds;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
@Override
public String toString() {
return "Blog{" +
"id=" + id +
", title='" + title + '\'' +
", content='" + content + '\'' +
", firstPicture='" + firstPicture + '\'' +
", flag='" + flag + '\'' +
", views=" + views +
", appreciation=" + appreciation +
", shareStatement=" + shareStatement +
", commentabled=" + commentabled +
", published=" + published +
", recommend=" + recommend +
", description='" + description + '\'' +
", tagIds='" + tagIds + '\'' +
", createTime=" + createTime +
", updateTime=" + updateTime +
", type=" + type +
", tags=" + tags +
", user=" + user +
", comments=" + comments +
'}';
}
}

View File

@ -0,0 +1,133 @@
package cn.allms.po;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@Entity
@Table(name = "t_comment")
public class Comment {
@Id
@GeneratedValue
private Long id;
private String nickname;
private String email;
private String content;
private String avatar;
@Temporal(TemporalType.TIMESTAMP)
private Date createTime;
@ManyToOne
private Blog blog;
@OneToMany(mappedBy = "parentComment")
private List<Comment> replyComments = new ArrayList<>();
@ManyToOne
private Comment parentComment;
private boolean adminComment;
public Comment() {
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getAvatar() {
return avatar;
}
public void setAvatar(String avatar) {
this.avatar = avatar;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public Blog getBlog() {
return blog;
}
public void setBlog(Blog blog) {
this.blog = blog;
}
public List<Comment> getReplyComments() {
return replyComments;
}
public void setReplyComments(List<Comment> replyComments) {
this.replyComments = replyComments;
}
public Comment getParentComment() {
return parentComment;
}
public void setParentComment(Comment parentComment) {
this.parentComment = parentComment;
}
public boolean isAdminComment() {
return adminComment;
}
public void setAdminComment(boolean adminComment) {
this.adminComment = adminComment;
}
@Override
public String toString() {
return "Comment{" +
"id=" + id +
", nickname='" + nickname + '\'' +
", email='" + email + '\'' +
", content='" + content + '\'' +
", avatar='" + avatar + '\'' +
", createTime=" + createTime +
", blog=" + blog +
", replyComments=" + replyComments +
", parentComment=" + parentComment +
", adminComment=" + adminComment +
'}';
}
}

View File

@ -0,0 +1,59 @@
package cn.allms.po;
import org.hibernate.validator.constraints.NotBlank;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
/**
* Created by limi on 2017/10/14.
*/
@Entity
@Table(name = "t_tag")
public class Tag {
@Id
@GeneratedValue
private Long id;
@NotBlank(message = "标签名不能为空")
private String name;
@ManyToMany(mappedBy = "tags")
private List<Blog> blogs = new ArrayList<>();
public Tag() {
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Blog> getBlogs() {
return blogs;
}
public void setBlogs(List<Blog> blogs) {
this.blogs = blogs;
}
@Override
public String toString() {
return "Tag{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}

View File

@ -0,0 +1,56 @@
package cn.allms.po;
import org.hibernate.validator.constraints.NotBlank;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
@Entity
@Table(name = "t_type")
public class Type {
@Id
@GeneratedValue
private Long id;
@NotBlank(message = "分类名不能为空")
private String name;
@OneToMany(mappedBy = "type")
private List<Blog> blogs = new ArrayList<>();
public Type() {
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Blog> getBlogs() {
return blogs;
}
public void setBlogs(List<Blog> blogs) {
this.blogs = blogs;
}
@Override
public String toString() {
return "Type{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}

View File

@ -0,0 +1,127 @@
package cn.allms.po;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@Entity
@Table(name = "t_user")
public class User {
@Id
@GeneratedValue
private Long id;
private String nickname;
private String username;
private String password;
private String email;
private String avatar;
private Integer type;
@Temporal(TemporalType.TIMESTAMP)
private Date createTime;
@Temporal(TemporalType.TIMESTAMP)
private Date updateTime;
@OneToMany(mappedBy = "user")
private List<Blog> blogs = new ArrayList<>();
public User() {
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getAvatar() {
return avatar;
}
public void setAvatar(String avatar) {
this.avatar = avatar;
}
public Integer getType() {
return type;
}
public void setType(Integer type) {
this.type = type;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public Date getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Date updateTime) {
this.updateTime = updateTime;
}
public List<Blog> getBlogs() {
return blogs;
}
public void setBlogs(List<Blog> blogs) {
this.blogs = blogs;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", nickname='" + nickname + '\'' +
", username='" + username + '\'' +
", password='" + password + '\'' +
", email='" + email + '\'' +
", avatar='" + avatar + '\'' +
", type=" + type +
", createTime=" + createTime +
", updateTime=" + updateTime +
'}';
}
}

View File

@ -0,0 +1,106 @@
package cn.allms.service;
import cn.allms.po.Blog;
import cn.allms.vo.BlogQuery;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import java.util.List;
import java.util.Map;
/**
* @Auther: 南迪叶先生:https://www.cnblogs.com/ye888/
* @Date: 2019/10/3
* @Description: com.yrp.po
* @version: 1.0
*/
public interface BlogService {
/**
* 通过id查询单条博客
*
* @param id
* @return
*/
Blog getBlog(Long id);
/**
* 通过id查询单条博客前端使用
*
* @param id
* @return
*/
Blog getAadConvertBlog(Long id);
/**
* 分页查询博客
*
* @param pageable
* @param blog
* @return
*/
Page<Blog> ListBlog(Pageable pageable, BlogQuery blog);
/**
* 分页查询显示博客列表
*
* @param pageable
* @return
*/
Page<Blog> ListBlog(Pageable pageable);
Page<Blog> ListBlog(Long tagId, Pageable pageable);
/**
* 模糊查询分页显示博客列表
*
* @param pageable
* @param query
* @return
*/
Page<Blog> ListBlog(Pageable pageable, String query);
/**
* 显示推荐的博客列表
*
* @return
*/
List<Blog> listRecommendBlogTop(Integer size);
/**
* 显示归档信息
*
* @return
*/
Map<String, List<Blog>> archiveBlog();
Long countBlog();
/**
* 修改
*
* @param blog
* @return
*/
Blog saveBlog(Blog blog);
/**
* 修改
*
* @param id
* @param blog
* @return
*/
Blog updateBlog(Long id, Blog blog);
/**
* 删除
*
* @param id
*/
void deleteBlog(Long id);
}

View File

@ -0,0 +1,29 @@
package cn.allms.service;
import cn.allms.po.Comment;
import java.util.List;
/**
* 评论
*/
public interface CommentService {
/**
* 通过id查询评论列表
*
* @param blogId
* @return
*/
List<Comment> listCommentByBlogId(Long blogId);
/**
* 保存评论信息
*
* @param comment
* @return
*/
Comment saveComment(Comment comment);
}

View File

@ -0,0 +1,73 @@
package cn.allms.service;
import cn.allms.po.Tag;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import java.util.List;
/**
* 分类管理接口
*/
public interface TagService {
/**
* 新增
*
* @return
*/
Tag saveTag(Tag tag);
/**
* 单个查询
*
* @param id
* @return
*/
Tag getTag(Long id);
/**
* 分页查询
*
* @param pageable
* @return
*/
Page<Tag> ListTag(Pageable pageable);
List<Tag> ListTagTop(Integer size);
/**
* 查询所有标签
*
* @return
*/
List<Tag> ListTag();
List<Tag> ListTag(String ids);
/**
* 修改
*
* @param id
* @param tag
* @return
*/
Tag updateTag(Long id, Tag tag);
/**
* 删除
*
* @param id
*/
void deleteTag(Long id);
/**
* 通过分类名称
*
* @return
*/
Tag getTagByName(String name);
}

View File

@ -0,0 +1,71 @@
package cn.allms.service;
import cn.allms.po.Type;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import java.util.List;
/**
* 分类管理接口
*/
public interface TypeService {
/**
* 新增
*
* @return
*/
Type saveType(Type type);
/**
* 单个查询
*
* @param id
* @return
*/
Type getType(Long id);
/**
* 分页查询
*
* @param pageable
* @return
*/
Page<Type> ListType(Pageable pageable);
/**
* 查询所有分类
*
* @return
*/
List<Type> listType();
List<Type> listTypeTop(Integer size);
/**
* 修改
*
* @param id
* @param type
* @return
*/
Type updateType(Long id, Type type);
/**
* 删除
*
* @param id
*/
void deleteType(Long id);
/**
* 通过分类名称查询分类
*
* @return
*/
Type getTypeByName(String name);
}

View File

@ -0,0 +1,21 @@
package cn.allms.service;
import cn.allms.po.User;
/**
* @Auther: 南迪叶先生:https://www.cnblogs.com/ye888/
* @Date: 2019/9/28
* @Description: com.yrp.service
* @version: 1.0
*/
public interface Userservice {
/**
* 用户登录检测
*
* @param
* @return
*/
User checkUser(String username, String password);
}

View File

@ -0,0 +1,168 @@
package cn.allms.service.impl;
import cn.allms.po.Blog;
import cn.allms.vo.BlogQuery;
import cn.allms.exception.NotFoundException;
import cn.allms.dao.BlogRepository;
import cn.allms.service.BlogService;
import cn.allms.po.Type;
import cn.allms.util.MarkdownUtils;
import cn.allms.util.MyBeanUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.persistence.criteria.*;
import java.util.*;
/**
* @Auther: 南迪叶先生:https://www.cnblogs.com/ye888/
* @Date: 2019/10/3
* @Description: com.yrp.service.impl
* @version: 1.0
*/
@Service
public class BlogServiceImpl implements BlogService {
@Autowired
private BlogRepository blogRepository;
@Override
public Blog getBlog(Long id) {
return blogRepository.findById(id).orElse(null);
}
@Override
public Blog getAadConvertBlog(Long id) {
Blog blog = blogRepository.findById(id).orElse(null);
if (blog == null) {
throw new NotFoundException("该博客不存在!");
}
Blog b = new Blog();
BeanUtils.copyProperties(blog, b);
b.setContent(MarkdownUtils.markdownToHtmlExtensions(b.getContent()));
blogRepository.updateViews(id);
return b;
}
@Override
public Page<Blog> ListBlog(Pageable pageable, BlogQuery blog) {
return blogRepository.findAll(new Specification<Blog>() {
/**
*
* @param root 要查询的对象
* @param cq 查询的一个条件容器
* @param cb 设置具体某一个条件的表达式(模糊查询)
* @return
*/
@Override
public Predicate toPredicate(Root<Blog> root, CriteriaQuery<?> cq, CriteriaBuilder cb) {
List<Predicate> predicates = new ArrayList<>();
if (!"".equals(blog.getTitle()) && blog.getTitle() != null) {
predicates.add(cb.like(root.<String>get("title"), "%" + blog.getTitle() + "%"));
}
if (blog.getTypeId() != null) {
predicates.add(cb.equal(root.<Type>get("type").get("id"), blog.getTypeId()));
}
if (blog.isRecommend()) {
predicates.add(cb.equal(root.<Boolean>get("recommend"), blog.isRecommend()));
}
cq.where(predicates.toArray(new Predicate[predicates.size()]));
return null;
}
}, pageable);
}
@Override
public Page<Blog> ListBlog(Pageable pageable) {
return blogRepository.findAll(pageable);
}
@Override
public Page<Blog> ListBlog(Long tagId, Pageable pageable) {
return blogRepository.findAll(new Specification<Blog>() {
@Override
public Predicate toPredicate(Root<Blog> root, CriteriaQuery<?> cq, CriteriaBuilder cb) {
Join join = root.join("tags");
return cb.equal(join.get("id"), tagId);
}
}, pageable);
}
@Override
public Page<Blog> ListBlog(Pageable pageable, String query) {
return blogRepository.findByQuery(query, pageable);
}
@Override
public List<Blog> listRecommendBlogTop(Integer size) {
Sort sort = Sort.by(Sort.Direction.DESC, "updateTime");
Pageable pageable = PageRequest.of(0, size, sort);
return blogRepository.findTop(pageable);
}
@Override
public Map<String, List<Blog>> archiveBlog() {
List<String> years = blogRepository.findGroupYear();
Map<String, List<Blog>> map = new HashMap<>();
for (String year : years) {
map.put(year, blogRepository.findByYear(year));
}
return map;
}
@Override
public Long countBlog() {
return blogRepository.count();
}
/**
* 发布博客
* (如何是新增博客,需要添加创建时间和修改时间以及初始化浏览量view = 0)
* (如果是修改博客,需要添加修改时间)
*
* @param blog
* @return
*/
@Transactional
@Override
public Blog saveBlog(Blog blog) {
//博客新增
return blogRepository.save(blog);
}
/**
* 所谓的修改其实是查询+保存
*
* @param id
* @param blog
* @return
*/
@Transactional
@Override
public Blog updateBlog(Long id, Blog blog) {
Blog b = blogRepository.findById(id).orElse(null);
//判断是否存在这条数据
if (b == null) {
throw new NotFoundException("该博客不存在");
}
b.setUpdateTime(new Date());
//copy有值属性 不覆盖
BeanUtils.copyProperties(blog, b, MyBeanUtils.getNullPropertyNames(blog));
return blogRepository.save(b);
}
@Transactional(rollbackFor = Exception.class)
@Override
public void deleteBlog(Long id) {
blogRepository.deleteById(id);
}
}

View File

@ -0,0 +1,50 @@
package cn.allms.service.impl;
import cn.allms.util.CommentUtil;
import cn.allms.dao.CommentRepository;
import cn.allms.po.Comment;
import cn.allms.service.CommentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Date;
import java.util.List;
/**
* @Auther: 南迪叶先生:https://www.cnblogs.com/ye888/
* @Date: 2019/10/10
* @Description: com.yrp.service.impl
* @version: 1.0
*/
@Service
public class CommentServiceImpl implements CommentService {
@Autowired
private CommentRepository commentRepository;
@Override
public List<Comment> listCommentByBlogId(Long blogId) {
Sort sort = Sort.by("createTime");
List<Comment> comments = commentRepository.findByBlogIdAndParentCommentNull(blogId, sort);
return CommentUtil.eachComment(comments);
}
@Transactional
@Override
public Comment saveComment(Comment comment) {
//回复: 如果有父级,需要将父级set进来,之后在保存
Long parentCommentId = comment.getParentComment().getId();
System.out.println(parentCommentId);
if (parentCommentId != -1) {
comment.setParentComment(commentRepository.findById(parentCommentId).orElse(null));
} else {
//发布评论:
comment.setParentComment(null);
}
comment.setCreateTime(new Date());
return commentRepository.save(comment);
}
}

View File

@ -0,0 +1,96 @@
package cn.allms.service.impl;
import cn.allms.po.Tag;
import cn.allms.service.TagService;
import cn.allms.exception.NotFoundException;
import cn.allms.dao.TagRepository;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
/**
* 标签实现
*/
@Service
public class TagServiceImpl implements TagService {
@Autowired
private TagRepository tagRepository;
@Transactional
@Override
public Tag saveTag(Tag tag) {
return tagRepository.save(tag);
}
@Override
public Tag getTag(Long id) {
return tagRepository.findById(id).orElse(null);
}
@Override
public Page<Tag> ListTag(Pageable pageable) {
return tagRepository.findAll(pageable);
}
@Override
public List<Tag> ListTagTop(Integer size) {
Sort sort = Sort.by(Sort.Direction.DESC, "blogs.size");
Pageable pageable = PageRequest.of(0, size, sort);
return tagRepository.findTop(pageable);
}
@Override
public List<Tag> ListTag() {
return tagRepository.findAll();
}
@Override
public List<Tag> ListTag(String ids) {
// todo
return tagRepository.findAll();
}
@Override
public Tag updateTag(Long id, Tag tag) {
Tag t = tagRepository.findById(id).orElse(null);
if (t == null) {
throw new NotFoundException("不存在该标签");
}
BeanUtils.copyProperties(tag, t);
return tagRepository.save(t);
}
@Override
public void deleteTag(Long id) {
tagRepository.deleteById(id);
}
@Override
public Tag getTagByName(String name) {
return tagRepository.findByName(name);
}
/*将字符串转化为集合*/
private List<Long> convertToList(String ids) {
List<Long> list = new ArrayList<>();
if (!"".equals(ids) && ids != null) {
String[] idarray = ids.split(",");
for (int i = 0; i < idarray.length; i++) {
list.add(new Long(idarray[i]));
}
}
return list;
}
}

View File

@ -0,0 +1,83 @@
package cn.allms.service.impl;
import cn.allms.exception.NotFoundException;
import cn.allms.dao.TypeRepository;
import cn.allms.po.Type;
import cn.allms.service.TypeService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
/**
* @Auther: 南迪叶先生:https://www.cnblogs.com/ye888/
* @Date: 2019/9/29
* @Description: com.yrp.service.impl
* @version: 1.0
*/
@Service
public class TypeServiceImpl implements TypeService {
@Autowired
private TypeRepository typeRepository;
@Transactional
@Override
public Type saveType(Type type) {
return typeRepository.save(type);
}
@Transactional
@Override
public Type getType(Long id) {
return typeRepository.findById(id).orElse(null);
}
@Transactional
@Override
public Page<Type> ListType(Pageable pageable) {
return typeRepository.findAll(pageable);
}
@Override
public List<Type> listType() {
return typeRepository.findAll();
}
@Override
public List<Type> listTypeTop(Integer size) {
Sort sort = Sort.by(Sort.Direction.DESC, "blogs.size");
Pageable pageable = PageRequest.of(0, size, sort);
return typeRepository.findTop(pageable);
}
@Transactional
@Override
public Type updateType(Long id, Type type) {
Type t = typeRepository.findById(id).orElse(null);
if (t == null) {
throw new NotFoundException("不存在该类型");
}
BeanUtils.copyProperties(type, t);
return typeRepository.save(t);
}
@Transactional(rollbackFor = Exception.class)
@Override
public void deleteType(Long id) {
typeRepository.deleteById(id);
}
@Override
public Type getTypeByName(String name) {
return typeRepository.findByName(name);
}
}

View File

@ -0,0 +1,30 @@
package cn.allms.service.impl;
import cn.allms.po.User;
import cn.allms.dao.UserRepository;
import cn.allms.service.Userservice;
import cn.allms.util.MD5Utils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @Auther: 南迪叶先生:https://www.cnblogs.com/ye888/
* @Date: 2019/9/28
* @Description: com.yrp.service.serviceimpl
* @version: 1.0
*/
@Service
public class UserServiceImpl implements Userservice {
@Autowired
private UserRepository userRepository;
@Override
public User checkUser(String username, String password) {
System.out.println(username);
System.out.println(password);
User user = userRepository.findByUsernameAndPassword(username, MD5Utils.code(password));
System.out.println(MD5Utils.code(password));
return user;
}
}

View File

@ -0,0 +1,71 @@
package cn.allms.util;
import cn.allms.po.Comment;
import org.springframework.beans.BeanUtils;
import java.util.ArrayList;
import java.util.List;
/**
* 评论格式化工具
*/
public class CommentUtil {
/**
* 循环每个顶级的评论节点
*
* @param comments
* @return
*/
public static List<Comment> eachComment(List<Comment> comments) {
List<Comment> commentsView = new ArrayList<>();
for (Comment comment : comments) {
Comment c = new Comment();
BeanUtils.copyProperties(comment, c);
commentsView.add(c);
}
//合并评论的各层子代到第一级子代集合中
combineChildren(commentsView);
return commentsView;
}
/**
* @param comments root根节点blog不为空的对象集合
* @return
*/
public static void combineChildren(List<Comment> comments) {
for (Comment comment : comments) {
List<Comment> replys1 = comment.getReplyComments();
for (Comment reply1 : replys1) {
//循环迭代找出子代存放在tempReplys中
recursively(reply1);
}
//修改顶级节点的reply集合为迭代处理后的集合
comment.setReplyComments(tempReplys);
//清除临时存放区
tempReplys = new ArrayList<>();
}
}
//存放迭代找出的所有子代的集合
public static List<Comment> tempReplys = new ArrayList<>();
/**
* 递归迭代剥洋葱
*
* @param comment 被迭代的对象
* @return
*/
public static void recursively(Comment comment) {
tempReplys.add(comment);//顶节点添加到临时存放集合
if (comment.getReplyComments().size() > 0) {
List<Comment> replys = comment.getReplyComments();
for (Comment reply : replys) {
tempReplys.add(reply);
if (reply.getReplyComments().size() > 0) {
recursively(reply);
}
}
}
}
}

View File

@ -0,0 +1,47 @@
package cn.allms.util;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
* Created by limi on 2017/10/15.
*/
public class MD5Utils {
/**
* MD5加密类
*
* @param str 要加密的字符串
* @return 加密后的字符串
*/
public static String code(String str) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(str.getBytes());
byte[] byteDigest = md.digest();
int i;
StringBuffer buf = new StringBuffer("");
for (int offset = 0; offset < byteDigest.length; offset++) {
i = byteDigest[offset];
if (i < 0)
i += 256;
if (i < 16)
buf.append("0");
buf.append(Integer.toHexString(i));
}
//32位加密
return buf.toString();
// 16位的加密
//return buf.toString().substring(8, 24);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
}
}
public static void main(String[] args) {
System.out.println(code("123"));
}
}

View File

@ -0,0 +1,89 @@
package cn.allms.util;
import org.commonmark.Extension;
import org.commonmark.ext.gfm.tables.TableBlock;
import org.commonmark.ext.gfm.tables.TablesExtension;
import org.commonmark.ext.heading.anchor.HeadingAnchorExtension;
import org.commonmark.node.Link;
import org.commonmark.node.Node;
import org.commonmark.parser.Parser;
import org.commonmark.renderer.html.AttributeProvider;
import org.commonmark.renderer.html.AttributeProviderContext;
import org.commonmark.renderer.html.AttributeProviderFactory;
import org.commonmark.renderer.html.HtmlRenderer;
import java.util.*;
/**
* Created by limi on 2017/10/22.
*/
public class MarkdownUtils {
/**
* markdown格式转换成HTML格式
*
* @param markdown
* @return
*/
public static String markdownToHtml(String markdown) {
Parser parser = Parser.builder().build();
Node document = parser.parse(markdown);
HtmlRenderer renderer = HtmlRenderer.builder().build();
return renderer.render(document);
}
/**
* 增加扩展[标题锚点表格生成]
* Markdown转换成HTML
*
* @param markdown
* @return
*/
public static String markdownToHtmlExtensions(String markdown) {
//h标题生成id
Set<Extension> headingAnchorExtensions = Collections.singleton(HeadingAnchorExtension.create());
//转换table的HTML
List<Extension> tableExtension = Arrays.asList(TablesExtension.create());
Parser parser = Parser.builder()
.extensions(tableExtension)
.build();
Node document = parser.parse(markdown);
HtmlRenderer renderer = HtmlRenderer.builder()
.extensions(headingAnchorExtensions)
.extensions(tableExtension)
.attributeProviderFactory(new AttributeProviderFactory() {
public AttributeProvider create(AttributeProviderContext context) {
return new CustomAttributeProvider();
}
})
.build();
return renderer.render(document);
}
/**
* 处理标签的属性
*/
static class CustomAttributeProvider implements AttributeProvider {
@Override
public void setAttributes(Node node, String tagName, Map<String, String> attributes) {
//改变a标签的target属性为_blank
if (node instanceof Link) {
attributes.put("target", "_blank");
}
if (node instanceof TableBlock) {
attributes.put("class", "ui celled table");
}
}
}
public static void main(String[] args) {
String table = "| hello | hi | 哈哈哈 |\n" +
"| ----- | ---- | ----- |\n" +
"| 斯维尔多 | 士大夫 | f啊 |\n" +
"| 阿什顿发 | 非固定杆 | 撒阿什顿发 |\n" +
"\n";
String a = "[imCoding 爱编程](http://www.lirenmi.cn)";
System.out.println(markdownToHtmlExtensions(a));
}
}

View File

@ -0,0 +1,35 @@
package cn.allms.util;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import java.beans.PropertyDescriptor;
import java.util.ArrayList;
import java.util.List;
/**
* Created by limi on 2017/10/21.
*/
public class MyBeanUtils {
/**
* 获取所有的属性值为空属性名数组
*
* @param source
* @return
*/
public static String[] getNullPropertyNames(Object source) {
BeanWrapper beanWrapper = new BeanWrapperImpl(source);
PropertyDescriptor[] pds = beanWrapper.getPropertyDescriptors();
List<String> nullPropertyNames = new ArrayList<>();
for (PropertyDescriptor pd : pds) {
String propertyName = pd.getName();
if (beanWrapper.getPropertyValue(propertyName) == null) {
nullPropertyNames.add(propertyName);
}
}
return nullPropertyNames.toArray(new String[nullPropertyNames.size()]);
}
}

View File

@ -0,0 +1,49 @@
package cn.allms.vo;
/**
* @Auther: 南迪叶先生:https://www.cnblogs.com/ye888/
* @Date: 2019/10/3
* @Description: com.yrp.vo
* @version: 1.0
*/
public class BlogQuery {
/**
* 标题
*/
private String title;
/**
* 分类ID
*/
private Long typeId;
/**
* 是否提交
*/
private boolean recommend;
public BlogQuery() {
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public Long getTypeId() {
return typeId;
}
public void setTypeId(Long typeId) {
this.typeId = typeId;
}
public boolean isRecommend() {
return recommend;
}
public void setRecommend(boolean recommend) {
this.recommend = recommend;
}
}

View File

@ -1,4 +1,24 @@
# 项目端口
server:
port: 9420
servlet:
context-path: /api
#数据库驱动mysql
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/mx-blog?useUnicode=true&characterEncoding=utf-8&useSSL=false
username: root
password: 123456
jpa:
hibernate:
ddl-auto: create
show-sql: false
#日志打印
logging:
level:
root: info
cn.allms: debug
file:
name: log/blog-dev.log
path: classpath:logback/logback-spring.xml

View File

@ -0,0 +1,23 @@
#thymeleaf模板
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/blog?useUnicode=true&characterEncoding=utf-8
username: root
password: 123456
jpa:
hibernate:
ddl-auto: none
show-sql: true
#日志打印
logging:
level:
root: warn
com.yrp: info
file:
name: log/-blogpro.log
# 服务端口
server:
port: 9420

View File

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

View File

@ -1,3 +1,19 @@
#thymeleaf模板
spring:
thymeleaf:
mode: HTML
profiles:
active: dev
messages:
basename: i18n/message
#博客头像
comment:
v2exUrl: https://sdn.geekzu.org/avatar/
avatar: /images/avatar.png
# 随机图片
random:
pictures:
- "https://api.rainss.cn/random.php?t="
- "https://api.rainss.cn/acgimgurl/acgurl.php?t="

View File

@ -0,0 +1,14 @@
###########################\u524D\u53F0\u9875\u9762########################
# \u9876\u90E8\u5BFC\u822A\u680F\u4FE1\u606F
index.home=\u9996\u9875
index.type=\u5206\u7C7B
index.tag=\u6807\u7B7E
index.archive=\u5F52\u6863
index.aboutme=\u7AD9\u957F\u4FE1\u606F
#\u5E95\u90E8\u5BFC\u822A\u680F\u4FE1\u606F
index.email=\u90AE\u7BB1:qfmx520@163.com
index.qq=QQ:745719461
index.about=\u4F5C\u8005\u662F\u4E00\u540Dspringboot\u7231\u597D\u8005,\u662F\u4E00\u540D\u6B63\u5728\u52AA\u529B\u594B\u6597\u7684\u5B66\u751F,\u4E3B\u8981\u5B66\u4E60JavaEE\u5F00\u53D1,Web\u524D\u7AEF\u7B49\u6280\u672F.\u4F5C\u8005\u662F\u4E2A\u559C\u6B22\u65C5\u884C\u7684\u5B69\u5B50,\u76EE\u6807\u662F\u6E38\u904D\u5168\u4E16\u754C\uFF01
index.copyright=copy @ 2020 - 2021 \u4f5c\u8005:\u6d45\u67ab\u6c90\u96ea \u6b64\u7ad9\u4ec5\u4f9b\u4e2a\u4eba\u6240\u6709 \u5982\u6709\u7591\u95ee\u8054\u7cfb\u4f5c\u8005
#\u4E8C\u7EF4\u7801\u626B\u7801\u9605\u8BFB\u5730\u5740
blog.serurl=127.0.0.1:80

View File

@ -0,0 +1,14 @@
###########################\u524D\u53F0\u9875\u9762########################
# \u9876\u90E8\u5BFC\u822A\u680F\u4FE1\u606F
index.home=Home
index.type=Type
index.tag=Tag
index.archive=Archive
index.aboutme=About Me
#\u5E95\u90E8\u5BFC\u822A\u680F\u4FE1\u606F
index.email=Email:qfmx520@163.com
index.qq=QQ:745719461
index.about=The author is a springboot enthusiast, a struggling student who mainly studies JavaEE development,Web front end and other technologies.
index.copyright=copy @ 2020 - 2021 \u4f5c\u8005:\u6d45\u67ab\u6c90\u96ea \u6b64\u7ad9\u4ec5\u4f9b\u4e2a\u4eba\u6240\u6709 \u5982\u6709\u7591\u95ee\u8054\u7cfb\u4f5c\u8005
#\u4E8C\u7EF4\u7801\u626B\u7801\u9605\u8BFB\u5730\u5740
blog.serurl=172.20.10.6:8080

View File

@ -0,0 +1,14 @@
###########################\u524D\u53F0\u9875\u9762########################
# \u9876\u90E8\u5BFC\u822A\u680F\u4FE1\u606F
index.home=\u9996\u9875
index.type=\u5206\u7C7B
index.tag=\u6807\u7B7E
index.archive=\u5F52\u6863
index.aboutme=\u7AD9\u957F\u4FE1\u606F
#\u5E95\u90E8\u5BFC\u822A\u680F\u4FE1\u606F
index.email=\u90AE\u7BB1:qfmx520@163.com
index.qq=QQ:745719461
index.about=\u4F5C\u8005\u662F\u4E00\u540Dspringboot\u7231\u597D\u8005,\u662F\u4E00\u540D\u6B63\u5728\u52AA\u529B\u594B\u6597\u7684\u5B66\u751F,\u4E3B\u8981\u5B66\u4E60JavaEE\u5F00\u53D1,Web\u524D\u7AEF\u7B49\u6280\u672F.\u4F5C\u8005\u662F\u4E2A\u559C\u6B22\u65C5\u884C\u7684\u5B69\u5B50,\u76EE\u6807\u662F\u6E38\u904D\u5168\u4E16\u754C\uFF01
index.copyright=copy @ 2020 - 2021 \u4f5c\u8005:\u6d45\u67ab\u6c90\u96ea \u6b64\u7ad9\u4ec5\u4f9b\u4e2a\u4eba\u6240\u6709 \u5982\u6709\u7591\u95ee\u8054\u7cfb\u4f5c\u8005
#\u4E8C\u7EF4\u7801\u626B\u7801\u9605\u8BFB\u5730\u5740
blog.serurl=172.20.10.6:8080

View File

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<!--包含Spring boot对logback日志的默认配置-->
<include resource="org/springframework/boot/logging/logback/defaults.xml" />
<property name="LOG_FILE" value="${LOG_FILE:-${LOG_PATH:-${LOG_TEMP:-${java.io.tmpdir:-/tmp}}}/spring.log}"/>
<include resource="org/springframework/boot/logging/logback/console-appender.xml" />
<!--重写了Spring Boot框架 org/springframework/boot/logging/logback/file-appender.xml 配置-->
<appender name="TIME_FILE"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
</encoder>
<file>${LOG_FILE}</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_FILE}.%d{yyyy-MM-dd}.%i</fileNamePattern>
<!--保留历史日志一个月的时间-->
<maxHistory>30</maxHistory>
<!--
Spring Boot默认情况下日志文件10M时会切分日志文件,这样设置日志文件会在100M时切分日志
-->
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>10MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
</appender>
<root level="INFO">
<appender-ref ref="CONSOLE" />
<appender-ref ref="TIME_FILE" />
</root>
</configuration>
<!--
1、继承Spring boot logback设置可以在appliaction.yml或者application.properties设置logging.*属性)
2、重写了默认配置设置日志文件大小在100MB时按日期切分日志切分后目录
my.2017-08-01.0 80MB
my.2017-08-01.1 10MB
my.2017-08-02.0 56MB
my.2017-08-03.0 53MB
......
-->

3625
src/main/resources/static/css/animate.css vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,196 @@
body{
/*background: url("../images/leaves.png");*/
background: url("../images/bg.png");
/*background: url("../images/green_dust_scratch_@2X.png");*/
/*background: url("../images/dust_scratches.png");*/
/*background: url("../images/gravel.png");*/
}
/**********内边距5个级别*********/
.m-padding-lr-responsive{
padding-left: 4em !important;
padding-right: 4em !important;
}
.m-padded-mini{
padding:0.2em !important;
}
.m-padded-tiny{
padding:0.3em !important;
}
.m-padded{
padding:1em !important;
}
.m-padded-tb-mini{
padding-top: 0.2em !important;
padding-bottom: 0.2em !important;
}
.m-padded-tb-tiny{
padding-top: 0.3em !important;
padding-bottom: 0.3em !important;
}
.m-padded-tb-small{
padding-top: 0.5em !important;
padding-bottom: 0.5em !important;
}
.m-padded-tb{
padding-top: 1em !important;
padding-bottom: 1em !important;
}
.m-padded-tb-large{
padding-top: 2em !important;
padding-bottom: 2em !important;
}
.m-padded-tb-big{
padding-top: 3em !important;
padding-bottom: 3em !important;
}
.m-padded-tb-huge{
padding-top: 4em !important;
padding-bottom: 4em !important;
}
.m-padded-tb-massive{
padding-top: 5em !important;
padding-bottom: 5em !important;
}
/**********外边距5个级别*********/
.m-margin-tb-tiny{
margin-top: 0.3em !important;
margin-bottom: 0.3em !important;
}
.m-margin-top-small{
margin-top: 0.5em !important;
}
.m-margin-top{
maring-top: 1em !important;
}
.m-margin-right{
margin-right: 1em !important;
}
.m-margin-left{
margin-left: 1em !important;
}
.m-margin-left-big{
margin-left: 3em !important;
}
.m-margin-top-large{
margin-top: 2em !important;
}
.m-margin{
margin: 1em 1em !important;
}
.m-margin-bottom-mini{
margin-bottom: 0.2em !important;
}
.m-margin-bottom-small{
margin-bottom: 0.5em !important;
}
/**********Test**********/
/*字体粗细*/
.m-text-thin{
font-weight: 300 !important;
}
/*字间距*/
.m-text-spaced{
letter-spacing: 1px !important;
}
/*行间距*/
.m-text-lined{
line-height: 1.8 !important;
}
/*字体样式组合*/
.m-text{
font-weight: 300 !important;
letter-spacing: 1px !important;
line-height: 1.8 !important;
}
/*设置字体颜色*/
.m-black{
color: #333 !important;
}
.m-teal{
color: #00B5AD !important;
}
/*透明度*/
.m-opacity-min{
opacity: 0.8 !important;
}
/*透明度*/
.m-opacity-tiny{
opacity: 0.6 !important;
}
/******* display*******/
.m-inline-blok{
display: inline-block !important;
}
/********position****/
.m-right-top{
position: absolute;
top: 0;
right: 0;
}
.m-fixed{
/*绝对定位*/
position: fixed !important;
z-index: 10 !important;
}
.m-right-button{
bottom: 0 !important;
right: 0 !important;
}
.m-container{
max-width: 72em !important;
margin: auto !important;
}
.m-container-small{
max-width: 60em !important;
margin: auto !important;
}
/***mobile***/
.m-mobile-show{
display: none !important;
}
.m-mobile-wide{
}
/***shadow****/
.m-shadow-small{
/*适配浏览器*/
-webkit-box-shadow:0 4px 8px rgba(0,0,0,0.2)!important;
box-shadow: 0 4px 8px rgba(0,0,0,0.2)!important;
}
/*手机端*/
@media screen and (max-width:768px){
.m-mobile-hide{
display: none !important;
}
.m-mobile-show{
display: block !important;
}
.m-padding-lr-responsive{
padding-left: 0 !important;
padding-right: 0 !important;
}
.m-mobile-clear{
padding-left: 0 !important;
padding-right: 0 !important;
}
.m-mobile-wide{
width: 100%;
}
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,335 @@
@charset "utf-8";
.typo p {
font-size: 16px;
font-weight: 300;
line-height: 1.8;
text-align: justify;
}
.typo li {
font-weight: 300;
padding: 4px 0;
}
/* 重设 HTML5 标签, IE 需要在 js 中 createElement(TAG) */
.typo article, aside, details, figcaption, figure, footer, header, menu, nav, section {
display: block;
}
/* HTML5 媒体文件跟 img 保持一致 */
.typo audio, canvas, video {
display: inline-block;
}
/* 要注意表单元素并不继承父级 font 的问题 */
.typo button, input, select, textarea {
font: 300 1em/1.8 PingFang SC, Lantinghei SC, Microsoft Yahei, Hiragino Sans GB, Microsoft Sans Serif, WenQuanYi Micro Hei, sans-serif;
}
.typo button::-moz-focus-inner,
.typo input::-moz-focus-inner {
padding: 0;
border: 0;
}
/* 去掉各Table cell 的边距并让其边重合 */
.typo table {
border-collapse: collapse;
border-spacing: 0;
}
/* 去除默认边框 */
.typo fieldset, img {
border: 0;
}
/* 块/段落引用 */
.typo blockquote {
position: relative;
color: #999;
font-weight: 400;
border-left: 1px solid #1abc9c;
padding-left: 1em;
margin: 1em 3em 1em 2em;
}
@media only screen and ( max-width: 640px ) {
.typo blockquote {
margin: 1em 0;
}
}
/* Firefox 以外,元素没有下划线,需添加 */
.typo acronym, abbr {
border-bottom: 1px dotted;
font-variant: normal;
}
/* 添加鼠标问号,进一步确保应用的语义是正确的(要知道,交互他们也有洁癖,如果你不去掉,那得多花点口舌) */
.typo abbr {
cursor: help;
}
/* 一致的 del 样式 */
.typo del {
text-decoration: line-through;
}
.typo address, caption, cite, code, dfn, em, th, var {
font-style: normal;
font-weight: 400;
}
/* 去掉列表前的标识, li 会继承,大部分网站通常用列表来很多内容,所以应该当去 */
.typo ul, ol {
list-style: none;
}
/* 对齐是排版最重要的因素, 别让什么都居中 */
.typo caption, th {
text-align: left;
}
.typo q:before,.typo q:after {
content: '';
}
/* 统一上标和下标 */
.typo sub,.typo sup {
font-size: 75%;
line-height: 0;
position: relative;
}
.typo :root sub,.typo :root sup {
vertical-align: baseline; /* for ie9 and other modern browsers */
}
.typo sup {
top: -0.5em;
}
.typo sub {
bottom: -0.25em;
}
/* 让链接在 hover 状态下显示下划线 */
.typo a {
color: #1abc9c;
}
.typo a:hover {
text-decoration: underline;
}
.typo a {
border-bottom: 1px solid #1abc9c;
}
.typo a:hover {
border-bottom-color: #555;
color: #555;
text-decoration: none;
}
/* 默认不显示下划线,保持页面简洁 */
.typo ins,.typo a {
text-decoration: none;
}
/* 专名号虽然 u 已经重回 html5 Draft但在所有浏览器中都是可以使用的
* 要做到更好向后兼容的话添加 class="typo-u" 来显示专名号
* 关于 <u> 标签http://www.whatwg.org/specs/web-apps/current-work/multipage/text-level-semantics.html#the-u-element
* 被放弃的是 4之前一直搞错 http://www.w3.org/TR/html401/appendix/changes.html#idx-deprecated
* 一篇关于 <u> 标签的很好文章http://html5doctor.com/u-element/
*/
.typo u,.typo .typo-u {
text-decoration: underline;
}
/* 标记,类似于手写的荧光笔的作用 */
.typo mark {
background: #fffdd1;
border-bottom: 1px solid #ffedce;
padding: 2px;
margin: 0 5px;
}
/* 代码片断 */
.typo pre,.typo code,.typo pre tt {
font-family: Courier, 'Courier New', monospace;
}
.typo pre {
background: #f8f8f8;
border: 1px solid #ddd;
padding: 1em 1.5em;
display: block;
-webkit-overflow-scrolling: touch;
}
/* 一致化 horizontal rule */
.typo hr {
border: none;
border-bottom: 1px solid #cfcfcf;
margin-bottom: 0.8em;
height: 10px;
}
/* 底部印刷体、版本等标记 */
.typo small,.typo .typo-small,
/* 图片说明 */
.typo figcaption {
font-size: 0.9em;
color: #888;
}
.typo strong,.typo b {
font-weight: bold;
color: #000;
}
/* 可拖动文件添加拖动手势 */
.typo [draggable] {
cursor: move;
}
.typo .clearfix:before,.typo .clearfix:after {
content: "";
display: table;
}
.typo .clearfix:after {
clear: both;
}
.typo .clearfix {
zoom: 1;
}
/* 强制文本换行 */
.typo .textwrap,.typo .textwrap td,.typo .textwrap th {
word-wrap: break-word;
word-break: break-all;
}
.typo .textwrap-table {
table-layout: fixed;
}
/* 提供 serif 版本的字体设置: iOS 下中文自动 fallback 到 sans-serif */
.typo .serif {
font-family: Palatino, Optima, Georgia, serif;
}
/* 保证块/段落之间的空白隔行 */
.typo p, .typo pre, .typo ul, .typo ol, .typo dl, .typo form, .typo hr, .typo table,
.typo-p, .typo-pre, .typo-ul, .typo-ol, .typo-dl, .typo-form, .typo-hr, .typo-table, blockquote {
margin-bottom: 1.2em
}
.typo h1,.typo h2,.typo h3,.typo h4,.typo h5,.typo h6 {
font-family: PingFang SC, Verdana, Helvetica Neue, Microsoft Yahei, Hiragino Sans GB, Microsoft Sans Serif, WenQuanYi Micro Hei, sans-serif;
font-weight: 100;
color: #000;
line-height: 1.35;
}
/* 标题应该更贴紧内容并与其他块区分margin 值要相应做优化 */
.typo h1, .typo h2, .typo h3, .typo h4, .typo h5, .typo h6,
.typo-h1, .typo-h2, .typo-h3, .typo-h4, .typo-h5, .typo-h6 {
margin-top: 1.2em;
margin-bottom: 0.6em;
line-height: 1.35;
}
.typo h1, .typo-h1 {
font-size: 2em;
}
.typo h2, .typo-h2 {
font-size: 1.8em;
}
.typo h3, .typo-h3 {
font-size: 1.6em;
}
.typo h4, .typo-h4 {
font-size: 1.4em;
}
.typo h5, .typo h6, .typo-h5, .typo-h6 {
font-size: 1.2em;
}
/* 在文章中,应该还原 ul 和 ol 的样式 */
.typo ul, .typo-ul {
margin-left: 1.3em;
list-style: disc;
}
.typo ol, .typo-ol {
list-style: decimal;
margin-left: 1.9em;
}
.typo li ul, .typo li ol, .typo-ul ul, .typo-ul ol, .typo-ol ul, .typo-ol ol {
margin-bottom: 0.8em;
margin-left: 2em;
}
.typo li ul, .typo-ul ul, .typo-ol ul {
list-style: circle;
}
/* 同 ul/ol在文章中应用 table 基本格式 */
.typo table th, .typo table td, .typo-table th, .typo-table td, .typo table caption {
border: 1px solid #ddd;
padding: 0.5em 1em;
color: #666;
}
.typo table th, .typo-table th {
background: #fbfbfb;
}
.typo table thead th, .typo-table thead th {
background: #f1f1f1;
}
.typo table caption {
border-bottom: none;
}
/* 去除 webkit 中 input 和 textarea 的默认样式 */
.typo-input, .typo-textarea {
-webkit-appearance: none;
border-radius: 0;
}
.typo-em, .typo em, legend, caption {
color: #000;
font-weight: inherit;
}
/* 着重号只能在少量少于100个字符且全是全角字符的情况下使用 */
.typo-em {
position: relative;
}
.typo-em:after {
position: absolute;
top: 0.65em;
left: 0;
width: 100%;
overflow: hidden;
white-space: nowrap;
content: "・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・";
}
/* Responsive images */
.typo img {
max-width: 100%;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 425 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,98 @@
/*
* Editor.md
*
* @file editormd.logo.css
* @version v1.5.0
* @description Open source online markdown editor.
* @license MIT License
* @author Pandao
* {@link https://github.com/pandao/editor.md}
* @updateTime 2015-06-09
*/
/*! prefixes.scss v0.1.0 | Author: Pandao | https://github.com/pandao/prefixes.scss | MIT license | Copyright (c) 2015 */
@font-face {
font-family: 'editormd-logo';
src: url("../fonts/editormd-logo.eot?-5y8q6h");
src: url(".../fonts/editormd-logo.eot?#iefix-5y8q6h") format("embedded-opentype"), url("../fonts/editormd-logo.woff?-5y8q6h") format("woff"), url("../fonts/editormd-logo.ttf?-5y8q6h") format("truetype"), url("../fonts/editormd-logo.svg?-5y8q6h#icomoon") format("svg");
font-weight: normal;
font-style: normal;
}
.editormd-logo,
.editormd-logo-1x,
.editormd-logo-2x,
.editormd-logo-3x,
.editormd-logo-4x,
.editormd-logo-5x,
.editormd-logo-6x,
.editormd-logo-7x,
.editormd-logo-8x {
font-family: 'editormd-logo';
speak: none;
font-style: normal;
font-weight: normal;
font-variant: normal;
text-transform: none;
font-size: inherit;
line-height: 1;
display: inline-block;
text-rendering: auto;
vertical-align: inherit;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.editormd-logo:before,
.editormd-logo-1x:before,
.editormd-logo-2x:before,
.editormd-logo-3x:before,
.editormd-logo-4x:before,
.editormd-logo-5x:before,
.editormd-logo-6x:before,
.editormd-logo-7x:before,
.editormd-logo-8x:before {
content: "\e1987";
/*
HTML Entity &#xe1987;
example: <span class="editormd-logo">&#xe1987;</span>
*/
}
.editormd-logo-1x {
font-size: 1em;
}
.editormd-logo-lg {
font-size: 1.2em;
}
.editormd-logo-2x {
font-size: 2em;
}
.editormd-logo-3x {
font-size: 3em;
}
.editormd-logo-4x {
font-size: 4em;
}
.editormd-logo-5x {
font-size: 5em;
}
.editormd-logo-6x {
font-size: 6em;
}
.editormd-logo-7x {
font-size: 7em;
}
.editormd-logo-8x {
font-size: 8em;
}
.editormd-logo-color {
color: #2196F3;
}

View File

@ -0,0 +1,2 @@
/*! Editor.md v1.5.0 | editormd.logo.min.css | Open source online markdown editor. | MIT License | By: Pandao | https://github.com/pandao/editor.md | 2015-06-09 */
/*! prefixes.scss v0.1.0 | Author: Pandao | https://github.com/pandao/prefixes.scss | MIT license | Copyright (c) 2015 */@font-face{font-family:editormd-logo;src:url(../fonts/editormd-logo.eot?-5y8q6h);src:url(.../fonts/editormd-logo.eot?#iefix-5y8q6h)format("embedded-opentype"),url(../fonts/editormd-logo.woff?-5y8q6h)format("woff"),url(../fonts/editormd-logo.ttf?-5y8q6h)format("truetype"),url(../fonts/editormd-logo.svg?-5y8q6h#icomoon)format("svg");font-weight:400;font-style:normal}.editormd-logo,.editormd-logo-1x,.editormd-logo-2x,.editormd-logo-3x,.editormd-logo-4x,.editormd-logo-5x,.editormd-logo-6x,.editormd-logo-7x,.editormd-logo-8x{font-family:editormd-logo;speak:none;font-style:normal;font-weight:400;font-variant:normal;text-transform:none;font-size:inherit;line-height:1;display:inline-block;text-rendering:auto;vertical-align:inherit;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.editormd-logo-1x:before,.editormd-logo-2x:before,.editormd-logo-3x:before,.editormd-logo-4x:before,.editormd-logo-5x:before,.editormd-logo-6x:before,.editormd-logo-7x:before,.editormd-logo-8x:before,.editormd-logo:before{content:"\e1987"}.editormd-logo-1x{font-size:1em}.editormd-logo-lg{font-size:1.2em}.editormd-logo-2x{font-size:2em}.editormd-logo-3x{font-size:3em}.editormd-logo-4x{font-size:4em}.editormd-logo-5x{font-size:5em}.editormd-logo-6x{font-size:6em}.editormd-logo-7x{font-size:7em}.editormd-logo-8x{font-size:8em}.editormd-logo-color{color:#2196F3}

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,11 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
<svg xmlns="http://www.w3.org/2000/svg">
<metadata>Generated by IcoMoon</metadata>
<defs>
<font id="icomoon" horiz-adv-x="1024">
<font-face units-per-em="1024" ascent="960" descent="-64" />
<missing-glyph horiz-adv-x="1024" />
<glyph unicode="&#x20;" d="" horiz-adv-x="512" />
<glyph unicode="&#xe1987;" d="M726.954 68.236l-91.855-56.319-21.517 106.748 113.371-50.43zM876.293 709.493l12.502 28.106c6.469 14.545 23.659 21.147 38.201 14.681l60.652-26.984c14.546-6.468 21.149-23.661 14.68-38.201l-12.502-28.106-113.536 50.505zM720.236 424.478l116.041 260.86-7.209 69.019h-130.248l-233.736-522.358-245.476 522.358h-133.528l-71.266-742.442h82.462l47.785 562.498 264.047-562.498h43.141l252.85 562.498 15.14-149.939zM761.891 11.915l-6.068 60.094 117.030 263.097 33.757-323.192-144.719 0.001zM621.638 137.007l113.54-50.503 246.486 554.111-113.536 50.506-246.489-554.114z" horiz-adv-x="1017" />
</font></defs></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

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