app端查看文章列表
一、需求分析
二、表结构分析
2.1 ap_article 文章基本信息表
2.2 ap_article_config 文章配置表

2.3 ap_article_content 文章内容表
2.4表关系
三、功能实现思路分析
1,在默认频道展示10条文章信息
2,可以切换频道查看不同种类文章
3,当用户下拉可以加载最新的文章(分页),本页文章列表中发布时间最大的时间为依据
4,当用户上拉可以加载更多的文章信息(按照发布时间),本页文章列表中发布时间最小的时间为依据
5,如果是当前频道的首页,前端传递默认参数:
-
maxBehotTime:0(毫秒)
-
minBehotTime:20000000000000(毫秒)—>2063年
sql分析
#1.按照发布时间倒序查询十条文章
select * from ap_article order by publish_time desc limit 10;
#2.下面这个aa是给ap-article表起别名
select * from ap_article aa order by aa.publish_time desc limit 10;
#3.按照发布频道倒序查询十条文章
select * from ap_article aa where aa.channel_id = 1 order by aa.publish_time desc limit 10;
#4.查询首页
select * from ap_article aa where aa.channel_id = 1 and aa.publish_time < '2063-04-19 01:00:59'
order by aa.publish_time desc limit 10;
#5.查询更多,例如2020-09-08 10:20:12这个时间是当前页发布时间最早的,那就要查比它还要早的(上滑)
select * from ap_article aa where aa.channel_id = 1 and aa.publish_time < '2020-09-08 10:20:12'
order by aa.publish_time desc LIMIT 10;
#6.查询最新,例如2020-09-07 22:30:33这个时间是当前页发布时间最晚的,那就要查比它还要晚的(下拉)
select * from ap_article aa where aa.channel_id = 1 and aa.publish_time > '2020-09-07 22:30:33'
order by aa.publish_time desc limit 10;
select * from ap_article;
#多表查询
select * from ap_article aa left join ap_article_config bb on aa.id = bb.article_id
where bb.is_down != 1
and bb.is_delete != 1
and aa.channel_id = 1
and aa.publish_time < '2063-04-19 01:00:59'
order by aa.publish_time desc limit 10;
四、搭建文章微服务,代码实现功能
在heima-leadnews-service新建子模块heima-leadnews-article
4.1启动类
package com.heima.article;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.context.annotation.Bean;
@SpringBootApplication()//exclude= DataSourceAutoConfiguration.class
@EnableDiscoveryClient
@MapperScan("com.heima.article.mapper")
public class ArticleApplication {
public static void main(String[] args) {
SpringApplication.run(ArticleApplication.class, args);
}
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
4.2 bootstrap.yml配置文件
server:
port: 51802
spring:
application:
name: leadnews-article
cloud:
nacos:
discovery:
server-addr: 192.168.1.2:8848
config:
server-addr: 192.168.1.2:8848
file-extension: yml
4.3 nacos配置管理中心
dataId要与本地配置的服务名一致
spring:
redis:
host: 192.168.1.2
# password: leadnews
port: 6379
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://192.168.1.2:3306/leadnews_article?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false
username: root
password: 1234
# 设置Mapper接口所对应的XML文件位置,如果你在Mapper接口中有自定义方法,需要进行该配置
mybatis-plus:
mapper-locations: classpath*:mapper/*.xml
# 设置别名包扫描路径,通过该属性可以给包中的类注册别名
type-aliases-package: com.heima.model.article.pojos
4.4接口定义

4.5 实体类
(1) 文章信息表ap_article,存储已发布的文章
package com.heima.model.article.pojos;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
* <p>
* 文章信息表,存储已发布的文章
* </p>
*/
@Data
@TableName("ap_article")
public class ApArticle implements Serializable {
@TableId(value = "id",type = IdType.ID_WORKER)
private Long id;
/**
* 文章标题
*/
private String title;
/**
* 作者id
*/
@TableField("author_id")
private Long authorId;
/**
* 作者名称
*/
@TableField("author_name")
private String authorName;
/**
* 频道id
*/
@TableField("channel_id")
private Integer channelId;
/**
* 频道名称
*/
@TableField("channel_name")
private String channelName;
/**
* 文章布局 0 无图文章 1 单图文章 2 多图文章
*/
private Short layout;
/**
* 文章标记 0 普通文章 1 热点文章 2 置顶文章 3 精品文章 4 大V 文章
*/
private Byte flag;
/**
* 文章封面图片 多张逗号分隔
*/
private String images;
/**
* 标签
*/
private String labels;
/**
* 点赞数量
*/
private Integer likes;
/**
* 收藏数量
*/
private Integer collection;
/**
* 评论数量
*/
private Integer comment;
/**
* 阅读数量
*/
private Integer views;
/**
* 省市
*/
@TableField("province_id")
private Integer provinceId;
/**
* 市区
*/
@TableField("city_id")
private Integer cityId;
/**
* 区县
*/
@TableField("county_id")
private Integer countyId;
/**
* 创建时间
*/
@TableField("created_time")
private Date createdTime;
/**
* 发布时间
*/
@TableField("publish_time")
private Date publishTime;
/**
* 同步状态
*/
@TableField("sync_status")
private Boolean syncStatus;
/**
* 来源
*/
private Boolean origin;
/**
* 静态页面地址
*/
@TableField("static_url")
private String staticUrl;
}
(2) ap_article_config,APP已发布文章配置表
package com.heima.model.article.pojos;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
/**
* <p>
* APP已发布文章配置表
* </p>
*
* @author itheima
*/
@Data
@TableName("ap_article_config")
public class ApArticleConfig implements Serializable {
@TableId(value = "id",type = IdType.ID_WORKER)
private Long id;
/**
* 文章id
*/
@TableField("article_id")
private Long articleId;
/**
* 是否可评论
* true: 可以评论 1
* false: 不可评论 0
*/
@TableField("is_comment")
private Boolean isComment;
/**
* 是否可以转发
* true: 可以转发 1
* false: 不可转发 0
*/
@TableField("is_forward")
private Boolean isForward;
/**
* 是否下架
* true: 下架 1
* false: 没有下架 0
*/
@TableField("is_down")
private Boolean isDown;
/**
* 是否已删除
* true: 删除 1
* false: 没有删除 0
*/
@TableField("is_delete")
private Boolean isDelete;
}
(3) 文章内容
package com.heima.model.article.pojos;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
@Data
@TableName("ap_article_content")
public class ApArticleContent implements Serializable {
@TableId(value = "id",type = IdType.ID_WORKER)
private Long id;
/**
* 文章id
*/
@TableField("article_id")
private Long articleId;
/**
* 文章内容
*/
private String content;
}
4.6 Mapper
package com.heima.article.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.heima.model.article.dtos.ArticleHomeDto;
import com.heima.model.article.pojos.ApArticle;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@Mapper
public interface ApArticleMapper extends BaseMapper<ApArticle> {
/**
*
* @param dto
* @param type 1代表加载更多,2加载最新
* @return
*/
List<ApArticle> loadArticleList(@Param("dto") ArticleHomeDto dto, @Param("type") Short type);
}
4.7 对应的映射文件
在resources中新建com/heima/article/mapper/ApArticleMapper.xml 如下配置:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.heima.article.mapper.ApArticleMapper">
<resultMap id="resultMap" type="com.heima.model.article.pojos.ApArticle">
<id column="id" property="id"/>
<result column="title" property="title"/>
<result column="author_id" property="authorId"/>
<result column="author_name" property="authorName"/>
<result column="channel_id" property="channelId"/>
<result column="channel_name" property="channelName"/>
<result column="layout" property="layout"/>
<result column="flag" property="flag"/>
<result column="images" property="images"/>
<result column="labels" property="labels"/>
<result column="likes" property="likes"/>
<result column="collection" property="collection"/>
<result column="comment" property="comment"/>
<result column="views" property="views"/>
<result column="province_id" property="provinceId"/>
<result column="city_id" property="cityId"/>
<result column="county_id" property="countyId"/>
<result column="created_time" property="createdTime"/>
<result column="publish_time" property="publishTime"/>
<result column="sync_status" property="syncStatus"/>
<result column="static_url" property="staticUrl"/>
</resultMap>
<select id="loadArticleList" resultMap="resultMap">
SELECT
aa.*
FROM
`ap_article` aa
LEFT JOIN ap_article_config aac ON aa.id = aac.article_id
<where>
and aac.is_delete != 1
and aac.is_down != 1
<!-- loadmore -->
<if test="type != null and type == 1">
and aa.publish_time <![CDATA[<]]> #{dto.minBehotTime}
</if>
<if test="type != null and type == 2">
and aa.publish_time <![CDATA[>]]> #{dto.maxBehotTime}
</if>
<if test="dto.tag != '__all__'">
and aa.channel_id = #{dto.tag}
</if>
</where>
order by aa.publish_time desc
limit #{dto.size}
</select>
</mapper>
4.7 Service
package com.heima.article.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.heima.model.article.dtos.ArticleHomeDto;
import com.heima.model.article.pojos.ApArticle;
import java.util.List;
public interface ApArticleService extends IService<ApArticle> {
//加载文章列表
List<ApArticle> load(Short type, ArticleHomeDto dto);
}
4.8 ServiceImpl
package com.heima.article.service.serviceimpl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.article.mapper.ApArticleMapper;
import com.heima.article.service.ApArticleService;
import com.heima.common.constants.ArticleConstants;
import com.heima.model.article.dtos.ArticleHomeDto;
import com.heima.model.article.pojos.ApArticle;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import java.util.Date;
import java.util.List;
@Service
@Transactional
@Slf4j
public class ApArticleServiceImpl extends ServiceImpl<ApArticleMapper, ApArticle> implements ApArticleService {
@Autowired
private ApArticleMapper apArticleMapper;
private static final short MAX_PAGE_SIZE = 50;
/**
*
* @param type 1加载更多,加载最新
* @param dto
* @return
*/
public List<ApArticle> load(Short type, ArticleHomeDto dto) {
//参数校验
//1.校验分页条数
Integer size = dto.getSize();
if(size == null || size == 0){
dto.setSize(10);
}
Math.min(size, MAX_PAGE_SIZE);
//2.type
if(!type.equals(ArticleConstants.LOADTYPE_LOAD_MORE) || type.equals(ArticleConstants.LOADTYPE_LOAD_NEW)){
type= ArticleConstants.LOADTYPE_LOAD_MORE;
}
//频道
if(StringUtils.isEmpty(dto.getTag())){
dto.setTag(ArticleConstants.DEFAULT_TAG);
}
//时间
if(dto.getMaxBehotTime() == null){
dto.setMaxBehotTime(new Date());
}
if(dto.getMinBehotTime() == null){
dto.setMinBehotTime(new Date());
}
List<ApArticle> apArticles = apArticleMapper.loadArticleList(dto, type);
return apArticles;
}
}
4.9 Controller
package com.heima.article.controller.v1;
import com.heima.article.service.ApArticleService;
import com.heima.common.constants.ArticleConstants;
import com.heima.model.article.dtos.ArticleHomeDto;
import com.heima.model.article.pojos.ApArticle;
import com.heima.model.common.dtos.ResponseResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RequestMapping("/api/v1/article")
public class ApArticleController {
@Autowired
private ApArticleService apArticleService;
/**
* 加载首页
* 请求路径:"api/v1/article/load"
* 请求方式:post
* 请求参数: ArticleHomeDto
* 响应数据:List<Aparticle>
*
*/
@PostMapping("/load")
public ResponseResult load(@RequestBody ArticleHomeDto dto){
List<ApArticle> apArticles = apArticleService.load(ArticleConstants.LOADTYPE_LOAD_MORE,dto);
return ResponseResult.okResult(apArticles);
}
/**
*加载更多
*请求路径:"api/v1/article/loadmore"
*请求方式:post
*请求参数: ArticleHomeDto
*响应数据:List<Aparticle>
*/
@PostMapping("/loadmore")
public ResponseResult loadmore(@RequestBody ArticleHomeDto dto){
List<ApArticle> apArticles = apArticleService.load(ArticleConstants.LOADTYPE_LOAD_MORE,dto);
return ResponseResult.okResult(apArticles);
}
/**
*加载最新
*请求路径:"api/v1/article/loadnew"
*请求方式:post
*请求参数: ArticleHomeDto
*响应数据:List<Aparticle>
*/
@PostMapping("/loadnew")
public ResponseResult loadnew(@RequestBody ArticleHomeDto dto){
List<ApArticle> apArticles = apArticleService.load(ArticleConstants.LOADTYPE_LOAD_NEW,dto);
return ResponseResult.okResult(apArticles);
}
}
五、配置文章微服务gateway网关断言规则
routes:
# 文章微服务
- id: article
uri: lb://leadnews-article
predicates:
- Path=/article/**
filters:
- StripPrefix= 1