本项目是前后端分离项目,而我们所做的只有完整的后端开发工作,前端已经写好,故不做任何开发,仅开发后端。项目包含完整的后端中前台和后台的代码编写
前端项目下载链接:
https://pan.baidu.com/s/1TdFs4TqxlHh4DXyLwYuejQ
提取码:mfkw
前端:Vue + ElementUI
后端:SpringBoot + SpringSecurity + Maven + MybatisPlus + Mysql + Redis + EasyExcel + Swagger2 + Echarts
Intellj IDEA 2022.1.4
jdk 1.8.0_381
Apache Maven 3.8.6
mysql 8.0.29
由于项目后端分为前台和后台两个部分,且都有用到相同的实体等等,所以考虑到代码的复用性,我们把后端的前台和后台管理的两个模块的相同代码抽取出来放在一个公共模块里。即两个子模块,一个父模块的多模块项目
两个子模块分别是:博客前台模块keke-blog,博客后台模块keke-admin
公共模块:keke-framework
在F盘中创建一个目录BlogProject然后在该目录下创建模块
将默认的项目结构修改如下
我们把这个工程,作为我们的父级工程,所以src目录就不再需要了,仅需要pom.xml配置文件即可。故把src目录删除,在pom.xml中添加至以下内容(注意要删除上面的properties标签)
4.0.0
com.keke
KekeBlog
1.0-SNAPSHOT
UTF-8
1.8
org.springframework.boot
spring-boot-dependencies
2.5.0
pom
import
com.alibaba
fastjson
1.2.33
io.jsonwebtoken
jjwt
0.9.0
com.baomidou
mybatis-plus-boot-starter
3.4.3
com.aliyun.oss
aliyun-sdk-oss
3.10.2
com.alibaba
easyexcel
3.0.5
io.springfox
springfox-swagger2
2.9.2
io.springfox
springfox-swagger-ui
2.9.2
org.apache.maven.plugins
maven-compiler-plugin
3.1
${java.version}
${java.version}
${project.build.sourceEncoding}
创建公共模块keke-framework,该模块实现依赖统一添加
公共模块的pom.xml文件配置如下,注意不要指定版本,因为其继承了父模块,版本已锁定
KekeBlog
com.keke
1.0-SNAPSHOT
4.0.0
keke-framework
org.springframework.boot
spring-boot-starter-web
org.projectlombok
lombok
true
org.springframework.boot
spring-boot-starter-test
test
org.springframework.boot
spring-boot-starter-data-redis
com.alibaba
fastjson
io.jsonwebtoken
jjwt
com.baomidou
mybatis-plus-boot-starter
mysql
mysql-connector-java
com.aliyun.oss
aliyun-sdk-oss
org.springframework.boot
spring-boot-starter-aop
com.alibaba
easyexcel
io.springfox
springfox-swagger2
io.springfox
springfox-swagger-ui
后台模块依赖于公共模块,所以我们修改pom.xml文件至如下
KekeBlog
com.keke
1.0-SNAPSHOT
4.0.0
keke-admin
com.keke
keke-framework
1.0-SNAPSHOT
可以看到,依赖都加载进入该子模块
操作同博客后台模块一样,在pom.xml文件中写入
KekeBlog
com.keke
1.0-SNAPSHOT
4.0.0
keke-blog
com.keke
keke-framework
1.0-SNAPSHOT
以上操作概括为一个项目为父模块,在父模块中锁定依赖的版本。父模块又有三个子模块,分别为公共模块,博客后台模块和博客前台模块。公共模块中添加项目所需要的模块,博客后台模块和博客前台模块去依赖公共模块,从而达到依赖的继承
可以看到,父模块中多了三个子module,并且KekeBlog为root(根),这样的Maven聚合工程,使得在Maven生命周期中,只需要操作父模块,其余所有模块就都会一起进行操作,省去了逐个模块操作的复杂工作
数据库sql文件不需要自己去写,解压sql.zip文件后有11个sql脚本,全部导入数据库即可
链接:https://pan.baidu.com/s/1DQCGN4wISSDlOkqnVWYwxA
提取码:mfkw
server:
port: 7777
spring:
# 数据库连接信息
datasource:
url: jdbc:mysql://localhost:3306/keke_blog?characterEncoding=utf-8&serverTimezone=Asia/Shanghai
username: root
password:
driver-class-name: com.mysql.cj.jdbc.Driver
servlet:
# 文件上传
multipart:
# 单个上传文件的最大允许大小
max-file-size: 20MB
# HTTP请求中包含的所有文件的总大小的最大允许值
max-request-size: 20MB
mybatis-plus:
configuration:
# 日志
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
# 逻辑删除的字段
logic-delete-field: delFlag
# 代表已删除的值
logic-delete-value: 1
# 代表未删除的值
logic-not-delete-value: 0
# 主键自增策略,以mysql数据库为准
id-type: auto
搜索EasyCode,安装这个插件
第一步连接数据库
第二步右键某个表,选中easycode,generate code
我选择在keke-framework模块下创建实体类,点击确定,查看生成的实体类如下
但它生成的实体类通常跟我们预期的不太符合,比如get/set方法我们期望用lombok去代替,那么我们应该去配置一下(图片上的是我配置好的)
可以在idea的settings里面修改
把entity.java.vm修改为如下,点击应用即可
##导入宏定义
$!{define.vm}
##保存文件(宏定义)
#save("/entity", ".java")
##包路径(宏定义)
#setPackageSuffix("entity")
##自动导入包(全局变量)
$!{autoImport.vm}
import java.io.Serializable;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
##表注释(宏定义)
#tableComment("表实体类")
@SuppressWarnings("serial")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class $!{tableInfo.name} {
#foreach($column in $tableInfo.fullColumn)
#if(${column.comment})//${column.comment}#end
private $!{tool.getClsNameByFullName($column.type)} $!{column.name};
#end
}
把dao.java.vm修改为如下,点击应用即可
##导入宏定义
$!{define.vm}
##设置表后缀(宏定义)
#setTableSuffix("Dao")
##保存文件(宏定义)
#save("/dao", "Dao.java")
##包路径(宏定义)
#setPackageSuffix("dao")
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
##表注释(宏定义)
#tableComment("表数据库访问层")
public interface $!{tableName} extends BaseMapper<$!tableInfo.name> {
}
把service.java.vm修改为如下,点击应用即可
##导入宏定义
$!{define.vm}
##设置表后缀(宏定义)
#setTableSuffix("Service")
##保存文件(宏定义)
#save("/service", "Service.java")
##包路径(宏定义)
#setPackageSuffix("service")
import com.baomidou.mybatisplus.extension.service.IService;
##表注释(宏定义)
#tableComment("表服务接口")
public interface $!{tableName} extends IService<$!tableInfo.name> {
}
把serviceimpl.java.vm修改为如下,点击应用即可
##导入宏定义
$!{define.vm}
##设置表后缀(宏定义)
#setTableSuffix("ServiceImpl")
##保存文件(宏定义)
#save("/service/impl", "ServiceImpl.java")
##包路径(宏定义)
#setPackageSuffix("service.impl")
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
##表注释(宏定义)
#tableComment("表服务实现类")
@Service("$!tool.firstLowerCase($tableInfo.name)Service")
public class $!{tableName} extends ServiceImpl<$!{tableInfo.name}Mapper, $!{tableInfo.name}> implements $!{tableInfo.name}Service {
}
第一步: 在keke-framework工程的src/main/java目录新建com.keke.domain.entity.Article类,写入如下
package com.keke.domain.entity;
import java.util.Date;
import java.io.Serializable;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 文章表(Article)表实体类
*
* @author makejava
* @since 2023-10-10 09:48:35
*/
@SuppressWarnings("serial")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Article {
private Long id;
//标题
private String title;
//文章内容
private String content;
//文章摘要
private String summary;
//所属分类id
private Long categoryId;
//缩略图
private String thumbnail;
//是否置顶(0否,1是)
private String isTop;
//状态(0已发布,1草稿)
private String status;
//访问量
private Long viewCount;
//是否允许评论 1是,0否
private String isComment;
private Long createBy;
private Date createTime;
private Long updateBy;
private Date updateTime;
//删除标志(0代表未删除,1代表已删除)
private Integer delFlag;
}
第二步: 在keke-framework工程的src/main/java目录新建com.keke.mapper.ArticleMapper接口,写入如下
package com.keke.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.keke.domain.entity.entity.Article;
/**
* 文章表(Article)表数据库访问层
*
* @author makejava
* @since 2023-10-10 09:59:36
*/
public interface ArticleMapper extends BaseMapper {
}
第三步: 在keke-framework工程的src/main/java目录新建com.huanf.service.ArticleService接口,写入如下
package com.keke.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.keke.domain.entity.entity.Article;
/**
* 文章表(Article)表服务接口
*
* @author makejava
* @since 2023-10-10 09:59:37
*/
public interface ArticleService extends IService {
}
第四步: 在keke-framework工程的src/main/java目录新建com.keke.service.impl.ArticleServiceImpl类,写入如下
package com.keke.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.keke.domain.entity.entity.Article;
import com.keke.mapper.ArticleMapper;
import com.keke.service.ArticleService;
import org.springframework.stereotype.Service;
/**
* 文章表(Article)表服务实现类
*
* @author makejava
* @since 2023-10-10 09:59:39
*/
@Service("articleService")
public class ArticleServiceImpl extends ServiceImpl implements ArticleService {
}
我们做完上面的一系列代码操作后,包结构应如下
第一步: 在keke-blog工程的src/main/java目录新建com.keke.KeBlogApplication类,作为启动类,写入如下
package com.keke;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class KeBlogApplication {
public static void main(String[] args) {
SpringApplication.run(KeBlogApplication.class,args);
}
}
第二步: 在keke-blog工程的resources目录新建File,文件名为application.yml文件,写入如下
server:
port: 7777
spring:
# 数据库连接信息
datasource:
url: jdbc:mysql://localhost:3306/keke_blog?characterEncoding=utf-8&serverTimezone=Asia/Shanghai
username: root
password:
driver-class-name: com.mysql.cj.jdbc.Driver
servlet:
# 文件上传
multipart:
# 单个上传文件的最大允许大小
max-file-size: 20MB
# HTTP请求中包含的所有文件的总大小的最大允许值
max-request-size: 20MB
mybatis-plus:
configuration:
# mp日志
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
# 逻辑删除的字段
logic-delete-field: delFlag
# 代表已删除的值
logic-delete-value: 1
# 代表未删除的值
logic-not-delete-value: 0
# 主键自增策略,以mysql数据库为准,而不是mp默认的雪花算法
id-type: auto
第三步: 在keke-blog工程的src/main/java目录新建com.keke.controller.ArticleController类,写入如下
package com.keke.controller;
import com.keke.domain.entity.entity.Article;
import com.keke.service.ArticleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.List;
@RestController
@RequestMapping("/article")
public class ArticleController {
@Autowired
private ArticleService articleService;
@GetMapping("/list")
public List test(){
return articleService.list();
}
}
第四步: 在keke-blog工程的KeBlogApplication引导类,修改为如下,主要就是添加了一个Mapper包扫描。不然我们用不了(注入不了)公共模块的类(bean)
package com.keke;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.keke.mapper")
public class KeBlogApplication {
public static void main(String[] args) {
SpringApplication.run(KeBlogApplication.class,args);
}
}
第五步,由于之前keke-blog还是依赖keke-framework公共类的老版本jar包,所以我们需要需要在maven中选择父工程重新install一下
第六步我们启动后访问还是报错,查看错误提示发现是实体类和数据库表没有映射到
果断在实体类添加@TableName注解,代码如下
package com.keke.domain.entity.entity;
import java.util.Date;
import java.io.Serializable;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 文章表(Article)表实体类
*
* @author makejava
* @since 2023-10-10 10:06:59
*/
@SuppressWarnings("serial")
@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("ke_article") //添加表名注解
public class Article {
private Long id;
//标题
private String title;
//文章内容
private String content;
//文章摘要
private String summary;
//所属分类id
private Long categoryId;
//缩略图
private String thumbnail;
//是否置顶(0否,1是)
private String isTop;
//状态(0已发布,1草稿)
private String status;
//访问量
private Long viewCount;
//是否允许评论 1是,0否
private String isComment;
private Long createBy;
private Date createTime;
private Long updateBy;
private Date updateTime;
//删除标志(0代表未删除,1代表已删除)
private Integer delFlag;
}
启动keke-blog工程,访问list接口成功
需要查询浏览量最高的前10篇文章的信息。要求展示文章标题和浏览量。把能让用户自己点击跳转到具体的文章详情进行浏览
注意:不能把草稿展示出来,不能把删除了的文章查询出来。要按照浏览量进行降序排序
第一步: 在keke-framework公共模块的src/main/java目录新建com.keke.enums.AppHttpCodeEnum类,写入如下,作用是封装code和msg
package com.keke.enums;
public enum AppHttpCodeEnum {
// 成功
SUCCESS(200,"操作成功"),
// 登录
NEED_LOGIN(401,"需要登录后操作"),
NO_OPERATOR_AUTH(403,"无权限操作"),
SYSTEM_ERROR(500,"出现错误"),
USERNAME_EXIST(501,"用户名已存在"),
PHONENUMBER_EXIST(502,"手机号已存在"), EMAIL_EXIST(503, "邮箱已存在"),
REQUIRE_USERNAME(504, "必需填写用户名"),
LOGIN_ERROR(505,"用户名或密码错误");
int code;
String msg;
AppHttpCodeEnum(int code, String errorMessage){
this.code = code;
this.msg = errorMessage;
}
public int getCode() {
return code;
}
public String getMsg() {
return msg;
}
}
第二步: 在keke-framework公共模块的domain目录新建ResponseResult类,写入如下,作为统一响应格式的类
package com.keke.domain;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.keke.enums.AppHttpCodeEnum;
import java.io.Serializable;
//统一响应格式。实体类,或者这个类严格来说叫响应体
@JsonInclude(JsonInclude.Include.NON_NULL)
public class ResponseResult implements Serializable {
private Integer code;
private String msg;
private T data;
public ResponseResult() {
this.code = AppHttpCodeEnum.SUCCESS.getCode();
this.msg = AppHttpCodeEnum.SUCCESS.getMsg();
}
public ResponseResult(Integer code, T data) {
this.code = code;
this.data = data;
}
public ResponseResult(Integer code, String msg, T data) {
this.code = code;
this.msg = msg;
this.data = data;
}
public ResponseResult(Integer code, String msg) {
this.code = code;
this.msg = msg;
}
public static ResponseResult errorResult(int code, String msg) {
ResponseResult result = new ResponseResult();
return result.error(code, msg);
}
//空参构造方法,这里会创建一个对象,msg和code都默认是成功的值,适用处理一些响应简单的接口
public static ResponseResult okResult() {
ResponseResult result = new ResponseResult();
return result;
}
public static ResponseResult okResult(int code, String msg) {
ResponseResult result = new ResponseResult();
return result.ok(code, null, msg);
}
public static ResponseResult okResult(Object data) {
ResponseResult result = setAppHttpCodeEnum(AppHttpCodeEnum.SUCCESS, AppHttpCodeEnum.SUCCESS.getMsg());
if(data!=null) {
result.setData(data);
}
return result;
}
public static ResponseResult errorResult(AppHttpCodeEnum enums){
return setAppHttpCodeEnum(enums,enums.getMsg());
}
public static ResponseResult errorResult(AppHttpCodeEnum enums, String msg){
return setAppHttpCodeEnum(enums,msg);
}
public static ResponseResult setAppHttpCodeEnum(AppHttpCodeEnum enums){
return okResult(enums.getCode(),enums.getMsg());
}
private static ResponseResult setAppHttpCodeEnum(AppHttpCodeEnum enums, String msg){
return okResult(enums.getCode(),msg);
}
public ResponseResult> error(Integer code, String msg) {
this.code = code;
this.msg = msg;
return this;
}
public ResponseResult> ok(Integer code, T data) {
this.code = code;
this.data = data;
return this;
}
public ResponseResult> ok(Integer code, T data, String msg) {
this.code = code;
this.data = data;
this.msg = msg;
return this;
}
public ResponseResult> ok(T data) {
this.data = data;
return this;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
第一步: 把keke-framework公共模块的ArticleService修改为如下,定义了hotArticleList方法
package com.keke.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.keke.domain.ResponseResult;
import com.keke.domain.entity.entity.Article;
/**
* 文章表(Article)表服务接口
*
* @author makejava
* @since 2023-10-10 09:59:37
*/
public interface ArticleService extends IService {
ResponseResult hotArticleList();
}
第二步: 把keke-framework公共模块的ArticleServiceImpl修改为如下,实现了hotArticleList方法
package com.keke.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.keke.domain.ResponseResult;
import com.keke.domain.entity.entity.Article;
import com.keke.mapper.ArticleMapper;
import com.keke.service.ArticleService;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 文章表(Article)表服务实现类
*
* @author makejava
* @since 2023-10-10 09:59:39
*/
@Service("articleService")
public class ArticleServiceImpl extends ServiceImpl implements ArticleService {
@Override
public ResponseResult hotArticleList() {
//查询热门文章 封装成ResponseResult返回
LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>();
//必须是正式文章
lambdaQueryWrapper.eq(Article::getStatus,0);
//按照浏览量进行排序
lambdaQueryWrapper.orderByDesc(Article::getViewCount);
//最多查询10条,设置mp分页对象的参数分别为1和10
Page page = new Page<>(1,10);
//将page对象和lambdaQueryWrapper查询条件封装成page
page(page,lambdaQueryWrapper);
//page.getRecords()获取到所有符合条件的数据(也就是文章)
List articles = page.getRecords();
//返回ResponseResult对象
return ResponseResult.okResult(articles);
}
}
第三步: 把keke-blog工程的ArticleController类,修改为如下,增加了文章列表的统一响应格式的代码
package com.keke.controller;
import com.keke.domain.ResponseResult;
import com.keke.domain.entity.entity.Article;
import com.keke.service.ArticleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.List;
@RestController
@RequestMapping("/article")
public class ArticleController {
@Autowired
private ArticleService articleService;
//测试
@GetMapping("/list")
public List test(){
return articleService.list();
}
//热门文章
@GetMapping("/hotArticleList")
public ResponseResult hotArticleList(){
ResponseResult result = articleService.hotArticleList();
return result;
}
}
第四步用postman发送请求,可以看到后端返回的响应数据,在控制台也打印出了mp的sql日志
第一步,解压前端工程,创建web目录结构如下,解压至如下目录
第二步: 运行前端项目,请确保电脑有安装node.js,然后以管理员身份打开命令行窗口,输入如下
cd f:
cd /BlogProject/web/keke-blog-vue
npm install
npm run dev
执行完上述操作后就会出现如下
打开前端页面http://localhost:8080后,发现并没有显示出来热门文章列表,F12打开控制台发现是CORS报错,原因是后端没有解决跨域问题,没有放行一些请求
在keke-framework工程的src/main/java目录新建com.keke.config.WebConfig类,写入如下,然后重新运行keke-blog工程的启动类
package com.keke.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
// 设置允许跨域的路径
registry.addMapping("/**")
// 设置允许跨域请求的域名
.allowedOriginPatterns("*")
// 是否允许cookie
.allowCredentials(true)
// 设置允许的请求方式
.allowedMethods("GET", "POST", "DELETE", "PUT")
// 设置允许的header属性
.allowedHeaders("*")
// 跨域允许时间
.maxAge(3600);
}
}
再次启动工程,访问前端页面,就可以看到热门文章列表了
我们在查询热门文章列表时,后端返回给前端的数据中,可以看到是返回了所有字段的数据
但通常前端是不需要所有字段的数据的,仅仅需要表中某几个字段的数据即可,并且在实际项目中, 有些敏感字段是不能返回给前端使得前台用户看到,为了满足这一需求,我们通常把响应给前端的数据(前端需要的数据所封装而成的对象)称为VO(是Value Object的缩写,表示值对象)
下面来创建热门文章VO,并把完善ServiceImpl代码
在keke-frameword工程的src/main/java目录新建com.keke.vo.HotArticleVO类,写入如下
package com.keke.domain.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class HotArticleVo {
//id
private Long id;
//标题
private String title;
//访问量
private Long viewCount;
}
这个工具类主要是实现不同Bean之间的相互转换,把一个bean对象中的字段拷贝到另一个bean对象的字段
它在spring项目中常用来封装VO对象向前端传递数据。
两对象中对应字段名和类型应完全相同,否则无法拷贝
在keke-framework工程的src/main/java目录新建com.keke.utils.BeanCopyUtils类,写入如下
package com.keke.utils;
import org.springframework.beans.BeanUtils;
import java.util.List;
import java.util.stream.Collectors;
//这个类用到很多泛型知识,可以对应去补一下
public class BeanCopyUtils {
//私有的空参构造方法
private BeanCopyUtils() {
}
//1.单个实体类的拷贝(暂时还用不上)。第一个参数是要拷贝的对象,第二个参数是类的字节码对象
public static V copyBean(Object source,Class clazz) {
//创建目标对象
V result = null;
try {
result = clazz.newInstance();
//实现属性拷贝。也就是把source的属性拷贝到这个目标对象。BeanUtils是spring提供的工具类
BeanUtils.copyProperties(source, result);
} catch (Exception e) {
e.printStackTrace();
}
//返回结果
return result;
}
//2.集合的拷贝(在ArticleServiceImpl类里面会使用到)。第一个参数是要拷贝的集合,第二个参数是类的字节码对象
public static List copyBeanList(List list,Class clazz){
//不使用for循环,使用stream流进行转换
return list.stream()
.map(o -> copyBean(o, clazz))
//把结果转换成集合
.collect(Collectors.toList());
}
}
实际项目中都不允许直接在代码中使用字面值(代码中的固定值)。都需要定义成常量来使用。这种方式有利于提高代码的可维护性。字面值如下图
在keke-framework工程的src/main/java目录新建com.keke.constants.SystemConstants类,写入如下,作用是定义常量
package com.keke.constants;
//字面值(代码中的固定值)处理,把字面值都在这里定义成常量
public class SystemConstants {
/**
* 文章是草稿
*/
public static final int ARTICLE_STATUS_DRAFT = 1;
/**
* 文章是正常分布状态
*/
public static final int ARTICLE_STATUS_NORMAL = 0;
/**
* 文章列表当前查询页数
*/
public static final int ARTICLE_STATUS_CURRENT = 1;
/**
* 文章列表每页显示的数据条数
*/
public static final int ARTICLE_STATUS_SIZE = 10;
}
把keke-framework工程的ArticleServiceImpl类修改为如下,调用BeanCopyUtils类的copyBeanList方法,将Article和HotArticlVo之间进行转换
package com.keke.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.keke.constants.SystemConstants;
import com.keke.domain.ResponseResult;
import com.keke.domain.entity.entity.Article;
import com.keke.domain.vo.HotArticleVo;
import com.keke.mapper.ArticleMapper;
import com.keke.service.ArticleService;
import com.keke.utils.BeanCopyUtils;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 文章表(Article)表服务实现类
*
* @author makejava
* @since 2023-10-10 09:59:39
*/
@Service("articleService")
public class ArticleServiceImpl extends ServiceImpl implements ArticleService {
@Override
public ResponseResult hotArticleList() {
//查询热门文章 封装成ResponseResult返回
LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>();
//必须是正式文章
lambdaQueryWrapper.eq(Article::getStatus, SystemConstants.ARTICLE_STATUS_NORMAL);
//按照浏览量进行排序
lambdaQueryWrapper.orderByDesc(Article::getViewCount);
//最多查询10条,设置mp分页对象的参数分别为1和10
Page page = new Page<>(SystemConstants.ARTICLE_STATUS_CURRENT,SystemConstants.ARTICLE_STATUS_SIZE);
//将page对象和lambdaQueryWrapper查询条件封装成page
page(page,lambdaQueryWrapper);
//page.getRecords()获取到所有符合条件的数据(也就是文章)
List articles = page.getRecords();
//BeanCopy
List hotArticleVos = BeanCopyUtils.copyBeanList(articles, HotArticleVo.class);
//返回ResponseResult对象
return ResponseResult.okResult(hotArticleVos);
}
}