8.文章图片上传、导航-文章分类、分类文章列表、标签文章列表

文章图片上传接口说明

接口url:/upload

请求方式:POST

请求参数:

参数名称参数类型说明
imagefile上传的文件名称

返回数据:  

{
    "success":true,
 	"code":200,
    "msg":"success",
    "data":"https://static.mszlu.com/aa.png"
}

导入七牛云的sdk(用于文件上传)

<dependency>
  <groupId>com.qiniu</groupId>
  <artifactId>qiniu-java-sdk</artifactId>
  <version>[7.7.0, 7.7.99]</version>
</dependency>

限制上传大小

spring:  
  servlet:
    multipart:
      # 上传文件总的最大值
      max-request-size: 20MB
      # 单个文件的最大值
      max-file-size: 2MB

七牛云工具类:

需要修改的有 存储区域、密钥、url(修改为自己域名的,不然前端找不到)、空间名

package com.example.blog.utils;

import com.alibaba.fastjson.JSON;
import com.qiniu.http.Response;
import com.qiniu.storage.Configuration;
import com.qiniu.storage.Region;
import com.qiniu.storage.UploadManager;
import com.qiniu.storage.model.DefaultPutRet;
import com.qiniu.util.Auth;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

@Component
public class QiNiuYunUtils
{

    //http://+域名
    public static final String url = "http://qiniuyun.suqiqaq.cn/";
    //七牛云密钥对应的ky-value
    @Value("${qiniu.accessKey}")
    private String accessKey;
    @Value("${qiniu.accessSecretKey}")
    private String accessSecretKey;

    public boolean upload(MultipartFile file, String fileName)
    {

        //构造一个带指定 Region 对象的配置类,需要看七牛云的存储区域
        Configuration cfg = new Configuration(Region.huanan());
        //...其他参数参考类注释
        UploadManager uploadManager = new UploadManager(cfg);
        //...生成上传凭证,然后准备上传
        String bucket = "suqiblog";//空间名
        //默认不指定key的情况下,以文件内容的hash值作为文件名
        try {
            byte[] uploadBytes = file.getBytes();
            Auth auth = Auth.create(accessKey, accessSecretKey);
            String upToken = auth.uploadToken(bucket);
            Response response = uploadManager.put(uploadBytes, fileName, upToken);
            //解析上传成功的结果
            DefaultPutRet putRet = JSON.parseObject(response.bodyString(), DefaultPutRet.class);
            return true;
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return false;
    }
}

yaml密钥配置:

qiniu:
  accessKey: Re1oK3cavH_3mYf83adaXASSzMpA5gfnFhDx3YIk
  accessSecretKey: uN_vAIZ1bIUh8eOCO_4mxLYqv_PWJOmR5cCahoKt

如果有自己的域名,七牛云可以添加域名,添加成功后,需要配置CNAME 

  

复制CNAME

在阿里云服务器控制台中:在控制台页面的左侧,产品与服务栏中选择 域名

在域名产品的列表中找到您加速域名对应的主域名,点击域名后面的 解析设置 或 解析,进入解析设置页。

 

 选择 添加记录 ,依次填写 主机记录记录类型 以及 记录值,其他可设为默认值。

主机记录,由于我的是qiniuyun.suqiqaq.cn,主机记录为qiniuyun

记录值就是复制的CNAME

 配置完成后,显示已配置

 配置成功后,SpringBoot2的qiniuyunUtils需要修改域名

QiniuUtils.java:

package com.example.blog.utils;

import com.alibaba.fastjson.JSON;
import com.qiniu.http.Response;
import com.qiniu.storage.Configuration;
import com.qiniu.storage.Region;
import com.qiniu.storage.UploadManager;
import com.qiniu.storage.model.DefaultPutRet;
import com.qiniu.util.Auth;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

@Component
public class QiniuUtils
{

    //http://+域名
    public static final String url = "http://qiniuyun.suqiqaq.cn/";
    //七牛云密钥对应的ky-value
    @Value("${qiniu.accessKey}")
    private  String accessKey;
    @Value("${qiniu.accessSecretKey}")
    private  String accessSecretKey;

    public  boolean upload(MultipartFile file,String fileName){

        //构造一个带指定 Region 对象的配置类,需要看七牛云的存储区域
        Configuration cfg = new Configuration(Region.huanan());
        //...其他参数参考类注释
        UploadManager uploadManager = new UploadManager(cfg);
        //...生成上传凭证,然后准备上传
        String bucket = "suqiblog";//空间名
        //默认不指定key的情况下,以文件内容的hash值作为文件名
        try {
            byte[] uploadBytes = file.getBytes();
            Auth auth = Auth.create(accessKey, accessSecretKey);
            String upToken = auth.uploadToken(bucket);
            Response response = uploadManager.put(uploadBytes, fileName, upToken);
            //解析上传成功的结果
            DefaultPutRet putRet = JSON.parseObject(response.bodyString(), DefaultPutRet.class);
            return true;
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return false;
    }
}
UploadController:
package com.example.blog.controller;

import com.example.blog.utils.QiniuUtils;
import com.example.blog.vo.Result;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.util.UUID;

@RestController
@RequestMapping("upload")
public class UploadController
{
    @Autowired
    private QiniuUtils qiniuUtils;

