当前位置:   article > 正文

黑马头条详解(一)

黑马头条

一、登录功能

1、需求分析

 2、表结构

3、加密方式(手动加密:md5+盐)

4、微服务结构

1.1、代码实现

①定义LoginDto

  1. package com.heima.model.user.dtos;
  2. import lombok.Data;
  3. @Data
  4. public class LoginDto {
  5. /**
  6. * 手机号
  7. */
  8. private String phone;
  9. /**
  10. * 密码
  11. */
  12. private String password;
  13. }

②定义控制层controller

  1. package com.heima.user.controller.v1;
  2. import com.heima.model.common.dtos.ResponseResult;
  3. import com.heima.model.user.dtos.LoginDto;
  4. import com.heima.user.service.ApUserService;
  5. import org.springframework.beans.factory.annotation.Autowired;
  6. import org.springframework.web.bind.annotation.PostMapping;
  7. import org.springframework.web.bind.annotation.RequestBody;
  8. import org.springframework.web.bind.annotation.RequestMapping;
  9. import org.springframework.web.bind.annotation.RestController;
  10. @RestController
  11. @RequestMapping("/api/v1/login")
  12. public class ApUserLoginController {
  13. @Autowired
  14. private ApUserService apUserService;
  15. @PostMapping("/login_auth")
  16. public ResponseResult login(@RequestBody LoginDto dto){
  17. return apUserService.login(dto);
  18. }
  19. }

③定义持久层mapper

  1. package com.heima.user.mapper;
  2. import com.baomidou.mybatisplus.core.mapper.BaseMapper;
  3. import com.heima.model.user.pojos.ApUser;
  4. import org.apache.ibatis.annotations.Mapper;
  5. @Mapper
  6. public interface ApUserMapper extends BaseMapper<ApUser> {
  7. }

④定义service

  1. package com.heima.user.service;
  2. import com.baomidou.mybatisplus.extension.service.IService;
  3. import com.heima.model.common.dtos.ResponseResult;
  4. import com.heima.model.user.dtos.LoginDto;
  5. import com.heima.model.user.pojos.ApUser;
  6. public interface ApUserService extends IService<ApUser> {
  7. /**
  8. * app端登录功能
  9. * @param dto
  10. * @return
  11. */
  12. public ResponseResult login(LoginDto dto);
  13. }

实现类

  1. package com.heima.user.service.impl;
  2. import com.baomidou.mybatisplus.core.toolkit.Wrappers;
  3. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
  4. import com.heima.model.common.dtos.ResponseResult;
  5. import com.heima.model.common.enums.AppHttpCodeEnum;
  6. import com.heima.model.user.dtos.LoginDto;
  7. import com.heima.model.user.pojos.ApUser;
  8. import com.heima.user.mapper.ApUserMapper;
  9. import com.heima.user.service.ApUserService;
  10. import com.heima.utils.common.AppJwtUtil;
  11. import lombok.extern.slf4j.Slf4j;
  12. import org.apache.commons.lang3.StringUtils;
  13. import org.springframework.stereotype.Service;
  14. import org.springframework.transaction.annotation.Transactional;
  15. import org.springframework.util.DigestUtils;
  16. import java.util.HashMap;
  17. import java.util.Map;
  18. @Service
  19. @Transactional
  20. @Slf4j
  21. public class ApUserServiceImpl extends ServiceImpl<ApUserMapper, ApUser> implements ApUserService {
  22. /**
  23. * app端登录功能
  24. * @param dto
  25. * @return
  26. */
  27. @Override
  28. public ResponseResult login(LoginDto dto) {
  29. //1.正常登录 用户名和密码
  30. if(StringUtils.isNoneBlank(dto.getPhone()) && StringUtils.isNoneBlank(dto.getPassword())){
  31. //1.1 根据手机号查询用户信息
  32. ApUser dbUser = getOne(Wrappers.<ApUser>lambdaQuery().eq(ApUser::getPhone, dto.getPhone()));
  33. if(dbUser == null){
  34. return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST,"用户信息不存在");
  35. }
  36. //1.2 比对密码
  37. String salt = dbUser.getSalt();
  38. String password = dto.getPassword();
  39. String pswd = DigestUtils.md5DigestAsHex((password+salt).getBytes());
  40. if(!pswd.equals(dbUser.getPassword())){
  41. return ResponseResult.errorResult(AppHttpCodeEnum.LOGIN_PASSWORD_ERROR);
  42. }
  43. //1.3 返回数据 jwt user
  44. String token = AppJwtUtil.getToken(dbUser.getId().longValue());
  45. Map<String,Object> map = new HashMap<>();
  46. map.put("token",token);
  47. dbUser.setSalt("");
  48. dbUser.setPassword("");
  49. map.put("user",dbUser);
  50. return ResponseResult.okResult(map);
  51. }else {
  52. //2.游客登陆
  53. Map<String,Object> map = new HashMap<>();
  54. map.put("token",AppJwtUtil.getToken(0L));
  55. return ResponseResult.okResult(map);
  56. }
  57. }
  58. }

 ⑤postman测试

 1.2、app端网关

通过网关进行路由,选择对应的微服务

 项目中网关的服务工程结构

 ①在heima-leadnews-gateway导入以下依赖

  1. <dependencies>
  2. <dependency>
  3. <groupId>org.springframework.cloud</groupId>
  4. <artifactId>spring-cloud-starter-gateway</artifactId>
  5. </dependency>
  6. <dependency>
  7. <groupId>com.alibaba.cloud</groupId>
  8. <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
  9. </dependency>
  10. <dependency>
  11. <groupId>com.alibaba.cloud</groupId>
  12. <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
  13. </dependency>
  14. <dependency>
  15. <groupId>io.jsonwebtoken</groupId>
  16. <artifactId>jjwt</artifactId>
  17. </dependency>
  18. </dependencies>

②在heima-leadnews-gateway下创建heima-leadnews-app-gateway微服务

启动类

  1. package com.heima.app.gateway;
  2. import org.springframework.boot.SpringApplication;
  3. import org.springframework.boot.autoconfigure.SpringBootApplication;
  4. import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
  5. @SpringBootApplication
  6. @EnableDiscoveryClient //开启注册中心
  7. public class AppGatewayApplication {
  8. public static void main(String[] args) {
  9. SpringApplication.run(AppGatewayApplication.class,args);
  10. }
  11. }

bootstrap.yml

  1. server:
  2. port: 51601
  3. spring:
  4. application:
  5. name: leadnews-app-gateway
  6. cloud:
  7. nacos:
  8. discovery:
  9. server-addr: 192.168.200.130:8848
  10. config:
  11. server-addr: 192.168.200.130:8848
  12. file-extension: yml

