

实现在网关中将userId存入请求头中,在网关中添加
java // 获取用户信息
Object userId = claimsBody.get("id");
// 存储到header中
ServerHttpRequest serverHttpRequest = request.mutate().headers(httpHeaders ->
httpHeaders.add("userId",userId+"")).build();
// 重置请求
exchange.mutate().request(serverHttpRequest);
在wmedia中添加自定义拦截器并将userId存入ThreadLocal中
javapackage com.heima.wemedia.interceptor;
import com.heima.model.wemedia.pojos.WmUser;
import com.heima.utils.thread.WmThreadLocalUtil;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Enumeration;
public class WmTokenInterceptor implements HandlerInterceptor {
/**
* 得到header中的用户信息并且存入到当前线程中
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String userId = request.getHeader("userId");
if (userId != null){
WmUser wmUser = new WmUser();
wmUser.setId(Integer.valueOf(userId));
WmThreadLocalUtil.setWmUser(wmUser);
}
return true;
}
/**
* 清理线程中的数据
* @param request
* @param response
* @param handler
* @param modelAndView
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
WmThreadLocalUtil.clear();
}
}
编写图片上传的业务代码
javapackage com.heima.wemedia.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.file.service.FileStorageService;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.wemedia.pojos.WmMaterial;
import com.heima.utils.thread.WmThreadLocalUtil;
import com.heima.wemedia.mapper.WmMaterialMapper;
import com.heima.wemedia.service.WmMaterialService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.Date;
import java.util.UUID;
@Slf4j
@Service
@Transactional
public class WmMaterialServiceImpl extends ServiceImpl<WmMaterialMapper, WmMaterial> implements WmMaterialService {
@Autowired
private FileStorageService fileStorageService;
/**
* 图片上传实现类
* @param multipartFile
* @return
*/
@Override
public ResponseResult uploadPicture(MultipartFile multipartFile) {
// 1.检查参数
if (multipartFile == null || multipartFile.getSize() == 0) {
return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
}
// 2.上传图片到MinIO
String fileId = null;
String fileName = UUID.randomUUID().toString().replace("-","");
String originalFilename = multipartFile.getOriginalFilename();
String postfix = originalFilename.substring(originalFilename.lastIndexOf("."));
try {
fileId = fileStorageService.uploadImgFile("",fileName+postfix,multipartFile.getInputStream());
log.info("上传图片到MinIo中{}",fileId);
} catch (IOException e) {
e.printStackTrace();
log.error("WmMaterialServiceImpl上传文件失败");
}
// 3.将图片存储到数据库中
WmMaterial wmMaterial = new WmMaterial();
wmMaterial.setUserId(WmThreadLocalUtil.getWmUser().getId());
wmMaterial.setUrl(fileId);
wmMaterial.setIsCollection((short)0);
wmMaterial.setType((short)0);
wmMaterial.setCreatedTime(new Date());
save(wmMaterial);
// 4.返回结果
return ResponseResult.okResult(wmMaterial);
}
}

实现流程

参数详情

返回参数说明

