【黑马头条】-day04自媒体文章审核-阿里云接口-敏感词分析DFA-图像识别OCR-异步调用MQ


文章目录

  • day4学习内容
  • 自媒体文章自动审核
    • 今日内容
  • 1 自媒体文章自动审核
    • 1.1 审核流程
    • 1.2 内容安全第三方接口
    • 1.3 引入阿里云内容安全接口
      • 1.3.1 添加依赖
      • 1.3.2 导入aliyun模块
      • 1.3.3 注入Bean测试
  • 2 app端文章保存接口
    • 2.1 表结构说明
    • 2.2 分布式id
      • 2.2.1 分布式id-技术选型
      • 2.2.2 雪花算法
      • 2.2.3 配置雪花算法
    • 2.3 保存app端文章-思路分析
    • 2.4 实现接口
      • 2.4.1 实现步骤
      • 2.4.2 定义feign接口
        • 2.4.2.1 导入feign远程调用依赖
        • 2.4.2.2 定义文章端远程接口
        • 2.4.2.3 导入ArticleDto
      • 2.4.3 实现feign接口
      • 2.4.4 创建mapper
      • 2.4.5 为AparticleConfig设置默认参数
      • 2.4.6 在ApArticleService的实现类ApArticleServiceImpl中实现方法
      • 2.4.7 启动ArticleApplication
  • 3 自媒体文章审核实现
    • 3.1 创建审核接口
    • 3.2 实现审核接口
    • 3.3 启动类扫描feign
    • 3.4 测试
  • 4 自媒体调用文章微服务feign远程调用服务降级
    • 4.1 feign远程调用服务降级处理的逻辑
    • 4.2 编写降级逻辑
    • 4.3 指定IArticleClient接口指向Feign降级逻辑
    • 4.4 加载feign降级逻辑
    • 4.5 配置降级策略
    • 4.6 测试
  • 5 文章审核异步调用
    • 5.1 在自动审核的方法加上@Async注解
    • 5.2 在文章发布后调用自动审核方法
    • 5.3 在启动类中添加注解开启异步调用
    • 5.4 综合测试
    • 5.5 使用rabbit MQ来完成异步调用
      • 5.5.1 引入依赖
      • 5.5.2 为微服务配置MQ
      • 5.5.3 改造方法,创建监听队列
      • 5.5.4 序列化MQ消息
      • 5.5.5 加上mq后的综合测试
  • 6 自管理敏感词过滤
    • 6.1 DFA实现原理
    • 6.2 DFA检索过程
    • 6.3 实现步骤
      • 6.3.1 创建敏感词表
      • 6.3.2 将wm_sensitive对应的实体类和mapper导入
      • 6.3.3 在阿里云接口前自行进行审查
      • 6.3.4 测试
  • 7 图片文字敏感词过滤
    • 7.1 文字图片识别
    • 7.2 Tesseract-OCR
    • 7.3 Tess4j案例
      • 7.3.1 导入依赖
      • 7.3.2 将训练好的分类器放入资源中
      • 7.3.3 demo
      • 7.3.4 结果
    • 7.4 图片文字敏感词过滤实现
      • 7.4.1 创建工具类
      • 7.4.2 工具类被其他微服务使用
      • 7.4.3 在微服务中配置
      • 7.4.4 添加实现
  • 8 静态文件生成
    • 8.1 实现思路
      • 8.1.1 生成minio接口和实现,并且异步调用
      • 8.1.2 修改saveArticle逻辑
      • 8.1.3 开启异步调用
      • 8.1.4 测试


day4学习内容

自媒体文章自动审核

在这里插入图片描述

今日内容

在这里插入图片描述

1 自媒体文章自动审核

1.1 审核流程

在这里插入图片描述

在这里插入图片描述

1.2 内容安全第三方接口

在这里插入图片描述

在这里插入图片描述

1.3 引入阿里云内容安全接口

在这里插入图片描述

1.3.1 添加依赖

在heima-leadnews-common包下引入依赖

<dependency>
    <groupId>com.aliyun</groupId>
    <artifactId>aliyun-java-sdk-core</artifactId>
    <version>4.1.1</version>
</dependency>
<dependency>
    <groupId>com.aliyun</groupId>
    <artifactId>aliyun-java-sdk-green</artifactId>
    <version>3.6.6</version>
</dependency>
<dependency>
    <groupId>com.alibaba.fastjson2</groupId>
    <artifactId>fastjson2</artifactId>
    <version>2.0.9</version>
</dependency>
<dependency>
    <groupId>com.aliyun.oss</groupId>
    <artifactId>aliyun-sdk-oss</artifactId>
    <version>2.8.3</version>
</dependency>

1.3.2 导入aliyun模块

放入heima-leadnews-common模块下的com.heima.common

哪个微服务使用,就在哪个微服务的nacos中配置

在heima-leadnews-wemedia中的nacos配置中心添加以下配置:

aliyun:
 accessKeyId: LTAI5tCWHCcfvqQzu8k2oKmX
 secret: auoKUFsghimbfVQHpy7gtRyBkoR4vc
#aliyun.scenes=porn,terrorism,ad,qrcode,live,logo
 scenes: terrorism

1.3.3 注入Bean测试

在resource中META-INF的spring-factories中自动配置

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.heima.common.exception.ExceptionCatch,\
  com.heima.common.aliyun.GreenTextScan,\
    com.heima.common.aliyun.GreenImageScan

在测试类中进行测试

@SpringBootTest(classes = WemediaApplication.class)
@RunWith(SpringRunner.class)
public class AliyunTest {

    @Autowired
    private GreenTextScan greenTextScan;

    @Autowired
    private GreenImageScan greenImageScan;

    @Autowired
    private FileStorageService fileStorageService;

    @Test
    public void testScanText() throws Exception {
        Map map = greenTextScan.greeTextScan("我是一个好人,冰毒");
        System.out.println(map);
    }

    @Test
    public void testScanImage() throws Exception {
        byte[] bytes = fileStorageService.downLoadFile("http://192.168.200.130:9000/leadnews/2021/04/26/ef3cbe458db249f7bd6fb4339e593e55.jpg");
        Map map = greenImageScan.imageScan(Arrays.asList(bytes));
        System.out.println(map);
    }
}

