SPRINGBOOT+VUE项目实战

第一章=>
1、ElementUI         
2、布局与主体             
3、增删改查               
4、路由                       
5、播放组件

第二章=>
6、分页                 
7、代码生成                 
8、导入导出               
9、用户登录                 
10、注册与异常处理   

第三章=>
11、JWT               
12、文件上传               
13、权限管理           
14、Redis

第四章=>
15、Echats             
16、百度地图               
17、Markdwon       
18、WangEditor

第五章=>
19、前台页面         
20、视频播放               
21、多级评论           
22、支付宝

第六章=>
23、购物车             
24、借书管理               
25、百度地图           
26、聊天室

第七章=>
27、考试系统         
28、邮箱登录               
29、活动预约           
30、电商系统

****************************************************************************************************************************************************************************

1、ElementUI 
【1】软件安装,开发环境配置。
JDK1.8、mysql5.7、node、navicat、idea2021
*************************************************************************
【2】安装vue-cli
npm i -g @vue/cli
*************************************************************************
vue -V
*************************************************************************
vue create white
*************************************************************************
用white记录模板创建即可。
*************************************************************************
npm config set registry https://registry.npmmirror.com/
npm config get registry
*************************************************************************
用WebStorm打开
*************************************************************************
【3】项目结构
http://localhost:8080/
App.vue通过路由展示了Home与About页面
*************************************************************************
【4】安装element
npm i element-ui -S
*************************************************************************使用
<el-button type="primary">主要点击</el-button>

****************************************************************************************************************************************************************************

2、布局与主体
【1】安装less
npm i less-loader less -S
************************************************************************样式调整而已
链接:https://pan.baidu.com/s/15YwRICJKS7aCiBqWcYeAKw 
提取码:iwr9 
完成主页面样式设计

****************************************************************************************************************************************************************************

5、Springboot框架搭建
【1】配置Lombok、Spring web、MyBatis Framework、MySQL Driver
【2】配置阿里云下载
<!--配置阿里云仓库下载-->
    <repositories>
        <repository>
            <id>nexus-aliyun</id>
            <name>nexus-aliyun</name>
            <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>
    <pluginRepositories>
        <pluginRepository>
            <id>public</id>
            <name>nexus-aliyun</name>
            <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </pluginRepository>
    </pluginRepositories>
【3】启动报错
Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.
*****************************************************************************
#数据库驱动
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#东八区+数据库连接配置
spring.datasource.url=jdbc:mysql://wdfgdzx.top:3306/black?serverTimezone=GMT2b%8
spring.datasource.username=root
spring.datasource.password=s19911009!
*****************************************************************************
black utf8mb4  utf8mb4_unicode_ci两个配置
*****************************************************************************
#0代表黑,1代表白。前端是8001
server.port=8000

****************************************************************************************************************************************************************************

6、Mybatis实现数据增删改查
【1】动态sql
【2】File -> Settings -> Editor -> File encodings --> 设置properties的编码
【3】mybatis配置
#指定mybatis配置
mybatis.mapper-locations=classpath:mapper/*.xml
*****************************************************************************properties
#0代表黑,1代表白,前端是8001
server.port=8000
#数据库驱动
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#数据库配置
spring.datasource.url=jdbc:mysql://wdfgdzx.top:3306/black?serverTimezone=GMT%2b8&allowMultiQueries=true&useAffectedRows=true
spring.datasource.username=root
spring.datasource.password=s19911009!
#指定mybatis配置
mybatis.mapper-locations=classpath:mapper/*.xml
【4】详细代码
链接:https://pan.baidu.com/s/1Jz80if48Z5pannN_TOAUTQ 
提取码:fq9j

****************************************************************************************************************************************************************************

8、分页查询实现
【1】控制器的路基
 @PostMapping("list_page")
    public HashMap list_page(@RequestBody User user) {
        Integer totalNum = userMapper.total();
        MyUtils.selectByPageManage(totalNum, user);
        HashMap hashMap = new HashMap();
        hashMap.put("total", totalNum);
        hashMap.put("data", userMapper.list_page(user));
        return hashMap;
    }
【2】跨域问题的处理
package com.black.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
@Configuration
public class Cross {
    // 当前跨域请求最大有效时长,默认1天
    private static final long MAX_DAY = 24 * 60 * 60;

    @Bean
    public CorsFilter buildCorsFilter() {
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.addAllowedOrigin("*"); // 访问源地址
        corsConfiguration.addAllowedHeader("*");// 访问源请求头
        corsConfiguration.addAllowedMethod("*"); // 访问源请求方法
        corsConfiguration.setMaxAge(MAX_DAY);// 设置最大有效时长

        UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
        urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration); // 对接口配置跨域设置
        return new CorsFilter(urlBasedCorsConfigurationSource);
    }
}
【3】配置axios
npm i axios -S
************************************************************************
// main.js
import axios from "axios"; // 导入axios

Vue.prototype.$http = axios //在Vue的原型上添加一个$http属性,该属性保存了axios
axios.defaults.baseURL = 'http://localhost:8000'

****************************************************************************************************************************************************************************

9、MybatisPlus与SwaggerUI
【1】pom配置
<!--mybatis-plus-->
<dependency>
	<groupId>com.baomidou</groupId>
	<artifactId>mybatis-plus-boot-starter</artifactId>
	<version>3.5.1</version>
</dependency>
【2】MybaitsPlus配置
package com.black.config;

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

@Configuration
@MapperScan("com.black.mapper") // !!!!!!!!!!!!!!
public class MybatisPlus {
    @Bean
    public MybatisPlusInterceptor buildMybatisPlusInterceptor() {
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return mybatisPlusInterceptor;
    }
}
【3】简化了UserMapper.interface与UserMapper.xml(只需要写复杂的即可,简单的MP代理了)
package com.black.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.black.pojo.User;
import org.apache.ibatis.annotations.Param;

import java.util.List;

public interface UserMapper extends BaseMapper<User> { // !!!!!!!!!!
    Integer total(@Param("user") User user); //总条数

    List<User> list_page(@Param("user") User user); //分页数据
}
******************************************************************************
MybatisPlus让数据库操作变得更简单...
******************************************************************************
@TableId(value = "id", type = IdType.AUTO) // 这个会影响是否按顺序增加!!!!!!!
@TableId(type = IdType.AUTO) // 当代码里的字段与数据库不同时,可以通过value="xxx"对应数据库字段
type = IdType.AUTO// 这个影响自增,所以很重要
【4】SwaggerUI简化postman的测试
<!-- swagger接口文档 -->
<dependency>
	<groupId>io.springfox</groupId>
	<artifactId>springfox-boot-starter</artifactId>
	<version>3.0.0</version>
</dependency>
******************************************************************************
# properties
# 引入swagger3.0时加入的配置 http://localhost:8000/swagger-ui/index.html
spring.mvc.pathmatch.matching-strategy=ant_path_matcher
******************************************************************************
package com.black.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

@Configuration
@EnableSwagger2
public class Swagger {
    @Bean
    public Docket restAPI() {
        return new Docket(DocumentationType.SWAGGER_2)
                .groupName("HIT的接口")
                .apiInfo(APIInfo())
                .useDefaultResponseMessages(true)
                .forCodeGeneration(false)
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.black.controller")) // !!!!!!!!!!!!
                .paths(PathSelectors.any())
                .build();
    }

    private ApiInfo APIInfo() {
        return new ApiInfoBuilder()
                .title("RESTful")
                .description("http://wdfgdzx.top:8001/")
                .termsOfServiceUrl("http://wdfgdzx.top:8001/")
                .contact(new Contact("HIT", "http://wdfgdzx.top:8001/", "wdfgdzx@163.com"))
                .version("V1.0")
                .build();
    }
}
******************************************************************************看自己收藏的CSDN一样的
http://localhost:8000/swagger-ui/index.html

****************************************************************************************************************************************************************************

10、VUE实现增删改查
【1】配置xml SQL查询
Editor->Inspections->SQL->No data sources configured 和 SQL dialect detection
看收藏的CSDN帖子
***************************************************************************
【2】安装配置axios
npm i axios -S
***************************************************************************
import axios from "axios";// 导入axios

/*1、配置后台请求接口*/
const $http = axios.create({  //在Vue的原型上添加一个$http属性,该属性保存了axios
  baseURL: "http://localhost:8000",
  timeout: 5000
})

/*2、请求拦截器,对发送请求钱做一些处理,比如统一加token/对请求参数统一加密等...*/
$http.interceptors.request.use(config => {
  config.headers['Content-Type'] = "application/json;charset=utf-8"  // 定义网络文件的类型和网页的编码,决定文件接收方将以什么形式、什么编码读取这个文件
  return config
})

/*3、响应拦截器,在接口响应后统一处理结果*/
$http.interceptors.response.use(res => {
  return res;
})

export default $http
***************************************************************************
import $http from './util/axios.js' // 引入定义的axios工具
Vue.prototype.$http = $http //引入util/axios.js暴露的$http来使用
***************************************************************************
【3】行数据脏改
/*修改窗口*/
  updateWindow(row) {
	this.userFormFlag = true
	this.userForm = JSON.parse(JSON.stringify(row)) // !!!!!!!!!!!!
  },

****************************************************************************************************************************************************************************

11、SpringBoot代码生成
【1】引入依赖
<!--代码生产-->
<dependency>
	<groupId>org.apache.velocity</groupId>
	<artifactId>velocity</artifactId>
	<version>1.7</version>
</dependency>
<dependency>
	<groupId>com.baomidou</groupId>
	<artifactId>mybatis-plus-generator</artifactId>
	<version>3.5.1</version>
</dependency>
**********************************************************************我只想说打扰了,哥哥
这么简单的代码,不至于生成,还覆盖了我原有的代码,过!!!!
不如直接复制+替换关键字不香吗???

****************************************************************************************************************************************************************************

