Personal development blog notes

 

 

1. background

1.1 Overview of topics

A long time ago I wanted to write his own blog, and taking advantage of the web is now scheduled school class-based topics, decided to give it to come out, but also the way review some web technologies and frameworks have learned.

Only after hands done in order to find deficiencies, such as dealing with some of the details, the general design of the table, write the business logic interface, as well as some bug exists, you can also make your page more skilled developers various functions.

1.2 Technology Selection

Back-end technology: Springboot + Spring + Mybatis + Druid + Swagger + + Mysql hot deployment

Front-end technology: html + css + js + Jquery + bootstrap + vue.js

2. The overall system function module

2.1 Requirements Analysis

Front-end needs analysis:

simple / beautiful - personally like the wind Mac as simple as simple as possible, of course, they have to look good; (Home Carousel Figure + Category + left navigation bar layout + blog details page)

preferably single page - a single page that purpose on the one hand the sake of brevity, on the other hand also for relatively simple to implement; (single page would not vue.js do SPA, or by way of a label-place jump simulate a single page)

adaptive - at least fit the common mobile resolution, and I do not want your blog diversity of display problems; (Bootstrap grid system with + CSS + JS achieve media queries)

 

Page may appear as shown:

 

 

figure 1

 

PS: After a message on pages and pages, and then realized resume page

 

 

 

figure 2

 

PS: Comments are yet to achieve, only achieved Bowen classification (CURD) and article management (common CURD) function

 

 

 

 

image 3

 

PS: statistics module yet to achieve

 

 

Table 2.2 Design

Personal blog system data structure design:

 

 

Table 1

 

PS: This menu FIG implemented with the inverse model Navicat

Table 2.3 Structure Analysis

1) classification information table (tbl_category_content):

TABLE `tbl_category_info` the CREATE (
` id` BIGINT (40) the AUTO_INCREMENT the NOT NULL,
`name` VARCHAR (20 is) the COMMENT the NOT NULL 'category name',
the` number` tinyint (10) NOT NULL DEFAULT '0' COMMENT ' the classification the number of articles ',
`create_by` the NOT NULL datetime the COMMENT' classification was created ',
` modified_by` the NOT NULL datetime the COMMENT' classification modification time ',
`is_effective` tinyint (1) the NOT NULL the dEFAULT' 1 'the COMMENT' is valid, default 1 is active, 0 inactive ',
a PRIMARY KEY ( `id`)
) = ENGINE the InnoDB the DEFAULT the CHARSET = UTF8;

Table 2

2) Article table of contents (tbl_article_content):

TABLE `tbl_article_content` the CREATE ( 
` id` BIGINT (40) the AUTO_INCREMENT the NOT NULL,
`content` text the NOT NULL,
` article_id` BIGINT (40) the COMMENT the NOT NULL 'corresponding to the article ID',
`create_by` the COMMENT datetime the NOT NULL 'Created ',
`modifield_by` the COMMENT datetime the NOT NULL' update ',
a PRIMARY KEY (` id`)
) = ENGINE the InnoDB the DEFAULT the CHARSET = UTF8;

Table 2

PS: the article content table is a separate sub-MD format because the article should be added to the database directly from the background, a large part of the text type, not on the basis of the article information in the table is to query efficiency, it does not require indexing large text fields

 

3) Article Information Table (tbl_article_info):

TABLE `tbl_article_info` the CREATE ( 
` id` BIGINT (40) the AUTO_INCREMENT the COMMENT the NOT NULL 'master key',
`title` VARCHAR (50) the NOT NULL the DEFAULT '' the COMMENT 'article title',
` summary` VARCHAR (300) the NOT NULL the DEFAULT ' 'COMMENT' Introduction The default within 100 characters',
`is_top` tinyint (1) the NOT NULL the dEFAULT '0' COMMENT 'article whether top, 0 is no and 1 is yes',
` traffic` int (10) the NOT NULL DEFAULT '0' COMMENT 'article views',
`create_by` the NOT NULL datetime the COMMENT 'creation time',
` modified_by` the NOT NULL datetime the COMMENT 'date modified',
PRIMARY KEY ( `id`)
) ENGINE = InnoDB the DEFAULT CHARSET = utf8 ;

 

