跳转至

前后端分离项目博客

image-20240716144542405

我觉得用注解更加简便,相比各种继承

前端工程在后台运行

使用命令

Start-Process -FilePath "npm" -ArgumentList "run dev" -NoNewWindow -PassThru

原本命令为npm run dev

进程序号会打印出来

查询进程

Get-Process

想要删除使用id号

Stop-Process -Id 11896

按时间查看进程

 Get-Process | Sort-Object StartTime

redis后台运行

 Start-Process -FilePath "redis-server"  -WindowStyle Hidden

记录进程号(该进程号为cmd进程号,非运行的nodejs工程进程号)

在启动进程时记录其进程ID,可以使用 Start-Process 命令的 -PassThru 参数。这将返回一个包含进程信息的对象,你可以从该对象中获取进程ID,并将其记录到文件或变量中。

示例:启动进程并记录进程ID

powershell复制代码# 启动进程并返回进程对象
$process = Start-Process -FilePath "npm" -ArgumentList "run dev" -NoNewWindow -PassThru

# 输出进程ID和进程名称
$process.Id
$process.ProcessName

# 保存进程ID到文件
$process.Id > "process_id.txt"

示例:从文件读取进程ID并终止进程

  1. 启动进程并记录进程ID

``` powershell复制代码# 启动进程并返回进程对象 $process = Start-Process -FilePath "npm" -ArgumentList "run dev" -NoNewWindow -PassThru

# 输出进程ID和进程名称 $process.Id $process.ProcessName

# 保存进程ID到文件 $process.Id > "process_id.txt" ```

  1. 读取进程ID并终止进程

``` powershell复制代码# 读取进程ID $processId = Get-Content "process_id.txt"

# 终止进程 Stop-Process -Id $processId

# 可选:删除记录的文件 Remove-Item "process_id.txt" ```

综合脚本示例

启动进程并记录进程ID:

powershell复制代码# 启动进程并返回进程对象
$process = Start-Process -FilePath "npm" -ArgumentList "run dev" -NoNewWindow -PassThru

# 输出进程ID和进程名称
Write-Output "Process ID: $($process.Id)"
Write-Output "Process Name: $($process.ProcessName)"

# 保存进程ID到文件
$process.Id | Out-File -FilePath "process_id.txt"

读取进程ID并终止进程:

powershell复制代码# 读取进程ID
$processId = Get-Content "process_id.txt"

# 终止进程
Stop-Process -Id $processId

# 可选:删除记录的文件
Remove-Item "process_id.txt"

通过这种方式,你可以在启动进程时记录其进程ID,并在需要时使用该ID来管理进程。

不扎实的东西

集合,反射,stream流

这段代码的作用是从一个文章列表中提取所有的分类ID,并将这些ID存储到一个Set<Long>集合中。具体解释如下:

Set<Long> categoryIds = articleList.stream()
    .map(article -> article.getCategoryId())
    .collect(Collectors.toSet());
  • articleList.stream(): 将文章列表转换成一个流(Stream)。
  • .map(article -> article.getCategoryId()): 将流中的每篇文章映射(转换)为它的分类ID。
  • .collect(Collectors.toSet()): 将映射后的分类ID收集到一个Set集合中,以去除重复的ID。

这样做的结果是categoryIds集合中包含了所有不重复的文章分类ID。

LambdaQueryWrapper&QueryWrapper

这是我之前没接触到的东西耶,当初认为mybatis的xml配置已经封圣了

MyBatis Plus 中的 getById() 方法是 IService 接口提供的静态方法,用于根据实体对象的的主键值从数据库中检索单个实体对象。

一个前端工程的部署搞死了我

Stream流

categories = categories.stream()
        .filter(category -> SystemConstants.Category_STATUS_NORMAL.equals(category.getStatus()))
        .collect(Collectors.toList());

Q:stream流中Map方法的作用是什么

A:

  • 将原始 Stream 中的每个元素都映射转换为另一个元素,从而得到一个新的 Stream。
  • 转换前后的元素类型可以不同。
  • map()方法不会修改原始 Stream 中的元素,而是创建一个新的 Stream,其中包含了原始 Stream 中每个元素经过转换后的结果。

Q:Stream中的.collect()传参Collectors.toList()和Collectors.toSet()有什么区别

A:Stream中的.collect()方法用于将Stream中的元素收集到集合中。Collectors.toList()Collectors.toSet()是两个常用的Collector实现,用于将Stream收集到ListSet中,分别。

