- 能够掌握自媒体文章审核的流程
- 能够使用阿里云安全服务检测文章内容
- 能够完成自媒体文章审核的功能
- 能够完成自媒体发布文章与审核对接
1.1.自动审核流程介绍
做为内容类产品,内容安全非常重要,所以需要进行对自媒体用户发布的文章进行审核以后才能到app端展示给用户。
审核的流程如下:也可以查看当前讲义文件夹下:自媒体文章发布时序图.pdf
1.当自媒体用户提交发布文章之后,会发消息给kafka提交审核,平台运营端接收文章信息
2.根据自媒体文章id查询文章信息
3.如果当前文章的状态为4(人工审核通过),则无需再进行自动审核审核,保存app文章相关数据即可
4.文章状态为8,发布时间小于等于当前时间,则直接保存app文章相关数据
5.文章状态为1,则进行自动审核
5.1 调用阿里云文本反垃圾服务,进行文本审核,如果审核不成功或需要人工审核,修改自媒体文章状态
5.2 调用阿里云图片审核服务,如果审核不通过或需要人工审核,修改自媒体文章状态
5.3 文章内容中是否有自管理的敏感词,如果有则审核不通过,修改自媒体文章状态
5.4 自媒体文章发布时间大于当前时间,修改自媒体文章状态为8(审核通过待发布状态)
5.5 审核通过,修改自媒体文章状态为 9 (审核通过)
6.保存app相关数据
ap_article_config 文章配置
ap_article 文章
ap_article_content 文章内容
ap_author 文章作者
7.创建索引(为后续app端的搜索功能做数据准备)
1.2.相关表结构介绍
自媒体库:leadnews_wemedia数据库,自媒体文章表:wm_news
创建对应的实体类(之前已经创建)
文章库:leadnews_article数据库,文章作者表:ap_author
创建对应的实体类(之前已经创建)
文章库:leadnews_article数据库,文章信息表:ap_article
- layout 文章布局 0 无图文章 1 单图文章 2 多图文章
- flag 文章标记 0 普通文章 1 热点文章 2 置顶文章 3 精品文章 4 大V 文章
- images 文章图片 多张逗号分隔
在heima-leadnews-model中创建ApArticle实体类
/**
* <p>
* 文章信息表,存储已发布的文章
* </p>
*
* @author itheima
*/
@Data
@TableName("ap_article")
public class ApArticle {
@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;
}
文章库:leadnews_article数据库,文章内容表:ap_article_content
在heima-leadnews-model中创建ApArticleContent实体类
@Data
@TableName("ap_article_content")
public class ApArticleContent {
@TableId(value = "id",type = IdType.ID_WORKER)
private Long id;
/**
* 文章id
*/
@TableField("article_id")
private Long articleId;
/**
* 文章内容
*/
private String content;
}
文章库:leadnews_article数据库,文章配置表:ap_article_config
在heima-leadnews-model中创建ApArticleConfig实体类
/**
* <p>
* APP已发布文章配置表
* </p>
*
* @author itheima
*/
@Data
@TableName("ap_article_config")
public class ApArticleConfig {
@TableId(value = "id",type = IdType.ID_WORKER)
private Long id;
/**
* 文章id
*/
@TableField("article_id")
private Long articleId;
/**
* 是否可评论
*/
@TableField("is_comment")
private Boolean isComment;
/**
* 是否转发
*/
@TableField("is_forward")
private Boolean isForward;
/**
* 是否下架
*/
@TableField("is_down")
private Boolean isDown;
/**
* 是否已删除
*/
@TableField("is_delete")
private Boolean isDelete;
}
1.3.自媒体微服务远程接口准备
1.3.1.自媒体Feign接口
1.需求说明和feign接口定义
在自动审核的时候需要自媒体的远程接口,如下:
1 根据文章id查询自媒体文章的数据
2 在审核的过程中,审核失败或者成功需要修改自媒体文章的状态
3 在文章进行保存的时候需要查询作者信息,需要通过自媒体用户关联查询作者信息
2.在heima-leadnews-admin的pom.xml文件中引入Feign的依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
3.在heima-leadnews-admin中创建WemediaFeign
@FeignClient("leadnews-wemedia")
public interface WemediaFeign {
/**
* 根据文章id查询自媒体文章的数据
* @param id
* @return
*/
@GetMapping("/api/v1/news/findOne/{id}")
public WmNews findById(@PathVariable("id") Integer id);
/**
* 在审核的过程中,审核失败或者成功需要修改自媒体文章的状态
* @param wmNews
* @return
*/
@PostMapping("/api/v1/news/update")
public ResponseResult updateWmNews(WmNews wmNews);
/**
* 在文章进行保存的时候需要查询作者信息,需要通过自媒体用户关联查询作者信息
* @param id
* @return
*/
@GetMapping("/api/v1/user/findOne/{id}")
public WmUser findWmUserById(@PathVariable("id") Integer id);
}
4.在heima-leadnews-apis中的WmNewsControllerApi接口中新增方法
/**
* 根据id查询文章信息
* @param id
* @return
*/
public WmNews findById(Integer id);
/**
* 修改文章
* @param wmNews
* @return
*/
public ResponseResult updateWmNews(WmNews wmNews);
5.在heima-leadnews-apis中的WmUserControllerApi接口中新增方法
/**
* 根据id查询用户
* @param id
* @return
*/
public WmUser findWmUserById(Integer id);
6.在heima-leadnews-wemedia中的WmNewsController实现方法
/**
* 根据id查询文章信息
* @param id
* @return
*/
@GetMapping("/findOne/{id}")
@Override
public WmNews findById(@PathVariable("id") Integer id) {
return wmNewsService.getById(id);
}
/**
* 修改文章
* @param wmNews
* @return
*/
@PostMapping("/update")
@Override
public ResponseResult updateWmNews(@RequestBody WmNews wmNews) {
boolean b = wmNewsService.updateById(wmNews);
return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
}
7.在heima-leadnews-wemedia中的WmUserController实现方法
/**
* 根据id查询用户
* @param id
* @return
*/
@GetMapping("/findOne/{id}")
@Override
public WmUser findWmUserById(@PathVariable("id") Integer id) {
return wmUserService.getById(id);
}
8.启动heima-leadnews-wemedia,使用postman测试
根据id查询文章信息测试
修改文章
根据id查询用户
1.3.2.文章feign接口
1.3.2.1.分布式id
随着业务的增长,文章表可能要占用很大的物理存储空间,为了解决该问题,后期使用数据库分片技术。将一个数据库进行拆分,通过数据库中间件连接。如果数据库中该表选用ID自增策略,则可能产生重复的ID,此时应该使用分布式ID生成策略来生成ID。
雪花算法实现
snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。其核心思想是:使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生 4096 个 ID),最后还有一个符号位,永远是0
mybatis-plus已经集成了雪花算法,完成以下两步即可在项目中集成雪花算法
1.在heima-leadnews-article的application.yml文件中进行分布式id的设置
mybatis-plus:
......
global-config:
datacenter-id: 1 #数据中心id
worker-id: 1 #机器中心id
注意:如果当前项目做集群了,那数据中心id和机器中心id至少其中一个要不一致,比如:
集群服务器1
datacenter-id: 1 #数据中心id
worker-id: 1 #机器中心id
集群服务器2
datacenter-id: 1 #数据中心id
worker-id: 2 #机器中心id
2.在heima-leadnews-model的ApArticle、ApArticleConfig、ApArticleContent实体类中的id属性中设置分布式id的标识
/*
IdType.AUTO : 数据库自增
IdType.INPUT : 自行输入ID
IdType.NONE : 未设置主键类型,这是默认值
IdType.UUID : 32位UUID字符串,只有当插入对象ID为空,才自动填充。
IdType.ID_WORKER : 分布式全局唯一ID,长整型类型,只有当插入对象ID为空,才自动填充。
IdType.ID_WORKER_STR:分布式全局唯一ID,字符串类型,只有当插入对象ID为空,才自动填充。
*/
@TableId(value = "id",type = IdType.ID_WORKER)
private Long id;
1.3.2.2.文章微服务远程调用接口准备
在文章审核成功以后需要在app的article库中新增文章数据
1.保存文章信息 ap_article,需要返回当前文章,并且需要获取保存后获取到的主键
2.保存文章配置信息 ap_article_config
3.保存文章内容 ap_article_content
4.在保存文章的时候需要关联作者,需要根据名称查询作者信息
1.在heima-leadnews-admin中创建ArticleFeign
@FeignClient("leadnews-article")
public interface ArticleFeign {
/**
* 保存文章信息 ap_article,需要返回当前文章,并且需要获取保存后获取到的主键
* @param apArticle
* @return
*/
@PostMapping("/api/v1/article/save")
public ApArticle saveAparticle(ApArticle apArticle);
/**
* 保存文章配置信息 ap_article_config
* @param apArticleConfig
* @return
*/
@PostMapping("/api/v1/article_config/save")
public ResponseResult saveArticleConfig(ApArticleConfig apArticleConfig);
/**
* 保存文章内容 ap_article_content
* @param apArticleContent
* @return
*/
@PostMapping("/api/v1/article_content/save")
public ResponseResult saveArticleContent(ApArticleContent apArticleContent);
/**
* 在保存文章的时候需要关联作者,需要根据名称查询作者信息
* @param name
* @return
*/
@GetMapping("/api/v1/author/findByName/{name}")
public ApAuthor selectAuthorByName(@PathVariable("name") String name);
}
1.3.2.3.文章微服务远程调用功能实现
1.将资料中的ApArticleConfigControllerApi、ApArticleContentControllerApi、ApArticleControllerApi拷贝到heima-leadnews-apis中
public interface ApArticleConfigControllerApi {
/**
* 保存app端文章配置
* @param apArticleConfig
* @return
*/
ResponseResult saveArticleConfig(ApArticleConfig apArticleConfig);
}
public interface ApArticleContentControllerApi {
/**
* 保存app端文章内容
* @param apArticleContent
* @return
*/
ResponseResult saveArticleContent(ApArticleContent apArticleContent);
}
public interface ApArticleControllerApi {
/**
* 保存app文章
* @param apArticle
* @return
*/
ApArticle saveArticle(ApArticle apArticle);
}
2.在heima-leadnews-apis的AuthorControllerApi新增接口
public interface AuthorControllerApi {
......
/**
* 根据名称查询作者
* @param name
* @return
*/
public ApAuthor selectAuthorByName(String name);
}
3.将资料中的ApArticleConfigMapper、ApArticleContentMapper、ApArticleMapper拷贝到heima-leadnews-article中
@Mapper
public interface ApArticleConfigMapper extends BaseMapper<ApArticleConfig> {
}
@Mapper
public interface ApArticleContentMapper extends BaseMapper<ApArticleContent> {
}
@Mapper
public interface ApArticleMapper extends BaseMapper<ApArticle> {
}
4.将资料中的ApArticleConfigService、ApArticleContentService、ApArticleService拷贝到heima-leadnews-article中
public interface ApArticleConfigService extends IService<ApArticleConfig> {
}
public interface ApArticleContentService extends IService<ApArticleContent> {
}
public interface ApArticleService extends IService<ApArticle> {
}
5.将资料中ApArticleConfigServiceImpl、ApArticleContentServiceImpl、ApArticleServiceImpl拷贝到heima-leadnews-article中
@Service
public class ApArticleConfigServiceImpl extends ServiceImpl<ApArticleConfigMapper, ApArticleConfig> implements ApArticleConfigService {
}
@Service
public class ApArticleContentServiceImpl extends ServiceImpl<ApArticleContentMapper,ApArticleContent> implements ApArticleContentService {
}
@Service
public class ApArticleServiceImpl extends ServiceImpl<ApArticleMapper, ApArticle> implements ApArticleService {
}
6.在heima-leadnews-article的AuthorController中实现接口方法
@RestController
@RequestMapping("/api/v1/author")
public class AuthorController implements AuthorControllerApi {
@Autowired
AuthorService authorService;
.......
/**
* 根据名称查询作者
* @param name
* @return
*/
@GetMapping("/findByName/{name}")
@Override
public ApAuthor selectAuthorByName(@PathVariable("name") String name) {
ApAuthor apAuthor = authorService.getOne(Wrappers.<ApAuthor>lambdaQuery().eq(ApAuthor::getName, name));
return apAuthor;
}
}
7.在heima-leadnews-article中创建ApArticleConfigController、ApArticleContentController、ApArticleController
@RestController
@RequestMapping("/api/v1/article_config")
public class ApArticleConfigController implements ApArticleConfigControllerApi {
@Autowired
private ApArticleConfigService apArticleConfigService;
/**
* 保存app端文章配置
* @param apArticleConfig
* @return
*/
@PostMapping("/save")
@Override
public ResponseResult saveArticleConfig(@RequestBody ApArticleConfig apArticleConfig) {
apArticleConfigService.save(apArticleConfig);
return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
}
}
@RestController
@RequestMapping("/api/v1/article_content")
public class ApArticleContentController implements ApArticleContentControllerApi {
@Autowired
private ApArticleContentService apArticleContentService;
/**
* 保存app端文章内容
* @param apArticleContent
* @return
*/
@PostMapping("/save")
@Override
public ResponseResult saveArticleContent(@RequestBody ApArticleContent apArticleContent) {
apArticleContentService.save(apArticleContent);
return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
}
}
@RestController
@RequestMapping("/api/v1/article")
public class ApArticleController implements ApArticleControllerApi {
@Autowired
private ApArticleService articleService;
/**
* 保存app文章
* @param apArticle
* @return
*/
@PostMapping("/save")
@Override
public ApArticle saveArticle(@RequestBody ApArticle apArticle) {
articleService.save(apArticle);
return apArticle;
}
}
8.启动当前项目,使用postman进行测试
保存文章信息测试
保存文章配置信息测试
保存文章内容测试
根据名称查询作者信息
1.4.自媒体文章审核准备-自管理敏感词查询
1.在heima-leadnews-admin的AdSensitiveMapper中新增接口
@Mapper
public interface AdSensitiveMapper extends BaseMapper<AdSensitive> {
public List<String> findAllSensitive();
}
2.在heima-leadnews-admin中创建映射配置文件
<?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.admin.mapper.AdSensitiveMapper">
<select id="findAllSensitive" resultType="string">
select sensitives from ad_sensitive
</select>
</mapper>
1.5.文章审核功能
1.5.1.业务层接口定义
1.在heima-leadnews-admin中创建WemediaNewsAutoScanService
public interface WemediaNewsAutoScanService {
/**
* 自媒体文章审核
* @param id
*/
public void autoScanByMediaNewsId(Integer id);
}
2.在heima-leadnews-admin中创建WemediaNewsAutoScanServiceImpl
具体是实现思路如下
1.5.2.业务逻辑实现-抽取内容和图片
在heima-leadnews-admin中的WemediaNewsAutoScanServiceImpl进行代码编写
@Service
@Log4j2
public class WemediaNewsAutoScanServiceImpl implements WemediaNewsAutoScanService {
@Autowired
private WemediaFeign wemediaFeign;
@GlobalTransactional
@Override
public void autoScanByMediaNewsId(Integer id) {
if (id == null) {
log.error("当前的审核id空");
return;
}
//1.根据id查询自媒体文章信息
WmNews wmNews = wemediaFeign.findById(id);
if (wmNews == null) {
log.error("审核的自媒体文章不存在,自媒体的id:{}", id);
return;
}
//2.文章状态为4(人工审核通过)直接保存数据和创建索引
if (wmNews.getStatus() == 4) {
//保存数据
saveAppArticle(wmNews);
return;
}
//3.文章状态为8 发布时间>当前时间 直接保存数据
if (wmNews.getStatus() == 8 && wmNews.getPublishTime().getTime() <= System.currentTimeMillis()) {
//保存数据
saveAppArticle(wmNews);
return;
}
//4.文章状态为1,待审核
if (wmNews.getStatus() == 1) {
//抽取文章内容中的纯文本和图片
Map<String, Object> contentAndImagesResult = handleTextAndImages(wmNews);
//4.1 文本审核
//4.2 图片审核
//4.3 自管理的敏感词审核
//4.4 发布时间大于当前时间,
//5.审核通过,修改自媒体文章状态为9 保存app端相关文章信息
}
}
/**
* 保存app文章相关的数据
*
* @param wmNews
*/
private void saveAppArticle(WmNews wmNews) {
}
/**
* 提取文本内容和图片
*
* @param wmNews
* @return
*/
private Map<String, Object> handleTextAndImages(WmNews wmNews) {
//文章的内容
String content = wmNews.getContent();
//存储纯文本内容
StringBuilder sb = new StringBuilder();
//存储图片
List<String> images = new ArrayList<>();
List<Map> contentList = JSONArray.parseArray(content, Map.class);
for (Map map : contentList) {
if (map.get("type").equals("text")) {
sb.append(map.get("value"));
}
if (map.get("type").equals("image")) {
images.add((String) map.get("value"));
}
}
if (wmNews.getImages() != null && wmNews.getType() != 0) {
String[] split = wmNews.getImages().split(",");
images.addAll(Arrays.asList(split));
}
Map<String, Object> resultMap = new HashMap<>();
resultMap.put("content", sb.toString());
resultMap.put("images", images);
return resultMap;
}
1.5.3.业务逻辑实现-文本审核
@GlobalTransactional
@Override
public void autoScanByMediaNewsId(Integer id) {
........
//4.文章状态为1,待审核
if (wmNews.getStatus() == 1) {
......
//4.1 文本审核
boolean textScanBoolean = handleTextScan((String) contentAndImagesResult.get("content"), wmNews);
if (!textScanBoolean) return;
........
}
}
/**
* 文本审核
*
* @param content
* @param wmNews
* @return
*/
private boolean handleTextScan(String content, WmNews wmNews) {
boolean flag = true;
try {
Map map = greeTextScan.greeTextScan(content);
//审核不通过
if (!map.get("suggestion").equals("pass")) {
//审核失败
if (map.get("suggestion").equals("block")) {
//修改自媒体文章的状态,并告知审核失败原因
updateWmNews(wmNews, (short) 2, "文章内容中有敏感词汇");
flag = false;
}
//人工审核
if (map.get("suggestion").equals("review")) {
//修改自媒体文章的状态,并告知审核失败原因
updateWmNews(wmNews, (short) 3, "文章内容中有不确定词汇");
flag = false;
}
}
} catch (Exception e) {
e.printStackTrace();
flag = false;
}
return flag;
}
/**
* 修改自媒体文章
*
* @param wmNews
* @param status
* @param msg
*/
private void updateWmNews(WmNews wmNews, short status, String msg) {
wmNews.setStatus(status);
wmNews.setReason(msg);
wemediaFeign.updateWmNews(wmNews);
}
1.5.4.业务逻辑实现-图片审核
@GlobalTransactional
@Override
public void autoScanByMediaNewsId(Integer id) {
......
//4.文章状态为1,待审核
if (wmNews.getStatus() == 1) {
.......
//4.2 图片审核
boolean imagesScanBoolean = handleImagesScan((List<String>) contentAndImagesResult.get("images"), wmNews);
if (!imagesScanBoolean) return;
.......
}
}
@Autowired
private GreenImageScan greenImageScan;
@Autowired
private FastDFSClient fastDFSClient;
@Value("${fdfs.url}")
private String fileServerUrl;
/**
* 审核图片
*
* @param images
* @param wmNews
* @return
*/
private boolean handleImagesScan(List<String> images, WmNews wmNews) {
if (images == null) {
return true;
}
boolean flag = true;
List<byte[]> imageList = new ArrayList<>();
try {
for (String image : images) {
String imageName = image.replace(fileServerUrl, "");
int index = imageName.indexOf("/");
String groupName = imageName.substring(0, index);
String imagePath = imageName.substring(index + 1);
byte[] imageByte = fastDFSClient.download(groupName, imagePath);
imageList.add(imageByte);
}
//阿里云图片审核
Map map = greenImageScan.imageScan(imageList);
//审核不通过
if (!map.get("suggestion").equals("pass")) {
//审核失败
if (map.get("suggestion").equals("block")) {
//修改自媒体文章的状态,并告知审核失败原因
updateWmNews(wmNews, (short) 2, "文章中图片有违规");
flag = false;
}
//人工审核
if (map.get("suggestion").equals("review")) {
//修改自媒体文章的状态,并告知审核失败原因
updateWmNews(wmNews, (short) 3, "文章图片有不确定元素");
flag = false;
}
}
} catch (Exception e) {
e.printStackTrace();
flag = false;
}
return flag;
}
1.5.5.业务逻辑实现-自管理敏感词审核
@GlobalTransactional
@Override
public void autoScanByMediaNewsId(Integer id) {
.......
//4.文章状态为1,待审核
if (wmNews.getStatus() == 1) {
.......
//4.3 自管理的敏感词审核
boolean sensitiveScanBoolean = handleSensitive((String) contentAndImagesResult.get("content"), wmNews);
if (!sensitiveScanBoolean) return;
//4.4 发布时间大于当前时间,
if (wmNews.getPublishTime().getTime() > System.currentTimeMillis()) {
//修改文章状态为8
updateWmNews(wmNews, (short) 8, "审核通过,待发布");
return;
}
//5.审核通过,修改自媒体文章状态为9 保存app端相关文章信息
saveAppArticle(wmNews);
}
}
@Autowired
private AdSensitiveMapper adSensitiveMapper;
/**
* 敏感词审核
*
* @param content
* @param wmNews
* @return
*/
private boolean handleSensitive(String content, WmNews wmNews) {
boolean flag = true;
List<String> allSensitive = adSensitiveMapper.findAllSensitive();
//初始化敏感词
SensitiveWordUtil.initMap(allSensitive);
//文章内容自管理敏感词过滤
Map<String, Integer> resultMap = SensitiveWordUtil.matchWords(content);
if (resultMap.size() > 0) {
log.error("敏感词过滤没有通过,包含了敏感词:{}", resultMap);
//找到了敏感词,审核不通过
updateWmNews(wmNews, (short) 2, "文章中包含了敏感词");
flag = false;
}
return flag;
}
/**
* 保存app文章相关的数据
*
* @param wmNews
*/
private void saveAppArticle(WmNews wmNews) {
}
1.5.6.业务逻辑实现-保存app端文章数据
@Autowired
ArticleFeign articleFeign;
/**
* 保存app文章相关的数据
*
* @param wmNews
*/
private void saveAppArticle(WmNews wmNews) {
//保存app文章
ApArticle apArticle = saveArticle(wmNews);
......
}
@Autowired
AdChannelMapper adChannelMapper;
/**
* 保存文章
* @param wmNews
* @return
*/
private ApArticle saveArticle(WmNews wmNews) {
ApArticle apArticle = new ApArticle();
apArticle.setTitle(wmNews.getTitle());
apArticle.setLayout(wmNews.getType());
apArticle.setImages(wmNews.getImages());
apArticle.setCreatedTime(new Date());
//获取作者相关信息
Integer wmUserId = wmNews.getUserId();
WmUser wmUser = wemediaFeign.findWmUserById(wmUserId);
if(wmUser != null){
String wmUserName = wmUser.getName();
ApAuthor apAuthor = articleFeign.selectAuthorByName(wmUserName);
if(apAuthor != null){
apArticle.setAuthorId(apAuthor.getId().longValue());
apArticle.setAuthorName(apAuthor.getName());
}
}
//获取频道相关信息
Integer channelId = wmNews.getChannelId();
AdChannel channel = adChannelMapper.selectById(channelId);
if(channel != null){
apArticle.setChannelId(channel.getId());
apArticle.setChannelName(channel.getName());
}
return articleFeign.saveAparticle(apArticle);
}
1.5.7.业务逻辑实现-保存app端文章配置和内容
/**
* 保存app文章相关的数据
*
* @param wmNews
*/
private void saveAppArticle(WmNews wmNews) {
....
//保存app文章配置
saveArticleConfig(apArticle);
//保存app文章内容
saveArticleContent(apArticle,wmNews);
//修改自媒体文章的状态为9
wmNews.setArticleId(apArticle.getId());
updateWmNews(wmNews,(short)9,"审核通过");
//TODO es索引创建
}
/**
* 创建app端文章配置信息
* @param apArticle
*/
private void saveArticleConfig(ApArticle apArticle) {
ApArticleConfig apArticleConfig = new ApArticleConfig();
apArticleConfig.setArticleId(apArticle.getId());
apArticleConfig.setIsForward(true);
apArticleConfig.setIsDelete(false);
apArticleConfig.setIsDown(true);
apArticleConfig.setIsComment(true);
articleFeign.saveArticleConfig(apArticleConfig);
}
/**
* 创建app端文章内容信息
* @param apArticle
* @param wmNews
*/
private void saveArticleContent(ApArticle apArticle, WmNews wmNews) {
ApArticleContent apArticleContent = new ApArticleContent();
apArticleContent.setArticleId(apArticle.getId());
apArticleContent.setContent(wmNews.getContent());
articleFeign.saveArticleContent(apArticleContent);
}
1.5.8.测试
在heima-leadnews-admin中编写单元测试类
@SpringBootTest(classes = AdminApplication.class)
@RunWith(SpringRunner.class)
public class WemediaNewsAutoScanServiceTest {
@Autowired
private WemediaNewsAutoScanService wemediaNewsAutoScanService;
@Test
public void testScanNews(){
wemediaNewsAutoScanService.autoScanByMediaNewsId(6219);
}
}
1.5.9.文章审核功能-发布文章提交审核定义监听接受消息
在审核文章流程的第一步,当自媒体人发布一篇文章后会马上进行审核,这个时候是通过消息中间件进行数据的传递的。所以说需要配置生产者和消费者。目前自媒体微服务就是生产者,admin就是消费者
1.5.9.1.生产者编写
1.在heima-leadnews-common项目中引入kafka 的依赖
<!-- kafkfa -->
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
</dependency>
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka-clients</artifactId>
</dependency>
<!--kafka stream-->
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka-streams</artifactId>
</dependency>
2.在heima-leadnews-wemedia的application.yml中配置
spring:
.....
kafka:
bootstrap-servers: 192.168.200.130:9092
producer:
retries: 10
key-serializer: org.apache.kafka.common.serialization.StringSerializer
value-serializer: org.apache.kafka.common.serialization.StringSerializer
3.修改heima-leadnews-wemedia的WmNewsServiceImpl
/**
* 保存或修改文章
*
* @param wmNews
* @param isSubmit
*/
private void saveWmNews(WmNews wmNews, Short isSubmit) {
wmNews.setStatus(isSubmit);
wmNews.setUserId(WmThreadLocalUtils.getUser().getId());
wmNews.setCreatedTime(new Date());
wmNews.setSubmitedTime(new Date());
wmNews.setEnable((short) 1);
boolean flag = false;
if (wmNews.getId() == null) {
flag = save(wmNews);
} else {
//如果是修改,则先删除素材与文章的关系
LambdaQueryWrapper<WmNewsMaterial> queryWrapper = new LambdaQueryWrapper();
queryWrapper.eq(WmNewsMaterial::getNewsId, wmNews.getId());
wmNewsMaterialMapper.delete(queryWrapper);
flag = updateById(wmNews);
}
//发送消息
if(flag){
kafkaTemplate.send(NewsAutoScanConstants.WM_NEWS_AUTO_SCAN_TOPIC,JSON.toJSONString(wmNews.getId()));
}
}
4.在heima-leadnews-common创建常量类
public class NewsAutoScanConstants {
public static final String WM_NEWS_AUTO_SCAN_TOPIC="wm.news.auto.scan.topic";
}
1.5.9.2.消费者编写
1.在heima-leadnews-admin中创建WemediaNewsAutoListener
@Component
public class WemediaNewsAutoListener {
@Autowired
WemediaNewsAutoScanService wemediaNewsAutoScanService;
@KafkaListener(topics = NewsAutoScanConstants.WM_NEWS_AUTO_SCAN_TOPIC)
public void recevieMessage(ConsumerRecord<?,?> record){
Optional<? extends ConsumerRecord<?, ?>> optional = Optional.ofNullable(record);
if(optional.isPresent()){
Object value = record.value();
wemediaNewsAutoScanService.autoScanByMediaNewsId(Integer.valueOf((String) value));
}
}
}
2.在heima-leadnews-admin的application.yml文件中配置kafka
spring:
.....
kafka:
bootstrap-servers: 192.168.200.130:9092
consumer:
group-id: ${spring.application.name}-kafka-group
key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
value-deserializer: org.apache.kafka.common.serialization.StringDeserializer
3.在heima-leadnews-admin的pom.xml中引入seata依赖
<dependency>
<groupId>com.heima</groupId>
<artifactId>heima-leadnews-seata</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
4.将file.conf和registry.conf拷贝到heima-leadnews-admin的resources目录下,并修改file.conf的内容
5.在heima-leadnews-admin的application.yml文件中配置
spring:
......
cloud:
......
alibaba:
seata:
tx-service-group: ${spring.application.name}_tx_group
6.在heima-leadnews-admin中创建SeataConfig
@Configuration
@ComponentScan("com.heima.seata.config")
public class SeataConfig {
}
7.在heima-leadnews-admin的WemediaNewsAutoScanServiceImpl的autoScanByMediaNewsId()方法上添加@GlobalTransactional
@GlobalTransactional
@Override
public void autoScanByMediaNewsId(Integer id) {
}
1.6.综合测试
服务启动列表:
1,nacos
2,seata
3,fastdfs
4,zookeeper&kafka
5,article微服务
6,wemedia微服务
7,启动wemedia网关微服务
8,admin微服务
9,启动前端系统wemedia
测试动作:在自媒体前端进行发布文章