黑马头条项目学习--Day3: 自媒体文章发布

news2025/4/6 0:15:44

Day3: 自媒体文章发布

  • Day3: 自媒体文章发布
    • 1) 素材管理-图片上传
      • a) 前期微服务搭建
      • b) 具体实现
    • 2) 素材管理-图片列表
      • a) 接口定义
      • b) 具体实现
    • 3) 素材管理-照片删除/收藏
      • a) 图片删除
        • a1) 接口定义
        • a2) 代码实现
      • b) 收藏与取消
        • b1) 接口定义
        • b2) 代码实现
    • 4) 文章管理-频道列表查询
      • a) 需求分析
      • b) 接口定义
      • c) 代码实现
    • 5) 文章管理-文章列表查询
      • a) 需求分析
      • b) 接口定义
      • c) 代码实现
    • 6) 文章管理-发布文章
      • a) 需求分析
      • b) 实现思路
      • c) 接口定义
      • d) 代码实现
    • 7) 文章管理-查看/删除/上下架
      • a) 查看详细
        • a1) 接口定义
        • a2) 代码实现
      • b) 删除文章
        • b1) 接口定义
        • b2) 代码实现
      • c) 上下架文章
        • c1) 接口定义
        • c2) 代码实现

Day3: 自媒体文章发布

自媒体后台搭建

在这里插入图片描述

在这里插入图片描述

①:资料中找到heima-leadnews-wemedia.zip解压

拷贝到heima-leadnews-service工程下,并指定子模块

执行leadnews-wemedia.sql脚本

添加对应的nacos配置

spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/leadnews_wemedia?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
    username: root
    password: root
# 设置Mapper接口所对应的XML文件位置,如果你在Mapper接口中有自定义方法,需要进行该配置
mybatis-plus:
  mapper-locations: classpath*:mapper/*.xml
  # 设置别名包扫描路径,通过该属性可以给包中的类注册别名
  type-aliases-package: com.heima.model.media.pojos

②:资料中找到heima-leadnews-wemedia-gateway.zip解压

拷贝到heima-leadnews-gateway工程下,并指定子模块

添加对应的nacos配置

spring:
  cloud:
    gateway:
      globalcors:
        cors-configurations:
          '[/**]': # 匹配所有请求
            allowedOrigins: "*" #跨域处理 允许所有的域
            allowedMethods: # 支持的方法
              - GET
              - POST
              - PUT
              - DELETE
      routes:
        # 平台管理
        - id: wemedia
          uri: lb://leadnews-wemedia
          predicates:
            - Path=/wemedia/**
          filters:
            - StripPrefix= 1

1) 素材管理-图片上传

图片上传的页面,首先是展示素材信息,可以点击图片上传,弹窗后可以上传图片

在这里插入图片描述

表结构

在这里插入图片描述

对应实体类:

/**
 * <p>
 * 自媒体图文素材信息表
 * </p>
 */
@Data
@TableName("wm_material")
public class WmMaterial 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("url")
    private String url;

    /**
     * 素材类型
            0 图片
            1 视频
     */
    @TableField("type")
    private Short type;

    /**
     * 是否收藏
     */
    @TableField("is_collection")
    private Short isCollection;

    /**
     * 创建时间
     */
    @TableField("created_time")
    private Date createdTime;

}

实现思路

在这里插入图片描述

a) 前期微服务搭建

网关进行token解析后,把解析后的用户信息存储到header中

//获得token解析后中的用户信息
Object userId = claimsBody.get("id");
//在header中添加新的信息
ServerHttpRequest serverHttpRequest = request.mutate().headers(httpHeaders -> {
    httpHeaders.add("userId", userId + "");
}).build();
//重置header
exchange.mutate().request(serverHttpRequest).build();

自媒体微服务使用拦截器获取到header中的的用户信息,并放入到threadlocal中

在heima-leadnews-utils中新增工具类

package com.heima.utils.thread;

import com.heima.model.wemedia.pojos.WmUser;

public class WmThreadLocalUtil {

    private final static ThreadLocal<WmUser> WM_USER_THREAD_LOCAL = new ThreadLocal<>();

    // 存入线程
    public static void  setUser(WmUser wmUser){
        WM_USER_THREAD_LOCAL.set(wmUser);
    }

    // 从线程中获取
    public static WmUser getUser(){
        return WM_USER_THREAD_LOCAL.get();
    }

    // 清理
    public static void clear(){
        WM_USER_THREAD_LOCAL.remove();
    }
}

在heima-leadnews-wemedia中新增拦截器

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.setUser(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();
    }
}

b) 具体实现