发布文章业务层代码
javapackage com.heima.wemedia.service.impl;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.common.constants.WemediaConstants;
import com.heima.common.exception.CustomException;
import com.heima.model.common.dtos.PageResponseResult;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.wemedia.dtos.WmNewsDto;
import com.heima.model.wemedia.dtos.WmNewsPageReqDto;
import com.heima.model.wemedia.pojos.WmMaterial;
import com.heima.model.wemedia.pojos.WmNews;
import com.heima.model.wemedia.pojos.WmNewsMaterial;
import com.heima.model.wemedia.pojos.WmUser;
import com.heima.utils.thread.WmThreadLocalUtil;
import com.heima.wemedia.mapper.WmMaterialMapper;
import com.heima.wemedia.mapper.WmNewsMapper;
import com.heima.wemedia.mapper.WmNewsMaterialMapper;
import com.heima.wemedia.service.WmNewsService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.servlet.View;
import java.util.*;
import java.util.stream.Collectors;
@Service
@Slf4j
@Transactional
public class WmNewsServiceImpl extends ServiceImpl<WmNewsMapper, WmNews> implements WmNewsService {
private final View error;
public WmNewsServiceImpl(View error) {
this.error = error;
}
@Autowired
private WmNewsMaterialMapper wmNewsMaterialMapper;
@Autowired
private WmMaterialMapper wmMaterialMapper;
/**
* 媒体端文章查询接口
* @param dto
* @return
*/
@Override
public ResponseResult findAll(WmNewsPageReqDto dto) {
// 参数检查
dto.checkParam();
// 分页条件查询
IPage page = new Page<>(dto.getPage(),dto.getSize());
LambdaQueryWrapper<WmNews> lambdaQueryWrapper = new LambdaQueryWrapper<>();
// 状态精确查询
if (dto.getStatus() !=null){
lambdaQueryWrapper.eq(WmNews::getStatus,dto.getStatus());
}
// 频道精确查询
if (dto.getChannelId() !=null){
lambdaQueryWrapper.eq(WmNews::getChannelId,dto.getChannelId());
}
// 根据时间查询
if (dto.getBeginPubDate() !=null&& dto.getEndPubDate()!=null){
lambdaQueryWrapper.between(WmNews::getPublishTime,dto.getBeginPubDate(),dto.getEndPubDate());
}
// 关键字模糊查询
if (StringUtils.isNotBlank(dto.getKeyword())){
lambdaQueryWrapper.like(WmNews::getTitle,dto.getKeyword());
}
WmUser user = WmThreadLocalUtil.getWmUser();
// 当前登录人检索
lambdaQueryWrapper.eq(WmNews::getUserId,user.getId());
// 按照发布时间倒序排序
lambdaQueryWrapper.orderByDesc(WmNews::getPublishTime);
page = page(page, lambdaQueryWrapper);
ResponseResult responseResult = new PageResponseResult(dto.getPage(),dto.getSize(),(int)page.getTotal());
responseResult.setData(page.getRecords());
return responseResult;
}
/**
* 文章保存修改或存为草稿实现方法
* @param dto
* @return
*/
@Override
public ResponseResult submitNews(WmNewsDto dto) {
// 条件判断
if (dto == null || dto.getContent() == null){
return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
}
// 保存或修改文章
WmNews wmNews = new WmNews();
// 属性名称和类型相同能够拷贝 拷贝属性到文章类中
BeanUtils.copyProperties(dto,wmNews);
if (dto.getImages() !=null){
// 将images列表转换成字符串s
String imageStr = org.apache.commons.lang.StringUtils.join(dto.getImages(),",");
wmNews.setImages(imageStr);
}
// 封面类型为-1
if (dto.getType().equals(WemediaConstants.WM_NEWS_TYPE_AUTO)){
wmNews.setType(null);
}
saveOrUpdateWmNews(wmNews);
// 判断是否为草稿,如果为草稿结束当前方法
if (dto.getStatus().equals(WmNews.Status.NORMAL.getCode())){
return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
}
// 提取图片url信息
List<String> materials = extractUrlInfo(dto.getContent());
// 提取封面url信息
// 保存文章内容图片与素材的关系
saveRelativeInfoForContent(materials,wmNews.getId());
// 保存文章封面图片与素材的关系 如果设置为自动则自动匹配封面
saveRelativeInfoForCover(dto,wmNews,materials);
return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
}
/**
* 保存文章封面图片与素材的关系 如果设置为自动则自动匹配封面
* 1.如果内容图片大于1小于3则设置单图 type 1
* 2.如果内容图片大于等于3则设置3图 type 3
* 3.如果内容图片没有则不设置图片 type 0
* 保存封面与文章的关系
* @param dto
* @param wmNews
* @param materials
*/
private void saveRelativeInfoForCover(WmNewsDto dto, WmNews wmNews, List<String> materials) {
List<String> images = dto.getImages();
if (dto.getType() == WemediaConstants.WM_NEWS_TYPE_AUTO){
// 多图
if (materials.size() >=3){
wmNews.setType(WemediaConstants.WM_NEWS_MANY_IMAGE);
images = materials.stream().limit(3).collect(Collectors.toList());
// 单图
} else if (materials.size()>=1) {
wmNews.setType(WemediaConstants.WM_NEWS_SINGLE_IMAGE);
images = materials.stream().limit(1).collect(Collectors.toList());
// 无图
} else{
wmNews.setType(WemediaConstants.WM_NEWS_NONE_IMAGE);
}
// 修改文章
if (images != null && images.size() > 0){
wmNews.setImages(org.apache.commons.lang.StringUtils.join(images,","));
}
updateById(wmNews);
if (images != null && images.size() > 0){
saveRelativeInfo(images,wmNews.getId(),WemediaConstants.WM_COVER_REFERENCE);
}
}
}
/**
* 提取文章内容分中的图片信息
* @param content
* @return
*/
private List<String> extractUrlInfo(String content) {
List<Map> maps = JSON.parseArray(content,Map.class);
List<String> materials = new ArrayList<>();
for (Map map : maps) {
if (map.get("type") .equals( "image")){
String imageUrl= (String) map.get("value");
materials.add(imageUrl);
}
}
return materials;
}
/**
* 保存图片与内容的关系
* @param materials
*/
private void saveRelativeInfoForContent(List<String> materials,Integer newsId) {
saveRelativeInfo(materials,newsId,WemediaConstants.WM_CONTENT_REFERENCE);
}
/**
* 保存文章与图片的关系到数据库中
* @param materials
* @param newsId
* @param wmContentReference
*/
private void saveRelativeInfo(List<String> materials, Integer newsId, Short wmContentReference) {
// 根据图片的url查询图片id
List<WmMaterial> wmMaterials= wmMaterialMapper.selectList(Wrappers.<WmMaterial>lambdaQuery().in(WmMaterial::getUrl, materials));
// 判断素材是否有效
if (wmMaterials == null ||wmMaterials.size()==0){
throw new CustomException(AppHttpCodeEnum.MATERIALS_REFERENCE_FAIL);
}
// 判断数据库查询的数据和url的数据是否
if (wmMaterials.size() != materials.size()){
throw new CustomException(AppHttpCodeEnum.MATERIALS_REFERENCE_FAIL);
}
List<Integer> idList = wmMaterials.stream().map(WmMaterial::getId).collect(Collectors.toList());
wmNewsMaterialMapper.saveRelations(idList,newsId,wmContentReference);
}
private void saveOrUpdateWmNews(WmNews wmNews) {
wmNews.setUserId(WmThreadLocalUtil.getWmUser().getId());
wmNews.setCreatedTime(new Date());
wmNews.setSubmitedTime(new Date());
wmNews.setEnable((short) 1); // 默认上架
if (wmNews.getId() == null){
// 保存
save(wmNews);
}else {
// 修改 删除文章图片已关联的关系
wmNewsMaterialMapper.delete(Wrappers.<WmNewsMaterial>lambdaQuery().eq(WmNewsMaterial::getNewsId,wmNews.getId()));
updateById(wmNews);
}
}
}

