• 黑马头条:app端文章查看


    黑马头条:app端文章查看

    文章列表加载

    1. 需求分析

    文章布局展示

    image-20210419151801252.png

    2. 表结构分析

    ap_article 文章基本信息表

    image-20210419151801252.png

    ap_article_config 文章配置表

    image-20210419151854868.png

    ap_article_content 文章内容表

    image-20210419151912063.png

    三张表关系分析

    image-20210419151938103

    3. 导入文章数据库

    3.1 导入数据库

    查看当天资料文件夹,在数据库连接工具中执行leadnews_article.sql

    3.2 导入对应的实体类

    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;
    
    /**
     * 

    * 文章信息表,存储已发布的文章 *

    * * @author itheima */
    @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; }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143

    ap_article_config文章配置对应实体类

    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;
    
    /**
     * 

    * APP已发布文章配置表 *

    * * @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; }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63

    ap_article_content 文章内容对应的实体类

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    4. 实现思路

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    1,在默认频道展示10条文章信息

    2,可以切换频道查看不同种类文章

    3,当用户下拉可以加载最新的文章(分页)本页文章列表中发布时间为最大的时间为依据

    4,当用户上拉可以加载更多的文章信息(按照发布时间)本页文章列表中发布时间最小的时间为依据

    5,如果是当前频道的首页,前端传递默认参数:

    • maxBehotTime:0(毫秒)

    • minBehotTime:20000000000000(毫秒)—>2063年

    5. 接口定义

    加载首页加载更多加载最新
    接口路径/api/v1/article/load/api/v1/article/loadmore/api/v1/article/loadnew
    请求方式POSTPOSTPOST
    参数ArticleHomeDtoArticleHomeDtoArticleHomeDto
    响应结果ResponseResultResponseResultResponseResult

    ArticleHomeDto

    package com.heima.model.article.dtos;
    
    import lombok.Data;
    
    import java.util.Date;
    
    @Data
    public class ArticleHomeDto {
    
        // 最大时间
        Date maxBehotTime;
        // 最小时间
        Date minBehotTime;
        // 分页size
        Integer size;
        // 频道ID
        String tag;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    6. 功能实现

    6.1:导入heima-leadnews-article微服务,资料在当天的文件夹中

    image-20210420000326669

    注意:需要在heima-leadnews-service的pom文件夹中添加子模块信息,如下:

    <modules>
        <module>heima-leadnews-usermodule>
        <module>heima-leadnews-articlemodule>
    modules>
    
    • 1
    • 2
    • 3
    • 4

    在idea中的maven中更新一下,如果工程还是灰色的,需要在重新添加文章微服务的pom文件,操作步骤如下:

    image-20210420001037992

    需要在nacos中添加对应的配置

    spring:
      datasource:
        driver-class-name: com.mysql.jdbc.Driver
        url: jdbc:mysql://localhost:3306/leadnews_article?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
        username: root
        password: root
    # 设置Mapper接口所对应的XML文件位置,如果你在Mapper接口中有自定义方法,需要进行该配置
    mybatis-plus:
      mapper-locations: classpath*:mapper/*.xml
      # 设置别名包扫描路径,通过该属性可以给包中的类注册别名
      type-aliases-package: com.heima.model.article.pojos
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    6.2:定义接口
    package com.heima.article.controller.v1;
    
    import com.heima.model.article.dtos.ArticleHomeDto;
    import com.heima.model.common.dtos.ResponseResult;
    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;
    
    @RestController
    @RequestMapping("/api/v1/article")
    public class ArticleHomeController {
    
    
        @PostMapping("/load")
        public ResponseResult load(@RequestBody ArticleHomeDto dto) {
            return null;
        }
    
        @PostMapping("/loadmore")
        public ResponseResult loadMore(@RequestBody ArticleHomeDto dto) {
            return null;
        }
    
        @PostMapping("/loadnew")
        public ResponseResult loadNew(@RequestBody ArticleHomeDto dto) {
            return null;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    6.3:编写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> {
    
        public List<ApArticle> loadArticleList(@Param("dto") ArticleHomeDto dto, @Param("type") Short type);
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    对应的映射文件

    在resources中新建mapper/ApArticleMapper.xml 如下配置:

    
    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
                
                <if test="type != null and type == 1">
                    and aa.publish_time  #{dto.minBehotTime}
                if>
                <if test="type != null and type == 2">
                    and aa.publish_time ]]> #{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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    6.4:编写业务层代码
    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 com.heima.model.common.dtos.ResponseResult;
    
    import java.io.IOException;
    
    public interface ApArticleService extends IService<ApArticle> {
    
        /**
         * 根据参数加载文章列表
         * @param loadtype 1为加载更多  2为加载最新
         * @param dto
         * @return
         */
        ResponseResult load(Short loadtype, ArticleHomeDto dto);
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    实现类:

    package com.heima.article.service.impl;
    
    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 com.heima.model.common.dtos.ResponseResult;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.commons.lang3.StringUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;
    
    import java.util.Date;
    import java.util.List;
    
    
    @Service
    @Transactional
    @Slf4j
    public class ApArticleServiceImpl  extends ServiceImpl<ApArticleMapper, ApArticle> implements ApArticleService {
    
        // 单页最大加载的数字
        private final static short MAX_PAGE_SIZE = 50;
    
        @Autowired
        private ApArticleMapper apArticleMapper;
    
        /**
         * 根据参数加载文章列表
         * @param loadtype 1为加载更多  2为加载最新
         * @param dto
         * @return
         */
        @Override
        public ResponseResult load(Short loadtype, ArticleHomeDto dto) {
            //1.校验参数
            Integer size = dto.getSize();
            if(size == null || size == 0){
                size = 10;
            }
            size = Math.min(size,MAX_PAGE_SIZE);
            dto.setSize(size);
    
            //类型参数检验
            if(!loadtype.equals(ArticleConstants.LOADTYPE_LOAD_MORE)&&!loadtype.equals(ArticleConstants.LOADTYPE_LOAD_NEW)){
                loadtype = 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());
            //2.查询数据
            List<ApArticle> apArticles = apArticleMapper.loadArticleList(dto, loadtype);
    
            //3.结果封装
            ResponseResult responseResult = ResponseResult.okResult(apArticles);
            return responseResult;
        }
        
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68

    定义常量类

    package com.heima.common.constants;
    
    public class ArticleConstants {
        public static final Short LOADTYPE_LOAD_MORE = 1;
        public static final Short LOADTYPE_LOAD_NEW = 2;
        public static final String DEFAULT_TAG = "__all__";
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    6.5:编写控制器代码
    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.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;
    
    @RestController
    @RequestMapping("/api/v1/article")
    public class ArticleHomeController {
    
    
        @Autowired
        private ApArticleService apArticleService;
    
        @PostMapping("/load")
        public ResponseResult load(@RequestBody ArticleHomeDto dto) {
            return apArticleService.load(ArticleConstants.LOADTYPE_LOAD_MORE,dto);
        }
    
        @PostMapping("/loadmore")
        public ResponseResult loadMore(@RequestBody ArticleHomeDto dto) {
            return apArticleService.load(ArticleConstants.LOADTYPE_LOAD_MORE,dto);
        }
    
        @PostMapping("/loadnew")
        public ResponseResult loadNew(@RequestBody ArticleHomeDto dto) {
            return apArticleService.load(ArticleConstants.LOADTYPE_LOAD_NEW,dto);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    6.6: swagger测试或前后端联调测试

    第一:在app网关的微服务的nacos的配置中心添加文章微服务的路由,完整配置如下:

    spring:
      cloud:
        gateway:
          globalcors:
            cors-configurations:
              '[/**]': # 匹配所有请求
                allowedOrigins: "*" #跨域处理 允许所有的域
                allowedMethods: # 支持的方法
                  - GET
                  - POST
                  - PUT
                  - DELETE
          routes:
            # 用户微服务
            - id: user
              uri: lb://leadnews-user
              predicates:
                - Path=/user/**
              filters:
                - StripPrefix= 1
            # 文章微服务
            - id: article
              uri: lb://leadnews-article
              predicates:
                - Path=/article/**
              filters:
                - StripPrefix= 1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    第二:启动nginx,直接使用前端项目测试,启动文章微服务,用户微服务、app网关微服务

    • 高性能

      作为高性能对象存储,在标准硬件条件下它能达到55GB/s的读、35GB/s的写速率

    • 可扩容

      不同MinIO集群可以组成联邦,并形成一个全局的命名空间,并跨越多个数据中心

    • SDK支持

      基于Minio轻量的特点,它得到类似Java、Python或Go等语言的sdk支持

    • 有操作页面

      面向用户友好的简单操作界面,非常方便的管理Bucket及里面的文件资源

    • 功能简单

      这一设计原则让MinIO不容易出错、更快启动

    • 丰富的API

      支持文件资源的分享连接及分享链接的过期策略、存储桶操作、文件列表访问及文件上传下载的基本功能等。

    • 文件变化主动通知

      存储桶(Bucket)如果发生改变,比如上传对象和删除对象,可以使用存储桶事件通知机制进行监控,并通过以下方式发布出去:AMQP、MQTT、Elasticsearch、Redis、NATS、MySQL、Kafka、Webhooks等。

  • 相关阅读:
    【Python&GIS】解决GIS属性表、矢量字段乱码,中文乱码
    你们看过《点燃我,温暖你》没有呀,里面比较火的那个爱心代码,今天小编用Python实现啦,这就是程序员的烂漫吗
    Docker之自定义镜像上传阿里云
    云安全-云原生k8s攻击点(8080,6443,10250未授权攻击点)
    深入分析:浏览器中输入一个URL会发生什么事情呢?
    OLED屏简介
    cf1200构造15道
    【Linux】系列入门摘抄笔记-6-tar打包压缩和vim编辑器
    CTK框架(二): 接口、插件和服务
    探索SOCKS5与SK5代理在现代网络环境中的应用
  • 原文地址:https://blog.csdn.net/xyhxuyonghao/article/details/134004028