隐藏页面特效

个人博客项目笔记_07

1|0写文章


写文章需要 三个接口:

  1. 获取所有文章类别

  2. 获取所有标签

  3. 发布文章

2|01. 所有文章分类


2|11.1 接口说明


接口url:/categorys

请求方式:GET

请求参数:

参数名称 参数类型 说明

返回数据:

{ "success":true, "code":200, "msg":"success", "data": [ {"id":1,"avatar":"/category/front.png","categoryName":"前端"}, {"id":2,"avatar":"/category/back.png","categoryName":"后端"}, {"id":3,"avatar":"/category/lift.jpg","categoryName":"生活"}, {"id":4,"avatar":"/category/database.png","categoryName":"数据库"}, {"id":5,"avatar":"/category/language.png","categoryName":"编程语言"} ] }

2|21.2 Controller


package com.cherriesovo.blog.controller; import com.cherriesovo.blog.service.CategoryService; import com.cherriesovo.blog.vo.Result; 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; @RestController @RequestMapping("categorys") public class CategoryController { @Autowired private CategoryService categoryService; @GetMapping public Result listCategory() { return categoryService.findAll(); } }

2|31.3 Service


public interface CategoryService { Result findAll(); //类别查询 }
@Service public class CategoryServiceImpl implements CategoryService { public CategoryVo copy(Category category){ CategoryVo categoryVo = new CategoryVo(); BeanUtils.copyProperties(category,categoryVo); return categoryVo; } public List<CategoryVo> copyList(List<Category> categoryList){ List<CategoryVo> categoryVoList = new ArrayList<>(); for (Category category : categoryList) { categoryVoList.add(copy(category)); } return categoryVoList; } @Override public Result findAll() { //SELECT * FROM category; List<Category> categories = this.categoryMapper.selectList(new LambdaQueryWrapper<>()); return Result.success(copyList(categories)); } }

3|02. 所有文章标签


3|12.1 接口说明


接口url:/tags

请求方式:GET

请求参数:

参数名称 参数类型 说明

返回数据:

{ "success": true, "code": 200, "msg": "success", "data": [ { "id": 5, "tagName": "springboot" }, { "id": 6, "tagName": "spring" }, { "id": 7, "tagName": "springmvc" }, { "id": 8, "tagName": "11" } ] }

3|22.2 Controller


@RestController @RequestMapping("tags") public class TagsController { @Autowired private TagService tagsService; @GetMapping public Result findAll(){ return tagsService.findAll(); } }

3|32.3 Service


public interface TagService { Result findAll(); //查询所有的文章标签 }

TagServiceImpl:

@Override public Result findAll() { //SELECT * FROM tag; List<Tag> tags = this.tagMapper.selectList(new LambdaQueryWrapper<>()); return Result.success(copyList(tags)); }

4|03. 发布文章


4|13.1 接口说明


接口url:/articles/publish

请求方式:POST

请求参数:

参数名称 参数类型 说明
title string 文章标题
id long 文章id(编辑有值)
body object({content: "ww", contentHtml: "

ww

↵"})
文章内容
category 文章类别
summary string 文章概述
tags [{id: 5}, {id: 6}] 文章标签

返回数据:

{ "success": true, "code": 200, "msg": "success", "data": {"id":12232323} }

4|23.2 Controller


package com.cherriesovo.blog.vo.params; import com.cherriesovo.blog.vo.CategoryVo; import com.cherriesovo.blog.vo.TagVo; import lombok.Data; import java.util.List; @Data public class ArticleParam { private Long id; private ArticleBodyParam body; private CategoryVo category; private String summary; private List<TagVo> tags; private String title; }
package com.cherriesovo.blog.vo.params; import lombok.Data; @Data public class ArticleBodyParam { private String content; private String contentHtml; }
//json数据进行交互 @RestController @RequestMapping("articles") public class ArticleController { /* * 发布文章 * */ @PostMapping("publish") public Result publish(@RequestBody ArticleParam articleParam){ return articleService.publish(articleParam); } }

4|33.3 Service


public interface ArticleService { //文章发布 Result publish(ArticleParam articleParam); }

ArticleServiceImpl:

ArticleServiceImpl共需要经历如下步骤:

  1. 创建一个 Article 对象,并设置其属性,最后将文章对象插入到数据库中。

    Article article = new Article(); article.setAuthorId(sysUser.getId()); article.setCategoryId(articleParam.getCategory().getId()); article.setCreateDate(System.currentTimeMillis()); article.setCommentCounts(0); article.setSummary(articleParam.getSummary()); //摘要 article.setTitle(articleParam.getTitle()); article.setViewCounts(0); article.setWeight(Article.Article_Common); //设置了文章的 bodyId 属性为 -1L。通常情况下,-1L 通常被用作一个特殊的标记,表示某个值无效或未设置 article.setBodyId(-1L); //内容id //插入之后会自动生成一个文章id this.articleMapper.insert(article);
  2. 将文章id与标签id进行关联——获取文章的标签列表,遍历标签列表,对每个标签执行以下操作:

    1. 创建一个 ArticleTag 对象,并设置其文章ID和标签ID。
    2. 将 ArticleTag 对象插入到数据库中(article_tag表)。
    List<TagVo> tags = articleParam.getTags(); if (tags != null) { for (TagVo tag : tags) { ArticleTag articleTag = new ArticleTag(); articleTag.setArticleId(article.getId()); articleTag.setTagId(tag.getId()); this.articleTagMapper.insert(articleTag); } }
  3. 文章内容存储(article_body表)

    ArticleBody articleBody = new ArticleBody(); articleBody.setContent(articleParam.getBody().getContent()); articleBody.setContentHtml(articleParam.getBody().getContentHtml()); articleBody.setArticleId(article.getId()); articleBodyMapper.insert(articleBody);
  4. 更新article表中的body属性

    article.setBodyId(articleBody.getId()); articleMapper.updateById(article);
  5. 设置返回值

    ArticleVo articleVo = new ArticleVo(); articleVo.setId(article.getId()); return Result.success(articleVo);
@Override @Transactional public Result publish(ArticleParam articleParam) { /* * 1、发布文章目的是构建article对象 * 2、作者id——当前的登录用户 * 3、要将标签加入到关联列表 * 4、body 内容存储 要的是bodyId * */ //此接口要加入到登录拦截中,否则会造成空指针异常 SysUser sysUser = UserThreadLocal.get(); Article article = new Article(); article.setAuthorId(sysUser.getId()); article.setCategoryId(articleParam.getCategory().getId()); article.setCreateDate(System.currentTimeMillis()); article.setCommentCounts(0); article.setSummary(articleParam.getSummary()); //摘要 article.setTitle(articleParam.getTitle()); article.setViewCounts(0); article.setWeight(Article.Article_Common); //设置了文章的 bodyId 属性为 -1L。通常情况下,-1L 通常被用作一个特殊的标记,表示某个值无效或未设置 article.setBodyId(-1L); //内容id //插入之后会自动生成一个文章id this.articleMapper.insert(article); List<TagVo> tags = articleParam.getTags(); if (tags != null) { for (TagVo tag : tags) { ArticleTag articleTag = new ArticleTag(); articleTag.setArticleId(article.getId()); articleTag.setTagId(tag.getId()); this.articleTagMapper.insert(articleTag); } } //body内容存储(article_body表) ArticleBody articleBody = new ArticleBody(); articleBody.setContent(articleParam.getBody().getContent()); articleBody.setContentHtml(articleParam.getBody().getContentHtml()); articleBody.setArticleId(article.getId()); articleBodyMapper.insert(articleBody); //更新article表中的body article.setBodyId(articleBody.getId()); articleMapper.updateById(article); //设置返回值 ArticleVo articleVo = new ArticleVo(); articleVo.setId(article.getId()); return Result.success(articleVo); }
package com.cherriesovo.blog.dao.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.cherriesovo.blog.dao.pojo.ArticleTag; public interface ArticleTagMapper extends BaseMapper<ArticleTag> { }
package com.cherriesovo.blog.dao.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.cherriesovo.blog.dao.pojo.ArticleBody; public interface ArticleBodyMapper extends BaseMapper<ArticleBody> { }
package com.cherriesovo.blog.vo; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; import lombok.Data; import java.util.List; @Data public class ArticleVo { //一定要记得加 要不然 会出现精度损失 @JsonSerialize(using = ToStringSerializer.class) private Long id; private String title; private String summary; private Integer commentCounts; private Integer viewCounts; private Integer weight; /** * 创建时间 */ private String createDate; private String author; private ArticleBodyVo body; private List<TagVo> tags; private CategoryVo category; }
package com.cherriesovo.blog.dao.pojo; import lombok.Data; @Data public class ArticleTag { private Long id; private Long articleId; private Long tagId; }

当然登录拦截器中,需要加入发布文章的配置:

WebMVCConfig:

@Override public void addInterceptors(InterceptorRegistry registry) { //拦截test接口,后续实际遇到需要拦截的接口时,在配置为真正的拦截接口 registry.addInterceptor(loginInterceptor) .addPathPatterns("/test") .addPathPatterns("/comments/create/change") .addPathPatterns("/articles/publish"); }

4|43.4 测试


5|04. AOP日志


定义一个自定义注解 LogAnnotation,用于在方法上添加日志相关的注解信息:

  • @Target(ElementType.METHOD):这个注解指定了 LogAnnotation 注解可以被应用于方法上。
  • @Retention(RetentionPolicy.RUNTIME):这个注解指定了 LogAnnotation 注解在运行时可见。
  • @Documented:这个注解指定了 LogAnnotation 注解将被包含在 Javadoc 中。
  • String module() default "";:这个注解定义了一个 module 属性,用于指定日志的模块,默认值为空字符串。
  • String operator() default "";:这个注解定义了一个 operator 属性,用于指定执行操作的操作者,默认值为空字符串。

这个自定义注解可以用于方法上,用于标记需要记录日志的方法,并且可以通过 moduleoperator 属性指定日志的模块和操作者。

Javadoc 是 Java 语言中用于生成 API 文档的工具。它能够根据源代码中的特定标记,自动生成与代码相关的文档。Javadoc 工具会扫描 Java 源代码中特定格式的注释,并根据这些注释生成 HTML 格式的 API 文档。这些注释通常以 /** 开头,以 */ 结尾,位于类、方法、字段等代码元素的前面。Javadoc 工具会解析这些注释中的标签和内容,并生成易于阅读和导航的 API 文档。)

package com.cherriesovo.blog.common.aop; import java.lang.annotation.*; /** * 日志注解 */ //TYPE代表可以放在类上面,METHOD代表可以放在方法上 @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface LogAnnotation { String module() default ""; String operator() default ""; }

LogAspect是一个使用了 Spring AOP的日志切面类:

  • @Aspect:这个注解标识了这个类是一个切面类,用于定义通知和切点的关系。

  • @Pointcut("@annotation(com.cherriesovo.blog.common.aop.LogAnnotation)"):这个注解定义了一个切点 logPointCut(),它表示当目标方法上存在 com.cherriesovo.blog.common.aop.LogAnnotation 注解时,这个切点会匹配到。

  • public void logPointCut() { }:这个方法定义了切点的具体内容,但方法体为空,因为它只是用于标识切点,实际的逻辑在通知方法中实现。

  • @Around("logPointCut()"):这个注解表示环绕通知,它表示在目标方法执行前后都会执行通知逻辑。

  • public Object around(ProceedingJoinPoint point) throws Throwable { }:这个方法是环绕通知的具体实现。在目标方法执行前记录开始时间,在执行后记录结束时间,并记录日志。

  • private void recordLog(ProceedingJoinPoint joinPoint, long time) { }:这个方法用于记录日志,它获取了目标方法的签名、注解信息、方法参数、请求信息等,并使用日志记录器将这些信息输出到日志中。

    • ProceedingJoinPoint 是 Spring AOP 中的一个接口,它提供了对连接点(Join Point)进行操作的功能。在面向切面编程中,连接点表示程序执行过程中的特定点,比如方法的调用或异常的处理等。

      ProceedingJoinPointJoinPoint 的子接口,在 Spring AOP 中,它专门用于表示可以执行的连接点,例如在环绕通知中,通过调用 proceed() 方法可以执行目标方法。

      通常,在环绕通知中,我们会将 ProceedingJoinPoint 对象作为参数传递给通知方法,在通知方法中可以通过调用 proceed() 方法来继续执行目标方法,也可以获取连接点的信息,如方法签名、参数等。

整个类的作用是,当目标方法被调用时,记录下方法的执行时间、方法的输入参数、请求的 IP 地址等信息,并将这些信息输出到日志中,以便进行日志记录和监控。

package com.cherriesovo.blog.common.aop; import com.alibaba.fastjson.JSON; import com.cherriesovo.blog.utils.HttpContextUtils; import com.cherriesovo.blog.utils.IpUtils; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; import java.lang.reflect.Method; import java.util.Date; /** * 日志切面 */ @Aspect //切面 定义了通知和切点的关系 @Component @Slf4j public class LogAspect { //切点 @Pointcut("@annotation(com.cherriesovo.blog.common.aop.LogAnnotation)") public void logPointCut() { } //通知类,环绕通知 @Around("logPointCut()") public Object around(ProceedingJoinPoint point) throws Throwable { long beginTime = System.currentTimeMillis(); //开始时间 //执行方法 Object result = point.proceed(); //执行时长(毫秒) long time = System.currentTimeMillis() - beginTime; //保存日志 recordLog(point, time); return result; } //记录日志 private void recordLog(ProceedingJoinPoint joinPoint, long time) { //获取了目标方法的签名信息 MethodSignature signature = (MethodSignature) joinPoint.getSignature(); //通过签名获取目标方法 Method method = signature.getMethod(); //获取了目标方法上的 LogAnnotation 注解,以便获取注解中的信息 LogAnnotation logAnnotation = method.getAnnotation(LogAnnotation.class); log.info("=====================log start================================"); log.info("module:{}",logAnnotation.module()); //输出日志中的模块信息 log.info("operation:{}",logAnnotation.operator()); //输出日志中的操作信息 String className = joinPoint.getTarget().getClass().getName(); //获取目标方法所属类的类名 String methodName = signature.getName(); //获取目标方法的方法名 //输出请求的方法名,格式为类名.方法名() log.info("request method:{}",className + "." + methodName + "()"); //请求的参数 Object[] args = joinPoint.getArgs(); //获取目标方法的参数列表 String params = JSON.toJSONString(args[0]); //参数列表转换为 JSON 格式的字符串,这里只获取了第一个参数 log.info("params:{}",params); //输出请求的参数信息 //获取request 设置IP地址 HttpServletRequest request = HttpContextUtils.getHttpServletRequest(); 获取当前的HttpServletRequest对象 log.info("ip:{}", IpUtils.getIpAddr(request)); //输出请求的 IP 地址 log.info("excute time : {} ms",time); //输出方法的执行时间 log.info("=====================log end================================"); } }
package com.cherriesovo.blog.utils; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; public class HttpContextUtils { /*用于获取当前线程的 HttpServletRequest 对象,通过 RequestContextHolder.getRequestAttributes() 获取到当前请求的属性对 象,然后将其转换为 ServletRequestAttributes,再调用 getRequest() 方法获取到 HttpServletRequest 对象。*/ public static HttpServletRequest getHttpServletRequest(){ return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); } }
package com.cherriesovo.blog.utils; import javax.servlet.http.HttpServletRequest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.StringUtils; //IP 地址获取工具类 IpUtils,用于从 HTTP 请求中获取客户端的真实 IP 地址 public class IpUtils { private static Logger logger = LoggerFactory.getLogger(IpUtils.class); /** * 获取IP地址 * 使用Nginx等反向代理软件, 则不能通过request.getRemoteAddr()获取IP地址 * 如果使用了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP地址,X-Forwarded-For中第一个非unknown的有效IP字符串,则为真实IP地址 */ public static String getIpAddr(HttpServletRequest request) { String ip = null; try { ip = request.getHeader("x-forwarded-for"); if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("Proxy-Client-IP"); } if (StringUtils.isEmpty(ip) || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("WL-Proxy-Client-IP"); } if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("HTTP_CLIENT_IP"); } if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("HTTP_X_FORWARDED_FOR"); } if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) { ip = request.getRemoteAddr(); } } catch (Exception e) { logger.error("IPUtils ERROR ", e); } // 使用代理,则获取第一个IP地址 if (StringUtils.isEmpty(ip) && ip.length() > 15) { if (ip.indexOf(",") > 0) { ip = ip.substring(0, ip.indexOf(",")); } } return ip; } }

__EOF__

本文作者CherriesOvO
本文链接https://www.cnblogs.com/zyj3955/p/18129831.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   CherriesOvO  阅读(21)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
历史上的今天:
2022-04-11 操作系统——第四章课后习题答案02
2021-04-11 《人月神话》读后感(第三四章)
2021-04-11 学习日报
点击右上角即可分享
微信分享提示