记录一次技术整合,包含微服务、mybatis-plus、vue、redis等

2 篇文章 0 订阅
1 篇文章 0 订阅

一、Mybatis-plus

01-Mybatis-plus的准备

官网:https://mp.baomidou.com/guide/#%E7%89%B9%E6%80%A7

用mybatis-plus我们在自动生成代码的基础上操作

导包

 <!--mybatis-plus-->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.0.5</version>
    </dependency>

    <!--mysql-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>

    <!--lombok用来简化实体类-->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>

配置

#数据库连接配置
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus?serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=123456
#mybatis日志,可以在控制台输出日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
#扫描xml配置
mybatis-plus.mapper-locations= classpath:mapper/*.xml

#全局设置主键生成策略
mybatis-plus.global-config.db-config.id-type=auto
#执行逻辑删除
mybatis-plus.global-config.db-config.logic-delete-value=1
mybatis-plus.global-config.db-config.logic-not-delete-value=0

#环境设置:dev、test、prod
spring.profiles.active=dev

使用的时候需要在启动类添加@MapperScan 注解,扫描 Mapper 文件夹

@SpringBootApplication
@MapperScan("cn.cbbgs.cloud.dao")
public class MybatisPlusApplication {
	......
}

02-自动添加值

主键自增策略

  • 要想主键自增需要配置如下主键策略

    • 需要在创建数据表的时候设置主键自增
    • 实体字段中配置 @TableId(type = IdType.ASSIGN_ID)
  /*自动生成主键,在实体类上添加*/
 @TableId(type = IdType.ASSIGN_ID)
 private String studentId;
#在配置文件上添加
#全局设置主键生成策略
mybatis-plus.global-config.db-config.id-type=auto

需要注意的是自动生成主键的Id都比较长,所以我们需要注意数据库中id属性的长度。

时间自动填充策略

  • 我们让数据库中的时间自动生成当前时间。第一步就是需要在数据库的时间字段将字段的类型设为timestamp,然后将字段的默认值设置为 CURRENT_TIMESTAMP

    image-20210204162905543

需要导入一个依赖

 <!--自动生成时间-->
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-commons</artifactId>
            <version>2.4.3</version>
        </dependency>

最后就是在实体类的字段上添加注解