2 app端文章保存接口

2.1 表结构说明

在这里插入图片描述

2.2 分布式id

在这里插入图片描述

2.2.1 分布式id-技术选型

在这里插入图片描述

2.2.2 雪花算法

在这里插入图片描述

2.2.3 配置雪花算法

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

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

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

在文章的微服务的nacos配置中leadnews-article中添加

spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/leadnews_article?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false
    username: root
    password: 123sjbsjb

# 设置Mapper接口所对应的XML文件位置,如果你在Mapper接口中有自定义方法,需要进行该配置
mybatis-plus:
  mapper-locations: classpath*:mapper/*.xml
  # 设置别名包扫描路径,通过该属性可以给包中的类注册别名
  type-aliases-package: com.heima.model.article.pojos
  #雪花算法
  global-config:
    datacenter-id: 1
    workerId: 1
minio:
  accessKey: minio
  secretKey: minio123
  bucket: leadnews
  endpoint: http://192.168.204.129:9000
  readPath: http://192.168.204.129:9000

在这里插入图片描述

2.3 保存app端文章-思路分析

在这里插入图片描述

2.4 实现接口

在这里插入图片描述

2.4.1 实现步骤

在这里插入图片描述

2.4.2 定义feign接口

2.4.2.1 导入feign远程调用依赖

在heima-leadnews-feign-api的pom.xml中导入依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2.4.2.2 定义文章端远程接口

heima-leadnews-feign-api定义com.heima.apis.article.IArticleClient接口

@FeignClient(value = "leadnews-article")

@FeignClient指定文章远程调用接口名称

@FeignClient(value = "leadnews-article")
public interface IArticleClient {

    @PostMapping("/api/v1/article/save")
    public ResponseResult saveArticle(@RequestBody ArticleDto dto) ;
}
2.4.2.3 导入ArticleDto

在heima-leadnews-model模块下com.heima.model.article.dto中导入ArticleDto类

@Data
public class ArticleDto  extends ApArticle {
    /**
     * 文章内容
     */
    private String content;
}

2.4.3 实现feign接口

在heima-leadnews-service模块下的heima-leadnews-article模块下创建com.heima.article.feign.ArticleClient类

@RestController
public class ArticleClient implements IArticleClient {
    @Autowired
    private ApArticleService apArticleService;

    @PostMapping("/api/v1/article/save")
    @Override
    public ResponseResult saveArticle(@RequestBody ArticleDto dto) {
        return apArticleService.saveArticle(dto);
    }
}

2.4.4 创建mapper

在heima-leadnews-service模块下的heima-leadnews-article模块下创建com.heima.article.mapper.ApArticleConfigMapper接口

@Mapper
public interface ApArticleConfigMapper extends BaseMapper<ApArticleConfig> {
}

2.4.5 为AparticleConfig设置默认参数

添加@NoArgsConstructor

public ApArticleConfig(Long articleId) {
    this.articleId = articleId;
    this.isDelete = false;
    this.isDown = false;
    this.isForward = true;
    this.isComment = true;
}

添加有参构造

2.4.6 在ApArticleService的实现类ApArticleServiceImpl中实现方法

ApArticleService接口

public interface ApArticleService extends IService<ApArticle>{
    /**
     * 加载文章列表
     * @param dto
     * @param type 1 加载更多 2 加载最新
     * @return
     */
    public ResponseResult load(ArticleHomeDto dto, Short type);

    /**
     * 保存文章
     * @param dto
     * @return
     */
    public ResponseResult saveArticle(ArticleHomeDto dto);
}

实现类,实现saveArticle方法

@Autowired
private ApArticleConfigMapper apArticleConfigMapper;
@Autowired
private ApArticleContentMapper apArticleContentMapper;
/**
 * 保存文章
 * @param dto
 * @return
 */
@Override
public ResponseResult saveArticle(ArticleDto dto) {
    //1.参数检查
    if(dto == null){
        return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
    }
    ApArticle apArticle = new ApArticle();
    //org.springframework.beans
    BeanUtils.copyProperties(dto, apArticle);
    //2.判断是否存在id
    if(dto.getId() == null) {
        //2.1 不存在id ,新增 文章、内容、配置
        save(apArticle);
        //2.1.2 保存文章配置
        ApArticleConfig apArticleConfig = new ApArticleConfig(apArticle.getId());
        apArticleConfigMapper.insert(apArticleConfig);
        //2.1.3 保存文章内容
        ApArticleContent apArticleContent = new ApArticleContent();
        apArticleContent.setArticleId(apArticle.getId());
        apArticleContent.setContent(dto.getContent());
        apArticleContentMapper.insert(apArticleContent);
    }
    else {
        //2.2 存在id,更新 文章、内容
        //2.2.1 更新文章
        updateById(apArticle);
        //2.2.2 更新文章内容
        ApArticleContent apArticleContent = apArticleContentMapper.selectOne(Wrappers
                .<ApArticleContent>lambdaQuery()
                .eq(ApArticleContent::getArticleId, dto.getId()));
        apArticleContent.setContent(dto.getContent());
        apArticleContentMapper.updateById(apArticleContent);
    }
    //3.返回结果 文章的id
    return ResponseResult.okResult(apArticle.getId());
}

2.4.7 启动ArticleApplication

在这里插入图片描述

在这里插入图片描述

刚刚是新增,如果是修改。

就会在json中传入id

在这里插入图片描述

在这里插入图片描述

成功修改

3 自媒体文章审核实现

3.1 创建审核接口

在heima-leadnews-service中heima-leadnews-wemedia中的service新增WmNewAutoScanService接口

public interface WmNewAutoScanService {
    /**
     * 自动审核媒体文章
     */
    public void  autoScanMediaNews(Integer id);
}

3.2 实现审核接口

@Service
@Slf4j
@Transactional
public class WmNewAutoScanServiceImpl implements WmNewAutoScanService {
    @Autowired
    private WmNewsMapper wmNewsMapper;
    @Qualifier("com.heima.apis.article.IArticleClient")
    @Autowired
    private IArticleClient iArticleClient;
    @Autowired
    private WmChannelMapper wmChannelMapper;
    @Autowired
    private WmUserMapper wmUserMapper;

