Daily-Blog项目前台日志(上)

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: Daily-Blog项目前台日志(上)

daily- blog项目



快速搭建项目


创建完数据库对应的表 等操作之后,进入idea


使用EasyCode快速创建工程


  1. 首先连接数据库
  2. 然后在对应的表上点击,然后GeneraleCode
  3. 如果想要删除表名中的前缀 ,就可以使用removePre
  4. 创建包、实体类、dao等都可以自动生成


image-20230311094114784.png


自动生成的代码修改


根据自己的需求,修改相应的代码 比如 : 删除其中的继承东西等


我们这里暂时不做任何修改


image-20230311101039158.png


接下来就是修改实体类对应的信息


比如: 我们需要添加实体类与数据库中表的对应关系用 @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项目,这样不同模块配置的内容才会加载出来


image-20230311101949457.png


表的设计分析


image-20230311102627224.png


通用的响应实体类 和 响应枚举


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;
    }
}

调用前端接口时出现权限不够


image-20230311130111226.png


同时会出现无法显示的问题, 那是因为我们前后端不在同一个域中,需要在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);
    }
}


博客前台



热门文章列表


image-20230311102117772.png



需求


查询出浏览量最高的前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优化


image-20230311141200213.png

需求 : 从我们的出的接口的返回值我们就可以看出,我们需要的只是博客内容的访问量 以及 博客名 而不是所有的内容都返回。这样不仅会造成信息泄露 ,如果文章字数过多,还会造成内存额外消耗。所以我们需要进行优化


经过处理的类我们叫做 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;
    }
}


分类列表需求

image-20230311145817223.png


需求


页面上需要展示分类列表, 用户可以通过点击具体的分类查看该分类下的文章列表。


注意: 1. 要求只展示有发布展示文章的分类 。 2. 必须是正常状态的分类


表信息


image-20230311150302648.png


接口设计


image-20230311151015334.png


@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);
    }
}

注意点: 了解函数式编程



相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
18天前
|
JSON Java 数据库
SpringBoot项目使用AOP及自定义注解保存操作日志
SpringBoot项目使用AOP及自定义注解保存操作日志
34 1
|
3月前
|
JSON 中间件 Go
go语言后端开发学习(四) —— 在go项目中使用Zap日志库
本文详细介绍了如何在Go项目中集成并配置Zap日志库。首先通过`go get -u go.uber.org/zap`命令安装Zap,接着展示了`Logger`与`Sugared Logger`两种日志记录器的基本用法。随后深入探讨了Zap的高级配置,包括如何将日志输出至文件、调整时间格式、记录调用者信息以及日志分割等。最后,文章演示了如何在gin框架中集成Zap,通过自定义中间件实现了日志记录和异常恢复功能。通过这些步骤,读者可以掌握Zap在实际项目中的应用与定制方法
131 1
go语言后端开发学习(四) —— 在go项目中使用Zap日志库
|
3月前
|
开发框架 .NET Docker
【Azure 应用服务】App Service .NET Core项目在Program.cs中自定义添加的logger.LogInformation,部署到App Service上后日志不显示Log Stream中的问题
【Azure 应用服务】App Service .NET Core项目在Program.cs中自定义添加的logger.LogInformation,部署到App Service上后日志不显示Log Stream中的问题
|
3月前
|
XML Java Maven
logback在springBoot项目中的使用 springboot中使用日志进行持久化保存日志信息
这篇文章详细介绍了如何在Spring Boot项目中使用logback进行日志记录,包括Maven依赖配置、logback配置文件的编写,以及实现的日志持久化和控制台输出效果。
logback在springBoot项目中的使用 springboot中使用日志进行持久化保存日志信息
|
3月前
|
数据可视化 Java API
如何在项目中快速引入Logback日志并搭配ELK使用
如何在项目中快速引入Logback日志并搭配ELK使用
|
3月前
|
开发框架 .NET API
如何在 ASP.NET Core Web Api 项目中应用 NLog 写日志?
如何在 ASP.NET Core Web Api 项目中应用 NLog 写日志?
178 0
|
3月前
|
监控 程序员 数据库
分享一个 .NET Core Console 项目中应用 NLog 写日志的详细例子
分享一个 .NET Core Console 项目中应用 NLog 写日志的详细例子
|
14天前
|
XML 安全 Java
【日志框架整合】Slf4j、Log4j、Log4j2、Logback配置模板
本文介绍了Java日志框架的基本概念和使用方法,重点讨论了SLF4J、Log4j、Logback和Log4j2之间的关系及其性能对比。SLF4J作为一个日志抽象层,允许开发者使用统一的日志接口,而Log4j、Logback和Log4j2则是具体的日志实现框架。Log4j2在性能上优于Logback,推荐在新项目中使用。文章还详细说明了如何在Spring Boot项目中配置Log4j2和Logback,以及如何使用Lombok简化日志记录。最后,提供了一些日志配置的最佳实践,包括滚动日志、统一日志格式和提高日志性能的方法。
123 30
【日志框架整合】Slf4j、Log4j、Log4j2、Logback配置模板
|
1月前
|
XML JSON Java
Logback 与 log4j2 性能对比:谁才是日志框架的性能王者?
【10月更文挑战第5天】在Java开发中,日志框架是不可或缺的工具,它们帮助我们记录系统运行时的信息、警告和错误,对于开发人员来说至关重要。在众多日志框架中,Logback和log4j2以其卓越的性能和丰富的功能脱颖而出,成为开发者们的首选。本文将深入探讨Logback与log4j2在性能方面的对比,通过详细的分析和实例,帮助大家理解两者之间的性能差异,以便在实际项目中做出更明智的选择。
226 3
|
1月前
|
存储 缓存 关系型数据库
MySQL事务日志-Redo Log工作原理分析
事务的隔离性和原子性分别通过锁和事务日志实现,而持久性则依赖于事务日志中的`Redo Log`。在MySQL中,`Redo Log`确保已提交事务的数据能持久保存,即使系统崩溃也能通过重做日志恢复数据。其工作原理是记录数据在内存中的更改,待事务提交时写入磁盘。此外,`Redo Log`采用简单的物理日志格式和高效的顺序IO,确保快速提交。通过不同的落盘策略,可在性能和安全性之间做出权衡。
1633 14