③在nacos的配置中心创建dataid为leadnews-app-gateway的yml配置

  1. spring:
  2. cloud:
  3. gateway:
  4. globalcors:
  5. add-to-simple-url-handler-mapping: true
  6. corsConfigurations:
  7. '[/**]':
  8. allowedHeaders: "*"
  9. allowedOrigins: "*"
  10. allowedMethods:
  11. - GET
  12. - POST
  13. - DELETE
  14. - PUT
  15. - OPTION
  16. routes:
  17. # 平台管理
  18. - id: user
  19. uri: lb://leadnews-user
  20. predicates:
  21. - Path=/user/**
  22. filters:
  23. - StripPrefix= 1

环境搭建完成以后,启动项目网关和用户两个服务,使用postman进行测试

 1.3、全局过滤器实现jwt校验

流程图

思路分析:

1. 用户进入网关开始登陆,网关过滤器进行判断,如果是登录,则路由到后台管理微服务进行登录
2. 用户登录成功,后台管理微服务签发JWT TOKEN信息返回给用户
3. 用户再次进入网关开始访问,网关过滤器接收用户携带的TOKEN 
4. 网关过滤器解析TOKEN ,判断是否有权限,如果有,则放行,如果没有则返回未认证错误

再网关微服务中新建全局过滤器

  1. package com.heima.app.gateway.filter;
  2. import com.heima.app.gateway.util.AppJwtUtil;
  3. import io.jsonwebtoken.Claims;
  4. import lombok.extern.slf4j.Slf4j;
  5. import org.apache.commons.lang.StringUtils;
  6. import org.springframework.cloud.gateway.filter.GatewayFilterChain;
  7. import org.springframework.cloud.gateway.filter.GlobalFilter;
  8. import org.springframework.core.Ordered;
  9. import org.springframework.http.HttpStatus;
  10. import org.springframework.http.server.reactive.ServerHttpRequest;
  11. import org.springframework.http.server.reactive.ServerHttpResponse;
  12. import org.springframework.stereotype.Component;
  13. import org.springframework.web.server.ServerWebExchange;
  14. import reactor.core.publisher.Mono;
  15. @Component
  16. @Slf4j
  17. public class AuthorizeFilter implements Ordered, GlobalFilter {
  18. @Override
  19. public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
  20. //1.获取request和response
  21. ServerHttpRequest request = exchange.getRequest();
  22. ServerHttpResponse response = exchange.getResponse();
  23. //2.判断是否是登录
  24. if (request.getURI().getPath().contains("/login")) {
  25. //放行
  26. return chain.filter(exchange);
  27. }
  28. //3.获取token
  29. String token = request.getHeaders().getFirst("token");
  30. //4.判断token是否存在
  31. if (StringUtils.isBlank(token)) {
  32. response.setStatusCode(HttpStatus.UNAUTHORIZED);
  33. return response.setComplete();//结束请求并返回给前端401
  34. }
  35. //5.判断token是否有效
  36. try {
  37. Claims claimsBody = AppJwtUtil.getClaimsBody(token);
  38. //是否是过期
  39. int result = AppJwtUtil.verifyToken(claimsBody); //-1:有效,0:有效,1:过期,2:过期
  40. if (result == 1 || result == 2) {
  41. response.setStatusCode(HttpStatus.UNAUTHORIZED);
  42. return response.setComplete();//结束请求并返回给前端401
  43. }
  44. }catch (Exception e){
  45. e.printStackTrace();
  46. response.setStatusCode(HttpStatus.UNAUTHORIZED);
  47. return response.setComplete();//结束请求并返回给前端401
  48. }
  49. //6.放行
  50. return chain.filter(exchange);
  51. }
  52. /**
  53. * 设置优先级 值越小 优先级越高
  54. *
  55. * @return
  56. */
  57. @Override
  58. public int getOrder() {
  59. return 0;
  60. }
  61. }

 1.4、app前端项目集成

通过nginx来进行配置,功能如下

- 通过nginx的反向代理功能访问后台的网关资源
- 通过nginx的静态服务器功能访问前端静态页面

二、app端文章列表

需求

文章表结构

 为什么要拆分文章表

使用垂直分表的方式

实现思路及实现功能

2.1、文章列表功能实现

①导入heima-leadnews-article微服务。注意:需要在heima-leadnews-service的pom文件夹中添加子模块信息,如下:

 在nacos中进行如下配置

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

 ②定义控制层controller

  1. package com.heima.article.controller.v1;
  2. import com.heima.article.service.ApArticleService;
  3. import com.heima.common.constants.ArticleConstants;
  4. import com.heima.model.article.dtos.ArticleHomeDto;
  5. import com.heima.model.common.dtos.ResponseResult;
  6. import org.springframework.beans.factory.annotation.Autowired;
  7. import org.springframework.web.bind.annotation.PostMapping;
  8. import org.springframework.web.bind.annotation.RequestBody;
  9. import org.springframework.web.bind.annotation.RequestMapping;
  10. import org.springframework.web.bind.annotation.RestController;
  11. @RestController
  12. @RequestMapping("/api/v1/article")
  13. public class ArticleHomeController {
  14. @Autowired
  15. private ApArticleService apArticleService;
  16. /**
  17. * 加载首页
  18. * @param dto
  19. * @return
  20. */
  21. @PostMapping("/load")
  22. public ResponseResult load(@RequestBody ArticleHomeDto dto) {
  23. return apArticleService.load(dto, ArticleConstants.LOADTYPE_LOAD_MORE);
  24. }
  25. /**
  26. * 加载更多
  27. * @param dto
  28. * @return
  29. */
  30. @PostMapping("/loadmore")
  31. public ResponseResult loadMore(@RequestBody ArticleHomeDto dto) {
  32. return apArticleService.load(dto, ArticleConstants.LOADTYPE_LOAD_MORE);
  33. }
  34. /**
  35. * 加载最新
  36. * @param dto
  37. * @return
  38. */
  39. @PostMapping("/loadnew")
  40. public ResponseResult loadNew(@RequestBody ArticleHomeDto dto) {
  41. return apArticleService.load(dto, ArticleConstants.LOADTYPE_LOAD_NEW);
  42. }
  43. }

③定义持久层mapper

  1. package com.heima.article.mapper;
  2. import com.baomidou.mybatisplus.core.mapper.BaseMapper;
  3. import com.heima.model.article.dtos.ArticleHomeDto;
  4. import com.heima.model.article.pojos.ApArticle;
  5. import org.apache.ibatis.annotations.Mapper;
  6. import org.apache.ibatis.annotations.Param;
  7. import java.util.List;
  8. @Mapper
  9. public interface ApArticleMapper extends BaseMapper<ApArticle> {
  10. public List<ApArticle> loadArticleList(@Param("dto") ArticleHomeDto dto, @Param("type") Short type);
  11. }