    @Override
    public void autoScanMediaNews(Integer id) {
        //1.查询自媒体文章
        WmNews wmNews = wmNewsMapper.selectById(id);
        if (wmNews == null) {
            throw new RuntimeException("自媒体文章不存在");
        }
        if(wmNews.getStatus().equals(WmNews.Status.SUBMIT.getCode())){
            Map<String,List<String>> scanMaterialsList = extractImageAndContent(wmNews);
            //2.调用阿里云接口审核文本内容
            List<String> contentTexts =scanMaterialsList.get("contentTexts");
            boolean isTextScan =true;
            if(!isTextScan)return;
            //3.调用阿里云接口审核图片内容
            List<String> imagesUrls =scanMaterialsList.get("imagesUrls");
            boolean isImageScan =true;
            if(!isImageScan)return;
            if(isTextScan && isImageScan) {
                //审核通过
                wmNews.setStatus((short) 9);
                wmNews.setReason("审核通过");
            }
        }
        //4.审核成功保存app端的相关文章数据
        ArticleDto dto=new ArticleDto();
        BeanUtils.copyProperties(wmNews,dto);
        //布局
        dto.setLayout(wmNews.getType());
        //频道
        dto.setChannelId(wmNews.getChannelId());
        //频道名称
        WmChannel wmChannel = wmChannelMapper.selectById(wmNews.getChannelId());
        if(wmChannel!=null){
            dto.setChannelName(wmChannel.getName());
        }
        //作者
        dto.setAuthorId(Long.valueOf(wmNews.getUserId()));
        //作者名称
        WmUser wmUser= wmUserMapper.selectById(wmNews.getUserId());
        if(wmUser!=null){
            dto.setAuthorName(wmUser.getName());
        }
        //设置文章id
        if(wmNews.getArticleId()!=null){
            dto.setId(wmNews.getArticleId());
        }
        dto.setCreatedTime(new Date());
        ResponseResult responseResult = iArticleClient.saveArticle(dto);
        if(responseResult.getCode().equals(200)){
            //保存成功
            wmNews.setArticleId((Long)responseResult.getData());
            wmNewsMapper.updateById(wmNews);
        }
        else{
            //保存失败
            throw new RuntimeException("保存app端文章失败");
        }
    }

    private Map<String,List<String>> extractImageAndContent(WmNews wmNews) {
        //提取文章内容
        String content = wmNews.getContent();
        List<String> imagesUrls =new ArrayList<>();
        List<String> contentTexts =new ArrayList<>();
        Map<String,List<String>> scanMaterialsList =new HashMap<>();
        List<Map> maps = JSON.parseArray(content, Map.class);
        //提取文章图片
        for (Map map : maps) {
            if(map.get("type").equals("image")){
                String imgUrl = (String) map.get("value");
                imagesUrls.add(imgUrl);
            }
            if(map.get("type").equals("text")){
                String text = (String) map.get("value");
                contentTexts.add(text);
            }
        }
        scanMaterialsList.put("imagesUrls",imagesUrls);
        scanMaterialsList.put("contentTexts",contentTexts);
        return scanMaterialsList;
    }
}

3.3 启动类扫描feign

调用Feign远程接口时,要在启动类中加入@EnableFeignClients(basePackages = “com.heima.apis”)来对feign的api进行扫描,同时也要引入feign-api模块的依赖

<dependency>
    <groupId>com.heima</groupId>
    <artifactId>heima-leadnews-feign-api</artifactId>
</dependency>
@SpringBootApplication
@EnableDiscoveryClient
@MapperScan("com.heima.wemedia.mapper")
@EnableFeignClients(basePackages = "com.heima.apis")
public class WemediaApplication {

    public static void main(String[] args) {
        SpringApplication.run(WemediaApplication.class,args);
    }

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

3.4 测试

转到WmNewAutoScanService接口中,ctrl+shift+T创建测试类

在这里插入图片描述

@SpringBootTest(classes = WemediaApplication.class)
@RunWith(SpringRunner.class)
class WmNewAutoScanServiceTest {
    @Autowired
    private WmNewAutoScanService wmNewAutoScanService;
    @Test
    void autoScanMediaNews() {
        wmNewAutoScanService.autoScanMediaNews(6236);
    }
}

4 自媒体调用文章微服务feign远程调用服务降级

在这里插入图片描述

4.1 feign远程调用服务降级处理的逻辑

在这里插入图片描述

4.2 编写降级逻辑

在heima-leadnews-feign-api模块下编写降级逻辑com.heima.apis.article.fallback.IArticleClientFallback类,实现IArticleClient接口

@Component
public class IArticleClientFallback implements IArticleClient {
    @Override
    public ResponseResult saveArticle(ArticleDto dto) {
        return ResponseResult.errorResult(AppHttpCodeEnum.SERVER_ERROR,"获取数据失败");
    }
}

4.3 指定IArticleClient接口指向Feign降级逻辑

@FeignClient(value = "leadnews-article",fallback = IArticleClientFallback.class)

@FeignClient(value = "leadnews-article",fallback = IArticleClientFallback.class)
public interface IArticleClient {

