app端保存接口
背景:
当审核通过后,将自媒体发布的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进行测试。
package com.heima.apis.article; import com.heima.model.article.dtos.ArticleDto; import com.heima.model.common.dtos.ResponseResult; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @FeignClient(value = "leadnews-article") public interface IArticleClient { @PostMapping("/api/v1/article/save") public ResponseResult saveArticle(@RequestBody ArticleDto dto); }
/** * 保存到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.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.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.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
@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 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.getCode())){
//从内容中抽取文本内容和图片
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)){
throw new RuntimeException("WmNewsAutoScanServiceImpl-文章审核,保存app端相关文章数据失败");
}
//回填articleId
wmNews.setArticleId((Long) responseResult.getData());
updateWmNews(wmNews, (short) 9, "审核通过");
}
}
@Autowired
private IArticleClient articleClient;
/**
* 将自媒体文章保存到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 = articleClient.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("image")){
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")