区别

  • 返回结果类型不同:
  • Collectors.toList()返回一个List类型的集合,该集合中的元素保持原有的顺序。
  • Collectors.toSet()返回一个Set类型的集合,该集合中的元素是唯一的,并且不保证顺序。
  • 内存占用不同:
  • Collectors.toList()通常比Collectors.toSet()占用更多的内存,因为List需要额外存储元素的顺序信息。
  • Collectors.toSet()通常比Collectors.toList()占用更少的内存,因为Set不需要存储元素的顺序信息。
  • 性能不同:
  • Collectors.toList()通常比Collectors.toSet()效率更高,因为List的插入操作通常比Set的插入操作更快速。
  • Collectors.toSet()在需要去重的情况下比Collectors.toList()更有效,因为Set可以自动去除重复的元素。

选择指南

  • 需要保留元素的顺序: 使用Collectors.toList().
  • 需要去除重复的元素: 使用Collectors.toSet().
  • 需要更高的性能: 使用Collectors.toList().
  • 需要更少的内存占用: 使用Collectors.toSet().

以下是一些使用示例:

// 将Stream中的元素收集到List中
List<String> names = Arrays.asList("Alice", "Bob", "Charlie").stream().collect(Collectors.toList());

// 将Stream中的元素收集到Set中
Set<Integer> numbers = Arrays.asList(1, 2, 3, 1, 2, 3).stream().collect(Collectors.toSet());

跨域问题

image-20240717161034085

package com.irreplace.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * @author Me
 * @version 1.0
 * @date 2024/7/17 16:18
 * @Description:解决跨域问题
 */

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        // 设置允许跨域的路径
        registry.addMapping("/**")
        // 设置允许跨域请求的域名
                .allowedOriginPatterns("*")
        // 是否允许cookie
                .allowCredentials(true)
        // 设置允许的请求方式
                .allowedMethods("GET", "POST", "DELETE", "PUT")
        // 设置允许的header属性
                .allowedHeaders("*")
        // 跨域允许时间
                .maxAge(3600);
    }
}

Bean拷贝封装成工具类

这啥啊我完全看不懂气死了啊

package com.irreplace.utils;

import org.springframework.beans.BeanUtils;

import java.util.List;
import java.util.stream.Collectors;

/**
 * @author Me
 * @version 1.0
 * @date 2024/7/17 22:18
 * @Description:封装成前端需要的字段,工具类
 */

public class BeanCopyUtils {
    private BeanCopyUtils() {
    }
    public static <V> V copyBean(Object source,Class<V> clazz) {
        //创建目标对象
        V result = null;
        try {
            result = clazz.newInstance();
        //实现属性copy
            BeanUtils.copyProperties(source, result);
        } catch (Exception e) {
            e.printStackTrace();
        }
        //返回结果
        return result;
    }
    public static <O,V> List<V> copyBeanList(List<O> list, Class<V> clazz){
        return list.stream()
                .map(o -> copyBean(o, clazz))
                .collect(Collectors.toList());
    }
}

EasyCode模板配置

包的宏定义,mapper出问题?

yml配置mybatis逻辑删除

这段配置是用于配置 MyBatis-Plus 的全局配置,特别是数据库配置部分。具体解释如下:

mybatis-plus:
  global-config:
    db-config:
      logic-delete-field: delFlag
      logic-delete-value: 1
      logic-not-delete-value: 0
      id-type: auto
  • mybatis-plus: MyBatis-Plus 的配置根节点。
  • global-config: 全局配置。
  • db-config: 数据库配置。

具体字段解释:

  1. logic-delete-field: delFlag
  2. 指定逻辑删除的字段名。在数据库表中,这个字段用于标记记录是否被逻辑删除。比如,当 delFlag 为 1 时表示记录被删除,当为 0 时表示记录未被删除。

  3. logic-delete-value: 1

  4. 表示逻辑删除时,该字段的值。这里表示如果 delFlag 字段的值为 1,则该记录被逻辑删除。

  5. logic-not-delete-value: 0

  6. 表示逻辑未删除时,该字段的值。这里表示如果 delFlag 字段的值为 0,则该记录未被逻辑删除。

  7. id-type: auto

  8. 指定主键生成策略为自动增长(自增主键)。这意味着在插入新记录时,主键值会自动生成。

总结:这段配置主要用于设置 MyBatis-Plus 中关于逻辑删除和主键生成的全局配置,方便在操作数据库时对逻辑删除字段进行统一管理,并自动处理主键的生成。

解析CategoryServiceImpl.getCategoryList()

总体代码

/**
 * @author Me
 * @version 1.0
 * @date 2024/7/18 12:19
 * @Description:分类
 */
