目录
1. 文章图片上传
1.1 接口说明
接口url:/upload
请求方式:POST
请求参数:
参数名称 | 参数类型 | 说明 |
image | file | 上传的文件名称 |
返回数据:
{
"success": true,
"code": 200,
"msg": "success",
"data": "http://rzeu8sn8o.hn-bkt.clouddn.com//d4d2e0f3-aeda-4104-8ba3-5a116bce506f.png"
}
文件上传到七牛云 导入依赖
<dependency>
<groupId>com.qiniu</groupId>
<artifactId>qiniu-java-sdk</artifactId>
<version>[7.7.0, 7.7.99]</version>
</dependency>
1.2 Controller
package com.pjp.blog.controller;
import com.pjp.blog.utils.QiniuUtils;
import com.pjp.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) {
//原始文件名称 比如 aa.png
String originalFilename = file.getOriginalFilename();
//唯一的文件名称
String fileName = UUID.randomUUID().toString() + "." + StringUtils.substringAfterLast(originalFilename, ".");
//文件上传到七牛云 云服务器 按量付费 速度快 把图片发放到离用户最近的服务器上
//降低我们自身应用服务器的带宽消耗
boolean upload = qiniuUtils.upload(file, fileName);
if (upload) {
return Result.success(qiniuUtils.url + "/"+fileName);
}
return Result.fail(20001, "上传失败");
}
}
配置上传文件参数
#上传文件总的最大值
spring.servlet.multipart.max-request-size=20MB
#单个文件的最大值
spring.servlet.multipart.max-file-size=5MB
1.3 使用七牛云
用户访问图片资源时,图片一般占用的带宽比较大,如果很多人同时访问图片资源,就可能会造成应用服务器网络拥堵,解决办法是使用专门的图片云服务器(我们这里采用免费的七牛云) ,用户去图片服务器访问图片资源,降低应用服务器的带宽压力。
工具类
package com.pjp.blog.utils;
import com.alibaba.fastjson.JSON;
import com.qiniu.common.Zone;
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 {
public static final String url = "http://rzeu8sn8o.hn-bkt.clouddn.com/";
@Value("${qiniu.accessKey}")
private String accessKey;
@Value("${qiniu.accessSecretKey}")
private String accessSecretKey;
public boolean upload(MultipartFile file, String fileName) {
//构造一个带指定 Region 对象的配置类
Configuration cfg = new Configuration(Zone.zone2());
//...其他参数参考类注释
UploadManager uploadManager = new UploadManager(cfg);
//...生成上传凭证,然后准备上传
String bucket = "pjpblog";
//默认不指定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;
}
}
配置自己七牛云的密钥
qiniu.accessKey=
qiniu.accessSecretKey=
1.4 测试
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": "好多语言,该学哪个?"
}
]
}
@Data
public class CategoryVo {
private Long id;
private String avatar;
private String categoryName;
private String description;
}
2.1.2 Controller
package com.pjp.blog.controller;
import com.pjp.blog.service.CategoryService;
import com.pjp.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 {
@GetMapping("detail")
public Result categoriesDetail(){
return categoryService.findAllDetail();
}
}
2.1.3 Service
/**
* 查询所有分类及详细信息
* @return
*/
Result findAllDetail();
@Override
public Result findAllDetail() {
List<Category> categories = categoryMapper.selectList(new LambdaQueryWrapper<>());
return Result.success(copyList(categories));
}
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": "css",
"avatar": "/static/tag/css.png"
}
]
}
@Data
public class TagVo {
private Long id;
private String tagName;
private String avatar;
}
2.2.3 Controller
@GetMapping("detail")
public Result findAllDetail(){
return tagService.findAllDetail();
}
2.2.4 Service
/**
* 所有标签及详细信息
*
* @return
*/
Result findAllDetail();
@Override
public Result findAllDetail() {
LambdaQueryWrapper<Tag> queryWrapper = new LambdaQueryWrapper<>();
List<Tag> tags = this.tagMapper.selectList(queryWrapper);
return Result.success(copyList(tags));
}
测试
3. 分类文章列表
3.1 接口说明
接口url:/category/detail/{id}
请求方式:GET
请求参数:
参数名称 | 参数类型 | 说明 |
id | 分类id | 路径参数 |
返回数据:
{
"success": true,
"code": 200,
"msg": "success",
"data":
{
"id": 1,
"avatar": "/static/category/front.png",
"categoryName": "前端",
"description": "前端是什么,大前端"
}
}
@Data
public class PageParams {
private int page = 1;
private int pageSize = 10;
private Long categoryId;
private Long tagId;
}
3.2 Controller
@GetMapping("detail/{id}")
public Result categoriesDetailById(@PathVariable("id") Long id){
return categoryService.categoriesDetailById(id);
}
3.3 Service
CategoryService
/**
* 根据id查询具体分类信息
*
* @param id
* @return
*/
Result categoriesDetailById(long id);
@Override
public Result categoriesDetailById(long id) {
Category category = categoryMapper.selectById(id);
return Result.success(copy(category));
}
ArticleServiceImpl
@Override
public Result listArticle(PageParams pageParams) {
Page<Article> page = new Page<>(pageParams.getPage(), pageParams.getPageSize());
// LambdaQueryWrapper可以使用lambda表达式构建SQL查询语句
LambdaQueryWrapper<Article> queryWrapper = new LambdaQueryWrapper<>();
//查询文章的参数 加上分类id,判断不为空 加上分类条件
if (pageParams.getCategoryId() != null) {
queryWrapper.eq(Article::getCategoryId, pageParams.getCategoryId());
}
// 按时间进行排序
// queryWrapper.orderByDesc(“属性”)——根据属性降序排序
// queryWrapper.orderByDesc(Article::getCreateDate);
// 是否置顶排序
// queryWrapper.orderByDesc(Article::getWeight);
queryWrapper.orderByDesc(Article::getWeight, Article::getCreateDate);
Page<Article> articlePage = articleMapper.selectPage(page, queryWrapper);
// getRecords();查询到的数据
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"
}
}
4.2 Controller
@RestController
@RequestMapping("tags")
public class TagsController {
@Autowired
private TagService tagService;
@GetMapping("detail/{id}")
public Result findDetailById(@PathVariable("id") Long id) {
return tagService.findDetailById(id);
}
}
4.3 Service
/**
* 根据id查询标签详细信息
*
* @param id
* @return
*/
Result findDetailById(Long id);
@Override
public Result findDetailById(Long id) {
Tag tag = this.tagMapper.selectById(id);
return Result.success(tag);
}
4.4 修改原有的查询文章接口
@Override
public Result listArticle(PageParams pageParams) {
Page<Article> page = new Page<>(pageParams.getPage(), pageParams.getPageSize());
// LambdaQueryWrapper可以使用lambda表达式构建SQL查询语句
LambdaQueryWrapper<Article> queryWrapper = new LambdaQueryWrapper<>();
//查询文章的参数 加上分类id,判断不为空 加上分类条件
if (pageParams.getCategoryId() != null) {
queryWrapper.eq(Article::getCategoryId, pageParams.getCategoryId());
}
List<Long> articleIdList = new ArrayList<>();
if (pageParams.getTagId() != null) {
//加入标签 条件查询
//article表中 并没有tag字段 去articleTag表中查 一篇文章有多个标签
LambdaQueryWrapper<ArticleTag> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(ArticleTag::getTagId, pageParams.getTagId());
List<ArticleTag> articleTags = articleTagMapper.selectList(lambdaQueryWrapper);
for (ArticleTag articleTag : articleTags) {
articleIdList.add(articleTag.getArticleId());
}
if (articleIdList.size() > 0) {
// and id in(1,2,3)
queryWrapper.in(Article::getId,articleIdList);
}
}
// 按时间进行排序
// queryWrapper.orderByDesc(“属性”)——根据属性降序排序
// queryWrapper.orderByDesc(Article::getCreateDate);
// 是否置顶排序
// queryWrapper.orderByDesc(Article::getWeight);
queryWrapper.orderByDesc(Article::getWeight, Article::getCreateDate);
Page<Article> articlePage = articleMapper.selectPage(page, queryWrapper);
// getRecords();查询到的数据
List<Article> records = articlePage.getRecords();
// 数据不能直接返回,和前端进行数据交互
List<ArticleVo> articleVoList = copyList(records, true, true);
return Result.success(articleVoList);
}
4.5 测试
5. 归档文章列表
5.1 接口说明
接口url:/articles
请求方式:POST
请求参数:
参数名称 | 参数类型 | 说明 |
year | string | 年 |
month | string | 月 |
返回数据:
{
"success": true,
"code": 200,
"msg": "success",
"data": [文章列表,数据同之前的文章列表接口]
}
5.2 文章列表参数
在之前的PageParams加上属性 year ,month 并重写getMonth方法
@Data
public class PageParams {
private int page = 1;
private int pageSize = 10;
private Long categoryId;
private Long tagId;
private String year;
private String month;
public String getMonth(){
if (this.month != null && this.month.length() == 1){
return "0"+this.month;
}
return this.month;
}
}
5.3 使用自定义sql 实现文章列表
@Override
public Result listArticle(PageParams pageParams) {
Page<Article> page = new Page<>(pageParams.getPage(), pageParams.getPageSize());
IPage<Article> articleIPage = this.articleMapper.listArticle(page,
pageParams.getCategoryId(),
pageParams.getTagId(),
pageParams.getYear(),
pageParams.getMonth());
//getRecords();是用来获取当前页的数据,即查询的记录
return Result.success(copyList(articleIPage.getRecords(), true, true, true, true));
}
ArticleMapper
public interface ArticleMapper extends BaseMapper<Article> {
/**
* 文章归档
* @return
*/
List<Archives> listArchives();
IPage<Article> listArticle(Page<Article> page,
Long categoryId,
Long tagId,
String year,
String month);
}
ArticleMapper.xml
<resultMap id="articleMap" type="com.pjp.blog.dao.pojo.Article">
<id column="id" property="id"/>
<result column="author_id" property="authorId"/>
<result column="comment_counts" property="commentCounts"/>
<result column="create_date" property="createDate"/>
<result column="summary" property="summary"/>
<result column="title" property="title"/>
<result column="view_counts" property="viewCounts"/>
<result column="weight" property="weight"/>
<result column="body_id" property="bodyId"/>
<result column="category_id" property="categoryId"/>
</resultMap>
<!-- IPage<Article> listArticle(Page<Article> page,
Long categoryId,
Long tagId,
String year,
String month);-->
<select id="listArticle" resultMap="articleMap">
select * from ms_article
<where>
1 = 1
<!--test元素的作用是判断所指定的表达式是否成立,如果成立,则执行下面的SQL语句。-->
<if test="categoryId != null">
and category_id=#{categoryId}
</if>
<if test="tagId != null">
and id in (select article_id from ms_article_tag where tag_id=#{tagId})
</if>
<if test="year != null and year.length>0 and month != null and month.length>0">
and (FROM_UNIXTIME(create_date/1000,'%Y') =#{year} and FROM_UNIXTIME(create_date/1000,'%m')=#{month})
</if>
</where>
order by weight,create_date desc
</select>