    @PostMapping("/api/v1/article/save")
    public ResponseResult saveArticle(@RequestBody ArticleDto dto) ;
}

4.4 加载feign降级逻辑

因为IArticleClientFallback是在com.heima.apis.article.fallback包下,并不能被spring通过@Component直接加载

因此需要在使用的微服务中加载feign

如使用的微服务是heima-leadnews-wemedia,所以要在com.heima.wemedia.config下创建InitConfig类加载feign降级策略

@Configuration
@ComponentScan("com.heima.apis.article.fallback")
public class InitConfig {
}

4.5 配置降级策略

要么在bootstrap中开启,要么在nacos中实现热更新

这里采用nacos热更新

feign:
  # 开启feign对hystrix熔断降级的支持
  hystrix:
    enabled: true
  # 修改调用超时时间
  client:
    config:
      default:
        connectTimeout: 2000
        readTimeout: 2000

在这里插入图片描述

4.6 测试

当前设置超时2s进行降级,测试一下

在com.heima.article.service.impl.ApArticleServiceImpl类中的saveArticle方法添加睡眠3秒进行测试

@Override
public ResponseResult saveArticle(ArticleDto dto) {
    try {
        Thread.sleep(3000);
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }
    //1.参数检查
    if(dto == null){

这次审核6239

@SpringBootTest(classes = WemediaApplication.class)
@RunWith(SpringRunner.class)
class WmNewAutoScanServiceTest {
    @Autowired
    private WmNewAutoScanService wmNewAutoScanService;
    @Test
    void autoScanMediaNews() {
        wmNewAutoScanService.autoScanMediaNews(6239);
    }
}

在这里插入图片描述

在这里插入图片描述

5 文章审核异步调用

在这里插入图片描述

在这里插入图片描述

5.1 在自动审核的方法加上@Async注解

Springboot集成异步线程调用

@Override
@Async//表明这是一个异步方法
public void autoScanMediaNews(Integer id) {
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }

5.2 在文章发布后调用自动审核方法

//5.审核文章
wmNewAutoScanService.autoScanMediaNews(wmNews.getId());
@Autowired
private WmNewAutoScanService wmNewAutoScanService;
@Override
public ResponseResult submitNews(WmNewsDto wmNewsDto) {
    // 0.参数检查
    if(wmNewsDto == null||wmNewsDto.getContent()==null){
        return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
    }
    //1. 保存或修改文章
    WmNews wmNews = new WmNews();
    BeanUtils.copyProperties(wmNewsDto,wmNews);
    //1.1 封面
    if(wmNewsDto.getImages()!=null&& wmNewsDto.getImages().size()>0){
        String imageStr = StringUtils.join(wmNewsDto.getImages(), ",");
        wmNews.setImages(imageStr);
    }
    //1.2 如果封面为自动-1,则需要手动设置封面规则
    if(wmNewsDto.getType().equals(WemediaConstants.WM_NEWS_TYPE_AUTO)){
        wmNews.setType(null);
    }
    saveOrUpdateWmNews(wmNews);
    //2.判断是否为草稿,如果为草稿结束当前方法
    if(wmNews.getStatus().equals(WmNews.Status.NORMAL.getCode())){
        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }
    //3.不是草稿,保存文章内容与图片素材的关系
    //3.1 获取文章内容的图片素材
    List<String> imageList=extractUrlInfo(wmNewsDto.getContent());
    saveRelativeInfoForContent(imageList,wmNews.getId());

    //4.不是草稿,保存文章封面图片与图片素材的关系
    saveRelativeInfoForCover(wmNewsDto,wmNews,imageList);

    //5.审核文章
    wmNewAutoScanService.autoScanMediaNews(wmNews.getId());

    return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
}

5.3 在启动类中添加注解开启异步调用

在自媒体引导类中使用@EnableAsync注解开启异步调用

@SpringBootApplication
@EnableDiscoveryClient
@MapperScan("com.heima.wemedia.mapper")
@EnableFeignClients(basePackages = "com.heima.apis")
@EnableAsync//开启异步
public class WemediaApplication {

    public static void main(String[] args) {
        SpringApplication.run(WemediaApplication.class,args);
    }

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

5.4 综合测试

在这里插入图片描述

5.5 使用rabbit MQ来完成异步调用

我的异步调用只要在启动类中加入@EnableAsync就报错,迫不得已采用rabbitMQ

5.5.1 引入依赖

在heima-leadnews-service中引入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
</dependency>

5.5.2 为微服务配置MQ

在heima-leadnews-article和wemedia的配置文件中添加配置

spring:
  rabbitmq:
    host: 192.168.204.129
    port: 5672
    virtual-host: /
    username: itcast
    password: 123321

5.5.3 改造方法,创建监听队列

修改heima-leadnews-wemedia下的com.heima.wemedia.service.impl.WmNewAutoScanServiceImpl类中的autoScanMediaNews方法

@Autowired
private RabbitTemplate rabbitTemplate;
@Override
public void autoScanMediaNews(Integer id) {
    //1.查询自媒体文章
    WmNews wmNews = wmNewsMapper.selectById(id);
    if (wmNews == null) {
        throw new RuntimeException("自媒体文章不存在");
    }
    if(wmNews.getStatus().equals(WmNews.Status.SUBMIT.getCode())){
        Map<String,List<String>> scanMaterialsList = extractImageAndContent(wmNews);
        //2.调用阿里云接口审核文本内容
        List<String> contentTexts =scanMaterialsList.get("contentTexts");
        boolean isTextScan =true;
        if(!isTextScan)return;
        //3.调用阿里云接口审核图片内容
        List<String> imagesUrls =scanMaterialsList.get("imagesUrls");
        boolean isImageScan =true;
        if(!isImageScan)return;
        if(isTextScan && isImageScan) {
            //审核通过
            wmNews.setStatus((short) 9);
            wmNews.setReason("审核通过");
        }
    }
    //4.审核成功保存app端的相关文章数据
    ArticleDto dto=new ArticleDto();
    BeanUtils.copyProperties(wmNews,dto);
    //布局
    dto.setLayout(wmNews.getType());
    //频道
    dto.setChannelId(wmNews.getChannelId());
    //频道名称
    WmChannel wmChannel = wmChannelMapper.selectById(wmNews.getChannelId());
    if(wmChannel!=null){
        dto.setChannelName(wmChannel.getName());
    }
    //作者
    dto.setAuthorId(Long.valueOf(wmNews.getUserId()));
    //作者名称
    WmUser wmUser= wmUserMapper.selectById(wmNews.getUserId());
    if(wmUser!=null){
        dto.setAuthorName(wmUser.getName());
    }
    //设置文章id
    if(wmNews.getArticleId()!=null){
        dto.setId(wmNews.getArticleId());
    }
    dto.setCreatedTime(new Date());

    //2.rabbitmq异步处理
    Map<String,Object> map=new HashMap<>();
    map.put("dto",dto);
    map.put("wmNewsId",id);
    rabbitTemplate.convertAndSend("article.queue", map);
    /*ResponseResult responseResult = iArticleClient.saveArticle(dto);
    if(responseResult.getCode().equals(200)){
        //保存成功
        wmNews.setArticleId((Long)responseResult.getData());
        wmNewsMapper.updateById(wmNews);
    }
    else{
        //保存失败
        log.error("保存app端文章失败,responseResult: {}", responseResult);
        throw new RuntimeException("保存app端文章失败");
    }*/
}
rabbitTemplate.convertAndSend("article.queue", map);

发送到article.queue队列

在heima-leadnews-article模块下创建com.heima.article.mq.ArticleMessageConsumer消费者监听类监听article.queue

@Slf4j
@Component
public class ArticleMessageConsumer {
    @Autowired
    private IArticleClient iArticleClient;
    @Autowired
    private RabbitTemplate rabbitTemplate;

    @RabbitListener(bindings =@QueueBinding(
            value=@Queue(name="article.queue"),
            exchange=@Exchange(name="article.direct",type= ExchangeTypes.FANOUT)))
    public void processMessage(Map<String,Object> map) {
        ObjectMapper objectMapper = new ObjectMapper();
        Object dto = map.get("dto");
        Integer id= (Integer) map.get("wmNewsId");
        LinkedHashMap<String, Object> linkedHashMap = (LinkedHashMap<String, Object>) dto;
        ArticleDto articleDto = objectMapper.convertValue(linkedHashMap, ArticleDto.class);
        // 异步处理文章数据
        ResponseResult responseResult = iArticleClient.saveArticle(articleDto);
        if(responseResult.getCode().equals(200)){
            WmNews wmNews = new WmNews();
            BeanUtils.copyProperties(dto, wmNews);
            wmNews.setArticleId((Long)responseResult.getData());
            Map<String,Object> params = new HashMap<>();
            params.put("id", id);
            params.put("wmNews", wmNews);
            params.put("articleId",(Long)responseResult.getData());
            rabbitTemplate.convertAndSend("wmNews.queue", params);
            log.info("发送params成功,param: {}", params);
        }
        else{
            //保存失败
            log.error("保存app端文章失败,responseResult: {}", responseResult);
            throw new RuntimeException("保存app端文章失败");
        }
    }
}

ResponseResult responseResult = iArticleClient.saveArticle(articleDto);回填的id发到wmNews.queue

在heima-leadnews-wemedia模块下创建com.heima.wemedia.mq.ReceiveWmNewsId消费者监听类监听wmNews.queue

@Component
@Slf4j
public class ReceiveWmNewsId {
    @Autowired
    private WmNewsMapper wmNewsMapper;

    @RabbitListener(bindings =@QueueBinding(
            value=@Queue(name="wmNews.queue"),
            exchange=@Exchange(name="wmNews.direct",type= ExchangeTypes.FANOUT)))
    public void processMessage(Map<String,Object> map) {
        ObjectMapper objectMapper = new ObjectMapper();
        Integer id= (Integer)map.get("id");
        Object wmNews= map.get("wmNews");
        Long articleId= (Long)map.get("articleId");
        LinkedHashMap<String, Object> linkedHashMap = (LinkedHashMap<String, Object>) wmNews;
        WmNews articleDto = objectMapper.convertValue(linkedHashMap, WmNews.class);
        WmNews oldwmNews = wmNewsMapper.selectById(id);
        BeanUtils.copyProperties(wmNews,oldwmNews);
        oldwmNews.setStatus((short) 9);
        oldwmNews.setReason("审核通过");
        oldwmNews.setArticleId(articleId);
        int i = wmNewsMapper.updateById(oldwmNews);
        if(i == 0){
            log.error("更新自媒体文章失败,wmNews: {}", oldwmNews);
            throw new RuntimeException("更新自媒体文章失败");
        }
    }
}

5.5.4 序列化MQ消息

在heima-leadnews-article和wemedia的启动类中添加序列化器

@SpringBootApplication
@EnableDiscoveryClient
@MapperScan("com.heima.wemedia.mapper")
@EnableFeignClients(basePackages = "com.heima.apis")
public class WemediaApplication {

    public static void main(String[] args) {
        SpringApplication.run(WemediaApplication.class,args);
    }

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
    @Bean
    public MessageConverter messageConverter(){
        return new Jackson2JsonMessageConverter();
    }
    @Bean
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory, MessageConverter messageConverter) {
        RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
        rabbitTemplate.setMessageConverter(messageConverter);
        return rabbitTemplate;
    }
}
@SpringBootApplication
@EnableDiscoveryClient
@MapperScan("com.heima.article.mapper")
public class ArticleApplication {

    public static void main(String[] args) {
        SpringApplication.run(ArticleApplication.class,args);
    }

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
    @Bean
    public MessageConverter messageConverter(){
        return new Jackson2JsonMessageConverter();
    }
    @Bean
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory, MessageConverter messageConverter) {
        RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
        rabbitTemplate.setMessageConverter(messageConverter);
        return rabbitTemplate;
    }
}

5.5.5 加上mq后的综合测试

在这里插入图片描述

测试通过在MQ上也检测到消息

在这里插入图片描述

6 自管理敏感词过滤

在这里插入图片描述

6.1 DFA实现原理

在这里插入图片描述

6.2 DFA检索过程

在这里插入图片描述

6.3 实现步骤

在这里插入图片描述

6.3.1 创建敏感词表

在leadnews-wemedia数据库中到入wm_sensitive.sql

6.3.2 将wm_sensitive对应的实体类和mapper导入

@Data
@TableName("wm_sensitive")
public class WmSensitive implements Serializable {
    private static final long serialVersionUID = 1L;

    /**
     * 主键
     */
    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;

    /**
     * 敏感词
     */
    @TableField("sensitives")
    private String sensitives;

    /**
     * 创建时间
     */
    @TableField("created_time")
    private Date createdTime;
}
@Mapper
public interface WmSensitiveMapper extends BaseMapper<WmSensitive> {
}

6.3.3 在阿里云接口前自行进行审查

boolean isSensitive= handleSensitiveWords(contentTexts,wmNews);

if(wmNews.getStatus().equals(WmNews.Status.SUBMIT.getCode())){
    Map<String,List<String>> scanMaterialsList = extractImageAndContent(wmNews);
    //2.调用阿里云接口审核文本内容
    List<String> contentTexts =scanMaterialsList.get("contentTexts");
    //2.1 敏感词过滤
    boolean isSensitive= handleSensitiveWords(contentTexts,wmNews);
    boolean isTextScan =true;
    if(!isTextScan)return;

    //3.调用阿里云接口审核图片内容
    List<String> imagesUrls =scanMaterialsList.get("imagesUrls");
    boolean isImageScan =true;
@Autowired
private WmSensitiveMapper wmSensitiveMapper;
private boolean handleSensitiveWords(List<String> contentTexts, WmNews wmNews) {
    boolean isSensitive = true;
    //1.获取所有敏感词
    List<WmSensitive> wmSensitiveList = wmSensitiveMapper.selectList(Wrappers.<WmSensitive>lambdaQuery().select(WmSensitive::getSensitives));
    List<String> collect = wmSensitiveList.stream().map(WmSensitive::getSensitives).collect(Collectors.toList());
    //2.初始化敏感词库
    SensitiveWordUtil.initMap(collect);
    //3.遍历文章内容查看是否包含敏感词
    for(String contentText:contentTexts){
        Map<String, Integer> map = SensitiveWordUtil.matchWords(contentText);
        if(map.size()>0){
            //4.如果包含敏感词,修改文章状态
            wmNews.setStatus((short) 2);
            wmNews.setReason("文章内容包含敏感词");
            wmNewsMapper.updateById(wmNews);
            isSensitive = false;
            break;
        }
    }
    return isSensitive;
}

6.3.4 测试

在这里插入图片描述

7 图片文字敏感词过滤

7.1 文字图片识别

在这里插入图片描述

7.2 Tesseract-OCR

在这里插入图片描述

7.3 Tess4j案例

在这里插入图片描述

7.3.1 导入依赖

在heima-leadnews-test模块下的tess4j-demo的模块下导入依赖

<dependency>
    <groupId>net.sourceforge.tess4j</groupId>
    <artifactId>tess4j</artifactId>
    <version>4.1.1</version>
</dependency>

7.3.2 将训练好的分类器放入资源中

在这里插入图片描述

7.3.3 demo

在tess4j-demo的Applcation中

public class Application {
    /**
     * 识别图片中的文字
     * @param args
     */
    public static void main(String[] args) {
        // 1.创建Tesseract对象
        Tesseract tesseract = new Tesseract();
        // 2.设置训练库的位置
        tesseract.setDatapath("D:\\Code\\JavaCode\\HeimaToutiao\\heima-leadnews\\heima-leadnews-test\\tess4j-demo\\src\\main\\resources\\tessdata");
        // 3.设置识别语言
        tesseract.setLanguage("chi_sim");
        // 4.设置识别图片
        File file = new File("D:\\Code\\JavaCode\\HeimaToutiao\\heima-leadnews\\heima-leadnews-test\\tess4j-demo\\src\\main\\resources\\testdata\\testImage.png");
        // 5.识别图片
        try {
            String result = tesseract.doOCR(file);
            System.out.println(result.replace("\\n|\\r", ""));
        } catch (TesseractException e) {
            e.printStackTrace();
        }
    }
}

7.3.4 结果

在这里插入图片描述

7.4 图片文字敏感词过滤实现

在这里插入图片描述

7.4.1 创建工具类

在heima-leadnews-common中创建com.heima.common.tess4j.Tess4jClient工具类,封装tess4j

@Getter
@Setter
@Component
@ConfigurationProperties(prefix = "tess4j")
public class Tess4jClient {

    private String dataPath;
    private String language;

    public String doOCR(BufferedImage image) throws TesseractException {
        //创建Tesseract对象
        ITesseract tesseract = new Tesseract();
        //设置字体库路径
        tesseract.setDatapath(dataPath);
        //中文识别
        tesseract.setLanguage(language);
        //执行ocr识别
        String result = tesseract.doOCR(image);
        //替换回车和tal键  使结果为一行
        result = result.replaceAll("\\r|\\n", "-").replaceAll(" ", "");
        return result;
    }

}

7.4.2 工具类被其他微服务使用

想让工具类被其他微服务使用就要拷贝全路径,在当前的resource中的META-INF的spring.factories中添加配置

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.heima.common.exception.ExceptionCatch,\
  com.heima.common.aliyun.GreenTextScan,\
    com.heima.common.aliyun.GreenImageScan,\
    com.heima.common.tess4j.Tess4jClient

7.4.3 在微服务中配置

在heima-leadnews-wemedia中的resource的boostrap.yml中进行配置

tess4j:
  data-path: D:\Code\JavaCode\HeimaToutiao\heima-leadnews\heima-leadnews-test\tess4j-demo\src\main\resources\tessdata
  language: chi_sim

7.4.4 添加实现

在WmNewsAutoScanServiceImpl中的handleImageScan方法上添加如下代码

try {
    for (String image : images) {
        byte[] bytes = fileStorageService.downLoadFile(image);

        //图片识别文字审核---begin-----

        //从byte[]转换为butteredImage
        ByteArrayInputStream in = new ByteArrayInputStream(bytes);
        BufferedImage imageFile = ImageIO.read(in);
        //识别图片的文字
        String result = tess4jClient.doOCR(imageFile);

        //审核是否包含自管理的敏感词
        boolean isSensitive = handleSensitiveScan(result, wmNews);
        if(!isSensitive){
            return isSensitive;
        }
        //图片识别文字审核---end-----
        imageList.add(bytes);

    } 
}catch (Exception e){
    e.printStackTrace();
}

8 静态文件生成

在这里插入图片描述

8.1 实现思路

我们在保存/修改文章时就应该同时异步的的生成静态文件,生成静态文件上传到minio中

8.1.1 生成minio接口和实现,并且异步调用

在com.heima.article.service.ArticleFreemarkerService接口

生成静态文件,上传到minio中

public interface ArticleFreemarkerService {
    /**
     * 生成静态化页面
     * @param apArticle
     * @param content
     */
    public void buildArticleToMinio(ApArticle apArticle,String content);
}
@Service
@Slf4j
@Transactional
public class ArticleFreemarkerServiceImpl implements ArticleFreemarkerService {
    @Autowired
    private ApArticleContentMapper apArticleContentMapper;
    @Autowired
    private Configuration configuration;
    @Autowired
    private FileStorageService fileStorageService;
    @Autowired
    private ApArticleService apArticleService;
    /**
     * 生成静态化页面
     * @param apArticle
     * @param content
     */
    @Async
    @Override
    public void buildArticleToMinio(ApArticle apArticle, String content) {
        if(StringUtils.isNotBlank(content)){
            //1.文章内容通过freemarker生成静态html页面
            Template template = null;
            //2 输出流
            StringWriter writer = new StringWriter();
            try {
                template = configuration.getTemplate("article.ftl");
                //2.1 创建模型
                Map<String,Object> contentDataModel=new HashMap();
                //content是固定的,因为article.ftl中有<#if content??>${content}</#if>
                //因为apArticleContent.getContent()获取的是字符串,所以需要转换成对象
                contentDataModel.put("content", JSONArray.parseArray(content));
                //2.2 合成方法
                template.process(contentDataModel,writer);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
            //3.把静态页面上传到minio
            //3.1 文件流
            InputStream inputStream = new ByteArrayInputStream(writer.toString().getBytes());
            String path = fileStorageService.uploadHtmlFile("",apArticle.getId()+".html",inputStream);
            //4.把静态页面的路径保存到数据库
            apArticleService.update(Wrappers
                    .<ApArticle>lambdaUpdate()
                    .eq(ApArticle::getId,apArticle.getId())
                    .set(ApArticle::getStaticUrl,path));
        }

    }
}

8.1.2 修改saveArticle逻辑

修改com.heima.article.service.impl.ApArticleServiceImpl的saveArticle方法,添加buildArticleToMinio

articleFreemarkerService.buildArticleToMinio(apArticle, dto.getContent());
    @Autowired
    private ApArticleConfigMapper apArticleConfigMapper;
    @Autowired
    private ApArticleContentMapper apArticleContentMapper;
    @Autowired
    private ArticleFreemarkerService articleFreemarkerService;
    /**
     * 保存文章
     * @param dto
     * @return
     */
    @Override
    public ResponseResult saveArticle(ArticleDto dto) {
        //1.参数检查
        if(dto == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        ApArticle apArticle = new ApArticle();
        //org.springframework.beans
        BeanUtils.copyProperties(dto, apArticle);
        //2.判断是否存在id
        if(dto.getId() == null) {
            //2.1 不存在id ,新增 文章、内容、配置
            save(apArticle);
            //2.1.2 保存文章配置
            ApArticleConfig apArticleConfig = new ApArticleConfig(apArticle.getId());
            apArticleConfigMapper.insert(apArticleConfig);
            //2.1.3 保存文章内容
            ApArticleContent apArticleContent = new ApArticleContent();
            apArticleContent.setArticleId(apArticle.getId());
            apArticleContent.setContent(dto.getContent());
            apArticleContentMapper.insert(apArticleContent);
        }
        else {
            //2.2 存在id,更新 文章、内容
            //2.2.1 更新文章
            updateById(apArticle);
            //2.2.2 更新文章内容
            ApArticleContent apArticleContent = apArticleContentMapper.selectOne(Wrappers
                    .<ApArticleContent>lambdaQuery()
                    .eq(ApArticleContent::getArticleId, dto.getId()));
            apArticleContent.setContent(dto.getContent());
            apArticleContentMapper.updateById(apArticleContent);
        }
        //异步调用 生成静态文件上传到minio中
        articleFreemarkerService.buildArticleToMinio(apArticle, dto.getContent());

        //3.返回结果 文章的id
        return ResponseResult.okResult(apArticle.getId());
    }
}

8.1.3 开启异步调用

引导类加上@EnableAsyn

@SpringBootApplication
@EnableDiscoveryClient
@MapperScan("com.heima.article.mapper")
@EnableAsync
public class ArticleApplication {

    public static void main(String[] args) {
        SpringApplication.run(ArticleApplication.class,args);
    }

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
    @Bean
    public MessageConverter messageConverter(){
        return new Jackson2JsonMessageConverter();
    }
    @Bean
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory, MessageConverter messageConverter) {
        RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
        rabbitTemplate.setMessageConverter(messageConverter);
        return rabbitTemplate;
    }
}

8.1.4 测试

在这里插入图片描述

查看minio有没有生成

在这里插入图片描述

生成成功,查看数据库,有html生成,说明功能成功

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/497351.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

uniApp使用XR-Frame创建3D场景(6)播放模型动画

上篇文章讲述了如何将XR-Frame作为子组件集成到uniApp中使用 这篇我们讲解播放模型动画 先看源码 <xr-scene render-system"alpha:true" bind:ready"handleReady"> <xr-node visible"{{sec6}}"><xr-light type"ambient&qu…

uniapp vue-cli项目配置devServer和outPutDir

上一次说了使用vue-cli创建uni-app项目&#xff0c;然后使用第三方工具开发打包&#xff0c;利用jekins发布等。 这一次我想解决以下问题&#xff1a; 1我想配置devServer供开发环境使用&#xff0c;这样也可以解决开发环境的跨域问题 2以前我的每个版本信息都配置在package…

Pycharm服务器配置python解释器并结合内网穿透实现公网远程开发

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

Excel:使用VLOOKUP函数,抓取指定数据,后一个列

Excel:使用VLOOKUP函数&#xff0c;抓取指定数据&#xff0c;后一个列 我们有这样一个数据源 要是实现这个页面的赋值 就是对应关系映射 使用 VLOOKUP(A2,Sheet2!$A$2:$B$9,2,FALSE)第一个参数是需要匹配的单元格。 第二个参数是数据源&#xff0c;我这里数据源用的是shee…

没学数模电可以玩单片机吗?

我们首先来看一下数电模电在单片机中的应用。数电知识在单片机中主要解决各种数字信号的处理、运算&#xff0c;如数制转换、数据运算等。模电知识在单片机中主要解决各种模拟信号的处理问题&#xff0c;如采集光照强度、声音的分贝、温度等模拟信号。而数电、模电的相互转换就…

单片机之串口通信

目录 串口介绍 通信的基本概念 并行通信和串行通信 同步通信和异步通信 串行异步通信方式 串行同步通信方式 通信协议 单片机常见通信接口 串行通信三种模式 串口参数 传输速度 ​串口的连接 电平标准 串行口的组成 串口数据缓冲寄存器 串行口控制寄存器 串口…

【IntelliJ IDEA】运行测试报错解决方案(附图)

IntelliJ IDEA 版本 2023.3.4 (Ultimate Edition) 测试报错信息 命令行过长。 通过 JAR 清单或通过类路径文件缩短命令行&#xff0c;然后重新运行 解决方案 修改运行配置&#xff0c;里面如果没有缩短命令行&#xff0c;需要再修改选项里面勾选缩短命令行让其显示&#x…

python基础 | 核心库:PIL

1、读取图像信息 查看图像信息 读取同一文件夹下的文件 可加 ./可不加 rom PIL import Image img Image.open(image.jpg) # 打开图像文件(注意:是去掉文件头的纯数据) print(img.format) # 图像格式(如BMP PNG JPEG 等) print(img.size) # 图像大小(…

灵动翻译音频文件字幕提取及翻译;剪映视频添加字幕

参考&#xff1a;视频音频下载工具 https://tuberipper.com/21/save/mp3 1、灵动翻译音频文件字幕提取及翻译 灵动翻译可以直接chorme浏览器插件安装&#xff1a; 点击使用&#xff0c;可以上传音频文件 上传后自动翻译&#xff0c;然后点击译文即可翻译成中文&#xff0c;…

【VMware Workstation】公司所有主机和虚拟机ip互通,以及虚拟机目录迁移

文章目录 1、场景2、环境3、实战3.1、所有主机和虚拟机ip互通Stage 1 : 【虚拟机】设置为桥接模式Stage 2 : 【虚拟机】设置ipStage 3 : 【路由器】ARP 静态绑定MACStage 3-1 ping 路由器 ipStage 3-2 【静态绑定】虚拟机查看mac地址Stage 3-3 【静态绑定】路由器ARP 静态绑定 …

【QT+QGIS跨平台编译】040:【geos_c+Qt跨平台编译】(一套代码、一套框架,跨平台编译)

点击查看专栏目录 文章目录 一、geos_c介绍二、文件下载三、文件分析四、pro文件五、编译实践一、geos_c介绍 GEOS_C(GEOS C++接口)是GEOS库的C语言版本,它提供了一套丰富的API,允许开发者在C++程序中执行复杂的几何形状处理和空间关系分析。GEOS_C是基于JTS(Java Topolog…

【MySQL】MySQL5.6---windows版本安装(附安装包)

期待您的关注 mysql5.6点击此处下载 提取码&#xff1a;gckb 我将mysql5.6-windows版本的解压包放到了上方的连接当中&#xff0c;如若不想使用我提供的安装包大家还可以在官方网站自行下载。 官方地址&#xff1a;https://dev.mysql.com/downloads/mysql/ 首先检查本地有没…

macOS 13 Ventura (苹果最新系统) v13.6.6正式版

macOS 13 Ventura是苹果电脑的全新操作系统&#xff0c;它为用户带来了众多引人注目的新功能和改进。该系统加强了FaceTime和视频通话的体验&#xff0c;同时优化了邮件、Safari浏览器和日历等内置应用程序&#xff0c;使其更加流畅、快速和安全。特别值得一提的是&#xff0c;…

量化交易软件开发定制的步骤

量化交易软件的定制开发是一个复杂而精细的过程&#xff0c;需要经过一系列步骤来确保最终交付的软件符合客户的需求并具有高度的可靠性和效率。以下是量化交易软件开发定制的主要步骤&#xff1a; 1. 需求分析与规划 在开始开发之前&#xff0c;首先需要与客户深入沟通&…

2.4 比较检验 机器学习

目录 常见比较检验方法 总述 2.4.1 假设检验 2.4.2 交叉验证T检验 2.4.3 McNemar 检验 接我们的上一篇《性能度量》&#xff0c;那么我们在某种度量下取得评估结果后&#xff0c;是否可以直接比较以评判优劣呢&#xff1f;实际上是不可以的。因为我们第一&#xff0c;测试…

MySQL创建表:练习题

练习题&#xff1a; 创建一个名为"students"的数据库&#xff0c;并切换到该数据库。 在"students"数据库中创建一个名为"grades"的表&#xff0c;包含以下字段&#xff1a; id: 整数类型 name: 字符串类型&#xff0c;学生姓名 subject: 字符串…

安全的内网通讯软件,WorkPlus定制化 IM/办公门户解决方案

如今处于数字化转型的“加速期”&#xff0c;政企正经历着一场数字化迭代升级的时代浪潮。而不少企业都已具备了数字化管理的意识&#xff0c;数字化应用场景也在全面推开。WorkPlus不断推动信息技术与企业业务深度融合&#xff0c;作为安全的内网通讯软件&#xff0c;为企业提…

uniApp使用XR-Frame创建3D场景(8)粒子系统

上篇文章讲述了如何将XR-Frame作为子组件集成到uniApp中使用 本片我们详细讲解一下xr-frame的粒子系统 先看源码 <xr-scene render-system"alpha:true" bind:ready"handleReady"> <xr-node visible"{{sec8}}"><xr-asset-load t…

JVM常见垃圾收集算法

JVM常见垃圾收集算法 标记-清除算法复制算法标记-整理&#xff08;标记压缩&#xff09;算法分代收集算法新生代和老年代分代收集算法工作机制 面试题&#xff1a;为什么分代收集算法把堆分成年轻代和老年代&#xff1f; 标记-清除算法 最基础的算法&#xff0c;分标记和清除两…

GPT-1原理-Improving Language Understanding by Generative Pre-Training

文章目录 前言提出动机模型猜想模型提出模型结构模型参数 模型预训练训练的目标训练方式训练参数预训练数据集预训练疑问点 模型微调模型输入范式模型训练微调建议微调疑问点 实验结果分析 前言 首先想感慨一波 这是当下最流行的大模型的的开篇之作&#xff0c;由OpenAI提出。…