对应的映射文件,在resources中新建mapper/ApArticleMapper.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  3. <mapper namespace="com.heima.article.mapper.ApArticleMapper">
  4. <resultMap id="resultMap" type="com.heima.model.article.pojos.ApArticle">
  5. <id column="id" property="id"/>
  6. <result column="title" property="title"/>
  7. <result column="author_id" property="authorId"/>
  8. <result column="author_name" property="authorName"/>
  9. <result column="channel_id" property="channelId"/>
  10. <result column="channel_name" property="channelName"/>
  11. <result column="layout" property="layout"/>
  12. <result column="flag" property="flag"/>
  13. <result column="images" property="images"/>
  14. <result column="labels" property="labels"/>
  15. <result column="likes" property="likes"/>
  16. <result column="collection" property="collection"/>
  17. <result column="comment" property="comment"/>
  18. <result column="views" property="views"/>
  19. <result column="province_id" property="provinceId"/>
  20. <result column="city_id" property="cityId"/>
  21. <result column="county_id" property="countyId"/>
  22. <result column="created_time" property="createdTime"/>
  23. <result column="publish_time" property="publishTime"/>
  24. <result column="sync_status" property="syncStatus"/>
  25. <result column="static_url" property="staticUrl"/>
  26. </resultMap>
  27. <select id="loadArticleList" resultMap="resultMap">
  28. SELECT
  29. aa.*
  30. FROM
  31. `ap_article` aa
  32. LEFT JOIN ap_article_config aac ON aa.id = aac.article_id
  33. <where>
  34. and aac.is_delete != 1
  35. and aac.is_down != 1
  36. <!-- loadmore -->
  37. <if test="type != null and type == 1">
  38. and aa.publish_time <![CDATA[<]]> #{dto.minBehotTime}
  39. </if>
  40. <if test="type != null and type == 2">
  41. and aa.publish_time <![CDATA[>]]> #{dto.maxBehotTime}
  42. </if>
  43. <if test="dto.tag != '__all__'">
  44. and aa.channel_id = #{dto.tag}
  45. </if>
  46. </where>
  47. order by aa.publish_time desc
  48. limit #{dto.size}
  49. </select>
  50. </mapper>

④定义业务层service

  1. package com.heima.article.service;
  2. import com.baomidou.mybatisplus.extension.service.IService;
  3. import com.heima.model.article.dtos.ArticleHomeDto;
  4. import com.heima.model.article.pojos.ApArticle;
  5. import com.heima.model.common.dtos.ResponseResult;
  6. public interface ApArticleService extends IService<ApArticle> {
  7. /**
  8. * 加载文章列表
  9. * @param dto
  10. * @param type 1 加载更多 2 加载最新
  11. * @return
  12. */
  13. public ResponseResult load(ArticleHomeDto dto,Short type);
  14. }

实现类

  1. package com.heima.article.service.impl;
  2. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
  3. import com.heima.article.mapper.ApArticleMapper;
  4. import com.heima.article.service.ApArticleService;
  5. import com.heima.common.constants.ArticleConstants;
  6. import com.heima.model.article.dtos.ArticleHomeDto;
  7. import com.heima.model.article.pojos.ApArticle;
  8. import com.heima.model.common.dtos.ResponseResult;
  9. import lombok.extern.slf4j.Slf4j;
  10. import org.apache.commons.lang.StringUtils;
  11. import org.springframework.beans.factory.annotation.Autowired;
  12. import org.springframework.stereotype.Service;
  13. import org.springframework.transaction.annotation.Transactional;
  14. import java.util.Date;
  15. import java.util.List;
  16. @Service
  17. @Transactional
  18. @Slf4j
  19. public class ApArticleServiceImpl extends ServiceImpl<ApArticleMapper, ApArticle> implements ApArticleService {
  20. @Autowired
  21. private ApArticleMapper apArticleMapper;
  22. private final static short MAX_PAGE_SIZE = 50;
  23. /**
  24. * 加载文章列表
  25. *
  26. * @param dto
  27. * @param type 1 加载更多 2 加载最新
  28. * @return
  29. */
  30. @Override
  31. public ResponseResult load(ArticleHomeDto dto, Short type) {
  32. //1. 校验参数
  33. //分页条数的校验
  34. Integer size = dto.getSize();
  35. if (size == null || size == 0) {
  36. size = 10;
  37. }
  38. //分页的值不超过50
  39. size = Math.min(size, MAX_PAGE_SIZE);
  40. dto.setSize(size);
  41. //校验参数type
  42. if (!type.equals(ArticleConstants.LOADTYPE_LOAD_MORE) && !type.equals(ArticleConstants.LOADTYPE_LOAD_NEW)) {
  43. type = ArticleConstants.LOADTYPE_LOAD_MORE;
  44. }
  45. //频道参数校验
  46. if (StringUtils.isBlank(dto.getTag())) {
  47. dto.setTag(ArticleConstants.DEFAULT_TAG);
  48. }
  49. //时间校验
  50. if (dto.getMaxBehotTime() == null) dto.setMaxBehotTime(new Date());
  51. if (dto.getMinBehotTime() == null) dto.setMinBehotTime(new Date());
  52. //2.查询
  53. List<ApArticle> articleList = apArticleMapper.loadArticleList(dto, type);
  54. //3.返回结果
  55. return ResponseResult.okResult(articleList);
  56. }
  57. }

