D3
内容介绍
jdk8新特性,stream流,lambda表达式
自媒体前后端搭建
步骤
sql——
实体——
微服务拷贝,配置nacos——
spring:datasource:driver-class-name: com.mysql.jdbc.Driverurl: jdbc:mysql://192.168.233.136:3306/leadnews_wemedia?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTCusername: rootpassword: 123456
# 设置Mapper接口所对应的XML文件位置,如果你在Mapper接口中有自定义方法,需要进行该配置
mybatis-plus:mapper-locations: classpath*:mapper/*.xml# 设置别名包扫描路径,通过该属性可以给包中的类注册别名type-aliases-package: com.heima.model.media.pojos
自媒体微服务网关,nacos注册服务
spring:cloud:gateway:globalcors:cors-configurations:'[/**]': # 匹配所有请求allowedOrigins: "*" #跨域处理 允许所有的域allowedMethods: # 支持的方法- GET- POST- PUT- DELETEroutes:# 平台管理- id: wemediauri: lb://leadnews-wemediapredicates:- Path=/wemedia/**filters:- StripPrefix= 1
启动自媒体微服务也网关微服务,进行测试
成功启动
前端页面
nginx虚拟主机server 反代到服务器,和跳转静态资源 8802
下载前端压缩包放入文件夹
配置文件
upstream heima-wemedia-gateway{server localhost:51602;
}server {listen 8802;location / {root E:\区块链比赛\区块链赛前B站笔记++++\Spring系列\培训班项目文件\黑马头条\D1\资料\D1\前端项目\wemedia-web\wemedia-web;index index.html;}location ~/wemedia/MEDIA/(.*) {proxy_pass http://heima-wemedia-gateway/$1;proxy_set_header HOST $host; # 不改变源请求头的值proxy_pass_request_body on; #开启获取请求体proxy_pass_request_headers on; #开启获取请求头proxy_set_header X-Real-IP $remote_addr; # 记录真实发出请求的客户端IPproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; #记录代理信息}
}
改完记得 nginx -s reload
http://localhost:8802/ 访问测试
素材管理
图片上传
效果图
表结构
拷贝实体类
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 java.io.Serializable;
import java.util.Date;/*** <p>* 自媒体图文素材信息表* </p>** @author itheima*/
@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;}
思路
前端请求——》网关拦截获取token存header——》拦截器将header信息存thread线程——》minio存文件拿url——》db记录url
为什么不在网关存threa? 不同微服务的上下文是隔离的 gateway和wemedia
编码思路
-
自媒体网关服务 从token里获取claimbody的id值,存到header
-
发送请求携带header到wemedia服务,设置拦截器,获取header存线程
- 静态方法生成线程,设置set,clear,get 线程对象的静态方法,外部拦截器调用
- 存完线程当拦截器的请求执行完毕也就是postHandle方法被触发时要清除线程,否则一个请求一个线程,100个用户每个用户发100个请求,100*100内存溢出
- 拦截器要在webmvc里注册,拦截所有请求
-
上传图片接口
网关服务AuthorizeFilter过滤器
//获取用户id
Object userId = claimsBody.get("id");
//将用户id放到请求头中
ServerHttpRequest httpRequest = request.mutate().header("userId", userId.toString()).build();
// 1. 更新请求信息
// 在处理请求时,您可能需要在请求头中添加或修改某些信息(例如用户 ID)。
// 通过重置请求,您确保后续的处理逻辑(如过滤器、拦截器或控制器)能够获取到这些更新后的请求头。
exchange.mutate().request(httpRequest).build();
Utils模块新增工具类
新建thread包
package com.heima.wemedia.interceptor;import com.heima.model.wemedia.pojos.WmUser;
import com.heima.utils.thread.WmThreadLocalUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Optional;@Slf4j
public class WmTokenInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//得到header中的信息String userId = request.getHeader("userId");Optional<String> optional = Optional.ofNullable(userId);if(optional.isPresent()){//把用户id存入threadloacl中WmUser wmUser = new WmUser();wmUser.setId(Integer.valueOf(userId));WmThreadLocalUtils.setUser(wmUser);log.info("wmTokenFilter设置用户信息到threadlocal中...");}return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {log.info("清理threadlocal...");WmThreadLocalUtil.clear();}
}
新建interceptor包
package com.heima.wemedia.interceptor;import com.heima.model.wemedia.pojos.WmUser;
import com.heima.utils.thread.WmThreadLocalUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Optional;@Slf4j
public class WmTokenInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//得到header中的信息String userId = request.getHeader("userId");if(userId!=null){//把用户id存入threadloacl中WmUser wmUser = new WmUser();wmUser.setId(Integer.valueOf(userId));WmThreadLocalUtil.setUser(wmUser);log.info("wmTokenFilter设置用户信息到threadlocal中...");}return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {log.info("清理threadlocal...");WmThreadLocalUtil.clear();}
}
添加拦截器在webconfig
package com.heima.wemedia.config;import com.heima.wemedia.interceptor.WmTokenInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration
public class WebMvcConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new WmTokenInterceptor()).addPathPatterns("/**");}
}
上传接口+mapper+service
-
前端传递过来的mutilparfile参数名不能修改为file或其他,不然报凑
-
成功则回显url图片地址给img src get渲染访问
引入minio 依赖
wemedia服务
<dependencies><dependency><groupId>com.heima</groupId><artifactId>heima-file-starter</artifactId><version>1.0-SNAPSHOT</version></dependency>
</dependencies>
nacos登记minio的配置文件
minio:accessKey: miniosecretKey: minio123bucket: leadnewsendpoint: http://192.168.233.136:9000readPath: http://192.168.233.136:9000
mapper
package com.heima.wemedia.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.heima.model.wemedia.pojos.WmMaterial;
import org.apache.ibatis.annotations.Mapper;@Mapper
public interface WmMaterialMapper extends BaseMapper<WmMaterial> {
}
service
package com.heima.wemedia.service;public interface WmMaterialService extends IService<WmMaterial> {/*** 图片上传* @param multipartFile* @return*/public ResponseResult uploadPicture(MultipartFile multipartFile);}
impl
- 检查文件大小
- 上传minio(文件名字重写)
- 保存数据库
package com.heima.wemedia.service.impl;import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.file.service.FileStorageService;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.wemedia.pojos.WmMaterial;
import com.heima.utils.thread.WmThreadLocalUtil;
import com.heima.wemedia.mapper.WmMaterialMapper;
import com.heima.wemedia.service.WmMaterialService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;import java.io.IOException;
import java.util.Date;
import java.util.UUID;@Slf4j
@Service
@Transactional
public class WmMaterialServiceImpl extends ServiceImpl<WmMaterialMapper, WmMaterial> implements WmMaterialService {@Autowiredprivate FileStorageService fileStorageService;/*** 图片上传* @param multipartFile* @return*/@Overridepublic ResponseResult uploadPicture(MultipartFile multipartFile) {//1.检查参数if(multipartFile == null || multipartFile.getSize() == 0){return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);}//2.上传图片到minIO中String fileName = UUID.randomUUID().toString().replace("-", "");//aa.jpgString originalFilename = multipartFile.getOriginalFilename();String postfix = originalFilename.substring(originalFilename.lastIndexOf("."));String fileId = null;try {fileId = fileStorageService.uploadImgFile("", fileName + postfix, multipartFile.getInputStream());log.info("上传图片到MinIO中,fileId:{}",fileId);} catch (IOException e) {e.printStackTrace();log.error("WmMaterialServiceImpl-上传文件失败");}//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);}}
接口
package com.heima.wemedia.controller.v1;import com.heima.model.common.dtos.ResponseResult;
import com.heima.wemedia.service.WmMaterialService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;@RestController
@RequestMapping("/api/v1/material")
public class WmMaterialController {@Autowiredprivate WmMaterialService wmMaterialService;@PostMapping("/upload_picture")public ResponseResult uploadPicture(MultipartFile multipartFile){return wmMaterialService.uploadPicture(multipartFile);}}
重启wemedia服务
启动前端项目进行测试
http://localhost:8802/
图片查询
效果图
接口定义
实体
文件自带的,pageDto,后续用来给继承然后传达分页信息 list,默认值为page(1) size(10)
model下的wemedia的dto增加
@Data
public class WmMaterialDto extends PageRequestDto {/*** 1 收藏* 0 未收藏*/private Short isCollection;
}
实现
- 核对前端传来的分页参数
- mp分页查询
- 返回结果
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.file.service.FileStorageService;
import com.heima.model.common.dtos.PageRequestDto;
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.wemedia.dtos.WmMaterialDto;
import com.heima.model.wemedia.pojos.WmMaterial;
import com.heima.utils.thread.WmThreadLocalUtil;
import com.heima.wemedia.mapper.WmMaterialMapper;
import com.heima.wemedia.service.WmMaterialService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;import java.io.IOException;
import java.util.Date;
import java.util.UUID;@Slf4j
@Service
@Transactional
public class WmMaterialServiceImpl extends ServiceImpl<WmMaterialMapper, WmMaterial> implements WmMaterialService {@Autowiredprivate FileStorageService fileStorageService;/*** 图片上传** @param multipartFile* @return*/@Overridepublic ResponseResult uploadPicture(MultipartFile multipartFile) {// 1.检查参数if (multipartFile == null || multipartFile.getSize() == 0) {return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);}// 2.上传图片到minIO中String fileName = UUID.randomUUID().toString().replace("-", "");// aa.jpgString originalFilename = multipartFile.getOriginalFilename();String postfix = originalFilename.substring(originalFilename.lastIndexOf("."));String fileId = null;try {fileId = fileStorageService.uploadImgFile("", fileName + postfix, multipartFile.getInputStream());log.info("上传图片到MinIO中,fileId:{}", fileId);} catch (IOException e) {e.printStackTrace();log.error("WmMaterialServiceImpl-上传文件失败");}// 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);}@Overridepublic ResponseResult findList(WmMaterialDto dto) {// 1. 核对前端传来的分页参数dto.checkParam();// 2. mp分页查询IPage page = new Page(dto.getPage(), dto.getSize());// 判断是否收藏是收藏则 条件为所有为收藏的图片LambdaQueryWrapper<WmMaterial> wrapper = new LambdaQueryWrapper<>();if (dto.getIsCollection() != null && dto.getIsCollection() == 1) {wrapper.eq(WmMaterial::getIsCollection, dto.getIsCollection());}// 如果不是收藏则以用户来展示所有图片// 如果两种结果都需要以用户id查询,不同的是 是否查询收藏的条件,那么把共同条件放后,额外条件放前// 过五关斩六将思维wrapper.eq(WmMaterial::getUserId, WmThreadLocalUtil.getUser().getId());// 时间倒叙wrapper.orderByDesc(WmMaterial::getCreatedTime);page = page(page, wrapper);// 3. 返回结果// 多态,向上转型,获取更多可选字段,构建返回值对象ResponseResult result = new PageResponseResult(dto.getPage(), dto.getSize(), (int) page.getTotal());result.setData(page.getRecords());return result;}}
复制mp分页拦截器到引导类
@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));return interceptor;}
接口类
@PostMapping("list")public ResponseResult list(@RequestBody WmMaterialDto wmMaterialDto){return wmMaterialService.findList(wmMaterialDto);}
byd token应该设置久一点的,还得重新登录
频道列表查询
表结构
频道名,描述,默认,排序
实体
拷贝频道实体到model
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 java.io.Serializable;
import java.util.Date;/*** <p>* 频道信息表* </p>** @author itheima*/
@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;}
接口定义
直接把所有频道给查出来,一点不带分页的噢
老规矩了,接口,service,impl,mapper 接着拷贝咯
接口
package com.heima.wemedia.controller.v1;import com.heima.model.common.dtos.ResponseResult;
import com.heima.wemedia.service.WmChannelService;
import org.checkerframework.checker.units.qual.A;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/api/v1/channel")
public class WmchannelController {@Autowiredprivate WmChannelService wmchannelService;@GetMapping("/channels")public ResponseResult findAll() {return wmchannelService.findAll();}
}
mapper
package com.heima.wemedia.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.heima.model.wemedia.pojos.WmChannel;
import org.apache.ibatis.annotations.Mapper;@Mapper
public interface WmChannelMapper extends BaseMapper<WmChannel> {
}
service
package com.heima.wemedia.service;import com.baomidou.mybatisplus.extension.service.IService;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.wemedia.pojos.WmChannel;public interface WmChannelService extends IService<WmChannel> {/*** 查询所有频道* @return*/public ResponseResult findAll();}
impl
package com.heima.wemedia.service.impl;import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.wemedia.pojos.WmChannel;
import com.heima.wemedia.mapper.WmChannelMapper;
import com.heima.wemedia.service.WmChannelService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;@Service
@Transactional
@Slf4j
public class WmChannelServiceImpl extends ServiceImpl<WmChannelMapper, WmChannel> implements WmChannelService {/*** 查询所有频道* @return*/@Overridepublic ResponseResult findAll() {return ResponseResult.okResult(list());}
}
联调测试
刷新查看
文章列表
表结构
type,布局状态
status,文章状态
实体类
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;}}}
知识点
由于状态太多,1,2,3,4,5记不住对应啥属性,因此我们设置一个枚举类,属性code,,类似于NORMAL= new Status,有点像孙悟空,分身出来了多个,每个都有各自的code值
Dto前端查询类
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;/*** 所属频道ID*/private Integer channelId;/*** 关键字*/private String keyword;
}
接口定义
就是前端查询的表单参数
接口
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> {/*** 查询文章* @param dto* @return*/public ResponseResult findAll(WmNewsPageReqDto dto);}
impl
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.model.common.dtos.PageResponseResult;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.wemedia.dtos.WmNewsPageReqDto;
import com.heima.model.wemedia.pojos.WmNews;
import com.heima.model.wemedia.pojos.WmUser;
import com.heima.utils.thread.WmThreadLocalUtil;
import com.heima.wemedia.mapper.WmNewsMapper;
import com.heima.wemedia.service.WmNewsService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;@Service
@Slf4j
@Transactional
public class WmNewsServiceImpl extends ServiceImpl<WmNewsMapper, WmNews> implements WmNewsService {/*** 查询文章* @param dto* @return*/@Overridepublic ResponseResult findAll(WmNewsPageReqDto dto) {//1.检查参数if(dto == null){return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);}//分页参数检查dto.checkParam();//获取当前登录人的信息WmUser user = WmThreadLocalUtil.getUser();if(user == null){return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);}//2.分页条件查询IPage page = new Page(dto.getPage(),dto.getSize());LambdaQueryWrapper<WmNews> lambdaQueryWrapper = new LambdaQueryWrapper<>();//状态精确查询if(dto.getStatus() != null){lambdaQueryWrapper.eq(WmNews::getStatus,dto.getStatus());},//频道精确查询if(dto.getChannelId() != null){lambdaQueryWrapper.eq(WmNews::getChannelId,dto.getChannelId());}//时间范围查询if(dto.getBeginPubDate()!=null && dto.getEndPubDate()!=null){lambdaQueryWrapper.between(WmNews::getPublishTime,dto.getBeginPubDate(),dto.getEndPubDate());}//关键字模糊查询if(StringUtils.isNotBlank(dto.getKeyword())){lambdaQueryWrapper.like(WmNews::getTitle,dto.getKeyword());}//查询当前登录用户的文章lambdaQueryWrapper.eq(WmNews::getUserId,user.getId());//发布时间倒序查询lambdaQueryWrapper.orderByDesc(WmNews::getCreatedTime);page = page(page,lambdaQueryWrapper);//3.结果返回ResponseResult responseResult = new PageResponseResult(dto.getPage(),dto.getSize(),(int)page.getTotal());responseResult.setData(page.getRecords());return responseResult;}}
参数非空检验,分页初始化,获取登陆人信息,分页lambda构造器,状态频道精准——时间范围——关键字模糊查like查,发布时间倒序查,结果返回 分页三要素,和data
以上代码直接copy了,写吐了
联调测试
又得退出重登了,这里图片没有是以为minio是黑马的url
发布文章
需求分析
点T图标,文本编辑, 点图标图标,上传图片,还开源进行修改
定时发布
封面图
存草稿0,提交1
表结构
关系表存在的意义为,删除素材前先得把所有文章图片都删了,草稿就没事,直接删
思路
有id是修改,删除关联关系,修改后判断是否是草稿,草稿直接存,不是就将内容里的新素材关联该文章
没id则新增,草稿保存,非草稿关联多素材,同时关联用户设置的封面素材,如果没设置则默认规则(推荐文章的第一张图)
接口定义
接口定义-响应
实体类
package com.heima.model.wemedia.dtos;import lombok.Data;import java.util.Date;
import java.util.List;@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;
}
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 java.io.Serializable;/*** <p>* 自媒体图文引用素材信息表* </p>** @author itheima*/
@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;}
mapper
package com.heima.wemedia.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.heima.model.wemedia.pojos.WmNewsMaterial;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;import java.util.List;@Mapper
public interface WmNewsMaterialMapper extends BaseMapper<WmNewsMaterial> {void saveRelations(@Param("materialIds") List<Integer> materialIds,@Param("newsId") Integer newsId, @Param("type")Short type);
}
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="materialIds" index="ord" item="mid" separator=",">(#{mid},#{newsId},#{type},#{ord})</foreach></insert></mapper>
mid素材id nid文章id
常量
common包
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;
}
AppHttpEnum增加如下
// 自媒体文章错误 3501~3600MATERIASL_REFERENCE_FAIL(3501,"素材引用失效");
service
增加
/*** 发布修改文章或保存为草稿* @param dto* @return*/public ResponseResult submitNews(WmNewsDto dto);
impl
-
内容为空返回错误
-
判断保存还是修改
-
判断是不是草稿,草稿则不提交,结束方法
-
不是草稿,保存素材与文章的关系
-
不是草稿,保存封面图片与文章的关系
具体内容为
- 内容空值校验
- 拷贝前端残缺内容(BeanUtils,相同属性名&&类型拷贝)
- 存news的images封面用字符串。转前端LIst为String
- 封面类型自动(-1) 根据内容的图片数量来设置封面类型 3张就三图,1,2张就单图,0张就无图类型
- 根据id 来设置保存或修改,并额外的数据,userId,publishTime…
- 草稿就结束,不是草稿就开始保存封面图或者内容图的引用关系
- 抽取content内所有的内容图,根据文章id依次对应设置关系,当然要看素材库里有无该素材,失效或者素材与数据库不匹配则抛异常,最后根据素材对象获取id集合,依次添加关系
- 封面图关系类型,同时由于前面删除了所有封面,将新的封面string 串也就是 url1,url2,url3保存,最后调用修改方法
- 以上就是为了设置封面图片类型type,最后一个判断只要封面图片大于0则设置关系图,不大于0说明是无图,不管
/*** 发布修改文章或保存为草稿* @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){//[1dddfsd.jpg,sdlfjldk.jpg]--> 1dddfsd.jpg,sdlfjldk.jpgString 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 = ectractUrlInfo(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* 3,如果内容没有图片,无图 type 0** 第二个功能:保存封面图片与素材的关系* @param dto* @param wmNews* @param materials*/
private void saveRelativeInfoForCover(WmNewsDto dto, WmNews wmNews, List<String> materials) {List<String> images = dto.getImages();//如果当前封面类型为自动,则设置封面类型的数据if(dto.getType().equals(WemediaConstants.WM_NEWS_TYPE_AUTO)){//多图if(materials.size() >= 3){wmNews.setType(WemediaConstants.WM_NEWS_MANY_IMAGE);images = materials.stream().limit(3).collect(Collectors.toList());}else if(materials.size() >= 1 && materials.size() < 3){//单图wmNews.setType(WemediaConstants.WM_NEWS_SINGLE_IMAGE);images = materials.stream().limit(1).collect(Collectors.toList());}else {//无图wmNews.setType(WemediaConstants.WM_NEWS_NONE_IMAGE);}//修改文章if(images != null && images.size() > 0){wmNews.setImages(StringUtils.join(images,","));}updateById(wmNews);}if(images != null && images.size() > 0){saveRelativeInfo(images,wmNews.getId(),WemediaConstants.WM_COVER_REFERENCE);}}/*** 处理文章内容图片与素材的关系* @param materials* @param newsId*/
private void saveRelativeInfoForContent(List<String> materials, Integer newsId) {saveRelativeInfo(materials,newsId,WemediaConstants.WM_CONTENT_REFERENCE);
}@Autowired
private WmMaterialMapper wmMaterialMapper;/*** 保存文章图片与素材的关系到数据库中* @param materials* @param newsId* @param type*/
private void saveRelativeInfo(List<String> materials, Integer newsId, Short type) {if(materials!=null && !materials.isEmpty()){//通过图片的url查询素材的idList<WmMaterial> dbMaterials = wmMaterialMapper.selectList(Wrappers.<WmMaterial>lambdaQuery().in(WmMaterial::getUrl, materials));//判断素材是否有效if(dbMaterials==null || dbMaterials.size() == 0){//手动抛出异常 第一个功能:能够提示调用者素材失效了,第二个功能,进行数据的回滚throw new CustomException(AppHttpCodeEnum.MATERIASL_REFERENCE_FAIL);}if(materials.size() != dbMaterials.size()){throw new CustomException(AppHttpCodeEnum.MATERIASL_REFERENCE_FAIL);}List<Integer> idList = dbMaterials.stream().map(WmMaterial::getId).collect(Collectors.toList());//批量保存wmNewsMaterialMapper.saveRelations(idList,newsId,type);}}/*** 提取文章内容中的图片信息* @param content* @return*/
private List<String> ectractUrlInfo(String content) {List<String> materials = new ArrayList<>();List<Map> maps = JSON.parseArray(content, Map.class);for (Map map : maps) {if(map.get("type").equals("image")){String imgUrl = (String) map.get("value");materials.add(imgUrl);}}return materials;
}@Autowired
private WmNewsMaterialMapper wmNewsMaterialMapper;/*** 保存或修改文章* @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 {//修改//删除文章图片与素材的关系wmNewsMaterialMapper.delete(Wrappers.<WmNewsMaterial>lambdaQuery().eq(WmNewsMaterial::getNewsId,wmNews.getId()));updateById(wmNews);}}
接口新增
@PostMapping("/submit")
public ResponseResult submitNews(@RequestBody WmNewsDto dto){return wmNewsService.submitNews(dto);
}
联调测试
1为单图,3为多图 0为无图
1
1为主图封面引用 0为内容引用
-
设置引用类型type(封面主图,内容图)
-
提前保存(没id增加,有id修改并删除所有关系(后续在根据修改后的内容增加关系))
-
设置对应关系(素材未丢失情况,根据内容有几张,自动几张)
(这里有两个setImage的操作好像一个是为了直接给草稿设置封面图,一个是为了给提交设置封面图,草稿的话直接返回所有封面图,提交的话只设置了根据内容摘取的前X张)
然后再次进行修改图片即可(第二次是给提交而不是草稿内容去修改的) -
saveRelativeInfo 校验素材库,保存关系图
-
saveRelativeInfoForContent 无业务操作,指定引用类型为内容引用
-
saveRelativeInfoForCover 自动/非自动 非自动就本来有type了, 自动就根据内容图片设置type,
最后根据传上来的封面(非自动) 或是内容里扒出来的自动进行关系保存(自动) -
由于加了Transaction注解,一旦中途一个错,直接回滚