背景:
当审核通过后,将自媒体发布的news存入leadnews_article数据库,让移动端用户能够访问到
分布式id
随着业务的增长,文章表可能要占用很大的物理存储空间,为了解决该问题,后期使用数据库分片技术。将一个数据库进行拆分,通过数据库中间件连接。如果数据库中该表选用ID自增策略,则可能产生重复的ID,此时应该使用分布式ID生成策略来生成ID。


snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。其核心思想是:使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生 4096 个 ID),最后还有一个符号位,永远是0
文章端相关的表都使用雪花算法生成id,包括ap_article、 ap_article_config、 ap_article_content
保存app端文章-思路分析:
在文章审核成功以后需要在app的article库中新增文章数据:
1.保存文章信息 ap_article
2.保存文章配置信息 ap_article_config
3.保存文章内容 ap_article_content
注:自媒体文章wm-news审核成功后会分配一个article-id,当修改后再次通过审核,文章表需要根据article-id修改更新对应文章信息
①:在heima-leadnews-feign-api中定义接口
导入feign远程调用依赖
定义文章端的远程接口
②:在heima-leadnews-article实现feign接口
③:新建ApArticleConfigMapper类到文章微服务mapper包下
④:在ApArticleService中新增保存方法进行实现
⑤:postman进行测试。
/** * 保存到app端相关文章 * @param dto * @return */ @Override public ResponseResult saveArticle(ArticleDto dto) { //1、检查参数 if(dto == null){ return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID); } ApArticle apArticle = new ApArticle(); BeanUtils.copyProperties(dto, apArticle); //2、判断是否存在id 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); //修改文章内容 ApArticleContent apArticleContent = apArticleContentMapper.selectOne(Wrappers.<ApArticleContent>lambdaQuery().eq(ApArticleContent::getArticleId, apArticle.getId())); apArticleContent.setContent(dto.getContent()); apArticleContentMapper.updateById(apArticleContent); } //3、结果返回, 文章id return ResponseResult.okResult(apArticle.getId()); }
feign远程接口调用方式:

Wemedia-service进行审核
1、通过id查询自媒体文章
2、从自媒体文章中抽取文本和图片,图片包含内容图片和封面图片
3、审核文本和图片,若不通过修改审核状态,有一个不通过则结束,
4、文本和图片通过审核后,利用feign进行远程调用leadnews-article中实现了IArticleClient接口的ArticleClient,存储app文章到app文章数据库的方法。修改审核状态为已通过审核。
5、存储完成后,将ArticleClient返回的articleId回填到自媒体文章数据库。
package com.heima.wemedia.service.impl; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.heima.apis.article.IArticleClient; import com.heima.common.aliyun.GreenImageScan; import com.heima.common.aliyun.GreenTextScan; import com.heima.file.service.FileStorageService; import com.heima.model.article.dtos.ArticleDto; import com.heima.model.common.dtos.ResponseResult; import com.heima.model.wemedia.pojos.WmChannel; import com.heima.model.wemedia.pojos.WmNews; import com.heima.model.wemedia.pojos.WmUser; import com.heima.wemedia.mapper.WmChannelMapper; import com.heima.wemedia.mapper.WmNewsMapper; import com.heima.wemedia.mapper.WmUserMapper; import com.heima.wemedia.service.WmNewsAutoScanService; import com.heima.wemedia.service.WmNewsService; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.apache.hadoop.io.retry.AtMostOnce; import org.checkerframework.checker.units.qual.A; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.*; import java.util.stream.Collectors; @Service @Slf4j @Transactional public class WmNewsAutoScanServiceImpl implements WmNewsAutoScanService { @Autowired private WmNewsService wmNewsService; @Autowired private GreenTextScan greenTextScan; @Autowired private GreenImageScan greenImageScan; @Autowired private FileStorageService fileStorageService; @Autowired private IArticleClient iArticleClient; @Autowired private WmChannelMapper wmChannelMapper; @Autowired private WmUserMapper wmUserMapper; /** * 自媒体文章审核 * @param id 自媒体文章id */ @Override public void autoScanWmNews(Integer id) { //1、查询自媒体文章 WmNews wmNews = wmNewsService.getById(id); if(wmNews == null){ throw new RuntimeException("WmNewsAutoScanServiceImpl-文章不存在"); } //当自媒体文章处于待审核状态1时才会进行审核 if(wmNews.getStatus().equals(WmNews.Status.SUBMIT)){ //从内容中抽取文本内容和图片 Map<String, Object> textandImages = handleTextAndImages(wmNews); //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; //4、审核成功,将自媒体文章保存到app端的文章数据 ResponseResult responseResult = saveAppArticle(wmNews); if(responseResult.getCode().equals(200)){ }else{ throw new RuntimeException("WmNewsAutoScanServiceImpl-文章审核,保存app端相关文章数据失败"); } //回填articleId wmNews.setArticleId((Long) responseResult.getData()); updateWmNews(wmNews, (short) 9, "审核通过"); } } /** * 将自媒体文章保存到app端文章的数据库 * @param wmNews */ private ResponseResult saveAppArticle(WmNews wmNews) { ArticleDto dto = new ArticleDto(); //属性拷贝 BeanUtils.copyProperties(wmNews, dto); dto.setLayout(wmNews.getType()); //频道:拷贝过来的属性只有chanelId,没有channelName WmChannel wmChannel = wmChannelMapper.selectById(wmNews.getChannelId()); if (wmChannel != null) { dto.setChannelName(wmChannel.getName()); } //author_id dto.setAuthorId(wmNews.getUserId().longValue()); //author_name 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); return responseResult; } /** * 审核图片 * @param images * @param wmNews * @return */ private boolean handleImageScan(List<String> images, WmNews wmNews){ boolean flag = true; if(images == null || images.size() == 0){ return flag; } // //下载图片 minIO // //图片去重 // images = images.stream().distinct().collect(Collectors.toList()); // ArrayList<byte[]> imageList = new ArrayList<>(); // for (String image : images) { // byte[] bytes = fileStorageService.downLoadFile(image); // imageList.add(bytes); // // } // try { // //审核图片 // Map map = greenImageScan.imageScan(imageList); // if(map != null){ // //审核失败 // if(map.get("suggestion").equals("block")){ // flag = false; // updateWmNews(wmNews, (short) 1, "当前文章图片存在违规内容"); // } // //不确定信息,需要人工审核 // if(map.get("suggestion").equals("review")){ // flag = false; // updateWmNews(wmNews, (short) 3, "当前文章图片存在不确定"); // } // } // } catch (Exception e) { // flag = false; // e.printStackTrace(); // } return flag; } /** * 审核文本内容 * @param content * @param wmNews * @return */ private boolean handleTextScan(String content, WmNews wmNews){ boolean flag = true; //审核标题和内容 if((wmNews.getTitle()+'-'+content).length() == 0){ return flag; } // try { // Map map = greenTextScan.greeTextScan(wmNews.getTitle()+'-'+content); // if(map != null){ // //审核失败 // if(map.get("suggestion").equals("block")){ // flag = false; // updateWmNews(wmNews, (short) 1, "当前文章中存在违规内容"); // } // //不确定信息,需要人工审核 // if(map.get("suggestion").equals("review")){ // flag = false; // updateWmNews(wmNews, (short) 3, "文章内容存在不确定"); // } // } // } catch (Exception e) { // flag = false; // e.printStackTrace(); // } return flag; } /** * 修改文章内容 * @param wmNews * @param status * @param reason */ private void updateWmNews(WmNews wmNews, short status, String reason) { wmNews.setStatus(status); wmNews.setReason(reason); wmNewsService.updateById(wmNews); } /** * 1、从自媒体文章内容中抽取文本内容和图片 * 2、提取文章封面图片 * @param wmNews */ private Map<String, Object> handleTextAndImages(WmNews wmNews) { StringBuilder stringBuilder = new StringBuilder(); List<String> images = new ArrayList<>(); //1、从自媒体文章内容中抽取文本内容和图片 if(StringUtils.isNotBlank(wmNews.getContent())){ List<Map> maps = JSONArray.parseArray(wmNews.getContent(), Map.class); for(Map map: maps){ if(map.get("type").equals("text")){ stringBuilder.append(map.get("value")); } if(map.get("type").equals("images")){ images.add((String) map.get("value")); } } } //2、提取文章封面 if(StringUtils.isNotBlank(wmNews.getImages())){ String[] split = wmNews.getImages().split(","); images.addAll(Arrays.asList(split)); } Map<String, Object> resultMap = new HashMap<>(); resultMap.put("content", stringBuilder.toString()); resultMap.put("images", images); return resultMap; } }
在自媒体微服务启动类尚开启feign服务并设置扫描feign接口包路径
@EnableFeignClients(basePackages = "com.heima.apis")