//设置时间格式和自动生成时间
@JsonFormat(shape= JsonFormat.Shape.STRING,pattern="yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
    @CreatedDate
    private Date createTime;

03-Mybatis-plus的增删改查

//自动生成代码之后,我们对dao层进行操作,对service层也可以
//注入dao层
    @Resource
    private StudentDao studentDao;
    //查询所有,需要传入一个querywapper,查询全部不需要条件,传入null
    @RequestMapping("/findbyall")
    public String findByAll(){
        //传入一个条件,querywapper,不用传入条件就是null
       List <Student> student = studentDao.selectList(null);
        return "---"+student;
    }

    //插入
    @RequestMapping("/insert")
    public String insert( String studentName){
        Student student = new Student();
        student.setStudentName(studentName);
        int insert = studentDao.insert(student);
        return "---"+insert;
    }

    //根据ID更新
    @RequestMapping("/update/{name}/{id}")
    public String update(@PathVariable String name,@PathVariable String id){
        Student student = new Student();
        student.setStudentName(name);
        student.setStudentId(id);
        int insert = studentDao.updateById(student);
        return "---"+insert;
    }

    /*查询学生*/
    @RequestMapping("/selectbyid/{id}")
    public String selectbyid(@PathVariable String id, Model model){
        Student student = studentDao.selectById(id);

        model.addAttribute("student",student);
        return "html/list";
    }

    //输入多个id批量查询集合
    @RequestMapping("/selectbyidlist")
    public String selectbyidlist(){

        List<Student> list = studentDao.selectBatchIds(Arrays.asList(1,2,3));
        return "---"+list;
    }

    /*map来封装*/
    @RequestMapping("/selectbyidmap")
    public String selectbyidmap(Model model){

        HashMap map = new HashMap();
        map.put("student_Name","2333");
        //map.put("studentid","1");
        List<Student> list = studentDao.selectByMap(map);
        model.addAttribute("list",list);
        return "html/list";
    }

    /*删除*/

    @RequestMapping("/delectbyid/{id}")
    public String delectbyid(@PathVariable String id){

        int deleteById = studentDao.deleteById(id);
        return "---"+deleteById;
    }



逻辑删除

  • 物理删除:真实删除,将对应数据从数据库中删除,之后查询不到此条被删除数据
  • 逻辑删除:假删除,将对应数据中代表是否被删除字段状态修改为“被删除状态”,之后在数据库中仍旧能看到此条数据记录

需要在数据库添加一个字段 ‘deleted’,用来做成标志

然后在实体类加上注解

@TableLogic
private Integer deleted;

在配置类上添加

mybatis-plus.global-config.db-config.logic-delete-value=1
mybatis-plus.global-config.db-config.logic-not-delete-value=0

或者是给spring配置类上注入一个

@Override
public void insertFill(MetaObject metaObject) {
    ......
    this.setFieldValByName("deleted", 0, metaObject);//这个deleted和实体类的字段一样
}
  • 测试后发现,数据并没有被删除,deleted字段的值由0变成了1
  • 测试后分析打印的sql语句,是一条update
  • **注意:**被删除数据的deleted 字段的值必须是 0,才能被选取出来执行逻辑删除的操作
  • MyBatis Plus中查询操作也会自动添加逻辑删除字段的判断

04-wapper条件构造器

 @RequestMapping("/selectone/{id}")
    public String selectone(@PathVariable String id){
        /*
        *
        * where   like  eq  等于
        *
        * */
        
        QueryWrapper<Student> wrapper = new QueryWrapper();
      //  wrapper.eq("studentname","2333");
        wrapper.like("studentname",'2');
        int deleteById = studentDao.selectCount(wrapper);
        return "---"+deleteById;
    }

参考官网,不一一列举

05-分页

MyBatis Plus自带分页插件,只要简单的配置即可实现分页功能

自动注入一个配置类

@Bean
public PaginationInterceptor paginationInterceptor() {
    return new PaginationInterceptor();
}

在配置类里面使用

测试一下

@Test
public void testSelectPage() {

    Page<User> page = new Page<>(1,5);
    //这里需要传入一个wapper条件,没有就填null
    userMapper.selectPage(page, null);

    page.getRecords();//得到当前对象
    System.out.println(page.getCurrent());
    System.out.println(page.getPages());
    System.out.println(page.getSize());
    System.out.println(page.getTotal());
    System.out.println(page.hasNext());
    System.out.println(page.hasPrevious());
}

如果是分页加上xml怎么做:

在dao层里面 :

//这里传入一个  Page<OrderInfo> page  类型就是Page类型,泛型指向的是你返回的数据的javabean,不一定是对应的数据库的实体类

List<OrderInfo> findOrderBybusId(Page<OrderInfo> page, @Param("busId") String busId, @Param("current") Integer current1, @Param("size") Integer size1);


//mapper.xml 等同于编写一个普通查询,mybatis-plus 自动替你分页,重点普通的查询,没有什么骚操作,会自动分页,因为我们dao层传入的是一个Page类
//在controller怎么写


在controller怎么写:

/*
     *  Page 分页对象,xml中可以从里面进行取值,传递参数 Page 即自动分页,
     * 必须放在第一位(你可以继承Page实现自己的分页对象)
     * */


    /*查询商家的订单*/
   @GetMapping("findorderbybusid/{busId}/{current1}/{size1}")
    public R findOrderBybusId(@PathVariable String busId,@PathVariable Integer current1,@PathVariable Integer size1){
      Page<OrderInfo> page = new Page<>(current1,size1);
       List<OrderInfo> ordersList = ordersService.findOrderBybusId(page, busId,current1,size1);
       Map<String,Object> ordersMap = new HashMap<>();
       ordersMap.put("ordersList",ordersList);
       ordersMap.put("current",page.getCurrent());
       ordersMap.put("size",page.getSize());
       ordersMap.put("total",page.getTotal());
       return R.ok().data("ordersMap",ordersMap);
   }

//在这里,只需要new 一个Page对象  Page<OrderInfo> page = new Page<>(current1,size1);
//传入两个参数,一个当前页码,一个长度。然后将这个page作为参数传递给service,然后service传递给dao。

//service层


 @Resource private OrdersDao ordersDao;
    @Override
    public List<OrderInfo> findOrderBybusId(Page<OrderInfo> page,String busId, Integer current1, Integer size1) {
        return ordersDao.findOrderBybusId(page, busId,current1,size1);
    }

//然后就是最开始的dao层,用Page对象来接受service传递来的page参数,然后xml自动进行分页,需要注意的是page必须作为第一个参数



二、Swagger

了解一下跨域,浏览器从一个域名的网页去请求另一个域名的资源时,域名、端口、协议任一不同,都是跨域 。前后端分离开发中,需要考虑ajax跨域的问题。

这里我们可以从服务端解决这个问题

在Controller类上添加注解

@CrossOrigin //跨域

我们可以单独创建一个微服务来实现Swagger

依赖

  <!--swagger-->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <scope>provided </scope>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <scope>provided </scope>
        </dependency>

创建Swagger配置类,这个可以原样复制

@Configuration
@EnableSwagger2
public class SwaggerConfig {

    @Bean
    public Docket webApiConfig(){

        return new Docket(DocumentationType.SWAGGER_2)
                .groupName("webApi")
                .apiInfo(webApiInfo())
                .select()
                .paths(Predicates.not(PathSelectors.regex("/admin/.*")))
                .paths(Predicates.not(PathSelectors.regex("/error.*")))
                .build();

    }
    
    private ApiInfo webApiInfo(){

        return new ApiInfoBuilder()
                .title("网站-xx中心API文档")
                .description("本文档描述了xx中心微服务接口定义")
                .version("1.0")
                .contact(new Contact("Helen", "http://cbbgs.cn", "55317332@qq.com"))
                .build();
    }
}

接下来我们在需要使用的微服务引入这个模块

<!--这个依赖是自己创建swagger微服务的坐标,填自己的坐标-->
<dependency>
    <groupId>cn.cbbgs</groupId>
    <artifactId>service-Swagger</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</dependency>

然后在需要使用的微服务模块的启动类配置一个注解

img

然后访问:http://localhost:8080/swagger-ui.html

三、统一返回结果对象

项目中我们会将响应封装成json返回,一般我们会将所有接口的数据格式统一, 使前端(iOS Android, Web)对数据的操作更一致、轻松。

一般情况下,统一返回数据格式没有固定的格式,只要能描述清楚返回的数据状态以及要返回的具体数据就可以。但是一般会包含状态码、返回消息、数据这几部分内容

例如,我们的系统要求返回的基本数据格式如下:

{
  "success": true,
  "code": 20000,
  "message": "成功",
  "data": {
    "items": [
      {
        "id": "1",
        "name": "刘德华",
        "intro": "毕业于师范大学数学系,热爱教育事业,执教数学思维6年有余"
      }
    ]
  }
}

创建一个返回码的实体类

public interface ResultCode {

    public static Integer SUCCESS = 20000;

    public static Integer ERROR = 20001;
}

创建一个结果类

@Data
public class R {
    @ApiModelProperty(value = "是否成功")
    private Boolean success;

    @ApiModelProperty(value = "返回码")
    private Integer code;

    @ApiModelProperty(value = "返回消息")
    private String message;

    @ApiModelProperty(value = "返回数据")
    private Map<String, Object> data = new HashMap<String, Object>();

    private R(){}

    public static R ok(){
        R r = new R();
        r.setSuccess(true);
        r.setCode(ResultCode.SUCCESS);
        r.setMessage("成功");
        return r;
    }

    public static R error(){
        R r = new R();
        r.setSuccess(false);
        r.setCode(ResultCode.ERROR);
        r.setMessage("失败");
        return r;
    }

    public R success(Boolean success){
        this.setSuccess(success);
        return this;
    }

    public R message(String message){
        this.setMessage(message);
        return this;
    }

    public R code(Integer code){
        this.setCode(code);
        return this;
    }

    public R data(String key, Object value){
        this.data.put(key, value);
        return this;
    }

    public R data(Map<String, Object> map){
        this.setData(map);
        return this;
    }
}

接下来和swagger一样,如何这个结果类是一个单独的微服务,那么在使用的时候就需要引入这个微服务的坐标

<dependency>
    <groupId>cb.cbbgs</groupId>
    <artifactId>service-R</artifactId>
    <version>1.0</version>
</dependency>

以后在controller中我们就可以使用

@GetMapping
public R list(){
    List<Teacher> list = teacherService.list(null);
    return R.ok().data("items", list);
}

四、node

01-下载安装

简单的说 Node.js 就是运行在服务端的 JavaScript。

Node.js是一个事件驱动I/O服务端JavaScript环境,基于Google的V8引擎,V8引擎执行Javascript的速度非常快,性能非常好.

官网:https://nodejs.org/en/

中文网:http://nodejs.cn/

LTS:长期支持版本

Current:最新版

查看版本 node -v

02-NMP

NPM全称Node Package Manager,是Node.js包管理工具,是全球最大的模块生态系统,里面所有的模块都是开源免费的;也是Node.js的包管理工具,相当于前端的Maven 。

我们通过npm 可以很方便地下载js库,管理前端工程。

Node.js默认安装的npm包和工具的位置:Node.js目录\node_modules

  • 在这个目录下你可以看见 npm目录,npm本身就是被NPM包管理器管理的一个工具,说明 Node.js已经集成了npm工具
#在命令提示符输入 npm -v 可查看当前npm版本
npm -v

项目初始化

#建立一个空文件夹,在命令提示符进入该文件夹  执行命令初始化
npm init
#按照提示输入相关信息,如果是用默认值则直接回车即可。
#name: 项目名称
#version: 项目版本号
#description: 项目描述
#keywords: {Array}关键词,便于用户搜索到我们的项目
#最后会生成package.json文件,这个是包的配置文件,相当于maven的pom.xml
#我们之后也可以根据需要进行修改。

#如果想直接生成 package.json 文件,那么可以使用命令
npm init -y  

修改npm镜像

#经过下面的配置,以后所有的 npm install 都会经过淘宝的镜像地址下载
npm config set registry https://registry.npm.taobao.org 

#查看npm配置信息
npm config list

npm命令的使用

#使用 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 包名

03-webpack

#全局安装
npm install -g webpack webpack-cli
#查看版本号
webpack -v

五、Element

01-环境准备

而vue-element-admin是基于element-ui 的一套后台管理系统集成方案。

**功能:**https://panjiachen.github.io/vue-element-admin-site/zh/guide/#功能

**GitHub地址:**https://github.com/PanJiaChen/vue-element-admin

项目在线预览:https://panjiachen.gitee.io/vue-element-admin

先下载项目包。

# 解压压缩包
# 进入目录
cd vue-element-admin-master

# 安装依赖
npm install

# 启动。执行后,浏览器自动弹出并访问http://localhost:9527/
npm run dev

vueAdmin-template是基于vue-element-admin的一套后台管理系统基础模板(最少精简版),可作为模板进行二次开发。

**GitHub地址:**https://github.com/PanJiaChen/vue-admin-template

**建议:**你可以在 vue-admin-template 的基础上进行二次开发,把 vue-element-admin当做工具箱,想要什么功能或者组件就去 vue-element-admin 那里复制过来。

# 解压压缩包
# 进入目录
cd vue-admin-template-master

# 安装依赖
npm install

# 启动。执行后,浏览器自动弹出并访问http://localhost:9528/
npm run dev

通过这个来创建自己的项目

将vue-admin-template-master重命名为自己的项目名

修改信息

package.json

{
    "name": "xxx-admin",
    ......
    "description": "xxx后台管理系统",
    "author": "Helen <cbbgs@qq.com>",
    ......
}

config/index.js中修改端口

port: 9528

src/views/login/index.vue 4行

<h3 class="title">xxx后台管理系统</h3>
<!--28行-->
<el-button :loading="loading" type="primary" style="width:100%;" @click.native.prevent="handleLogin">
    登录
</el-button>

index.html

<title>xxx后台管理系统</title>

src/main.js(项目的js入口),第7行,修改语言为 zh-CN,使用中文语言环境

import locale from 'element-ui/lib/locale/lang/zh-CN' // lang i18n

项目目录结构

src 
├── api // 各种接口 
├── assets // 图片等资源 
├── components // 各种公共组件,非公共组件在各自view下维护 
├── icons //svg icon 
├── router // 路由表 
├── store // 存储 
├── styles // 各种样式 
├── utils // 公共工具,非公共工具,在各自view下维护 
├── views // 各种layout
├── App.vue //***项目顶层组件*** 
├── main.js //***项目入口文件***
└── permission.js //认证入口

运行项目

npm run dev

img

02-路由

src/main.js

......
import router from './router' //引入路由模块
......
new Vue({
  el: '#app',
  router, //挂载路由
  store,
  render: h => h(App)
})

修改 src/router/index.js 文件 复制出来修改即可

  // 讲师管理
  {
    path: '/edu/teacher',
    component: Layout,
    redirect: '/edu/teacher/list',
    name: 'Teacher',
    meta: { title: '讲师管理', icon: 'peoples' },
    children: [
      {
        path: 'list',
        name: 'EduTeacherList',
        component: () => import('@/views/edu/teacher/list'),
        meta: { title: '讲师列表' }
      },
      {
        path: 'create',
        name: 'EduTeacherCreate',
        component: () => import('@/views/edu/teacher/form'),
        meta: { title: '添加讲师' }
      },
      {
        path: 'edit/:id',
        name: 'EduTeacherEdit',
        component: () => import('@/views/edu/teacher/form'),
        meta: { title: '编辑讲师', noCache: true },
        hidden: true
      }
    ]
  },

创建vue组件的时候是在src/views文件夹下创建以下文件夹和文件[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8aEU1r2H-1628041823893)(技术集合.assets/105c6a93-efd3-4e7b-937a-d46c202b7084.png)]

