【Spring Cloud】新闻头条微服务项目:文章内容安全审核(需求分析及前期准备)

简介: 主要介绍了文章审核的流程以及如何在移动端文章微服务实现保存文章远程接口,为下一篇文章做准备。

一:审核流程

1.需求分析

       在上一篇文章中我们提到了创作者创作完成之后可以选择提交草稿或者提交审核,当创作者选择提交审核时候后台就需要对文章内容及封面等进行审核,以避免出现违规内容。在我项目中我使用的自动审核工具为腾讯云的文本内容安全和图片内容安全,新用户可以免费试用一个月。至于项目如何整合内容安全我这里就不再赘述了,可以参考我这篇文章:使用腾讯云进行安全检测

2.实现流程

image.gif编辑

       当创作者点击提交文章审核之后,就需要异步调用自动审核方法实现对文章文本及图片的审核,我们要先从文章中提取出文本信息(包括标题)及图片信息(包括封面),然后再分别调用不同的方法分别对文本及图片进行审核。审核结果有三种,可以分为审核通过、审核不通过、审核不确定。当审核通过之后可以直接调用文章微服务中的方法来将文章进行保存并发布,当审核不通过时候则修改文章状态为“审核不通过”,当审核结果为不确定时候这时候就需要人工进行审核,这一部分功能我们后面再实现,现在只需要修改文章状态即可。

二:移动端保存文章接口

1.分布式ID

       随着业务的增长,文章表可能要占用很大的物理存储空间,为了解决该问题,后期使用数据库分片技术。将一个数据库进行拆分,通过数据库中间件连接。如果数据库中该表选用ID自增策略,则可能产生重复的ID,此时应该使用分布式ID生成策略来生成ID。

image.gif编辑        snowflake(雪花算法)是Twitter开源的分布式ID生成算法,结果是一个long型的ID。其核心思想是:使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生 4096 个 ID),最后还有一个符号位,永远是0  

image.gif编辑

文章端相关的表都使用雪花算法生成id,包括ap_article、 ap_article_config、 ap_article_content

mybatis-plus已经集成了雪花算法,完成以下两步即可在项目中集成雪花算法

第一:在实体类中的id上加入如下配置,指定类型为AUTO

@TableId(value = "id",type = IdType.ASSIGN_ID)
private Long id;

image.gif

第二:在application.yml文件中配置数据中心id和机器id

mybatis-plus:
  mapper-locations: classpath*:mapper/*.xml
  # 设置别名包扫描路径,通过该属性可以给包中的类注册别名
  type-aliases-package: com.my.model.article.pojos
  global-config:
    datacenter-id: 1
    workerId: 1

image.gif

datacenter-id:数据中心id(取值范围:0-31)

workerId:机器id(取值范围:0-31)

2.思路分析

image.gif编辑        还记得前面我们说过为了减少数据库压力我们在移动端文章表设置了三张表,分别为文章信息表,文章配置表,文章内容表。当文章完成审核将文章信息传到文章微服务进行保存时候同样也需要先根据文章是否存在id来判断是修改文章还是保存文章,假如文章存在id则直接修改文章信息即可。假如不存在id则需要保存文章信息、文章配置、文章内容。

3.Feign接口

(1)接口说明

说明
接口路径 /api/v1/article/save
请求方式 POST
参数 ArticleDto
响应结果 ResponseResult

ArticleDto  

package com.my.model.article.dtos;
import com.my.model.article.pojos.ApArticle;
import lombok.Data;
@Data
public class ArticleDto  extends ApArticle {
    /**
     * 文章内容
     */
    private String content;
}

image.gif

(2)代码实现

在tbug-headlines-feign-api中新增接口:

①导入feign依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

image.gif

② 定义文章端接口

image.gif编辑

package com.my.apis.article;
import com.my.model.article.dtos.ArticleDto;
import com.my.model.common.dtos.ResponseResult;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.*;
@FeignClient(value = "headlines-article")
public interface IArticleClient {
    /**
     * 保存文章至App端
     * @param dto
     * @return
     */
    @PostMapping("/api/v1/article/save")
    ResponseResult saveArticle(@RequestBody ArticleDto dto) ;
}

image.gif

在tbug-headlines-article中实现方法

image.gif编辑

package com.my.article.feign;
import com.my.apis.article.IArticleClient;
import com.my.article.service.ApArticleService;
import com.my.model.article.dtos.ArticleDto;
import com.my.model.common.dtos.ResponseResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
public class ArticleClient implements IArticleClient {
    @Autowired
    private ApArticleService apArticleService;
    @Override
    @PostMapping("/api/v1/article/save")
    public ResponseResult saveArticle(ArticleDto dto) {
        return apArticleService.saveArticle(dto);
    }
}

image.gif

在ApArticleService中新增方法:

/**
* 保存app端文章
* @param dto
* @return
*/
ResponseResult saveArticle(ArticleDto dto);