业务流程


技术选型

雪花算法


接口示例

业务代码
java /**
* 保存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, dto.getId()));
apArticleContent.setContent(dto.getContent());
apArticleContentMapper.updateById(apArticleContent);
}
// 3.结果返回 文章id
return ResponseResult.okResult(apArticle.getId());
}
编写wemedia端业务逻辑 使用feign远程调用app端的保存article的方法
javapackage 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.WmNewsMapper;
import com.heima.wemedia.mapper.WmUserMapper;
import com.heima.wemedia.service.WmNewsAutoScanService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
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
@Transactional
@Slf4j
public class WmNewsAutoScanServiceImpl implements WmNewsAutoScanService {
@Autowired
private WmNewsMapper wmNewsMapper;
@Autowired
private GreenTextScan greenTextScan;
@Autowired
private GreenImageScan greenImageScan;
@Autowired
private FileStorageService fileStorageService;
@Autowired
private IArticleClient articleClient;
@Autowired
private WmChannelMapper wmChannelMapper;
@Autowired
private WmUserMapper wmUserMapper;
@Override
public void autoScanWmNews(Integer id) {
// 1.查询文章 自媒体文章
WmNews wmNews = wmNewsMapper.selectById(id);
if (wmNews == null) {
throw new RuntimeException("文章不存在");
}
if (wmNews.getStatus().equals(WmNews.Status.SUBMIT.getCode())){
// 从content中提取纯文本和图片
Map<String,Object> textAndImages = hendleTextAndImages(wmNews);
// 2.审核文本内容 aliyun接口
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("文章审核保存文章app端失败");
}
// 回填数据
wmNews.setArticleId((Long)responseResult.getData());
updateWmNews(wmNews,(short)9,"审核成功");
}
}
/**
* 保存app端的文章相关数据
* @param wmNews
*/
private ResponseResult saveAppArticle(WmNews wmNews) {
ArticleDto articleDto = new ArticleDto();
// 属性拷贝
BeanUtils.copyProperties(wmNews,articleDto);
// 文章的布局
articleDto.setLayout(wmNews.getType());
// 文章频道
WmChannel wmChannel = wmChannelMapper.selectById(wmNews.getChannelId());
if (wmChannel!=null){
articleDto.setChannelName(wmChannel.getName());
}
// 文章作者
articleDto.setAuthorId(Long.valueOf(wmNews.getUserId()));
WmUser wmUser = wmUserMapper.selectById(wmNews.getUserId());
if (wmUser!=null){
articleDto.setAuthorName(wmUser.getName());
}
if (wmNews.getArticleId() != null){
// 如果媒体文章的app文章id不为空则表示之前审核成功过则将app文章id传入dto
articleDto.setId(wmNews.getArticleId());
}
articleDto.setCreatedTime(new Date());
ResponseResult responseResult = articleClient.saveArticle(articleDto);
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;
}
// 图片去重
images = images.stream().distinct().collect(Collectors.toList());
List<byte[]> imageList = new ArrayList<>();
// 下载图片 minio
for (String image : images) {
byte[] bytes= fileStorageService.downLoadFile(image);
imageList.add(bytes);
}
// 审核图片
try {
Map map = greenImageScan.myImageScan(imageList);
if (map!=null){
if (map.get("suggestion").equals("block")){
flag = false;
// 审核失败
updateWmNews(wmNews,(short)2,"当前文章图片中存在违规内容");
}
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() == 1){
return flag;
}
try {
Map map = greenTextScan.myGreeTextScan(wmNews.getTitle()+"-"+content);
if (map!=null){
if (map.get("suggestion").equals("block")){
flag = false;
// 审核失败
updateWmNews(wmNews,(short)2,"当前文章中存在违规内容");
}
if (map.get("suggestion").equals("review")){
flag = false;
// 不确定信息需要人工审核
updateWmNews(wmNews,(short)3,"当前文章存在不确定内容");
}
}
} catch (Exception e) {
flag = false;
e.printStackTrace();
}
return flag;
}
/**
* 修改文章内容
* @param wmNews
*/
private void updateWmNews(WmNews wmNews,short status,String reason) {
wmNews.setStatus(status);
wmNews.setReason(reason);
wmNewsMapper.updateById(wmNews);
}
/**
* 提取文章的文本内容
* 提取文章的封面
* @param wmNews
* @return
*/
private Map<String, Object> hendleTextAndImages(WmNews wmNews) {
Map<String, Object> textAndImages = new HashMap<>();
// 1.从文章内容中提取文章文本和图片
// 存储纯文本内容
StringBuilder stringBuilder = new StringBuilder();
// 存储图片内容
List<String> images = new ArrayList<>();
if (StringUtils.isNotBlank(wmNews.getContent())){
List<Map> list = JSONArray.parseArray(wmNews.getContent(),Map.class);
for (Map map : list) {
if (map.get("type").equals("text")){
stringBuilder.append(map.get("value"));
}
if (map.get("type").equals("image")){
images.add((String) map.get("value"));
}
}
}
// 提取文章封面
if (StringUtils.isNotBlank(wmNews.getImages())){
String[] imageArray = wmNews.getImages().split(",");
images.addAll(Arrays.asList(imageArray));
}
textAndImages.put("content",stringBuilder.toString());
textAndImages.put("images",images);
return textAndImages;
}
}