img

03、富文本编辑器

参考 https://panjiachen.gitee.io/vue-element-admin/#/components/tinymce

https://panjiachen.gitee.io/vue-element-admin/#/example/create

Tinymce是一个传统javascript插件,默认不能用于Vue.js因此需要做一些特殊的整合步骤

步骤:

复制脚本库

将脚本库复制到项目的static目录下(在vue-element-admin-master的static路径下)

配置html变量

在/build/webpack.dev.conf.js 中添加配置

使在html页面中可是使用这里定义的BASE_URL变量

new HtmlWebpackPlugin({
......,
templateParameters: {
BASE_URL: config.dev.assetsPublicPath + config.dev.assetsSubDirectory
 }
})

引入js脚本

在根目录/index.html 中引入js脚本

<script src=<%= BASE_URL %>/tinymce4.7.5/tinymce.min.js></script>
<script src=<%= BASE_URL %>/tinymce4.7.5/langs/zh_CN.js></script>

组件引入

为了让Tinymce能用于Vue.js项目,vue-element-admin-master对Tinymce进行了封装,下面我们将它引 入到我们的课程信息页面

复制组件到src/components/Tinymce,

课程信息组件中引入 Tinymce

import Tinymce from '@/components/Tinymce'
export default {
components: { Tinymce },
......
}