image.gif

实现类:

@Autowired
private ApArticleConfigMapper apArticleConfigMapper;
@Autowired
private ApArticleContentMapper apArticleContentMapper;
@Autowired
private ArticleFreemarkerService articleFreemarkerService;
/**
 * 保存app端文章
 * @param dto
 * @return
 */
@Override
public ResponseResult saveArticle(ArticleDto dto) {
    log.info("开始保存或修改app端文章...");
    //1.参数校验
    if(dto == null) {
        return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
    }
    //2.拷贝属性
    ApArticle apArticle = new ApArticle();
    BeanUtils.copyProperties(dto,apArticle);
    //3.文章id为空,直接保存
    if(dto.getId() == null) {
        log.info("保存文章...");
        //3.1保存文章
        save(apArticle);
        //3.2保存文章配置
        ApArticleConfig apArticleConfig = new ApArticleConfig(apArticle.getId());
        apArticleConfigMapper.insert(apArticleConfig);
        //3.3保存文章内容
        ApArticleContent apArticleContent = new ApArticleContent();
        apArticleContent.setArticleId(apArticle.getId());
        apArticleContent.setContent(dto.getContent());
        apArticleContentMapper.insert(apArticleContent);
    } else {
        log.info("修改文章...");
        //4.文章存在id,修改文章内容
        //4.1更新文章信息
        updateById(apArticle);
        //4.2更新文章内容
        String content = dto.getContent();
        ApArticleContent apArticleContent = apArticleContentMapper.selectOne(Wrappers.
                <ApArticleContent>lambdaQuery().eq(ApArticleContent::getArticleId, dto.getId()));
        apArticleContent.setContent(content);
        apArticleContentMapper.updateById(apArticleContent);
        //4.3修改文章状态
        ApArticleConfig apArticleConfig = apArticleConfigMapper.selectOne(Wrappers.<ApArticleConfig>lambdaQuery().
                eq(ApArticleConfig::getArticleId, dto.getId()));
        if(apArticleConfig != null && apArticleConfig.getIsDown()) {
            apArticleConfig.setIsDown(false);
            apArticleConfigMapper.updateById(apArticleConfig);
        }
    }
    //5.异步调用生成静态页面上传值MinIO
    articleFreemarkerService.buildArticleToMinIO(apArticle,dto.getContent());
    return ResponseResult.okResult(apArticle.getId());
}

image.gif

上传静态页面到MinIO方法:

/**
 * 异步生成静态页面到Minio
 * @param apArticle
 * @param apArticleContent
 */
@Override
@Async
public void buildArticleToMinIO(ApArticle apArticle, String apArticleContent) {
    StringWriter out = new StringWriter();
    if(StringUtils.isNotBlank(apArticleContent)){
        try {
            if(apArticle.getStaticUrl() != null) {
                //删除原来Url
                fileStorageService.delete(apArticle.getStaticUrl());
                log.info("成功删除原来的html静态文件");
            }
            log.info("开始生成新的静态html文件...");
            //1.文章内容通过freemarker生成html文件
            Template template = configuration.getTemplate("article.ftl");
            Map<String, Object> params = new HashMap<>();
            params.put("content", JSONArray.parseArray(apArticleContent));
            template.process(params, out);
        } catch (Exception e) {
            e.printStackTrace();
        }
        //2.把html文件上传到minio中
        InputStream is = new ByteArrayInputStream(out.toString().getBytes());
        String path = fileStorageService.uploadHtmlFile("", apArticle.getId() + ".html", is);
        log.info("将html文件上传到minio中:{}",path);
        //3.修改ap_article表,保存static_url字段
        ApArticle article = new ApArticle();
        article.setId(apArticle.getId());
        article.setStaticUrl(path);
        apArticleMapper.updateById(article);
        //4.创建索引,发送消息
        createArticleESIndex(article,apArticleContent,path);
    }
}

网络异常,图片无法展示
|

关于异步调用,要在相应的启动类上面添加@EnableAsync注解

