JavaWeb_LeadNews_Day4-阿里云内容安全, 雪花算法, app文章保存, 自媒体文章审核

news/2024/11/24 9:34:45/

JavaWeb_LeadNews_Day4-阿里云内容安全, 雪花算法, app文章保存, 自媒体文章审核

  • 阿里云内容安全
  • 分布式主键策略-雪花算法
  • app文章保存
    • 具体实现
    • 总结
  • 自媒体文章审核
    • 提取内容和图片
    • 审核内容和图片
    • 保存app端文章
    • Feign远程调用降级
    • 发布文章异步调用
    • 自管理敏感词
    • 图片文字识别
      • 入门
      • 集成至文字审核
    • 静态文章生成, 异步
  • 来源

阿里云内容安全

  • 依赖
    <!-- 内容安全 -->
    <dependency><groupId>com.aliyun</groupId><artifactId>aliyun-java-sdk-core</artifactId>
    </dependency>
    <dependency><groupId>com.aliyun</groupId><artifactId>aliyun-java-sdk-green</artifactId>
    </dependency>
    
  • 实现
    @SpringBootTest(classes = WemediaApplication.class)
    @RunWith(SpringRunner.class)
    public class AliyunTest {@Autowiredprivate GreenTextScan greenTextScan;@Autowiredprivate FileStorageService fileStorageService;@Autowiredprivate GreenImageScan greenImageScan;/*** 测试文本内容审核*/@Testpublic void testScanText() throws Exception {Map map = greenTextScan.greeTextScan("我是一个好人, 冰毒");System.out.println(map);}/*** 测试图片审核*/@Testpublic void testScanImage() throws Exception {byte[] bytes = fileStorageService.downLoadFile("http://192.168.174.133:9000/leadnews/2023/07/22/7086dfbae7d1478f9ffc7784351e8708.png");List<byte[]> list = new ArrayList<>();list.add(bytes);Map map = greenImageScan.imageScan(list);System.out.println(map);}
    }
    
  • 总结
    • 工具类太复杂, 云盾内容安全也没开通(要企业认证), 看看测试好了
    • 测试图片审核会报错, 可能是没开通

分布式主键策略-雪花算法

  • 背景
    随着业务的增长,文章表可能要占用很大的物理存储空间,为了解决该问题,
    后期使用数据库分片技术。将一个数据库进行拆分,通过数据库中间件连接。
    如果数据库中该表选用ID自增策略,则可能产生重复的ID,
    此时应该使用分布式ID生成策略来生成ID。
    
  • 技术选型
    方案优势劣势
    redis(INCR)生成一个全局连续递增的数字类型主键增加了一个外部组件的依赖, Redis不可以, 则整个数据库将无法再插入
    UUID全局唯一, Mysql也有UUID实现36个字符组成, 占用空间大
    snowflake算法全局唯一, 数字类型, 存储成本低机器规模大于1024台无法支持
  • 雪花算法
    snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。
    其核心思想是:使用41bit作为毫秒数,
    10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),
    12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生4096个ID),
    最后还有一个符号位,永远是0
    

app文章保存

具体实现

  • feign远程调用依赖
    <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
    
  • 文章端远程接口
    // 定义接口
    @FeignClient("leadnews-article")
    public interface IArticleClient {@PostMapping("/api/v1/article/save")ResponseResult saveArticle(@RequestBody ArticleDto dto);
    }// 实现接口
    @RestController
    public class ArticleClient implements IArticleClient {@Autowiredprivate ApArticleService apArticleService;@PostMapping("/api/v1/article/save")@Overridepublic ResponseResult saveArticle(@RequestBody ArticleDto dto) {return apArticleService.saveArticle(dto);}
    }
    
  • app文章保存
    // Dto
    public class ArticleDto  extends ApArticle {/*** 文章内容*/private String content;
    }// Service
    public ResponseResult saveArticle(ArticleDto dto) {// 1. 检查参数if(dto == null){return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);}ApArticle apArticle = new ApArticle();BeanUtils.copyProperties(dto, apArticle);// 2. 保存或修改文章if(dto.getId() == null){// 2.1 不存在id// 保存文章save(apArticle);// 保存文章配置ApArticleConfig apArticleConfig = new ApArticleConfig(apArticle.getId());apArticleConfigMapper.insert(apArticleConfig);// 保存文章内容ApArticleContent apArticleContent = new ApArticleContent();apArticleContent.setArticleId(apArticle.getId());apArticleContent.setContent(dto.getContent());apArticleContentMapper.insert(apArticleContent);}else{// 2.2 存在id// 修改文章updateById(apArticle);// 修改文章内容LambdaQueryWrapper<ApArticleContent> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.eq(ApArticleContent::getArticleId, dto.getId());ApArticleContent apArticleContent = apArticleContentMapper.selectOne(queryWrapper);apArticleContentMapper.updateById(apArticleContent);}// 3. 返回文章idreturn ResponseResult.okResult(apArticle.getId());
    }
    