组件模板

<!-- xxx简介-->
<el-form-item label="xxx简介">
<tinymce :height="300" v-model="courseInfo.description"/>
</el-form-item>

<style scoped>
.tinymce-container {
line-height: 29px;
}
</style>

六、阿里云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>

配置文件

#服务端口
server.port=8002
#服务名
spring.application.name=service-oss
#环境设置:dev、test、prod
spring.profiles.active=dev

#阿里云 OSS
#不同的服务器,地址不同
aliyun.oss.file.endpoint=oss-cn-beijing.aliyuncs.com
aliyun.oss.file.keyid=LTAI4G3tA4U7bpUAnqk163PF
aliyun.oss.file.keysecret=JqLTUKUBu8ZmGFh5TshtzbLfHFhyKd
#bucket可以在控制台创建,也可以使用java代码创建
aliyun.oss.file.bucketname=cbbgs-guli

启动类

如果怒不希望去加载数据库就可以加上@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)注解

/*不加载数据库*/
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@ComponentScan(basePackages = "cn.cbbgs")//扫描导入的自己写的包 swagger
public class OssApplication {
    public static void main(String[] args) {
        SpringApplication.run(OssApplication.class,args);
    }

}

创建常量读取工具类:ConstantPropertiesUtil.java

@Component
public class ConstantPropertilsUtils 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;

    @Override
    public void afterPropertiesSet() throws Exception {
        END_POINT = endpoint;
        ACCESS_KEY_ID = keyId;
        ACCESS_KEY_SECRET = keySecret;
        BUCKET_NAME = bucketName;

    }
}

