当前位置: 首页 > news >正文

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

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.Driverurl: jdbc:mysql://localhost:3306/leadnews_wemedia?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTCusername: rootpassword: 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- DELETEroutes:# 平台管理- id: wemediauri: lb://leadnews-wemediapredicates:- 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*/@Overridepublic 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*/@Overridepublic 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: miniosecretKey: minio123bucket: leadnewsendpoint: http://192.168.200.130:9000readPath: http://192.168.200.130:9000

实现代码

创建WmMaterialController

@RestController
@RequestMapping("/api/v1/material")
public class WmMaterialController {@Autowiredprivate 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 {@Autowiredprivate FileStorageService fileStorageService;/*** 图片上传* @param multipartFile* @return*/@Overridepublic 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 {@Autowiredprivate 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*/@Overridepublic 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 {@Autowiredprivate 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*/@Overridepublic 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 --> Stringif (dto.getImages() != null && dto.getImages().size() > 0){String imageStr = StringUtils.join(dto.getImages(), ",");wmNews.setImages(imageStr);}// 如果当前封面类型为自动,传值为-1if (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查询素材的idLambdaQueryWrapper<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);
}

相关文章:

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

Day3: 自媒体文章发布 Day3: 自媒体文章发布1) 素材管理-图片上传a) 前期微服务搭建b) 具体实现 2) 素材管理-图片列表a) 接口定义b) 具体实现 3) 素材管理-照片删除/收藏a) 图片删除a1) 接口定义a2) 代码实现 b) 收藏与取消b1) 接口定义b2) 代码实现 4) 文章管理-频道列表查询…...

使用frp实现内网穿透

1、介绍 ​ 当我们想把内网的一些资源暴露在公网上时&#xff0c;可以使用内网穿透功能。比如公司的内网服务器&#xff0c;部署了平时需要开发的项目&#xff0c;但是回到家中无法访问&#xff0c;就可以使用内网穿透&#xff0c;将公司内网的接口映射到一台公网的服务器上&a…...

安装 opendr 踩坑记

复现早期的优化算法需要用到opendr&#xff0c;踩坑一天记录&#xff01;&#xff01;&#xff01; 测试是否成功安装的命令 >>> import opendr >>> opendr.demo("texture")失败案例 python 3.8.15 numpy1.23.0 mayavi4.8.1 chumpy0.70 cpython…...

各地区-各行业法人单位、区划数63个指标(2010-2022年)

一、数据介绍 数据名称&#xff1a;各地区-各行业法人单位、区划数63个指标 数据年份&#xff1a;2010-2022年&#xff08;法人单位至2021年&#xff09; 数据样本&#xff1a;404条 数据来源&#xff1a;自我整理 二、指标说明 行政区划代码 地区 长江经济带 经度 纬…...

W5500-EVB-PICO作为TCP Client 进行数据回环测试(五)

前言 上一章我们用W5500-EVB-PICO开发板通过DNS解析www.baidu.com&#xff08;百度域名&#xff09;成功得到其IP地址&#xff0c;那么本章我们将用我们的开发板作为客户端去连接服务器&#xff0c;并做数据回环测试&#xff1a;收到服务器发送的数据&#xff0c;并回传给服务器…...

web前端面试--递归(斐波那契数列)

web面试题 本人是一个web前端开发工程师&#xff0c;主要是vue框架&#xff0c;整理了一些面试题&#xff0c;今后也会一直更新&#xff0c;有好题目的同学欢迎评论区分享 ;-&#xff09; web面试题专栏&#xff1a;点击此处 文章目录 web面试题定义源码测试示例 之前去笔试&…...

Vue3 Props组件简单应用(父组件获取子组件数据)

去官网学习→Props | Vue.js 运行示例&#xff1a; 代码&#xff1a;App.vue <template><img alt"Vue logo" src"./assets/logo.png"><h2>Vue Props数据传递</h2><h4>子组件中的数据&#xff1a;{{ content }}</h4>…...

Mybatis查询

