0


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

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

阿里云内容安全

  • 依赖<!-- 内容安全 --><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)publicclassAliyunTest{@AutowiredprivateGreenTextScan greenTextScan;@AutowiredprivateFileStorageService fileStorageService;@AutowiredprivateGreenImageScan greenImageScan;/** * 测试文本内容审核 */@TestpublicvoidtestScanText()throwsException{Map map = greenTextScan.greeTextScan("我是一个好人, 冰毒");System.out.println(map);}/** * 测试图片审核 */@TestpublicvoidtestScanImage()throwsException{byte[] bytes = fileStorageService.downLoadFile("http://192.168.174.133:9000/leadnews/2023/07/22/7086dfbae7d1478f9ffc7784351e8708.png");List<byte[]> list =newArrayList<>(); 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")publicinterfaceIArticleClient{@PostMapping("/api/v1/article/save")ResponseResultsaveArticle(@RequestBodyArticleDto dto);}// 实现接口@RestControllerpublicclassArticleClientimplementsIArticleClient{@AutowiredprivateApArticleService apArticleService;@PostMapping("/api/v1/article/save")@OverridepublicResponseResultsaveArticle(@RequestBodyArticleDto dto){return apArticleService.saveArticle(dto);}}
  • app文章保存// DtopublicclassArticleDtoextendsApArticle{/** * 文章内容 */privateString content;}// ServicepublicResponseResultsaveArticle(ArticleDto dto){// 1. 检查参数if(dto ==null){returnResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);}ApArticle apArticle =newApArticle();BeanUtils.copyProperties(dto, apArticle);// 2. 保存或修改文章if(dto.getId()==null){// 2.1 不存在id// 保存文章save(apArticle);// 保存文章配置ApArticleConfig apArticleConfig =newApArticleConfig(apArticle.getId()); apArticleConfigMapper.insert(apArticleConfig);// 保存文章内容ApArticleContent apArticleContent =newApArticleContent(); apArticleContent.setArticleId(apArticle.getId()); apArticleContent.setContent(dto.getContent()); apArticleContentMapper.insert(apArticleContent);}else{// 2.2 存在id// 修改文章updateById(apArticle);// 修改文章内容LambdaQueryWrapper<ApArticleContent> queryWrapper =newLambdaQueryWrapper<>(); queryWrapper.eq(ApArticleContent::getArticleId, dto.getId());ApArticleContent apArticleContent = apArticleContentMapper.selectOne(queryWrapper); apArticleContentMapper.updateById(apArticleContent);}// 3. 返回文章idreturnResponseResult.okResult(apArticle.getId());}

总结

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

自媒体文章审核

提取内容和图片

  • 实现思路
  1. 通过id查询文章
  2. 查询文章状态, 需要是提交, 待审核状态
  3. 获取文章内容和图片
  • 具体实现@Service@Slf4j@TransactionalpublicclassWmNewsAutoScanServiceImplimplementsWmNewsAutoScanService{@AutowiredprivateWmNewsMapper wmNewsMapper;/** * 自媒体文章审核 * @param id */@OverridepublicvoidautoScanWmNews(Integer id)throwsException{// 查询文章WmNews wmNews = wmNewsMapper.selectById(id);if(wmNews ==null){thrownewRuntimeException("WmNewsAutoScanServiceImpl-文章不存在");}if(wmNews.getStatus()==WmNews.Status.SUBMIT.getCode()){// 获取图片和文本内容Map<String,Object> textAndImages =handleTextAndImages(wmNews);}}/** * 获取图片和文本内容 * @param wmNews * @return */privateMap<String,Object>handleTextAndImages(WmNews wmNews){// 图片路径List<String> imgList =newArrayList<>();// 文本内容StringBuilder builder =newStringBuilder();// 从自媒体文章的内容中提取文章和图片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"));}elseif(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 =newHashMap<>(); resultMap.put("content", builder.toString()); resultMap.put("images", imgList);return resultMap;}}
  • 总结 - Arrays.asListString数组转化为List<String>- ListaddAll方法可以将List<>直接全加进来

审核内容和图片

  • 具体实现@Service@Slf4j@TransactionalpublicclassWmNewsAutoScanServiceImplimplementsWmNewsAutoScanService{@AutowiredprivateWmNewsMapper wmNewsMapper;@AutowiredprivateFileStorageService fileStorageService;@AutowiredprivateGreenTextScan greenTextScan;@AutowiredprivateGreenImageScan greenImageScan;/** * 自媒体文章审核 * @param id */@OverridepublicvoidautoScanWmNews(Integer id)throwsException{...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 */privatebooleanhandleImageScan(List<String> images,WmNews wmNews){boolean flag =true;if(images ==null|| images.size()==0){return flag;}List<byte[]> list =newArrayList<>();// 图片去重 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;thrownewRuntimeException(e);}return flag;}/** * 审核纯文本内容 * @param content * @param wmNews * @return */privatebooleanhandleTextScan(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 */privatevoidupdateWmNews(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@TransactionalpublicclassWmNewsAutoScanServiceImplimplementsWmNewsAutoScanService{.../**
     * 自媒体文章审核
     * @param id
     */@OverridepublicvoidautoScanWmNews(Integer id)throwsException{...if(wmNews.getStatus()==WmNews.Status.SUBMIT.getCode()){...// 4. 审核通过, 保存app端相关文章ResponseResult responseResult =saveAppArticle(wmNews);if(!responseResult.getCode().equals(200)){thrownewRuntimeException("WmNewsAutoScanServiceImpl-文字审核, 保存app端相关文章数据失败");}// 回填article_id
            wmNews.setArticleId((Long) responseResult.getData());updateWmNews(wmNews,9,"审核成功");}}@AutowiredprivateIArticleClient articleClient;@AutowiredprivateWmChannelMapper wmChannelMapper;@AutowiredprivateWmUserMapper wmUserMapper;/**
     * 保存app端相关的文章数据
     * @param wmNews
     */privateResponseResultsaveAppArticle(WmNews wmNews){ArticleDto dto =newArticleDto();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(newDate());ResponseResult responseResult = articleClient.saveArticle(dto);return responseResult;}}
  • 总结 - 保存完, 要将返回的文章id, 保存为news表中的article_id

Feign远程调用降级

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

发布文章异步调用

  1. 在自动审核上加异步注解@AsyncpublicvoidautoScanWmNews(Integer id){...}
  2. 文章发布成功后, 调用自动审核publicResponseResultsubmitNews(WmNewsDto dto){... wmNewsAutoScanService.autoScanWmNews(wmNews.getId());returnResponseResult.okResult(AppHttpCodeEnum.SUCCESS);}
  3. 自媒体启动类添加注解@EnableAsyncpublicclassWemediaApplication{...}

自管理敏感词

  • 方案介绍 方案说明数据库模糊查询效率太低String.indexOf(“”)查找数据库量大的话也是比较慢全文检索分词再匹配DFA算法确定有穷自动机(一种数据结构)
  • 具体实现privatebooleanhandleSensitiveScan(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算法和字典树差不多

图片文字识别

入门

  • 什么是OCROCR(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>
  • 案例publicstaticvoidmain(String[] args)throwsTesseractException{// 创建实例ITesseract tesseract =newTesseract();// 设置字体库路径 tesseract.setDatapath("D:\\workspace\\tessdata");// 设置语言 简体中文 tesseract.setLanguage("chi_sim");File file =newFile("D:\\csdn图库\\微服务.png");// 识别图片String res = tesseract.doOCR(file).replaceAll("\\r|\\n","-");System.out.println("识别的结果为:"+res);}

集成至文字审核

  • 导入依赖, spring工厂加入工具类, 在调用工具类的服务中配置参数
  • 工具类@Getter@Setter@Component@ConfigurationProperties(prefix ="tess4j")publicclassTess4jClient{privateString dataPath;privateString language;publicStringdoOCR(BufferedImage image)throwsTesseractException{//创建Tesseract对象ITesseract tesseract =newTesseract();//设置字体库路径 tesseract.setDatapath(dataPath);//中文识别 tesseract.setLanguage(language);//执行ocr识别String result = tesseract.doOCR(image);//替换回车和tal键 使结果为一行 result = result.replaceAll("\\r|\\n","-").replaceAll(" ","");return result;}}
  • 具体实现// 从byte[]转换为BufferedImageByteArrayInputStream in =newByteArrayInputStream(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@TransactionalpublicclassArticleFreemarkerServiceImplimplementsArticleFreemarkerService{@AutowiredprivateConfiguration configuration;@AutowiredprivateFileStorageService fileStorageService;@AutowiredprivateApArticleService apArticleService;/**
     * 生成静态文件上传到minIO中
     * @param article
     * @param content
     */@Override@AsyncpublicvoidbuildArticleToMinIO(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 =newHashMap<>();
                data.put("content",JSONArray.parseArray(content));
                out =newStringWriter();//    合成
                template.process(data, out);}catch(Exception e){thrownewRuntimeException(e);}// 3. 把html上传到minio中InputStream in =newByteArrayInputStream(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));}}}

来源

黑马程序员. 黑马头条


本文转载自: https://blog.csdn.net/Y_cen/article/details/131971418
版权归原作者 Y_cen 所有, 如有侵权,请联系我们删除。

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

还没有评论