table 3

The following table is associated

PS: with the correlation table, in order not to make the back-end multi-table join query, the query efficiency impact, so you do not need to create a foreign key,

Let backend manual Service layer performs the function of the foreign key, greatly reducing the pressure on the database.


4) article classification (tbl_article_category):

TABLE `tbl_article_category` the CREATE ( 
` id` bigint (40) the NOT NULL AUTO_INCREMENT,
`sort_id` bigint (40) the NOT NULL the COMMENT 'classification of the above mentioned id',
` article_id` bigint (40) the NOT NULL the COMMENT 'article the above mentioned id',
`create_by` datetime NOT NULL COMMENT 'creation time',
`modified_by` datetime NOT NULL COMMENT 'update',
` is_effective` tinyint (1) the dEFAULT '1' the COMMENT 'indicates that the current data is valid, the default is a valid, 0 invalid'
KEY a PRIMARY ( `id`)
) = ENGINE the InnoDB the DEFAULT the CHARSET = UTF8;

 

Table 4

5) article in question chart (tbl_article_picture):

TABLE `tbl_article_picture` the CREATE ( 
` id` BIGINT (40) the AUTO_INCREMENT the NOT NULL,
`article_id` BIGINT (40) the COMMENT the NOT NULL 'corresponding to the article ID',
` picture_url` VARCHAR (100) the NOT NULL the DEFAULT '' the COMMENT 'image url' ,
`create_by` the NOT NULL datetime the COMMENT 'creation time',
` modified_by` the NOT NULL datetime the COMMENT 'update',
PRIMARY KEY ( `id`)
) ENGINE = InnoDB the DEFAULT CHARSET = utf8 the COMMENT = 'this table is used to hold the title Figure url, every article should have a topic map ';

table 5

PS: title figure drawings and articles for Carousel details of the front-page article with pictures of Home

3. prototype reference

3.1 Reference distal Prototype

 

Home:

 

 

Figure 4

 

Article Category Page:

 

 

Figure 5

 

 

Article details page:

 

 

Image 6

3.2 back-end prototype reference

 

 

Figure 7

4. Project to build

4.1 Springboot project configuration

 

 

 

Figure 8

 

PS: The picture above shows from Springboot official website set up after Springboot initializer Springboot project, carried out in the above

After the second development, the directory structure of the final development of the above

 

Maven project is dependent on: Pom following documents

 

 

 

Figure 9

 

The project directory structure will be briefly described:

  • controller: the controller (MVC C-module for processing requests and ResfulAPI url mapping design)
  • dao:实际上这个包可以改名叫mapper,因为里面放的应该是MyBatis逆向工程自动生成之后的mapper类。(就是数据访问对象层,访问数据库的,增删改查的方法都在这里)
  • entity:实体类,(MVC中M模块,Model,对应表的JavaBean)还会有一些MyBatis生成的example
  • generator:MyBatis逆向工程生成类
  • interceptor:SpringBoot 拦截器 (拦截后台管理系统的请求,判断有无管理员登陆的权限)
  • service:Service层,里面还有一层impl目录 (业务逻辑接口的开发都在这里)
  • util:一些工具类可以放在里面 (Markdown格式转html的工具类也在这里)
  • mapper:用于存放MyBatis逆向工程生成的.xml映射文件
  • static:这个目录存放一些静态文件,简单了解了一下Vue的前后端分离,前台文件以后也需要放在这个目录下面(放网页和JS,CSS,image的地方)

 

4.2 Mybatis框架集成配置

1. Springboot继承Mybatis是通过依赖starter来集成Mybatis框架的

Pom依赖如下:

 

 

图10

 

2. Mybatis逆向工程:

 

 

图11

 