12、VUE使用路由展示左侧栏
【1】props: ['fff_top_foldData', 'fff_top_foldClick'],/*说明props还可以接受函数,我日尼玛哦*/
【2】路由守卫
*************************************************************************使用vuex解决导航路径问题
npm i vuex -S --force
*************************************************************************myVuex.js
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const myVuex = new Vuex.Store({
  state: {
    currentPathName: '' // 当前路径名称
  },
  mutations: {
    setStateCurrentPathName(state) {
      state.currentPathName = localStorage.getItem("currentPathName")
    }
  }
})

export default myVuex
*************************************************************************main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router/router.js'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
import './assets/main.css' // 全局样式表
import $http from './util/axios.js' // 引入定义的axios工具
import myVuex from "@/store/myVuex.js"; // 引入vuex管理全局存储


Vue.prototype.$http = $http //引入util/axios.js暴露的$http来使用

Vue.config.productionTip = false
Vue.use(ElementUI, {size: 'mini'}) // 挂载elementUI

new Vue({
  router,
  myVuex,
  render: h => h(App)
}).$mount('#app')
*************************************************************************router.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import Manage from "@/views/Manage.vue";
import User from "@/views/User.vue";
import Home from "@/views/Home.vue";
import myVuex from "@/store/myVuex";

Vue.use(VueRouter)

const routes = [
  {
    path: '/',
    name: 'manage',
    component: Manage,
    redirect: "/home",
    children: [
      {path: 'user', name: '用户管理', component: User},
      {path: 'home', name: '首页', component: Home}
    ]
  },
  {
    path: '/about',
    name: 'about',
    // route level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
  }
]

const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes
})

/*路由守卫*/
router.beforeEach((to, from, next) => {
  // console.log(to)
  localStorage.setItem("setStateCurrentPathName", to.name) // 设置即将访问的路由名称,为了在Header中使用
  myVuex.commit("setPath") // 触发myVuex里的数据更新
  next()
})

export default router
*************************************************************************Top.vue

****************************************************************************************************************************************************************************

13、SpringBoot实现导入导出
【1】说明学只能是学。但是和军队管理的用还差的很多,还是要多用,做项目来成长。
@GetMapping("/list_export")
    public String list_export(HttpServletResponse httpServletResponse) throws Exception {
        //1、从数据查询
        List<User> userList = userMapper.selectList(null);
        ExcelWriter excelWriter = ExcelUtil.getWriter(true);
        //2、自定义标题
        excelWriter.addHeaderAlias("id", "序号");
        excelWriter.addHeaderAlias("name", "姓名");
        excelWriter.addHeaderAlias("nick", "昵称");
        excelWriter.addHeaderAlias("email", "邮箱");
        excelWriter.addHeaderAlias("phone", "手机");
        excelWriter.addHeaderAlias("address", "地址");
        excelWriter.setOnlyAlias(true); // 仅写出指定字段
        excelWriter.write(userList, true);
        //3、设置浏览器响应的格式
        httpServletResponse.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
        String fileName = URLEncoder.encode("用户信息", "UTF-8");
        httpServletResponse.setHeader("Content-Disposition", "attachment;filename=" + fileName + ".xlsx");
        //4、拿到输出流并刷新
        ServletOutputStream servletOutputStream = httpServletResponse.getOutputStream();
        excelWriter.flush(servletOutputStream, true);
        servletOutputStream.close();
        excelWriter.close();
        return "导出成功";
    }
【2】学用需要结合,这样才真正掌握某一项技能。军队管理!!!!!
【3】批量插入的mybatis的写法,重要!!!!!!!!!!!!!!!!!!!!!
<insert id="list_insert" parameterType="java.util.List">
【4】导入的第二种方式
********************************************************************忽略表头中文
List<CsvRow> rowList = data.getRows();  // 忽略表头文件,直接读取内容
rowList.remove(0);
row.get(5)
********************************************************************
写死了,唯一的优点是不用管表头名字
【5】前端调用
********************************************************************
/*导出*/
list_export() {
    window.open("http://localhost:8000/user/list_export")
},
********************************************************************
<el-upload style="display: inline-block"
		   action="http://localhost:8000/user/list_import"
		   name="multipartFile" // !!!!!!!!!!!!!多看文档
		   :show-file-list="false"
		   accept="xlsx"
		   :on-success="onImportSuccess">
	<el-button type="primary" class="ml-5">导入<i class="el-icon-upload2"></i></el-button>
</el-upload>

****************************************************************************************************************************************************************************

14、Springboot+VUE实现登录
【1】登录页的路由+布局
【2】后台写登录接口
@RestController
@RequestMapping("/big")
public class BigController {
    @Resource
    UserMapper userMapper;

    @PostMapping("login")
    public String login(@RequestBody User user) {
        // 拿到用户输入的账号密码
        User existUser = userMapper.selectUserByNamePassword(user);
        if (existUser == null) {
            return "账号或密码错误"; // 好简单!!!!!!!!!!!
        }
        return "登录成功";
    }
}
*************************************************************************
400报错,如果后端要求的传参没有传递也会报错400。比如big/login要求传user,如果没传则报错400
*************************************************************************
<el-form :rules="ruleList" :model="user"><!--用来校验表单-->
*************************************************************************
<el-form-item prop="name"><!--必须el-form-item prop="xxx"才能生效-->
再次说明学以致用最重要!!!!!!!!!!!!!!!!!!!!!!!!
*************************************************************************校验规则先通过才发送请求
ref="userForm"
*************************************************************************用到了引用ref
loginClick() {
this.$refs["userForm"].validate(valid => {
  if (valid) { // 表单校验合法
	this.$http.post("/big/login", this.user).then(res => {
	  if (res.data !== "登录成功") {
		this.$message.error("用户名或密码错误")
	  } else {
		this.$router.push("/")
	  }
	})
  } else {
	return false
  }
})
}
【3】假如数据库存在脏数据,就是有重复的用户名+密码,怎么处理?
用List判断数据是否存在
*************************************************************************
用try catch,捕获到异常然后再处理

****************************************************************************************************************************************************************************

15、Springboot+Vue实现注册与异常处理
【1】登录信息存储
package com.black.util;

// 常量类
public class Constants {
    public static final String CODE_200 = "200"; // 成功
    public static final String CODE_400 = "400"; // 参数错误
    public static final String CODE_401 = "401"; // 权限不足
    public static final String CODE_500 = "500"; // 系统错误
    public static final String CODE_600 = "600"; // 其他错误
}
*******************************************************************************
package com.black.util;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

// 返回结果包装类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Res {
    private String code;
    private String message;
    private Object object;

    // 成功的两个方法
    public static Res success() {
        return new Res(Constants.CODE_200, "", null);
    }

    public static Res success(Object object) {
        return new Res(Constants.CODE_200, "", object);
    }

    // 不成功的方法
    public static Res error() {
        return new Res(Constants.CODE_500, "系统错误", null);
    }

    public static Res error(String code, String message) {
        return new Res(code, message, null);
    }
}
【2】全局异常处理,最终还是用到了Res,这个先保留了解下...
【3】前端的处理
localStorage.setItem("user", JSON.stringify(res.data.object)) // 存储用户信息到浏览器
*******************************************************************************
user: localStorage.getItem("user") ? JSON.parse(localStorage.getItem("user")) : {}
*******************************************************************************
logoutClick() {
	this.$router.push("/login")
	localStorage.removeItem("user") // 移出浏览器用户信息
	this.$message.success("退出成功")
  }
【4】注册页面与接口的改造,统一返回封装的Res

****************************************************************************************************************************************************************************

16、Springboot使用JWT
【1】JWT全称是json web token
它将用户信息加密到token里,服务器不保存任何用户信息。
服务器通过使用保存的密匙验证token的正确性,只要正确即可通过验证。
***************************************************************************
优点:
简洁:通过URL POST参数或者在http header发送,数据量小,传输速度也快。
***************************************************************************
自包含,避免了多次的数据库查询。
***************************************************************************
因为token是以json加密的形式保存在客户端的,所以JWT是跨语言的,原则上任何web形式都支持。
***************************************************************************
不需要在服务端保存会话信息,特别适用于分布式的---微服务---。
***************************************************************************
缺点:
无法作废已颁布的令牌;
不易于应对数据过期;
【2】组成,一个token是三个组成部分。
header头部;载荷payload;签证singature。用.分割
***************************************************************************
<!--JWT-->
<dependency>
	<groupId>com.auth0</groupId>
	<artifactId>java-jwt</artifactId>
	<version>3.10.3</version>
</dependency>
***************************************************************************
package com.black.util;

import cn.hutool.core.date.DateUtil;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.black.pojo.User;

import java.util.Date;

public class Token {
    public static String productToken(User user) {
        return JWT.create().withAudience(user.getId() + "") // user.id作为载荷
                .withExpiresAt(DateUtil.offsetHour(new Date(), 2)) //2小时后token过期
                .sign(Algorithm.HMAC256(user.getPassword())); // 以 password 作为 token 的密钥
    }
}
【3】token的重要作用
/*2、请求拦截器,对发送请求钱做一些处理,比如统一加token/对请求参数统一加密等...*/
$http.interceptors.request.use(config => {
  config.headers['Content-Type'] = "application/json;charset=utf-8"  // 定义网络文件的类型和网页的编码,决定文件接收方将以什么形式、什么编码读取这个文件

  // 从本地存储拿,设置请求头!!!!!!!!!!!!
  let user = localStorage.getItem("user") ? JSON.parse(localStorage.getItem("user")) : {}
  if (user) {
    config.headers['token'] = user.token; // 设置
  }

  return config
})
***************************************************************************后台拦截器
权限验证不同过,前端提示
/*3、响应拦截器,在接口响应后统一处理结果*/
$http.interceptors.response.use(res => {
  //alert(JSON.stringify(res))
  if (res.data.code === "401") {
    elementUI.Message({
      message: res.data.message,
      type: "error"
    })
  }
  return res;
})
【4】后台获取当前用户
private static UserMapper staticUserMapper; // 因为@Resource不能定义为静态,这里是为了转存
@Resource
private UserMapper userMapper; // 因为有个静态方法引用了

