第四章 自媒体文章发布

学习目标

  • 完成自媒体文章列表查询功能
  • 完成自媒体文章的发布功能
  • 完成自媒体文章的查询
  • 完成自媒体文章的删除功能
  • 完成自媒体文章的上下架功能

    1 自媒体文章列表查询

    1.1 需求分析

    day04 自媒体文章发布的 - 图1

    1.2 表结构和实体类

    wm_news 自媒体文章表
    day04 自媒体文章发布的 - 图2
    需求:
  1. 如果有文章标题,按照文章标题模糊查询
  2. 如果有频道信息,按照频道ID查询
  3. 如果有文章状态,按照状态信息进行查询
  4. 如果开始时间,结束时间不为空按照时间区间查询
  5. 按照登录用户ID去查询
  6. 按照创建时间降序
  7. 分页查询,返回结果设置host 地址 为图片访问前缀

自媒体文章实体类

  1. package com.heima.model.wemedia.pojos;
  2. import com.baomidou.mybatisplus.annotation.IdType;
  3. import com.baomidou.mybatisplus.annotation.TableField;
  4. import com.baomidou.mybatisplus.annotation.TableId;
  5. import com.baomidou.mybatisplus.annotation.TableName;
  6. import lombok.Data;
  7. import org.apache.ibatis.type.Alias;
  8. import java.io.Serializable;
  9. import java.util.Date;
  10. /**
  11. * <p>
  12. * 自媒体图文内容信息表
  13. * </p>
  14. *
  15. * @author itheima
  16. */
  17. @Data
  18. @TableName("wm_news")
  19. public class WmNews implements Serializable {
  20. private static final long serialVersionUID = 1L;
  21. /**
  22. * 主键
  23. */
  24. @TableId(value = "id", type = IdType.AUTO)
  25. private Integer id;
  26. /**
  27. * 自媒体用户ID
  28. */
  29. @TableField("user_id")
  30. private Integer userId;
  31. /**
  32. * 标题
  33. */
  34. @TableField("title")
  35. private String title;
  36. /**
  37. * 图文内容
  38. */
  39. @TableField("content")
  40. private String content;
  41. /**
  42. * 文章布局
  43. 0 无图文章
  44. 1 单图文章
  45. 3 多图文章
  46. */
  47. @TableField("type")
  48. private Short type;
  49. /**
  50. * 图文频道ID
  51. */
  52. @TableField("channel_id")
  53. private Integer channelId;
  54. @TableField("labels")
  55. private String labels;
  56. /**
  57. * 创建时间
  58. */
  59. @TableField("created_time")
  60. private Date createdTime;
  61. /**
  62. * 提交时间
  63. */
  64. @TableField("submited_time")
  65. private Date submitedTime;
  66. /**
  67. * 当前状态
  68. 0 草稿
  69. 1 提交(待审核)
  70. 2 审核失败
  71. 3 人工审核
  72. 4 人工审核通过
  73. 8 审核通过(待发布)
  74. 9 已发布
  75. */
  76. @TableField("status")
  77. private Short status;
  78. /**
  79. * 定时发布时间,不定时则为空
  80. */
  81. @TableField("publish_time")
  82. private Date publishTime;
  83. /**
  84. * 拒绝理由
  85. */
  86. @TableField("reason")
  87. private String reason;
  88. /**
  89. * 发布库文章ID
  90. */
  91. @TableField("article_id")
  92. private Long articleId;
  93. /**
  94. * //图片用逗号分隔
  95. */
  96. @TableField("images")
  97. private String images;
  98. @TableField("enable")
  99. private Short enable;
  100. //状态枚举类***
  101. @Alias("WmNewsStatus")
  102. public enum Status{
  103. NORMAL((short)0),SUBMIT((short)1),FAIL((short)2),ADMIN_AUTH((short)3),ADMIN_SUCCESS((short)4),SUCCESS((short)8),PUBLISHED((short)9);
  104. short code;
  105. Status(short code){
  106. this.code = code;
  107. }
  108. public short getCode(){
  109. return this.code;
  110. }
  111. }
  112. }

1.3 接口定义

接口描述: 分页带条件查询自媒体文章列表
接口地址:/api/v1/news/list
请求方式:POST
请求数据类型:application/json
响应数据类型:application/json
请求示例:

{
    "beginPubDate": "",
  "endPubDate": "",
    "channelId": 0,
    "keyword": "",
    "page": 0,
    "size": 0,
    "status": 0
}

请求参数:

参数名称 参数说明 in 是否必须 数据类型 schema
dto dto body true WmNewsPageReqDto WmNewsPageReqDto
  beginPubDate 开始时间 false string(date-time)
  endPubDate 结束时间 false string(date-time)
  channelId 频道id false integer(int32)
  keyword 文章名称 false string
  page 当前第几页 false integer(int32)
  size 每页显示条数 false integer(int32)
  status 文章状态 false integer(int32)

WmNewsPageReqDto

package com.heima.model.wemedia.dtos;
import com.heima.model.common.dtos.PageRequestDTO;
import lombok.Data;
import java.util.Date;
@Data
public class WmNewsPageReqDTO extends PageRequestDTO {
    private Short status;//状态
    private Date beginPubDate;//开始时间
    private Date endPubDate;//结束时间
    private Integer channelId;//所属频道ID
    private String keyword;//关键字
}

1.4 mapper实现

创建com.heima.wemedia.mapper.WmNewsMapper

package com.heima.wemedia.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.heima.model.wemedia.pojos.WmNews;
public interface WmNewsMapper extends BaseMapper<WmNews> {
}

1.5 service代码实现

定义WmNewsService接口:com.heima.wemedia.service.WmNewsService

package com.heima.wemedia.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.wemedia.dtos.WmNewsPageReqDTO;
import com.heima.model.wemedia.pojos.WmNews;
public interface WmNewsService extends IService<WmNews> {
    /**
     * 查询所有自媒体文章
     * @return
     */
    public ResponseResult findList(WmNewsPageReqDTO dto);
}

定义实现类 com.heima.wemedia.service.impl.WmNewsServiceImpl