DFA算法

javapackage com.heima.utils.common;
import java.util.*;
public class SensitiveWordUtil {
public static Map<String, Object> dictionaryMap = new HashMap<>();
/**
* 生成关键词字典库
* @param words
* @return
*/
public static void initMap(Collection<String> words) {
if (words == null) {
System.out.println("敏感词列表不能为空");
return ;
}
// map初始长度words.size(),整个字典库的入口字数(小于words.size(),因为不同的词可能会有相同的首字)
Map<String, Object> map = new HashMap<>(words.size());
// 遍历过程中当前层次的数据
Map<String, Object> curMap = null;
Iterator<String> iterator = words.iterator();
while (iterator.hasNext()) {
String word = iterator.next();
curMap = map;
int len = word.length();
for (int i =0; i < len; i++) {
// 遍历每个词的字
String key = String.valueOf(word.charAt(i));
// 当前字在当前层是否存在, 不存在则新建, 当前层数据指向下一个节点, 继续判断是否存在数据
Map<String, Object> wordMap = (Map<String, Object>) curMap.get(key);
if (wordMap == null) {
// 每个节点存在两个数据: 下一个节点和isEnd(是否结束标志)
wordMap = new HashMap<>(2);
wordMap.put("isEnd", "0");
curMap.put(key, wordMap);
}
curMap = wordMap;
// 如果当前字是词的最后一个字,则将isEnd标志置1
if (i == len -1) {
curMap.put("isEnd", "1");
}
}
}
dictionaryMap = map;
}
/**
* 搜索文本中某个文字是否匹配关键词
* @param text
* @param beginIndex
* @return
*/
private static int checkWord(String text, int beginIndex) {
if (dictionaryMap == null) {
throw new RuntimeException("字典不能为空");
}
boolean isEnd = false;
int wordLength = 0;
Map<String, Object> curMap = dictionaryMap;
int len = text.length();
// 从文本的第beginIndex开始匹配
for (int i = beginIndex; i < len; i++) {
String key = String.valueOf(text.charAt(i));
// 获取当前key的下一个节点
curMap = (Map<String, Object>) curMap.get(key);
if (curMap == null) {
break;
} else {
wordLength ++;
if ("1".equals(curMap.get("isEnd"))) {
isEnd = true;
}
}
}
if (!isEnd) {
wordLength = 0;
}
return wordLength;
}
/**
* 获取匹配的关键词和命中次数
* @param text
* @return
*/
public static Map<String, Integer> matchWords(String text) {
Map<String, Integer> wordMap = new HashMap<>();
int len = text.length();
for (int i = 0; i < len; i++) {
int wordLength = checkWord(text, i);
if (wordLength > 0) {
String word = text.substring(i, i + wordLength);
// 添加关键词匹配次数
if (wordMap.containsKey(word)) {
wordMap.put(word, wordMap.get(word) + 1);
} else {
wordMap.put(word, 1);
}
i += wordLength - 1;
}
}
return wordMap;
}
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("法轮");
list.add("法轮功");
list.add("冰毒");
initMap(list);
String content="我是一个好人,并不会卖冰毒,也不操练法轮功,我真的不卖冰毒";
Map<String, Integer> map = matchWords(content);
System.out.println(map);
}
}

