【黑马头条】-day03自媒体素材上传及管理-文章上传及管理-自动配图


文章目录


day3学习内容

在这里插入图片描述

1 自媒体后台搭建

1.1 总体架构

在这里插入图片描述

1.2 搭建步骤

在这里插入图片描述

1.2.1 基础环境与数据准备

导入sql文件

在这里插入图片描述

1.2.2 导入实体类WmLoginDto、WmUser

将自媒体对应的实体类导入heima-leadnews-model模块下的com.heima.model.wemedia包下
在这里插入图片描述

@Data
public class WmLoginDto {

    /**
     * 用户名
     */
    private String name;
    /**
     * 密码
     */
    private String password;
}
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_user")
public class WmUser implements Serializable {

    private static final long serialVersionUID = 1L;

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

    @TableField("ap_user_id")
    private Integer apUserId;

    @TableField("ap_author_id")
    private Integer apAuthorId;

    /**
     * 登录用户名
     */
    @TableField("name")
    private String name;

    /**
     * 登录密码
     */
    @TableField("password")
    private String password;

    /**
     * 盐
     */
    @TableField("salt")
    private String salt;

    /**
     * 昵称
     */
    @TableField("nickname")
    private String nickname;

    /**
     * 头像
     */
    @TableField("image")
    private String image;

    /**
     * 归属地
     */
    @TableField("location")
    private String location;

    /**
     * 手机号
     */
    @TableField("phone")
    private String phone;

    /**
     * 状态
            0 暂时不可用
            1 永久不可用
            9 正常可用
     */
    @TableField("status")
    private Integer status;

    /**
     * 邮箱
     */
    @TableField("email")
    private String email;

    /**
     * 账号类型
            0 个人 
            1 企业
            2 子账号
     */
    @TableField("type")
    private Integer type;

    /**
     * 运营评分
     */
    @TableField("score")
    private Integer score;

    /**
     * 最后一次登录时间
     */
    @TableField("login_time")
    private Date loginTime;

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

}

1.2.3 导入自媒体微服务模块

将heima-leadnews-wemedia模块导入heima-leadnews-service模块下,并修改heima-leadnews-service的pom文件的依赖,添加wemedia依赖

<modules>
    <module>heima-leadnews-user</module>
    <module>heima-leadnews-article</module>
    <module>heima-leadnews-wemedia</module>
</modules>

并且修改bootstrap.yml

server:
  port: 51803
spring:
  application:
    name: leadnews-wemedia
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.204.129:8848
      config:
        server-addr: 192.168.204.129:8848
        file-extension: yml

1.2.4 将自媒体微服务配置注册到Nacos

将heima-leadnews-wemedia微服务注册到Nacos

微服务名:leadnews-wemedia

注册到Nacos

在这里插入图片描述

在这里插入图片描述

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

在这里插入图片描述

1.2.5 导入自媒体网关模块

将heima-leadnews-wemedia-gateway模块导入heima-leadnews-gateway模块下

在这里插入图片描述

并且修改heima-leadnews-gateway的pom文件时期添加对heima-leadnews-wemedia-gateway的依赖

在这里插入图片描述

并且修改bootstrap.yml

server:
  port: 51602
spring:
  application:
    name: leadnews-wemedia-gateway
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.204.129:8848
      config:
        server-addr: 192.168.204.129:8848
        file-extension: yml

1.2.6 将自媒体网关配置注册到Nacos

微服务名:leadnews-wemedia-gateway

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.2.7 启动自媒体及其网关服务

在这里插入图片描述

2 自媒体前端的搭建

在这里插入图片描述

在这里插入图片描述

2.1 解压wemeadia-web静态资源

将wemeadia-web解压后与app-web放入同一目录

在这里插入图片描述

2.2 为wemeadia-web配置Nginx

在Nginx的conf目录下的leadnews.conf下创建heima-leadnews-wemedia.conf

在这里插入图片描述

upstream  heima-wemedia-gateway{
    server localhost:51602;
}

server {
	listen 8802;
	location / {
        root D:/Code/JavaCode/HeimaToutiao/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;   # 记录真实发出请求的客户端IP
		proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;  #记录代理信息
	}
}

因为heima-app-gateway网关监听51602,Nginx要将8802端口转发到51602再通过Nacos转发到自媒体服务端口51603

location ~/wemedia/MEDIA/(.*)是因为前端发送的请求的url是这,所以要与前端保持一致