⑤在app网关的微服务的nacos的配置中心添加文章微服务的路由

  1. spring:
  2. cloud:
  3. gateway:
  4. globalcors:
  5. cors-configurations:
  6. '[/**]': # 匹配所有请求
  7. allowedOrigins: "*" #跨域处理 允许所有的域
  8. allowedMethods: # 支持的方法
  9. - GET
  10. - POST
  11. - PUT
  12. - DELETE
  13. routes:
  14. # 用户微服务
  15. - id: user
  16. uri: lb://leadnews-user
  17. predicates:
  18. - Path=/user/**
  19. filters:
  20. - StripPrefix= 1
  21. # 文章微服务
  22. - id: article
  23. uri: lb://leadnews-article
  24. predicates:
  25. - Path=/article/**
  26. filters:
  27. - StripPrefix= 1

2.2、文章详情功能实现

需求分析

实现方案一

实现方案二 

2.2.1、Freemarker

输出静态化文件

 2.2.2、MinIO

快速入门,list.html文件上传到minio中,并且可以在浏览器中访问

 ①创建工程,引入依赖

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5. <parent>
  6. <artifactId>heima-leadnews-test</artifactId>
  7. <groupId>com.heima</groupId>
  8. <version>1.0-SNAPSHOT</version>
  9. </parent>
  10. <modelVersion>4.0.0</modelVersion>
  11. <artifactId>minio-demo</artifactId>
  12. <properties>
  13. <maven.compiler.source>8</maven.compiler.source>
  14. <maven.compiler.target>8</maven.compiler.target>
  15. </properties>
  16. <dependencies>
  17. <dependency>
  18. <groupId>io.minio</groupId>
  19. <artifactId>minio</artifactId>
  20. <version>7.1.0</version>
  21. </dependency>
  22. <dependency>
  23. <groupId>org.springframework.boot</groupId>
  24. <artifactId>spring-boot-starter-web</artifactId>
  25. </dependency>
  26. <dependency>
  27. <groupId>org.springframework.boot</groupId>
  28. <artifactId>spring-boot-starter-test</artifactId>
  29. </dependency>
  30. </dependencies>
  31. </project>

②创建启动类

  1. package com.heima.minio;
  2. import org.springframework.boot.SpringApplication;
  3. import org.springframework.boot.autoconfigure.SpringBootApplication;
  4. @SpringBootApplication
  5. public class MinIOApplication {
  6. public static void main(String[] args) {
  7. SpringApplication.run(MinIOApplication.class,args);
  8. }
  9. }

③创建测试类,上传html文件

  1. package com.heima.minio.test;
  2. import io.minio.MinioClient;
  3. import io.minio.PutObjectArgs;
  4. import java.io.FileInputStream;
  5. public class MinIOTest {
  6. public static void main(String[] args) {
  7. FileInputStream fileInputStream = null;
  8. try {
  9. fileInputStream = new FileInputStream("D:\\list.html");;
  10. //1.创建minio链接客户端
  11. MinioClient minioClient = MinioClient.builder().credentials("minio", "minio123").endpoint("http://192.168.200.130:9000").build();
  12. //2.上传
  13. PutObjectArgs putObjectArgs = PutObjectArgs.builder()
  14. .object("list.html")//文件名
  15. .contentType("text/html")//文件类型
  16. .bucket("leadnews")//桶名词 与minio创建的名词一致
  17. .stream(fileInputStream, fileInputStream.available(), -1) //文件流
  18. .build();
  19. minioClient.putObject(putObjectArgs);
  20. System.out.println("http://192.168.200.130:9000/leadnews/list.html");
  21. } catch (Exception ex) {
  22. ex.printStackTrace();
  23. }
  24. }
  25. }

封装MinIO为starter

2.2.3、静态模板展示 

 ①在文章微服务中添加FreeMarker和Minio依赖

  1. <dependencies>
  2. <dependency>
  3. <groupId>org.springframework.boot</groupId>
  4. <artifactId>spring-boot-starter-freemarker</artifactId>
  5. </dependency>
  6. <dependency>
  7. <groupId>com.heima</groupId>
  8. <artifactId>heima-file-starter</artifactId>
  9. <version>1.0-SNAPSHOT</version>
  10. </dependency>
  11. </dependencies>

②新建ApArticleContentMapper

  1. package com.heima.article.mapper;
  2. import com.baomidou.mybatisplus.core.mapper.BaseMapper;
  3. import com.heima.model.article.pojos.ApArticleContent;
  4. import org.apache.ibatis.annotations.Mapper;
  5. @Mapper
  6. public interface ApArticleContentMapper extends BaseMapper<ApArticleContent> {
  7. }

③在artile微服务中新增测试类(后期新增文章的时候创建详情静态页,目前暂时手动生成)

  1. package com.heima.article.test;
  2. import com.alibaba.fastjson.JSONArray;
  3. import com.baomidou.mybatisplus.core.toolkit.Wrappers;
  4. import com.heima.article.ArticleApplication;
  5. import com.heima.article.mapper.ApArticleContentMapper;
  6. import com.heima.article.mapper.ApArticleMapper;
  7. import com.heima.file.service.FileStorageService;
  8. import com.heima.model.article.pojos.ApArticle;
  9. import com.heima.model.article.pojos.ApArticleContent;
  10. import freemarker.template.Configuration;
  11. import freemarker.template.Template;
  12. import org.apache.commons.lang3.StringUtils;
  13. import org.junit.Test;
  14. import org.junit.runner.RunWith;
  15. import org.springframework.beans.factory.annotation.Autowired;
  16. import org.springframework.boot.test.context.SpringBootTest;
  17. import org.springframework.test.context.junit4.SpringRunner;
  18. import java.io.ByteArrayInputStream;
  19. import java.io.InputStream;
  20. import java.io.StringWriter;
  21. import java.util.HashMap;
  22. import java.util.Map;
  23. @SpringBootTest(classes = ArticleApplication.class)
  24. @RunWith(SpringRunner.class)
  25. public class ArticleFreemarkerTest {
  26. @Autowired
  27. private Configuration configuration;
  28. @Autowired
  29. private FileStorageService fileStorageService;
  30. @Autowired
  31. private ApArticleMapper apArticleMapper;
  32. @Autowired
  33. private ApArticleContentMapper apArticleContentMapper;
  34. @Test
  35. public void createStaticUrlTest() throws Exception {
  36. //1.获取文章内容
  37. ApArticleContent apArticleContent = apArticleContentMapper.selectOne(Wrappers.<ApArticleContent>lambdaQuery().eq(ApArticleContent::getArticleId, 1390536764510310401L));
  38. if(apArticleContent != null && StringUtils.isNotBlank(apArticleContent.getContent())){
  39. //2.文章内容通过freemarker生成html文件
  40. StringWriter out = new StringWriter();
  41. Template template = configuration.getTemplate("article.ftl");
  42. Map<String, Object> params = new HashMap<>();
  43. params.put("content", JSONArray.parseArray(apArticleContent.getContent()));
  44. template.process(params, out);
  45. InputStream is = new ByteArrayInputStream(out.toString().getBytes());
  46. //3.把html文件上传到minio中
  47. String path = fileStorageService.uploadHtmlFile("", apArticleContent.getArticleId() + ".html", is);
  48. //4.修改ap_article表,保存static_url字段
  49. ApArticle article = new ApArticle();
  50. article.setId(apArticleContent.getArticleId());
  51. article.setStaticUrl(path);
  52. apArticleMapper.updateById(article);
  53. }
  54. }
  55. }

三、自媒体文章发布

功能

自媒体端环境搭建

前端

3.1、自媒体素材管理

 功能

表结构

问题

1.在素材表中的用户信息如何得到? user_id
2.素材的信息保存到什么位置? 

3.1.1、素材表中的用户信息如何得到问题解决思路

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

  1. package com.heima.wemedia.gateway.filter;
  2. import com.heima.wemedia.gateway.util.AppJwtUtil;
  3. import io.jsonwebtoken.Claims;
  4. import lombok.extern.slf4j.Slf4j;
  5. import org.apache.commons.lang.StringUtils;
  6. import org.springframework.cloud.gateway.filter.GatewayFilterChain;
  7. import org.springframework.cloud.gateway.filter.GlobalFilter;
  8. import org.springframework.core.Ordered;
  9. import org.springframework.http.HttpStatus;
  10. import org.springframework.http.server.reactive.ServerHttpRequest;
  11. import org.springframework.http.server.reactive.ServerHttpResponse;
  12. import org.springframework.stereotype.Component;
  13. import org.springframework.web.server.ServerWebExchange;
  14. import reactor.core.publisher.Mono;
  15. @Component
  16. @Slf4j
  17. public class AuthorizeFilter implements Ordered, GlobalFilter {
  18. @Override
  19. public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
  20. //1.获取request和response对象
  21. ServerHttpRequest request = exchange.getRequest();
  22. ServerHttpResponse response = exchange.getResponse();
  23. //2.判断是否是登录
  24. if (request.getURI().getPath().contains("/login")) {
  25. //放行
  26. return chain.filter(exchange);
  27. }
  28. //3.获取token
  29. String token = request.getHeaders().getFirst("token");
  30. //4.判断token是否存在
  31. if (StringUtils.isBlank(token)) {
  32. response.setStatusCode(HttpStatus.UNAUTHORIZED);
  33. return response.setComplete();
  34. }
  35. //5.判断token是否有效
  36. try {
  37. Claims claimsBody = AppJwtUtil.getClaimsBody(token);
  38. //是否是过期
  39. int result = AppJwtUtil.verifyToken(claimsBody);
  40. if (result == 1 || result == 2) {
  41. response.setStatusCode(HttpStatus.UNAUTHORIZED);
  42. return response.setComplete();
  43. }
  44. //———————————————————————————————————————————————————————————————————————————————————————
  45. //解决思路
  46. //获取用户信息
  47. Object userId = claimsBody.get("id");
  48. //存入header
  49. ServerHttpRequest serverHttpRequest = request.mutate().headers(httpHeaders -> {
  50. httpHeaders.add("userId", userId + "");
  51. }).build();
  52. //重置请求
  53. exchange.mutate().request(serverHttpRequest);
  54. //———————————————————————————————————————————————————————————————————————————————————————
  55. } catch (Exception e) {
  56. e.printStackTrace();
  57. }
  58. //6.放行
  59. return chain.filter(exchange);
  60. }
  61. /**
  62. * 优先级设置 值越小 优先级越高
  63. *
  64. * @return
  65. */
  66. @Override
  67. public int getOrder() {
  68. return 0;
  69. }
  70. }

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