配置

javapackage com.heima.common.tess4j;
import lombok.Getter;
import lombok.Setter;
import net.sourceforge.tess4j.ITesseract;
import net.sourceforge.tess4j.Tesseract;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.awt.image.BufferedImage;
@Getter
@Setter
@Component
@ConfigurationProperties(prefix = "tess4j")
public class Tess4jClient {
private String dataPath;
private String language;
public String doOCR(BufferedImage image) throws Exception {
// 创建tesseract对象
ITesseract instance = new Tesseract();
// 设置字体库路径
instance.setDatapath(dataPath);
// 设置语言
instance.setLanguage(language);
String result = instance.doOCR(image);
result = result.replaceAll("\\r\\n", "-").replaceAll(" ", "");
return result;
}
}



redis实现

实现思路

悲观锁和乐观锁

mybatis-plus集成乐观锁

导入相关文件测试redis
javapackage com.heima.schedule.test;
import com.heima.common.redis.CacheService;
import com.heima.schedule.ScheduleApplication;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.Set;
@SpringBootTest(classes = ScheduleApplication.class)
@RunWith(SpringRunner.class)
public class RedisTest {
@Autowired
private CacheService cacheService;
@Test
public void testList(){
// 在list左边添加元素
cacheService.lLeftPush("list_001","hello world,redis");
// 获取list右边的元素并删除
String list = cacheService.lRightPop("list_001");
System.out.println(list);
}
@Test
public void testZset(){
// 添加数据到Zset中 分值
// cacheService.zAdd("zset_key_001","hello_zset_001",1000);
// cacheService.zAdd("zset_key_001","hello_zset_002",4000);
// cacheService.zAdd("zset_key_001","hello_zset_003",3000);
// cacheService.zAdd("zset_key_001","hello_zset_004",7000);
Set<String> zsetKey001 = cacheService.zRangeByScore("zset_key_001", 0, 5000);
System.out.println(zsetKey001);
}
}
业务实现