proxy_passhttp://heima-wemedia-gateway/$1也就是heima-wemedia-gatewaylocalhost:51602

(.*)最后会替换$1

2.3 重新启动Nginx

nginx

访问http://localhost:8802/#/login

在这里插入图片描述

成功进入

3 自媒体素材管理

在这里插入图片描述

3.1 表结构

在这里插入图片描述

3.1.1 拷贝实体类WmMaterial

拷贝WmMaterial实体类到heima-leadnews-model模块下的com.heima.model.wemedia.pojos包下

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

}

3.2 图片上传实现思路

在这里插入图片描述

3.2.1 token解析用户id存入header

在heima-leadnews-wemedia-gateway中对http请求中的token进行解析

因为在AppJwtUtil类中最开始组装JWT时,就将用户id传入claimMaps中,最后通过Jwts.builder().addClaims(claimMaps)存入JWT令牌中

public static String getToken(Long id) {
    Map<String, Object> claimMaps = new HashMap<>();
    claimMaps.put("id", id);
    long currentTime = System.currentTimeMillis();
    return Jwts.builder()
            .setId(UUID.randomUUID().toString())
            .setIssuedAt(new Date(currentTime))  //签发时间
            .setSubject("system")  //说明
            .setIssuer("heima") //签发者信息
            .setAudience("app")  //接收用户
            .compressWith(CompressionCodecs.GZIP)  //数据压缩方式
            .signWith(SignatureAlgorithm.HS512, generalKey()) //加密方式
            .setExpiration(new Date(currentTime + TOKEN_TIME_OUT * 1000))  //过期时间戳
            .addClaims(claimMaps) //cla信息
            .compact();
}

之后在wemedia的拦截器AuthorizeFilter中,先一步步判断:

1.获取request和response对象

2.判断是否是登录

3.获取token

4.判断token是否存在

5.判断token是否有效

在第五步中通过Claims claimsBody = AppJwtUtil.getClaimsBody(token);对token进行解析得到claimsBody,再从claimsBody中就能拿到当前用户的id。

@Component
@Slf4j
public class AuthorizeFilter implements Ordered, GlobalFilter {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //1.获取request和response对象
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();

        //2.判断是否是登录
        if(request.getURI().getPath().contains("/login")){
            //放行
            return chain.filter(exchange);
        }

        //3.获取token
        String token = request.getHeaders().getFirst("token");

        //4.判断token是否存在
        if(StringUtils.isBlank(token)){
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return response.setComplete();
        }

        //5.判断token是否有效
        try {
            Claims claimsBody = AppJwtUtil.getClaimsBody(token);
            //是否是过期
            int result = AppJwtUtil.verifyToken(claimsBody);
            if(result == 1 || result  == 2){
                response.setStatusCode(HttpStatus.UNAUTHORIZED);
                return response.setComplete();
            }
            //获取用户信息
            Integer userId = (Integer) claimsBody.get("id");
            //将用户信息放入到header中
            ServerHttpRequest serverHttpRequest = request.mutate().headers(httpHeaders -> {
                httpHeaders.add("userId", userId + "");
            }).build();
            //重置请求
            exchange.mutate().request(serverHttpRequest);

        } catch (Exception e) {
            e.printStackTrace();
        }

        //6.放行
        return chain.filter(exchange);
    }

    /**
     * 优先级设置  值越小  优先级越高
     * @return
     */
    @Override
    public int getOrder() {
        return 0;
    }
}

在将用户id存入header中并重置请求

//获取用户信息
Integer userId = (Integer) claimsBody.get("id");
//将用户信息放入到header中
ServerHttpRequest serverHttpRequest = request.mutate().headers(httpHeaders -> {
    httpHeaders.add("userId", userId + "");
}).build();
//重置请求
exchange.mutate().request(serverHttpRequest);

3.2.2 为微服务创建拦截器解析token

在heima-leadnews-service模块中的heima-leadnews-wemedia创建创建com.heima.wemedia.interceptor.WmTokenInterceptor拦截器类,但是拦截器需要将用户信息存入当前线程中,所以需要WmThreadLocalUtil来创建线程并存入。

3.2.2.1 创建WmThreadLocalUtils线程工具类

在heima-leadnews-utils模块中创建com.heima.utils.thread.WmThreadLocalUtils