接口定义

说明
接口路径/api/v1/material/upload_picture
请求方式POST
参数MultipartFile
响应结果ResponseResult

自媒体微服务集成heima-file-starter

①:导入heima-file-starter

<dependencies>
    <dependency>
        <groupId>com.heima</groupId>
        <artifactId>heima-file-starter</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
</dependencies>

②:在自媒体微服务的配置中心添加以下配置:

minio:
  accessKey: minio
  secretKey: minio123
  bucket: leadnews
  endpoint: http://192.168.200.130:9000
  readPath: http://192.168.200.130:9000

实现代码

创建WmMaterialController

@RestController
@RequestMapping("/api/v1/material")
public class WmMaterialController {
    
    @Autowired
    private WmMaterialService wmMaterialService;
    
    @PostMapping("/upload_picture")
    public ResponseResult uploadPicture(MultipartFile multipartFile){
        return wmMaterialService.uploadPicture(multipartFile);
    }
}	

创建WmMaterialMapper

@Mapper
public interface WmMaterialMapper extends BaseMapper<WmMaterial> {
}

创建WmMaterialServiceImpl,及其父类接口

@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) {

        try {
            // 1.检测参数
            if (multipartFile == null && multipartFile.getSize() == 0) {
                return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
            }

            // 2.上传图片到minIO中
            String fileName = UUID.randomUUID().toString().replace("-", "");

            // 获取图片的后缀名
            String originalFilename = multipartFile.getOriginalFilename();
            String postfix = originalFilename.substring(originalFilename.lastIndexOf("."));


            String fileId = fileStorageService.uploadImgFile("", fileName + postfix, multipartFile.getInputStream());
            log.info("上传图片到MinIO中,field:{}", fileId);

            // 3.把图片保存到数据库中
            WmMaterial wmMaterial = new WmMaterial();
            wmMaterial.setUserId(WmThreadLocalUtil.getUser().getId());
            wmMaterial.setUrl(fileId);
            wmMaterial.setIsCollection((short) 0);
            wmMaterial.setType((short) 0);
            wmMaterial.setCreatedTime(new Date());
            save(wmMaterial);

            // 4.返回结果
            return ResponseResult.okResult(wmMaterial);

        } catch (IOException e) {
            e.printStackTrace();
            log.error("WmMaterialServiceImpl-上传文件失败");
        }

        return ResponseResult.errorResult(AppHttpCodeEnum.SERVER_ERROR);
    }
}

2) 素材管理-图片列表

a) 接口定义

说明
接口路径/api/v1/material/list
请求方式POST
参数WmMaterialDto
响应结果ResponseResult

b) 具体实现

在WmMaterialController中,定义相关接口与方法

@PostMapping("/list")
public ResponseResult findList(@RequestBody WmMaterialDto dto){
    return wmMaterialService.findList(dto);
}

在WmMaterialServiceImpl中,实现方法,及其父类接口

/**
 * 素材列表查询
 * @param dto
 * @return
 */
@Override
public ResponseResult findList(WmMaterialDto dto) {
    // 1.检测参数
    dto.checkParam();

    // 2.分页查询
    IPage page = new Page(dto.getPage(), dto.getSize());
    LambdaQueryWrapper<WmMaterial> lqw = new LambdaQueryWrapper<>();

    // 2.1.设置查询条件
    // 2.1.1.是否收藏
    if (dto.getIsCollection() != null && dto.getIsCollection() == 1){
        lqw.eq(WmMaterial::getIsCollection, dto.getIsCollection());
    }

    // 2.1.2按照用户查询
    lqw.eq(WmMaterial::getUserId, WmThreadLocalUtil.getUser().getId());

    // 2.1.3按照时间倒序查询
    lqw.orderByDesc(WmMaterial::getCreatedTime);

    page(page, lqw);

    // 3.结果返回
    ResponseResult responseResult = new PageResponseResult(dto.getPage(), dto.getSize(), (int) page.getTotal());
    responseResult.setData(page.getRecords());

    return responseResult;
}

3) 素材管理-照片删除/收藏

a) 图片删除

a1) 接口定义

说明
接口路径/api/v1/material/del_picture/{id}
请求方式GET
参数Integer id
响应结果ResponseResult

返回结果实例:

在这里插入图片描述

a2) 代码实现

1.在WmMaterialController,实现相关接口方法

@GetMapping("/del_picture/{id}")
public ResponseResult deletePictureById(@PathVariable Integer id){
    return wmMaterialService.deletePictureById(id);
}

2.定义WmMaterialServiceImpl,实现相关业务逻辑方法,及其父类接口