PS:逆向工程用于自动根据配置的数据库来生成Entity类,和mapper映射文件和mapper映射接口(用来操作数据库的),相当于自动生成了一大堆的sql语句(增删改查),上一层直接调用DAO层的接口即可访问数据库 (松耦合)

 

4.3 Restful设计与Swagger2配置

1.概要:RestfulAPI是一种HTTP请求的规范,可以用到put请求表示更新数据,

Delete请求表示删除数据,post请求表示添加数据,get请求表示查询数据,合理的运用

HTTP方法来完成请求,避免了以前WEB开发只用get 和post请求的这种不规范设计

格式为下图:

 

 

图12

 

2. Swaager文档用于图形化RestfulAPI风格的接口,效果如下图:

 

 

图13

4.4 数据库连接池配置和日志配置

1. 采用了Druid数据库连接池(当今最实用,效率也很高的阿里巴巴的连接池)

 

 

图14

 

 

2. 日志配置: Springboot天生集成了logback日志,所以不需要再重新导入新的日志框架,

                     直接复制日志配置文件即可,但注意名字要按格式来,才能被加载,如图:

 

 

 

图15

 

4.5 拦截器配置

登陆拦截器代码如下:(还用Cookie实现了30分钟有效期的自动登陆)

 

public class BackInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //通过session判断是否已经登陆
        String username = (String)request.getSession().getAttribute("username");
        String name = "zhanp";


        //传输的加密先放一边,后面再看下

        //先判断Session不为空,说明已经登陆了
        if(StringUtils.isEmpty(username)){
            Cookie[] cookies = request.getCookies();
            //判断cookie中有没有自动登陆的凭证
            if(cookies!=null){
                for(Cookie cookie:cookies){
                    if(!StringUtils.isEmpty(cookie)&&cookie.getName().equals(name)){
                        return true;
                    }
                }
            }else{
                return false;
            }
        }
        return true;
    }
}

 

 

5. 后端开发过程

5.1 Entity层开发

 

 

图16

 

 

图17

 

这些实体类对应的是Mysql中建立的表的名字,属性名字为表的字段名

 

5.2 Service层开发

 

 

图18

1. 比如文章的业务接口开发有

 1.1 添加文章->要填充文章内容表,文章-分类表,文章-题图表,文章信息表,还要修改相应分类下的文章数目

 

    public void addArticle(ArticleDto articleDto) {
        //1.填充文章信息表----title/summary/isTop
//       前端不可能给你Id的,这是后端自动生产的,要在后端获取Id
//        Long id = articleDto.getId();
        String title = articleDto.getTitle();
        String summary = articleDto.getSummary();
        Boolean isTop = articleDto.getIsTop();

        ArticleInfo articleInfo = new ArticleInfo();
        articleInfo.setTitle(title);
        articleInfo.setSummary(summary);
        articleInfo.setIsTop(isTop);

        //1.1 写入文章信息表中
        //1.2 并查询新增的文章Id。。。因为返回主键也需要select和插入处于同一事务,所以不会返回正确的插入后的主键
        articleInfoMapper.insertSelective(articleInfo) ;
        //从参数里返回主键
        Long id = articleInfo.getId();

        //2. 填充文章-内容表----文章Id/content
        ArticleContent articleContent = new ArticleContent();
        articleContent.setArticleId(id);
        articleContent.setContent(articleDto.getContent());

        //2.1 写入文章-内容表
         articleContentMapper.insertSelective(articleContent);

        //3. 填充文章 - 分类表---文章Id/分类Id
        ArticleCategory articleCategory = new ArticleCategory();
        articleCategory.setArticleId(id);
        articleCategory.setSortId(articleDto.getCategoryId());

        //3.1 写入文章 - 分类表
        articleCategoryMapper.insertSelective(articleCategory);

        //3.2 分类下的文章信息 + 1
        Long sortId = articleCategory.getSortId();
        //查询你源分类信息条目
        CategoryInfo categoryInfo = categoryInfoMapper.selectByPrimaryKey(sortId);
        //文章+1
        categoryInfo.setNumber((byte) (categoryInfo.getNumber()+1));
        categoryInfoMapper.updateByPrimaryKeySelective(categoryInfo);

        //4. 填充文章-题图表 ---文章Id/图片url
        ArticlePicture articlePicture = new ArticlePicture();
        articlePicture.setArticleId(id);
        articlePicture.setPictureUrl(articleDto.getPictureUrl());

        //4.1写入 文章-题图表
        articlePictureMapper.insertSelective(articlePicture);

    }

 

 1.2 更新文章:
 * 根据封装的ArticleDto参数 选择性的更新文章
 * warning: ArticleDto参数后台按实际情况应该只有文章基础信息的Id,和图片url,内容content,分类Id这种,
 *          而不会有从表的主键Id,所以除了文章信息表外,其他从表需要根据文章Id关联查询出来
 * 比如更新文章基础信息(title,summary,isTop)
 * 更新文章-分类表的信息
 * 更新文章-题图表的信息
 *
 * 还有更新文章时分类信息改了的话,要调用分类文章-的api updateArticleCategory()去重新统计分类下的数目,这个写漏了