public class WmThreadLocalUtil {
    private static final ThreadLocal<WmUser> WM_USER_THREAD_LOCAL = new ThreadLocal<>();

    public static void setUser(WmUser user) {
        WM_USER_THREAD_LOCAL.set(user);
    }

    public static WmUser getUser() {
        return WM_USER_THREAD_LOCAL.get();
    }

    public static void clear() {
        WM_USER_THREAD_LOCAL.remove();
    }
}
3.2.2.2 创建拦截器WmTokenInterceptor

在heima-leadnews-service模块中的heima-leadnews-wemedia创建创建com.heima.wemedia.interceptor.WmTokenInterceptor拦截器类,因为WmThreadLocalUtils线程工具类都是静态方法,故可以直接通过WmThreadLocalUtil.setUser()直接进行调用

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 ex
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        WmThreadLocalUtil.clear();
    }
}
3.2.2.3 通过配置类将WmTokenInterceptor导入Spring中

在heima-leadnews-service模块中的heima-leadnews-wemedia中创捷com.heima.wemedia.config.WebMvcConfig

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new WmTokenInterceptor()).addPathPatterns("/**");
    }
}

通过@ConfigurationInterceptorRegistry registry添加拦截器WmTokenInterceptor拦截所有路径("/**")

3.2.3 完成图片上传

在这里插入图片描述

3.2.3.1 创建Controller、Service、Mapper

在heima-leadnews-service模块中的heima-leadnews-wemedia中创捷com.heima.wemedia.controller.v1.WmMaterialController

@RestController
@RequestMapping("/api/v1/material")
public class WmMaterialController {

    @PostMapping("/upload_picture")
    public ResponseResult uploadPicture(MultipartFile multipartFile) {
        return null;
    }
}

Service接口

public interface WmMaterialService extends IService<WmMaterial> {


}

实现类

@Slf4j
@Service
@Transactional
public class WmMaterialServiceImpl extends ServiceImpl<WmMaterialMapper, WmMaterial> implements WmMaterialService {


}

Mapper接口

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

为heima-leadnews-wemedia引入minio依赖

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

将minio的配置放入nacos中的leadnews-wemedia配置中

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

Controller

@RestController
@RequestMapping("/api/v1/material")
public class WmMaterialController {
    @Autowired
    private WmMaterialService wmMaterialService;

    @PostMapping("/upload_picture")
    public ResponseResult uploadPicture(MultipartFile multipartFile) {
        return wmMaterialService.uploadPicture(multipartFile);
    }
}

Service接口

public interface WmMaterialService extends IService<WmMaterial> {
    public ResponseResult uploadPicture(MultipartFile multipartFile);
}

Service实现类

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

        //1.校验参数
        if(multipartFile == null||multipartFile.getSize() == 0){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        //2.上传图片到minIO中
        //2.1 使用UUID生成文件名
        String fileName = UUID.randomUUID().toString().replace("-", "");
        //2.2 拿到文件的后缀
        String originalFilename = multipartFile.getOriginalFilename();
        String fileType = originalFilename.substring(originalFilename.lastIndexOf("."));
        //2.3 上传文件
        String fileId=null;
        try {
            fileId = fileStorageService.uploadImgFile("", fileName + fileType, 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);//0 图片 1 视频
        wmMaterial.setCreatedTime(new Date());
        save(wmMaterial);
        //4.返回结果
        return ResponseResult.okResult(wmMaterial);
    }
}
3.2.3.3 测试

启动微服务及网关

访问http://localhost:8802/#/login

在这里插入图片描述

浏览器访问url也正常,userid也与浏览器登陆时admin能对的上

在这里插入图片描述

3.3 图片列表及收藏

3.3.1 接口定义

在这里插入图片描述

在这里插入图片描述

3.3.2 创建接收参数WmMaterialDto

在heima-leadnews-model模块中的com.heima.model.wemedia.dtos中创建WmMaterialDto

@Data
public class WmMaterialDto extends PageRequestDto {

    /**
     * 1 收藏
     * 0 未收藏
     */
    private Short isCollection;
}

3.3.2 添加三层业务

在heima-leadnews-service模块中的heima-leadnews-wemedia中的com.heima.wemedia.controller.v1.WmMaterialController中添加新的方法

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

service接口及实现

public interface WmMaterialService extends IService<WmMaterial> {
    public ResponseResult uploadPicture(MultipartFile multipartFile);