java代码实现
package com.heima.schedule.service.impl;
import com.alibaba.fastjson.JSON;
import com.heima.common.constants.ScheduleConstants;
import com.heima.common.redis.CacheService;
import com.heima.model.schedule.dtos.Task;
import com.heima.model.schedule.pojos.Taskinfo;
import com.heima.model.schedule.pojos.TaskinfoLogs;
import com.heima.schedule.mapper.TaskinfoLogsMapper;
import com.heima.schedule.mapper.TaskinfoMapper;
import com.heima.schedule.service.TaskService;
import lombok.extern.slf4j.Slf4j;
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.Calendar;
import java.util.Date;
@Service
@Transactional
@Slf4j
public class TaskServiceImpl implements TaskService {
@Autowired
private TaskinfoMapper taskinfoMapper;
@Autowired
private CacheService cacheService;
@Autowired
private TaskinfoLogsMapper taskinfoLogsMapper;
@Override
public Long addTask(Task task) {
// 1.添加任务到数据库中
Boolean success = addTaskToDb(task);
// 添加任务到redis
if (success){
TaskToCache(task);
}
return task.getTaskId();
}
/**
* 把任务添加到redis中
* @param task
*/
private void TaskToCache(Task task) {
String key = ScheduleConstants.TOPIC+task.getTaskType()+"_"+task.getPriority();
// 获取5分钟之后的时间 毫秒值
Calendar calendar = Calendar.getInstance();
// 增加5分钟
calendar.add(Calendar.MINUTE,5);
// 将时间转换为毫秒值
Long nextScheduleTime = calendar.getTimeInMillis();
if (task.getExecuteTime() <=System.currentTimeMillis()){
// 2.1如果任务执行时间小于等于当前时间
cacheService.lLeftPush(key, JSON.toJSONString(task));
} else if (task.getExecuteTime() <= nextScheduleTime) {
// 2.2如果任务执行时间大于当前时间 && 小于预设时间 存入zset
cacheService.zAdd(ScheduleConstants.FUTURE+key,JSON.toJSONString(task),task.getExecuteTime());
}
}
/**
* 添加任务到数据库中
* @param task
* @return
*/
private Boolean addTaskToDb(Task task) {
Boolean flag = false;
try {
// 保存任务表
Taskinfo taskinfo = new Taskinfo();
BeanUtils.copyProperties(task,taskinfo);
taskinfo.setExecuteTime(new Date(task.getExecuteTime()));
taskinfoMapper.insert(taskinfo);
// 设置taskID
task.setTaskId(taskinfo.getTaskId());
// 保存任务日志数据
TaskinfoLogs taskinfoLogs = new TaskinfoLogs();
BeanUtils.copyProperties(taskinfo,taskinfoLogs);
// 乐观锁版本 初始为1
taskinfoLogs.setVersion(1);
// 初始化状态
taskinfoLogs.setStatus(ScheduleConstants.SCHEDULED);
taskinfoLogsMapper.insert(taskinfoLogs);
flag = true;
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("task数据添加失败"+e);
}
return flag;
}
}
取消任务

