1. 工程搭建 前端的工程運行流程: 進入項目目錄執行cmd命令: 若是第一次啟動需要依次輸入如下命令: npm install npm run build npm run dev 之後直接執行 npm run dev 即可! 1.1 新建maven工程 新建maven工程blog作為父工程,然後在 ...
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)的請求訪問資源。
常見的跨域配置包括:
- 設置響應頭:後端伺服器可以在 HTTP 響應頭中添加特定的欄位,如
Access-Control-Allow-Origin
、Access-Control-Allow-Methods
等,來指定允許跨域請求的來源、請求方法等信息。- 使用代理:在開發環境中,可以配置代理伺服器來轉發 API 請求,使得前端代碼和後端 API 請求處於同一個功能變數名稱下,避免跨域問題。
- JSONP 跨域請求:JSONP 是一種跨域請求的方式,通過動態創建
<script>
標簽實現跨域數據獲取,不受同源策略的限制。- 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.pojo和com.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()
方法,用於告知資料庫查詢時的分頁信息,以及接收資料庫返回的分頁查詢結果。
copy
方法用於將Article
對象轉換為ArticleVo
對象,並根據參數isAuthor
、isBody
和isTags
決定是否需要拷貝作者信息、文章正文和標簽信息。
- 使用
BeanUtils.copyProperties
方法將Article
對象article
的屬性複製到articleVo
中。這裡使用了 Spring 的BeanUtils
工具類來實現屬性的拷貝。- 將
Article
對象的創建時間屬性createDate
轉換為指定格式的字元串,並設置到articleVo
對象的createDate
屬性中。這裡使用了 Joda-Time 庫的DateTime
類來進行日期時間的格式化。- 如果
isTags
參數為true
,則通過tagService.findTagsByArticleId(articleId)
方法獲取該文章的標簽信息,並設置到articleVo
對象的tags
屬性中。- 如果
isAuthor
參數為true
,則通過sysUserService.findUserById(authorId)
方法獲取作者的用戶信息,並從中獲取昵稱(使用getNickname()
方法),然後將昵稱設置到articleVo
對象的author
屬性中- listArticlesPage分頁查詢文章列表,並將查詢結果轉換為對應的
ArticleVo
對象列表返回給調用方。
- 首先創建了一個
QueryWrapper<Article>
對象queryWrapper
,用於構建查詢條件。然後創建了一個Page<Article>
對象page
,表示要查詢的分頁信息- 調用
articleMapper.selectPage(page, queryWrapper)
方法進行分頁查詢文章數據。這裡使用了 MyBatis-Plus 提供的selectPage
方法,它會根據傳入的Page
對象和查詢條件進行分頁查詢,並返回一個分頁後的文章數據對象articlePage
- 通過調用
getRecords()
方法,可以獲取當前頁的記錄列表,即符合查詢條件的文章記錄列表。這個方法會返回一個List<Article>
對象,其中包含了當前頁的所有文章記錄。- 將分頁查詢得到的文章記錄列表
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>