ThreadLocal

  1. package com.heima.utils.thread;
  2. import com.heima.model.wemedia.pojos.WmUser;
  3. public class WmThreadLocalUtil {
  4. private final static ThreadLocal<WmUser> WM_USER_THREAD_LOCAL = new ThreadLocal<>();
  5. /**
  6. * 添加用户
  7. * @param wmUser
  8. */
  9. public static void setUser(WmUser wmUser){
  10. WM_USER_THREAD_LOCAL.set(wmUser);
  11. }
  12. /**
  13. * 获取用户
  14. */
  15. public static WmUser getUser(){
  16. return WM_USER_THREAD_LOCAL.get();
  17. }
  18. /**
  19. * 清理用户
  20. */
  21. public static void clear(){
  22. WM_USER_THREAD_LOCAL.remove();
  23. }
  24. }

拦截器 

  1. package com.heima.wemedia.interceptor;
  2. import com.heima.model.wemedia.pojos.WmUser;
  3. import com.heima.utils.thread.WmThreadLocalUtil;
  4. import org.springframework.web.servlet.HandlerInterceptor;
  5. import org.springframework.web.servlet.ModelAndView;
  6. import javax.servlet.http.HttpServletRequest;
  7. import javax.servlet.http.HttpServletResponse;
  8. public class WmTokenInterceptor implements HandlerInterceptor {
  9. /**
  10. * 得到header信息并存入当前线程中
  11. * @param request
  12. * @param response
  13. * @param handler
  14. * @return
  15. * @throws Exception
  16. */
  17. @Override
  18. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
  19. String userId = request.getHeader("userId");
  20. if (userId!=null) {
  21. //存入当前线程中
  22. WmUser wmUser = new WmUser();
  23. wmUser.setId(Integer.valueOf(userId));
  24. WmThreadLocalUtil.setUser(wmUser);
  25. }
  26. return true;
  27. }
  28. /**
  29. * 清理线程中的数据
  30. * @param request
  31. * @param response
  32. * @param handler
  33. * @param modelAndView
  34. * @throws Exception
  35. */
  36. @Override
  37. public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
  38. WmThreadLocalUtil.clear();
  39. }
  40. }

配置拦截器生效

  1. package com.heima.wemedia.config;
  2. import com.heima.wemedia.interceptor.WmTokenInterceptor;
  3. import org.springframework.context.annotation.Configuration;
  4. import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
  5. import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
  6. @Configuration
  7. public class WebMvcConfig implements WebMvcConfigurer {
  8. @Override
  9. public void addInterceptors(InterceptorRegistry registry) {
  10. registry.addInterceptor(new WmTokenInterceptor()).addPathPatterns("/**");
  11. }
  12. }

3.1.2、图片上传功能

 ①在自媒体导入heima-file-starter(MinIO)

  1. <dependencies>
  2. <dependency>
  3. <groupId>com.heima</groupId>
  4. <artifactId>heima-file-starter</artifactId>
  5. <version>1.0-SNAPSHOT</version>
  6. </dependency>
  7. </dependencies>

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

  1. minio:
  2. accessKey: minio
  3. secretKey: minio123
  4. bucket: leadnews
  5. endpoint: http://192.168.200.130:9000
  6. readPath: http://192.168.200.130:9000

③创建WmMaterialController

  1. package com.heima.wemedia.controller.v1;
  2. import com.heima.model.common.dtos.ResponseResult;
  3. import com.heima.wemedia.service.WmMaterialService;
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.web.bind.annotation.PostMapping;
  6. import org.springframework.web.bind.annotation.RequestMapping;
  7. import org.springframework.web.bind.annotation.RestController;
  8. import org.springframework.web.multipart.MultipartFile;
  9. @RestController
  10. @RequestMapping("/api/v1/material")
  11. public class WmMaterialController {
  12. @Autowired
  13. private WmMaterialService wmMaterialService;
  14. @PostMapping("/upload_picture")
  15. public ResponseResult uploadPicture(MultipartFile multipartFile) {
  16. return wmMaterialService.uploadPicture(multipartFile);
  17. }
  18. }

④ 创建mapper

  1. package com.heima.wemedia.mapper;
  2. import com.baomidou.mybatisplus.core.mapper.BaseMapper;
  3. import com.heima.model.wemedia.pojos.WmMaterial;
  4. import org.apache.ibatis.annotations.Mapper;
  5. @Mapper
  6. public interface WmMaterialMapper extends BaseMapper<WmMaterial> {
  7. }

⑤service

  1. package com.heima.wemedia.service;
  2. import com.baomidou.mybatisplus.extension.service.IService;
  3. import com.heima.model.common.dtos.ResponseResult;
  4. import com.heima.model.wemedia.pojos.WmMaterial;
  5. import org.springframework.web.multipart.MultipartFile;
  6. public interface WmMaterialService extends IService<WmMaterial> {
  7. /**
  8. * 图片上传
  9. * @param multipartFile
  10. * @return
  11. */
  12. public ResponseResult uploadPicture(MultipartFile multipartFile);
  13. }