@Override
public void updateArticle(ArticleDto articleDto) {

    Long id = articleDto.getId();

    //1.文章基础信息表
    //1.1 填充ArticleInfo参数
    ArticleInfo articleInfo = new ArticleInfo();
    articleInfo.setId(id);
    articleInfo.setSummary(articleDto.getSummary());
    articleInfo.setIsTop(articleDto.getIsTop());
    articleInfo.setTitle(articleDto.getTitle());
    articleInfo.setTraffic(articleDto.getTraffic());

    articleInfoMapper.updateByPrimaryKeySelective(articleInfo);

    //2. 文章-分类表

    //根据文章Id----找出对应的文章分类表 的条目
    ArticleCategoryExample articleCategoryExample = new ArticleCategoryExample();
    ArticleCategoryExample.Criteria articleCategoryExampleCriteria = articleCategoryExample.createCriteria();
    articleCategoryExampleCriteria.andArticleIdEqualTo(id);
    List<ArticleCategory> articleCategoryList = articleCategoryMapper.selectByExample(articleCategoryExample);
    ArticleCategory category = articleCategoryList.get(0);

    //2.1 先检查源分类Id与更新过来的分类Id是否相等
    // 如果分类被修改过了,那么分类下的文章数目也要修改
    //前者是源Id,后者是更新过来的Id
    Long sourceSortId = category.getSortId();
    Long categoryId = articleDto.getCategoryId();
    if(!sourceSortId.equals(categoryId)){
        //2.3 更新分类下的文章信息
        updateArticleCategory(id,categoryId);
    }

    //3.文章-题图表

    ArticlePictureExample articlePictureExample = new ArticlePictureExample();
    articlePictureExample.or().andArticleIdEqualTo(id);

    List<ArticlePicture> pictureList = articlePictureMapper.selectByExample(articlePictureExample);
    ArticlePicture articlePicture = pictureList.get(0);
    articlePicture.setPictureUrl(articleDto.getPictureUrl());
    articlePictureMapper.updateByPrimaryKeySelective(articlePicture);

    //4.文章-内容表
    ArticleContentExample articleContentExample = new ArticleContentExample();
    articleContentExample.or().andArticleIdEqualTo(id);
    List<ArticleContent> contentList = articleContentMapper.selectByExample(articleContentExample);
    ArticleContent articleContent = contentList.get(0);

    articleContent.setContent(articleDto.getContent());

    articleContentMapper.updateByPrimaryKeyWithBLOBs(articleContent);
}

1.3 获取一篇文章(根据文章Id)

@Override

