java 黑马头条 day4 自媒体文章发布 自媒体文章列表查询 频道列表展示 自媒体文章-发布、修改、保存草稿 自媒体文章-根据id查询 自媒体文章-删除

1 自媒体文章列表查询

1.1 需求分析 

1.2 表结构和实体类

wm_news 自媒体文章表

需求:

  1. 如果有文章标题,按照文章标题模糊查询

  2. 如果有频道信息,按照频道ID查询

  3. 如果有文章状态,按照状态信息进行查询

  4. 如果开始时间,结束时间不为空按照时间区间查询

  5. 按照登录用户ID去查询

  6. 按照创建时间降序

  7. 分页查询,返回结果设置host 地址 为图片访问前缀

自媒体文章实体类

package com.heima.model.wemedia.pojos;
​
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import org.apache.ibatis.type.Alias;
​
import java.io.Serializable;
import java.util.Date;
​
/**
 * <p>
 * 自媒体图文内容信息表
 * </p>
 *
 * @author itheima
 */
@Data
@TableName("wm_news")
public class WmNews implements Serializable {
​
    private static final long serialVersionUID = 1L;
​
    /**
     * 主键
     */
    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;
​
    /**
     * 自媒体用户ID
     */
    @TableField("user_id")
    private Integer userId;
​
    /**
     * 标题
     */
    @TableField("title")
    private String title;
​
    /**
     * 图文内容
     */
    @TableField("content")
    private String content;
​
    /**
     * 文章布局
            0 无图文章
            1 单图文章
            3 多图文章
     */
    @TableField("type")
    private Short type;
​
    /**
     * 图文频道ID
     */
    @TableField("channel_id")
    private Integer channelId;
​
    @TableField("labels")
    private String labels;
​
    /**
     * 创建时间
     */
    @TableField("created_time")
    private Date createdTime;
​
    /**
     * 提交时间
     */
    @TableField("submited_time")
    private Date submitedTime;
​
    /**
     * 当前状态
            0 草稿
            1 提交(待审核)
            2 审核失败
            3 人工审核
            4 人工审核通过
            8 审核通过(待发布)
            9 已发布
     */
    @TableField("status")
    private Short status;
​
    /**
     * 定时发布时间,不定时则为空
     */
    @TableField("publish_time")
    private Date publishTime;
​
    /**
     * 拒绝理由
     */
    @TableField("reason")
    private String reason;
​
    /**
     * 发布库文章ID
     */
    @TableField("article_id")
    private Long articleId;
​
    /**
     * //图片用逗号分隔
     */
    @TableField("images")
    private String images;
​
    @TableField("enable")
    private Short enable;
    
     //状态枚举类***
    @Alias("WmNewsStatus")
    public enum Status{
        NORMAL((short)0),SUBMIT((short)1),FAIL((short)2),ADMIN_AUTH((short)3),ADMIN_SUCCESS((short)4),SUCCESS((short)8),PUBLISHED((short)9);
        short code;
        Status(short code){
            this.code = code;
        }
        public short getCode(){
            return this.code;
        }
    }
​
}

2 频道列表展示

2.1需求分析

文章展示列表页面打开的时候,默认自动加载频道列表数据进行展示,就是查询所有频道数据

2.2 接口定义

接口描述: 查询所有频道的数据

接口地址:/api/v1/channel/channels

请求方式:GET

请求数据类型:*

响应数据类型:application/json

请求参数: 暂无

2.3 功能实现

admin-service 端,修改AdChannelController类,添加如下方法,mapper和service在之前已经定义

@ApiOperation("查询全部频道")
    @GetMapping("/channels")
    public ResponseResult findAll() {
        List<AdChannel> list = channelService.list();
        return ResponseResult.okResult(list);
    }

2.4 修改自媒体网关

配置中心 修改 wemedia-gateway 自媒体网关的yml配置文件中添加如下配置