@Autowired
private WmNewsMaterialMapper wmNewsMaterialMapper;
/**
 * 删除图片
 * @param id
 * @return
 */
@Override
public ResponseResult deletePictureById(Integer id) {
    // 1.检查参数
    if (id == null) {
        return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
    }

    // 2.检查是否有文章在引用该图片
    LambdaQueryWrapper<WmNewsMaterial> lqw = new LambdaQueryWrapper<>();
    lqw.eq(WmNewsMaterial::getMaterialId, id);
    List<WmNewsMaterial> wmNewsMaterials = wmNewsMaterialMapper.selectList(lqw);

    if (wmNewsMaterials.size() > 0){
        // 照片正在被引用,无法删除
        return ResponseResult.errorResult(AppHttpCodeEnum.MATERIAL_IS_REFERENCE);
    }

    // 3.没有引用,可以删除
    // 3.1.删除minIO中的照片数据
    WmMaterial wmMaterial = getById(id);
    fileStorageService.delete(wmMaterial.getUrl());
    // 3.2.删除数据库中的照片数据
    removeById(id);

    return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
}

b) 收藏与取消

b1) 接口定义

取消收藏

说明
接口路径/api/v1/material/cancel_collect/{id}
请求方式GET
参数Integer id
响应结果ResponseResult

收藏

说明
接口路径/api/v1/material/collect/{id}
请求方式GET
参数Integer id
响应结果ResponseResult

返回结果实例:

在这里插入图片描述

b2) 代码实现

1.在WmMaterialController,实现相关接口方法

@GetMapping("/cancel_collect/{id}")
public ResponseResult cancelCollectPicture(@PathVariable Integer id){
    return wmMaterialService.cancelCollectPicture(id);
}

@GetMapping("/collect/{id}")
public ResponseResult collectPicture(@PathVariable Integer id){
    return wmMaterialService.collectPicture(id);
}

2.定义WmMaterialServiceImpl,实现相关业务逻辑方法,及其父类接口

/**
 * 取消收藏照片
 * @param id
 * @return
 */
@Override
public ResponseResult cancelCollectPicture(Integer id) {
    // 1.检查参数
    if (id == null) {
        return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
    }

    // 2.建立参数模型
    WmMaterial wmMaterial = new WmMaterial();
    wmMaterial.setId(id);
    wmMaterial.setIsCollection(WemediaConstants.CANCEL_COLLECT_MATERIAL);
    super.updateById(wmMaterial);

    return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
}

/**
 * 收藏照片
 * @param id
 * @return
 */
@Override
public ResponseResult collectPicture(Integer id) {
    // 1.检查参数
    if (id == null) {
        return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
    }

    // 2.建立参数模型
    WmMaterial wmMaterial = new WmMaterial();
    wmMaterial.setId(id);
    wmMaterial.setIsCollection(WemediaConstants.COLLECT_MATERIAL);
    super.updateById(wmMaterial);

    return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
}

4) 文章管理-频道列表查询

a) 需求分析

在这里插入图片描述

表结构

wm_channel 频道信息表

在这里插入图片描述

对应实体类:

/**
 * @author Kyle
 * @date 2023/8/10 13:16
 * 频道信息表
 */
@Data
@TableName("wm_channel")
public class WmChannel implements Serializable {

    private static final long serialVersionUID = 1L;

    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;

    /**
     * 频道名称
     */
    @TableField("name")
    private String name;

    /**
     * 频道描述
     */
    @TableField("description")
    private String description;

    /**
     * 是否默认频道
     * 1:默认     true
     * 0:非默认   false
     */
    @TableField("is_default")
    private Boolean isDefault;

    /**
     * 是否启用
     * 1:启用   true
     * 0:禁用   false
     */
    @TableField("status")
    private Boolean status;

    /**
     * 默认排序
     */
    @TableField("ord")
    private Integer ord;

    /**
     * 创建时间
     */
    @TableField("created_time")
    private Date createdTime;
}

b) 接口定义

说明
接口路径/api/v1/channel/channels
请求方式GET
参数
响应结果ResponseResult

c) 代码实现

1.定义controller,实现相关接口方法

@RestController
@RequestMapping("/api/v1/channel")
public class WmChannelController {
    @Autowired
    private WmChannelService wmChannelService;

    @GetMapping("/channels")
    public ResponseResult findAll(){
        return wmChannelService.findAll();
    }
}

2.定义WmChannelMapper

@Mapper
public interface WmChannelMapper extends BaseMapper<WmChannel> {
}

3.定义WmChannelServiceImpl,实现相关业务逻辑方法,及其父类接口