@Service
@Slf4j
public class CategoryServiceImpl  extends ServiceImpl<CategoryMapper,Category> implements CategoryService {
   @Autowired
   private ArticleService articleService;
    @Override
    public ResponseResult getCategoryList() {
        //先查询文章,返回正常显示的文章
        LambdaQueryWrapper<Article> lambdaWrapper = new LambdaQueryWrapper<>();
        lambdaWrapper.eq(Article::getStatus, SystemConstants.ARTICLE_STATUS_NORMAL);
        List<Article> articleList = articleService.list(lambdaWrapper);

        //筛选出正常显示的文章id号,并且去除重复
        Set<Long> collectIds = articleList.stream()
                .map(article -> article.getCategoryId())
                .collect(Collectors.toSet());
        //查询分类表,stream过滤出状态为正常的文章分类
        List<Category> categories = listByIds(collectIds);
        categories = categories.stream()
                .filter(category -> SystemConstants.Category_STATUS_NORMAL.equals(category.getStatus()))
                .collect(Collectors.toList());
        //封装vo
        List<CategoryVo> categoryVos = BeanCopyUtils.copyBeanList(categories, CategoryVo.class);
       //log.info(categoryVos.toString());
        return ResponseResult.successResult(categoryVos);
    }
}


LambdaQueryWrapper使用

这段代码是使用 MyBatis-Plus 构建查询条件并查询文章列表。具体解释如下:

  //先查文章表,状态为已发布文章
LambdaQueryWrapper<Article> articleWrapper = new LambdaQueryWrapper<>();
articleWrapper.eq(Article::getStatus, SystemConstants.ARTICLE_STATUS_NORMAL);
List<Article> articleList = articleService.list(articleWrapper);
  1. LambdaQueryWrapper<Article> articleWrapper = new LambdaQueryWrapper<>();
  2. 创建一个 LambdaQueryWrapper 对象,用于构建查询条件。LambdaQueryWrapper 是 MyBatis-Plus 提供的一个工具类,用于以 lambda 表达式的方式构建查询条件。

  3. articleWrapper.eq(Article::getStatus, SystemConstants.ARTICLE_STATUS_NORMAL);

  4. 使用 eq 方法构建查询条件,表示查询 status 字段等于 SystemConstants.ARTICLE_STATUS_NORMAL 的记录。
  5. Article::getStatus 是 lambda 表达式,表示 Article 类中的 getStatus 方法,即查询 status 字段。
  6. SystemConstants.ARTICLE_STATUS_NORMAL 是一个常量,表示正常状态的文章。

  7. List<Article> articleList = articleService.list(articleWrapper);

  8. 使用 articleServicelist 方法根据构建的查询条件 articleWrapper 查询符合条件的文章列表,并将结果存储在 articleList 中。

总结:这段代码的作用是查询状态为正常(ARTICLE_STATUS_NORMAL)的文章列表,并将查询结果存储在 articleList 中。

两处stream流使用

//筛选出正常显示的文章id号,并且去除重复
Set<Long> collectIds = articleList.stream()
        .map(article -> article.getCategoryId())
        .collect(Collectors.toSet());
//查询分类表,stream过滤出状态为正常的文章分类
List<Category> categories = listByIds(collectIds);
categories = categories.stream()
        .filter(category -> SystemConstants.Category_STATUS_NORMAL.equals(category.getStatus()))
        .collect(Collectors.toList());

Stream 是 Java 8 引入的一种新的抽象序列,用于处理集合数据的方法。它提供了一种高效且易于理解的方式来对集合进行操作和处理。以下是一些常用的 Stream 方法:

  1. forEach
  2. 对流中的每个元素执行指定的操作。

java stream.forEach(element -> System.out.println(element));

  1. map
  2. 将流中的每个元素通过指定的函数映射成另一个元素。

java Stream<String> upperCaseStream = stream.map(String::toUpperCase);

  1. filter
  2. 根据指定的条件过滤流中的元素。

java Stream<String> filteredStream = stream.filter(s -> s.length() > 5);

  1. collect
  2. 将流中的元素收集到一个集合或者其他数据结构中。

java List<String> collectedList = stream.collect(Collectors.toList());

  1. reduce
  2. 将流中的元素通过指定的二元操作符进行归约操作,得到一个最终的结果。

java Optional<String> reducedString = stream.reduce((s1, s2) -> s1 + ", " + s2);

  1. findFirstfindAny
  2. findFirst 返回流中的第一个元素;findAny 返回流中的任意一个元素(在并行流中有用)。

java Optional<String> firstElement = stream.findFirst(); Optional<String> anyElement = stream.findAny();

  1. count
  2. 返回流中元素的数量。

java long count = stream.count();

  1. skiplimit
  2. skip 跳过流中的前几个元素;limit 限制流中元素的数量。

