Titulares de Dark Horse de 2023. Proyecto de microservicio. Notas de aprendizaje (3)

Publicación de artículo de medios propios

inserte la descripción de la imagen aquí

1. Construcción de front-end y back-end de medios propios

1.1 Construcción de fondo

inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí

①: busque heima-leadnews-wemedia.zip en los datos y descomprímalo

Cópielo en el proyecto heima-leadnews-service y especifique el submódulo
inserte la descripción de la imagen aquí

Ejecute el script leadnews-wemedia.sql

Agregue la configuración de nacos correspondiente

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

②: busque heima-leadnews-wemedia-gateway.zip en los datos y descomprímalo

Cópielo en el proyecto heima-leadnews-gateway y especifique el submódulo
inserte la descripción de la imagen aquí

Agregue la configuración de nacos correspondiente

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

Tenga en cuenta aquí que es necesario configurar redis, de lo contrario, se informará un error al iniciar
inserte la descripción de la imagen aquí

③: busque la carpeta de clase en los datos

Copie la carpeta wemedia en com.heima.model en el módulo heima-leadnews-model

1.2 Construcción en primer plano

inserte la descripción de la imagen aquí

Use el mismo nginx para acceder a múltiples proyectos a través de la función de host virtual de nginx

Pasos de construcción:

①: busque wemedia-web.zip en los datos y descomprímalo

②: agregue el archivo heima-leadnews-wemedia.conf en el directorio leadnews.conf en nginx

  • Modificación de la dirección de la puerta de enlace (localhost: 51602)

  • Modificación del directorio del proyecto front-end (directorio descomprimido por wemedia-web)

  • Modificación del puerto de acceso (8802)

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

server {
    
    
	listen 8802;
	location / {
    
    
		root F:/javawebwork/heima-leadnews-html/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;  #记录代理信息
	}
}

③: Inicie nginx, inicie el microservicio de medios propios y la puerta de enlace correspondiente

④: Función de inicio de sesión de prueba de depuración conjunta
inserte la descripción de la imagen aquí
Acceso: http://localhost:8802/
cuenta:
contraseña de administrador: 123456

inserte la descripción de la imagen aquí
Después de iniciar sesión, la interfaz es la siguiente:
inserte la descripción de la imagen aquí

2. Gestión de material automediático

2.1 Carga de material

2.2.1 Análisis de la demanda

inserte la descripción de la imagen aquí

La página de carga de imágenes muestra primero la información del material, puede hacer clic en la imagen para cargar y puede cargar la imagen después de la ventana emergente

2.2.2 Gestión de materiales - carga de imágenes - estructura de la tabla

Tabla de información de material gráfico multimedia wm_material
inserte la descripción de la imagen aquí

Clase de entidad correspondiente:

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;

}

pensar:
inserte la descripción de la imagen aquí

2.2.3 Ideas de implementación

inserte la descripción de la imagen aquí

①: El front-end envía una solicitud para cargar imágenes, el tipo es MultipartFile

②: después de que la puerta de enlace analiza el token, almacena la información del usuario analizada en el encabezado

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

③: El microservicio de medios propios utiliza el interceptor para obtener la información del usuario en el encabezado y colocarla en threadlocal

Agregar clase de herramienta en heima-leadnews-utils

Nota: debe encontrar la clase de entidad WmUser de los datos y copiarla en el proyecto modelo

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<>();

    /**
     * 添加用户
     * @param wmUser
     */
    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();
    }
}

Agregar interceptor en heima-leadnews-wemedia

package com.heima.wemedia.interceptor;

import com.heima.model.wemedia.pojos.WmUser;
import com.heima.utils.thread.WmThreadLocalUtils;
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 {
    
    
    @Override
    public 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;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    
    
        log.info("清理threadlocal...");
        WmThreadLocalUtils.clear();
    }
}