    @PostMapping
    public Result upload(@RequestParam("image")MultipartFile file)
    {
        //获得原始文件名称
        String originalFilename = file.getOriginalFilename();
        //创建唯一的文件名称
        //StringUtils.substringAfterLast(originalFilename, ".")获取在.之后的字符串
        String fileName = UUID.randomUUID().toString() + "." + StringUtils.substringAfterLast(originalFilename, ".");
        //上传文件到哪里=》云服务器,与应用服务器隔开,不占用他的带宽
        boolean isUpload = qiniuUtils.upload(file, fileName);
        if(isUpload)
        {
            return Result.success(QiniuUtils.url + fileName);
        }else
            return Result.fail(20001,"上传失败");
    }
}

2. 导航-文章分类

2.1 查询所有的文章分类

2.1.1 接口说明

接口url:/categorys/detail

请求方式:GET

请求参数:无

返回数据:

{
    "success": true, 
    "code": 200, 
    "msg": "success", 
    "data": [
        {
            "id": 1, 
            "avatar": "/static/category/front.png", 
            "categoryName": "前端", 
            "description": "前端是什么,大前端"
        }, 
        {
            "id": 2, 
            "avatar": "/static/category/back.png", 
            "categoryName": "后端", 
            "description": "后端最牛叉"
        }, 
        {
            "id": 3, 
            "avatar": "/static/category/lift.jpg", 
            "categoryName": "生活", 
            "description": "生活趣事"
        }, 
        {
            "id": 4, 
            "avatar": "/static/category/database.png", 
            "categoryName": "数据库", 
            "description": "没数据库,啥也不管用"
        }, 
        {
            "id": 5, 
            "avatar": "/static/category/language.png", 
            "categoryName": "编程语言", 
            "description": "好多语言,该学哪个?"
        }
    ]
}

这里前端所需要的数据直接对应的就是category类,把category表查到数据之后,还需要查该分类下对应的文章数量,然后再封装成CategoryDetailVo对象返回

CategoryController:
package com.example.blog.controller;

import com.example.blog.entity.Category;
import com.example.blog.service.CategoryService;
import com.example.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 categories()
    {
        return categoryService.findAll();
    }

    @GetMapping("/detail")
    public Result categoriesDetail()
    {
        return categoryService.findAllDetail();
    }
}
CategoryServiceImpl:
package com.example.blog.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.example.blog.dao.mapper.CategoryMapper;
import com.example.blog.entity.Category;
import com.example.blog.service.CategoryService;
import com.example.blog.vo.CategoryVo;
import com.example.blog.vo.Result;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

@Service
public class CategoryServiceImpl implements CategoryService
{

    @Autowired
    private CategoryMapper categoryMapper;

    @Autowired
    private ArticleMapper articleMapper;

    @Override
    public CategoryVo findCategoryById(Long categoryId)
    {
        Category category = categoryMapper.selectById(categoryId);
        return copy(category);
    }

    @Override
    public Result findAllDetail()
    {
        List<Category> categories = categoryMapper.selectList(null);
        List<CategoryDetailVo> categoryDetailVos = new ArrayList<>();
        for (Category category : categories) {
            LambdaQueryWrapper<Article> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(Article::getCategoryId, category.getId());
            Integer articlesNumber = articleMapper.selectCount(queryWrapper);
            categoryDetailVos.add(copyCategoryDetailList(category, articlesNumber));
        }
        return Result.success(categoryDetailVos);
    }

    private CategoryDetailVo copyCategoryDetailList(Category category, Integer articlesNumber)
    {
        CategoryDetailVo categoryDetailVo = new CategoryDetailVo();
        BeanUtils.copyProperties(category, categoryDetailVo);
        categoryDetailVo.setArticlesNumber(articlesNumber);
        return categoryDetailVo;
    }

}
CategoryDetailVo:
package com.example.blog.vo;

import lombok.Data;

@Data
public class CategoryDetailVo {
    private String id;

    private String avatar;

    private String categoryName;

    private String description;

