Loading

黑马头条项目[修改版]-第七天

0.黑马头条-学习目标

  • 能够掌握自媒体文章审核的流程
  • 能够使用阿里云安全服务检测文章内容
  • 能够完成自媒体文章审核的功能
  • 能够完成自媒体发布文章与审核对接

1.自媒体文章自动审核

1.1.自动审核流程介绍

做为内容类产品,内容安全非常重要,所以需要进行对自媒体用户发布的文章进行审核以后才能到app端展示给用户。

审核的流程如下:也可以查看当前讲义文件夹下:自媒体文章发布时序图.pdf

1604411723624

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

1604412003489

创建对应的实体类(之前已经创建)

文章库:leadnews_article数据库,文章作者表:ap_author

1604412277783

创建对应的实体类(之前已经创建)

文章库:leadnews_article数据库,文章信息表:ap_article

1604412445403

  • 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

1604412789215

在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

1604412993378

在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查询文章信息测试

1604415235591

修改文章

1604415490604

根据id查询用户

1604415744761

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

1604627667197

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进行测试

保存文章信息测试

1604630409283

保存文章配置信息测试

1604630578195

保存文章内容测试

1604630729043

根据名称查询作者信息

1604630846724

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

具体是实现思路如下

1604633168539

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的内容

1604662952021

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

测试动作:在自媒体前端进行发布文章

posted @ 2021-07-20 10:36  sstu  阅读(704)  评论(0编辑  收藏  举报