总结

  • dto中没有配置表的数据, 所以保存时设置配置的默认值, 修改时不用修改文章配置表
  • 返回文章id是需要在wm_news表中设置article_id, 新闻表中有个文章id字段

自媒体文章审核

提取内容和图片

  • 实现思路
  1. 通过id查询文章
  2. 查询文章状态, 需要是提交, 待审核状态
  3. 获取文章内容和图片
  • 具体实现
    @Service
    @Slf4j
    @Transactional
    public class WmNewsAutoScanServiceImpl implements WmNewsAutoScanService {@Autowiredprivate WmNewsMapper wmNewsMapper;/*** 自媒体文章审核* @param id*/@Overridepublic void autoScanWmNews(Integer id) throws Exception {// 查询文章WmNews wmNews = wmNewsMapper.selectById(id);if(wmNews == null){throw new RuntimeException("WmNewsAutoScanServiceImpl-文章不存在");}if(wmNews.getStatus() == WmNews.Status.SUBMIT.getCode()) {// 获取图片和文本内容Map<String, Object> textAndImages = handleTextAndImages(wmNews);}}  /*** 获取图片和文本内容* @param wmNews* @return*/private Map<String, Object> handleTextAndImages(WmNews wmNews) {// 图片路径List<String> imgList = new ArrayList<>();// 文本内容StringBuilder builder = new StringBuilder();// 从自媒体文章的内容中提取文章和图片if(StringUtils.isNotBlank(wmNews.getContent())) {List<Map> maps = JSONArray.parseArray(wmNews.getContent(), Map.class);for (Map map : maps) {if (map.get("type").equals("image")) {imgList.add((String) map.get("value"));} else if (map.get("type").equals("text")) {builder.append(map.get("value"));}}}// 提取文章的封面图片if(StringUtils.isNotBlank(wmNews.getImages())) {String[] split = wmNews.getImages().split(",");imgList.addAll(Arrays.asList(split));}Map<String, Object> resultMap = new HashMap<>();resultMap.put("content", builder.toString());resultMap.put("images", imgList);return resultMap;}
    }
    
  • 总结
    • Arrays.asListString数组转化为List<String>
    • ListaddAll方法可以将List<>直接全加进来

审核内容和图片

  • 具体实现
    
    @Service
    @Slf4j
    @Transactional
    public class WmNewsAutoScanServiceImpl implements WmNewsAutoScanService {@Autowiredprivate WmNewsMapper wmNewsMapper;@Autowiredprivate FileStorageService fileStorageService;@Autowiredprivate GreenTextScan greenTextScan;@Autowiredprivate GreenImageScan greenImageScan;/*** 自媒体文章审核* @param id*/@Overridepublic void autoScanWmNews(Integer id) throws Exception {...if(wmNews.getStatus() == WmNews.Status.SUBMIT.getCode()) {...// 2. 文章文本审核boolean isTextScan = handleTextScan((String) textAndImages.get("content"), wmNews);if(!isTextScan) return;// 3. 文章图片审核boolean isImageScan = handleImageScan((List<String>) textAndImages.get("images"), wmNews);if(!isImageScan) return;}}/*** 审核图片* @param images* @param wmNews* @return*/private boolean handleImageScan(List<String> images, WmNews wmNews) {boolean flag = true;if(images == null || images.size() == 0){return flag;}List<byte[]> list = new ArrayList<>();// 图片去重images = images.stream().distinct().collect(Collectors.toList());for (String image : images) {byte[] bytes = fileStorageService.downLoadFile(image);list.add(bytes);}try {Map map = greenImageScan.imageScan(list);if(map != null){// 违规内容if(map.get("suggestion").equals("block")){flag = false;updateWmNews(wmNews, 2, "当前文章中存在违规内容");}// 不确定内容, 需要人工审核if(map.get("suggestion").equals("review")){flag = false;updateWmNews(wmNews, 3, "当前文章中存在不确定内容");}}} catch (Exception e) {flag = false;throw new RuntimeException(e);}return flag;}/*** 审核纯文本内容* @param content* @param wmNews* @return*/private boolean handleTextScan(String content, WmNews wmNews) {boolean flag = true;content += wmNews.getTitle();if(StringUtils.isEmpty(content)){return flag;}try {Map map = greenTextScan.greeTextScan(content);if(map != null){// 违规内容if(map.get("suggestion").equals("block")){flag = false;updateWmNews(wmNews, 2, "当前文章中存在违规内容");}// 不确定内容, 需要人工审核if(map.get("suggestion").equals("review")){flag = false;updateWmNews(wmNews, 3, "当前文章中存在不确定内容");}}} catch (Exception e) {flag = false;e.printStackTrace();}return flag;}/*** 修改文章内容* @param wmNews* @param status* @param reason*/private void updateWmNews(WmNews wmNews, int status, String reason) {wmNews.setStatus((short) status);wmNews.setReason(reason);wmNewsMapper.updateById(wmNews);}
    }
    
  • 总结
    • 之前误以为stream流中可以通过return+collect(Collectors.toList)修改原数据内容, 而是stream().map((item)->{...})中的item若是对象, 则可以通过修改对象中的属性来修改原数据内容, 而直接通过给对象赋值来修改对象则不行, 因为这相当于让item指向新的地址, 这没有什么意义, 跟原本的数据无关.
    • idea快捷键: ctrl+alt+m, 抽取方法.

保存app端文章

  • 具体实现
@Service
@Slf4j
@Transactional
public class WmNewsAutoScanServiceImpl implements WmNewsAutoScanService {.../*** 自媒体文章审核* @param id*/@Overridepublic void autoScanWmNews(Integer id) throws Exception {...if(wmNews.getStatus() == WmNews.Status.SUBMIT.getCode()) {...// 4. 审核通过, 保存app端相关文章ResponseResult responseResult = saveAppArticle(wmNews);if(!responseResult.getCode().equals(200)){throw new RuntimeException("WmNewsAutoScanServiceImpl-文字审核, 保存app端相关文章数据失败");}// 回填article_idwmNews.setArticleId((Long) responseResult.getData());updateWmNews(wmNews, 9, "审核成功");}}@Autowiredprivate IArticleClient articleClient;@Autowiredprivate WmChannelMapper wmChannelMapper;@Autowiredprivate WmUserMapper wmUserMapper;/*** 保存app端相关的文章数据* @param wmNews*/private ResponseResult saveAppArticle(WmNews wmNews) {ArticleDto dto = new ArticleDto();BeanUtils.copyProperties(wmNews, dto);// 文章的布局dto.setLayout(wmNews.getType());// 频道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(wmNews != null){dto.setAuthorName(wmUser.getName());}// 设置文字idif(wmNews.getArticleId() != null){dto.setId(wmNews.getArticleId());}dto.setCreatedTime(new Date());ResponseResult responseResult = articleClient.saveArticle(dto);return responseResult;}
}
  • 总结
    • 保存完, 要将返回的文章id, 保存为news表中的article_id

Feign远程调用降级

  • 介绍
    • 服务降级是服务自我保护的一种方式,或者保护下游服务的一种方式,用于确保服务不会受请求突增影响变得不可用,确保服务不会崩溃
    • 服务降级虽然会导致请求失败,但是不会导致阻塞
  • 编写降级实现类
    @Component
    public class IArticleClientFallback implements IArticleClient {@Overridepublic ResponseResult saveArticle(ArticleDto dto)  {return ResponseResult.errorResult(AppHttpCodeEnum.SERVER_ERROR,"获取数据失败");}
    }
    
  • 自媒体微服务扫描降级类
    @Configuration
    @ComponentScan("com.heima.apis.article.fallback")
    public class InitConfig {
    }
    
  • feign接口指定降级类
    @FeignClient(value = "leadnews-article",fallback = IArticleClientFallback.class)
    public interface IArticleClient {...
    }
    
  • 配置开启服务降级, 并指定响应时间
    feign:
    # 开启feign对hystrix熔断降级的支持
    hystrix:enabled: true
    # 修改调用超时时间
    client:config:default:connectTimeout: 2000readTimeout: 2000
    
  • 在文章微服务延时
    public ResponseResult saveArticle(ArticleDto dto) {// 测试feign调用降级try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}
    }
    
  • 总结: 麻

发布文章异步调用

  1. 在自动审核上加异步注解
    @Async
    public void autoScanWmNews(Integer id){...
    }
    
  2. 文章发布成功后, 调用自动审核
    public ResponseResult submitNews(WmNewsDto dto) {...wmNewsAutoScanService.autoScanWmNews(wmNews.getId());return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }
    
  3. 自媒体启动类添加注解
    @EnableAsync
    public class WemediaApplication {...
    }
    

自管理敏感词

  • 方案介绍
    方案说明
    数据库模糊查询效率太低
    String.indexOf(“”)查找数据库量大的话也是比较慢
    全文检索分词再匹配
    DFA算法确定有穷自动机(一种数据结构)
  • 具体实现
    private boolean handleSensitiveScan(String content, WmNews wmNews) {boolean flag = true;// 查询敏感词List<WmSensitive> wmSensitiveList = wmSensitiveMapper.selectList(Wrappers.<WmSensitive>lambdaQuery().select(WmSensitive::getSensitives));List<String> sensitiveList = wmSensitiveList.stream().map(WmSensitive::getSensitives).collect(Collectors.toList());// 初始化敏感词库SensitiveWordUtil.initMap(sensitiveList);// 查看文章中是否包含敏感词Map<String, Integer> wordMap = SensitiveWordUtil.matchWords(content);if(wordMap.size() > 0){updateWmNews(wmNews, 2, "当前文章中存在违规内容:" + wordMap);flag = false;}return flag;
    }
    
  • 总结
    • 使用DFA算法实现敏感词检测, 感觉DFA算法和字典树差不多

图片文字识别

入门

  • 什么是OCR
    OCR(Optical Character Recognition,光学字符识别)是指电子设备(例如扫描仪或数码相机)检查纸上打印的字符,
    通过检测暗、亮的模式确定其形状,然后用字符识别方法将形状翻译成计算机文字的过程
    
  • 方案
    方案说明
    百度OCR收费
    Tesseract-OCRGoogle维护的开源OCR引擎, 支持Java, Python等语言调用
    Test4j封装了Tesseract-OCR, 支持Java调用
  • tess4j依赖
    <dependencies><dependency><groupId>net.sourceforge.tess4j</groupId><artifactId>tess4j</artifactId><version>4.1.1</version></dependency>
    </dependencies>
    
  • 案例
    public static void main(String[] args) throws TesseractException {// 创建实例ITesseract tesseract = new Tesseract();// 设置字体库路径tesseract.setDatapath("D:\\workspace\\tessdata");// 设置语言 简体中文tesseract.setLanguage("chi_sim");File file = new File("D:\\csdn图库\\微服务.png");// 识别图片String res = tesseract.doOCR(file).replaceAll("\\r|\\n", "-");System.out.println("识别的结果为:"+res);
    }
    

集成至文字审核

  • 导入依赖, spring工厂加入工具类, 在调用工具类的服务中配置参数
  • 工具类
    @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;}
    }
    
  • 具体实现
    // 从byte[]转换为BufferedImage
    ByteArrayInputStream in = new ByteArrayInputStream(bytes);
    BufferedImage imageFile = ImageIO.read(in);
    // 识别图片文字
    String result = tess4jClient.doOCR(imageFile);
    System.out.println("识别内容: " + result);
    // 审核是否包含敏感词
    boolean isSensitive = handleSensitiveScan(result, wmNews);
    if(!isSensitive){return isSensitive;
    }
    

静态文章生成, 异步

@Service
@Slf4j
@Transactional
public class ArticleFreemarkerServiceImpl implements ArticleFreemarkerService {@Autowiredprivate Configuration configuration;@Autowiredprivate FileStorageService fileStorageService;@Autowiredprivate ApArticleService apArticleService;/*** 生成静态文件上传到minIO中* @param article* @param content*/@Override@Asyncpublic void buildArticleToMinIO(ApArticle article, String content) {if(StringUtils.isNotBlank(content)) {// 2. 文章内容通过freemarker生成html文件Template template = null;StringWriter out = null;try {template = configuration.getTemplate("article.ftl");//    数据模型Map<String, Object> data = new HashMap<>();data.put("content", JSONArray.parseArray(content));out = new StringWriter();//    合成template.process(data, out);} catch (Exception e) {throw new RuntimeException(e);}// 3. 把html上传到minio中InputStream in = new ByteArrayInputStream(out.toString().getBytes());String path = fileStorageService.uploadHtmlFile("", article.getId()+".html", in);// 4. 修改ap_article表, 保存static_url字段apArticleService.update(Wrappers.<ApArticle>lambdaUpdate().eq(ApArticle::getId, article.getId()).set(ApArticle::getStaticUrl, path));}}
}

来源

黑马程序员. 黑马头条


http://www.ppmy.cn/news/988195.html

相关文章

Idea 2023.2 maven 打包时提示 waring 问题解决

Version idea 2023.2 问题 使用 Maven 打包 &#xff0c;控制台输出 Waring 信息 [WARNING] [WARNING] Plugin validation issues were detected in 7 plugin(s) [WARNING] [WARNING] * org.apache.maven.plugins:maven-dependency-plugin:3.3.0 [WARNING] * org.apache.…

极海APM32F4xx Tiny】学习笔记08-RTT 链表使用方法

极海APM32F4xx Tiny】学习笔记08-RTT 链表使用方法 1.双向链表基本结构 #define rt_inline static __inline /** * 双向链表 */ struct rt_list_node { struct rt_list_node *next; /**< point to next node. */ struct rt_list_node *prev; /**< point to prev node. …

maven引入本地jar包的简单方式【IDEA】【SpringBoot】

前言 想必点进来看这篇文章的各位&#xff0c;都是已经习惯了Maven从中央仓库或者阿里仓库直接拉取jar包进行使用。我也是&#x1f921;&#x1f921;。 前两天遇到一个工作场景&#xff0c;对接三方平台&#xff0c;结果对方就是提供的一个jar包下载链接&#xff0c;可给我整…

【Java从入门到大牛】集合进阶上篇

&#x1f525; 本文由 程序喵正在路上 原创&#xff0c;CSDN首发&#xff01; &#x1f496; 系列专栏&#xff1a;Java从入门到大牛 &#x1f320; 首发时间&#xff1a;2023年7月29日 &#x1f98b; 欢迎关注&#x1f5b1;点赞&#x1f44d;收藏&#x1f31f;留言&#x1f43…

ubuntu 安装 Pycharm社区版

在Ubuntu中安装pycharm社区版_上玄下纁的博客-CSDN博客 里面可以创建快捷方式&#xff0c;蛮好用的

RFID原理与应用课程笔记

一、RFID技术概述 1.rfid技术的特点&#xff1a; 1.1自动识别&#xff1a;1.光学符号识别&#xff08;OCR&#xff09; 2.条形码识别 3.智能卡识别 4.射频识别&#xff08;RFID&#xff09; 5.生物识别 &#xff08;指纹识别&#xff0c;语音识别&#xff09; 1.2.RFID自动…

MTK联发科安卓核心板MT8385(Genio 500)规格参数资料_性能介绍

简介 MT8385安卓核心板 是一个高度集成且功能强大的物联网平台&#xff0c;具有以下主要特性&#xff1a; l 四核 Arm Cortex-A73 处理器 l 四核Arm Cortex-A53处理器 l Arm Mali™-G72 MP3 3D 图形加速器 (GPU)&#xff0c;带有 Vulkan 1.0、OpenGL ES 3.2 和 OpenCL™ 2.x …

wps文档xsl函数使用问题

wps xsl 使用tranpose转置行列&#xff0c;提示#value错误 TRANSPOSE(A2:A15) 打开开发工具&#xff0c;wps默认的编程语言是java script&#xff0c;可以切换到vbs 添加如下代码 function test() {Range("A16:N16").Value2 WorksheetFunction.Transpose(Range(&q…