@Service
@Slf4j
@Transactional
public class WmChannelServiceImpl extends ServiceImpl<WmChannelMapper, WmChannel> implements WmChannelService {
    /**
     * 查询所有频道
     * @return
     */
    @Override
    public ResponseResult findAll() {
        return ResponseResult.okResult(list());
    }
}

5) 文章管理-文章列表查询

a) 需求分析

在这里插入图片描述

表结构分析

wm_news 自媒体文章表

在这里插入图片描述

对应实体类:

@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;
        }
    }

}

b) 接口定义

** **说明
接口路径/api/v1/news/list
请求方式POST
参数WmNewsPageReqDto
响应结果ResponseResult

WmNewsPageReqDto :

@Data
public class WmNewsPageReqDto extends PageRequestDto {

    /**
     * 状态
     */
    private Short status;
    /**
     * 开始时间
     */
    private Date beginPubDate;
    /**
     * 结束时间
     */
    private Date endPubDate;
    /**
     * 所属频道ID
     */
    private Integer channelId;
    /**
     * 关键字
     */
    private String keyword;
}

c) 代码实现

1.定义controller,实现相关接口方法

@RestController
@RequestMapping("/api/v1/news")
public class WmNewsController {
    @Autowired
    private WmNewsService wmNewsService;

    @PostMapping("/list")
    public ResponseResult findList(@RequestBody WmNewsPageReqDto dto){
        return wmNewsService.findList(dto);
    }
}

2.定义WmNewsMapper

@Mapper
public interface WmNewsMapper extends BaseMapper<WmNews> {
}

3.定义WmNewsServiceImpl,实现相关业务逻辑方法,及其父类接口

@Service
@Slf4j
@Transactional
public class WmNewsServiceImpl extends ServiceImpl<WmNewsMapper, WmNews> implements WmNewsService {
    /**
     * 条件查询文章列表
     * @param dto
     * @return
     */
    @Override
    public ResponseResult findList(WmNewsPageReqDto dto) {
        // 1.检查参数
        // 分页检查
        dto.checkParam();

        // 2.分页的条件查询
        IPage page = new Page(dto.getPage(), dto.getSize());
        LambdaQueryWrapper<WmNews> lqw = new LambdaQueryWrapper<>();

        // 查询当前登录人的文章
        lqw.eq(WmNews::getUserId, WmThreadLocalUtil.getUser().getId());

        // 状态精确查询
        lqw.eq(dto.getStatus() != null, WmNews::getStatus, dto.getStatus());

        // 频道精确查询
        lqw.eq(dto.getChannelId() != null, WmNews::getChannelId, dto.getChannelId());

        // 时间范围查询
        if (dto.getBeginPubDate() != null && dto.getEndPubDate() != null){
            lqw.between(WmNews::getPublishTime, dto.getBeginPubDate(), dto.getEndPubDate());
        }

        // 关键字模糊查询
        lqw.like(StringUtils.isNotBlank(dto.getKeyword()), WmNews::getTitle, dto.getKeyword());

        // 按照发布时间倒序查询
        lqw.orderByDesc(WmNews::getPublishTime);

        page = page(page, lqw);

        // 3.结果返回
        ResponseResult responseResult = new PageResponseResult(dto.getPage(), dto.getSize(), (int) page.getTotal());
        responseResult.setData(page.getRecords());

        return responseResult;
    }
}

6) 文章管理-发布文章

a) 需求分析

在这里插入图片描述

表结构分析

wm_material 素材表

在这里插入图片描述

wm_news_material 文章素材关系表

在这里插入图片描述

在这里插入图片描述

wm_news_material表对应的实体类:

@Data
@TableName("wm_news_material")
public class WmNewsMaterial implements Serializable {

    private static final long serialVersionUID = 1L;

    /**
     * 主键
     */
    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;

    /**
     * 素材ID
     */
    @TableField("material_id")
    private Integer materialId;

    /**
     * 图文ID
     */
    @TableField("news_id")
    private Integer newsId;

    /**
     * 引用类型
            0 内容引用
            1 主图引用
     */
    @TableField("type")
    private Short type;

    /**
     * 引用排序
     */
    @TableField("ord")
    private Short ord;

}

b) 实现思路

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

在这里插入图片描述

1.前端提交发布或保存为草稿

2.后台判断请求中是否包含了文章id

3.如果不包含id,则为新增

​ 3.1 执行新增文章的操作

​ 3.2 关联文章内容图片与素材的关系

​ 3.3 关联文章封面图片与素材的关系

4.如果包含了id,则为修改请求