java@Override
public Long addTask(Task task) {
// 1.添加任务到数据库中
Boolean success = addTaskToDb(task);
// 添加任务到redis
if (success){
TaskToCache(task);
}
return task.getTaskId();
}
/**
* 把任务添加到redis中
* @param task
*/
private void TaskToCache(Task task) {
String key = task.getTaskType()+"_"+task.getPriority();
// 获取5分钟之后的时间 毫秒值
Calendar calendar = Calendar.getInstance();
// 增加5分钟
calendar.add(Calendar.MINUTE,5);
// 将时间转换为毫秒值
Long nextScheduleTime = calendar.getTimeInMillis();
if (task.getExecuteTime() <=System.currentTimeMillis()){
// 2.1如果任务执行时间小于等于当前时间
cacheService.lLeftPush(ScheduleConstants.TOPIC+key, JSON.toJSONString(task));
} else if (task.getExecuteTime() <= nextScheduleTime) {
// 2.2如果任务执行时间大于当前时间 && 小于预设时间 存入zset
cacheService.zAdd(ScheduleConstants.FUTURE+key,JSON.toJSONString(task),task.getExecuteTime());
}
}
@Override
public boolean cancelTask(Long taskId) {
boolean flag = false;
// 更新任务日志 删除任务
Task task = updateDb(taskId,ScheduleConstants.CANCELLED);
// 删除redis的数据
if (task != null){
removeTaskFromCache(task);
flag = true;
}
return flag;
}
/**
*
* 删除redis中的数据
* @param task
*/
private void removeTaskFromCache(Task task) {
String key = task.getTaskType()+"_"+task.getPriority();
if (task.getExecuteTime() <= System.currentTimeMillis()){
cacheService.lRemove(ScheduleConstants.TOPIC+key,0,JSON.toJSONString(task));
}else{
cacheService.zRemove(ScheduleConstants.FUTURE+key,JSON.toJSONString(task));
}
}
/**
* 更新任务日志 删除任务
* @param taskId
* @param status
* @return
*/
private Task updateDb(Long taskId, Integer status) {
Task task = new Task();
try {
// 删除任务信息
taskinfoMapper.deleteById(taskId);
// 更新任务日志
TaskinfoLogs taskinfoLogs = taskinfoLogsMapper.selectById(taskId);
taskinfoLogs.setStatus(status);
taskinfoLogsMapper.updateById(taskinfoLogs);
BeanUtils.copyProperties(taskinfoLogs,task);
task.setExecuteTime(taskinfoLogs.getExecuteTime().getTime());
}catch(Exception e){
log.error("删除任务失败"+e);
}
return task;
}
消费任务