实现类

  1. package com.heima.wemedia.service.impl;
  2. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
  3. import com.heima.file.service.FileStorageService;
  4. import com.heima.model.common.dtos.ResponseResult;
  5. import com.heima.model.common.enums.AppHttpCodeEnum;
  6. import com.heima.model.wemedia.pojos.WmMaterial;
  7. import com.heima.utils.thread.WmThreadLocalUtil;
  8. import com.heima.wemedia.mapper.WmMaterialMapper;
  9. import com.heima.wemedia.service.WmMaterialService;
  10. import lombok.extern.slf4j.Slf4j;
  11. import org.springframework.beans.factory.annotation.Autowired;
  12. import org.springframework.stereotype.Service;
  13. import org.springframework.transaction.annotation.Transactional;
  14. import org.springframework.web.multipart.MultipartFile;
  15. import java.io.IOException;
  16. import java.util.Date;
  17. import java.util.UUID;
  18. @Slf4j
  19. @Service
  20. @Transactional
  21. public class WmMaterialServiceImpl extends ServiceImpl<WmMaterialMapper, WmMaterial> implements WmMaterialService {
  22. @Autowired
  23. private FileStorageService fileStorageService;
  24. /**
  25. * 图片上传
  26. *
  27. * @param multipartFile
  28. * @return
  29. */
  30. @Override
  31. public ResponseResult uploadPicture(MultipartFile multipartFile) {
  32. //1.检查参数
  33. if (multipartFile == null || multipartFile.getSize() == 0) {
  34. return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
  35. }
  36. //2.上传图片到minIO
  37. String fileName = UUID.randomUUID().toString().replace("-", "");
  38. //原图片的名字a.jpg
  39. String originalFilename = multipartFile.getOriginalFilename();
  40. String postfix = originalFilename.substring(originalFilename.lastIndexOf("."));
  41. String fileId = null;
  42. try {
  43. fileId = fileStorageService.uploadImgFile("", fileName + postfix, multipartFile.getInputStream());
  44. log.info("上传图片到MinIO中,fileID:{}", fileId);
  45. } catch (IOException e) {
  46. e.printStackTrace();
  47. log.info("WmMaterialServiceImpl-上传文件失败");
  48. }
  49. //3.保存数据到数据库
  50. WmMaterial wmMaterial = new WmMaterial();
  51. wmMaterial.setUserId(WmThreadLocalUtil.getUser().getId());
  52. wmMaterial.setUrl(fileId);
  53. wmMaterial.setIsCollection((short) 0);
  54. wmMaterial.setType((short) 0);
  55. wmMaterial.setCreatedTime(new Date());
  56. save(wmMaterial);
  57. //4.返回结果
  58. return ResponseResult.okResult(wmMaterial);
  59. }
  60. }

3.1.3、素材列表查询

接口定义

 ①创建WmMaterialDto

  1. package com.heima.model.wemedia.dtos;
  2. import com.heima.model.common.dtos.PageRequestDto;
  3. import lombok.Data;
  4. @Data
  5. public class WmMaterialDto extends PageRequestDto {
  6. /**
  7. * 1 收藏
  8. * 0 未收藏
  9. */
  10. private Short isCollection;
  11. }

②在WmMaterialController添加方法

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

③ 在WmMaterialService中新增方法

  1. /**
  2. * 素材列表查询
  3. * @param dto
  4. * @return
  5. */
  6. public ResponseResult findList( WmMaterialDto dto);

实现类中实现

  1. /**
  2. * 素材列表查询
  3. *
  4. * @param dto
  5. * @return
  6. */
  7. @Override
  8. public ResponseResult findList(WmMaterialDto dto) {
  9. //1.检查参数
  10. dto.checkParam();
  11. //2.分页查询
  12. IPage page = new Page(dto.getPage(), dto.getSize());
  13. LambdaQueryWrapper<WmMaterial> lambdaQueryWrapper = new LambdaQueryWrapper<>();
  14. //是否收藏
  15. if (dto.getIsCollection() != null && dto.getIsCollection() == 1) {
  16. lambdaQueryWrapper.eq(WmMaterial::getIsCollection, dto.getIsCollection());
  17. }
  18. //按照用户查询
  19. lambdaQueryWrapper.eq(WmMaterial::getUserId, WmThreadLocalUtil.getUser().getId());
  20. //按照时间倒叙查询
  21. lambdaQueryWrapper.orderByDesc(WmMaterial::getCreatedTime);
  22. page = page(page, lambdaQueryWrapper);
  23. //3.返回结果
  24. ResponseResult responseResult = new PageResponseResult(dto.getPage(), dto.getSize(), (int) page.getTotal());
  25. responseResult.setData(page.getRecords());
  26. return responseResult;
  27. }

④在引导类WemediaApplication中添加mybatis-plus的分页拦截器

  1. @Bean
  2. public MybatisPlusInterceptor mybatisPlusInterceptor() {
  3. MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
  4. interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
  5. return interceptor;
  6. }

 3.2、自媒体文章管理

3.2.1、查询所有频道

功能如下

功能实现 

①创建WmchannelController

  1. package com.heima.wemedia.controller.v1;
  2. import com.heima.model.common.dtos.ResponseResult;
  3. import com.heima.wemedia.service.WmChannelService;
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.web.bind.annotation.GetMapping;
  6. import org.springframework.web.bind.annotation.RequestMapping;
  7. import org.springframework.web.bind.annotation.RestController;
  8. @RestController
  9. @RequestMapping("/api/v1/channel")
  10. public class WmchannelController {
  11. @Autowired
  12. private WmChannelService wmChannelService;
  13. @GetMapping("/channels")
  14. public ResponseResult findAll(){
  15. return wmChannelService.findAll();
  16. }
  17. }

②创建WmChannelMapper

  1. package com.heima.wemedia.mapper;
  2. import com.baomidou.mybatisplus.core.mapper.BaseMapper;
  3. import com.heima.model.wemedia.pojos.WmChannel;
  4. import org.apache.ibatis.annotations.Mapper;
  5. @Mapper
  6. public interface WmChannelMapper extends BaseMapper<WmChannel> {
  7. }

③创建 WmChannelService 

  1. package com.heima.wemedia.service;
  2. import com.baomidou.mybatisplus.extension.service.IService;
  3. import com.heima.model.common.dtos.ResponseResult;
  4. import com.heima.model.wemedia.pojos.WmChannel;
  5. public interface WmChannelService extends IService<WmChannel> {
  6. /**
  7. * 查询所有频道
  8. * @return
  9. */
  10. public ResponseResult findAll();
  11. }

实现类

  1. package com.heima.wemedia.service.impl;
  2. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
  3. import com.heima.model.common.dtos.ResponseResult;
  4. import com.heima.model.wemedia.pojos.WmChannel;
  5. import com.heima.wemedia.mapper.WmChannelMapper;
  6. import com.heima.wemedia.service.WmChannelService;
  7. import lombok.extern.slf4j.Slf4j;
  8. import org.springframework.stereotype.Service;
  9. import org.springframework.transaction.annotation.Transactional;
  10. @Service
  11. @Transactional
  12. @Slf4j
  13. public class WmChannelServiceImpl extends ServiceImpl<WmChannelMapper, WmChannel> implements WmChannelService {
  14. /**
  15. * 查询所有频道
  16. * @return
  17. */
  18. @Override
  19. public ResponseResult findAll() {
  20. return ResponseResult.okResult(list());
  21. }
  22. }