​ 4.1 删除该文章与素材的所有关系

​ 4.2 执行修改操作

​ 4.3 关联文章内容图片与素材的关系

​ 4.4 关联文章封面图片与素材的关系

c) 接口定义

说明
接口路径/api/v1/news/submit
请求方式POST
参数WmNewsDto
响应结果ResponseResult

在这里插入图片描述

WmNewsDto 实体类

@Data
public class WmNewsDto {
    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;
     /**
     * 提交时间
     */
    private Date submitedTime; 
     /**
     * 状态 提交为1  草稿为0
     */
    private Short status;
     
     /**
     * 封面图片列表 多张图以逗号隔开
     */
    private List<String> images;
}

d) 代码实现

1.定义WmNewsController,实现相关接口方法

@PostMapping("/submit")
public ResponseResult submitNews(@RequestBody WmNewsDto dto){
    return wmNewsService.submitNews(dto);
}

2.定义WmNewsMaterialMapper,批量保存文章信息关系

@Mapper
public interface WmNewsMaterialMapper extends BaseMapper<WmNewsMaterial> {

    /**
     * 批量保存文章信息关系
     * @param materialIds
     * @param newsId
     * @param type
     */
    void saveRelations(@Param("materialIds") List<Integer> materialIds,@Param("newsId") Integer newsId, @Param("type")Short type);
}

3.创建WmNewsMaterialMapper.xml文件,动态SQL实现 批量保存文章信息关系

<?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="materialIds" index="ord" item="mid" separator=",">
            (#{mid},#{newsId},#{type},#{ord})
        </foreach>
    </insert>

</mapper>

4.定义 Wemedia常量类

package com.heima.common.constants;

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_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_COVER_REFERENCE = 1;
}

5.在WmNewsServiceImpl,实现相关业务逻辑方法,及其父类接口

@Autowired
private WmNewsMaterialMapper wmNewsMaterialMapper;

@Autowired
private WmMaterialMapper wmMaterialMapper;

/**
 * 发布修改文章或保存为草稿
 *
 * @param dto
 * @return
 */
@Override
public ResponseResult submitNews(WmNewsDto dto) {
    // 0.条件判断
    if (dto == null && dto.getContent() == null){
        return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
    }

    // 1.保存或修改文章
    WmNews wmNews = new WmNews();
    BeanUtils.copyProperties(dto, wmNews);
    // 封面图片 list --> String
    if (dto.getImages() != null && dto.getImages().size() > 0){
        String imageStr = StringUtils.join(dto.getImages(), ",");
        wmNews.setImages(imageStr);
    }
    // 如果当前封面类型为自动,传值为-1
    if (dto.getType().equals(WemediaConstants.WM_NEWS_TYPE_AUTO)){
        wmNews.setType(null);
    }

    saveOrUpdateWmNews(wmNews);

    // 2.判断是否为草稿,如果为草稿,结束当前方法
    if (dto.getStatus().equals(WmNews.Status.NORMAL.getCode())){
        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }

    // 3.不是草稿,保存文章内容图片与素材的关系
    // 提取到文章内容中所有图片的信息
    List<String> materials = extractUrlInfo(dto.getContent());
    saveRelativeInfoForContent(materials, wmNews.getId());

    // 4.不是草稿,保存文章封面图片与素材的关系:如果当前布局是自动的,需要匹配封面图片
    saveRelativeInfoForCover(dto, wmNews, materials);

    return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
}

/**
 * 第一个功能:如果当前封面类型为自动,则设置封面类型的数据
 *  匹配规则:
 *      1. 如果内容图片大于等于1,小于3  单图  type 1
 *      2. 如果内容图片大于等于3 多图  type 3
 *      2. 如果内容没有图片 无图  type 0
 * 第二个功能:保存封面图片与素材的关系
 * @param dto
 * @param wmNews
 * @param materials
 */
private void saveRelativeInfoForCover(WmNewsDto dto, WmNews wmNews, List<String> materials) {
    List<String> images = dto.getImages();

    // 1.如果当前封面类型为自动,则设置封面类型的数据
    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);
    }

    // 2.保存封面图片与素材的关系
    if (images != null && images.size() > 0){
        saveRelativeInfo(images, wmNews.getId(), WemediaConstants.WM_COVER_REFERENCE);
    }
}

/**
 * 处理文章内容图片与素材的关系
 * @param materials
 * @param newsId
 */
private void saveRelativeInfoForContent(List<String> materials, Integer newsId) {
    saveRelativeInfo(materials, newsId, WemediaConstants.WM_CONTENT_REFERENCE);
}

