SpringBoot学习项目-博客系统-part8
写文章
- 文档写了,需要三个接口
1.1、所有文章分类
- 根据数据返回形式,需要标签表中的一切除了表述
CategoryController
@RestController
@RequestMapping("categorys")
public class CategoryController {
@Autowired
private CategoryService categoryService;
@GetMapping
public Result listCategory() {
return categoryService.findAll();
}
}
ServiceImpl
- 根据数据形式返回
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() {
List<Category> categories = this.categoryMapper.selectList(new LambdaQueryWrapper<>());
return Result.success(copyList(categories));
}
1.2、所有文章标签
- 因为是标签,所以还是在tagcontroller进行,需要tag表中的id、tagName
controller
@GetMapping
public Result findAll(){
return tagService.findAll();
}
serviceImpl
@Override
public Result findAll() {
List<Tag> tags = this.tagMapper.selectList(new LambdaQueryWrapper<>());
return Result.success(copyList(tags));
}
1.3、发布文章
- 关于文章的,还是在articlecontroller中写
- 根据返回的形式,涉及了多张表,自定义一个文章发布的时候的类,属性根据文档创建,注意tag部分要使用集合,可能一个文章有多个文章标签。
ArticleParam
@Data
public class ArticleParam {
private Long id;
private ArticleBodyParam body;
private CategoryVo category;
private String summary;
private List<TagVo> tags;
private String title;
}
ArticleBodyParam
@Data
public class ArticleBodyParam {
private String content;
private String contentHtml;
}
controller
@PostMapping("publish")
public Result publish(@RequestBody ArticleParam articleParam){
return articleService.publish(articleParam);
}
serviceImpl
@Override
@Transactional
public Result publish(ArticleParam articleParam) {
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);
article.setBodyId(-1L);
this.articleMapper.insert(article);
//tags
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);
}
}
ArticleBody articleBody = new ArticleBody();
articleBody.setContent(articleParam.getBody().getContent());
articleBody.setContentHtml(articleParam.getBody().getContentHtml());
articleBody.setArticleId(article.getId());
articleBodyMapper.insert(articleBody);
article.setBodyId(articleBody.getId());
articleMapper.updateById(article);
ArticleVo articleVo = new ArticleVo();
articleVo.setId(article.getId());
return Result.success(articleVo);
}
ArticleVo
@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;
}
ArticleTag
@Data
public class ArticleTag {
private Long id;
private Long articleId;
private Long tagId;
}
- 这里默认是先要登陆才能进行文章发布,所以将文章发布的接口添加到拦截器中
WebConfig
@Override
public void addInterceptors(InterceptorRegistry registry) {
//拦截test接口,根据需要拦截的接口时候,配置为真正的拦截接口
registry.addInterceptor(loginInterceptor)
.addPathPatterns("/test")
.addPathPatterns("/comments/create/change")
.addPathPatterns("/articles/publish");
}
AOP日志
- 为什么要日志?之后的生产的时候,可以通过日志看运行时间,接口出错通过日志可以看出参数,做复现。
- AOP日志不能切入原有的代码,AOP本身的特性就是面向切面编程,在不改变原有方法的基础上进行增强。
- 加上LogAnnotation注解代表对此接口记录日志
/**
* 首页 文章列表
* @param pageParams
* @return
*/
@PostMapping
@LogAnnotation(module="文章",operator="获取文章列表")
public Result listArticle(@RequestBody PageParams pageParams){
// int i = 10/0;
return articleService.listArticle(pageParams);
}
- 开发注解
//Type 代表可以放在类上面 Method 代表可以放在方法上
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LogAnnotation {
String module() default "";
String operator() default "";
}
- 当然开发了注解依然没用,要想生效就得开发AOP
@Component
@Aspect //切面 定义了通知和切点的关系
@Slf4j
public class LogAspect {
// 定义切点,写类全路径
@Pointcut("@annotation(com.lbj.blog.common.aop.LogAnnotation)")
public void pt(){}
//环绕通知
@Around("pt()")
public Object log(ProceedingJoinPoint joinPoint) throws Throwable {
long beginTime = System.currentTimeMillis();
//执行方法
Object result = joinPoint.proceed();
//执行时长(毫秒)
long time = System.currentTimeMillis() - beginTime;
//保存日志,slf4j
recordLog(joinPoint, time);
return result;
}
private void recordLog(ProceedingJoinPoint joinPoint, long time) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
LogAnnotation logAnnotation = method.getAnnotation(LogAnnotation.class);
log.info("=====================log start================================");
log.info("module:{}",logAnnotation.module());
log.info("operator:{}",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]);
log.info("params:{}",params);
//获取request 设置IP地址
HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
log.info("ip:{}", IpUtils.getIpAddr(request));
log.info("excute time : {} ms",time);
log.info("=====================log end================================");
}
}
- 记录一下自己当初学Spring的感受,只知道spring两大特性:IOC和AOP,但是AOP不知道如何去使用,所以这个AOP日志是一个很好的学习理解,
spring