    private Integer articlesNumber;
}

2.2 查询所有的标签

2.2.1 接口说明

接口url:/tags/detail

请求方式:GET

请求参数:无

返回数据:

{
    "success": true, 
    "code": 200, 
    "msg": "success", 
    "data": [
        {
            "id": 5, 
            "tagName": "springboot", 
            "avatar": "/static/tag/java.png"
        }, 
        {
            "id": 6, 
            "tagName": "spring", 
            "avatar": "/static/tag/java.png"
        }, 
        {
            "id": 7, 
            "tagName": "springmvc", 
            "avatar": "/static/tag/java.png"
        }, 
        {
            "id": 8, 
            "tagName": "11", 
            "avatar": "/static/tag/css.png"
        }
    ]
}

同理,这里前端所需要的数据直接对应的就是tag类,把tag表数据查到之后,还需要查该标签下对应的文章数量,然后再封装成TagDetailVo对象返回

TagsController:
package com.example.blog.controller;

import com.example.blog.service.TagService;
import com.example.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("/tags")
public class TagsController
{
    @Autowired
    private TagService tagService;

    @GetMapping
    public Result findAll()
    {
        return tagService.findAll();
    }

    @GetMapping("detail")
    public Result findAllDetail()
    {
        return tagService.findAllDetail();
    }

}
TagServiceImpl:
package com.example.blog.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.api.R;
import com.example.blog.dao.mapper.TagMapper;
import com.example.blog.entity.Category;
import com.example.blog.entity.Tag;
import com.example.blog.service.TagService;
import com.example.blog.vo.Result;
import com.example.blog.vo.TagVo;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

@Service
public class TagServiceImpl implements TagService
{

    @Autowired
    private TagMapper tagMapper;

    @Autowired
    private ArticleTagMapper articleTagMapper;

     @Override
    public Result findAllDetail()
    {
        List<Tag> tags = tagMapper.selectList(null);
        List<TagDetailVo> tagDetailVos = new ArrayList<>();
        for (Tag tag : tags)
        {
            LambdaQueryWrapper<ArticleTag> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(ArticleTag::getTagId,tag.getId());
            Integer articlesNumber = articleTagMapper.selectCount(queryWrapper);
            tagDetailVos.add(copyTagDetailList(tag, articlesNumber));
        }
        return Result.success(tagDetailVos);
    }

    private TagDetailVo copyTagDetailList(Tag tag, Integer articlesNumber)
    {
        TagDetailVo tagDetailVo = new TagDetailVo();
        BeanUtils.copyProperties(tag,tagDetailVo);
        tagDetailVo.setArticlesNumber(articlesNumber);
        return tagDetailVo;
    }
}

3. 分类文章列表

3.1 接口说明

接口url:/categorys/detail/{id}

请求方式:GET

请求参数:

参数名称参数类型说明
id分类id路径参数

返回数据:

{
    "success": true, 
    "code": 200, 
    "msg": "success", 
    "data": 
        {
            "id": 1, 
            "avatar": "/static/category/front.png", 
            "categoryName": "前端", 
            "description": "前端是什么,大前端"
        }
}

​点击某一个文章的分类之后,需要将该文章分类的详情返回,以及哪些文章是这个分类,把这些文章的作为一个列表返回

文章分类的详情:

CategoryController:

package com.example.blog.controller;

import com.example.blog.entity.Category;
import com.example.blog.service.CategoryService;
import com.example.blog.vo.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
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("detail/{id}")//文章分类id(categoryId)
    public Result categoriesDetailById(@PathVariable("id") Long id)
    {
        return categoryService.categoriesDetailById(id);
    }
}
CategoryServiceImpl:
package com.example.blog.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.example.blog.dao.mapper.CategoryMapper;
import com.example.blog.entity.Category;
import com.example.blog.service.CategoryService;
import com.example.blog.vo.CategoryVo;
import com.example.blog.vo.Result;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

@Service
public class CategoryServiceImpl implements CategoryService
{

    @Autowired
    private CategoryMapper categoryMapper;

    @Override
    public Result categoriesDetailById(Long id)
    {
        Category category = categoryMapper.selectById(id);
        return Result.success(category);
    }

}

哪些文章是这个分类,把这些文章的作为一个列表返回:

逻辑:需要调整之前写的文章列表接口,queryWrapper需要加上数据库的categoryId为前端传入的categoryId,才添加进去

需要修改前端参数
PageParams:
package com.example.blog.vo.params;


import lombok.Data;

@Data
public class PageParams
{
    //当前的页数
    private int page = 1;

    //每页查询的数量
    private int pageSize = 10;

    private Long categoryId;