@PostConstruct
public void setUserMapper() {
	staticUserMapper = userMapper;
}
***************************************************************************
// 获取当前用户
public static User getNowUser() {
	try {
		HttpServletRequest httpServletRequest = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest(); //拿到httpServletRequest
		String token = httpServletRequest.getHeader("token");//拿到token
		if (StrUtil.isNotBlank(token)) {
			String id = JWT.decode(token).getAudience().get(0); //拿到user.id
			return staticUserMapper.selectById(id);//拿到user
		}
	} catch (Exception e) {
		e.printStackTrace();
		return null;
	}
	return null;
}
***************************************************************************
System.err.println(JWTInterceptor.getNowUser()); // 全局可以获取
任何方法,任何Controller去使用

****************************************************************************************************************************************************************************

17、文件上传
【1】swagger-ui的配置,在放行代码指定这些,JWT拦截里放行。牛批!!!!!
"/swagger**/**",
"/webjars/**",
"/v2/**",
"/doc.html"
**************************************************************************文件操作都放行
"/document/**"
**************************************************************************上传不要@RequestBody
public String upload(Document document) throws Exception 
**************************************************************************
#上传文件大小指定(单个)
spring.servlet.multipart.max-file-size=100MB
**************************************************************************
File uploadFile = new File(uploadDir + "/" + IdUtil.fastSimpleUUID() + "." + type);
**************************************************************************
【2】同样的图片去重43分钟,利用md5值是否相同来比较,这个是自己想的解决办法,必须赞!!!!!!!!!!!!
@PostMapping("/upload")
public String upload(Document document) throws Exception {
	String originName = document.getMultipartFile().getOriginalFilename();
	String type = FileUtil.extName(originName);
	long size = document.getMultipartFile().getSize();
	// 1、存到磁盘、存储数据库
	File uploadDir = new File(MyUtils.getFinalPath());
	System.err.println(uploadDir);
	if (!uploadDir.exists()) {
		System.out.println(uploadDir.mkdir()); //不存在创建新目录
	}
	// 2、最终文件路径
	String fileUUID = IdUtil.fastSimpleUUID() + "." + type;
	String url = "http://localhost:8000/document/" + fileUUID;
	File uploadFile = new File(uploadDir + "/" + fileUUID);
	//3、获取md5
	if (!uploadFile.exists()) {
		document.getMultipartFile().transferTo(uploadFile); // 这时候才存磁盘!!!!!
	}
	String md5 = SecureUtil.md5(uploadFile);
	// 4、判断数据库里md5是否存在
	document.setMd5(md5);
	List<Document> documentList = documentMapper.md5_list(document); // 数据库查询到相同md5的对象
	System.out.println(documentList);
	if (documentList.size() != 0) {
		url = documentList.get(0).getUrl(); // 存在的话,url直接从已存在数据库信息拿到
		System.out.println(uploadFile.delete());// 删除本地存储的文件
	}
	// 5、存数据库
	Document insertDocument = new Document();
	insertDocument.setUrl(url);
	insertDocument.setName(originName);
	insertDocument.setType(type);
	insertDocument.setSize(size / 1024);
	insertDocument.setMd5(md5);
	documentMapper.insert(insertDocument);
	return url;
}
**************************************************************************
这里青戈处理了很久....我尼玛!!!!!!!!
而且青戈处理的不对....浪费良久后青戈处理对了...
还是谦虚点,还是有比我牛批的地方...
【3】上传图像,这个name很重要的!!!!!!!!!!!
<!--头像-->
<el-upload
		name="multipartFile"
		class="avatar-uploader"
		action="http://localhost:8000/document/upload"
		:show-file-list="false"
		:on-success="onSuccess">
	<img v-if="userForm.avatar" :src="userForm.avatar" class="avatar">
	<i v-else class="el-icon-plus avatar-uploader-icon"></i>
