個人部落格專案筆記_01

CherriesOvO發表於2024-04-08

1. 工程搭建

前端的工程執行流程:

進入專案目錄執行cmd命令:

若是第一次啟動需要依次輸入如下命令:

npm install
npm run build
npm run dev

之後直接執行 npm run dev 即可!

1.1 新建maven工程

新建maven工程blog作為父工程,然後在父工程中建立子工程blog-api

向父工程的pom.xml檔案中匯入依賴。

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.cherriesovo</groupId>
    <artifactId>blog</artifactId>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>blog-api</module>
        <module>blog-admin</module>
    </modules>
    <packaging>pom</packaging>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.0</version>
        <relativePath/>
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencyManagement>
    <dependencies>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.76</version>
        </dependency>

        <dependency>
            <groupId>commons-collections</groupId>
            <artifactId>commons-collections</artifactId>
            <version>3.2.2</version>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.3</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/joda-time/joda-time -->
        <dependency>
            <groupId>joda-time</groupId>
            <artifactId>joda-time</artifactId>
            <version>2.10.10</version>
        </dependency>
    </dependencies>
    </dependencyManagement>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

1.2 配置

  • 在子工程的resources資料夾下建立application.properties檔案:

mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl:列印日誌

mybatis-plus.global-config.db-config.table-prefix=ms_:標識資料庫中表名的字首為 ms_

#server 埠號與前端對應
server.port= 8888
spring.application.name=cherriesovo_blog
# datasource
spring.datasource.url=jdbc:mysql://localhost:3306/blog?useUnicode=true&characterEncoding=UTF-8&serverTimeZone=UTC
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

#mybatis-plus
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
mybatis-plus.global-config.db-config.table-prefix=ms_
  • 在com.cherriesovo.blog下建立config包,建立MybatisPlusConfig配置類,用於配置MyBatis-Plus 的分頁外掛,使得在使用 MyBatis-Plus 進行資料庫操作時能夠支援分頁查詢功能:
package com.cherriesovo.blog.config;

import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration	//註解表示這是一個配置類
//掃描指定的包路徑下的 MyBatis Mapper 介面,並將其註冊到 Spring 容器中
@MapperScan("com.cherriesovo.blog.dao.mapper")
public class MybatisplusConfig {

    //配置MyBatis-Plus 的分頁外掛,使得在使用 MyBatis-Plus 進行資料庫操作時能夠支援分頁查詢功能
    
    @Bean	//表示一個 Spring Bean 的定義
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        //PaginationInnerInterceptor 是內部攔截器,用於實現分頁功能
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
        return interceptor;
    }
}

跨域配置是指在 Web 開發中處理跨域資源共享的設定和策略。當一個網頁透過 AJAX 請求獲取其他域名下的資源時,如果目標資源所在的域名、協議、埠與當前頁面不一致,就會出現跨域請求。為了加強安全性,瀏覽器會阻止跨域請求,除非目標伺服器允許來自其他域的請求。

在 Vue 專案中,當前端程式碼部署在一個域名下,而後端 API 服務部署在另一個域名下時,就會涉及到跨域請求。為了解決跨域問題,需要在後端伺服器上進行跨域配置,以允許特定來源(origin)的請求訪問資源。

常見的跨域配置包括:

  1. 設定響應頭:後端伺服器可以在 HTTP 響應頭中新增特定的欄位,如 Access-Control-Allow-OriginAccess-Control-Allow-Methods 等,來指定允許跨域請求的來源、請求方法等資訊。
  2. 使用代理:在開發環境中,可以配置代理伺服器來轉發 API 請求,使得前端程式碼和後端 API 請求處於同一個域名下,避免跨域問題。
  3. JSONP 跨域請求:JSONP 是一種跨域請求的方式,透過動態建立 <script> 標籤實現跨域資料獲取,不受同源策略的限制。
  4. CORS 中介軟體:一些後端框架提供了專門用於處理 CORS 的中介軟體或外掛,透過簡單的配置即可實現跨域資源共享。
  • 在config包下,建立WebMVCConfig跨域配置類:
package com.cherriesovo.blog.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

//WebMvcConfigurer 是 Spring MVC 的配置介面,透過實現該介面可以對 Spring MVC 進行配置。
@Configuration
public class WebMVCConfig  implements WebMvcConfigurer {

    /*
    * 該方法用於配置跨域請求的規則。在這裡,它指定了允許來自 http://localhost:8080 的請求訪問所有的資源(/**),
    *並且允許跨域請求的請求頭和方法。
     */
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        //跨域配置,不可設定為*,不安全, 前後端分離專案,可能域名不一致
        //本地測試 埠不一致 也算跨域
        //允許http://localhost:8080訪問所有埠
        registry.addMapping("/**").allowedOrigins("http://localhost:8080");
    }
}

1.3 啟動類

package com.cherriesovo.blog;

import org.springframework.boot.autoconfigure.SpringBootApplication;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class BlogApp {

    public static void main(String[] args) {
        SpringApplication.run(BlogApp.class,args);
    }
}

2. 首頁-文章列表

2.1 介面說明

介面url:/articles

請求方式:POST

請求引數:

引數名稱 引數型別 說明
page int 當前頁數
pageSize int 每頁顯示的數量

返回資料:

{
    "success": true,
    "code": 200,
    "msg": "success",
    "data": [
        {
            "id": 1,
            "title": "springboot介紹以及入門案例",
            "summary": "透過Spring Boot實現的服務,只需要依靠一個Java類,把它打包成jar,並透過`java -jar`命令就可以執行起來。\r\n\r\n這一切相較於傳統Spring應用來說,已經變得非常的輕便、簡單。",
            "commentCounts": 2,
            "viewCounts": 54,
            "weight": 1,
            "createDate": "2609-06-26 15:58",
            "author": "12",
            "body": null,
            "tags": [
                {
                    "id": 5,
                    "avatar": null,
                    "tagName": "444"
                },
                {
                    "id": 7,
                    "avatar": null,
                    "tagName": "22"
                },
                {
                    "id": 8,
                    "avatar": null,
                    "tagName": "11"
                }
            ],
            "categorys": null
        },
        {
            "id": 9,
            "title": "Vue.js 是什麼",
            "summary": "Vue (讀音 /vjuː/,類似於 view) 是一套用於構建使用者介面的漸進式框架。",
            "commentCounts": 0,
            "viewCounts": 3,
            "weight": 0,
            "createDate": "2609-06-27 11:25",
            "author": "12",
            "body": null,
            "tags": [
                {
                    "id": 7,
                    "avatar": null,
                    "tagName": "22"
                }
            ],
            "categorys": null
        },
        {
            "id": 10,
            "title": "Element相關",
            "summary": "本節將介紹如何在專案中使用 Element。",
            "commentCounts": 0,
            "viewCounts": 3,
            "weight": 0,
            "createDate": "2609-06-27 11:25",
            "author": "12",
            "body": null,
            "tags": [
                {
                    "id": 5,
                    "avatar": null,
                    "tagName": "444"
                },
                {
                    "id": 6,
                    "avatar": null,
                    "tagName": "33"
                },
                {
                    "id": 7,
                    "avatar": null,
                    "tagName": "22"
                },
                {
                    "id": 8,
                    "avatar": null,
                    "tagName": "11"
                }
            ],
            "categorys": null
        }
    ]
}

2.2 編碼

2.2.1 表結構

文章表:

CREATE TABLE `blog`.`ms_article`  (
  `id` bigint(0) NOT NULL AUTO_INCREMENT,
  `comment_counts` int(0) NULL DEFAULT NULL COMMENT '評論數量',
  `create_date` bigint(0) NULL DEFAULT NULL COMMENT '建立時間',
  `summary` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '簡介',
  `title` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '標題',
  `view_counts` int(0) NULL DEFAULT NULL COMMENT '瀏覽數量',
  `weight` int(0) NOT NULL COMMENT '是否置頂',
  `author_id` bigint(0) NULL DEFAULT NULL COMMENT '作者id',
  `body_id` bigint(0) NULL DEFAULT NULL COMMENT '內容id',		#和article_body表連線
  `category_id` int(0) NULL DEFAULT NULL COMMENT '類別id',	#和category表連線
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 25 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

標籤表:

#`id`欄位沒有其他意義,只是代表一條資料,每條資料中存放著文章id和標籤id的對應關係,真正的標籤id存放在ms_tag表中。
# 兩張表透過tag_id進行連線
CREATE TABLE `blog`.`ms_article_tag`  (
  `id` bigint(0) NOT NULL AUTO_INCREMENT,
  `article_id` bigint(0) NOT NULL,
  `tag_id` bigint(0) NOT NULL,
  PRIMARY KEY (`id`) USING BTREE,
  INDEX `article_id`(`article_id`) USING BTREE,
  INDEX `tag_id`(`tag_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

使用者表:

CREATE TABLE `blog`.`ms_sys_user`  (
  `id` bigint(0) NOT NULL AUTO_INCREMENT,
  `account` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '賬號',
  `admin` bit(1) NULL DEFAULT NULL COMMENT '是否管理員',
  `avatar` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '頭像',
  `create_date` bigint(0) NULL DEFAULT NULL COMMENT '註冊時間',
  `deleted` bit(1) NULL DEFAULT NULL COMMENT '是否刪除',
  `email` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '郵箱',
  `last_login` bigint(0) NULL DEFAULT NULL COMMENT '最後登入時間',
  `mobile_phone_number` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '手機號',
  `nickname` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '暱稱',
  `password` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '密碼',
  `salt` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '加密鹽',
  `status` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '狀態',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 16 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
  • 建立包com.cherriesovo.blog.dao.pojocom.cherriesovo.blog.dao.mapper

文章實體類:

package com.cherriesovo.blog.dao.pojo;

import lombok.Data;

@Data
public class Article {

    public static final int Article_TOP = 1;

    public static final int Article_Common = 0;

    private Long id;

    private String title;

    private String summary;

    private int commentCounts;

    private int viewCounts;

    /**
     * 作者id
     */
    private Long authorId;
    /**
     * 內容id
     */
    private Long bodyId;
    /**
     *類別id
     */
    private Long categoryId;

    /**
     * 置頂
     */
    private int weight = Article_Common;


    /**
     * 建立時間
     */
    private Long createDate;
}

使用者實體類:

package com.mszlu.blog.dao.pojo;

import lombok.Data;

@Data
public class SysUser {

    private Long id;

    private String account;

    private Integer admin;

    private String avatar;

    private Long createDate;

    private Integer deleted;

    private String email;

    private Long lastLogin;

    private String mobilePhoneNumber;

    private String nickname;

    private String password;

    private String salt;

    private String status;
}

標籤實體類:

package com.mszlu.blog.dao.pojo;

import lombok.Data;

@Data
public class Tag {

    private Long id;

    private String avatar;

    private String tagName;

}

2.2.2 Controller

在 Spring Boot 專案中,vo 目錄通常用來存放值物件(Value Object)。值物件是一種用於封裝多個屬性或欄位的簡單物件,通常用於資料傳輸、資料展示或領域模型中。

值物件的特點是它們是不可變的(Immutable),也就是說一旦建立之後,其屬性值就不能再被修改。這種不可變性使得值物件更加安全和可靠,可以避免出現意外的狀態變化。

在專案中,vo 目錄可能包含一些用於表示業務領域中的概念或承載某個特定用途的值物件。這些值物件可以用於封裝一些複雜的資料結構,提供更加清晰和可讀性強的程式碼。

例如,一個電子商務系統中的訂單物件可以包含多個屬性,如訂單號、下單時間、商品列表等。為了方便傳輸和展示,可以定義一個 OrderVO 值物件,其中包含上述屬性的對應欄位。在業務邏輯中,可以使用 OrderVO 物件進行資料傳輸和展示,並且保持其不可變性。

總之,vo 目錄用來存放值物件,將複雜的資料結構封裝起來,提高程式碼的可讀性和可維護性。

  • 建立包com.cherriesovo.blog.vo.params;在該包下建立實體類PageParams用來存放頁面引數
package com.cherriesovo.blog.vo.params;

import lombok.Data;

@Data
public class PageParams {
    private int page = 1;
    private int pageSize = 10;

}
  • 在包com.cherriesovo.blog.vo.params下建立實體類Result用來存放返回結果
package com.cherriesovo.blog.vo.params;

import com.cherriesovo.blog.dao.pojo.Article;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Result {

    private boolean success;

    private Integer code;

    private String msg; //資訊

    private Object data;    //資料


    public static Result success(Object data) {
        return new Result(true,200,"success",data);
    }
    public static Result fail(Integer code, String msg) {
        return new Result(false,code,msg,null);
    }
}
  • 建立文章控制類ArticleController

1、@RequestBody這個註解用於將 HTTP 請求的內容(JSON 格式的資料)繫結到 PageParams 物件上。HTTP 請求的內容包含page和pageSize兩個引數,PageParams 是一個值物件。

2、articleService.listArticlesPage(pageParams):呼叫 ArticleService 中的 listArticlesPage 方法,傳入 PageParams 物件,以獲取文章列表。

3、Result.success(articles)Result 是一個統一結果返回的工具類,Result.success 方法用於返回成功的結果,該結果也是一個Result類,其中包含文章列表資料。這個方法會將文章列表轉換為 JSON 格式並返回給客戶端

package com.cherriesovo.blog.controller;

import com.cherriesovo.blog.dao.pojo.Article;
import com.cherriesovo.blog.service.ArticleService;
import com.cherriesovo.blog.vo.params.ArticleVo;
import com.cherriesovo.blog.vo.params.PageParams;
import com.cherriesovo.blog.vo.params.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

//json資料進行互動,其中的方法會返回 JSON 格式的資料
@RestController
@RequestMapping("articles")
public class ArticleController {

    //將ArticleService 自動注入到控制器中,這樣控制器就可以呼叫 ArticleService 中的方法。
    @Autowired
    private ArticleService articleService;
    //Result是統一結果返回
    @PostMapping
    public Result articles(@RequestBody PageParams pageParams) {
        //ArticleVo 頁面接收的資料
        List<ArticleVo> articles = articleService.listArticlesPage(pageParams);

        return Result.success(articles);
    }


}

  • 建立ArticleVo來接收頁面的資料(data)

package com.cherriesovo.blog.vo.params;


import com.cherriesovo.blog.dao.pojo.SysUser;
import com.cherriesovo.blog.dao.pojo.Tag;
import lombok.Data;

import java.util.List;

@Data
public class ArticleVo {

    private Long id;

    private String title;

    private String summary;

    private int commentCounts;

    private int viewCounts;

    private int weight;
    /**
     * 建立時間
     */
    private String createDate;

    private String author;

//    private ArticleBodyVo body;

    private List<TagVo> tags;

//    private List<CategoryVo> categorys;

}

2.2.3 Service

分頁查詢文章列表

package com.cherriesovo.blog.service;

//import com.cherriesovo.blog.vo.Archive;
import com.cherriesovo.blog.vo.ArticleVo;
import com.cherriesovo.blog.vo.params.PageParams;

import java.util.List;

public interface ArticleService {
    //分頁查詢文章列表
    List<ArticleVo> listArticlesPage(PageParams pageParams);

}

1、QueryWrapper

queryWrapper = new QueryWrapper<>()詳解:

QueryWrapper<Article> 是 MyBatis-Plus 提供的一個查詢條件構造器,用於構建資料庫查詢條件。

在這段程式碼中,QueryWrapper<Article> queryWrapper = new QueryWrapper<>(); 建立了一個 QueryWrapper 物件,並指定其泛型為 Article,表示要查詢的是 Article 資料庫表。

QueryWrapper 類提供了一系列方法,可以透過鏈式呼叫來設定查詢條件,例如 eq()ne()like() 等。這些方法可以根據需要來組合使用,構建出複雜的查詢條件。

在這裡,queryWrapper 物件是用於分頁查詢文章資料的條件構造器,但是在程式碼中沒有顯式地設定任何查詢條件。這種情況下,如果不設定任何查詢條件,QueryWrapper 會預設查詢整個表的資料,即相當於 SELECT * FROM Article

如果想要新增具體的查詢條件,可以在 queryWrapper 上使用相應的方法進行設定。例如,可以使用 eq("column", value) 方法來設定等於某個欄位值的查詢條件。

示例:

javaCopy CodequeryWrapper.eq("author", "John Doe"); // 查詢作者為 "John Doe" 的文章
queryWrapper.like("title", "Java"); // 查詢標題中包含 "Java" 的文章

最後,在分頁查詢時將 queryWrapper 物件傳遞給 selectPage() 方法,這樣可以在查詢過程中應用設定的查詢條件。

2、Page

page = new Page<>(pageParams.getPage(), pageParams.getPageSize())詳解:

該語句建立了一個 Page 物件,用於表示分頁查詢的相關資訊。

Page 是 MyBatis-Plus 提供的一個分頁物件,它包含了分頁查詢所需的各種資訊,如當前頁碼、每頁大小、總記錄數等。

在這段程式碼中,pageParams 是傳入的分頁引數物件,其中包括了當前頁碼和每頁大小。透過呼叫 getPage()getPageSize() 方法,可以獲取分頁引數的具體值。

然後,將獲取到的當前頁碼和每頁大小作為引數傳遞給 Page 的構造方法 new Page<>(pageParams.getPage(), pageParams.getPageSize()),從而建立了一個 Page 物件 page

這個 page 物件會在後續的分頁查詢中被傳遞給 selectPage() 方法,用於告知資料庫查詢時的分頁資訊,以及接收資料庫返回的分頁查詢結果。

  1. copy 方法用於將 Article 物件轉換為 ArticleVo 物件,並根據引數 isAuthorisBodyisTags 決定是否需要複製作者資訊、文章正文和標籤資訊。
    1. 使用 BeanUtils.copyProperties 方法將 Article 物件 article 的屬性複製到 articleVo 中。這裡使用了 Spring 的 BeanUtils 工具類來實現屬性的複製。
    2. Article 物件的建立時間屬性 createDate 轉換為指定格式的字串,並設定到 articleVo 物件的 createDate 屬性中。這裡使用了 Joda-Time 庫的 DateTime 類來進行日期時間的格式化。
    3. 如果 isTags 引數為 true,則透過 tagService.findTagsByArticleId(articleId) 方法獲取該文章的標籤資訊,並設定到 articleVo 物件的 tags 屬性中。
    4. 如果 isAuthor 引數為 true,則透過 sysUserService.findUserById(authorId) 方法獲取作者的使用者資訊,並從中獲取暱稱(使用 getNickname() 方法),然後將暱稱設定到 articleVo 物件的 author 屬性中
  2. listArticlesPage分頁查詢文章列表,並將查詢結果轉換為對應的 ArticleVo 物件列表返回給呼叫方。
    1. 首先建立了一個 QueryWrapper<Article> 物件 queryWrapper,用於構建查詢條件。然後建立了一個 Page<Article> 物件 page,表示要查詢的分頁資訊
    2. 呼叫 articleMapper.selectPage(page, queryWrapper) 方法進行分頁查詢文章資料。這裡使用了 MyBatis-Plus 提供的 selectPage 方法,它會根據傳入的 Page 物件和查詢條件進行分頁查詢,並返回一個分頁後的文章資料物件 articlePage
    3. 透過呼叫 getRecords() 方法,可以獲取當前頁的記錄列表,即符合查詢條件的文章記錄列表。這個方法會返回一個 List<Article> 物件,其中包含了當前頁的所有文章記錄。
    4. 將分頁查詢得到的文章記錄列表 articlePage.getRecords() 傳入 copyList() 方法中,同時傳入三個布林型別的引數
package com.cherriesovo.blog.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.cherriesovo.blog.dao.mapper.ArticleMapper;
import com.cherriesovo.blog.dao.pojo.Article;
import com.cherriesovo.blog.service.ArticleService;
//import com.cherriesovo.blog.service.SysUserService;
////import com.cherriesovo.blog.service.TagsService;
////import com.cherriesovo.blog.vo.ArticleBodyVo;
import com.cherriesovo.blog.service.SysUserService;
import com.cherriesovo.blog.service.TagService;
import com.cherriesovo.blog.vo.ArticleVo;
//import com.cherriesovo.blog.vo.TagVo;
import com.cherriesovo.blog.vo.params.PageParams;
import org.joda.time.DateTime;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

@Service
public class ArticleServiceImpl implements ArticleService {
    @Autowired
    private ArticleMapper articleMapper;
    @Autowired
    private TagService tagService;
    @Autowired
    private SysUserService sysUserService;

    public ArticleVo copy(Article article,boolean isAuthor,boolean isBody,boolean isTags){
        ArticleVo articleVo = new ArticleVo();
        BeanUtils.copyProperties(article, articleVo);

        articleVo.setCreateDate(new DateTime(article.getCreateDate()).toString("yyyy-MM-dd HH:mm"));
        //並不是所有的介面都需要標籤,作者資訊
        if(isTags){
            Long articleId = article.getId();
            articleVo.setTags(tagService.findTagsByArticleId(articleId));
        }
        if(isAuthor){
            Long authorId = article.getAuthorId();
            //getNickname()用於獲取某個物件或實體的暱稱或別名
            articleVo.setAuthor(sysUserService.findUserById(authorId).getNickname());
        }
        return articleVo;
    }

    private List<ArticleVo> copyList(List<Article> records,boolean isAuthor,boolean isBody,boolean isTags) {
        List<ArticleVo> articleVoList = new ArrayList<>();
        for (Article article : records) {
            ArticleVo articleVo = copy(article,isAuthor,isBody,isTags);
            articleVoList.add(articleVo);	//add() 方法是 ArrayList 類的一個方法,用於向列表末尾新增元素
        }
        return articleVoList;
    }


    @Override
    public List<ArticleVo> listArticlesPage(PageParams pageParams) {
    //  分頁查詢article資料庫表
        QueryWrapper<Article> queryWrapper = new QueryWrapper<>();
        Page<Article> page = new Page<>(pageParams.getPage(),pageParams.getPageSize());
        Page<Article> articlePage = articleMapper.selectPage(page, queryWrapper);	//分頁查詢文章資料
        List<ArticleVo> articleVoList = copyList(articlePage.getRecords(),true,false,true);
        return articleVoList;
    }
}

package com.cherriesovo.blog.service;

import com.cherriesovo.blog.dao.pojo.SysUser;

public interface SysUserService {
    SysUser findUserById(Long id);
}

package com.cherriesovo.blog.service.impl;

import com.cherriesovo.blog.dao.mapper.SysUserMapper;
import com.cherriesovo.blog.dao.pojo.SysUser;
import com.cherriesovo.blog.service.SysUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class SysUserServiceImpl implements SysUserService {

    @Autowired
    private SysUserMapper sysUserMapper;

    @Override
    public SysUser findUserById(Long userId) {
        //selectById() 方法是 MyBatis-Plus 提供的
        SysUser sysUser = sysUserMapper.selectById(userId);
        if (sysUser == null) {
            sysUser = new SysUser();
            sysUser.setNickname("CherriesOvO");
        }
        return sysUser;
    }
}

package com.cherriesovo.blog.service;

import com.cherriesovo.blog.vo.TagVo;

import java.util.List;

public interface TagService {
    List<TagVo> findTagsByArticleId(Long articleId);
}

package com.cherriesovo.blog.service.impl;

import com.cherriesovo.blog.dao.mapper.TagMapper;
import com.cherriesovo.blog.dao.pojo.Tag;
import com.cherriesovo.blog.service.TagService;
import com.cherriesovo.blog.vo.TagVo;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

@Service    //將類標記為服務元件,供其他元件使用
public class TagServiceImpl implements TagService {
    @Autowired
    private TagMapper tagMapper;

    public TagVo copy(Tag tag){
        TagVo tagVo = new TagVo();
        BeanUtils.copyProperties(tag,tagVo);
        return tagVo;
    }
    public List<TagVo> copyList(List<Tag> tagList){
        List<TagVo> tagVoList = new ArrayList<>();
        for (Tag tag : tagList) {
            tagVoList.add(copy(tag));
        }
        return tagVoList;
    }

    @Override
    public List<TagVo> findTagsByArticleId(Long articleId) {
        //mybatisplus無法進行多表查詢
        List<Tag> tags = tagMapper.findTagsByArticleId(articleId);
        return copyList(tags);
    }
}

2.2.4 Dao

ArticleMapper

package com.cherriesovo.blog.dao.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.cherriesovo.blog.dao.pojo.Article;

//BaseMapper<Article>是mybatisplus提供的,可以很方便的查詢Article這張表
/*1、通常情況下,BaseMapper 可能包含一些通用的資料庫操作方法的定義,
    比如插入資料、更新資料、刪除資料、查詢資料等。
    而 ArticleMapper 則可以在此基礎上新增針對 Article 實體類的特定資料庫操作方法,如根據標題查詢文章、根據作者查詢文章等。
2、透過這樣的設計,可以實現程式碼的複用,避免重複編寫相似的資料庫操作方法。
    在具體的實現中,可以在 ArticleMapper 介面中編寫與 Article 實體類相關的資料庫操作方法,並在其中呼叫 BaseMapper 中定義的通用方法來實現具體的業務邏輯。
 */
public interface ArticleMapper extends BaseMapper<Article> {
}

TagMapper

package com.cherriesovo.blog.dao.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.cherriesovo.blog.dao.pojo.Tag;

import java.util.List;

public interface TagMapper extends BaseMapper<Tag> {

    //根據文章id查詢標籤列表
    List<Tag> findTagsByArticleId(Long articleId);
}

SysUserMapper

package com.cherriesovo.blog.dao.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.cherriesovo.blog.dao.pojo.SysUser;

public interface SysUserMapper extends BaseMapper<SysUser> {
}

com.cherriesovo.blog.dao,mapper.TagMapper.xml

(注意:xml檔案的目錄結構要與其對應的Mapper保持一致。)

<?xml version="1.0" encoding="UTF-8" ?>
<!--MyBatis配置檔案-->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.mszlu.blog.dao.TagMapper">

    <sql id="all">
        id,avatar,tag_name as tagName
    </sql>

    <select id="findTagsByArticleId" parameterType="long" resultType="com.mszlu.blog.dao.pojo.Tag">
        select <include refid="all" />  from ms_tag
        <where>
            id in
            (select tag_id from ms_article_tag where article_id = #{articleId})
        </where>
    </select>
</mapper>

出現的問題:

org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.cherriesovo.blog.dao.mapper.TagMapper.findTagsByArticleId

解決方法:

手動將resources資料夾設定為資源目錄

2.2.5 測試

3. 首頁-最熱標籤

3.1 介面說明

介面url:/tags/hot

請求方式:GET

請求引數:無

返回資料:

{
    "success": true,
    "code": 200,
    "msg": "success",
    "data": [
        {
            "id":1,
            "tagName":"4444"
        }
    ]
}

3.2 編碼

3.2.1 Controller

package com.cherriesovo.blog.controller;

import com.cherriesovo.blog.service.TagService;
import com.cherriesovo.blog.vo.Result;
import com.cherriesovo.blog.vo.TagVo;
import org.springframework.beans.factory.annotation.Autowired;
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.RestController;

import java.util.List;

@RestController
@RequestMapping("tags")
public class TagsController {

    @Autowired
    private TagService tagsService;

    //路徑:/tags/hot
    @GetMapping("/hot")
    public Result listHotTags() {
        int limit = 6;  //查詢最熱的6個標籤
        List<TagVo> tagVoList = tagsService.hot(limit);
        return Result.success(tagVoList);
    }

}
package com.cherriesovo.blog.vo;

import lombok.Data;

@Data
public class TagVo {

    private  Long id;
    private String tagName;
}

3.2.2 Service

package com.cherriesovo.blog.service;

import com.cherriesovo.blog.vo.TagVo;

import java.util.List;

public interface TagService {
    List<TagVo> hot(int limit);
}
package com.cherriesovo.blog.service.impl;

import com.cherriesovo.blog.dao.mapper.TagMapper;
import com.cherriesovo.blog.dao.pojo.Tag;
import com.cherriesovo.blog.service.TagService;
import com.cherriesovo.blog.vo.Result;
import com.cherriesovo.blog.vo.TagVo;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Collection;
import java.util.Collections;

@Service    //將類標記為服務元件,供其他元件使用
public class TagServiceImpl implements TagService {
    @Autowired
    private TagMapper tagMapper;

    public TagVo copy(Tag tag){
        TagVo tagVo = new TagVo();
        BeanUtils.copyProperties(tag,tagVo);
        return tagVo;
    }
    public List<TagVo> copyList(List<Tag> tagList){
        List<TagVo> tagVoList = new ArrayList<>();
        for (Tag tag : tagList) {
            tagVoList.add(copy(tag));
        }
        return tagVoList;
    }

    //最熱標籤
    @Override
    public List<TagVo> hot(int limit) {
        //什麼是最熱標籤?
        /*
            1、標籤擁有的文章數量最多——最熱標籤
            2、查詢:根據tag_id進行group by 分組、計數,從大到小排序,取前limit
        */
        List<Long> hotsTagIds = tagMapper.findHotsTagIds(limit);
        if (CollectionUtils.isEmpty(hotsTagIds)){
            return Collections.emptyList();
        }
        //需求的是tagId和tagName
        List<Tag> tagList = tagMapper.findTagsByTagIds(hotsTagIds);
        return copyList(tagList);
    }
}


3.2.3 Dao

package com.mszlu.blog.dao;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.mszlu.blog.dao.pojo.Tag;

import java.util.List;

public interface TagMapper extends BaseMapper<Tag> {
    
    List<Long> findHotsTagIds(int limit);
}

<?xml version="1.0" encoding="UTF-8" ?>
<!--MyBatis配置檔案-->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.cherriesovo.blog.dao.mapper.TagMapper">

<!--List<Long> findHotsTagIds(int limit);-->
    <select id="findHotsTagIds" parameterType="int" resultType="long">
        select tag_id from `ms_article_tag` group by tag_id order by count(*) desc limit #{limit}
    </select>
    
</mapper>

3.2.4 測試

相關文章