- id: admin
  uri: lb://leadnews-admin
  predicates:
  - Path=/admin/**
  filters:
  - StripPrefix= 1

3 自媒体文章-发布、修改、保存草稿

3.1 需求分析

保存文章,除了需要wm_news表以外,还需要另外两张表

3.2 思路分析

  1. 参数校验

  2. 保存或修改文章

    • 该功能为保存、修改(是否有id)、保存草稿的共用方法

    • 如果有id修改文章,先删除所有素材关联关系

    • 如果没有id,保存文章

  3. 关联文章和素材关系表

    • 关联内容中的图片与素材的关系

    • 关联封面中的图片与素材的关系

      封面图片如果选择是自动需要从内容中截图图片做为封面图片

      截取规则为:

      • 内容图片的个数小于等于2 则为单图截图一张图

      • 内容图片大于2,则为多图,截图三张图

      • 内容中没有图片,则为无图

  4. 封装返回成功结果

前端给后端传递的参数格式:

{
    "title":"传智教育于2021年1月12日在深交所上市",
    "type":"1",  # 0:无图 1:单图  3:多图 -1:自动
    "labels":"黑马头条",
    "publishTime":"2021-01-14T11:35:49.000Z",
    "channelId":1,
    "images":[  # 文章封面 集合
        "https://heimaleadnewsoss.oss-cn-shanghai.aliyuncs.com/material/2021/1/20210113/20214312094300211.jpg"
    ],
    "status":1,
    # 文章内容 字符串
    "content":"[  
        {
            "type":"text",
            "value":"传智教育于2021年1月12日在深交所上市"
        },
        {
            "type":"image",
            "value":"https://heimaleadnewsoss.oss-cn-shanghai.aliyuncs.com/material/2021/1/20210113/20210112100045327.jpg"
        }
    ]"
}

3.3 接口定义

接口描述: 自媒体文章-发布、修改、保存草稿

接口地址:/api/v1/news/submit

请求方式:POST

请求数据类型:application/json

响应数据类型:application/json

请求示例:

{
    "channelId": 0,
    "content": "",
    "enable": 0,
    "id": 0,
    "images": [],
    "labels": "",
    "publishTime": "",
    "reason": "",
    "status": 0,
    "submitedTime": "",
    "title": "",
    "type": 0
}

请求参数:

参数名称参数说明in是否必须数据类型schema
wmNewswmNewsbodytrueWmNewsDtoWmNewsDto
  channelId频道idtrueinteger(int32)
  content文章内容truestring
  enable是否上架falseinteger(int32)
  id文章idtrueinteger(int32)修改存在
  images图片列表falsearraystring
  labels标签truestring
  publishTime定时发布时间truestring(date-time)
  reason原因falsestring
  status文章状态trueinteger(int32)提交为1 草稿为0
  submitedTime提交时间falsestring(date-time)
  title文章标题truestring
  type文章类型falseinteger(int32)0 无图 1 单图 3 多图 -1 自动

WmNewsDto用于接收前端传递参数

package com.heima.model.media.dtos;

import lombok.Data;

import java.util.Date;
import java.util.List;

@Data
public class WmNewsDTO {
    // 文章ID
    private Integer id;
     /**
     * 标题
     */
    private String title;
     /**
     * 频道id
     */
    private Integer channelId;
     /**
     * 标签
     */
    private String labels;
     /**
     * 发布时间
     */
    private Date publishTime;
     /**
     * 文章内容
     */
    private String content;
     /**
     * 文章封面类型  0 无图 1 单图 3 多图 -1 自动
     */
    private Short type;
     /**
     * 是否上架  0 下架  1 上架
     */
    private Short enable;
     /**
     * 提交时间
     */
    private Date submitedTime; 
     /**
     * 状态 提交为1  草稿为0
     */
    private Short status;
     /**
     * 拒绝理由
     */
    private String reason; 
     /**
     * 封面图片列表
     */
    private List<String> images;
}

3.4 mapper定义

修改WmNewsMaterialMapper,添加一个方法,用来批量添加数据,用于素材与文章关系做关联

public interface WmNewsMaterialMapper extends BaseMapper<WmNewsMaterial> {
    /**
     * 保存文章和素材的关联关系
     * @param wmMaterialIds 素材id集合
     * @param newsId        文章ID
     * @param type          文章封面类型  0 内容引用  1 封面引用
     */
    public void saveRelations(@Param("wmMaterialIds") List<Integer> wmMaterialIds,
                              @Param("newsId") Integer newsId,
                              @Param("type") Short type);
}