    public ResponseResult findList(@RequestBody WmMaterialDto wmMaterialDto);
}
/**
 * 查询素材列表
 * @param wmMaterialDto
 * @return
 */
@Override
public ResponseResult findList(WmMaterialDto wmMaterialDto) {
    //1.校验参数
    wmMaterialDto.checkParam();
    //2.分页查询
    IPage pageCheck=new Page(wmMaterialDto.getPage(),wmMaterialDto.getSize());
    LambdaQueryWrapper<WmMaterial> lambdaQueryWrapper = new LambdaQueryWrapper<>();
    //2.1是否收藏
    if(wmMaterialDto.getIsCollection()!=null&&wmMaterialDto.getIsCollection()==1){
        lambdaQueryWrapper.eq(WmMaterial::getIsCollection,wmMaterialDto.getIsCollection());
    }
    //2.2 按照用户查询
    lambdaQueryWrapper.eq(WmMaterial::getUserId,WmThreadLocalUtil.getUser().getId());
    //2.3 按照时间排序
    lambdaQueryWrapper.orderByDesc(WmMaterial::getCreatedTime);
    //2.4 查询
    pageCheck = page(pageCheck, lambdaQueryWrapper);
    //3.结果返回
    ResponseResult responseResult = new PageResponseResult(wmMaterialDto.getPage(), wmMaterialDto.getSize(), (int) pageCheck.getTotal());
    responseResult.setData(pageCheck.getRecords());
    return responseResult;
}

mapper

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

3.3.3 使mp生效,启动类注入mp

使分页插件生效interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL))

@SpringBootApplication
@EnableDiscoveryClient
@MapperScan("com.heima.wemedia.mapper")
public class WemediaApplication {

    public static void main(String[] args) {
        SpringApplication.run(WemediaApplication.class,args);
    }

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

3.3.4 重启服务

在这里插入图片描述

已经实现

4 自媒体文章管理

4.1 频道列表查询

4.1.1 需求分析

在这里插入图片描述

4.1.2 表结构

在这里插入图片描述

4.1.3 导入实体类WmChannel

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

}

4.1.4 接口定义

在这里插入图片描述

4.1.5 实现

4.1.5.1 添加三层业务

Controller

@RestController
@RequestMapping("/api/v1/channel")
public class WmChannelController {
    @Autowired
    private WmChannelService wmChannelService;
    @GetMapping("/channels")
    public ResponseResult findList(){
        return wmChannelService.findList();
    }

}

Service接口及实现

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

Mapper

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

在这里插入图片描述

4.2 文章列表加载

4.2.1 需求分析

在这里插入图片描述

4.2.2 表结构分析

在这里插入图片描述

4.2.3 导入实体类WmNews

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

4.2.4 接口定义

在这里插入图片描述

在这里插入图片描述

4.2.5 实现

4.2.5.1 添加三层业务

Controller

@RestController
@RequestMapping("/api/v1/news")
public class WmNewsController {
    @Autowired
    private WmNewsService wmNewsService;
    @PostMapping("/list")
    public ResponseResult findList(@RequestBody WmNewsPageReqDto wmNewsPageReqDto){
        return wmNewsService.findList(wmNewsPageReqDto);
    }
}

Service接口及实现