java/**
* 按照类型和优先级拉取任务
* @param type
* @param priority
* @return
*/
@Override
public Task pull(int type, int priority) {
Task task = null;
try {
String key = type+"_"+priority;
// 从redis中拉取任务
String task_Json = cacheService.lRightPop(ScheduleConstants.TOPIC+key);
if (StringUtils.isNotBlank(task_Json)){
task = JSON.parseObject(task_Json,Task.class);
// 修改数据库信息
updateDb(task.getTaskId(),ScheduleConstants.EXECUTED);
}
} catch (Exception e) {
e.printStackTrace();
log.error("拉取任务异常"+e);
}
return task;
}
未来数据定时刷新


redis通道

代码示例
java@Scheduled(cron = "0 */1 * * * ?") // 每分钟执行
public void refresh(){
log.info("未来数据定时刷新--定时任务");
// 获取所有未来数据的集合
Set<String> futureKeys = cacheService.scan(ScheduleConstants.FUTURE+"*");
// 按照key和分值查询符合条件的数据
for (String futureKey : futureKeys) {
// 获取当前数据的key
String topicKey = ScheduleConstants.TOPIC+futureKey.split(ScheduleConstants.FUTURE)[1];
// 按照key和分值查找符合条件的key
Set<String> tasks = cacheService.zRangeByScore(futureKey, 0, System.currentTimeMillis());
// 同步数据
if (!tasks.isEmpty()){
cacheService.refreshWithPipeline(futureKey,topicKey,tasks);
log.info("成功将"+futureKey+"刷新到了"+topicKey);
}
}
}
@EnableScheduling // 开启调度任务 启动类添加
分布式锁解决方法抢用的问题


代码实现
java /**
* 加锁
*
* @param name
* @param expire
* @return
*/
public String tryLock(String name, long expire) {
name = name + "_lock";
String token = UUID.randomUUID().toString();
RedisConnectionFactory factory = stringRedisTemplate.getConnectionFactory();
RedisConnection conn = factory.getConnection();
try {
//参考redis命令:
//set key value [EX seconds] [PX milliseconds] [NX|XX]
Boolean result = conn.set(
name.getBytes(),
token.getBytes(),
Expiration.from(expire, TimeUnit.MILLISECONDS),
RedisStringCommands.SetOption.SET_IF_ABSENT //NX
);
if (result != null && result)
return token;
} finally {
RedisConnectionUtils.releaseConnection(conn, factory,false);
}
return null;
}
数据库同步到redis

代码实现
java /**
* 定时同步到redis中
*/
@PostConstruct // 启动服务自动调用
@Scheduled(cron = "0 */5 * * * ?") // 每5分钟执行一次
public void reloadData(){
// 清理缓存中的数值
clearCache();
// 查询符合条件的数据 小于未来5分中的数据
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.MINUTE,5);
List<Taskinfo> taskinfos = taskinfoMapper.selectList(Wrappers.<Taskinfo>lambdaQuery().lt(Taskinfo::getExecuteTime, calendar.getTime()));
// 将数据同步到redis中
if (taskinfos!=null&&taskinfos.size()>0){
for (Taskinfo taskinfo : taskinfos) {
Task task = new Task();
BeanUtils.copyProperties(taskinfo,task);
task.setExecuteTime(taskinfo.getExecuteTime().getTime());
TaskToCache(task);
}
}
log.info("数据库中的数据同步到了redis");
}
public void clearCache(){
// 查询所有的key
Set<String> topicKeys = cacheService.scan(ScheduleConstants.TOPIC+"*");
Set<String> futureKeys = cacheService.scan(ScheduleConstants.FUTURE+"*");
// 清理缓存中的数据
cacheService.delete(topicKeys);
cacheService.delete(futureKeys);
}
本文作者:钱小杰
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!