controller

@RestController
@CrossOrigin
@RequestMapping("/endoss/fileoss")
public class OssController {
    @Autowired private OssServiceImpl ossService;
    @PostMapping("/upload")
    public R upLoad(MultipartFile file){// 上传文件
        //获取上传文件
       String url= ossService.upLoadFile(file);
        return  R.ok().data("url",url);

    }
}

service

@Service
public class OssServiceImpl implements OssSeervice {
    public String upLoadFile(MultipartFile file) {
        //在这里实现上传文件


        // Endpoint以杭州为例,其它Region请按实际情况填写。
        String endpoint = ConstantPropertilsUtils.END_POINT;
        // 云账号AccessKey有所有API访问权限,建议遵循阿里云安全最佳实践,创建并使用RAM子账号进行API访问或日常运维,请登录 https://ram.console.aliyun.com 创建。
        String accessKeyId = ConstantPropertilsUtils.ACCESS_KEY_ID;
        String accessKeySecret = ConstantPropertilsUtils.ACCESS_KEY_SECRET;
        String bucketName=ConstantPropertilsUtils.BUCKET_NAME;
        /*文件名称*/
        /*随机字符串,解决文件重名的问题*/
        String uuid = UUID.randomUUID().toString().replaceAll("-","");
         String fileName = file.getOriginalFilename();
        fileName = uuid+fileName;

        /*文件按日期分类*/
        //得到当前日期
        String date = new DateTime().toString("yyyy/MM/dd");
        //拼接
        fileName = date+"/"+fileName;   //阿里云会帮我创建文件夹  a/b/1.jpg  阿里云会帮忙创建 a/ b文件夹

        String url="https://"+bucketName+"."+endpoint+"/"+fileName;
        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);

        try{

            // 上传文件流。
            InputStream inputStream = file.getInputStream();
            ossClient.putObject(bucketName,fileName , inputStream);

            // 关闭OSSClient。
            ossClient.shutdown();


            /*
            * https://cbbgs-guli.oss-cn-beijing.aliyuncs.com/touxiang/192151-1532776911475c.jpg
            * */

        }catch (Exception e){

        }

        return url;
    }
}

七、EasyExcel

导入依赖

  <!-- https://mvnrepository.com/artifact/com.alibaba/easyexcel -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>easyexcel</artifactId>
        <version>2.1.1</version>
    </dependency>

创建实体类 设置表头和添加的数据字段

import com.alibaba.excel.annotation.ExcelProperty;

//设置表头和添加的数据字段
public class DemoData {
    //设置表头名称
    @ExcelProperty("学生编号")
    private int sno;
    
	//设置表头名称
    @ExcelProperty("学生姓名")
    private String sname;

    public int getSno() {
        return sno;
    }

    public void setSno(int sno) {
        this.sno = sno;
    }

    public String getSname() {
        return sname;
    }

    public void setSname(String sname) {
        this.sname = sname;
    }

    @Override
    public String toString() {
        return "DemoData{" +
                "sno=" + sno +
                ", sname='" + sname + '\'' +
                '}';
    }
}

实现写操作

//循环设置要添加的数据,最终封装到list集合中
private static List<DemoData> data() {
    List<DemoData> list = new ArrayList<DemoData>();
    for (int i = 0; i < 10; i++) {
        DemoData data = new DemoData();
        data.setSno(i);
        data.setSname("张三"+i);
        list.add(data);
    }
    return list;
}
//实现最终的添加操作
public static void main(String[] args) throws Exception {
    // 写法1
    String fileName = "F:\\11.xlsx";
    // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
    // 如果这里想使用03 则 传入excelType参数即可
    EasyExcel.write(fileName, DemoData.class).sheet("写入方法一").doWrite(data());
}

读操作

创建实体类

import com.alibaba.excel.annotation.ExcelProperty;
public class ReadData {
    //设置列对应的属性
    @ExcelProperty(index = 0)
    private int sid;
    
    //设置列对应的属性
    @ExcelProperty(index = 1)
    private String sname;

    public int getSid() {
        return sid;
    }
    public void setSid(int sid) {
        this.sid = sid;
    }
    public String getSname() {
        return sname;
    }
    public void setSname(String sname) {
        this.sname = sname;
    }
    @Override
    public String toString() {
        return "ReadData{" +
                "sid=" + sid +
                ", sname='" + sname + '\'' +
                '}';
    }
}

创建读取操作的监听器

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.exception.ExcelDataConvertException;
import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

//创建读取excel监听器
public class ExcelListener extends AnalysisEventListener<ReadData> {

    //创建list集合封装最终的数据
    List<ReadData> list = new ArrayList<ReadData>();

    //一行一行去读取excle内容
    @Override
    public void invoke(ReadData user, AnalysisContext analysisContext) {
       System.out.println("***"+user);
        list.add(user);
    }