public interface WmNewsService extends IService<WmNews> {
    ResponseResult findList(WmNewsPageReqDto wmNewsPageReqDto);
}
@Service
public class WmNewsServiceImpl extends ServiceImpl<WmNewsMapper, WmNews> implements WmNewsService {
    @Override
    public ResponseResult findList(WmNewsPageReqDto wmNewsPageReqDto) {
        // 1.参数检查
        if(wmNewsPageReqDto == null){
            return ResponseResult.errorResult(400,"参数错误");
        }
        wmNewsPageReqDto.checkParam();
        // 2.分页查询
        IPage pageCheck=new Page(wmNewsPageReqDto.getPage(),wmNewsPageReqDto.getSize());
        // 2.0 按照不同需求查询
        LambdaQueryWrapper<WmNews> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        //2.1 文章状态精确查询
        if(wmNewsPageReqDto.getStatus()!=null){
            lambdaQueryWrapper.eq(WmNews::getStatus,wmNewsPageReqDto.getStatus());
        }
        //2.2 关键字模糊匹配
        if(StringUtils.isNotBlank(wmNewsPageReqDto.getKeyword())){
            lambdaQueryWrapper.like(WmNews::getTitle,wmNewsPageReqDto.getKeyword());
        }
        //2.3 所属频道精确查询
        if(wmNewsPageReqDto.getChannelId()!=null){
            lambdaQueryWrapper.eq(WmNews::getChannelId,wmNewsPageReqDto.getChannelId());
        }
        //2.4 时间范围查询
        if(wmNewsPageReqDto.getBeginPubDate()!=null&&wmNewsPageReqDto.getEndPubDate()!=null){
            lambdaQueryWrapper.between(WmNews::getPublishTime,wmNewsPageReqDto.getBeginPubDate(),wmNewsPageReqDto.getEndPubDate());
        }
        //2.5 查询当前用户的文章
        lambdaQueryWrapper.eq(WmNews::getUserId, WmThreadLocalUtil.getUser().getId());
        //2.6 排序
        lambdaQueryWrapper.orderByDesc(WmNews::getPublishTime);
        pageCheck = page(pageCheck, lambdaQueryWrapper);
        // 3.返回结果
        ResponseResult responseResult = new PageResponseResult(wmNewsPageReqDto.getPage(), wmNewsPageReqDto.getSize(), (int) pageCheck.getTotal());
        responseResult.setData(pageCheck.getRecords());
        return responseResult;
    }
}

Mapper

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

在这里插入图片描述

4.3 发布文章

4.3.1 需求分析

在这里插入图片描述

4.3.2 表结构分析

在这里插入图片描述

4.3.3 导入表实体WmNewsMaterial、WmNewsDto

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

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

4.3.4 实现思路

在这里插入图片描述

4.3.5 接口定义

在这里插入图片描述

在这里插入图片描述

响应

在这里插入图片描述

4.3.6 实现

在WmNewsController中添加新方法submitNews

@RestController
@RequestMapping("/api/v1/news")
public class WmNewsController {
    @Autowired
    private WmNewsService wmNewsService;
    @PostMapping("/list")
    public ResponseResult findList(@RequestBody WmNewsPageReqDto wmNewsPageReqDto){
        return wmNewsService.findList(wmNewsPageReqDto);
    }

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

Service及实现
在这里插入图片描述

public interface WmNewsService extends IService<WmNews> {
    ResponseResult findList(WmNewsPageReqDto wmNewsPageReqDto);

    ResponseResult submitNews(WmNewsDto wmNewsDto);
}
@Override
public ResponseResult submitNews(WmNewsDto wmNewsDto) {
    // 0.参数检查
    if(wmNewsDto == null||wmNewsDto.getContent()==null){
        return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
    }
    //1. 保存或修改文章
    WmNews wmNews = new WmNews();
    BeanUtils.copyProperties(wmNewsDto,wmNews);
    //1.1 封面
    if(wmNewsDto.getImages()!=null&& wmNewsDto.getImages().size()>0){
        String imageStr = StringUtils.join(wmNewsDto.getImages(), ",");
        wmNews.setImages(imageStr);
    }
    //1.2 如果封面为自动-1,则需要手动设置封面规则
    if(wmNewsDto.getImages().equals(WemediaConstants.WM_NEWS_TYPE_AUTO)){
        wmNews.setType(WemediaConstants.WM_NEWS_TYPE_AUTO);
    }
    saveOrUpdateWmNews(wmNews);
    //2.判断是否为草稿,如果为草稿结束当前发放
    if(wmNews.getStatus().equals(WmNews.Status.NORMAL.getCode())){
        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }
    //3.不是草稿,保存文章内容与图片素材的关系
    //3.1 获取文章内容的图片素材
    List<String> imageList=extractUrlInfo(wmNewsDto.getContent());
    saveRelativeInfoForContent(imageList,wmNews.getId());

    //4.不是草稿,保存文章封面图片与图片素材的关系
    
    //
    return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);

}

/**
 * 保存文章封面图片与图片素材的关系
 * @param imageList
 * @param id
 */
private void saveRelativeInfoForContent(List<String> imageList, Integer id) {
    saveRelativeInfo(imageList,id,WemediaConstants.WM_CONTENT_REFERENCE);

}

@Autowired
private WmMaterialMapper wmMaterialMapper;
/**
 * 保存文章图片与素材的关系到数据库
 * @param imageList
 * @param id
 * @param wmContentReference
 */