/**
 * 保存文章图片与素材的关系到数据库中
 * @param materials
 * @param newsId
 * @param type
 */
private void saveRelativeInfo(List<String> materials, Integer newsId, Short type) {
    if (materials == null && materials.isEmpty()){
        throw new CustomException(AppHttpCodeEnum.PARAM_INVALID);
    }

    // 通过图片的url查询素材的id
    LambdaQueryWrapper<WmMaterial> lqw = new LambdaQueryWrapper<>();
    lqw.in(WmMaterial::getUrl, materials);
    List<WmMaterial> dbMaterials = wmMaterialMapper.selectList(lqw);

    // 判断素材是否有效
    if (dbMaterials == null || dbMaterials.size() == 0){
        // 手动抛出异常
        throw new CustomException(AppHttpCodeEnum.MATERIAL_REFERENCE_FAIL);
    }

    // 传递参数的数量 与 数据库查询的数量 不匹配
    if (materials.size() != dbMaterials.size()){
        // 手动抛出异常
        throw new CustomException(AppHttpCodeEnum.MATERIAL_REFERENCE_FAIL);
    }

    List<Integer> idList = dbMaterials.stream().map(WmMaterial::getId).collect(Collectors.toList());

    // 批量保存
    wmNewsMaterialMapper.saveRelations(idList, newsId, type);
}

/**
 * 提前文章内容中的图片信息
 * @param content
 * @return
 */
private List<String> extractUrlInfo(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;
}

/**
 * 保存或修改文章
 * @param wmNews
 */
private void saveOrUpdateWmNews(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 {
        // 修改文章
        // 删除文章图片与素材的关系
        LambdaQueryWrapper<WmNewsMaterial> lqw = new LambdaQueryWrapper<>();
        lqw.eq(WmNewsMaterial::getNewsId, wmNews.getId());
        wmNewsMaterialMapper.delete(lqw);

        updateById(wmNews);
    }
}

7) 文章管理-查看/删除/上下架

a) 查看详细

a1) 接口定义

接口描述

说明
接口路径/api/v1/news/one/{id}
请求方式GET
参数文章id
响应结果ResponseResult

返回结果实例:

在这里插入图片描述

a2) 代码实现

1.定义WmNewsController,实现相关接口方法

@GetMapping("/one/{id}")
public ResponseResult selectNewsById(@PathVariable Integer id){
    return wmNewsService.selectNewsById(id);
}

2.在WmNewsServiceImpl,实现相关业务逻辑方法,及其父类接口

/**
 * 查看文章详细
 * @param id
 * @return
 */
@Override
public ResponseResult selectNewsById(Integer id) {
    // 检查参数
    if (id == null) {
        return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
    }

    WmNews wmNews = getById(id);
    return ResponseResult.okResult(wmNews);
}

b) 删除文章

b1) 接口定义

接口描述

说明
接口路径/api/v1/news/del_news/{id}
请求方式GET
参数文章id
响应结果ResponseResult

在这里插入图片描述

b2) 代码实现

1.定义WmNewsController,实现相关接口方法

@GetMapping("/del_news/{id}}")
public ResponseResult deleteNewsById(@PathVariable Integer id){
    return wmNewsService.deleteNewsById(id);
}

2.在WmNewsServiceImpl,实现相关业务逻辑方法,及其父类接口

/**
 * 根据id删除文章
 * @param id
 * @return
 */
@Override
public ResponseResult deleteNewsById(Integer id) {
    // 1.检查参数
    if (id == null) {
        return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
    }

    WmNews wmNews = getById(id);
    // 2.判断文章是否存在
    if (wmNews == null) {
        return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
    }

    // 3.判断文章是否已发布
    if (wmNews.getStatus().equals(WmNews.Status.PUBLISHED)){
        // 已发布文章,不能删除
        return ResponseResult.errorResult(AppHttpCodeEnum.MATERIAL_IS_PUBLISHED);
    }

    // 4.可以删除文章
    // 4.1.删除wm_news数据库
    removeById(id);
    // 4.2.删除wm_news_material数据库
    LambdaQueryWrapper<WmNewsMaterial> lqw = new LambdaQueryWrapper<>();
    lqw.eq(WmNewsMaterial::getNewsId, id);
    wmNewsMaterialMapper.delete(lqw);

    return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
}

c) 上下架文章

c1) 接口定义

接口描述

说明
接口路径/api/v1/news/down_or_up
请求方式POST
参数WmNewsDto
响应结果ResponseResult

WmNewsDto

在这里插入图片描述

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DyDxyDFZ-1691677688774)(C:\Users\captaindeng\AppData\Roaming\Typora\typora-user-images\image-20230810172336769.png)]