public ArticleDto getOneById(Long id) {
    ArticleDto articleDto = new ArticleDto();

    //1. 文章信息表内的信息 填充 到 Dto
    ArticleInfo articleInfo = articleInfoMapper.selectByPrimaryKey(id);

    //1.1 增加浏览量 + 1
    ArticleInfo info = new ArticleInfo();
    info.setId(id);
    info.setTraffic(articleInfo.getTraffic()+1);
    articleInfoMapper.updateByPrimaryKeySelective(info);

    articleDto.setId(id);
    articleDto.setTitle(articleInfo.getTitle());
    articleDto.setSummary(articleInfo.getSummary());
    articleDto.setIsTop(articleInfo.getIsTop());
    //没用到缓存,所以访问量统计还是在SQL操作这里增加把(一个博客,做啥缓存啊)
    articleDto.setCreateBy(articleInfo.getCreateBy());
    articleDto.setTraffic(articleInfo.getTraffic()+1);

    //2. 文章内容表内的信息 填充 到 Dto
    ArticleContentExample articleContentExample = new ArticleContentExample();
    articleContentExample.or().andArticleIdEqualTo(id);
    List<ArticleContent> contentList = articleContentMapper.selectByExampleWithBLOBs(articleContentExample);
    ArticleContent articleContent = contentList.get(0);
    articleDto.setContent(articleContent.getContent());
    //填充关联表的主键,其他业务可能通过调用getOneById 拿到Dto里的这个主键
    articleDto.setArticleContentId(articleContent.getId());

    //3.文章-分类表内的信息 填充 到 Dto
    ArticleCategoryExample articleCategoryExample = new ArticleCategoryExample();
    articleCategoryExample.or().andArticleIdEqualTo(id);
    List<ArticleCategory> articleCategories = articleCategoryMapper.selectByExample(articleCategoryExample);
    ArticleCategory articleCategory = articleCategories.get(0);

    //3.1设置文章所属的分类Id+ 从表主键 --从表
    Long sortId = articleCategory.getSortId();
    articleDto.setCategoryId(sortId);
    articleDto.setArticleCategoryId(articleCategory.getId());

    //3.2找分类主表 --设置分类信息
    CategoryInfo categoryInfo = categoryInfoMapper.selectByPrimaryKey(sortId);
    articleDto.setCategoryName(categoryInfo.getName());
    articleDto.setCategoryNumber(categoryInfo.getNumber());

    //4.文章-题图表
    ArticlePictureExample articlePictureExample = new ArticlePictureExample();
    articlePictureExample.or().andArticleIdEqualTo(id);
    List<ArticlePicture> articlePictures = articlePictureMapper.selectByExample(articlePictureExample);
    ArticlePicture picture = articlePictures.get(0);
    //4.1设置图片Dto
    articleDto.setArticlePictureId(picture.getId());
    articleDto.setPictureUrl(picture.getPictureUrl());

    return articleDto;
}

1.4 找出分类下所有的文章信息

@Override
public List<ArticleWithPictureDto> listByCategoryId(Long id) {
    //1. 先找出分类下所有的文章
    ArticleCategoryExample articleCategoryExample = new ArticleCategoryExample();
    articleCategoryExample.or().andSortIdEqualTo(id);
    List<ArticleCategory> articleCategories = articleCategoryMapper.selectByExample(articleCategoryExample);

    ArrayList<ArticleWithPictureDto> list = new ArrayList<>();
    //1.1遍历
    for(ArticleCategory articleCategory:articleCategories){
        ArticleWithPictureDto articleWithPictureDto = new ArticleWithPictureDto();

        //1.1.1 取出文章
        Long articleId = articleCategory.getArticleId();
        ArticleInfo articleInfo = articleInfoMapper.selectByPrimaryKey(articleId);

        //1.1.2 取出文章对应的图片url
        ArticlePictureExample articlePictureExample = new ArticlePictureExample();
        articlePictureExample.or().andArticleIdEqualTo(articleId);
        List<ArticlePicture> articlePictures = articlePictureMapper.selectByExample(articlePictureExample);
        ArticlePicture picture = articlePictures.get(0);

        articleWithPictureDto.setId(articleId);
        articleWithPictureDto.setArticlePictureId(picture.getId());
        articleWithPictureDto.setTitle(articleInfo.getTitle());
        articleWithPictureDto.setSummary(articleInfo.getSummary());
        articleWithPictureDto.setIsTop(articleInfo.getIsTop());
        articleWithPictureDto.setTraffic(articleInfo.getTraffic());

        articleWithPictureDto.setPictureUrl(picture.getPictureUrl());

        list.add(articleWithPictureDto);
    }

    return list;
}

 

 

