目錄
1.1)需求分析
1.2)表結構分析
ap_article 文章基本信息表
?ap_article_config 文章配置表
ap_article_content 文章內容表
導入文章數據庫
實現思路
接口定義
功能實現
定義接口
編寫mapper文件
編寫業務層代碼
實現類:
定義常量類
編寫控制器代碼
swagger測試
1.1)需求分析
文章布局展示:
所謂文章加載,指的是從數據庫中將文章的數據查詢出來,然后傳遞給前端頁面進行展示。
根據圖片可得,我們的文章對象可以是無圖,單圖,多圖等多種形態。
1.2)表結構分析
在表結構設計上,采用了分庫分表的思想,簡而言之就是根據我們項目來創建庫,然后將該庫的表進行細分,如圖所示,我們建立了一個名為leadnews_article庫,專門用來存放黑馬頭條中跟文章有關的信息,其中有文章表,文章配置表,文章內容表。這樣的好處就是將文章大寬表進行細分,冷熱數據分離,大字段分離等。像我們的微服務項目,經常會這樣設計表。
在這里,嵌入一個Java數據類型和MySQL數據類型的對照表,方便在開發和數據庫設計時作為參考,直接跳轉鏈接直達:
Java和MySQL數據類型對照表https://blog.csdn.net/weixin_43822632/article/details/144501788?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522af79b0f2b9c1bdd19e156c4a27fe22d7%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=af79b0f2b9c1bdd19e156c4a27fe22d7&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_ecpm_v1~rank_v31_ecpm-5-144501788-null-null.142^v102^pc_search_result_base4&utm_term=java%E5%92%8Cmysql%E7%9A%84%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B%E5%AF%B9%E7%85%A7%E5%9B%BE&spm=1018.2226.3001.4187
ap_article 文章基本信息表
代碼如下:
-- auto-generated definition
create table ap_article
(id bigint unsigned auto_incrementprimary key,title varchar(50) null comment '標題',author_id int unsigned null comment '文章作者的ID',author_name varchar(20) null comment '作者昵稱',channel_id int unsigned null comment '文章所屬頻道ID',channel_name varchar(10) null comment '頻道名稱',layout tinyint unsigned null comment '文章布局0 無圖文章1 單圖文章2 多圖文章',flag tinyint unsigned null comment '文章標記0 普通文章1 熱點文章2 置頂文章3 精品文章4 大V 文章',images varchar(1000) null comment '文章圖片多張逗號分隔',labels varchar(500) null comment '文章標簽最多3個 逗號分隔',likes int unsigned null comment '點贊數量',collection int unsigned null comment '收藏數量',comment int unsigned null comment '評論數量',views int unsigned null comment '閱讀數量',province_id int unsigned null comment '省市',city_id int unsigned null comment '市區',county_id int unsigned null comment '區縣',created_time datetime null comment '創建時間',publish_time datetime null comment '發布時間',sync_status tinyint(1) default 0 null comment '同步狀態',origin tinyint unsigned default '0' null comment '來源',static_url varchar(150) null
)comment '文章信息表,存儲已發布的文章';
?ap_article_config 文章配置表
代碼如下:
-- auto-generated definition
create table ap_article_config
(id bigint unsigned auto_increment comment '主鍵'primary key,article_id bigint unsigned null comment '文章ID',is_comment tinyint unsigned null comment '是否可評論',is_forward tinyint unsigned null comment '是否轉發',is_down tinyint unsigned null comment '是否下架',is_delete tinyint unsigned null comment '是否已刪除'
)comment 'APP已發布文章配置表';create index idx_article_idon ap_article_config (article_id);
ap_article_content 文章內容表
代碼如下:
-- auto-generated definition
create table ap_article_content
(id bigint unsigned auto_increment comment '主鍵'primary key,article_id bigint unsigned null comment '文章ID',content longtext null comment '文章內容'
)comment 'APP已發布文章內容表';create index idx_article_idon ap_article_content (article_id);
導入文章數據庫
ap_article文章表對應實體
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>** @author itheima*/@Data
@TableName("ap_article")//指定數據庫中的名字
public class ApArticle implements Serializable {//IdType.ID_WORKER指雪花算法生成id@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;
}
ap_article_config文章配置對應實體類
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 lombok.NoArgsConstructor;import java.io.Serializable;/*** <p>* APP已發布文章配置表* </p>** @author itheima*/@Data
@NoArgsConstructor//添加無參構造方法
@TableName("ap_article_config")
//文章配置類,是一個pojo類,和數據庫中對應的表一一對應
public class ApArticleConfig implements Serializable {//添加有參構造方法,因為這個構造方法只有一個參數字段,所以需要手動聲明//如果是需要全參構造方法,則可以直接在類上使用@AllArgsConstructor注解//這個構造方法是只給articleId賦值,其他字段采取默認值即可public ApArticleConfig(Long articleId){this.articleId = articleId;//文章idthis.isComment = true;//默認開啟評論this.isForward = true;//默認開啟轉發this.isDelete = false;//默認沒有刪除this.isDown = false;//默認沒有下架}@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;
}
ap_article_content 文章內容對應的實體類
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 {//IdType.ID_WORKER指雪花算法生成id@TableId(value = "id",type = IdType.ID_WORKER)private Long id;/*** 文章id*/@TableField("article_id")private Long articleId;/*** 文章內容*/private String content;
}
實現思路
1,在默認頻道展示10條文章信息
2,可以切換頻道查看不同種類文章
3,當用戶下拉可以加載最新的文章(分頁)本頁文章列表中發布時間為最大的時間為依據
4,當用戶上拉可以加載更多的文章信息(按照發布時間)本頁文章列表中發布時間最小的時間為依據
5,如果是當前頻道的首頁,前端傳遞默認參數:
-
maxBehotTime:0(毫秒)
-
minBehotTime:20000000000000(毫秒)--->2063年
接口定義
ArticleHomeDto
import lombok.Data;import java.util.Date;@Data//注解@Data是lombok提供的注解,它能自動生成get、set方法,還能自動生成equals、hashCode、toString方法。
//這是文章首頁dto,用來封裝從前端傳遞過來的請求參數
public class ArticleHomeDto {// 最大時間Date maxBehotTime;// 最小時間Date minBehotTime;// 分頁sizeInteger size;// 頻道IDString tag;
}
功能實現
導入heima-leadnews-article微服務:
注意:需要在heima-leadnews-service的pom文件夾中添加子模塊信息,如下:
當我們在父工程下創建一個子工程時,需要在父工程中添加<module>標簽,指名子工程的名字。
需要在nacos中添加對應的配置:
spring:datasource:driver-class-name: com.mysql.jdbc.Driverurl: jdbc:mysql://localhost:3306/leadnews_article?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTCusername: rootpassword: root
# 設置Mapper接口所對應的XML文件位置,如果你在Mapper接口中有自定義方法,需要進行該配置
mybatis-plus:mapper-locations: classpath*:mapper/*.xml# 設置別名包掃描路徑,通過該屬性可以給包中的類注冊別名type-aliases-package: com.heima.model.article.pojos
定義接口
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;}
}
編寫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);}
對應的映射文件
在resources中新建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">SELECTaa.*FROM`ap_article` aaLEFT JOIN ap_article_config aac ON aa.id = aac.article_id<where>and aac.is_delete != 1and 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 desclimit #{dto.size}</select></mapper>
編寫業務層代碼
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);}
實現類:
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;@Autowiredprivate ApArticleMapper apArticleMapper;/*** 根據參數加載文章列表* @param loadtype 1為加載更多 2為加載最新* @param dto* @return*/@Overridepublic 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;}}
定義常量類
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__";}
編寫控制器代碼
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 {@Autowiredprivate 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);}
}
swagger測試
第一:在app網關的微服務的nacos的配置中心添加文章微服務的路由,完整配置如下:
spring:cloud:gateway:globalcors:cors-configurations:'[/**]': # 匹配所有請求allowedOrigins: "*" #跨域處理 允許所有的域allowedMethods: # 支持的方法- GET- POST- PUT- DELETEroutes:# 用戶微服務- id: useruri: lb://leadnews-userpredicates:- Path=/user/**filters:- StripPrefix= 1# 文章微服務- id: articleuri: lb://leadnews-articlepredicates:- Path=/article/**filters:- StripPrefix= 1
?第二:啟動nginx,直接使用前端項目測試,啟動文章微服務,用戶微服務、app網關微服務