    //读取excel表头信息
    @Override
    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
        System.out.println("表头信息:"+headMap);
    }

    //读取完成后执行
    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
    }
}

最终的读取

 public static void main(String[] args) throws Exception {

        // 写法1:
        String fileName = "F:\\01.xlsx";
        // 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭
        EasyExcel.read(fileName, ReadData.class, new ExcelListener()).sheet().doRead();

        // 写法2:
        InputStream in = new BufferedInputStream(new FileInputStream("F:\\01.xlsx"));
        ExcelReader excelReader = EasyExcel.read(in, ReadData.class, new ExcelListener()).build();
        ReadSheet readSheet = EasyExcel.readSheet(0).build();
        excelReader.read(readSheet);
        // 这里千万别忘记关闭,读的时候会创建临时文件,到时磁盘会崩的
        excelReader.finish();
}

八、富文本编辑器

参考

https://panjiachen.gitee.io/vue-element-admin/#/components/tinymce

https://panjiachen.gitee.io/vue-element-admin/#/example/create

Tinymce是一个传统javascript插件,默认不能用于Vue.js因此需要做一些特殊的整合步骤

在 /build/webpack.dev.conf.js 中添加配置

new HtmlWebpackPlugin({
    ......,
    templateParameters: {
        BASE_URL: config.dev.assetsPublicPath + config.dev.assetsSubDirectory
    }
})

在/index.html 中引入js脚本

<script src=<%= BASE_URL %>/tinymce4.7.5/tinymce.min.js></script>
<script src=<%= BASE_URL %>/tinymce4.7.5/langs/zh_CN.js></script>

复制组件

src/components/Tinymce 到components

在需要的地方引入

import Tinymce from '@/components/Tinymce'

export default {
  components: { Tinymce },
  ......
}

组件模板

<!-- 课程简介-->
<el-form-item label="课程简介">
    <tinymce :height="300" v-model="courseInfo.description"/>
</el-form-item>

样式

img

八、Nacos

让idea出现RunDashboard

20200816192550184

打开idea的.idea文件的workspace

image-20210121165741949

修改找到,没有就直接添加进去

 <component name="RunDashboard">
    <option name="configurationTypes">
      <set>
        <option value="SpringBootApplicationConfigurationType" />
      </set>
    </option>
    <option name="ruleStates">
      <list>
        <RuleState>
          <option name="name" value="ConfigurationTypeDashboardGroupingRule" />
        </RuleState>
        <RuleState>
          <option name="name" value="StatusDashboardGroupingRule" />
        </RuleState>
      </list>
    </option>
    <option name="contentProportion" value="0.13436294" />
  </component>

Nacos

前提已经安装好nacos 我是docker安装

依赖

<!--服务注册-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

配置

# nacos服务地址
spring.cloud.nacos.discovery.server-addr=47.93.49.117:8848

在客户端微服务启动类中添加注解

@EnableDiscoveryClient

九、OpenFeign

使用OpenFeign是在nacos基础上,要实现服务的注册

OpenFeign主要是在服务调用端使用

依赖

<!--服务调用-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

在消费者启动类添加注解

@EnableFeignClients

创建client包,创建一个接口来调用

@FeignClient注解用于指定从哪个服务中调用功能 ,名称与被调用的服务名保持一致。

@GetMapping注解用于对被调用的微服务进行地址映射。

@PathVariable注解一定要指定参数名称,否则出错

@Component注解防止,在其他位置注入CodClient时idea报错

@FeignClient("service-vod")
@Component
public interface VodClient {
	@DeleteMapping(value = "/eduvod/vod/video/{videoId}")
	public R removeVideo(@PathVariable("videoId") String videoId);
}

在调用端的VideoServiceImpl中调用client中的方法

@Override
public boolean removeVideoById(String id) {

    //查询云端视频id
    Video video = baseMapper.selectById(id);
    String videoSourceId = video.getVideoSourceId();
    //删除视频资源
    if(!StringUtils.isEmpty(videoSourceId)){
        vodClient.removeVideo(videoSourceId);
    }

    Integer result = baseMapper.deleteById(id);
    return null != result && result > 0;
}

img

十、OPenfeign结合Hystrix

依赖

  <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
        </dependency>

        <!--hystrix依赖,主要是用  @HystrixCommand -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>

        <!--服务注册-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!--服务调用-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

配置文件

#开启熔断机制
feign.hystrix.enabled=true
# 设置hystrix超时时间,默认1000ms
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=6000

,创建一个接口来调用

@Component
//熔断方法的指向     fallback = VodFileDegradeFeignClient.class
@FeignClient(value = "service-vod",fallback = VodFileDegradeFeignClient.class)//需要调用的服务提供者的服务名
public interface VodClient {
    //定义删除的路径
    //在这里就是实现服务调用,去调用的上传视频的微服务
    @DeleteMapping("/eduvod/video/removeAlyVideo/{id}")
    public R removeAlyVideo(@PathVariable String id) ;

    //删除多个的
    @DeleteMapping("/eduvod/video/delete-batch")
    public R deleteBatch(@RequestParam("videoIdList") List<String> videoIdList) ;
}

熔断的方法就是上面接口的实现类

//加入服务熔断
//调用出错就执行这里的方法