c2) 代码实现

1.定义WmNewsController,实现相关接口方法

@PostMapping("/down_or_up")
public ResponseResult newsDownOrUp(@RequestBody WmNewsDto dto){
    return wmNewsService.newsDownOrUp(dto);
}

2.在WmNewsServiceImpl,实现相关业务逻辑方法,及其父类接口

/**
 * 上下架文章
 * @param dto
 * @return
 */
@Override
public ResponseResult newsDownOrUp(WmNewsDto dto) {
    // 1.检查参数
    if (dto == null) {
        return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
    }

    // 2.判断文章是否存在
    WmNews wmNews = getById(dto.getId());
    if (wmNews == null) {
        return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
    }

    // 3.文章存在,判断是否是发布状态:正在发布状态,不能操作上下架
    if (wmNews.getStatus().equals(WmNews.Status.PUBLISHED)){
        return ResponseResult.errorResult(AppHttpCodeEnum.MATERIAL_IS_PUBLISHED);
    }

    // 4.可以操作上下架
    wmNews.setEnable(dto.getEnable());
    updateById(wmNews);

    return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/861266.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

前端面试自我介绍

前端面试自我介绍精选篇1 各位面试官大家好&#xff0c;我叫__&#xff0c;就读于__大学__学&#xff0c;大学本科学历&#xff0c;我的求职意向是与金融专业相关的职位&#xff0c;本人拥有较强的学习能力&#xff0c;能快速适应工作环境&#xff0c;兴趣爱好广泛&#xff0c…

nginx文件共享、服务状态和location模块的配置介绍

一.文件共享功能 1.清空html目录下文件并新建你要共享的文件 2.修改nginx.conf文件&#xff0c;开启autoindex功能 3.测试 二.状态模块 1.修改nginx.conf文件 2.测试 &#xff08;1&#xff09;使用刚才定义的IP/nginx_status进行访问 &#xff08;2&#xff09;status参…

Qt应用开发(基础篇)——工具箱 QToolBox

一、前言 QToolBox类继承于QFrame&#xff0c;QFrame继承于QWidget&#xff0c;是Qt常用的基础工具部件。 框架类QFrame介绍 QToolBox工具箱类提供了一列选项卡窗口&#xff0c;当前项显示在当前选项卡下面&#xff0c;适用于分类浏览、内容展示、操作指引这一类的使用场景。 二…

点的复合运动

一、问题所在 对于复合运动中的牵连运动一直很蒙&#xff0c;之前做题的时候都是靠经验&#xff0c;比如圆盘选择圆心做动系原点、连杆选择牵连点做原点等&#xff0c;今天重新整理了一下。 牵连运动的定义是动系相对于定系的运动&#xff0c;这个定义就很模糊。如果是指动系…

MySQL之 show profile 相关总结

MySQL之 show profile 相关总结 MySQL官网show profile介绍&#xff1a;https://dev.mysql.com/doc/refman/8.0/en/show-profile.html 1. 简介 show profile 和 show profiles 命令用于展示SQL语句的资源使用情况&#xff0c;包括CPU的使用&#xff0c;CPU上下文切换&#xf…

【Linux】认识“协议“序列化和反序列化

目录 前言 1 应用层 2 在谈协议 3 序列化和反序列化 4 网络版计算器 4.1 指定协议 request结构体 response结构体 4.2 服务端编写 4.3 客户端的编写 5 Json for C 的序列化和反序列化使用样例 前言 之前的socket编程&#xff0c;都是在通过系统调用层面&#xff0c;…

自制电子农历

水文大师上线。今天一水电子农历牌。 首先讲讲电子配件&#xff0c;一来是电子小屏幕的选择&#xff0c;遇到文字比较多的&#xff0c;尤其是汉字&#xff0c;不要选传统那款128x64 oled&#xff0c;绝对放不下(找到最牛的超小免费字体至少要在8pixel以上才能看清楚)。我选了i…

内核裁剪与驱动编译

linux设备驱动以内核模块的形式出现&#xff0c;编写linux内核模块编程是学习linux设备驱动的先决条件。 在编译linux内核之前要先配置linux内核。每个板子都有其对应的默认配置文件&#xff0c;这些默认配置文件保存在arch/arm/configs 目录中。比如xilinx_zynq_defconfig作为…

图扑软件入选信通院《高质量数字化转型产品及服务全景图 (2023)》

7 月 27 日&#xff0c;由中国信通院主办的“2023 数字生态发展大会”暨中国信通院“铸基计划”年中会议在北京召开。本次大会重磅发布了《高质量数字化转型产品及服务全景图&#xff08;2023 上半年度&#xff09;》。图扑软件凭借自研 HT for Web 数字孪生可视化产品成功入选…

(C++)继承

目录 1.继承的概念及定义 1.1继承的概念 1.2继承定义 1.2.1定义格式 1.2.2继承方式和访问限定符 1.2.3继承基类成员访问方式的变化 2.基类和派生类对象赋值转换 3.继承中的作用域 4.派生类的默认成员函数 5.继承与友元 6.继承与静态成员 7.复杂的菱形继承及菱形虚拟…

OSPF无法建立领居的原因有哪些(第三十五课)

1 配置OSPF 1.1 思路 1&#xff0c;配置IP地址 2&#xff0c;配置OSPF 配置进程号 route-id进入区域宣告网段 配置IP地址 R1路由表 ------------------------------------------------------------------------------ Routing Tables: Public Destinations : 10 …

【LeetCode】55. 跳跃游戏 - 贪婪算法

目录标题 2023-8-10 16:27:05 55. 跳跃游戏 2023-8-10 16:27:05 class Solution {public boolean canJump(int[] nums) {int n nums.length;int arrivalLocation 0;for (int i 0; i < n; i) {if (i < arrivalLocation) {arrivalLocation Math.max(arrivalLocation,…

matplotlib 笔记 注释annotate

在图中的特定位置添加文本注释、箭头和连接线&#xff0c;以便更清晰地解释图形中的数据或信息 主要参数 text文本内容xy箭头指向的目标点的坐标xytext注释文本的坐标arrowprops 一个字典&#xff0c;指定注释箭头的属性&#xff0c;如颜色、箭头样式等 没有arrowprops的时候…

CentOS7安装MySQL8(RPM方式)

第一步&#xff1a;解压 tar -xvf mysql-8.0.34-1.el7.x86_64.rpm-bundle.tar -C /usr/local/java/mysql 第二步&#xff1a;按顺序安装rpm包 # rpm -ivh mysql-community-common-8.0.34-1.el7.x86_64.rpm# rpm -ivh mysql-community-client-plugins-8.0.34-1.el7.x86_64.rpm…

layui 集成 ztree异步加载

首先&#xff0c;layui环境搭建&#xff0c;ztree环境引入 ztree的js和css都要引入&#xff0c;我这里暂时用的是core包> 静态&#xff0c;一句话就够了 <!-- 左侧菜单树形组件 --><div class"layui-col-md3"><div class"layui-footer "…

Leetcode 977. 有序数组的平方

题目&#xff1a; Leetcode 977. 有序数组的平方 描述&#xff1a; 给你一个按 非递减顺序 排序的整数数组 nums&#xff0c;返回 每个数字的平方 组成的新数组&#xff0c;要求也按 非递减顺序 排序 思路&#xff1a; 双指针法 数组其实是有序的&#xff0c; 只不过负数平方之…

Gradio入门,并搭个鸡兔同笼问题小应用,附源码(MindOpt)

应用链接&#xff1a; https://979427749bc9ceec34.gradio.live 是公开访问链接&#xff0c;3天有效。在modelscope中的创空间怎么长期发布我还在研究。后面补上。 应用图如下&#xff0c;源代码见正文。 知道Gradio AI大模型快速生成应用的工具&#xff0c; 在Huggingface …

使用 Docker 部署 canal 服务实现MySQL和ES实时同步

参考 ClientAdapter: Canal的Adapter配置项目 Sync ES&#xff1a;Canal的Adapter中ES同步的配置项 使用 Docker 部署 canal 服务 docker canal-server canal-adapter mysql Canal&#xff08;基于Docker同步mysql数据到elasticsearch&#xff09; Canal部署过程中的错误 0. 环…

dns的负载分配是什么

DNS 负载分配是使用 DNS 系统对传入的网络流量进行分配的一种技术。这可以是基于多种策略来分配的&#xff0c;从简单的轮询到更复杂的基于地理位置或服务器健康状况的分配。下面是 DNS 负载分配的几种常见形式&#xff1a; 轮询&#xff08;Round Robin&#xff09;&#xff1…

【LeetCode】102. 二叉树的层序遍历、107. 二叉树的层序遍历 II

作者&#xff1a;小卢 专栏&#xff1a;《Leetcode》 喜欢的话&#xff1a;世间因为少年的挺身而出&#xff0c;而更加瑰丽。 ——《人民日报》 102. 二叉树的层序遍历 102. 二叉树的层序遍历 给你二叉树的根节点 root &#xff0c;返回其节…