    private Long tagId;
}
ArticleServiceImpl:
/**
     * 分页查询 article数据库表
     * @param pageParams
     * @return
     */
    @Override
    //加上此注解 代表要对此接口记录日志
    @LogAnnotation(module="文章",operator="获取文章列表")
    public Result listArticle(PageParams pageParams)
    {
        Page<Article> page = new Page<>(pageParams.getPage(), pageParams.getPageSize());
        LambdaQueryWrapper<Article> queryWrapper = new LambdaQueryWrapper<>();/*查询器*/
        queryWrapper.orderByDesc(Article::getWeight);/*是否置顶进行排序*/
        queryWrapper.orderByDesc(Article::getCreateDate);/*根据创建时间进行降序排序 order by create_date desc*/

        //如果ageParams.getCategoryId()不为空,说明是查找的某个文章分类下的文章列表,
        //如果为空,说明查的是总文章列表
        if(null != pageParams.getCategoryId())
        {
            queryWrapper.eq(Article::getCategoryId,pageParams.getCategoryId());
        }
        Page<Article> articlePage = articleMapper.selectPage(page, queryWrapper);/*等同于编写一个普通list查询,mybatis-plus自动替你分页*/
        List<Article> records = articlePage.getRecords();
        List<ArticleVo> articleVoList = copyList(records,true,true);
        return Result.success(articleVoList);
    }

4. 标签文章列表

4.1 接口说明

接口url:/tags/detail/{id}

请求方式:GET

请求参数:

参数名称参数类型说明
id标签id路径参数

返回数据:

{
    "success": true, 
    "code": 200, 
    "msg": "success", 
    "data": 
        {
            "id": 5, 
            "tagName": "springboot", 
            "avatar": "/static/tag/java.png"
        }
}

同理,点击某一个文章的标签之后,需要将该文章标签的详情返回,以及哪些文章是这个标签,把这些文章的内容作为一个列表返回

该文章标签的详情返回:

TagsController:
    @GetMapping("/detail/{id}")
    public Result findTagDetailById(@PathVariable("id")Long id)
    {
        return tagService.findTagById(id);
    }
TagServiceImpl:
    /**
     * 通过标签id查询标签详情
     * @param id
     * @return
     */
    @Override
    public Result findTagById(Long id)
    {
        Tag tag = tagMapper.selectById(id);
        return Result.success(tag);
    }

 对应的,查询文章列表的接口也需要修改

这里的article并没有tagId这个字段,而是通过ms_article_tag来查找的tag
所以这里的逻辑为:

1.通过传入的tagId,如果ms_article_tag表中tagId等于前端传入的tagId,就添加到articleTags中

2.遍历articleTags每个articleId,并添加到articleIdList中

3.通过articleIdList添加查询条件,如果article表中的articleId在articleIdList中,才添加

 /**
     * 分页查询 article数据库表
     * @param pageParams
     * @return
     */
    @Override
    //加上此注解 代表要对此接口记录日志
    @LogAnnotation(module="文章",operator="获取文章列表")
    public Result listArticle(PageParams pageParams)
    {
        Page<Article> page = new Page<>(pageParams.getPage(), pageParams.getPageSize());
        LambdaQueryWrapper<Article> queryWrapper = new LambdaQueryWrapper<>();/*查询器*/
        queryWrapper.orderByDesc(Article::getWeight);/*是否置顶进行排序*/
        queryWrapper.orderByDesc(Article::getCreateDate);/*根据创建时间进行降序排序 order by create_date desc*/

        if(null != pageParams.getCategoryId())//如果ageParams.getCategoryId()不为空,说明是查找的某个文章分类下的文章列表,如果为空,说明查的是总文章列表
        {
            queryWrapper.eq(Article::getCategoryId,pageParams.getCategoryId());
        }
        //这里的article并没有tagId这个字段,而是通过ms_article_tag来查找的
        //所以需要通过ms_article_tag表查到对应的articleId列表,再通过articleId列表查找article列表
        if(null != pageParams.getTagId())
        {
            List<Long> articleIdList = new ArrayList<>();
            LambdaQueryWrapper<ArticleTag> ATQueryWrapper = new LambdaQueryWrapper<>();
            ATQueryWrapper.eq(ArticleTag::getTagId,pageParams.getTagId());
            List<ArticleTag> articleTags = articleTagMapper.selectList(ATQueryWrapper);
            for(ArticleTag articleTag: articleTags)
            {
                articleIdList.add(articleTag.getArticleId());
            }
            if(articleIdList.size() > 0)
            {
                queryWrapper.in(Article::getId,articleIdList);
            }
        }
        Page<Article> articlePage = articleMapper.selectPage(page, queryWrapper);/*等同于编写一个普通list查询,mybatis-plus自动替你分页*/
        List<Article> records = articlePage.getRecords();
        List<ArticleVo> articleVoList = copyList(records,true,true);
        return Result.success(articleVoList);
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值