新建resources\mapper\WmNewsMaterialMapper.xml文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.heima.wemedia.mapper.WmNewsMaterialMapper">
    <insert id="saveRelations">
        insert into wm_news_material (material_id, news_id, type, ord)
        values
        <foreach collection="wmMaterialIds" item="materialId" index="ord" separator=",">
            (#{materialId}, #{newsId}, #{type}, #{ord})
        </foreach>
    </insert>
</mapper>

素材mapper 新增 根据素材列表查询 相关素材id方法

public interface WmMaterialMapper extends BaseMapper<WmMaterial> {
    /**
     * 根据素材资源路径,查询相关素材id
     * @param urls 素材路径
     * @param userId
     * @return
     */
    public List<Integer> selectRelationsIds(@Param("urls") List<String> urls,
                                            @Param("userId") Integer userId);
}

定义mapper.WmMaterialMapper.xml实现查询方法

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.heima.wemedia.mapper.WmMaterialMapper">
    <select id="selectRelationsIds" resultType="Integer">
        select id from wm_material
        <where>
            url IN
            <foreach item="url" collection="urls" open="(" close=")" separator=",">
                #{url}
            </foreach>
            and user_id = #{userId}
        </where>
    </select>
</mapper>

3.5 业务层代码

常量类准备:com.heima.common.constants.wemedia.WmMediaConstans

package com.heima.common.constants.wemedia;
/**
 * @Description: 素材常量
 * @Version: V1.0
 */
public class WemediaConstants {
    // 是否收藏
    public static final Short COLLECT_MATERIAL = 1;//收藏
    public static final Short CANCEL_COLLECT_MATERIAL = 0;//取消收藏
    // 文章类型
    public static final String WM_NEWS_TYPE_IMAGE = "image";
    // 文章状态
    public static final Short WM_NEWS_DRAFT_STATUS = 0; //草稿
    public static final Short WM_NEWS_SUMMIT_STATUS = 1; //提交
    public static final Short WM_NEWS_AUTHED_STATUS = 8; //审核通过
    public static final Short WM_NEWS_PUBLISH_STATUS = 9; //已发布
    // 文章封面选图
    public static final Short WM_NEWS_NONE_IMAGE = 0; //无图
    public static final Short WM_NEWS_SINGLE_IMAGE = 1; //单图
    public static final Short WM_NEWS_MANY_IMAGE = 3; //多图
    public static final Short WM_NEWS_TYPE_AUTO = -1; //图文类型自动
    // 文章图片引用
    public static final Short WM_CONTENT_REFERENCE = 0;
    public static final Short WM_IMAGE_REFERENCE = 1;
    // 文章上下架状态
    public static final Short WM_NEWS_UP = 1; // 上架
    public static final Short WM_NEWS_DOWN = 0; // 下架
}

在WmNewsService类中新增方法

    /**
     * 自媒体文章发布
     * @param wmNewsDto
     * @return
     */
    ResponseResult submitNews(WmNewsDTO dto);

3.5.1 保存或修改文章

  • 该功能为保存、修改(是否有id)、保存草稿的共用方法

  • 如果有id修改文章,先删除所有素材关联关系

  • 如果没有id,保存文章

    @Override
    public ResponseResult submitNews(WmNewsDTO dto) {
        // 1 参数校验
        if (StringUtils.isBlank(dto.getContent())) {
            CustException.cust(AppHttpCodeEnum.PARAM_INVALID);
        }
        // 校验是否登陆
        WmUser user = WmThreadLocalUtils.getUser();
        if (user == null){
            CustException.cust(AppHttpCodeEnum.NEED_LOGIN);
        }
        // 2 保存或修改文章
        WmNews wmNews = new WmNews();
        // 将dto参数里面的值设置到wmNews
        BeanUtils.copyProperties(dto, wmNews);
        //如果文章布局是自动,需要设置为null
        if (dto.getType().equals(WemediaConstants.WM_NEWS_TYPE_AUTO)) {
            wmNews.setType(null);
        }
        // 处理dto参数 images封面集合 转换成 字符串
        String images = imageListToStr(dto.getImages());
        wmNews.setImages(images);
        wmNews.setUserId(user.getId());
        saveWmNews(wmNews);
        // 如果是草稿  直接返回
        if (WemediaConstants.WM_NEWS_DRAFT_STATUS.equals(dto.getStatus())){
            return ResponseResult.okResult();
        }
        // TODO 3.1 抽取文章中关联的图片路径
        // TODO 3.2 关联文章内容中的图片和素材关系
        // TODO 3.3 关联文章封面中的图片和素材关系  封面可能是选择自动或者是无图
        return ResponseResult.okResult();
    }

图片集合转字符串

/**
 * 图片列表转字符串,并去除图片前缀
 * @param images 图片列表
 * @param webSite 图片前缀
 */
private String imageListToStr(List<String> images, String webSite) {
    return images.stream()  // 获取流
                    .map((url) -> url.replace(webSite, ""))  // 对流数据的中间操作
                    .collect(Collectors.joining(","));
}

保存文章

    @Autowired
    WmNewsMaterialMapper wmNewsMaterialMapper;
    /**
     * 保存或修改文章
     * @param wmNews 文章对象(前端传递)
     */
    private void saveWmNews(WmNews wmNews) {
        wmNews.setCreatedTime(new Date());
        wmNews.setUserId(WmThreadLocalUtils.getUser().getId());
        wmNews.setSubmitedTime(new Date());
        wmNews.setEnable(WemediaConstants.WM_NEWS_UP); // 上架
        if (wmNews.getId()==null) { // 保存操作
            save(wmNews);
        }else {  // 修改
            // 当前文章 和 素材关系表数据删除
            wmNewsMaterialMapper.delete(Wrappers.<WmNewsMaterial>lambdaQuery()
                    .eq(WmNewsMaterial::getNewsId, wmNews.getId()));
            updateById(wmNews);
        }
    }

小知识点:

3.5.2 解析内容中图片列表

/**
     * 自媒体文章发布
     * @param wmNewsDto
     * @param isSubmit 是否为提交 1 为提交 0为草稿
     * @return
     */
@Override
public ResponseResult saveNews(WmNewsDto wmNewsDto, Short isSubmit) {
​
  // 上面代码略 ..........................
  //3.1 抽取文章中关联的图片路径
   List<String> materials = parseContentImages(dto.getContent());
  // 下面代码略 ..........................
}   
​
    /**
     * 抽取文章内容中 所引用的所有图片
     * @param content 文章内容
     * @return
     */
    private List<String> parseContentImages(String content) {
        List<Map> contents = JSON.parseArray(content, Map.class);
        // 遍历文章内容   将所有 type为image的 value获取出来  去除前缀路径
        return contents.stream()
                // 过滤type=image所有的集合
                .filter( map -> map.get("type").equals(WemediaConstants.WM_NEWS_TYPE_IMAGE))
                // 获取到image下的value  图片url
                .map(x -> (String)x.get("value"))
                // 图片url去除前缀
                .map(url-> url.replace(webSite,"").replace(" ",""))
                // 去除重复的路径
                .distinct()
                // stream 转成list集合
                .collect(Collectors.toList());
    }

参考前端传递参数数据格式解析即可

3.5.3 关联文章内容中的图片和素材关系

/**
     * 自媒体文章发布
     * @param wmNewsDto
     * @param isSubmit 是否为提交 1 为提交 0为草稿
     * @return
     */
@Override
public ResponseResult saveNews(WmNewsDto wmNewsDto, Short isSubmit) {
  // 上面代码略 ..........................
​
  // 3.2 关联文章内容中的图片和素材关系
 if (!CollectionUtils.isEmpty(materials)) {
 saveRelativeInfo(materials, wmNews.getId(),WemediaConstants.WM_CONTENT_REFERENCE);
        }
  // 下面代码略 ..........................
}
    @Autowired
    WmMaterialMapper wmMaterialMapper;
    /**
     * 保存素材和文章关系
     * @param urls  素材列表
     * @param newsId     文章ID
     * @param type       类型 0:内容素材  1:封面素材
     */
    private void saveRelativeInfo(List<String> urls, Integer newsId, Short type) {
        //1 查询文章内容中的图片对应的素材ID
        List<Integer> ids = wmMaterialMapper.selectRelationsIds(urls,
                WmThreadLocalUtils.getUser().getId());
        //2 判断素材是否缺失
        if(CollectionUtils.isEmpty(ids) || ids.size() < urls.size()){
            CustException.cust(AppHttpCodeEnum.DATA_NOT_EXIST,"相关素材缺失,保存文章失败");
        }
        //3 保存素材关系
        wmNewsMaterialMapper.saveRelations(ids,newsId,type);
    }

启动服务测试

  • admin-service

  • wemedia-service

  • wemedia-gateway

  • 前端自媒体服务

注意当前登录人下需要上传几张图片

3.5.4 关联文章封面中的图片和素材关系

/**
     * 自媒体文章发布
     * @param wmNewsDto
     * @param isSubmit 是否为提交 1 为提交 0为草稿
     * @return
     */
@Override
public ResponseResult submitNews(WmNewsDto wmNewsDto) {
  // 上面代码略 ..........................
  // 3.3 关联文章封面中的图片和素材关系  封面可能是选择自动或者是无图
  saveRelativeInfoForCover(dto,materials, wmNews);
  return ResponseResult.okResult();
}
​
    /**
     * 【3.3】 关联文章封面中的图片和素材关系
     * @param dto  前端用户选择封面信息数据
     * @param materials  从内容中解析的图片列表
     * @param wmNews     文章ID    
     */
   private void saveRelativeInfoForCover(WmNewsDTO dto, List<String> materials, WmNews wmNews) {
        // 前端用户选择的图
        List<String> images = dto.getImages();
        // 自动获取封面 ****
        if (WemediaConstants.WM_NEWS_TYPE_AUTO.equals(dto.getType())) {
            int materialSize = materials.size();
            if (materialSize > 0 && materialSize <= 2) {  // 单图
                images =  materials.stream().limit(1).collect(Collectors.toList());
                wmNews.setType(WemediaConstants.WM_NEWS_SINGLE_IMAGE);
            } else if (materialSize > 2) { // 多图
                images =  materials.stream().limit(3).collect(Collectors.toList());
                wmNews.setType(WemediaConstants.WM_NEWS_MANY_IMAGE);
            } else {  // 无图
                wmNews.setType(WemediaConstants.WM_NEWS_NONE_IMAGE);
            }
            if (images != null && images.size() > 0) {
                // 将图片集合 转为字符串  url1,url2,url3
                wmNews.setImages(imageListToStr(images));
            }
            updateById(wmNews);
        }
        // 保存图片列表和素材的关系
        if (images != null && images.size() > 0) {
            images = images.stream().map(x->x.replace(webSite,"")
                    .replace(" ","")).collect(Collectors.toList());
            saveRelativeInfo(images, wmNews.getId(), WemediaConstants.WM_IMAGE_REFERENCE);
        }
    }

3.6 控制层

在WmNewsController类中新增方法

    @ApiOperation(value = "发表文章",notes = "发表文章,保存草稿,修改文章 共用的方法")
    @PostMapping("/submit")
    public ResponseResult submitNews(@RequestBody WmNewsDTO dto) {
        return wmNewsService.submitNews(dto);
    }

3.7 测试

postman工具测试

4 自媒体文章-根据id查询

4.1 需求分析

4.2 接口定义

接口描述: 根据id查询文章

接口地址:/api/v1/news/one/{id}

请求方式:GET

请求数据类型:*

响应数据类型:application/json

请求参数:

参数名称参数说明in是否必须数据类型schema
id文章主键idpathtrueinteger(int32)

4.3 业务层代码编写

在WmNewsService接口中新增方法 根据id查询文章的方法

/**
     * 根据文章id查询文章
     * @return
     */
ResponseResult findWmNewsById(Integer id);

实现类:

    @Override
    public ResponseResult findWmNewsById(Integer id) {
        //1 参数检查
        if (id == null) {
            CustException.cust(AppHttpCodeEnum.PARAM_INVALID);
        }
        //2 执行查询
        WmNews wmNews = getById(id);
        if (wmNews == null) {
            CustException.cust(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        //3 返回结果
        ResponseResult result = ResponseResult.okResult(wmNews);
        result.setHost(webSite);
        return result;
    }

4.4 控制层

在WmNewsController新增方法 查询文章

    @ApiOperation(value = "根据id查询自媒体文章")
    @GetMapping("/one/{id}")
    public ResponseResult findWmNewsById(@PathVariable("id") Integer id) {
        return wmNewsService.findWmNewsById(id);
    }

4.5 测试

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值