</el-upload>
【4】处理保存后,左上角头像的更新问题(初步处理,没有一键触发!!!!)
子传父、父传子
**************************************************************************Person.vue
// 触发父级更新user方法
this.$emit("person_fff_user", this.user)
**************************************************************************Composite.vue
data() {
  return {
	foldData: 'foldClass el-icon-s-fold',// 折叠类
	collapseFlag: false, // 是否折叠
	sideWidth: 200, // 左侧边栏宽度
	logoTextShowFlag: true,
	user: localStorage.getItem("user") ? JSON.parse(localStorage.getItem("user")) : {}
	// 这个不取,第一次展示就是空了!!!!!!!!!
  }
},
methods: {
  /*子传父*/
  async person_fff_user(val) { //这里val就是person传过来的user
	const {data: res} = await this.$http.post("user/select", val) // 从后台获取数据
	// console.log(res.object)
	this.user = res.object
  },
  
<Top :fff_top_fold-data="foldData"
 :fff_top_foldClick="foldClick"
 :fff_top_user="user"> <!--向person传递user-->
</Top>

**************************************************************************Top.vue
<Top :fff_top_fold-data="foldData"
	 :fff_top_foldClick="foldClick"
	 :fff_top_user="user"> <!--向person传递user-->
</Top>

props: ['fff_top_foldData', 'fff_top_foldClick', 'fff_top_pathName', 'fff_top_user'],/*说明props还可以接受函数,我日尼玛哦*/

<div style="display: inline-block;">
	<img :src="fff_top_user.avatar"
		 style="width: 30px;border-radius: 10%;position: relative;top:5px;right: 5px;">
	<span>{{fff_top_user.name}}</span><i class="el-icon-arrow-down" style="margin-left: 5px"></i>
</div>
**************************************************************************
Person----Composite---Top
子----------父------------子

****************************************************************************************************************************************************************************

18、整合Echarts
【1】配置
npm i echarts -S
************************************************************************
import * as echarts from 'echarts'
折线图 柱状图 饼图
************************************************************************
this.$http.post("/echarts/select").then(res => {
  // this.$http.post("/echarts/vip").then(res => {
  // console.log(res)
  // alert(JSON.stringify(res.data.object.x))
  // alert(JSON.stringify(res.data.object.y))
  option.xAxis.data = res.data.object.x;
  // alert(JSON.stringify(res))
  // option.xAxis.data = ["第一季度", "第二季度", "第三季度", "第四季度",]; // 演示从数据库查询的
  option.series[0].data = res.data.object.y;
  option && myChart.setOption(option);
})
请求后台更改数据,主要是属性的更改,与后台json结合,加上option的位置要在更改后放置!!!
【2】概览部分
<!--头部-->
<el-row :gutter="10">
	<el-col :span="6">
		<el-card style="color: #409eff">
			<div><i class="el-icon-user-solid"></i> 用户总数</div>
			<div style="text-align: center;padding: 10px 0;font-weight: bold;font-size: 20px">
				100
			</div>
		</el-card>
	</el-col>
	<el-col :span="6">
		<el-card style="color: #67C23A">
			<div><i class="el-icon-money"></i> 销售总量</div>
			<div style="text-align: center;padding: 10px 0;font-weight: bold;font-size: 20px">
				100
			</div>
		</el-card>
	</el-col>
	<el-col :span="6">
		<el-card style="color: #E6A23C">
			<div><i class="el-icon-bank-card"></i> 收益总额</div>
			<div style="text-align: center;padding: 10px 0;font-weight: bold;font-size: 20px">
				¥100000,00
			</div>
		</el-card>
	</el-col>
	<el-col :span="6">
		<el-card style="color: #F56C6C">
			<div><i class="el-icon-s-shop"></i> 门店总数</div>
			<div style="text-align: center;padding: 10px 0;font-weight: bold;font-size: 20px">
				20
			</div>
		</el-card>
	</el-col>
</el-row>
************************************************************************
option = {
  title: {
	left: 'center' // 标题属性
  },
  tooltip: { // 鼠标移入显示数据标签
	trigger: 'item'
  },
  legend: { // 显示说明左侧
	orient: 'vertical',
	left: 'left'
  },
  xAxis: {
	type: 'category',
	data: []
  },
  yAxis: {
	type: 'value'
  },
  series: [
	{
	  name: 'member', // 和legend是绑定的
	  data: [],
	  type: 'line'
	}
  ]
};

****************************************************************************************************************************************************************************

20、权限管理
【1】Role
复制前端的Role,全局替换。修改下细节字段即可
*****************************************************************************
复制后端的Role、RoleMapper.interface、RoleMapper.xml、RoleController
*****************************************************************************
修改下细节字段即可
【2】菜单分配页实现新功能 32分钟
<!--树形控件-->
<el-tree
:data="menu.data"
show-checkbox
node-key="id"
:default-expanded-keys="[2]"
:default-checked-keys="[3]"
@check-change="onCheckChange">
</el-tree>
*****************************************************************************
如果是PostMapping,用post必须指定body里一个{}空对象查询
如果要求登录注意header参数的token设置
*****************************************************************************关键代码
/*找到所有*/
@PostMapping("/list_all")
public Res list_all(@RequestBody Menu menu) {
List<Menu> menuList = menuMapper.selectList(null); // 查询所有
List<Menu> fatherMenuList = menuList.stream().filter(temp -> temp.getFather() == null).collect(Collectors.toList());// 找出father为null的一级菜单
for (Menu item : fatherMenuList) { // 找出每一个一级菜单的子菜单,并设置给item(父菜单)
List<Menu> childrenList = menuList.stream().filter(temp -> item.getId().equals(temp.getFather())).collect(Collectors.toList());
item.setChildren(childrenList);
}
return Res.success(fatherMenuList);
}
【2】角色拥有的菜单,一个字段搞定的事情,青戈非要再用一个表+实体类....
private String menuIds; // 拥有的菜单集合
@TableField(exist = false)
private List<Integer> menuIdList;
*****************************************************************************这个问题处理
确实有点难度,就在于如果传参与获取的类型不一致,尽量前后端不要绑定,
绑定就很难处理,调用会报错400。分开后就不存在这个问题,特别是参与获取的类型不一致
menuIdList: [],  前端需要的是数组!!!!!!!!!
this.roleForm.menuIds ,后端需要的参数是字符串!!!!!!!!!!!!!!!!!!!
【3】动态路由
给用户一个角色role
*****************************************************************************
role.name不允许重复设置。
索引-角色名称/name/UNIQUE/BTREE---后端捕获异常,返回给前端提示...
*****************************************************************************多测试,空指针bug
@PostMapping("/select")
public Res select(@RequestBody Role role) {
Role existRole = roleMapper.selectById(role.getId());
// 处理成前端需要的格式menuIds ->menuIdList  提供前端需要的格式!!!!!!!!!!!!!!!!!!!
List<Integer> menuIdList = new ArrayList<>();
if (existRole.getMenuIds() != null) {
String menuIds = existRole.getMenuIds().replace("[", "").replace("]", "");
String[] tempArray = menuIds.split(",");
for (String temp : tempArray) {
menuIdList.add(Integer.parseInt(temp));
}
}
    List<Menu> menuList = menuMapper.selectBatchIds(menuIdList);
existUser.setMenuList(menuList);// !!!!!!!!!都是为了这个
return Res.success(existRole);// 需要返回对象
}
*****************************************************************************
我的方法节省了很大的功夫,同时也有很多细节BUG需要耐心排查。
*****************************************************************************30分钟
removeIf()等,我都不需要写这些东西,因为存的好,表建的好!!!!!!!!!
*****************************************************************************都浓缩成这个方法了!!!!!!!
@PostMapping("/login")
public Res login(@RequestBody User user) {
// 拿到用户输入的账号密码
User existUser;
try {
existUser = userMapper.selectUserByNamePassword(user);
} catch (Exception e) { // 如果系统中存在多条等异常情况
e.printStackTrace();
return Res.error(Constants.CODE_500, "系统错误");
}
if (existUser == null) {
return Res.error(Constants.CODE_400, "用户名或密码错误");
}
// 设置token,保证existUser要用id与password
String token = Token.productToken(existUser);
existUser.setToken(token);
existUser.setPassword("xxx"); // 拿到token后,保护用户密码

Role role = new Role();
role.setName(existUser.getRole());
//根据用户角色,查到角色完整信息
Role existRole = roleMapper.selectRoleByName(role);
//根据角色的菜单,生成菜单List,设置到existUser中
List<Integer> menuIdList = new ArrayList<>();
if (existRole.getMenuIds() != null) { // 存在菜单
String menuIds = existRole.getMenuIds().replace("[", "").replace("]", "");
String[] tempArray = menuIds.split(",");
for (String temp : tempArray) {
menuIdList.add(Integer.parseInt(temp));
}
}
// 不存在菜单,设置为空
List<Menu> menuList = menuMapper.selectBatchIds(menuIdList);
List<Menu> fatherMenuList = menuList.stream().filter(temp -> temp.getFather() == null).collect(Collectors.toList());// 找出father为null的一级菜单
for (Menu item : fatherMenuList) { // 找出每一个一级菜单的子菜单,并设置给item(父菜单)
List<Menu> childrenList = menuList.stream().filter(temp -> item.getId().equals(temp.getFather())).collect(Collectors.toList());
item.setChildren(childrenList);
}
existUser.setMenuList(fatherMenuList);// !!!!!!!!!都是为了这个

return Res.success(existUser);// 这里返回existUser 是因为虽然用户输入的是名+密码,但是实际拿到还有图标等信息
}
*****************************************************************************实现动态菜单
确实有省功夫的地方,但是青戈的这个思路还是很值得学习的!!!!!!
<div v-for="item in menuList" :key="item.id">
<!--有path一级菜单,全靠login Controller返回的父子关系数据-->
<div v-if="item.path">
<el-menu-item :index="item.path">
<i :class="item.icon"></i><!--这里如果不放在上面,收缩时会看不到-->
<template slot="title">
<span>{{item.name}}</span>
</template>
</el-menu-item>
</div>
<!--二级菜单-->
<div v-else>
<el-submenu :index="item.id+''">
<template slot="title">
<i :class="item.icon"></i>
<span>{{item.name}}</span>
</template>
<div v-for="subItem in item.children" :key="subItem.id">
<el-menu-item :index="subItem.path">
<i :class="subItem.icon"></i>
<span>{{subItem.name}}</span>
</el-menu-item>
</div>
</el-submenu>
</div>
</div>
*****************************************************************************
<el-menu :default-openeds="openList"
                 background-color="rgb(48,65,86)"
                 text-color="white"
                 active-text-color="yellow"
                 :collapse-transition="false"
                 :collapse="fff_left_collapseFlag"
                 router>
*****************************************************************************
openList: localStorage.getItem("menuList") ? JSON.parse(localStorage.getItem("menuList")).map(v => v.id + '') : []
【4】动态路由终版起飞
export const setRoutes = () => {
  const menuList = JSON.parse(localStorage.getItem("menuList"));
  if (menuList) {
    const finalRoute = { // 动态的添加进去
      path: '/', component: Composite,
      redirect: "/login",
      children: []
    }
    menuList.forEach(item => {
      if (item.path) { // 一级路由
        let menu = {
          path: item.path.replace("/", ""),
          name: item.name,
          component: () => import('../views/' + item.vue + '.vue')
        }
        finalRoute.children.push(menu)
      } else if (item.children.length) {
        item.children.forEach(subItem => { // 二级路由
          if (subItem.path) {
            let subMenu = {
              path: subItem.path.replace("/", ""),
              name: subItem.name,
              component: () => import('../views/' + subItem.vue + '.vue')
            }
            finalRoute.children.push(subMenu)
          }
        })
      }
    })
    // 动态添加到现有路由
    router.addRoute(finalRoute)
  }
}
*****************************************************************************Login.vue
import {setRoutes} from '../router/router.js'
setRoutes() // 动态设置当前用户路由
*****************************************************************************处理其他细节问题
router.js里调下自己!!!!!!!!!!!!!!!
setRoutes();// 刷新页面的时候刷新路由
*****************************************************************************报错重复路由...
vue-router.esm.js?3423:16 [vue-router] Duplicate named routes definition: { name: "主页", path: "/home" }
*****************************************************************************解决,没有的时候才添加
// 动态添加到现有路由
const routeNameList = router.getRoutes().map(v => v.name)
if (!routeNameList.includes('composite')) {
  router.addRoute(finalRoute)
}
*****************************************************************************访问其他页面提示404
{path: '*', name: '404', component: NotFound},
*****************************************************************************
<template>
    <div style="text-align: center;margin-top: 300px;">
        <h1>404 NotFound 请您检查路由地址是否正确</h1>
        <br>
        <h2>您也可以联系微信:15921291928</h2>
    </div>
</template>

<script>
  export default {
    name: "NotFound"
  }
</script>

<style scoped>

</style>
*****************************************************************************自己的逻辑又解决了一个问题!!!!
//后台处理下,选择主页1 选择用户管理3,但是没传过来2系统管理的问题。就是记录了子菜单,但是没有父菜单,动态路由的时候有问题
List<Integer> tempList = new ArrayList<>(); // 用另一个容器存储,防止遍历时出现问题
for (int i = 0; i < menuIdList.size(); i++) {
tempList.add(menuIdList.get(i));
}
for (Integer menuId : menuIdList) {
Menu menu = menuMapper.selectById(menuId);
if (menu.getFather() != null && !tempList.contains(menu.getFather())) {// 这个就是有孩子,但是集合id里面没有对应父级id
tempList.add(menu.getFather()); // 临时容器存储
}
}
// 不存在菜单,设置为空
List<Menu> menuList = menuMapper.selectBatchIds(tempList);

List<Menu> fatherMenuList = menuList.stream().filter(temp -> temp.getFather() == null).collect(Collectors.toList());// 找出father为null的一级菜单
for (Menu item : fatherMenuList) { // 找出每一个一级菜单的子菜单,并设置给item(父菜单)
List<Menu> childrenList = menuList.stream().filter(temp -> item.getId().equals(temp.getFather())).collect(Collectors.toList());
item.setChildren(childrenList);
}
existUser.setMenuList(fatherMenuList);// !!!!!!!!!都是为了这个

return Res.success(existUser);// 这里返回existUser 是因为虽然用户输入的是名+密码,但是实际拿到还有图标等信息
*****************************************************************************分配完毕的重新登录
setRoutes()// 动态设置当前用户路由
localStorage.setItem("user", JSON.stringify(res.data.object)) // 存储用户信息到浏览器
localStorage.setItem("menuList", JSON.stringify(res.data.object.menuList)) // 存储菜单信息
*****************************************************************************
if (!localStorage.getItem("user")) { // 首次登录必须刷新路由,否则存在权限不对应问题
  router.addRoute(finalRoute)
}
*****************************************************************************这个可以处理,也有弊端
created() {
if (window.location.href.indexOf("#reloaded") == -1) {
window.location.href = window.location.href + "#reloaded";
window.location.reload();
}
},
*****************************************************************************全局搜索
Ctrl + Shift + R
myVuex.commit("userLogout")  全局退出登录
*****************************************************************************操作完管理员角色需要重新登录
giveMenu(row) {
this.roleForm.id = row.id; // 为了分配菜单用的
this.roleForm.name = row.name
*****************************************************************************
if (this.$refs.tree !== undefined && this.roleForm.id !== undefined) {
  if (this.roleForm.name === "管理员") {
myVuex.commit("userLogout")
  }
}
*****************************************************************************
父级菜单的处理,还真不能存到数据库。就后台动态返回就完事了...
【5】解决一些系统BUG
访问http://localhost:8080/  提示404问题解决
*****************************************************************************404与登录页面处理
if (to.matched.length === 0) { // 未找到路由的情况
const storeMenuList = localStorage.getItem("menuList")
if (storeMenuList) {
  next("/404")
} else {
  next("/login") // 如果没有菜单信息跳转到登录页面
}
}
next() // 放行
*****************************************************************************牛批 
网友水友...发现了我发现的问题,青戈说的好像也对
知识的学习,重要的在于运用,我日TM
404BUG的处理。根源还是没有添加路由,还是上次的路由。如何处理呢?
重置路由就解决了
// 重置路由的方法
export const clearRouter = () => {
  router.matcher = new VueRouter({
    mode: 'history',
    base: process.env.BASE_URL,
    routes
  })
}
*****************************************************************************
userLogout(state) { // 全局退出方法
  // 清空缓存
  localStorage.removeItem("user")
  localStorage.removeItem("menuList")
  router.push("/login")
  // 重置清空路由
  clearRouter()
}
*****************************************************************************
终于终于搞定了 牛批的版本

****************************************************************************************************************************************************************************

24、服务器部署
【1】自己动手解决,系统菜单收缩后仍展示问题
买阿里云、开端口号
******************************************************************************
firewall-cmd --zone=public --list-ports #查看历史开启端口
systemctl status firewalld #查看防火墙状态
systemctl start firewalld #开启防火墙
firewall-cmd --zone=public --add-port=端口号/tcp --permanent #开启新的端口号
firewall-cmd --reload #重启防火墙
iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 端口号 #将80映射到某个端口
firewall-cmd --zone=public --remove-port=端口号/tcp --permanent # 关闭端口号
【2】nginx的配置
#把默认的80端口改成8002端口。代理dist目录/root/site/white/dist
cd /usr/local/nginx/conf
vim nginx.conf               
******************************************************************************               
cd /usr/local/nginx/sbin
./nginx
ps -ef | grep nginx
******************************************************************************
netstat -lnp|grep 8002
kill -9 21458
******************************************************************************
访问http://wdfgdzx.top:8002/
******************************************************************************代理如下目录
location / {
	  root /root/site/white/dist;
	  index index.html index.htm;
	  try_files $uri $uri/ /index.html;
}
******************************************************************************重启
cd /usr/local/nginx/sbin
./nginx -s reload
【3】配置动态whiteIp  blackIp地址  25-36分钟
// 线上前端IP
export const whiteIp = "wdfgdzx.top:8000"
******************************************************************************前端
localhost.js white.js
******************************************************************************后端
localhost.properties    black.properties
【4】使用的技术
前端:VUE2 Vue-Router Vuex ElementUI Axios
后端:SpringBoot Hutool Poi Lombok MybatisPlus

****************************************************************************************************************************************************************************

26、1V1、1对多、多V多查询
【1】以老师、学生教学为示例
*****************************************************************************41
一个表四个复制
【2】主要是数据库的查询练习,看着学习吧。1V1查询
*****************************************************************************
<!--1.2查询分页数据-->
*****************************************************************************每次select的时候
用了left join
用teacher_id去left join user.id,从而获得user.nick as 当做teacherName去使用。在前端展示
!!!!!!!!!!!!!!!牛批,悟透了!!!!!!! 哈哈哈哈哈
*****************************************************************************
从科目到讲课老师:是1V1
从讲课老师到多个科目:是1V多  老师是1    课程是多
【3】老师查看自己教授的课程。1V多
【4】学生选课 多V多
<!--设置学生选课信息,先删除后设置-->

****************************************************************************************************************************************************************************

32、个人中心、修改头像、数据联动/修改密码
【1】个人中心还需要讲吗?.............
一个个人中心讲解了30分钟....
【2】修改密码,autocomplete="new-password"这个好用,不会自动填充密码
<el-form-item label="用户名">
	<el-input v-model="user.name" disabled autocomplete="off"></el-input>
</el-form-item>

<el-form-item label="新密码" prop="newPassword">
	<el-input v-model="passwordForm.newPassword" autocomplete="new-password" show-password></el-input>
</el-form-item>

<el-form-item label="确认新密码" prop="confirmPassword">
	<el-input v-model="passwordForm.confirmPassword" autocomplete="new-password"
			  show-password></el-input>
</el-form-item>

****************************************************************************************************************************************************************************

33、集成wangeditor
【1】安装
npm i wangeditor -S --force
********************************************************************************
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date recordTime;
private String modifyPerson;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date modifyTime;
********************************************************************************
Ctrl+R   区分大小写后,批量替换,直接起飞!!!!!!!!!!!!!
********************************************************************************
Error in v-on handler: "Error: 无效的节点选择器:content"
nextTick解决这个问题
********************************************************************************
// 新增的时候开始操作wangEditor
this.$nextTick(() => {
const editor = new E('#content')
editor.create()
})
********************************************************************************23分
对于wangEditor的打开窗口显示,我解决了一个历史性难题,牛批!!!!!!!!!!!
********************************************************************************
@PostMapping("/editor")
public JSON editor(MultipartFile multipartFile) throws IOException { // 这里是三方WangEditor发起的请求,所以不能用包装对象,emmm!!!!!!
	String originName = multipartFile.getOriginalFilename();
	String type = FileUtil.extName(originName);
	long size = multipartFile.getSize();
	// 1、存到磁盘、存储数据库
	File uploadDir = new File(MyUtils.getFinalPath());
	System.err.println(uploadDir);
	if (!uploadDir.exists()) {
		System.out.println(uploadDir.mkdir()); //不存在创建新目录
	}
	// 2、最终文件路径
	String fileUUID = IdUtil.fastSimpleUUID() + "." + type;
	String url = blackIp + "/document/" + fileUUID;
	File uploadFile = new File(uploadDir + "/" + fileUUID);
	//3、获取md5
	if (!uploadFile.exists()) {
		multipartFile.transferTo(uploadFile); // 这时候才存磁盘!!!!!
	}
	String md5 = SecureUtil.md5(uploadFile);
	Document document = new Document();
	// 4、判断数据库里md5是否存在
	document.setMd5(md5);
	List<Document> documentList = documentMapper.md5_list(document); // 数据库查询到相同md5的对象
	System.out.println(documentList);
	if (documentList.size() != 0) {
		url = documentList.get(0).getUrl(); // 存在的话,url直接从已存在数据库信息拿到
		System.out.println(uploadFile.delete());// 删除本地存储的文件
	}
	// 5、存数据库
	Document insertDocument = new Document();
	insertDocument.setUrl(url);
	insertDocument.setName(originName);
	insertDocument.setType(type);
	insertDocument.setSize(size / 1024);
	insertDocument.setMd5(md5);
	documentMapper.insert(insertDocument);

	JSONObject jsonObject = new JSONObject();
	// 第一个设置
	jsonObject.set("errno", 0);
	JSONObject data = new JSONObject();
	data.set("url", url);
	JSONArray jsonArray = new JSONArray();
	jsonArray.add(data);
	// 第二个设置
	jsonObject.set("data", jsonArray);

	return jsonObject;
}

****************************************************************************************************************************************************************************

19、前台页面
【1】先跟着老师实现吧,整合后面自己思考下!!!!
还是很多收获的...
*************************************************************************************
不管是否前台后台一起写,组件名最好不要重名,懂吧!!!!!!!!!!!!
 // 配置前台页面路由
  {
    path: '/white',
    name: 'White',
    component: White, // 这是综合管理页面,访问每个子页面的时候它都会展示
    children: [
      {path: '/main', name: 'Main', component: Main},
    ]
  },
*************************************************************************************
注意在这里加了判断:控制路由跳转
if (res.data.object.role === "学生") {
  this.$router.push("/main") // 跳转前台首页
} else { // 跳到后台首页
  this.$router.push("/home")
  this.$message.success("登录成功,欢迎您")
}
*************************************************************************************
登录什么的可以复用原有的管理端....
*************************************************************************************
<el-menu :default-active="'1'" class="el-menu-demo" mode="horizontal" router>
这个router让我排查了一会!!!!!!!!!!!!!!!!!!!!!!!!!!
最后记得带router,日尼玛
*************************************************************************************
青戈真是哥,1小时的视频,我学习捉摸了很久才get到精髓所在。
*************************************************************************************
个人信息页面、修改密码加回来 emmm,卧槽了!!!!!!!!!!!

****************************************************************************************************************************************************************************

14、实现Redis缓存效果
【1】缓存数据,让用户体验更好,速度更快,避免对数据库的多次请求。
spring自带的也有cache缓存,但是我们重点学习redis
****************************************************************************************
单机测试:发现,没有缓存,接口会频繁请求数据库,会有打满风险。
【2】主角:Springboot集成Redis
Redis的学习安装什么的自己在学习一遍吧!!!!!!!!!!!!!!
<!--Redis-->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
****************************************************************************************
// 刷新Redis  其实是直接清空!!!!!!!!!!!! 
// 如果用到了,在增删改的时候都需要调用下flushRedis(Constants.LIST_ALL_KEY);
private void flushRedis(String key) {
	stringRedisTemplate.delete(key);
}
****************************************************************************************
实现了减轻数据库压力的作用。
当然这样比较粗放,可以更精细化,比如删除那一条,重新从数据库查询,重新set
感觉也差不多!!!!!!!!!!!!!!!!!!
****************************************************************************************
更优的方法,是去除之前的缓存json--->变成List--->操作List的增删改(和数据库保持一致的
的同时,不用二次查询数据库...)!!!!!!!!!!!!!!!!!!!
--->NoSQL???好像也还行......

****************************************************************************************************************************************************************************

20、视频播放
【1】先实现后台文件的预览功能,配置使用kkfileview(不好用 算了)
写个Video页面,然后再写Detail页面
*************************************************************************************
npm i vue-video-player -S --force
*************************************************************************************
npm i video.js -S --force
*************************************************************************************
<template>
    <div class="Detail-container" style="margin-top: 5px;">
        <div id="player">
            <video-player class="video-player vjs-big-play-centered"
                          ref="videoPlayer"
                          :playsinline="false"
                          :options="playerOptions">
            </video-player>
        </div>
    </div>
</template>

<script>
  // 在组件中引入
  import {videoPlayer} from 'vue-video-player'
  import 'vue-video-player/src/custom-theme.css'
  import 'video.js/dist/video-js.css'

  export default {
    name: "Detail",
    // 注册
    components: {videoPlayer},
    data() {
      return {
        // video: {},
        // 视频控制设置
        playerOptions: {
          playbackRates: [0.5, 1.0, 1.5, 2.0], // 可选的播放速度
          autoplay: false, // 如果为true,浏览器准备好时开始回放。
          muted: false, // 默认情况下将会消除任何音频。
          loop: false, // 是否视频一结束就重新开始。
          preload: "auto", // 建议浏览器在<video>加载元素后是否应该开始下载视频数据。auto浏览器选择最佳行为,立即开始加载视频(如果浏览器支持)
          language: "zh-CN",
          aspectRatio: "16:9", // 将播放器置于流畅模式,并在计算播放器的动态大小时使用该值。值应该代表一个比例 - 用冒号分隔的两个数字(例如"16:9"或"4:3")
          fluid: true, // 当true时,Video.js player将拥有流体大小。换句话说,它将按比例缩放以适应其容器。
          sources: [
            {
              type: "video/mp4", // 类型
              src: "", // url地址
            },
          ],
          poster: "", // 封面地址
          notSupportedMessage: "此视频暂无法播放,请稍后再试", // 允许覆盖Video.js无法播放媒体源时显示的默认信息。
          controlBar: {
            timeDivider: true, // 当前时间和持续时间的分隔符
            durationDisplay: true, // 显示持续时间
            remainingTimeDisplay: true, // 是否显示剩余时间功能
            fullscreenToggle: true, // 是否显示全屏按钮
          }
        }
        // -------
      }
    },
    created() {
      let id = this.$route.query.id // 从上个路径拿到的id
      this.$http.post("/document/select_id", {id: id}).then(res => {
        // console.log(res)
        if (res.data.code = "200") {
          this.video = res.data.object;
          this.playerOptions.sources[0].src = res.data.object.url  // 赋值视频地址
        } else {
          this.$message.error(res.data.code)
        }
      })
    }
  }
</script>

<style scoped lang="less">

</style>

****************************************************************************************************************************************************************************

21、多级评论
【1】新建novel表和写对应的后端代码!!!!!!!!!!!
【2】处理前端和安装相关插件
npm i mavon-editor@2.10.4 -S --force
***************************************************************************************
import mavonEditor from 'mavon-editor'
import 'mavon-editor/dist/css/index.css'

Vue.use(mavonEditor) // 挂载
***************************************************************************************
import axios from "axios";
***************************************************************************************
imgAdd(pos, $file) {
	let $vm = this.$refs.md
	// 第一步.将图片上传到服务器.
	const formData = new FormData();
	formData.append('file', $file);
	axios({
	  url: `http://${whiteIp}/document/upload`, // 这是自己后台的接口
	  method: 'post',
	  data: formData,
	  headers: {'Content-Type': 'multipart/form-data'},
	}).then((res) => {
	  // 第二步.将返回的url替换到文本原位置![...](./0) -> ![...](url)
	  /**
	   * $vm 指为mavonEditor实例,可以通过如下两种方式获取
	   * 1. 通过引入对象获取: `import {mavonEditor} from ...` 等方式引入后,
	   * `$vm`为`mavonEditor`
	   * 2. 通过$refs获取: html声明ref : `<mavon-editor ref=md ></mavon-editor>,
	   * `$vm`为 `this.$refs.md`
	   */
	  $vm.$img2Url(pos, res.data);
	})
  },
***************************************************************************************
处理小说内容查看的方式 30分钟
【3】最最中重点:小说评论功能的实现
@PostMapping("/select_tree_by_novel_id/{novelId}")
public Res select_tree_by_novel_id(@PathVariable Integer novelId) {
	List<Discuss> discussList = discussMapper.selectListByNovelId(novelId);
	List<Discuss> rootList = discussList.stream().filter(discuss -> discuss.getRootId() == null).collect(Collectors.toList()); // 所有没有父级的
	for (Discuss discuss : rootList) {
		List<Discuss> childrenList = discussList.stream().filter(temp -> temp.getRootId() != null && temp.getRootId().equals(discuss.getId())).collect(Collectors.toList());
		// 设置父级评论的用户昵称+用户ID
		childrenList.forEach(children -> {
			Discuss tempDisCuss = discussMapper.selectById(children.getFatherId()); // 根据父id查到对应的评论!!!!注意父id和根id的区别!!!!!!!!
			User tempUser = userMapper.selectById(tempDisCuss.getUserId()); // 用用户ID查询到用户
			// 设置父级用户ID+昵称
			children.setFatherUserId(tempUser.getId());
			children.setFatherUserNick(tempUser.getNick());
		});
		discuss.setDiscussChildrenList(childrenList);
	}
	return Res.success(rootList); // 注意返回的是rootList!!!!!!!!!!!!!!!!
}
***************************************************************************************
@PostMapping("/insertOrUpdate")
public Res insertOrUpdate(@RequestBody Discuss discuss) { // @RequestBody很重要
	if (discuss.getId() != null) {
		discussMapper.updateById(discuss);
	} else {
		if (discuss.getFatherId() != null) {
			Discuss fatherDiscuss = discussMapper.selectById(discuss.getFatherId()); // 找到父节点
			if (fatherDiscuss.getFatherId() != null) { // 说明父节点,也有父节点,这时候要操作了!!!!!!!!!!!!
				discuss.setRootId(fatherDiscuss.getRootId());  // 设置和父节点同样的祖宗ID!!!!!!!!!!!!!!!!!
				// 这里和childrenList用rootId筛选就形成了妙处!!!!!!!!不然就陷入死循环了,秒秒秒呀!!!!!!!!!!!!!!!
				// 这里和childrenList用rootId筛选就形成了妙处!!!!!!!!不然就陷入死循环了,秒秒秒呀!!!!!!!!!!!!!!!
				// 这里和childrenList用rootId筛选就形成了妙处!!!!!!!!不然就陷入死循环了,秒秒秒呀!!!!!!!!!!!!!!!
				// 这里和childrenList用rootId筛选就形成了妙处!!!!!!!!不然就陷入死循环了,秒秒秒呀!!!!!!!!!!!!!!!
				// 这里和childrenList用rootId筛选就形成了妙处!!!!!!!!不然就陷入死循环了,秒秒秒呀!!!!!!!!!!!!!!!
				// 这里和childrenList用rootId筛选就形成了妙处!!!!!!!!不然就陷入死循环了,秒秒秒呀!!!!!!!!!!!!!!!
			}
		}
		discussMapper.insert(discuss);
	}
	return Res.success(null);
}

****************************************************************************************************************************************************************************

22、支付宝
【1】生成公钥私钥的工具
https://opendocs.alipay.com/common/02kipk?pathHash=0d20b438
【2】沙箱环境
https://openhome.alipay.com/develop/sandbox/app
27分钟,orderMapper内容看不到!!!!!!!!!!!!!!!
【3】图书管理、订单管理页面...25分钟
package com.black.controller;
import com.alipay.easysdk.factory.Factory;
import com.alipay.easysdk.payment.page.models.AlipayTradePagePayResponse;
import com.black.mapper.OrdersMapper;
import com.black.pojo.Alipay;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/alipay")
public class AlipayController {
    @Resource
    private OrdersMapper ordersMapper;

    @GetMapping("/pay")
    public String pay(Alipay aliPay) {
        AlipayTradePagePayResponse alipayTradePagePayResponse = null;
        try {
            // 发起API调用请求,以创建首付款二维码为例
            alipayTradePagePayResponse = Factory.Payment.Page().pay(aliPay.getSubject(),
                    aliPay.getTraceNo(),
                    aliPay.getTotalAmount(),
                    "");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return alipayTradePagePayResponse.getBody();
    }

    @PostMapping("/notify")
    public String notify(HttpServletRequest httpServletRequest) throws Exception {
        if (httpServletRequest.getParameter("trade_status").equals("TRADE_SUCCESS")) {
            System.out.println("---支付宝异步回调---");
            Map<String, String> paramsMap = new HashMap<>();
            Map<String, String[]> requestParamsMap = httpServletRequest.getParameterMap();
            for (String name : requestParamsMap.keySet()) {
                paramsMap.put(name, httpServletRequest.getParameter(name));
            }
            String tradeNo = paramsMap.get("out_trade_no");
            String gmtPayment = paramsMap.get("gmt_payment");
            String alipayNo = paramsMap.get("trade_no");
            // 支付宝验签
            if (Factory.Payment.Common().verifyNotify(paramsMap)) {
                System.out.println("交易名称" + paramsMap.get("subject"));
                System.out.println("交易状态" + paramsMap.get("trade_status"));
                System.out.println("支付宝交易凭证" + paramsMap.get("trade_no"));
                System.out.println("商户订单号" + paramsMap.get("out_trade_no"));
                System.out.println("交易金额" + paramsMap.get("total_amount"));
                System.out.println("买家在支付宝唯一id" + paramsMap.get("buyer_id"));
                System.out.println("买家付款时间" + paramsMap.get("gmt_payment"));
                System.out.println("买家付款金额" + paramsMap.get("gmt_pay_amount"));
                // 更新订单为已支付
                ordersMapper.updateState(tradeNo, "已支付", gmtPayment, alipayNo);
            }
        }
        return "success";
    }
}
************************************************************************************
<update id="updateState">
	update orders
	set state=#{state},
		payment_time=#{gmtPayment},
		alipay_no=#{alipayNo}
	where no =#{tradeNo}
</update>
【4】退款功能
<!--支付宝-->
<!-- https://mvnrepository.com/artifact/com.alipay.sdk/alipay-easysdk -->
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>4.22.110.ALL</version>
</dependency>
************************************************************************************
用这个包
@GetMapping("/return_pay")
public Res return_pay(Alipay alipay) throws Exception {
	// 7天无理由退款
	String now = DateUtil.now();
	Orders orders = ordersMapper.getByNo(alipay.getTraceNo());
	if (orders != null) {
		// hutool工具类,判断时间间隔
		long between = DateUtil.between(DateUtil.parseDateTime(orders.getPaymentTime()), DateUtil.parseDateTime(now), DateUnit.DAY);
		if (between > 7) {
			return Res.error("-1", "该订单已超过7天,不支持退款");
		}
	}

	// 1. 创建Client,通用SDK提供的Client,负责调用支付宝的API
	AlipayClient alipayClient = new DefaultAlipayClient(GATEWAY_URL,
			alipayConfig.getAppId(), alipayConfig.getAppPrivateKey(), FORMAT, CHARSET,
			alipayConfig.getAlipayPublicKey(), SIGN_TYPE);
	// 2. 创建 Request,设置参数
	AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
	JSONObject bizContent = new JSONObject();
	bizContent.set("trade_no", alipay.getAlipayTraceNo());  // 支付宝回调的订单流水号
	bizContent.set("refund_amount", alipay.getTotalAmount());  // 订单的总金额
	bizContent.set("out_request_no", alipay.getTraceNo());   //  我的订单编号

	request.setBizContent(bizContent.toString());

	// 3. 执行请求
	AlipayTradeRefundResponse response = alipayClient.execute(request);
	if (response.isSuccess()) {  // 退款成功,isSuccess 为true
		System.out.println("调用成功");

		// 4. 更新数据库状态
		ordersMapper.updatePayState(alipay.getTraceNo(), "已退款", now);
		return Res.success();
	} else {   // 退款失败,isSuccess 为false
		System.out.println(response.getBody());
		return Res.error(response.getCode(), response.getBody());
	}
}

****************************************************************************************************************************************************************************

23、购物车  1小时10分钟
【1】商品
【2】购物车
【3】订单中心

****************************************************************************************************************************************************************************

24、在线考试

****************************************************************************************************************************************************************************

·

****************************************************************************************************************************************************************************

26、聊天室
 【1】两个浏览器,两个账号!!!
 WsConfig
 WsUtil
 ***************************************************************************************
 设置拦截器:
  public void addInterceptors(InterceptorRegistry interceptorRegistry) {
        interceptorRegistry.addInterceptor(jwtInterceptor()) // 调用@Bean返回的
                .addPathPatterns("/**")// 拦截的所有请求,判断token是否合法来决定是否需要登录。
                .excludePathPatterns(
                        "/big/login",
                        "/big/register",
                        "/document/**",
                        "/**/list_export",
                        "/**/list_import",
                        "/swagger**/**",
                        "/webjars/**",
                        "/v2/**",
                        "/doc.html",
                        "/alipay/**",
                        "ws_server/**"); // 需要配置4个swagger放行
    }
***************************************************************************************
学习之,改进之!!!!!!!!!!!!!!!!!!
<template>
    <div class="Chat-container" style="padding: 10px;margin-bottom:50px; ">
        <el-row>
            <!--在线人数区域-->
            <el-col :span="4">
                <el-card style="width: 300px;height: 300px;color: #333333">
                    <div style="padding-bottom: 10px;border-bottom: 1px solid #cccccc">
                        在线用户<span style="font-size: 12px;">(点击聊天气泡开始聊天)</span>
                    </div>
                    <div style="padding: 10px 0;" v-for="item in userList" :key="item.name">
                        <span>{{item.name}}</span>
                        <i class="el-icon-chat-dot-round" style="margin-left: 10px;font-size: 16px;cursor: pointer"
                           @click="clickChatUser(item)"></i>
                        <span style="font-size: 12px;color: limegreen;margin-left: 5px;" v-if="item.name===chatUser">chatting...</span>
                    </div>
                </el-card>
            </el-col>
            <!--聊天窗口-->
            <el-col :span="20">
                <div style="width: 800px;margin: 0 auto;background-color: white;border-radius: 5px;box-shadow: 0 0 10px #cccccc">
                    <div style="text-align: center;line-height: 50px;">
                        WsWeb聊天室<span style="color: limegreen" v-if="chatUser">(与{{chatUser}}聊天中...)</span>
                    </div>
                    <div style="height: 350px;overflow: auto;border-top:1px solid #ccc" v-html="content"></div>
                    <div style="height: 200px;">
                        <textarea v-model="text"
                                  style="height: 160px;width: 100%;padding: 20px; border: none;border-top: 1px solid #ccc;border-bottom: 1px solid #ccc;outline: none">
                        </textarea>
                        <div style="text-align: right;padding-right: 10px">
                            <el-button type="primary" size="mini" @click="send">发送</el-button>
                        </div>
                    </div>
                </div>
            </el-col>
        </el-row>
    </div>
</template>

<script>
  import {whiteIp} from "../../public/config";

  let socket;
  export default {
    name: "Chat",
    data() {
      return {
        user: {},
        userList: [],
        chatUser: '',
        chatUserAvatar: '',
        text: '',
        messageList: [],
        content: ''
      }
    },
    created() {
      this.wsInit()
    },
    methods: {
      clickChatUser(item) {
        this.chatUser = item.name;
        this.$http.post("/user/select", item).then(res => {
          if (res.data.code = "200") {
            // console.log(res)
            this.chatUserAvatar = res.data.object.avatar;
          }
        })
      },
      wsInit() {
        this.user = localStorage.getItem("user") ? JSON.parse(localStorage.getItem("user")) : {} // 获取本地存储用户
        let userName = this.user.name
        let _this = this;
        if (typeof (WebSocket) == 'undefined') {
          console.log('您的浏览器不支持ws...')
        } else {
          console.log('您的浏览器支持ws!!!')
          let socketUrl = "ws://" + whiteIp + "/ws_server/" + userName
          if (socket != null) {
            socket.close()
            socket = null
          }
          socket = new WebSocket(socketUrl);
          // ws的几个事件,在vue中定义
          socket.onopen = function () {
            console.log('ws已经打开...')
          }
          socket.onmessage = function (message) {
            console.log('收到数据===' + message.data)
            let data = JSON.parse(message.data)
            if (data.userList) {
              _this.userList = data.userList.filter(item => item.name !== userName)
            } else {
              // if (data.from === _this.chatUser)
              if (data.from) {
                // console.log(data.from)
                _this.chatUser = data.from;
                _this.$http.post("/user/select", {name: data.from}).then(res => {
                  if (res.data.code = "200") {
                    // console.log(res)
                    _this.chatUserAvatar = res.data.object.avatar;
                    _this.messageList.push(data)
                    // 构建发送的消息
                    _this.createContent(data.from, null, data.text)
                  }
                })
              }
            }
          }
          // 关闭事件
          socket.onclose = function () {
            console.log('ws已关闭...')
          }
          socket.onerror = function () {
            console.log('发生错误...')
          }
        }
      },
      send() {
        if (!this.chatUser) {
          this.$message({type: 'warning', message: "请选择聊天对象"})
          return;
        }
        if (!this.text) {
          this.$message({type: 'warning', message: "请输入内容"})
        } else {
          if (typeof (WebSocket) == "undefined") {
            console.log("您的浏览器不支持WebSocket");
          } else {
            console.log("您的浏览器支持WebSocket");
            // 组装待发送的消息 json
            // {"from": "zhang", "to": "admin", "text": "聊天文本"}
            let message = {from: this.user.name, to: this.chatUser, text: this.text}
            socket.send(JSON.stringify(message));  // 将组装好的json发送给服务端,由服务端进行转发
            this.messageList.push({user: this.user.name, text: this.text})
            // 构建消息内容,本人消息
            this.createContent(null, this.user.name, this.text)
            this.text = '';
          }
        }
      },
      createContent(remoteUser, nowUser, text) {  // 这个方法是用来将 json的聊天消息数据转换成 html的。
        let html
        // 当前用户消息
        if (nowUser) { // nowUser 表示是否显示当前用户发送的聊天消息,绿色气泡
          html = "<div class=\"el-row\" style=\"padding: 5px 0\">\n" +
            "  <div class=\"el-col el-col-22\" style=\"text-align: right; padding-right: 10px\">\n" +
            "    <div class=\"tip left\">" + text + "</div>\n" +
            "  </div>\n" +
            "  <div class=\"el-col el-col-2\">\n" +
            "  <span class=\"el-avatar el-avatar--circle\" style=\"height: 40px; width: 40px; line-height: 40px;\">\n" +
            "    <img src=\"" + this.user.avatar + "\" style=\"object-fit: cover;\">\n" +
            "  </span>\n" +
            "  </div>\n" +
            "</div>";
        } else if (remoteUser) {   // remoteUser表示远程用户聊天消息,蓝色的气泡
          html = "<div class=\"el-row\" style=\"padding: 5px 0\">\n" +
            "  <div class=\"el-col el-col-2\" style=\"text-align: right\">\n" +
            "  <span class=\"el-avatar el-avatar--circle\" style=\"height: 40px; width: 40px; line-height: 40px;\">\n" +
            "    <img src=\"" + this.chatUserAvatar + "\" style=\"object-fit: cover;\">\n" +
            "  </span>\n" +
            "  </div>\n" +
            "  <div class=\"el-col el-col-22\" style=\"text-align: left; padding-left: 10px\">\n" +
            "    <div class=\"tip right\">" + text + "</div>\n" +
            "  </div>\n" +
            "</div>";
        }
        console.log(html)
        this.content += html;
      }
    }
  }
</script>

<style>
    .tip {
        color: white;
        text-align: center;
        border-radius: 10px;
        font-family: sans-serif;
        padding: 10px;
        width: auto;
        display: inline-block !important;
        display: inline;
    }

    .right {
        background-color: deepskyblue;
    }

    .left {
        background-color: forestgreen;
    }
</style>

****************************************************************************************************************************************************************************

30、电商系统与主流
【1】电商系统:
在终端执行这两个就能运行起来了:
set NODE_OPTIONS=--openssl-legacy-provider
npm run serve
【2】博客系统:
set NODE_OPTIONS=--openssl-legacy-provider
npm run serve
****************************************************************************************
npm i --force
set NODE_OPTIONS=--openssl-legacy-provider
npm run dev
【3】电影系统
Solution1:
npm install -g npm-check-updates
ncu -u
npm install

****************************************************************************************************************************************************************************

方式一:安装python解决(正确配置系统环境变量),python(v2.7 recommended, 
v3.x.x is not supported) - 推荐
下载:http://www.python.org/ftp/python/2.7.3/python-2.7.3.msi
自行下载
npm install --python=python2.7 #先下载
npm config set python python2.7 #再设置环境
**********************************************************************************************
# npm install node-sass@latest
npm install node-sass@4.12 --force 好使  卧槽
**********************************************************************************************
删除node_modules文件 
卸载webpack npm remove webpack-dev-server 
重装指定版本webpack npm install webpack-dev-server@2.9.1

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/601006.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

初始C++(一)

目录 前言&#xff1a; 命名空间&#xff1a; 总结&#xff1a; 前言&#xff1a; C语言学好了&#xff0c;现在当然要进阶了&#xff0c;那么就是从C开始。 C兼容C&#xff0c;支持其中90%的语法。可能有很多同学听说过C#&#xff0c;C#和C没有关系&#xff0c;是微软研究出…

SD-WAN对云服务的优化

在云服务日益普及的当下&#xff0c;SD-WAN技术正成为众多企业优化网络连接的首选方案。其通过优化云集成和连接&#xff0c;以及增强应用程序性能&#xff0c;为企业带来了前所未有的业务效益。这种革新性的云连接方式极大地促进了企业对全球劳动力和潜在客户的触达能力。 软件…

Java中Maven的依赖管理

依赖介绍 是指当前项目运行所需要的jar包&#xff0c;一个项目中可以引入多个依赖 配置 在pom.xml中编写<dependencies>标签 在<dependencies>中使用<dependency>引入标签 定义坐标的groupId、rtifactId、version 点击刷新按钮、引入新坐标 例如引入下…

git bash各分支修改内容不同但合并后不显示冲突问题

在跟着廖雪峰老师的git学习时&#xff0c;按部就班的执行明后&#xff0c;发现 而不是出现原文的结果 解决方法&#xff1a; 切换位feature分支&#xff0c;再合并 git switch feature1 git merge master 此时我们发现&#xff1a; 后面再跟着原文敲就可以了

【spark RDD】spark 之 Kryo高性能序列化框架

文章目录 一. RDD序列化的原因二. Kryo序列化框架三. spark 配置 kryo 序列化1. 设定kryo序列化2. 注册序列化类&#xff08;非必须&#xff0c;但是强烈建议做&#xff09;3. 配置 spark.kryoserializer.buffer 一. RDD序列化的原因 Spark初始化工作是在Driver端进行的&#…

机器人系统ros2-开发实践07-将机器人的状态广播到 tf2(Python)

上个教程将静态坐标系广播到 tf2&#xff0c;基于这个基础原理这个教程将演示机器人的点位状态发布到tf2 1. 写入广播节点 我们首先创建源文件。转到learning_tf2_py我们在上一教程中创建的包。在src/learning_tf2_py/learning_tf2_py目录中输入以下命令来下载示例广播示例代码…

【机器学习与实现】线性回归示例——波士顿房价分析

目录 一、创建Pandas对象并查看数据的基本情况二、使用皮尔逊相关系数分析特征之间的相关性三、可视化不同特征与因变量MEDV&#xff08;房价中值&#xff09;间的相关性四、划分训练集和测试集并进行回归分析 一、创建Pandas对象并查看数据的基本情况 boston.csv数据集下载&a…

Grotesque系列靶机Grotesque1

第一步信息收集&#xff1a; 靶机ip&#xff1a;192.168.108.131 攻击机IP&#xff1a;192.168.108.128 nmap扫描靶机的可用端口&#xff1a; 发现http服务的端口存在66端口和80端口 扫描一下靶机端口的http服务&#xff1a; 可以看到&#xff0c;默认的80端口是不存在的&am…

25_Scala集合Tuple

文章目录 tuple1.元组定义2.Tuple元素访问3.如果元素的len2&#xff0c;称之为键值对对象&#xff0c;也称之为对偶元组4.补充上节Map5.Map集合遍历6.集合之间相互转化 tuple 概念&#xff1a;scala语言采用特殊的方式将无关的数据作为一个整体&#xff0c;组合在一起’ 1.元…

【Git】Git学习-13:Gitee和GitLab的使用

学习视频链接&#xff1a;【GeekHour】一小时Git教程_哔哩哔哩_bilibili​编辑https://www.bilibili.com/video/BV1HM411377j/?vd_source95dda35ac10d1ae6785cc7006f365780 流程 1. 创建仓库/已有仓库 2. 克隆到本地/在远程仓库关联 git clone 仓库地址 git remote add 仓库别…

小语言模型的潜力

想象一下这样一个世界&#xff1a;智能助手不在云端&#xff0c;而是在你的手机上&#xff0c;无缝了解你的需求并以闪电般的速度做出响应。这不是科幻小说&#xff0c;而是科幻小说。这是小语​​言模型 (SLM) 的希望&#xff0c;这是一个快速发展的领域&#xff0c;有可能改变…

聪明与诚实:社会信任的桥梁

在现代社会中&#xff0c;我们经常听到这样的评价&#xff1a;“某人真聪明。”然而&#xff0c;当我们深入思考时&#xff0c;会发现“聪明”这个词背后所承载的含义并不单一。聪明和狡诈往往被混淆&#xff0c;而诚实的价值却时常被忽视。在一个高度诚信的社会里&#xff0c;…

CentOS 自建gitlab仓库:安装相关工具

所需环境 Node 安装项目依赖、项目打包运行Nginx 前端项目部署&#xff08;正向代理、反向代理、负载均衡等&#xff09;Git 自动化部署时 拉取代码使用GitLab 代码仓库GitLab-Runner GitLab的CI/CD执行器 一、安装Node 检测是否已安装 常用node -v 命令检测。 如果已安装&a…

【挑战30天首通《谷粒商城》】-【第一天】03、简介-分布式基础概念

文章目录 课程介绍 ( 本章了解即可&#xff0c;可以略过)1、微服务简而言之: 2、集群&分布式&节点2.1、定义2.2、示例 3、远程调用4、负载均衡常见的负裁均衡算法: 5、服务注册/发现&注册中心6、配置中心7、服务熔断&服务降级7.1、服务熔断7.2、服务降级 8、AP…

NVIDIA: RULER新测量方法让大模型现形

1 引言 最近在人工智能系统工程和语言模型设计方面的进展已经实现了语言模型上下文长度的高效扩展。以前的工作通常采用合成任务,如密钥检索和大海捞针来评估长上下文语言模型(LMs)。然而,这些评估在不同工作中使用不一致,仅揭示了检索能力,无法衡量其他形式的长上下文理解。 …

(1day)致远M3 log 敏感信息泄露漏洞(Session)复现

前言 系统学习web漏洞挖掘以及项目实战也有一段时间了,发现在漏洞挖掘过程中难免会碰到一些历史漏洞,来帮助自己或是提高自己挖洞和及时发现漏洞效率,于是开始创建这个专栏,对第一时间发现的1day以及历史漏洞进行复现,来让自己更加熟悉漏洞类型以及历史漏洞,方便自己在后续的项…

为什么会查询不到DNS信息?怎么排查?

DNS&#xff08;域名系统&#xff09;是将域名转换为相应 IP 地址的关键系统。查询 DNS 信息具有重要作用&#xff0c;通过查询 DNS 信息&#xff0c;我们可以知道域名对应的 IP 地址&#xff0c;这是最主要的信息&#xff0c;使设备能与目标服务器进行通信&#xff1b;其次是域…

微信小程序03: 获取不限制的小程序二维码

全文目录,一步到位 1.前言简介1.1 专栏传送门1.1.1 上文小总结1.1.2 上文传送门 2. 获取不限制二维码操作2.1 准备工作2.1.1 请先复制00篇的统一封装代码2.1.2 修改配置文件中的参数 2.2 具体代码使用与注释如下2.2.1 业务代码如下2.2.2 代码解释(一)[无需复制]2.2.3 创建Base6…

每日一题5:Pandas-修改列

一、每日一题 一家公司决定增加员工的薪水。 编写一个解决方案&#xff0c;将每个员工的薪水乘以2来 修改 salary 列。 返回结果格式如下示例所示。 解答&#xff1a; import pandas as pddef modifySalaryColumn(employees: pd.DataFrame) -> pd.DataFrame:employees.loc[…

C语言之数据结构之栈和队列的运用

目录 1. 用队列实现栈1.1 思路讲解1.2 代码实现 2. 用栈实现队列1.1 思路讲解1.2 代码实现 总结 •͈ᴗ•͈ 个人主页&#xff1a;御翮 •͈ᴗ•͈ 个人专栏&#xff1a;C语言数据结构 •͈ᴗ•͈ 欢迎大家关注和订阅!!! 1. 用队列实现栈 题目描述&#xff1a; 请你仅使用两个…
最新文章