相关文章
|
6月前
|
数据可视化 Java BI
将 Spring 微服务与 BI 工具集成:最佳实践
本文探讨了 Spring 微服务与商业智能(BI)工具集成的潜力与实践。随着微服务架构和数据分析需求的增长,Spring Boot 和 Spring Cloud 提供了构建可扩展、弹性服务的框架,而 BI 工具则增强了数据可视化与实时分析能力。文章介绍了 Spring 微服务的核心概念、BI 工具在企业中的作用,并深入分析了两者集成带来的优势,如实时数据处理、个性化报告、数据聚合与安全保障。同时,文中还总结了集成过程中的最佳实践,包括事件驱动架构、集中配置管理、数据安全控制、模块化设计与持续优化策略,旨在帮助企业构建高效、智能的数据驱动系统。
348 1
将 Spring 微服务与 BI 工具集成:最佳实践
|
6月前
|
存储 安全 Java
管理 Spring 微服务中的分布式会话
在微服务架构中,管理分布式会话是确保用户体验一致性和系统可扩展性的关键挑战。本文探讨了在 Spring 框架下实现分布式会话管理的多种方法,包括集中式会话存储和客户端会话存储(如 Cookie),并分析了它们的优缺点。同时,文章还涵盖了与分布式会话相关的安全考虑,如数据加密、令牌验证、安全 Cookie 政策以及服务间身份验证。此外,文中强调了分布式会话在提升系统可扩展性、增强可用性、实现数据一致性及优化资源利用方面的显著优势。通过合理选择会话管理策略,结合 Spring 提供的强大工具,开发人员可以在保证系统鲁棒性的同时,提供无缝的用户体验。
143 0
|
6月前
|
消息中间件 Java 数据库
Spring 微服务中的数据一致性:最终一致性与强一致性
本文探讨了在Spring微服务中实现数据一致性的策略,重点分析了最终一致性和强一致性的定义、优缺点及适用场景。结合Spring Boot与Spring Cloud框架,介绍了如何根据业务需求选择合适的一致性模型,并提供了实现建议,帮助开发者在分布式系统中确保数据的可靠性与同步性。
436 0
|
6月前
|
监控 Java 数据库
从零学 Dropwizard:手把手搭轻量 Java 微服务,告别 Spring 臃肿
Dropwizard 整合 Jetty、Jersey 等成熟组件,开箱即用,无需复杂配置。轻量高效,启动快,资源占用少,内置监控、健康检查与安全防护,搭配 Docker 部署便捷,是构建生产级 Java 微服务的极简利器。
727 117
|
5月前
|
监控 Cloud Native Java
Spring Boot 3.x 微服务架构实战指南
🌟蒋星熠Jaxonic,技术宇宙中的星际旅人。深耕Spring Boot 3.x与微服务架构,探索云原生、性能优化与高可用系统设计。以代码为笔,在二进制星河中谱写极客诗篇。关注我,共赴技术星辰大海!(238字)
1066 2
Spring Boot 3.x 微服务架构实战指南
|
5月前
|
负载均衡 Java API
《深入理解Spring》Spring Cloud 构建分布式系统的微服务全家桶
Spring Cloud为微服务架构提供一站式解决方案,涵盖服务注册、配置管理、负载均衡、熔断限流等核心功能,助力开发者构建高可用、易扩展的分布式系统,并持续向云原生演进。
|
6月前
|
消息中间件 Java Kafka
消息队列比较:Spring 微服务中的 Kafka 与 RabbitMQ
本文深入解析了 Kafka 和 RabbitMQ 两大主流消息队列在 Spring 微服务中的应用与对比。内容涵盖消息队列的基本原理、Kafka 与 RabbitMQ 的核心概念、各自优势及典型用例,并结合 Spring 生态的集成方式,帮助开发者根据实际需求选择合适的消息中间件,提升系统解耦、可扩展性与可靠性。
447 1
消息队列比较:Spring 微服务中的 Kafka 与 RabbitMQ
|
6月前
|
监控 安全 Java
Spring Cloud 微服务治理技术详解与实践指南
本文档全面介绍 Spring Cloud 微服务治理框架的核心组件、架构设计和实践应用。作为 Spring 生态系统中构建分布式系统的标准工具箱,Spring Cloud 提供了一套完整的微服务解决方案,涵盖服务发现、配置管理、负载均衡、熔断器等关键功能。本文将深入探讨其核心组件的工作原理、集成方式以及在实际项目中的最佳实践,帮助开发者构建高可用、可扩展的分布式系统。
402 1
|
6月前
|
jenkins Java 持续交付
使用 Jenkins 和 Spring Cloud 自动化微服务部署
随着单体应用逐渐被微服务架构取代,企业对快速发布、可扩展性和高可用性的需求日益增长。Jenkins 作为领先的持续集成与部署工具,结合 Spring Cloud 提供的云原生解决方案,能够有效简化微服务的开发、测试与部署流程。本文介绍了如何通过 Jenkins 实现微服务的自动化构建与部署,并结合 Spring Cloud 的配置管理、服务发现等功能,打造高效、稳定的微服务交付流程。
799 0
使用 Jenkins 和 Spring Cloud 自动化微服务部署
|
Java 测试技术 微服务
微服务——SpringBoot使用归纳——Spring Boot中的项目属性配置——少量配置信息的情形
本课主要讲解Spring Boot项目中的属性配置方法。在实际开发中,测试与生产环境的配置往往不同,因此不应将配置信息硬编码在代码中,而应使用配置文件管理,如`application.yml`。例如,在微服务架构下,可通过配置文件设置调用其他服务的地址(如订单服务端口8002),并利用`@Value`注解在代码中读取这些配置值。这种方式使项目更灵活,便于后续修改和维护。
278 0