//然后在接口中添加一个属性
@Component
public class VodFileDegradeFeignClient implements VodClient{
    @Override
    public R removeAlyVideo(String id) {
        return R.error().message("出错了");
    }

    @Override
    public R deleteBatch(List<String> videoIdList) {
        return R.error().message("出错了");
    }
}

十一、服务端渲染技术NUXT

Nuxt.js 是一个基于 Vue.js 的轻量级应用框架,可用来创建服务端渲染 (SSR) 应用,也可充当静态站点引擎生成静态站点应用,具有优雅的代码结构分层和热加载等特性。

官网网站:

https://zh.nuxtjs.org/

下载压缩包:https://github.com/nuxt-community/starter-template/archive/master.zip

将template中的内容复制到 自己的新项目

01-环境准备

修改package.json

name、description、author(必须修改这里,否则项目无法安装)

"name": "cbbgs",
 "version": "1.0.0",
 "description": "xxx前台网站",
 "author": "Helen <2629812077@qq.com>",

修改nuxt.config.js

修改title: ‘{{ name }}’、content: ‘{{escape description }}’

这里的设置最后会显示在页面标题栏和meta数据中

head: {
    title: '谷粒学院 - Java视频|HTML5视频|前端视频|Python视频|大数据视频-自学拿1万+月薪的IT在线视频课程,谷粉力挺,老学员为你推荐',
    meta: [
      { charset: 'utf-8' },
      { name: 'viewport', content: 'width=device-width, initial-scale=1' },
      { hid: 'keywords', name: 'keywords', content: '谷粒学院,IT在线视频教程,Java视频,HTML5视频,前端视频,Python视频,大数据视频' },
      { hid: 'description', name: 'description', content: '谷粒学院是国内领先的IT在线视频学习平台、职业教育平台。截止目前,谷粒学院线上、线下学习人次数以万计!会同上百个知名开发团队联合制定的Java、HTML5前端、大数据、Python等视频课程,被广大学习者及IT工程师誉为:业界最适合自学、代码量最大、案例最多、实战性最强、技术最前沿的IT系列视频课程!' }
    ],
    link: [
      { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }
    ]
},

安装依赖

#安装依赖
npm install
#运行
npm run dev

目录结构

1)资源目录 assets

用于组织未编译的静态资源如 LESS、SASS 或 JavaScript。

(2)组件目录 components

用于组织应用的 Vue.js 组件。Nuxt.js 不会扩展增强该目录下 Vue.js 组件,即这些组件不会像页面组件那样有 asyncData 方法的特性。

(3)布局目录 layouts

用于组织应用的布局组件。

(4)页面目录 pages

用于组织应用的路由及视图。Nuxt.js 框架读取该目录下所有的 .vue 文件并自动生成对应的路由配置。

(5)插件目录 plugins

用于组织那些需要在 根vue.js应用 实例化之前需要运行的 Javascript 插件。

(6)nuxt.config.js 文件

nuxt.config.js 文件用于组织Nuxt.js 应用的个性化配置,以便覆盖默认配置。

02-幻灯片插件

安装插件

npm install vue-awesome-swiper

在 plugins 文件夹下新建文件 nuxt-swiper-plugin.js,内容是

import Vue from 'vue'
import VueAwesomeSwiper from 'vue-awesome-swiper/dist/ssr'

Vue.use(VueAwesomeSwiper)

在 nuxt.config.js 文件中配置插件

将 plugins 和 css节点 复制到 module.exports节点下

module.exports = {
  // some nuxt config...
  plugins: [
    { src: '~/plugins/nuxt-swiper-plugin.js', ssr: false }
  ],

  css: [
    'swiper/dist/css/swiper.css'
  ]
}

我们可以把页头和页尾提取出来,形成布局页

修改layouts目录下default.vue,从静态页面中复制首页,修改了原始文件中的资源路径为~/assets/,将主内容区域的内容替换成

内容如下:

<template>
  <div>
    <!-- 页头部分 -->
      
    <!-- 内容的区域 -->
    <nuxt/>
      
    <!-- 页尾部分 -->
  </div>
</template>

img

03-路由

1、固定路由

(1)使用router-link构建路由,地址是/course

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xWjwpSLi-1628041823898)(技术集合.assets/3171c885-c389-43d9-b141-63c9aef6a701.png)]

(2)在page目录创建文件夹course ,在 course 目录创建index.vue

<template>
  <div>
    课程列表
  </div>
</template>

2、动态路由

如果我们需要根据id查询一条记录,就需要使用动态路由。NUXT的动态路由是以下划线开头的vue文件,参数名为下划线后边的文件名

在pages下的course目录下创建_id.vue

<template>
  <div>
    讲师详情
  </div>
</template>

img

03-封装axios

下载axios ,使用命令 npm install axios

创建utils文件夹,utils下创建request.js

import axios from 'axios'
// 创建axios实例
const service = axios.create({
  baseURL: 'http://localhost:8201', // api的base_url
  timeout: 20000 // 请求超时时间
})
export default service

十二、Redis

依赖

<!-- redis -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

<!-- spring2.X集成redis所需common-pool2-->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
    <version>2.6.0</version>
</dependency>

配置文件

#redis端口号
spring.redis.host=192.168.44.132
spring.redis.port=6379
spring.redis.database= 0
spring.redis.timeout=1800000

