①分库分表思想
文章表一对一为什么要拆分?因为文章的内容会非常大,查询效率会很低,我们经常操作文章的基本信息,不会很经常查询文章内容。充分发挥高频数据的操作效率。
②freemarker和minIO
由于文章内容数据量过大,我们通过freemarker创建出静态页面并且上传到minIO中存储,前端访问时直接访问静态页面。
③自媒体素材管理
数据库表:
素材表:创作者可上传素材到其中,并且可以设置收藏与否
文章表:创作者所发布的文章的表,包括每一篇文章的用户ID,标题,内容,封面格式,频道,当前状态
文章素材关系表:文章与素材引用的关系表格
业务逻辑:!!!!非常重要要捋清楚
①你写一篇文章,可以选择保存草稿或者直接发布。
②如果你是保存草稿,先要判断该文章是否已经存在,疑问点:为什么草稿也要判断是否已经存在,因为还有编辑草稿的操作,当你把之前的草稿进行重新编辑时提交就要判断是否存在。如果是新增草稿,就新增一篇文章,并把内容中的图片与素材的关系表添加数据进行绑定,而且也要把封面的图片与素材进行绑定。然后就可以结束。
③如果是发布文章,因为文章也有编辑发布和直接发布操作。所以也是要判断是否已经存在该文章。如果是直接发布文章,则不会存在ID,然后把内容和封面的图片与素材的绑定关系添加到数据库的文章素材关系表中即可。但是如果是把已经发布的文章进行编辑然后再发布,首先也是要判断是否存在当然这一步判断肯定是存在的,然后就需要把旧版本的文章内容和封面所绑定的图片进行删除,然后再更新新的绑定关系。
代码实现:
首先要熟悉前端传过来的参数:
代码:
public class WmNewsServiceImpl extends ServiceImpl<WmNewsMapper,WmNews> implements WmNewsService {
@Override
public ResponseResult findList(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());
}
//查询当前登录人的文章
lambdaQueryWrapper.eq(WmNews::getUserId, WmThreadLocalUtil.getUser().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;
}
@Override
public ResponseResult submitNews(WmNewsDto dto) {
//条件判断,判断前端穿过来的值不为空
if(dto == null || dto.getContent() == null){
return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
}
//保存或者修改文章,把基本信息保存到News文章对象中
WmNews wmNews = new WmNews();
BeanUtils.copyProperties(dto,wmNews);
//图片的类型转换,把字符串中的封面图片路径提取出来,保存到Nes对象中
if(dto.getImages() != null && dto.getImages().size()>0){
String imgesStr = StringUtils.join(dto.getImages(), ",");
wmNews.setImages(imgesStr);
}
//如果封面类型为自动,先把封面类型设置为空
if(dto.getType().equals(WemediaConstants.WM_NEWS_TYPE_AUTO)){
wmNews.setType(null);
}
//直接保存到数据库中,并且把图片和素材的关系删除
saveOrUpdateNews(wmNews);
//判断是否为草稿就可以直接返回,如果是,结束方法
if(dto.getStatus().equals(WmNews.Status.NORMAL.getCode())){
return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
}
//不是草稿,保存文章内容图片与素材的关系
//获取到内容中的图片信息
List<String> materials = ectractUrlInfo(dto.getContent());
//将正文中的图片信息+文章的ID,即正文图片与素材的绑定关系保存到数据库中
saveRelativeInfoForContent(materials,wmNews.getId());
//保存文章封面图片与素材的关系
saveRelativeInfoForCover(dto,wmNews,materials);
return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
}
/**
* 根据封面规则去存储
* @param dto
* @param wmNews
* @param materials
*/
//内容图片等于1 小于3 单图 type 1
//大于3 多图 type 3
//没图 无图 type 0
//保存封面与素材的关系
@Autowired
private WmMaterialMapper wmMaterialMapper;
@Autowired
private WmNewsMaterialMapper wmNewsMaterialMapper;
//保存或修改文章
private void saveOrUpdateNews(WmNews wmNews) {
//补全属性
wmNews.setUserId(WmThreadLocalUtil.getUser().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);
}
}
//提取文章内容的图片信息
private List<String> ectractUrlInfo(String content) {
List<String> materials = new ArrayList<>();
List<Map> maps = JSON.parseArray(content, Map.class);
for (Map map : maps) {
if(map.get("type").equals("image")){
String imgUrl = (String) map.get("value");
materials.add(imgUrl);
}
}
return materials;
}
//处理文章内容图片与素材的关系
private void saveRelativeInfoForContent(List<String> materials, Integer newsId) {
saveRelativeInfo(materials,newsId, WemediaConstants.WM_CONTENT_REFERENCE);
}
//保存文章内容图片和素材的关系到数据库中
private void saveRelativeInfo(List<String> materials, Integer newsId, Short type) {
if(materials != null && !materials.isEmpty()){
//通过图片查询素材的id
List<WmMaterial> dbMaterials = wmMaterialMapper.selectList(Wrappers.<WmMaterial>lambdaQuery().in(WmMaterial::getUrl, materials));
//判断素材是否有效
if(dbMaterials == null || dbMaterials.size() == 0){
//手动抛异常
throw new CustomException(AppHttpCodeEnum.MATERIAL_REFERENCE_FALL);
}
if(materials.size() != dbMaterials.size()){
throw new CustomException(AppHttpCodeEnum.MATERIAL_REFERENCE_FALL);
}
List<Integer> idList = dbMaterials.stream().map(WmMaterial::getId).collect(Collectors.toList());
//批量保存
wmNewsMaterialMapper.saveRelations(idList,newsId,type);
}
}
//保存封面图片和素材的关系到数据库中
private void saveRelativeInfoForCover(WmNewsDto dto, WmNews wmNews, List<String> materials) {
List<String> images = dto.getImages();
if(dto.getType().equals(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 && materials.size() <3){
//单图
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(StringUtils.join(images,","));
}
updateById(wmNews);
}
if(images != null && images.size() > 0){
saveRelativeInfo(images,wmNews.getId(),WemediaConstants.WM_COVER_REFERENCE);
}
}
}