Vue
单向绑定v-bind,简写 :
除了使用插值表达式{{}}进行数据渲染,也可以使用 v-bind指令,它的简写的形式就是一个冒号(:)
<body>
<div id="app">
<h1 v-bind:title="message">
test
</h1>
</div>
<script src="vue.min.js"></script>
<script>
new Vue({
el: '#app',
data: {
message: 'hello,vue!'
}
})
</script>
</body>
双向绑定v-model
当v-model绑定的值改变时,其他和他绑定的值一样的也跟着改变。
<body>
<div id="app">
<!-- v-bind:value只能进行单向的数据渲染 -->
<input type="text" v-bind:value="searchMap.keyWord">
<!-- v-model 可以进行双向的数据绑定 -->
<input type="text" v-model="searchMap.keyWord">
<p>您要查询的是:{{searchMap.keyWord}}</p>
</div>
<script src="vue.min.js"></script>
<script>
new Vue({
el: '#app',
data: {
searchMap:{
keyWord: '尚硅谷'
}
}
})
</script>
</body>
事件绑定v-on,简写 @
<body>
<div id="app">
<button v-on:click="search()">点我</button>
<!-- 简写 -->
<button @click="search()">点我</button>
</div>
<script src="vue.min.js"></script>
<script>
new Vue({
el: '#app',
data: {
},
methods: {
search() {
alert("hahaha")
}
}
})
</script>
</body>
加深
配置v-bind使用,当单机事件时,显示信息
<body>
<div id="app">
<button @click="search()">点我</button>
<p><a :href="result.site" target="_blank">{{result.title}}</a></p>
</div>
<script src="vue.min.js"></script>
<script>
new Vue({
el: '#app',
data: {
result: {}
},
methods: {
search() {
console.log("hahaha")
this.result = {
"title":"百度",
"site":"http://www.baidu.com"
}
}
}
})
</script>
</body>
v-if
<body>
<div id="app">
<input type="checkbox" v-model="ok">同意许可协议
<!-- v:if条件指令:还有v-else、v-else-if 切换开销大 -->
<h1 v-if="ok">Dragon</h1>
<h1 v-else>no</h1>
</div>
<script src="vue.min.js"></script>
<script>
new Vue({
el: '#app',
data: {
ok: false
}
})
</script>
</body>
v-for
<body>
<div id="app">
<table border="1">
<tr v-for="user in userList">
<td>{{user.id}}</td>
<td>{{user.name}}</td>
<td>{{user.age}}</td>
</tr>
</table>
</div>
<script src="vue.min.js"></script>
<script>
new Vue({
el: '#app',
data: {
userList: [
{id:1,name:'张3',age:12},
{id:2,name:'张4',age:13},
{id:3,name:'张5',age:14}
]
}
})
</script>
</body>
生命周期
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sN4MwaZK-1652586714636)(C:/Users/hasee/Pictures/javaWeb/Vue/生命周期.png)]
总结
概括就是:在页面渲染之前会执行created方法,页面数据渲染之后会执行mounted方法。
Vue路由
引入js
<script src="vue.min.js"></script>
<script src="vue-router.min.js"></script>
编写html
<div id="app">
<h1>Hello App!</h1>
<p>
<!-- 使用 router-link 组件来导航. -->
<!-- 通过传入 `to` 属性指定链接. -->
<!-- <router-link> 默认会被渲染成一个 `<a>` 标签 -->
<router-link to="/">首页</router-link>
<router-link to="/student">会员管理</router-link>
<router-link to="/teacher">讲师管理</router-link>
</p>
<!-- 路由出口 -->
<!-- 路由匹配到的组件将渲染在这里 -->
<router-view></router-view>
</div>
编写js
<script>
// 1. 定义(路由)组件。
// 可以从其他文件 import 进来
const Welcome = { template: '<div>欢迎</div>' }
const Student = { template: '<div>student list</div>' }
const Teacher = { template: '<div>teacher list</div>' }
// 2. 定义路由
// 每个路由应该映射一个组件。
const routes = [
{ path: '/', redirect: '/welcome' }, //设置默认指向的路径
{ path: '/welcome', component: Welcome },
{ path: '/student', component: Student },
{ path: '/teacher', component: Teacher }
]
// 3. 创建 router 实例,然后传 `routes` 配置
const router = new VueRouter({
routes // (缩写)相当于 routes: routes
})
// 4. 创建和挂载根实例。
// 从而让整个应用都有路由功能
const app = new Vue({
el: '#app',
router
})
// 现在,应用已经启动了!
</script>
axios
固定结构
<body>
<div id="app">
</div>
<script src="vue.min.js"></script>
<script src="axios.min.js"></script>
<script>
new Vue({
el: '#app',
//固定结构
data: {//data定义变量和初始值
},
created() {//页面渲染之前执行
//调用定义的方法
},
methods: {//定义具体方法
}
})
</script>
</body>
测试
<body>
<div id="app">
<ul v-for="user in userList">
<li>姓名:{{user.name}},年龄:{{user.age}}</li>
</ul>
</div>
<script src="vue.min.js"></script>
<script src="axios.min.js"></script>
<script>
new Vue({
el: '#app',
//固定结构
data: {//data定义变量和初始值
//定义初始值,接受json数据
userList:[]
},
created() {//页面渲染之前执行
//调用定义的方法
this.getUserList()
},
methods: {//定义具体方法
//查询所有的数据
getUserList() {
//使用axios发送ajax请求
axios.get("data.json")//请求路径
//请求成功执行then方法
.then(response => {
//通过response响应的数据返回给初始值
this.userList = response.data.data.items
})
//请求失败执行catch方法
.catch(error => {
})
}
}
})
</script>
</body>
Node.js
简单理解就是javaScript的运行环境。
NPM
相当于maven中pom依赖
配置淘宝镜像
#经过下面的配置,以后所有的 npm install 都会经过淘宝的镜像地址下载
npm config set registry https://registry.npm.taobao.org
#查看npm配置信息
npm config list
npm install命令的使用
#使用 npm install 安装依赖包的最新版,
#模块安装的位置:项目目录\node_modules
#安装会自动在项目目录下添加 package-lock.json文件,这个文件帮助锁定安装包的版本
#同时package.json 文件中,依赖包会被添加到dependencies节点下,类似maven中的 <dependencies>
npm install jquery
#npm管理的项目在备份和传输的时候一般不携带node_modules文件夹
npm install #根据package.json中的配置下载依赖,初始化项目
#如果安装时想指定特定的版本
npm install jquery@2.1.x
#devDependencies节点:开发时的依赖包,项目打包到生产环境的时候不包含的依赖
#使用 -D参数将依赖添加到devDependencies节点
npm install --save-dev eslint
#或
npm install -D eslint
#全局安装
#Node.js全局安装的npm包和工具的位置:用户目录\AppData\Roaming\npm\node_modules
#一些命令行工具常使用全局安装的方式
npm install -g webpack
其它命令
#更新包(更新到最新版本)
npm update 包名
#全局更新
npm update -g 包名
#卸载包
npm uninstall 包名
#全局卸载
npm uninstall -g 包名
Babel
简介
Babel是一个广泛使用的转码器,可以将ES6代码转为ES5代码,从而在现有环境执行执行。
这意味着,你可以现在就用 ES6 编写程序,而不用担心现有环境是否支持。
安装
安装命令行转码工具
Babel提供babel-cli工具,用于命令行转码。它的安装命令如下:
npm install --global babel-cli
#查看是否安装成功
babel --version
Babel的使用
初始化项目
npm init -y
创建文件
src/example.js
下面是一段ES6代码:
// 转码前
// 定义数据
let input = [1, 2, 3]
// 将数组的每个元素 +1
input = input.map(item => item + 1)
console.log(input)
配置.babelrc
Babel的配置文件是.babelrc,存放在项目的根目录下,该文件用来设置转码规则和插件,基本格式如下。
{
"presets": [],
"plugins": []
}
presets字段设定转码规则,将es2015规则加入 .babelrc:
{
"presets": ["es2015"],
"plugins": []
}
安装转码器
在项目中安装
npm install --save-dev babel-preset-es2015
转码
# 转码结果写入一个文件
mkdir dist1
# --out-file 或 -o 参数指定输出文件
babel src/example.js --out-file dist1/compiled.js
# 或者
babel src/example.js -o dist1/compiled.js
# 整个目录转码
mkdir dist2
# --out-dir 或 -d 参数指定输出目录
babel src --out-dir dist2
# 或者
babel src -d dist2
模块化
es5模块化
定义一个模块01.js
//定义两个方法
const sum = function(a,b){
return parseInt(a) + parseInt(b)
}
const subtract = function(a,b){
return parseInt(a) - parseInt(b)
}
//导出模块
module.exports = {
sum,
subtract
}
在02.js文件中引用01.js模块
//访问01中的方法
const a = require('./01.js')
console.log(a.sum(1,2))
console.log(a.subtract(2,1))
测试
node 02.js
es5总结
module.exports导出模块
require(‘./路径’)引用某个导出的模块
es6模块化
定义一个模块01.js
//定义方法
export function getList(){
console.log("getList......")
}
export function save(){
console.log("save.....")
}
在02.js文件中引用01.js模块
//调用01中的方法
import {getList,save} from './01.js'
//调用方法
console.log(getList())
console.log(save)
注意:这时的程序无法运行的,因为ES6的模块化无法在Node.js中执行,需要用Babel编辑成ES5后再执行。
转换的时候注意要在目录文件夹外转换。
测试转换后的文件
node 02.js
简化写法
导出模块
export default {
getList() {
console.log('获取数据列表2')
},
save() {
console.log('保存数据2')
}
}
导入模块
import a from "./userApi2.js"
a.getList()
a.save()
Webpack
Webpack 是一个前端资源加载/打包工具。它将根据模块的依赖关系进行静态分析,然后将这些模块按照指定的规则生成对应的静态资源。
从图中我们可以看出,Webpack 可以将多种静态资源 js、css、less 转换成一个静态文件,减少了页面的请求
简单说就是把多个js文件打包成一个js文件。
1、全局安装
npm install -g webpack webpack-cli
2、创建js文件用于打包操作
-
src下创建common.js
exports.info = function (str) { document.write(str); }
-
src下创建utils.js
exports.add = function (a, b) { return a + b; }
-
src下创建main.js,用于引入其他两个文件,打包入口
const common = require('./common'); const utils = require('./util'); common.info('Hello world!' + utils.add(100, 200));
3、创建webpack配置文件,配置打包信息
webpack.config.js
const path = require("path"); //Node.js内置模块
module.exports = {
entry: './src/main.js', //配置入口文件
output: {
path: path.resolve(__dirname, './demo'), //输出路径,__dirname:当前文件所在路径
filename: 'bundle.js' //输出文件
}
}
4、使用命令进行打包
webpack #有黄色警告,这个打包只有一行,不方便查看
webpack --mode=development #没有警告,这个打包后回事多行文件,推荐 ***********
#执行后查看bundle.js 里面包含了上面两个js文件的内容并惊醒了代码压缩
5、打包后测试
新建test.html
<script src="demo/bundle.js"></script>
css打包
在src下新建style.css文件
body{
background-color: red;
}
引入到main.js中
require("./style.css")
安装style-loader和 css-loader
Webpack 本身只能处理 JavaScript 模块,如果要处理其他类型的文件,就需要使用 loader 进行转换。
Loader 可以理解为是模块和资源的转换器。
首先我们需要安装相关Loader插件,css-loader 是将 css 装载到 javascript;style-loader 是让 javascript 认识css
npm install --save-dev style-loader css-loader
修改配置文件
在ouput后边添加
//...,
output:{},
module: {
rules: [
{
test: /\.css$/, //打包规则应用到以css结尾的文件上
use: ['style-loader', 'css-loader']
}
]
}
解决前后端跨域
1、在controller中加入注解解决
@CrossOrigin //解决跨域
2、使用网关解决
ById得到这一行的id
scope.row.id
路由跳转界面
this.$router.push({ path: '/teacher/list' })
阿里云oss
导入依赖
<dependencies>
<!-- 阿里云oss依赖 -->
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
</dependency>
<!-- 日期工具栏依赖 -->
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
</dependency>
</dependencies>
配置文件
#服务名
spring.application.name=service-oss
#环境设置:dev、test、prod
spring.profiles.active=dev
#阿里云 OSS
#不同的服务器,地址不同
aliyun.oss.file.endpoint=your endpoint
aliyun.oss.file.keyid=your accessKeyId
aliyun.oss.file.keysecret=your accessKeySecret
#bucket可以在控制台创建,也可以使用java代码创建
aliyun.oss.file.bucketname=edu-rb
测试,启动报错解决
原因是启动类自动去加载数据库了,解决办法
1、在配置文件中加上数据库的配置,虽然能解决,但是我们不许要数据库,这就有点多余了。
2、在启动类的注解**@SpringBootAppliation**参数中加上(exclude = DataSourceAutoConfiguration),表示不加载数据库。【推荐】
实现文件上传
从配置文件读取常量
创建常量读取工具类:ConstantPropertiesUtil.java
//当项目启动,spring加载之后,自动执行InitializingBean里的方法
@Component
public class ConstantPropertiesUtils implements InitializingBean {
@Value("${aliyun.oss.file.endpoint}")
private String endPoint;
@Value("${aliyun.oss.file.keyid}")
private String keyId;
@Value("${aliyun.oss.file.keysecret}")
private String keySecret;
@Value("${aliyun.oss.file.bucketname}")
private String bucketName;
//定义公开静态常量
public static String END_POINT;
public static String ACCESS_KEY_ID;
public static String ACCESS_KEY_SECRET;
public static String BUCKET_NAME;
//当spring执行时,上边的值初始化完成后,这个方法会自动执行
@Override
public void afterPropertiesSet() throws Exception {
END_POINT = endPoint;
ACCESS_KEY_ID = keyId;
ACCESS_KEY_SECRET = keySecret;
BUCKET_NAME = bucketName;
}
}
创建service层,创建controller,注入service
@Controller
@RestController("eduoss/fileoss")
public class OssController {
@Autowired
private OssService ossService;
//上传头像
@PostMapping
public ResultCode updateOssFile(MultipartFile file) {
//获取上传文件,MultipartFile
//返回上传到oss路径
String url = ossService.updateFileAvatar;
return ResultCode.ok().data("url",url);
}
}
在service实现上传文件
package com.dragon.serviceedu.service.impl;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.dragon.serviceedu.service.OssService;
import com.dragon.serviceedu.utils.ConstantPropertiesUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.InputStream;
@Service
public class OssServiceImpl implements OssService {
@Override
public String updateFileAvatar(MultipartFile file) {
//获取ConstantPropertiesUtils中的值
String endpoint = ConstantPropertiesUtils.END_POINT;
String accessKeyId = ConstantPropertiesUtils.ACCESS_KEY_ID;
String accessKeySecret = ConstantPropertiesUtils.ACCESS_KEY_SECRET;
String bucketName = ConstantPropertiesUtils.BUCKET_NAME;
try {
// 创建OSSClient实例。
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
//获取上传文件输入流
InputStream inputStream = file.getInputStream();
//获取文件名称
String filename = file.getOriginalFilename();
//在文件名字中添加uuid,不然上传文件覆盖之前文件
String uuid = UUID.randomUUID().toString().replaceAll("-", "");
filename = uuid + filename;
//把文件按照日期分类,创建文件目录 2020/12/15/01.jpg
String datePath = new DateTime().toString("yyyy/MM/dd");
//拼接
filename = datePath + "/" + filename;
//调用oos方法实现上穿
/*
* 参数一:bucketName
* 参数二:上传到oss文件路径或名称
* 参数三:上传文件输入流
* */
ossClient.putObject(bucketName, filename, inputStream);
// 关闭OSSClient。
ossClient.shutdown();
//把上传文件的路径返回
//需要手动拼接oss文件路径
//https://edu-rb.oss-cn-beijing.aliyuncs.com/2.jpg
String url = "https://"+bucketName+"."+endpoint+"/"+filename;
return url;
} catch (Exception e) {
}
return null;
}
}
Controller
package com.dragon.serviceedu.controller;
import com.dragon.commonutils.ResultCode;
import com.dragon.serviceedu.service.OssService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
@Api(description="阿里云文件管理")
@Controller
@RestController("eduoss/fileoss")
@CrossOrigin
public class OssController {
@Autowired
private OssService ossService;
//上传头像
@ApiOperation(value = "文件上传")
@PostMapping("upload")
public ResultCode updateOssFile(MultipartFile file) {
//获取上传文件,MultipartFile
//返回上传到oss路径
String url = ossService.updateFileAvatar(file);
return ResultCode.ok().data("url",url);
}
}
Nginx
tasklist /fi "imagename eq nginx.exe" #查看启动了几个nginx
taskkill /fi "imagename eq nginx.exe" /f #杀死所有nginx
EasyExecl
EasyExcel特点
- Java领域解析、生成Excel比较有名的框架有Apache poi、jxl等。但他们都存在一个严重的问题就是非常的耗内存。如果你的系统并发量不大的话可能还行,但是一旦并发上来后一定会OOM或者JVM频繁的full gc。
- EasyExcel是阿里巴巴开源的一个excel处理框架,以使用简单、节省内存著称。EasyExcel能大大减少占用内存的主要原因是在解析Excel时没有将文件数据一次性全部加载到内存中,而是从磁盘上一行行读取数据,逐个解析。
- EasyExcel采用一行一行的解析模式,并将一行的解析结果以观察者的模式通知处理(AnalysisEventListener)。
实现EasyExcel对Excel写操作
1、导入依赖
<!-- https://mvnrepository.com/artifact/com.alibaba/easyexcel -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.1.1</version>
</dependency>
还要对应一个poi依赖,这个在父类中导入过了
<!--xls-->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>${poi.version}</version>
</dependency>
<!--xlsx-->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>${poi.version}</version>
</dependency>
<poi.version>3.17</poi.version>
2、创建实体类
设置表头和添加的数据字段
@Data
public class DemoData {
//设置Excel表头名称
@ExcelProperty("学生编号")
private Integer sno;
@ExcelProperty("学生姓名")
private String sname;
}
3、编写测试类,实现写出
package com.dragon.serviceedu.excel;
import com.alibaba.excel.EasyExcel;
import java.util.ArrayList;
import java.util.List;
public class TestEasyExcel {
public static void main(String[] args) {
//设置写入文件的地址,和文件的名称
String fileName = "E:/write.xlsx";
//EasyExcel,实现写的方法
EasyExcel.write(fileName, DemoData.class).sheet("学生列表").doWrite(getData());
}
private static List<DemoData> getData() {
List<DemoData> list = new ArrayList<>();
for (int i = 1; i <= 10; i++) {
DemoData data = new DemoData();
data.setSno(i);
data.setSname("张" + i);
list.add(data);
}
return list;
}
}
实现Excel读操作
1、创建实体类
package com.dragon.serviceedu.excel;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
@Data
public class DemoData {
//设置Excel表头名称
@ExcelProperty(value = "学生编号",index = 0)
private Integer sno;
@ExcelProperty(value = "学生姓名",index = 1)
private String sname;
}
2、创建一个监听器
package com.dragon.serviceedu.listener;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.dragon.serviceedu.excel.DemoData;
import java.util.Map;
public class ExcelListener extends AnalysisEventListener<DemoData> {
//一行一行去读取excle内容
@Override
public void invoke(DemoData demoData, AnalysisContext analysisContext) {
System.out.println("***"+demoData);
}
//读取excel表头信息
@Override
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
System.out.println("表头信息:"+headMap);
}
//读取完成后执行
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
}
}
3、实现读的功能
//2、实现读的操作
String fileName = "E:/write.xlsx";
EasyExcel.read(fileName, DemoData.class, new ExcelListener()).sheet().doRead();
实现Excel读入数据库
1、创建对应实体类
@Data
public class SubjectData {
//设置列对应的属性
@ExcelProperty(index = 0)
private String oneSubjectName;
//设置列对应的属性
@ExcelProperty(index = 1)
private String twoSubjectName;
}
2、controller
@Api(description = "Excel上传")
@RestController
@RequestMapping("/serviceedu/subject")
@CrossOrigin
public class EduSubjectController {
@Autowired
private EduSubjectService subjectService;
//添加课程分类
//获取上传文件,把文件内容读取出来
@PostMapping("addSubject")
public ResultCode addSubject(MultipartFile file) {
//上传过来的Excel文件
subjectService.saveSubject(file,subjectService);
return ResultCode.ok();
}
}
3、service
public class EduSubjectServiceImpl extends ServiceImpl<EduSubjectMapper, EduSubject> implements EduSubjectService {
//添加课程分类
@Override
public void saveSubject(MultipartFile file , EduSubjectService subjectService) {
try {
//文件输入流
InputStream in = file.getInputStream();
//调用方法进行读取
EasyExcel.read(in, SubjectData.class, new SubjectExcelListener(subjectService)).sheet().doRead();
} catch (Exception e) {
e.printStackTrace();
}
}
}
4、创建监听器Listener
public class SubjectExcelListener extends AnalysisEventListener<SubjectData> {
//手动注入SubjectService
public EduSubjectService subjectService;
public SubjectExcelListener() {}
public SubjectExcelListener(EduSubjectService subjectService) {
this.subjectService = subjectService;
}
//一行一行去读取excle内容
@Override
public void invoke(SubjectData subjectData, AnalysisContext analysisContext) {
if (subjectData == null) {
throw new MyException(2001, "文件为空");
}
//一行一行读取,每次读取两个值,第一个值为一级分类,第二个值为二级分类
//判断一级分类是否重复
EduSubject existOneSubject = this.existOneSubject(subjectService, subjectData.getOneSubjectName());
if (existOneSubject == null) { //没有相同一级分类,进行添加
existOneSubject = new EduSubject();
existOneSubject.setParentId("0");
existOneSubject.setTitle(subjectData.getOneSubjectName()); //一级分类名称
subjectService.save(existOneSubject);
}
//获取一级分类id值
String pid = existOneSubject.getId();
//添加二级分类
//判断二级分类
EduSubject existTwoSubject = this.existTwoSubject(subjectService, subjectData.getTwoSubjectName(), pid);
if (existTwoSubject == null) {
existTwoSubject = new EduSubject();
existTwoSubject.setParentId(pid);
existTwoSubject.setTitle(subjectData.getTwoSubjectName()); //一级分类名称
subjectService.save(existTwoSubject);
}
}
//读取excel表头信息
@Override
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
System.out.println("表头信息:"+headMap);
}
//判断一级分类不能重复添加
private EduSubject existOneSubject(EduSubjectService subjectService,String name) {
QueryWrapper<EduSubject> wrapper = new QueryWrapper<>();
wrapper.eq("title", name);
wrapper.eq("parent_id", "0");
EduSubject oneSubject = subjectService.getOne(wrapper);
return oneSubject;
}
//判断一级分类不能重复添加
private EduSubject existTwoSubject(EduSubjectService subjectService,String name,String pid) {
QueryWrapper<EduSubject> wrapper = new QueryWrapper<>();
wrapper.eq("title", name);
wrapper.eq("parent_id", pid);
EduSubject TwoSubject = subjectService.getOne(wrapper);
return TwoSubject;
}
}
注意:这里应为Listener没有被spring接管,所以无法注入eduSubjectService,因此必须我们自己手动注入eduSubjectService。之后在保存数据库时,判断此标题是否重复。
阿里云视频点播
1、初始化
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.exceptions.ClientException;
public class InitObject {
public static DefaultAcsClient initVodClient(String accessKeyId, String accessKeySecret) throws ClientExcW3W35eption {
String regionId = "cn-shanghai"; // 点播服务接入区域
DefaultProfile profile = DefaultProfile.getProfile(regionId, accessKeyId, accessKeySecret);
DefaultAcsClient client = new DefaultAcsClient(profile);
return client;
}
}
2、获取视频地址
//获取视频地址
public static void getPlayUrl() throws Exception {
//1、根据视频id获取视频播放地址
//创建初始化对象
DefaultAcsClient client = InitObject.initVodClient("LTAI4G73PF2donPnup9AZ47q", "jVSBNUnjRKVaZRvr7mxumSn4I2HGin");
//创建获取视频地址的request
GetPlayInfoRequest request = new GetPlayInfoRequest();
//向request中设置视频id
request.setVideoId("5ec5b46c3551444cb6cccabcd2cff640");
//调用初始化对象里面的方法,传递request,获取数据
GetPlayInfoResponse response = client.getAcsResponse(request);
List<GetPlayInfoResponse.PlayInfo> playInfoList = response.getPlayInfoList();
//播放地址
for (GetPlayInfoResponse.PlayInfo playInfo : playInfoList) {
System.out.print("PlayInfo.PlayURL = " + playInfo.getPlayURL() + "\n");
}
}
3、获取视频凭证
public static void main(String[] args) throws Exception {
//根据视频id获取视频凭证
//创建初始化对象
DefaultAcsClient client = InitObject.initVodClient("LTAI4G73PF2donPnup9AZ47q", "jVSBNUnjRKVaZRvr7mxumSn4I2HGin");
//创建获取视频地址的request
GetVideoPlayAuthRequest request = new GetVideoPlayAuthRequest();
//设置视频id
request.setVideoId("054182ab67a847f5b30da3726914a616");
GetVideoPlayAuthResponse response = client.getAcsResponse(request);
System.out.println("视频凭证:" + response.getPlayAuth());
}
本地视频上传阿里云
package com.dragon.serviceedu.service.impl;
import com.aliyun.vod.upload.impl.UploadVideoImpl;
import com.aliyun.vod.upload.req.UploadStreamRequest;
import com.aliyun.vod.upload.resp.UploadStreamResponse;
import com.dragon.serviceedu.service.VodService;
import com.dragon.serviceedu.utils.ConstantVodUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.io.InputStream;
@Service
public class VodSerivceImpl implements VodService {
//上传到阿里云视频
@Override
public String uploadVideo(MultipartFile file) {
try {
//title上传之后显示的名称
//subString截取 . 之前的文件名
String title = file.getOriginalFilename().substring(0, file.getOriginalFilename().lastIndexOf("."));
//inputStream上传文件流
InputStream inputStream = file.getInputStream();
UploadStreamRequest request = new UploadStreamRequest(ConstantVodUtils.ACCESS_KEY_ID, ConstantVodUtils.ACCESS_KEY_SECRET,
title, file.getOriginalFilename(), inputStream);
UploadVideoImpl uploader = new UploadVideoImpl();
UploadStreamResponse response = uploader.uploadStream(request);
//如果设置回调URL无效,不影响视频上传,可以返回VideoId同时会返回错误码。
// 其他情况上传失败时,VideoId为空,此时需要根据返回错误码分析具体错误原因
String videoId = null;
if (response.isSuccess()) {
videoId = response.getVideoId();
} else {
videoId = response.getVideoId();
}
return videoId;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
}
单点登录SSO(single sign on)
实现多模块共享同意用户。
实现的三种常见方式:
- session广播机制实现【不推荐使用,如果模块多的话,复制的量太大】
- 使用cookie + redis实现
- 使用token实现**(自包含令牌)**
- token是什么?按照一定规则生成字符串,字符串可以包含用户信息。
JWT
- token是按照一定规则生成字符串,包含用户信息
- 规则是怎么样的,不一定
- 一般采用通用的规则,官方规则jwt
JWT就是给我们规定好了规则,使用JWT规则可以生成字符串,包含用户信息
OAuth2
OAuth2是什么?
OAuth2是针对特定问题的一种解决方案。主要可以解决两个问题:
-
开放系统间授权
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Sk8vzObE-1652586714639)(C:/Users/hasee/AppData/Roaming/Typora/typora-user-images/image-20201231221549601.png)]
-
分布式访问问题
微信扫码登录
步骤:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-G68UENiE-1652586714641)(C:/Users/hasee/AppData/Roaming/Typora/typora-user-images/image-20210102200802938.png)]
官方图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZbSi8CzU-1652586714642)(C:/Users/hasee/AppData/Roaming/Typora/typora-user-images/image-20210102200920276.png)]
定时任务 Scheduled
在固定时间执行代码。
-
启动类添加注解 @EnableScheduling //开启定时任务
-
创建定时任务类,使用cron表达式设置执行代码的时间,在方法上添加注解**@Scheduled**和cron表达式
@Component public class ScheduleTask { // 每个5秒执行一次 @Scheduled(cron = "0/5 * * * * ?") public void task() { System.out.println("ScheduleTask执行了............."); } }
-
在线生成cron表达式
http://cron.qqe2.com/
ECharts
简介
ECharts是百度的一个项目,后来百度把Echart捐给apache,用于图表展示,提供了常规的折线图、柱状图、散点图、饼图、K线图,用于统计的盒形图,用于地理数据可视化的地图、热力图、线图,用于关系数据可视化的关系图、treemap、旭日图,多维数据可视化的平行坐标,还有用于 BI 的漏斗图,仪表盘,并且支持图与图之间的混搭。
官方网站:https://echarts.apache.org
install
npm install --save echarts@4.1.0
canal数据同步工具
1、应用场景
在前面的统计分析功能中,我们采取了服务调用获取统计数据,这样耦合度高,效率相对较低,目前我采取另一种实现方式,通过实时同步数据库表的方式实现,例如我们要统计每天注册与登录人数,我们只需把会员表同步到统计库中,实现本地统计就可以了,这样效率更高,耦合度更低,Canal就是一个很好的数据库同步工具。canal是阿里巴巴旗下的一款开源项目,纯Java开发。基于数据库增量日志解析,提供增量数据订阅&消费,目前主要支持了MySQL。
2、Canal环境搭建
canal的原理是基于mysql binlog技术,所以这里一定需要开启mysql的binlog写入功能
开启mysql服务: service mysql start
(1)检查binlog功能是否有开启
mysql> show variables like 'log_bin';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| log_bin | OFF |
+---------------+-------+
1 row in set (0.00 sec)
(2)如果显示状态为OFF表示该功能未开启,开启binlog功能
1,修改 mysql 的配置文件 my.cnf
vi /etc/my.cnf
追加内容:
log-bin=mysql-bin #binlog文件名
binlog_format=ROW #选择row模式
server_id=1 #mysql实例id,不能和canal的slaveId重复
2,重启 mysql:
service mysql restart
3,登录 mysql 客户端,查看 log_bin 变量
mysql> show variables like 'log_bin';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| log_bin | ON|
+---------------+-------+
1 row in set (0.00 sec)
————————————————
如果显示状态为ON表示该功能已开启
(3)在mysql里面添加以下的相关用户和权限
CREATE USER 'canal'@'%' IDENTIFIED BY 'canal';
GRANT SHOW VIEW, SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'canal'@'%';
FLUSH PRIVILEGES;
3、下载安装Canal服务
下载地址:
https://github.com/alibaba/canal/releases
(1)下载之后,放到目录中,解压文件
cd /usr/local/canal
canal.deployer-1.1.4.tar.gz
tar zxvf canal.deployer-1.1.4.tar.gz
(2)修改配置文件
vi conf/example/instance.properties
#需要改成自己的数据库信息
canal.instance.master.address=192.168.44.132:3306
#需要改成自己的数据库用户名与密码
canal.instance.dbUsername=canal
canal.instance.dbPassword=canal
#需要改成同步的数据库表规则,例如只是同步一下表
#canal.instance.filter.regex=.*\\..*
canal.instance.filter.regex=guli_ucenter.ucenter_member
注:
mysql 数据解析关注的表,Perl正则表达式.
多个正则之间以逗号(,)分隔,转义符需要双斜杠(\\)
常见例子:
1. 所有表:.* or .*\\..*
2. canal schema下所有表: canal\\..*
3. canal下的以canal打头的表:canal\\.canal.*
4. canal schema下的一张表:canal.test1
5. 多个规则组合使用:canal\\..*,mysql.test1,mysql.test2 (逗号分隔)
注意:此过滤条件只针对row模式的数据有效(ps. mixed/statement因为不解析sql,所以无法准确提取tableName进行过滤)
(3)进入bin目录下启动
sh bin/startup.sh