spring.redis.lettuce.pool.max-active=20
spring.redis.lettuce.pool.max-wait=-1
#最大阻塞等待时间(负数表示没限制)
spring.redis.lettuce.pool.max-idle=5
spring.redis.lettuce.pool.min-idle=0

书写Redis配置类

@EnableCaching
@Configuration
public class RedisConfig extends CachingConfigurerSupport {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        template.setConnectionFactory(factory);
        //key序列化方式
        template.setKeySerializer(redisSerializer);
        //value序列化
        template.setValueSerializer(jackson2JsonRedisSerializer);
        //value hashmap序列化
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        return template;
    }

    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        //解决查询缓存转换异常的问题
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        // 配置序列化(解决乱码的问题),过期时间600秒
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofSeconds(600))
              .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
                .disableCachingNullValues();
        RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
                .cacheDefaults(config)
                .build();
        return cacheManager;
    }
}

redis注解介绍

缓存@Cacheable

根据方法对其返回结果进行缓存,下次请求时,如果缓存存在,则直接读取缓存数据返回;如果缓存不存在,则执行方法,并把返回的结果存入缓存中。一般用在查询方法上。

查看源码,属性值如下:

属性/方法名解释
value缓存名,必填,它指定了你的缓存存放在哪块命名空间
cacheNames与 value 差不多,二选一即可
key可选属性,可以使用 SpEL 标签自定义缓存的key

(2)缓存@CachePut

使用该注解标志的方法,每次都会执行,并将结果存入指定的缓存中。其他方法可以直接从响应的缓存中读取缓存数据,而不需要再去查询数据库。一般用在新增方法上。

查看源码,属性值如下:

属性/方法名解释
value缓存名,必填,它指定了你的缓存存放在哪块命名空间
cacheNames与 value 差不多,二选一即可
key可选属性,可以使用 SpEL 标签自定义缓存的key

(3)缓存@CacheEvict

使用该注解标志的方法,会清空指定的缓存。一般用在更新或者删除方法上

查看源码,属性值如下:

属性/方法名解释
value缓存名,必填,它指定了你的缓存存放在哪块命名空间
cacheNames与 value 差不多,二选一即可
key可选属性,可以使用 SpEL 标签自定义缓存的key
allEntries是否清空所有缓存,默认为 false。如果指定为 true,则方法调用后将立即清空所有的缓存
beforeInvocation是否在方法执行前就清空,默认为 false。如果指定为 true,则在方法执行前就会清空缓存

在方法上使用redis注解

//添加到redis中   
@Cacheable(value = "banner", key = "'selectIndexList'")
    @Override
    public List<CrmBanner> selectIndexList() {
        List<CrmBanner> list = baseMapper.selectList(new QueryWrapper<CrmBanner>().orderByDesc("sort"));
        return list;
    }

十三、Gateway

依赖

 <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>

配置文件

# 服务端口
server.port=8222
# 服务名
spring.application.name=service-gateway

# nacos服务地址
spring.cloud.nacos.discovery.server-addr=47.93.49.117:8848

#使用服务发现路由
spring.cloud.gateway.discovery.locator.enabled=true
#服务路由名小写
#spring.cloud.gateway.discovery.locator.lower-case-service-id=true

#设置路由id,建议用服务名
spring.cloud.gateway.routes[0].id=service-edu
#设置路由的uri,动态路由,用服务名进行路由
spring.cloud.gateway.routes[0].uri=lb://service-edu
#设置路由断言,代理servicerId为auth-service的/auth/路径
spring.cloud.gateway.routes[0].predicates= Path=/edu/**

#配置service-vod服务
spring.cloud.gateway.routes[1].id=service-vod
spring.cloud.gateway.routes[1].uri=lb://service-vod
spring.cloud.gateway.routes[1].predicates= Path=/eduvod/**

#配置service-ucenter服务
spring.cloud.gateway.routes[2].id=service-ucenter
spring.cloud.gateway.routes[2].uri=lb://service-ucenter
spring.cloud.gateway.routes[2].predicates= Path=/user/**

#配置service-oss服务
spring.cloud.gateway.routes[3].id=service-oss
spring.cloud.gateway.routes[3].uri=lb://service-oss
spring.cloud.gateway.routes[3].predicates= Path=/endoss/**

spring.cloud.gateway.routes[4].id=service-msm
spring.cloud.gateway.routes[4].uri=lb://service-msm
spring.cloud.gateway.routes[4].predicates= Path=/msm/**

spring.cloud.gateway.routes[5].id=service-cms
spring.cloud.gateway.routes[5].uri=lb://service-cms
spring.cloud.gateway.routes[5].predicates= Path=/baogu/**

yml

server: 
 port: 8222

spring:
  application:
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true
      routes:
      - id: SERVICE-ACL
        uri: lb://SERVICE-ACL
        predicates:
        - Path=/*/acl/** # 路径匹配
      - id: SERVICE-EDU
        uri: lb://SERVICE-EDU
        predicates:
        - Path=/eduservice/** # 路径匹配
      - id: SERVICE-UCENTER
        uri: lb://SERVICE-UCENTER
        predicates:
        - Path=/ucenter/** # 路径匹配
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

陈斌-cb

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值