返回实体类&#xff0c;必须指定返回类型&#xff0c; resultType不能省略&#xff0c;并且数据库字段名与实体类不一致会填充NULL&#xff0c;实体类我们一般都是驼峰&#xff0c;数据库字段一般都是下划线&#xff0c;所以在查询的时候可以起别名解决,属性填充本质上调用的是…...

如何让ES低成本、高性能?滴滴落地ZSTD压缩算法的实践分享

前文分别介绍了滴滴自研的ES强一致性多活是如何实现的、以及如何提升ES的性能潜力。由于滴滴ES日志场景每天写入量在5PB-10PB量级&#xff0c;写入压力和业务成本压力大&#xff0c;为了提升ES的写入性能&#xff0c;我们让ES支持ZSTD压缩算法&#xff0c;本篇文章详细展开滴滴…...

[数据集][目标检测]PCB板缺陷目标检测数据集VOC格式693张6类别

数据集格式&#xff1a;Pascal VOC格式(不包含分割路径的txt文件和yolo格式的txt文件&#xff0c;仅仅包含jpg图片和对应的xml) 图片数量(jpg文件个数)&#xff1a;693 标注数量(xml文件个数)&#xff1a;693 标注类别数&#xff1a;6 标注类别名称:["missing_hole",…...

Linux 安装中文输入法

在linux安装ibus输入法 在linux安装ibus输入法 加入开机自启动 运行以下命令以编辑IBus配置文件&#xff1a;如果没有该文件&#xff0c;则可以创建一个新文件。 nano ~/.xprofile将以下内容添加到文件中&#xff1a;这些命令将设置相应的环境变量并启动IBus守护进程。 ex…...

redisson

redisson 使用 1&#xff0c;导入依赖 <dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.12.5</version> </dependency>2&#xff0c;创建配置类 package com.woniu.config;impor…...

源码分析——HashMap(JDK1.8)源码+底层数据结构分析

文章目录 HashMap 简介底层数据结构分析JDK1.8之前JDK1.8之后 HashMap源码分析构造方法put方法get方法resize方法 HashMap常用方法测试 HashMap 简介 HashMap 主要用来存放键值对&#xff0c;它基于哈希表的Map接口实现&#xff0c;是常用的Java集合之一。 JDK1.8 之前 HashM…...

企业举办活动邀请媒体的意义和重要性

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 企业举办活动并邀请媒体的意义和重要性是多方面的&#xff0c;主要有以下一些&#xff1a; 1. 品牌曝光与宣传&#xff1a;邀请媒体参与企业活动可以提高企业的品牌曝光度。媒体报道能够…...

从零开始学python(十六)爬虫集群部署

前言 今天讲述Python框架源码专题最后一个部分&#xff0c;爬虫集群部署&#xff0c;前面更新了十五个从零开始学python的系列文章&#xff0c;分别是&#xff1a; 1.编程语法必修篇 2.网络编程篇 3.多线程/多进程/协程篇 4.MySQL数据库篇 5.Redis数据库篇 6.MongoDB数据库篇 …...

flutter

1.dart语言基础 数据类型 //fluttenum a 10;double b 10.0;int x 10;num c 10.0;//字符串拼接方式。和kotlin的是一样的。 也可以和java中一样做拼接。String testString "aaaaaaaaaaaaaaaaaaaaaaaaaaaa";String bbbbb "aaaaaaaaaaaaaaccccc";S…...

iOS 开发-编译第三方库 openssl及curl

1、前提 iOS编译库需要三个架构,arm64,arm64e,x86_64&#xff0c;其中x86_64为模拟器所需 iOS编译库需要下载xcode及对应的command line tool&#xff08;执行命令时可以自动下载&#xff09;&#xff0c;下载失败需要去官网搜索下载 2、openssl 参考iOS如何编译OpenSSL静态…...

运维监控学习笔记1

1、监控对象&#xff1a; 1、监控对象的理解&#xff1b;CPU是怎么工作的&#xff1b; 2、监控对象的指标&#xff1a;CPU使用率&#xff1b;上下文切换&#xff1b; 3、确定性能基准线&#xff1a;CPU负载多少才算高&#xff1b; 2、监控范围&#xff1a; 1、硬件监控&#x…...