3.2.2、查询自媒体文章

接口

功能实现

①创建WmNewsController

  1. package com.heima.wemedia.controller.v1;
  2. import com.heima.model.common.dtos.ResponseResult;
  3. import com.heima.model.wemedia.dtos.WmNewsPageReqDto;
  4. import com.heima.wemedia.service.WmNewsService;
  5. import org.springframework.beans.factory.annotation.Autowired;
  6. import org.springframework.web.bind.annotation.PostMapping;
  7. import org.springframework.web.bind.annotation.RequestBody;
  8. import org.springframework.web.bind.annotation.RequestMapping;
  9. import org.springframework.web.bind.annotation.RestController;
  10. @RestController
  11. @RequestMapping("/api/v1/news")
  12. public class WmNewsController {
  13. @Autowired
  14. private WmNewsService wmNewsService;
  15. @PostMapping("/list")
  16. public ResponseResult findAll(@RequestBody WmNewsPageReqDto dto){
  17. return wmNewsService.findAll(dto);
  18. }
  19. }

②创建 WmNewsMapper  

  1. package com.heima.wemedia.mapper;
  2. import com.baomidou.mybatisplus.core.mapper.BaseMapper;
  3. import com.heima.model.wemedia.pojos.WmNews;
  4. import org.apache.ibatis.annotations.Mapper;
  5. @Mapper
  6. public interface WmNewsMapper extends BaseMapper<WmNews> {
  7. }

③创建WmNewsService 

  1. package com.heima.wemedia.service;
  2. import com.baomidou.mybatisplus.extension.service.IService;
  3. import com.heima.model.common.dtos.ResponseResult;
  4. import com.heima.model.wemedia.dtos.WmNewsPageReqDto;
  5. import com.heima.model.wemedia.pojos.WmNews;
  6. public interface WmNewsService extends IService<WmNews> {
  7. /**
  8. * 查询文章
  9. * @param dto
  10. * @return
  11. */
  12. public ResponseResult findAll(WmNewsPageReqDto dto);
  13. }

 实现类

  1. package com.heima.wemedia.service.impl;
  2. import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
  3. import com.baomidou.mybatisplus.core.metadata.IPage;
  4. import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
  5. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
  6. import com.heima.model.common.dtos.PageResponseResult;
  7. import com.heima.model.common.dtos.ResponseResult;
  8. import com.heima.model.common.enums.AppHttpCodeEnum;
  9. import com.heima.model.wemedia.dtos.WmNewsPageReqDto;
  10. import com.heima.model.wemedia.pojos.WmNews;
  11. import com.heima.model.wemedia.pojos.WmUser;
  12. import com.heima.utils.thread.WmThreadLocalUtil;
  13. import com.heima.wemedia.mapper.WmNewsMapper;
  14. import com.heima.wemedia.service.WmNewsService;
  15. import lombok.extern.slf4j.Slf4j;
  16. import org.apache.commons.lang3.StringUtils;
  17. import org.springframework.stereotype.Service;
  18. import org.springframework.transaction.annotation.Transactional;
  19. @Service
  20. @Slf4j
  21. @Transactional
  22. public class WmNewsServiceImpl extends ServiceImpl<WmNewsMapper, WmNews> implements WmNewsService {
  23. /**
  24. * 查询文章
  25. * @param dto
  26. * @return
  27. */
  28. @Override
  29. public ResponseResult findAll(WmNewsPageReqDto dto) {
  30. //1.检查参数
  31. if(dto == null){
  32. return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
  33. }
  34. //分页参数检查
  35. dto.checkParam();
  36. //获取当前登录人的信息
  37. WmUser user = WmThreadLocalUtil.getUser();
  38. if(user == null){
  39. return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
  40. }
  41. //2.分页条件查询
  42. IPage page = new Page(dto.getPage(),dto.getSize());
  43. LambdaQueryWrapper<WmNews> lambdaQueryWrapper = new LambdaQueryWrapper<>();
  44. //状态精确查询
  45. if(dto.getStatus() != null){
  46. lambdaQueryWrapper.eq(WmNews::getStatus,dto.getStatus());
  47. }
  48. //频道精确查询
  49. if(dto.getChannelId() != null){
  50. lambdaQueryWrapper.eq(WmNews::getChannelId,dto.getChannelId());
  51. }
  52. //时间范围查询
  53. if(dto.getBeginPubDate()!=null && dto.getEndPubDate()!=null){
  54. lambdaQueryWrapper.between(WmNews::getPublishTime,dto.getBeginPubDate(),dto.getEndPubDate());
  55. }
  56. //关键字模糊查询
  57. if(StringUtils.isNotBlank(dto.getKeyword())){
  58. lambdaQueryWrapper.like(WmNews::getTitle,dto.getKeyword());
  59. }
  60. //查询当前登录用户的文章
  61. lambdaQueryWrapper.eq(WmNews::getUserId,user.getId());
  62. //发布时间倒序查询
  63. lambdaQueryWrapper.orderByDesc(WmNews::getCreatedTime);
  64. page = page(page,lambdaQueryWrapper);
  65. //3.结果返回
  66. ResponseResult responseResult = new PageResponseResult(dto.getPage(),dto.getSize(),(int)page.getTotal());
  67. responseResult.setData(page.getRecords());
  68. return responseResult;
  69. }
  70. }

3.2.3、发布文章

 需求

表结构

 实现思路

 接口

WmNewsDto数据结构

 响应结果

功能实现

①在新增WmNewsController中新增方法

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

②新增WmNewsMaterialMapper类,文章与素材的关联关系需要批量保存,索引需要定义mapper文件和对应的映射文件

  1. package com.heima.wemedia.mapper;
  2. import com.baomidou.mybatisplus.core.mapper.BaseMapper;
  3. import com.heima.model.wemedia.pojos.WmNewsMaterial;
  4. import org.apache.ibatis.annotations.Mapper;
  5. import org.apache.ibatis.annotations.Param;
  6. import java.util.List;
  7. @Mapper
  8. public interface WmNewsMaterialMapper extends BaseMapper<WmNewsMaterial> {
  9. void saveRelations(@Param("materialIds") List<Integer> materialIds,@Param("newsId") Integer newsId, @Param("type")Short type);
  10. }

