daily- blog项目
快速搭建项目
创建完数据库对应的表 等操作之后,进入idea
使用EasyCode快速创建工程
- 首先连接数据库
- 然后在对应的表上点击,然后GeneraleCode
- 如果想要删除表名中的前缀 ,就可以使用removePre
- 创建包、实体类、dao等都可以自动生成
自动生成的代码修改
根据自己的需求,修改相应的代码 比如 : 删除其中的继承东西等
我们这里暂时不做任何修改
接下来就是修改实体类对应的信息
比如: 我们需要添加实体类与数据库中表的对应关系用 @TableName("sg_article") , 对于主键自增的字段使用@TableId等
/** * 文章表(Article)表实体类 * * @author Ray2310 * @since 2023-03-11 09:42:17 */ @SuppressWarnings("serial") @TableName("sg_article") public class Article { @TableId 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; }
修改service、dao等层
对于Mapper层 ,因为使用的是Mybatis-plus,所以使用的Mapper就方便很多
public interface ArticleMapper extends BaseMapper<Article> { }
service层
public interface ArticleService extends IService<Article> { }
impl实现类上加注解
@Service public class ArticleServiceImpl extends ServiceImpl<ArticleMapper, Article> implements ArticleService { }
配置Controller及其注意事项
Controller层
@RestController @RequestMapping("/article") public class ArticleController { @Resource private ArticleService articleService; @GetMapping("/list") public List<Article> test(){ List<Article> list = articleService.list(); return list; } }
注意事项:
对于使用模块化项目,我们再配置完成后需要重新install项目,这样不同模块配置的内容才会加载出来
表的设计分析
通用的响应实体类 和 响应枚举
package com.blog.domain; import com.blog.enums.AppHttpCodeEnum; import com.fasterxml.jackson.annotation.JsonInclude; import java.io.Serializable; /** * 响应类 * @param <T> * @author Ray2310 */ @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.blog.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, "必需填写用户名"), CONTENT_NOT_NULL(506, "评论内容不能为空"), FILE_TYPE_ERROR(507, "文件类型错误,请上传png文件"), USERNAME_NOT_NULL(508, "用户名不能为空"), NICKNAME_NOT_NULL(509, "昵称不能为空"), PASSWORD_NOT_NULL(510, "密码不能为空"), EMAIL_NOT_NULL(511, "邮箱不能为空"), NICKNAME_EXIST(512, "昵称已存在"), 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; } }
调用前端接口时出现权限不够
同时会出现无法显示的问题, 那是因为我们前后端不在同一个域中,需要在mvc配置文件中配置跨域连调
@Configuration public class WebConfig implements WebMvcConfigurer { /** * 实现跨域配置 * @param registry */ @Override public void addCorsMappings(CorsRegistry registry) { // 设置允许跨域的路径 registry.addMapping("/**") // 设置允许跨域请求的域名 .allowedOriginPatterns("*") // 是否允许cookie .allowCredentials(true) // 设置允许的请求方式 .allowedMethods("GET", "POST", "DELETE", "PUT") // 设置允许的header属性 .allowedHeaders("*") // 跨域允许时间 .maxAge(3600); } }
博客前台
热门文章列表
需求
查询出浏览量最高的前10篇文章的信息。 要求展示文章标题和浏览量。八能够让用户自己点击跳转到具体的文章详请进行浏览
注意 : 不要把草稿展示出来 ,不要把删除的文章查询出来
接口设计
将返回值使用 通用的返回响应
controller层
//todo 查询热门文章 @GetMapping("/hotArticleList") public ResponseResult hotArticleList(){ //查询热门文章,然后封装成ResponseResult ,然后返回 return articleService.hotArticleList(); }
service层实现
@Service public class ArticleServiceImpl extends ServiceImpl<ArticleMapper, Article> implements ArticleService { //todo 查询热门文章 /*需求: 查询出浏览量最高的前10篇文章的信息。 要求展览示文章标题和浏量。八能够让用户自己点击跳转到具体的文章详请进行浏览 注意 :`不要把草稿展示出来 ,不要把删除的文章查询出来` */ @Override public ResponseResult hotArticleList() { //查询热门文章 封装返回 LambdaQueryWrapper<Article> queryWrapper = new LambdaQueryWrapper<>(); //用 LambdaQueryWrapper 写查询条件 queryWrapper.eq(Article::getStatus,0); queryWrapper.orderByDesc(Article::getViewCount); Page<Article> page = new Page(1,10); //判空 if (ObjectUtils.isEmpty(page)){ return ResponseResult.errorResult(AppHttpCodeEnum.valueOf("暂无热门文章")); } page(page,queryWrapper); List<Article> articles = page.getRecords(); return ResponseResult.okResult(articles); } }
这里老师的代码有错误,分页没有被加进去 ,我们自己需要修改
使用VO优化
需求 : 从我们的出的接口的返回值我们就可以看出,我们需要的只是博客内容的访问量 以及 博客名 而不是所有的内容都返回。这样不仅会造成信息泄露 ,如果文章字数过多,还会造成内存额外消耗。所以我们需要进行优化
经过处理的类我们叫做 VO类型的类
/** * 接口文档中要去响应回去的字段 */ @Data @AllArgsConstructor @NoArgsConstructor public class HotArticle { //文章id private Long id; //文章标题 private String title; //访问量 private Long viewCount; }
然后再通过类型拷贝,就可以将我们需要的数据返回,而不是返回所有【拷贝的原理是两个类的属性相同】
List<Article> articles = page.getRecords(); List<HotArticle> hotArticles = new ArrayList<>(); // 类的赋值拷贝 Article中的某些字段 ---> HotArticle //使用BeanUtils进行拷贝 for (Article article : articles){ HotArticle vo = new HotArticle(); BeanUtils.copyProperties(article,vo); hotArticles.add(vo); } return ResponseResult.okResult(hotArticles);
封装Bean拷贝工具类
/**
* 有关拷贝工具类的封装 * @author Ray2310 */ public class BeanCopyUtils { private BeanCopyUtils(){ } /** * 实现属性拷贝 * @param source * @param clazz * @return */ public static <V> V copyBean(Object source,Class<V> clazz){ //利用反射创建目标对象 V result = null; try { result = clazz.newInstance(); //实现属性的拷贝 BeanUtils.copyProperties(source,result); } catch (Exception e) { e.printStackTrace(); } //返回结果 return result; } /** * 如果是list集合的属性拷贝 ,就直接调用该方法 * @param list 源列表 * @param clazz 目标对象 * @param <V> 需要转换的类型的泛型 * @return 返回转换后的集合 */ public static <O,V> List<V> copyBeanList(List<O> list , Class<V> clazz ){ List<V> collect = list.stream() .map(o -> copyBean(o, clazz)) .collect(Collectors.toList()); return collect; } }
分类列表需求
需求
页面上需要展示分类列表, 用户可以通过点击具体的分类查看该分类下的文章列表。
注意: 1. 要求只展示有发布展示文章的分类 。 2. 必须是正常状态的分类
表信息
接口设计
@RequestMapping("/category") @RestController public class CategoryController { @Resource private CategoryService categoryService; //todo 分类请求 @GetMapping("/getCategoryList") public ResponseResult getCategoryList(){ return categoryService.getCategoryList(); } }
思路:
1.先在文章表中查询 status(文章发布or未发布)为 0 的,也就是发布了的 。还有就是未删除的
2.查出上一步的之后只需要查分类id就可以了(category_id)
3.然后再到category表中查出对应的名称即可
实现
首先我们使用EasyCode生成对应的mapper、pojo实体类等
完成service层等的代码实现
@Service public class CategoryServiceImpl extends ServiceImpl<CategoryMapper, Category> implements CategoryService { @Resource private ArticleService articleService; //todo 分类请求 @Override public ResponseResult getCategoryList() { //1. 先在文章表中查询 status(文章发布or未发布)为 0 的,也就是发布了的 。还有就是未删除的 LambdaQueryWrapper<Article> queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(Article::getStatus, SystemConstants.ARTICLE_STATUS_PUT); List<Article> articles = articleService.list(queryWrapper); //2. 查出上一步的之后只需要查分类id就可以了(category_id) //todo 函数式编程 ,用来将查询到的id查询category Set<Long> categoryIds = articles.stream() .map(new Function<Article, Long>() { @Override public Long apply(Article article) { return article.getCategoryId(); } }).collect(Collectors.toSet()); //3. 然后再到category表中查出对应的名称即可 ,还需要判断分类的状态是正常的 List<Category> categories = listByIds(categoryIds); //4. 判断分类的状态是正常的 List<Category> collect = categories.stream().filter(category -> category.getStatus().equals(SystemConstants.ARTICLE_CATEGORY_STATUS)).collect(Collectors.toList()); //5. 封装状态 List<CategoryVo> categoryVoList = BeanCopyUtils.copyBeanList(collect, CategoryVo.class); return ResponseResult.okResult(categoryVoList); } }
注意点: 了解函数式编程