由于找不到vcruntime140.dll,无法继续执行代码,三种修复方法

为什么我们很多人都遇到过打开电脑软件时候突然电脑就提示找不到vcruntime140.dll&#xff0c;或许vcruntime140.dll丢失&#xff0c;那么vcruntime140.dll到底是什么&#xff1f;为什么会丢失&#xff0c;丢失了要怎么解决修复呢&#xff1f;下面小编都会一一介绍给大家&#…...

【FPGA零基础学习之旅#10】按键消抖模块设计与验证(一段式状态机实现)

&#x1f389;欢迎来到FPGA专栏~按键消抖模块设计与验证 ☆* o(≧▽≦)o *☆嗨~我是小夏与酒&#x1f379; ✨博客主页&#xff1a;小夏与酒的博客 &#x1f388;该系列文章专栏&#xff1a;FPGA学习之旅 文章作者技术和水平有限&#xff0c;如果文中出现错误&#xff0c;希望大…...

变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析

一、变量声明设计&#xff1a;let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性&#xff0c;这种设计体现了语言的核心哲学。以下是深度解析&#xff1a; 1.1 设计理念剖析 安全优先原则&#xff1a;默认不可变强制开发者明确声明意图 let x 5; …...

[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解

突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 ​安全措施依赖问题​ GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...

ubuntu搭建nfs服务centos挂载访问

在Ubuntu上设置NFS服务器 在Ubuntu上&#xff0c;你可以使用apt包管理器来安装NFS服务器。打开终端并运行&#xff1a; sudo apt update sudo apt install nfs-kernel-server创建共享目录 创建一个目录用于共享&#xff0c;例如/shared&#xff1a; sudo mkdir /shared sud…...

rknn优化教程(二)

文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK&#xff0c;开始写第二篇的内容了。这篇博客主要能写一下&#xff1a; 如何给一些三方库按照xmake方式进行封装&#xff0c;供调用如何按…...

基于Flask实现的医疗保险欺诈识别监测模型

基于Flask实现的医疗保险欺诈识别监测模型 项目截图 项目简介 社会医疗保险是国家通过立法形式强制实施&#xff0c;由雇主和个人按一定比例缴纳保险费&#xff0c;建立社会医疗保险基金&#xff0c;支付雇员医疗费用的一种医疗保险制度&#xff0c; 它是促进社会文明和进步的…...

基础测试工具使用经验

背景 vtune&#xff0c;perf, nsight system等基础测试工具&#xff0c;都是用过的&#xff0c;但是没有记录&#xff0c;都逐渐忘了。所以写这篇博客总结记录一下&#xff0c;只要以后发现新的用法&#xff0c;就记得来编辑补充一下 perf 比较基础的用法&#xff1a; 先改这…...

《通信之道——从微积分到 5G》读书总结

第1章 绪 论 1.1 这是一本什么样的书 通信技术&#xff0c;说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号&#xff08;调制&#xff09; 把信息从信号中抽取出来&am…...

WordPress插件:AI多语言写作与智能配图、免费AI模型、SEO文章生成

厌倦手动写WordPress文章&#xff1f;AI自动生成&#xff0c;效率提升10倍&#xff01; 支持多语言、自动配图、定时发布&#xff0c;让内容创作更轻松&#xff01; AI内容生成 → 不想每天写文章&#xff1f;AI一键生成高质量内容&#xff01;多语言支持 → 跨境电商必备&am…...

SpringCloudGateway 自定义局部过滤器

场景&#xff1a; 将所有请求转化为同一路径请求&#xff08;方便穿网配置&#xff09;在请求头内标识原来路径&#xff0c;然后在将请求分发给不同服务 AllToOneGatewayFilterFactory import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; impor…...

Redis数据倾斜问题解决

Redis 数据倾斜问题解析与解决方案 什么是 Redis 数据倾斜 Redis 数据倾斜指的是在 Redis 集群中&#xff0c;部分节点存储的数据量或访问量远高于其他节点&#xff0c;导致这些节点负载过高&#xff0c;影响整体性能。 数据倾斜的主要表现 部分节点内存使用率远高于其他节…...