private void saveRelativeInfo(List<String> imageList, Integer id, Short wmContentReference) {
    if(imageList!=null||!imageList.isEmpty()){
        //通过图片url查询素材的id
        List<WmMaterial> dbMaterials = wmMaterialMapper.selectList(Wrappers.<WmMaterial>lambdaQuery().in(WmMaterial::getUrl, imageList));
        //判断素材是否有用
        if(dbMaterials==null||dbMaterials.size()==0){
            //手动抛出异常
            throw new CustomException(AppHttpCodeEnum.MATERIAL_REFERENCE_INVAILD);
        }
        if(dbMaterials.size()!=imageList.size()){
            throw new CustomException(AppHttpCodeEnum.MATERIAL_REFERENCE_INVAILD);
        }
        List<Integer> dbMaterialsIds = dbMaterials.stream().map(WmMaterial::getId).collect(Collectors.toList());
        wmNewsMaterialMapper.saveRelations(dbMaterialsIds,id,wmContentReference);
    }

}

/**
 * 提取文章内容中的图片信息
 * @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;
}

@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(WemediaConstants.WEMEDIA_ARTICLE_SHOW_ON);
    if(wmNews.getId()==null) {
        //如果没有就是新增
        save(wmNews);
    }
    else {
        //修改
        //删除文章与素材的关系
        wmNewsMaterialMapper.delete(Wrappers.<WmNewsMaterial>lambdaQuery().eq(WmNewsMaterial::getNewsId,wmNews.getId()));
        //更新文章
        updateById(wmNews);
    }


}

mapper

@Mapper
public interface WmNewsMaterialMapper extends BaseMapper<WmNewsMaterial> {
    void saveRelations(@Param("materialIds") List<Integer> materialIds,@Param("newsId")Integer newsId, @Param("type")Short type);
}

对应resource/mapper

<?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.3.7 完善service功能

@Service
public class WmNewsServiceImpl extends ServiceImpl<WmNewsMapper, WmNews> implements WmNewsService {
    @Override
    public ResponseResult findList(WmNewsPageReqDto wmNewsPageReqDto) {
        // 1.参数检查
        if(wmNewsPageReqDto == null){
            return ResponseResult.errorResult(400,"参数错误");
        }
        wmNewsPageReqDto.checkParam();
        // 2.分页查询
        IPage pageCheck=new Page(wmNewsPageReqDto.getPage(),wmNewsPageReqDto.getSize());
        // 2.0 按照不同需求查询
        LambdaQueryWrapper<WmNews> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        //2.1 文章状态精确查询
        if(wmNewsPageReqDto.getStatus()!=null){
            lambdaQueryWrapper.eq(WmNews::getStatus,wmNewsPageReqDto.getStatus());
        }
        //2.2 关键字模糊匹配
        if(StringUtils.isNotBlank(wmNewsPageReqDto.getKeyword())){
            lambdaQueryWrapper.like(WmNews::getTitle,wmNewsPageReqDto.getKeyword());
        }
        //2.3 所属频道精确查询
        if(wmNewsPageReqDto.getChannelId()!=null){
            lambdaQueryWrapper.eq(WmNews::getChannelId,wmNewsPageReqDto.getChannelId());
        }
        //2.4 时间范围查询
        if(wmNewsPageReqDto.getBeginPubDate()!=null&&wmNewsPageReqDto.getEndPubDate()!=null){
            lambdaQueryWrapper.between(WmNews::getPublishTime,wmNewsPageReqDto.getBeginPubDate(),wmNewsPageReqDto.getEndPubDate());
        }
        //2.5 查询当前用户的文章
        lambdaQueryWrapper.eq(WmNews::getUserId, WmThreadLocalUtil.getUser().getId());
        //2.6 排序
        lambdaQueryWrapper.orderByDesc(WmNews::getPublishTime);
        pageCheck = page(pageCheck, lambdaQueryWrapper);
        // 3.返回结果
        ResponseResult responseResult = new PageResponseResult(wmNewsPageReqDto.getPage(), wmNewsPageReqDto.getSize(), (int) pageCheck.getTotal());
        responseResult.setData(pageCheck.getRecords());
        return responseResult;
    }

    @Override
    public ResponseResult submitNews(WmNewsDto wmNewsDto) {
        // 0.参数检查
        if(wmNewsDto == null||wmNewsDto.getContent()==null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        //1. 保存或修改文章
        WmNews wmNews = new WmNews();
        BeanUtils.copyProperties(wmNewsDto,wmNews);
        //1.1 封面
        if(wmNewsDto.getImages()!=null&& wmNewsDto.getImages().size()>0){
            String imageStr = StringUtils.join(wmNewsDto.getImages(), ",");
            wmNews.setImages(imageStr);
        }
        //1.2 如果封面为自动-1,则需要手动设置封面规则
        if(wmNewsDto.getType().equals(WemediaConstants.WM_NEWS_TYPE_AUTO)){
            wmNews.setType(null);
        }
        saveOrUpdateWmNews(wmNews);
        //2.判断是否为草稿,如果为草稿结束当前方法
        if(wmNews.getStatus().equals(WmNews.Status.NORMAL.getCode())){
            return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
        }
        //3.不是草稿,保存文章内容与图片素材的关系
        //3.1 获取文章内容的图片素材
        List<String> imageList=extractUrlInfo(wmNewsDto.getContent());
        saveRelativeInfoForContent(imageList,wmNews.getId());

        //4.不是草稿,保存文章封面图片与图片素材的关系
        saveRelativeInfoForCover(wmNewsDto,wmNews,imageList);

        //
        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);

    }

    /**
     * 保存文章封面图片与图片素材的关系
     * 如果当前为自动,进行匹配:
     * 1、若当前图片大于等于1,小于三张,设置为单图 type=1
     * 2、若当前图片大于等于三张,设置为多图 type=3
     * 3、若当前没有图片,设置为无图 type=0
     *
     * @param wmNewsDto
     * @param wmNews
     * @param imageList
     */
    private void saveRelativeInfoForCover(WmNewsDto wmNewsDto, WmNews wmNews, List<String> imageList) {
        List<String> images = wmNewsDto.getImages();

        //处理封面为自动的情况
        if(wmNewsDto.getType().equals(WemediaConstants.WM_NEWS_TYPE_AUTO)){
            //单图
            if(imageList.size()>=1&&imageList.size()<3){
                wmNews.setType(WemediaConstants.WM_NEWS_SINGLE_IMAGE);
                images=imageList.stream().limit(1).collect(Collectors.toList());
            }
            else if(imageList.size()>=3){
                //多图
                wmNews.setType(WemediaConstants.WM_NEWS_MANY_IMAGE);
                images=imageList.stream().limit(3).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 imageList
     * @param id
     */
    private void saveRelativeInfoForContent(List<String> imageList, Integer id) {
        saveRelativeInfo(imageList,id,WemediaConstants.WM_CONTENT_REFERENCE);

    }

    @Autowired
    private WmMaterialMapper wmMaterialMapper;
    /**
     * 保存文章图片与素材的关系到数据库
     * @param imageList
     * @param id
     * @param wmContentReference
     */
    private void saveRelativeInfo(List<String> imageList, Integer id, Short wmContentReference) {
        if(imageList!=null&&!imageList.isEmpty()){
            //通过图片url查询素材的id
            List<WmMaterial> dbMaterials = wmMaterialMapper.selectList(Wrappers.<WmMaterial>lambdaQuery().in(WmMaterial::getUrl, imageList));
            //判断素材是否有用
            if(dbMaterials==null||dbMaterials.size()==0){
                //手动抛出异常
                throw new CustomException(AppHttpCodeEnum.MATERIAL_REFERENCE_INVAILD);
            }
            if(dbMaterials.size()!=imageList.size()){
                throw new CustomException(AppHttpCodeEnum.MATERIAL_REFERENCE_INVAILD);
            }
            List<Integer> dbMaterialsIds = dbMaterials.stream().map(WmMaterial::getId).collect(Collectors.toList());
            wmNewsMaterialMapper.saveRelations(dbMaterialsIds,id,wmContentReference);
        }

    }

    /**
     * 提取文章内容中的图片信息
     * @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;
    }

    @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(WemediaConstants.WEMEDIA_ARTICLE_SHOW_ON);
        if(wmNews.getId()==null) {
            //如果没有就是新增
            save(wmNews);
        }
        else {
            //修改
            //删除文章与素材的关系
            wmNewsMaterialMapper.delete(Wrappers.<WmNewsMaterial>lambdaQuery().eq(WmNewsMaterial::getNewsId,wmNews.getId()));
            //更新文章
            updateById(wmNews);
        }
    }
}

在这里插入图片描述

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

bblb

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值