对应的WmNewsMaterialMapper.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  3. <mapper namespace="com.heima.wemedia.mapper.WmNewsMaterialMapper">
  4. <insert id="saveRelations">
  5. insert into wm_news_material (material_id,news_id,type,ord)
  6. values
  7. <foreach collection="materialIds" index="ord" item="mid" separator=",">
  8. (#{mid},#{newsId},#{type},#{ord})
  9. </foreach>
  10. </insert>
  11. </mapper>

③在WmNewsService中新增方法

  1. /**
  2. * 发布文章或保存为草稿
  3. * @param dto
  4. * @return
  5. */
  6. public ResponseResult submitNews(WmNewsDto dto);

 实现类中对应的实现方法

  1. /**
  2. * 发布修改文章或保存为草稿
  3. * @param dto
  4. * @return
  5. */
  6. @Override
  7. public ResponseResult submitNews(WmNewsDto dto) {
  8. //0.条件判断
  9. if(dto == null || dto.getContent() == null){
  10. return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
  11. }
  12. //1.保存或修改文章
  13. WmNews wmNews = new WmNews();
  14. //属性拷贝 属性名词和类型相同才能拷贝
  15. BeanUtils.copyProperties(dto,wmNews);
  16. //封面图片 list---> string
  17. if(dto.getImages() != null && dto.getImages().size() > 0){
  18. //[1dddfsd.jpg,sdlfjldk.jpg]--> 1dddfsd.jpg,sdlfjldk.jpg
  19. String imageStr = StringUtils.join(dto.getImages(), ",");
  20. wmNews.setImages(imageStr);
  21. }
  22. //如果当前封面类型为自动 -1
  23. if(dto.getType().equals(WemediaConstants.WM_NEWS_TYPE_AUTO)){
  24. wmNews.setType(null);
  25. }
  26. saveOrUpdateWmNews(wmNews);
  27. //2.判断是否为草稿 如果为草稿结束当前方法
  28. if(dto.getStatus().equals(WmNews.Status.NORMAL.getCode())){
  29. return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
  30. }
  31. //3.不是草稿,保存文章内容图片与素材的关系
  32. //获取到文章内容中的图片信息
  33. List<String> materials = ectractUrlInfo(dto.getContent());
  34. saveRelativeInfoForContent(materials,wmNews.getId());
  35. //4.不是草稿,保存文章封面图片与素材的关系,如果当前布局是自动,需要匹配封面图片
  36. saveRelativeInfoForCover(dto,wmNews,materials);
  37. return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
  38. }
  39. /**
  40. * 第一个功能:如果当前封面类型为自动,则设置封面类型的数据
  41. * 匹配规则:
  42. * 1,如果内容图片大于等于1,小于3 单图 type 1
  43. * 2,如果内容图片大于等于3 多图 type 3
  44. * 3,如果内容没有图片,无图 type 0
  45. *
  46. * 第二个功能:保存封面图片与素材的关系
  47. * @param dto
  48. * @param wmNews
  49. * @param materials
  50. */
  51. private void saveRelativeInfoForCover(WmNewsDto dto, WmNews wmNews, List<String> materials) {
  52. List<String> images = dto.getImages();
  53. //如果当前封面类型为自动,则设置封面类型的数据
  54. if(dto.getType().equals(WemediaConstants.WM_NEWS_TYPE_AUTO)){
  55. //多图
  56. if(materials.size() >= 3){
  57. wmNews.setType(WemediaConstants.WM_NEWS_MANY_IMAGE);
  58. images = materials.stream().limit(3).collect(Collectors.toList());
  59. }else if(materials.size() >= 1 && materials.size() < 3){
  60. //单图
  61. wmNews.setType(WemediaConstants.WM_NEWS_SINGLE_IMAGE);
  62. images = materials.stream().limit(1).collect(Collectors.toList());
  63. }else {
  64. //无图
  65. wmNews.setType(WemediaConstants.WM_NEWS_NONE_IMAGE);
  66. }
  67. //修改文章
  68. if(images != null && images.size() > 0){
  69. wmNews.setImages(StringUtils.join(images,","));
  70. }
  71. updateById(wmNews);
  72. }
  73. if(images != null && images.size() > 0){
  74. saveRelativeInfo(images,wmNews.getId(),WemediaConstants.WM_COVER_REFERENCE);
  75. }
  76. }
  77. /**
  78. * 处理文章内容图片与素材的关系
  79. * @param materials
  80. * @param newsId
  81. */
  82. private void saveRelativeInfoForContent(List<String> materials, Integer newsId) {
  83. saveRelativeInfo(materials,newsId,WemediaConstants.WM_CONTENT_REFERENCE);
  84. }
  85. @Autowired
  86. private WmMaterialMapper wmMaterialMapper;
  87. /**
  88. * 保存文章图片与素材的关系到数据库中
  89. * @param materials
  90. * @param newsId
  91. * @param type
  92. */
  93. private void saveRelativeInfo(List<String> materials, Integer newsId, Short type) {
  94. if(materials!=null && !materials.isEmpty()){
  95. //通过图片的url查询素材的id
  96. List<WmMaterial> dbMaterials = wmMaterialMapper.selectList(Wrappers.<WmMaterial>lambdaQuery().in(WmMaterial::getUrl, materials));
  97. //判断素材是否有效
  98. if(dbMaterials==null || dbMaterials.size() == 0){
  99. //手动抛出异常 第一个功能:能够提示调用者素材失效了,第二个功能,进行数据的回滚
  100. throw new CustomException(AppHttpCodeEnum.MATERIASL_REFERENCE_FAIL);
  101. }
  102. if(materials.size() != dbMaterials.size()){
  103. throw new CustomException(AppHttpCodeEnum.MATERIASL_REFERENCE_FAIL);
  104. }
  105. List<Integer> idList = dbMaterials.stream().map(WmMaterial::getId).collect(Collectors.toList());
  106. //批量保存
  107. wmNewsMaterialMapper.saveRelations(idList,newsId,type);
  108. }
  109. }
  110. /**
  111. * 提取文章内容中的图片信息
  112. * @param content
  113. * @return
  114. */
  115. private List<String> ectractUrlInfo(String content) {
  116. List<String> materials = new ArrayList<>();
  117. List<Map> maps = JSON.parseArray(content, Map.class);
  118. for (Map map : maps) {
  119. if(map.get("type").equals("image")){
  120. String imgUrl = (String) map.get("value");
  121. materials.add(imgUrl);
  122. }
  123. }
  124. return materials;
  125. }
  126. @Autowired
  127. private WmNewsMaterialMapper wmNewsMaterialMapper;
  128. /**
  129. * 保存或修改文章
  130. * @param wmNews
  131. */
  132. private void saveOrUpdateWmNews(WmNews wmNews) {
  133. //补全属性
  134. wmNews.setUserId(WmThreadLocalUtil.getUser().getId());
  135. wmNews.setCreatedTime(new Date());
  136. wmNews.setSubmitedTime(new Date());
  137. wmNews.setEnable((short)1);//默认上架
  138. if(wmNews.getId() == null){
  139. //保存
  140. save(wmNews);
  141. }else {
  142. //修改
  143. //删除文章图片与素材的关系
  144. wmNewsMaterialMapper.delete(Wrappers.<WmNewsMaterial>lambdaQuery().eq(WmNewsMaterial::getNewsId,wmNews.getId()));
  145. updateById(wmNews);
  146. }
  147. }

本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号