热门文章列表
大约 5 分钟项目实战前后端分离博客项目
需求分析
需要查询浏览量最高的前10篇
文章的信息。要求展示文章标题
和浏览量
。把能让用户自己点击跳转
到具体的文章详情进行浏览。
注意:不能把草稿展示出来,不能把删除了的文章查询出来
。要按照浏览量进行降序排序
。
接口设计
请求地址:
/article/hotArticleList
请求方式:
GET
不需要请求参数。
响应格式:
{ "code": 200, "data": [ { "id": "1004", "title": "哔哩哔哩", "viewCount": "2233" }, { "id": "1000", "title": "SpringSecurity从入门到精通", "viewCount": "118" }, { "id": "1003", "title": "sdad", "viewCount": "44" } ], "msg": "操作成功" }
基础版本代码实现
统一响应格式和枚举响应码
这些是通用的,放在公共模块sanfen-framework
。
统一响应类
package com.sanfen.domain;
@JsonInclude(JsonInclude.Include.NON_NULL)
public class ResponseResult<T> 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);
}
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;
}
}
响应码枚举类
package com.sanfen.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, "必需填写用户名"),
REQUIRE_NICKNAME(508, "必需填写昵称"),
REQUIRE_EMAIL(509, "必需填写邮箱"),
REQUIRE_PASSWORD(510, "必需填写密码"),
LOGIN_ERROR(505,"用户名或密码错误"),
CONTENT_NOT_NULL(506, "评论内容不能为空"),
FILE_TYPE_ERROR(507,"文件类型错误, 请上传png图片"),
NICKNAME_EXIST(511, "昵称已存在");
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;
}
}
热门文章接口编写
controller
@RestController
@RequestMapping("/article")
public class ArticleController {
@Autowired
private ArticleService articleService;
/**
* 查询浏览量前10的文章
* @return 结果
*/
@GetMapping("/hotArticleList")
public ResponseResult hotArticleList(){
return articleService.hotArticleList();
}
}
service
@Service("articleService")
public class ArticleServiceImpl extends ServiceImpl<ArticleMapper, Article> implements ArticleService {
@Override
public ResponseResult hotArticleList() {
LambdaQueryWrapper<Article> queryWrapper = new LambdaQueryWrapper<>();
// 必须是正式文章
queryWrapper.eq(Article::getStatus, 0);
// 按浏览量降序
queryWrapper.orderByDesc(Article::getViewCount);
// 最多只查询10条
Page<Article> page = new Page<>(1, 10);
page(page);
List<Article> articles = page.getRecords();
return ResponseResult.okResult(articles);
}
}
测试:访问http://localhost:7777/article/hotArticleList

可以发现返回数据是文章表的全部字段,但是热门文章不需要这么多。我们可以使用VO进行优化,每个接口对应一个VO。
解决跨域问题
与VUE项目进行联调的时候,会出现跨域。所以我们需要配置一下。配置类博客前台和后台都会用,放到公共模块sanfen-framework
:
package com.hejin.config;
@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优化
目前我们的响应格式其实是不符合接口文档的标准的,多返回了很多字段。这是因为我们查询出来的结果是Article来封装的,Article中字段比较多。
我们在项目中一般最后还要把VO来接受查询出来的结果。一个接口对应一个VO,这样即使接口响应字段要修改也只要改VO即可
。
bean拷贝工具类
使用VO的时候涉及到bean的拷贝,使用工具类:
package com.hejin.util;
public class BeanCopyUtils {
private BeanCopyUtils() {
}
public static <V> V copyBean(Object source,Class<V> clazz) {
//创建目标对象
V result = null;
try {
result = clazz.newInstance();
//实现属性copy
BeanUtils.copyProperties(source, result);
} catch (Exception e) {
e.printStackTrace();
}
//返回结果
return result;
}
public static <O,V> List<V> copyBeanList(List<O> list, Class<V> clazz){
return list.stream()
.map(o -> copyBean(o, clazz))
.collect(Collectors.toList());
}
}
字面量处理
对于字面值,比如前10的热门文章。这个10一般使用静态常量来定义,比较好维护。不建议直接写在代码里。
package com.hejin.constants;
public class SystemConstants
{
/**
* 文章是草稿
*/
public static final int ARTICLE_STATUS_DRAFT = 1;
/**
* 文章是正常分布状态
*/
public static final int ARTICLE_STATUS_NORMAL = 0;
}
新建HotArticleVo
在sanfen-framework
新建HotArticleVo类:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class HotArticleVo {
private Long id;
/**
* 标题
*/
private String title;
/**
* 访问量
*/
private Long viewCount;
/**
* 热门文章页码
*/
public static final int HOT_ARTICLE_PAGE = 1;
/**
* 热门文章数量
*/
public static final int HOT_ARTICLE_SIZE = 10;
}
sanfen-framework
此时的项目结构:

修改ArticleServiceImpl
@Service("articleService")
public class ArticleServiceImpl extends ServiceImpl<ArticleMapper, Article> implements ArticleService {
@Override
public ResponseResult hotArticleList() {
LambdaQueryWrapper<Article> queryWrapper = new LambdaQueryWrapper<>();
// 必须是正式文章
queryWrapper.eq(Article::getStatus, SystemConstants.ARTICLE_STATUS_NORMAL);
// 按浏览量降序
queryWrapper.orderByDesc(Article::getViewCount);
// 最多只查询10条
Page<Article> page = new Page<>(SystemConstants.HOT_ARTICLE_PAGE, SystemConstants.HOT_ARTICLE_SIZE);
page(page);
List<Article> articles = page.getRecords();
// bean拷贝
List<HotArticleVo> hotArticleVos = BeanCopyUtils.copyBeanList(articles, HotArticleVo.class);
return ResponseResult.okResult(hotArticleVos);
}
}
测试访问:
http://localhost:7777/article/hotArticleList