Configure el interceptor para que surta efecto e intercepte todas las solicitudes

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 {
    
    

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

④: Cargue primero la imagen en minIO y obtenga la ruta a la solicitud de imagen——(2.2.5 Verifique la implementación de la función específica)

⑤: Guarde la identificación de usuario y la ruta en la imagen en la tabla de materiales——(2.2.5 Verifique la implementación de la función específica)

2.2.4 Definición de interfaz

inserte la descripción de la imagen aquí

ilustrar
ruta de interfaz /api/v1/material/subir_imagen
método de solicitud CORREO
parámetro MultipartFile
resultado de la respuesta RespuestaResultado

MultipartFile: el tipo de recepción de archivo especificado por Springmvc

Resultado de la respuesta:

El éxito necesita hacer eco de la imagen y devolver el objeto material.

{
    
    
  "host":null,
  "code":200,
  "errorMessage":"操作成功",
  "data":{
    
    
    "id":52,
    "userId":1102,
    "url":"http://192.168.200.130:9000/leadnews/2021/04/26/a73f5b60c0d84c32bfe175055aaaac40.jpg",
    "type":0,
    "isCollection":0,
    "createdTime":"2021-01-20T16:49:48.443+0000"
  }
}

fallar:

  • Fallo de parámetro
  • Error al cargar el artículo
2.2.5 Integración de microservicio de medios propios heima-file-starter

①: Importar heima-file-starter en el submódulo heima-leadnews-wemedia

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

②: Agregue la siguiente configuración en el centro de configuración del microservicio de medios propios:

minio:
  accessKey: minio
  secretKey: minio123
  bucket: leadnews
  endpoint: http://192.168.200.130:9000
  readPath: http://192.168.200.130:9000
2.2.6 Implementación específica

①: Crear WmMaterialController

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


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

}

②: mapeador

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> {
    
    
}

③: Capa empresarial:

package com.heima.wemedia.service;

public interface WmMaterialService extends IService<WmMaterial> {
    
    

    /**
     * 图片上传
     * @param multipartFile
     * @return
     */
    public ResponseResult uploadPicture(MultipartFile multipartFile);



}

Clase de implementación de la capa empresarial:

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 {
    
    

    @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中
        String fileName = UUID.randomUUID().toString().replace("-", "");
        //aa.jpg
        String 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);
    }

}

④: Controlador

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

    @Autowired
    private WmMaterialService wmMaterialService;


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

}

⑤: Prueba

Preste atención cuando realice pruebas aquí, asegúrese de volver a visitar
http://localhost:8802/
para iniciar el microservicio de medios propios y la puerta de enlace de medios propios, y use el proyecto front-end para las pruebas.

Después de cargar, la pantalla es exitosa, lo que indica que la función se realizó.
inserte la descripción de la imagen aquí
Después de que la carga sea exitosa, hay 2 datos más en la base de datos.
inserte la descripción de la imagen aquí

2.2 Consulta de lista de materiales

2.2.1 Definición de interfaz

inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí

ilustrar
ruta de interfaz /api/v1/material/lista
método de solicitud CORREO
parámetro WmMaterialDto
resultado de la respuesta RespuestaResultado

WmMaterialDto:

@Data
public class WmMaterialDto extends PageRequestDto {
    
    

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

Resultado de la respuesta:

{
    
    
  "host":null,
  "code":200,
  "errorMessage":"操作成功",
  "data":[
    {
    
    
    "id":52,
      "userId":1102,
      "url":"http://192.168.200.130:9000/leadnews/2021/04/26/ec893175f18c4261af14df14b83cb25f.jpg",
      "type":0,
      "isCollection":0,
      "createdTime":"2021-01-20T16:49:48.000+0000"
    },
    ....
  ],
  "currentPage":1,
  "size":20,
  "total":0
}
2.2.2 Realización de funciones

①: agregue un nuevo método en la clase WmMaterialController

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

②: se ha definido el mapeador

③: capa empresarial

Agregar nuevo método en WmMaterialService

/**
     * 素材列表查询
     * @param dto
     * @return
     */
public ResponseResult findList( WmMaterialDto dto);

Implementación:

/**
     * 素材列表查询
     * @param dto
     * @return
     */
@Override
public ResponseResult findList(WmMaterialDto dto) {
    
    

    //1.检查参数
    dto.checkParam();

    //2.分页查询
    IPage page = new Page(dto.getPage(),dto.getSize());
    LambdaQueryWrapper<WmMaterial> lambdaQueryWrapper = new LambdaQueryWrapper<>();
    //是否收藏
    if(dto.getIsCollection() != null && dto.getIsCollection() == 1){
    
    
        lambdaQueryWrapper.eq(WmMaterial::getIsCollection,dto.getIsCollection());
    }

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

    //按照时间倒序
    lambdaQueryWrapper.orderByDesc(WmMaterial::getCreatedTime);


    page = page(page,lambdaQueryWrapper);

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

④: Controlador:

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

⑤: En el módulo self-media heima-leadnews-wemedia, guíe el interceptor de paginación de Zhongtian mybatis-plus
inserte la descripción de la imagen aquí

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

Reinicie los dos proyectos, y las imágenes cargadas ya se pueden ver.
inserte la descripción de la imagen aquí

La prueba de paginación también es normal y correcta.
inserte la descripción de la imagen aquí
Modificamos manualmente 2 datos y los cambiamos a favoritos.
inserte la descripción de la imagen aquí
Después, los favoritos son los siguientes:
inserte la descripción de la imagen aquí

3. Gestión de artículos de medios propios

3.1 Consultar todos los canales

3.1.1 Análisis de requisitos

inserte la descripción de la imagen aquí

3.1.2 Estructura de la tabla

wm_channel tabla de información del canal
inserte la descripción de la imagen aquí
Clase de entidad correspondiente:

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;

}
3.1.3 Definición de interfaz
ilustrar
ruta de interfaz /api/v1/canal/canales
método de solicitud CONSEGUIR
parámetro ninguno
resultado de la respuesta RespuestaResultado

Resultado de la respuesta:

{
    
    
  "host": "null",
  "code": 0,
  "errorMessage": "操作成功",
  "data": [
    {
    
    
      "id": 4,
      "name": "java",
      "description": "java",
      "isDefault": true,
      "status": false,
      "ord": 3,
      "createdTime": "2019-08-16T10:55:41.000+0000"
    },
    Object {
    
      ... },
    Object {
    
      ... }
  ]
}
3.1.4 Realización de funciones

Definición de interfaz:

package com.heima.wemedia.controller.v1;

import com.heima.model.common.dtos.ResponseResult;
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 {
    
    


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

mapeador

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> {
    
    
}

servicio

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();


}

Clase de implementación

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
     */
    @Override
    public ResponseResult findAll() {
    
    
        return ResponseResult.okResult(list());
    }
}

capa de control

package com.heima.wemedia.controller.v1;

import com.heima.model.common.dtos.ResponseResult;
import com.heima.wemedia.service.WmChannelService;
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 {
    
    

    @Autowired
    private WmChannelService wmChannelService;

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

Reinicie la puerta de enlace y administre los submódulos del artículo, y encuentre que la lista está cargada
inserte la descripción de la imagen aquí

3.2 Consultar artículos de medios propios

3.2.1 Descripción de los requisitos

inserte la descripción de la imagen aquí

3.2.2 Análisis de estructura de tablas

wm_news lista de artículos de medios propios
inserte la descripción de la imagen aquí

Clase de entidad correspondiente:

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

}
3.2.3 Definición de interfaz
ilustrar
ruta de interfaz /api/v1/noticias/lista
método de solicitud CORREO
parámetro WmNewsPageReqDto
resultado de la respuesta RespuestaResultado

WmNewsPageReqDto:

package com.heima.model.wemedia.dtos;

import com.heima.model.common.dtos.PageRequestDto;
import lombok.Data;

import java.util.Date;

@Data
public class WmNewsPageReqDto extends PageRequestDto {
    
    

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

Resultado de la respuesta:

{
    
    
  "host": "null",
  "code": 0,
  "errorMessage": "操作成功",
  "data": [
    Object {
    
     ... },
    Object {
    
     ... },
    Object {
    
     ... }
    
  ],
  "currentPage":1,
  "size":10,
  "total":21
}
3.2.4 Realización de funciones

①: Añadir WmNewsController

package com.heima.wemedia.controller.v1;

import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.wemedia.dtos.WmNewsPageReqDto;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/v1/news")
public class WmNewsController {
    
    


    @PostMapping("/list")
    public ResponseResult findAll(@RequestBody WmNewsPageReqDto dto){
    
    
        return  null;
    }

}

②: Añadir WmNewsMapper

package com.heima.wemedia.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.heima.model.wemedia.pojos.WmNews;
import org.apache.ibatis.annotations.Mapper;

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

③: Agregar WmNewsService

package com.heima.wemedia.service;


import com.baomidou.mybatisplus.extension.service.IService;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.wemedia.dtos.WmNewsPageReqDto;
import com.heima.model.wemedia.pojos.WmNews;

public interface WmNewsService extends IService<WmNews> {
    
    

    /**
     * 查询文章
     * @param dto
     * @return
     */
    public ResponseResult findAll(WmNewsPageReqDto dto);

}

Clase de implementación:

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
     */
    @Override
    public 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;
    }
   
}

④: Controlador

package com.heima.wemedia.controller.v1;

import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.wemedia.dtos.WmNewsPageReqDto;
import com.heima.wemedia.service.WmNewsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/v1/news")
public class WmNewsController {
    
    


    @Autowired
    private WmNewsService wmNewsService;

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

}
3.2.5 Pruebas

Inicie el microservicio de medios propios de back-end y el microservicio de puerta de enlace de medios propios, y pruebe la consulta de la lista de artículos

inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí

3.3 Publicación de artículos

3.3.1 Análisis de la demanda

inserte la descripción de la imagen aquí

3.3.2 Análisis de estructura de tablas

Para guardar un artículo, además de la tabla wm_news, se requieren otras dos tablas

wm_material tabla de materiales
inserte la descripción de la imagen aquí

wm_news_material Tabla de relación de material del artículo

inserte la descripción de la imagen aquí

inserte la descripción de la imagen aquí

Entre ellas, las clases de entidad de las tablas wm_material y wm_news se han importado al proyecto, y las siguientes son las clases de entidad correspondientes a la tabla wm_news_material:

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;

}
3.3.3 Análisis del pensamiento de implementación

inserte la descripción de la imagen aquí

1. Envío frontal para publicación o guardado como borrador

2. Los antecedentes juzgan si la solicitud contiene el artículo id.

3. Si la identificación no está incluida, es nueva

3.1 Ejecutar la operación de agregar nuevos artículos

3.2 La relación entre imágenes y materiales del contenido del artículo relacionado

3.3 La relación entre la imagen de portada del artículo asociado y el material

4. Si se incluye la identificación, es una solicitud de modificación

​ 4.1 Eliminar todas las relaciones entre el artículo y el material

​ 4.2 Ejecutar operaciones de modificación

​ 4.3 La relación entre imágenes y materiales del contenido de artículos relacionados

​ 4.4 La relación entre la imagen de portada del artículo asociado y el material

3.3.4 Definición de interfaz
ilustrar
ruta de interfaz /api/v1/canal/enviar
método de solicitud CORREO
parámetro WmNoticiasDto
resultado de la respuesta RespuestaResultado

WmNoticiasDto

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

El formato de datos json pasado por el front-end es:

{
    
    
    "title":"黑马头条项目背景",
    "type":"1",//这个 0 是无图  1 是单图  3 是多图  -1 是自动
    "labels":"黑马头条",
    "publishTime":"2020-03-14T11:35:49.000Z",
    "channelId":1,
    "images":[
        "http://192.168.200.130/group1/M00/00/00/wKjIgl5swbGATaSAAAEPfZfx6Iw790.png"
    ],
    "status":1,
    "content":"[
    {
    
    
        "type":"text",
        "value":"随着智能手机的普及,人们更加习惯于通过手机来看新闻。由于生活节奏的加快,很多人只能利用碎片时间来获取信息,因此,对于移动资讯客户端的需求也越来越高。黑马头条项目正是在这样背景下开发出来。黑马头条项目采用当下火热的微服务+大数据技术架构实现。本项目主要着手于获取最新最热新闻资讯,通过大数据分析用户喜好精确推送咨询新闻"
    },
    {
    
    
        "type":"image",
        "value":"http://192.168.200.130/group1/M00/00/00/wKjIgl5swbGATaSAAAEPfZfx6Iw790.png"
    }
]"
}

Resultado de la respuesta:

{
    
    
    “code”:501,
    “errorMessage”:“参数失效"
}

{
    
    
    “code”:200,
    “errorMessage”:“操作成功"
}

{
    
    
    “code”:501,
    “errorMessage”:“素材引用失效"
}
3.3.5 Realización de funciones

①: agregue un nuevo método en el nuevo WmNewsController

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

②: agregue la clase WmNewsMaterialMapper, la relación entre artículos y materiales debe guardarse en lotes, y el índice debe definir archivos de mapeo y archivos de mapeo correspondientes

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
inserte la descripción de la imagen aquí

<?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>

③: preparación constante de la clase

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

④: Agregar un nuevo método en WmNewsService

/**
 *  发布文章或保存草稿
 * @param dto
 * @return
 */
public ResponseResult submitNews(WmNewsDto dto);

Implementación:

/**
     * 发布修改文章或保存为草稿
     * @param dto
     * @return
     */
@Override
public ResponseResult submitNews(WmNewsDto dto) {
    
    

    //0.条件判断
    if(dto == null || dto.getContent() == null){
    
    
        return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
    }

    //1.保存或修改文章

    WmNews wmNews = new WmNews();
    //属性拷贝 属性名词和类型相同才能拷贝
    BeanUtils.copyProperties(dto,wmNews);
    //封面图片  list---> string
    if(dto.getImages() != null && dto.getImages().size() > 0){
    
    
        //[1dddfsd.jpg,sdlfjldk.jpg]-->   1dddfsd.jpg,sdlfjldk.jpg
        String imageStr = StringUtils.join(dto.getImages(), ",");
        wmNews.setImages(imageStr);
    }
    //如果当前封面类型为自动 -1
    if(dto.getType().equals(WemediaConstants.WM_NEWS_TYPE_AUTO)){
    
    
        wmNews.setType(null);
    }

    saveOrUpdateWmNews(wmNews);

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

    //3.不是草稿,保存文章内容图片与素材的关系
    //获取到文章内容中的图片信息
    List<String> materials =  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查询素材的id
        List<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);
    }

}

④: Controlador

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

Cargar un artículo.
inserte la descripción de la imagen aquí
El artículo se agregó correctamente.
inserte la descripción de la imagen aquí
La consulta de la base de datos fue exitosa
inserte la descripción de la imagen aquí
. Guarde el borrador. No se generan datos en la tabla relacional. Es correcto.
inserte la descripción de la imagen aquí
Pruébelo automáticamente. Multi-imagen y multi
inserte la descripción de la imagen aquí
-imagen son correctos.
inserte la descripción de la imagen aquí

Supongo que te gusta

Origin blog.csdn.net/sinat_38316216/article/details/131509810
Recomendado
Clasificación