文章发布功能
发布文章功能涉及到的表
自媒体库:
标签表
图片素材表
内容信息表
内容图片关联表
敏感信息表
App端的文章库:
已发布文章的信息表
已发布文章得配置表
已发布文章的内容表
定时任务库:
任务信息表
任务信息日志表
发布文章功能涉及到的其他微服务:
需要远程调用app端的微服务来保存已发布的内容
需要远程调用定时任务功能进行文章的定时发布及审核
发布文章功能的具体业务逻辑
首先自媒体端的用户通过文章的编辑功能进行文章的编写以及内容图片的上传插入等操作后会有两个按钮一个是提交发布和保存草稿,此时用户点击的不管是保存还是草稿文章都会首先保存到自媒体自己的库.
新增或修改文章
按照正常逻辑保存草稿还是提交保存也都会涉及到是修改还是新增的操作,我们后端代码在用户进行保存之后也需要再进行做出判断,一般这种判断我们都是通过前端是否携带了文章的主键id来判断该文章是否是新增还是修改,如果是修改前端是会携带对应的文章id过来,新增则不需要携带id,我们id自增即可,在自媒体保存完成后我们再去判断该操作是保存草稿还是提交保存.
保存草稿
我们会在代码中做出对应的判断,前端会给我们一个标记,如果是草稿我们将直接存入自媒体的数据库当后直接结束所有的业务逻辑,我们给前端返回一个保存成功的信息,前端页面还会根据我们存入数据库的标记给用户一个草稿的标记,此时文章就不会进行发布,只能被自媒体用户自己查看修改,或者再次点击提交.
提交保存
保存图片
在用户点击了提交保存之后我们首先会对用户文章中的图片进行抽取和保存,
用户的文章中的图片涉及两个部分的图片,分别是封面上的图片和内容中的图片.
封面图片,分为单图,三图,无图和自动 ,由于涉及到多图前端回传来一个图片集合.
内容图片,就是指内容中的图片.
我们需要把内容中的图片url提取出来,并设置自媒体端文章信息表和图片素材的关联关系,保存到素材与文章信息的关联表 分别保存图片的url,自媒体端文章的id,以及引用类型(主图引用或是内容引用)
把封面图片中的图片url提取出来,提取时需要判断文章的封面类型,
有两种情况
我们通过判断该封面是自动封面还是用户手动上传的封面
如果当前封面类型为自动,则设置封面类型的数据 自动模式下封面图片我们会拿内容中图片填充到封面上
* 匹配规则:
* 1,如果内容图片大于等于1,小于3 封面就是单个图 单图 type 1 关联表就设置类型为1
* 2,如果内容图片大于等于3 封面就设置为多个图片 多图 type 3 关联表就设置类型为3
* 3,如果内容没有图片,封面就没有图片 无图 type 0 关联表就设置类型为0
判断完成我们根据类型具体规则去取出对应数量的图片封装到一个变量即可,可以直接放到一个String中用逗号分割
如果当前封面类型不是自动,那么只需要将前端传来的图片集合转换成字符串变量 封装到对象
当判断完成我们统一根据封装后的对象进行封面字段的修改
最后设置自媒体端文章信息表和图片素材的关联关系,保存到素材与文章信息的关联信息表,关联表中我们需要保存图片在素材表中的id,对应的文章id,以及图片的类型.
文章定时审核
由于我们是一个涉及到大量用户都会阅读的新闻类服务,那么图片保存之前我们就需要进行对文章内容以及图片审核,只有审核完成才能允许该内容同步到app端让用户看到.
发布时自媒体人可以选择此刻或是定时发布,然后我们后端会根据用户选择的时间定时审核,审核后文章才能发布到app端让用户看到.
我们的审核分为内容图片审核和内容文字的审核.
审核方式我们会通过阿里云的第三方接口进行审核图片以及文字,我们也可以通过自动以的一个敏感词库来进行内容审核,以及通过OCR技术提取图片内容后进行审核
定时任务
添加定时任务
在审核时我们会调用定时任务的微服务来生成定时任务来执行我们的审核逻辑.
首先我们组装一个定时任务需要用到的task来远程调用我们的微服务,task中我们要封装任务的执行时间,文章的id(重点),任务的优先级(这里一般有两种优先级,1.文章定时审优先级1 ,第三方接口调用失败,重试先级为2),任务类型 组装完成我们通过feign来远程调用添加可以创建定时任务的微服务.
通过task来创建定时任务,首先每一个定时任务都应该先加到定时任务的数据库,分别添加到TaskInfo和TaskInfologs两个表中,需要注意数据库一般存放的是具体的时分秒需要将时间戳转换一下存入.
存放定时任务的数据任务到期执行后就会清理掉,但是TaskInfologs的不会会永久保留
添加完成后,需要返回一个添加成功或者失败的标记
如果是true那么我们就继续将定时任务添加到redis中.
添加redis时,我们有以下规则:
将大于当前时间五分钟执行的任务方法放到redis 的zset集合
将要执行的任务从zset再同步到list当中去或者发布时间小于等于当前时间都直接给到list集合进行立即执行
其中选择将任务放到zset是因为我们可以通过将任务的时间戳赋值给zset集合的score分值来判断该任务是否要执行,而且我们考虑到redis是基于内存的所以我们并不会把所有的定时任务都放到zset当中去,只会把距离五分钟就要执行的任务放到zset当中去.
其中选择将任务放到list集合是因为list在大量执行任务时他的速度性能都要比zset要好很多.
任务添加到数据库中和redis当中后,该方法返回一个task的id即可
将数据库的任务信息定时每5分钟刷新到zset
我们还会在定时任务的微服务中创建两个方法定时的去将数据库中的符
和条件的任务添加到zset集合当中去和每分钟将数据从zset添加到list当中
数据库中的数据同步到redis的zset集合之前需要先清理一下redis中的数据.
清理数据具体清的就是之前同步过来的未来任务和立即执行的任务
同步数据我们会把执行时间大于当前五分钟时间内的所有任务进行同步,从数据库取出每个任务的task,遍历调用创建定时任务到zset集合的方法将任务添加到zset
将数据库的任务信息定时每分钟刷新到list
该任务执行前为了避免出现线程的抢占问题我们可以在执行前设置一下tryLock获取一个token值,有了该token的线程才能执行刷新任务
首先我们需要先获取到所有的未来执行的任务,然后修改他们的key,然后根据分值范围去获取所有的未来执行的任务 表达式:0< score <= System.currentTimeMillis()
然后将他们刷新到立即执行的list集合中,根据futureKey, topicKey, tasks
定时审核文章
我们会创建一个每分钟都会执行一次的方法(@Scheduled(fixedRate = 1000)),定时执行该方法,以确保将符合到期时间的任务进行执行.
方法内我们会通过feign远程调用微服务的特性,调用微服务中拉取任务的方法,将符合条件的获取出来.
拉取的逻辑思路:
我们根据立即执行的key来从右往左获取task,直接通过工具类获取的是task_json,然后转换成task对象,然后再将这个将要执行的任务信息在数据库当中修改信息.
数据库中修改taskinf表和taskinfoLog表的逻辑:
对于执行过的任务信息我们应该在数据库taskinfo表中立即清除只在taskinfoLogs表中保留即可,但是要修改taskinfoLogs该任务的状态为CANCELLED进行保存
修改完成后远程调用微服务的拉取方法也表示执行成功 返回task即可
获取成功后我们进行健壮性检验保证数据的可用性,判断状态码和数据是否存在
判断完成获取出task中封装的文章id
根据自媒体文章的id来执行文章审核的逻辑代码
未完待续.....