PS:还有一系列的接口开发在源码中查看吧

 

5.3 DTO层开发

 

 

图19

 

用于封装了多个实体类的属性,用于前后端交互的整体属性封装,便捷实用的进行

JSON数据交互

 

 

 

5.4 Controller层开发

 

 

图20

 

BaseController为后台控制器

ForeController为前台控制器

 

比如更新文章的Controller

 

 

图21

 

 

 

 

图22

 

 

  

 

图23

 

6. 前端开发过程

6.1 登陆页开发

Login.html

 

 

图24

 

 

图25

 

效果如下:

 

 

图26

还用了轮播图的形式

 

 

图27

 

url:为toLogin,代码实现为:

 

 

图28

 

6.2 分类管理页开发

 

 

图29

 

 

Category.html

 

 

图30

 

 

图31

 

图32

 

 

效果如下:

 

 

图33

6.3博文管理页开发

 

 

 

图34

 

 

图35

 

效果如下:

 

 

图36

 

 

 

图37

 

 

PS:还用了动态的placeholder来保存更新前的数据,在此上面做修改。这个是模态框

 

 

图38

 

PS:这些分类都是动态从数据库里拉过来的,不是静态的!!!

 

6.4 博客首页开发

 

 

图39

 

 

图40

导航栏 + 最新几篇文章的轮播图(点击可进入文章详情页)

 

6.5 博客文章详情页开发

代码如下:

 

 

图41

 

效果如下:

 

 

图42

 

 

 

图43

 

PS:这些都是动态的,非静态页面,静态就没有意义了

 

6.6 博文分类页面

 

 

图44

效果如下:

 

 

 

 

 

 

图45

 

 

 

图46

 

 

图47

 

PS:还设置了动态的分类选中效果,根据不同的分类显示不同的文章信息,点击文章信息,即可进入文章详情页

7. 项目心得总结

1. 以后要多加练习,多做项目来熟悉一般web项目的整个开发流程,比如搭建项目的环境,相应框架的配置。

2. 还要多总结开发过程中遇到的bug和一些细节的处理,比如这个效果怎么实现,这个功能用什么方法实现,要写个笔记好好记录一下,方便以后的开发,

不需要再次查询百度或谷歌。

3. 还要重视数据库,不要以为只会写几条增删改查的sql语句即可,关键是

对数据库的设计,对表的编排,关联表的运用,如何设计表结构让程序跑的更快,开发更方便。还要重视数据库的索引技术,分表分库,以后都可以深造

4. 不要停留在只知道这个技术,而不去动手实践,这些知识不实践就会忘。

比如Mybatis配置文件和框架整合,或Spring的配置,或Springboot的错误处理页面的定制,或者Thymeleaf模板引擎的熟练使用(虽然前后端分离以及不用这种类似JSP的模板引擎了),或者是事务的添加,又或者前后端密码校验的加密处理,以及前端CSS的布局,样式的熟练掌握,bootstrap常用的样式的实现,vue.js的细节和bug等等。

5.但是又不能停留在只会用这些表面的框架和技术,而不懂其原理,基础和原理是很重要的,对于后期的debug排查错误,对原理熟悉的,可以很快的找寻出是哪方面导致的问题。而且Spring框架的IOC和AOP概念贯穿了整个Spring全家桶的产品,所以一定要深刻理解和练习,还有对于Java基础的提高,比如反射技术(对应于Spring中的AOP实现,事务的实现,自动配置类的加载,动态代理等等)都用到反射技术。

Guess you like

Origin www.cnblogs.com/zhanp/p/10956803.html