package com.heima.wemedia.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.common.exception.CustException;
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.threadlocal.WmThreadLocalUtils;
import com.heima.model.wemedia.dtos.WmNewsPageReqDTO;
import com.heima.model.wemedia.pojos.WmNews;
import com.heima.model.wemedia.pojos.WmUser;
import com.heima.wemedia.mapper.WmNewsMapper;
import com.heima.wemedia.service.WmNewsService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
@Service
public class WmNewsServiceImpl extends ServiceImpl<WmNewsMapper, WmNews> implements WmNewsService {
    @Value("${file.oss.web-site}")
    String webSite;
    /**
     * 查询所有自媒体文章
     * @param dto
     * @return
     */
    @Override
    public ResponseResult findList(WmNewsPageReqDTO dto) {
        //1 参数检查
        if (dto == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        dto.checkParam();
        //2 条件封装执行查询
        LambdaQueryWrapper<WmNews> wrapper = new LambdaQueryWrapper<>();
        // 文章标题模糊查询
        wrapper.like(StringUtils.isNotBlank(dto.getKeyword()),WmNews::getTitle, dto.getKeyword());
        // 频道id
        wrapper.eq(dto.getChannelId() != null,WmNews::getChannelId, dto.getChannelId());
        // 文章状态
        wrapper.eq(dto.getStatus() != null,WmNews::getStatus, dto.getStatus());
        // 发布时间 >= 开始时间
        wrapper.ge(dto.getBeginPubDate() != null,WmNews::getPublishTime, dto.getBeginPubDate());
        // 发布时间 <= 开始时间
        wrapper.le(dto.getEndPubDate()!=null,WmNews::getPublishTime, dto.getBeginPubDate());
        // 当前自媒体人文章
        WmUser user = WmThreadLocalUtils.getUser();
        if (user == null) {
            CustException.cust(AppHttpCodeEnum.NEED_LOGIN);
        }
        wrapper.eq(WmNews::getUserId, user.getId());
        // 按照创建日期倒序
        wrapper.orderByDesc(WmNews::getCreatedTime);
        // 分页条件构建
        Page<WmNews> page = new Page<>(dto.getPage(), dto.getSize());
        //3 执行查询
        IPage<WmNews> pageResult = page(page, wrapper);
        //4 返回封装查询结果
        PageResponseResult result = new PageResponseResult(dto.getPage(), dto.getSize(), pageResult.getTotal());
        result.setData(pageResult.getRecords());
        // 处理文章图片 
        result.setHost(webSite);
       return result;
    }
}

提示:图片处理设置的host为webSite前缀,原因是前端代码已处理图片前缀。

1.6 controller代码实现

在自媒体微服务中定义controller:com.heima.wemedia.controller.WmNewsController

package com.heima.wemedia.controller.v1;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.wemedia.dtos.WmNewsPageReqDTO;
import com.heima.wemedia.service.WmNewsService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Api(value = "自媒体文章管理API",tags = "自媒体文章管理API")
@RestController
@RequestMapping("/api/v1/news")
public class WmNewsController {
    @Autowired
    private WmNewsService wmNewsService;
    @ApiOperation("根据条件查询文章列表")
    @PostMapping("/list")
    public ResponseResult findAll(@RequestBody WmNewsPageReqDTO wmNewsPageReqDto){
        return wmNewsService.findList(wmNewsPageReqDto);
    }
}

1.7 测试

使用postman测试需要先登录,获取token后访问
day04 自媒体文章发布的 - 图3
或打开页面进行测试
其中页面联调,需要修改状态传入后台的值,修改前端以下文件
day04 自媒体文章发布的 - 图4

2 频道列表展示

2.1需求分析

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

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

2.5 测试

重启admin-servicewemedia-gateway 微服务
使用postman测试或者直接打开页面测试即可

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

3.1 需求分析

day04 自媒体文章发布的 - 图6
保存文章,除了需要wm_news表以外,还需要另外两张表
wm_material 素材表
day04 自媒体文章发布的 - 图7
wm_news_material 文章素材关系表
day04 自媒体文章发布的 - 图8

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
wmNews wmNews body true WmNewsDto WmNewsDto
  channelId 频道id true integer(int32)
  content 文章内容 true string
  enable 是否上架 false integer(int32)
  id 文章id true integer(int32) 修改存在
  images 图片列表 false array string
  labels 标签 true string
  publishTime 定时发布时间 true string(date-time)
  reason 原因 false string
  status 文章状态 true integer(int32) 提交为1 草稿为0
  submitedTime 提交时间 false string(date-time)
  title 文章标题 true string
  type 文章类型 false integer(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);
          }
      }
    
    小知识点:
    day04 自媒体文章发布的 - 图9

    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工具测试
day04 自媒体文章发布的 - 图10
打开前端工程联调

  • 保存草稿
  • 提交文章待审核
    • 封面单图、多图、无图
    • 封面图自动测试

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

      4.1 需求分析

      day04 自媒体文章发布的 - 图11
      点击修改的时候,就是根据文章id查询,跳转至编辑页面进行展示
      day04 自媒体文章发布的 - 图12

      4.2 接口定义

      接口描述: 根据id查询文章
      接口地址:/api/v1/news/one/{id}
      请求方式:GET
      请求数据类型:*
      响应数据类型:application/json
      请求参数:
参数名称 参数说明 in 是否必须 数据类型 schema
id 文章主键id path true integer(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 测试

day04 自媒体文章发布的 - 图13
打开页面进行测试
day04 自媒体文章发布的 - 图14

5 自媒体文章-删除 (实战练习)

5.1 需求分析

day04 自媒体文章发布的 - 图15

  • 当文章状态为9(已发布)且已上架则不能删除文章,下架状态可以删除,如果是其他状态可以删除
  • 删除文章之前需要先把素材与文章的关系删除掉

    5.2 接口定义

    接口描述: 根据文章ID删除文章
    接口地址:/api/v1/news/del_news/{id}
    请求方式:GET
    请求数据类型:*
    响应数据类型:application/json
    请求参数:
参数名称 参数说明 in 是否必须 数据类型 schema
id id path true integer(int32)

5.3 业务层

WmNewsService添加删除方法

/**
     * 删除文章
     * @return
     */
    ResponseResult delNews(Integer id);

实现类:

@Override
    public ResponseResult delNews(Integer id) {
        //1.检查参数
        if(id == null){
            CustException.cust(AppHttpCodeEnum.PARAM_INVALID,"文章Id不可缺少");
        }
        //2.获取数据
        WmNews wmNews = getById(id);
        if(wmNews == null){
            CustException.cust(AppHttpCodeEnum.DATA_NOT_EXIST,"文章不存在");
        }
        //3.判断当前文章的状态  status==9  enable == 1
        if(wmNews.getStatus().equals(WmNews.Status.PUBLISHED.getCode())
                && wmNews.getEnable().equals(WemediaConstants.WM_NEWS_UP)){
            CustException.cust(AppHttpCodeEnum.DATA_NOT_EXIST,"文章已发布,不能删除");
        }
        //4.去除素材与文章的关系
        wmNewsMaterialMapper.delete(Wrappers.<WmNewsMaterial>lambdaQuery().eq(WmNewsMaterial::getNewsId,wmNews.getId()));
        //5.删除文章
        removeById(wmNews.getId());
        return ResponseResult.okResult();
    }

5.4 控制层

在WmNewsController中添加删除方法

@ApiOperation(value = "根据id删除自媒体文章")
    @GetMapping("/del_news/{id}")
    public ResponseResult delNews(@PathVariable("id") Integer id) {
        return wmNewsService.delNews(id);
    }

6 自媒体文章-上架、下架 (实战练习)

6.1 需求分析

day04 自媒体文章发布的 - 图16
day04 自媒体文章发布的 - 图17

  • 当前已经发布(状态为9)的文章可以上架(enable = 1),也可以下架(enable = 0)
  • 在上架和下架操作的同时,需要同步app端的文章配置信息,暂时不做,后期讲到审核文章的时候再优化

    6.2 接口定义

    接口描述: 文章上下架操作
    接口地址:/api/v1/news/down_or_up
    请求方式:POST
    请求数据类型:application/json
    响应数据类型:application/json
    请求示例: ```javascript {

    “enable”: 0, “id”: 0

}

**请求参数**:

| 参数名称 | 参数说明 | in | 是否必须 | 数据类型 | schema |
| --- | --- | --- | --- | --- | --- |
| dto | dto | body | true | WmNewsDto | WmNewsDto |
|   enable |  |  | true | integer(int32) |  |
|  id |  |  | true | integer(int32) |  |

<a name="4ce3f3a0"></a>
### 6.3 业务层
在WmNewsService新增方法
```java
/**
     * 上下架
     * @param dto
     * @return
     */
    ResponseResult downOrUp(WmNewsDTO dto);

实现类:

@Override
    public ResponseResult downOrUp(WmNewsDTO dto) {
        //1.检查参数
        if(dto == null || dto.getId() == null){
            CustException.cust(AppHttpCodeEnum.PARAM_INVALID);
        }
        Short enable = dto.getEnable();
        if(enable == null ||
                (!WemediaConstants.WM_NEWS_UP.equals(enable)&&!WemediaConstants.WM_NEWS_DOWN.equals(enable))){
            CustException.cust(AppHttpCodeEnum.PARAM_INVALID,"上下架状态错误");
        }
        //2.查询文章
        WmNews wmNews = getById(dto.getId());
        if(wmNews == null){
            CustException.cust(AppHttpCodeEnum.DATA_NOT_EXIST,"文章不存在");
        }
        //3.判断文章是否发布
        if(!wmNews.getStatus().equals(WmNews.Status.PUBLISHED.getCode())){ 
            CustException.cust(AppHttpCodeEnum.DATA_NOT_EXIST,"当前文章不是发布状态,不能上下架");
        }
        //4.修改文章状态,同步到app端(后期做)TODO
        update(Wrappers.<WmNews>lambdaUpdate().eq(WmNews::getId,dto.getId())
            .set(WmNews::getEnable,dto.getEnable()));
        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }

6.4 控制层

@ApiOperation(value = "自媒体文章上架或下架",notes = "enable 上架: 1 下架: 0")
    @PostMapping("/down_or_up")
    public ResponseResult downOrUp(@RequestBody WmNewsDTO dto) {
        return wmNewsService.downOrUp(dto);
    }

6.5 测试

前端页面测试
面试热点
JDK Stream流的使用方式
发布文章的流程(业务流程,涉及表,具体实现)