java Stream<String> skippedStream = stream.skip(2); Stream<String> limitedStream = stream.limit(5);

  1. sorted
  2. 对流中的元素进行排序。

java Stream<String> sortedStream = stream.sorted();

  1. distinct

    • 去除流中重复的元素。

    java Stream<String> distinctStream = stream.distinct();

  2. allMatch, anyMatch, noneMatch

    • allMatch 检查流中的所有元素是否都满足指定条件;
    • anyMatch 检查流中是否至少有一个元素满足指定条件;
    • noneMatch 检查流中是否所有元素都不满足指定条件。

    java boolean allMatched = stream.allMatch(s -> s.length() > 3); boolean anyMatched = stream.anyMatch(s -> s.startsWith("A")); boolean noneMatched = stream.noneMatch(s -> s.isEmpty());

这些是 Java 中 Stream 类中常用的一些方法。每个方法都提供了不同的功能,可以根据具体的需求来选择合适的方法来操作和处理流中的数据。

分页查询

image-20240718142831420

image-20240718143010333

MyBatis-Plus 拦截器

配置和注册一个 MyBatis-Plus 拦截器,用于在 MyBatis 执行 SQL 时自动拦截并实现分页功能

package com.irreplace.config;

import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author Me
 * @version 1.0
 * @date 2024/7/18 14:36
 * @Description:配置和注册一个 MyBatis-Plus 拦截器,用于在 MyBatis 执行 SQL 时自动拦截并实现分页功能。
 */

@Configuration
public class MybatisPlusConfig {
    /**
     * 3.4.0之后版本
     *
     * @return
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor mybatisPlusInterceptor = new
                MybatisPlusInterceptor();
        mybatisPlusInterceptor.addInnerInterceptor(new
                PaginationInnerInterceptor());
        return mybatisPlusInterceptor;
    }
}

文章详情分类

image-20240718191312331

从路径中获取请求参数记得添加注解

登陆

image-20240718213431303

image-20240718215755748

自定义登陆接口

注入AuthenticationManager

前台Blog的SecurityConfig.configure

   @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                //关闭csrf
                .csrf().disable()
                //不通过Session获取SecurityContext
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                // 对于登录接口 允许匿名访问
                .antMatchers("/login").anonymous()
                // 除上面外的所有请求全部不需要认证即可访问
                .anyRequest().permitAll();
        http.logout().disable();
                //允许跨域
        http.cors();
    }

这段代码是 Spring Security 配置的一部分,用于配置 Spring Security 的安全策略。它主要做了以下几件事情:

1. 关闭 CSRF 保护

CSRF(Cross-Site Request Forgery)是一种跨站请求伪造攻击,攻击者可以利用用户的已登录身份来执行一些未经授权的操作。Spring Security 默认启用 CSRF 保护,可以帮助防止此类攻击。但是,在某些情况下,您可能需要关闭 CSRF 保护。例如,如果您使用的是 REST API,则可能需要关闭 CSRF 保护,以便允许跨域请求。

2. 禁用 Session 创建

Spring Security 默认使用 Session 来存储 SecurityContext,SecurityContext 包含有关当前用户的安全信息。但是,如果您使用的是无状态应用程序,则不需要 Session。在这种情况下,您可以禁用 Session 创建。

3. 配置授权规则

授权规则定义了哪些用户可以访问哪些资源。在这段代码中,有两个授权规则:

  • 对于 /login 接口,允许匿名访问。这意味着任何用户都可以访问此接口,而无需登录。
  • 对于除 /login 接口之外的所有请求,都不需要认证即可访问。这意味着任何用户都可以访问这些请求,无论他们是否登录。

4. 禁用注销功能

默认情况下,Spring Security 提供注销功能,允许用户退出登录。但是,如果您不需要注销功能,可以禁用它。

5. 启用跨域支持

跨域是指来自不同域的 Web 应用程序之间的通信。默认情况下,浏览器会限制跨域请求。Spring Security 提供跨域支持,可以帮助您允许跨域请求。

总而言之,这段代码配置了以下安全策略:

  • 关闭 CSRF 保护
  • 禁用 Session 创建
  • 对于 /login 接口,允许匿名访问
  • 对于除 /login 接口之外的所有请求,都不需要认证即可访问
  • 禁用注销功能
  • 启用跨域支持

登录校验过滤器实现

记得要在httpconfig加入该过滤器http.addFilterbefore()?!!!

分页查询

 Page<Comment> commentPage = new Page<>(pageNum,pageSize);
        page(commentPage,commentLambdaQueryWrapper);

这就完成了?但我真的搞不懂

image-20240721230900311

image-20240721230935834