1 Star 7 Fork 1

Dk.l/wiki

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README

SpringBoot知识体系+Vue3 实战WIKI知识库系统

相关软件已传到QQ群文件中

  • jdk-8u221-windows-x64.exe
  • ideaIU-2019.2.3.exe
  • Git-2.23.0-64-bit.exe
  • mysql-installer-community-5.7.27.0.msi
  • jdk-8u261-linux-x64.tar.gz

源码下载

git clone https://git.imooc.com/coding-474/jiawawiki.git
会配置ssh的,可以用ssh:
git clone ssh://git@git.imooc.com:80/coding-474/jiawawiki.git
  • 数据库初始化脚本已传到QQ群中

项目初始化

  • 需要本地安装好idea, nodejs,jdk1.8, mysql8.0/5.7, navicat(数据库可视化工具)
  • 将下载好的源码,用idea打开
  • 刷新maven依赖
  • 安装vue cli,参照课程4-3
  • 初始化web模块
cd web
npm install
  • 新建数据库参照课程3-2,数据库配置在application.properties
  • 数据库初始脚本从QQ群文件中下载

项目启动

  • 启动服务端:WikiApplication
  • 启动前端网站:web\package.json

页面访问

  • 网站地址: http://localhost:8080
    初始用户名密码:test/test @ResponseBody 用来返回字符串或JSON对象,使用此注解,表示接口支持所有请求接口(GET、POST、PUT、DELETE)

@GetMapping 只接受GET方法,类似还有@PostMapping@PutMappingDeleteMapping

@RequestMapping用法:@RequestMapping(value = "/hello", method = RequestMethod.GET)

@RestController 一般用来返回字符串

@Controller 一般用来返回页面

@Primary注解,当有多个实现类是,首先注入此类

Alt + Enter 自动导包

Alt + Insert 键,打开Generate面板,包含getter and setter、constructor、toString等方法

如果Applicatiob和Controller类不在同一个包内,须要添加 @ComponentScan("Cotroller包全限定路径")

自动删除无用的引用file-->settings-->搜索 auto 在Auto Import中勾选Optimize import on the fly

bootstrap 配置,单个SpringBoot不会读取bootstrap配置,要SpringCloud架构下的SpringBoot应用才会读,一般用于动态配置,线上可以实时修改实时生效的配置,一般配合nacos使用。

  • 从数据库中获得汉字为乱码,在链接数据库的数据库源改为
<property name="url" value="jdbc:mysql://47.113.221.222:3306/mybatis?useSSL=false&amp;useUnicode=true&amp;characterEncoding=utf-8"/>
  • 突然飘红,出现红色的波浪下划线,错误提示:cannot access com.xx…xx.class
    • 1、重启大法,解决问题。
    • 2、如果重启不行,就清一下Idea缓存(根本原因),强制清除idea缓存方法: File > Invalidate Caches /Restart即可清理缓存
  • 如果数据库是5.7版本,使用8.0.22版本连接驱动,会导致sql中的汉字乱码

集成热部暑

  1. 引入依赖,使用SpringBoot内置的依赖,不需要添加``version</mark> 版本号

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
    </dependency>
    
  2. file-->settings-->搜索 compiler,再在右侧勾选 Build project automatically

  3. 按两次Shift 键,切换至Action (或者在help--> find Action...),搜索registr(注意:不是register,也不是选   Registration Actions`),勾选compiler.automake.allow.when.app.running

注意:只有保存配置文件(application.properties 等)Ctrl+ S, 才会触发热部署, 或者点击“小锤子” 图标编译;或者稍微改一下service层代码

  • 当复制一个类,在复制的类里按 Ctrl + R 进行替换(注意勾选 match case 区分大小写)

  • 复制一个类,如果在左边复制是复制一个新类,在右边复制是复制一个类名

  • new 类名 按下Ctrl + Alt + V 自动生成一个新类

  • 后端接口很多,为了前端能够统一处理逻辑(登录校验、权限校验),需要统一后端的返回值

  • 选中一个类,按 Shift + F6 进行类重命名

  • toString 一般用来打日志用

  • 两种for循环模板:foriiter

  • 在cotroller层不要出现domain实体ebook,用ebookResp(或ebookVo)代替

  • Ctrl + Y 删掉当前行

  • 有时idea提示程序包不存在,实际是有的。Maven可能出现编译缓存。maven—>clean即可.如果觉㧹代码没有错,但编译报错,就clean一下

    • 测试结果跟代码不符
    • 代码没错却编译出错
    • 引入了jar包却没反应
  • 养成习惯:改动再小,也要测试通过

第4章 vue + vue cli 项目搭建

  • Vuex 保存全局变量

  • main.ts是初始(配置)文件

  • Vue CLI 初始执行main.ts,将内容页App.vue渲染到index.html,完成页面显示

    "dependencice":{
        "vue": "^30.0.0",
        "vue-router": "^4.0.0-.",
        "vux": "^4.0.0-0"
    }
    

    ^ 表示如果有版本,将自动更新。package-lock.json 文件进行版本锁定

    • 安装ant-design-vue 一定要进入web里,注意避免直接在项目里。否则加载不出页面,并且会报错
    • 使用<router-view/> 标签来填充路由内容

第五章 前后端交互整合

  • setup() Vue3新增的初始化方法
  • “No 'Access-control-All-Origin' header is ... 出现里跨域问题。跨域可以这样理解,来自一个IP端口的页面(Vue项目),需要访问另一个IP断口的资源(Spring Boot请求接口),会产生跨域问题。
  • Ctrl + Shift +F 全局搜索
  • 在调用电子书接口之前会先发一个OPTIONS请求。
  • 修改了配置类或pom.xml,建议重启应用,不要热部署。

5-3 vue3数据绑定显示列表数据

  • 初始化逻辑都写到onMounted(生命周期函数)方法离,setup就放一些参数定义,方法定义setup执行的时候界面还没渲染好,这个时候如果去操作界面元素会报错。多利用生命周期钩子函数
  • 在response里面有一个data,这个data对应的就是后端返回的CommonResp的数据结构
  • 所谓的响应式的数据就是说js里面动态修改的值
  • vue3 新增了ref,用来定义响应式数据,要用这个ref,需要import进来,html代码要拿到响应式变量,需要在setup最后return,使用{{xxx}}来获取变量
setup(){
    console.log("setup");
    const ebooks = ref();
    onMounted(() => {
      axios.get("http://localhost:8880/ebook/list?name=Spring").then((response) => {
        console.log(response);
        const data = response.data;
        ebooks.value = data.content;
        console.log(response);
      })
    })
    return {
      ebooks
    }
  }
  • reactive里面一般放一个对
setup(){
    console.log("setup");
    const ebooks1 =reactive({books: []});
    onMounted(() => {
      axios.get("http://localhost:8880/ebook/list?name=Spring").then((response) => {
        console.log(response);
        const data = response.data;
        ebooks.value = data.content;
        ebooks1.books = data.content;
        console.log(response);
      })
    })
    return {
      ebooks2: toRef(ebooks1,"books")
    }
  }

上述的两种方法都可以用,但是我们在项目中,尽量统一,要么全用ref,要么全用reactive

5-4 电子书列表界面展示

const pagination = {
      onChange: (page: number) => {
        console.log(page);
      },
      pageSize: 3,
    };
  • (page: number) 因为它是一个参数,就是onChange后边那段是一个回调函数,是一个function。在参数连边加number的话。我们需要把它用括号括起来。
  • 一次性将图标库导入,而不需要每个页面导入
  • 使用图标库,需要自行安装 npm install --save @ant-design/icons-vue

全局使用图标:

  1. 切换到main.js

  2. 添加import * as Icons from '@ant-design/icons-vue'

  3. 在下边继续添加

    const icons: any = Icons;
    for (const i in icons) {
    	app.component(i, icons[i]);
    }
    
    1. 改造代码:

      createApp(App).use(store).use(router).use(Antd).mount('#app');
      

      为:

      const app = createApp(App);
      app.use(store).use(router).use(Antd).mount('#app');
      
createApp(App).use(store).use(router).use(Antd).mount('#app');
  • style 标签下的scoped:表示这里的样式只在当前组件(Antdv的组件,例.ant-avatar)起作用
<style scoped>
	.ant-avatar {
  width: 50px;
  height: 50px;
  line-height: 50px;
  border-radius: 8%;
  margin: 5px 0;
</style>

5-5 Vue CLI多环境配置

  • 增加开发和生产配置文件
  • 修改编译和启动支持多环境
  • 修改axios请求地址支持多环境

自定义参数:VUE_APP_XXX必需以VUE_APP_开头,自定义文件放在web目录

NODE_ENV=production
VUE_APP_SERVER=http://127.0.0.1:8080

5-9 SpringAOP的使用

三个通知:前置、后置、环绕。

第6章 电子书管理功能开发

6-1 章节介绍

  • 增加电子书管理页面:页面、路由、菜单
  • 电子书列表展示:表格组件、查询表单、后端列表查询接口
  • 前后端分页处理:后端分页插件、前端分页组件、参数传递
  • 电子书编辑:模态框组件、表单组件、后端保存接口
  • 电子书新增:界面复用编辑功能、后端复用保存接口、雪花算法
  • 电子书删除:确认组件、后端删除接口
  • 参数校验:集成SpringBoot Validation

6-2 增加电子书管理页面

Shift + F6 重命名

6-3 电子书表格展示

  • slots:自定义渲染
  • title:表头渲染
  • customRender:值渲染
 {
    dataIndex: 'name',
    key: 'name',
    slots: { title: 'customTitle', customRender: 'name' },
  }

6-4 使用PageHelper实现后端分页

PageHelper.startPage(1,3);
// 语句只对第一个select起作用,如果后边有两个select,第二个不执行
PageInfo<Ebook> pageInfo = new PageInfo<>(elookList);
LOG.info("总行数:" + pageInfor.getTotal());
LOG.info("总页数:" + pageInfor.getPages());
  • 分页四个元素:

    • 页码:(pageNume:1)

    • 数量:(pageSize:3)

    • 总行数:pageInfor.getTotal()

    • 总页数:pageInfor.getPages()

后两个,建议返回total,前端还可以自己去计算总页数;但是如果返回总页数,前端不能确切的知道总条数。

6-5 封装分页请求参数和返回参数

  • Alt + Insert 键,生成get set方法
  • Lombok 有反射,万一写的性能不好,多用一层框架,就多一层风险
  • 不确定的数据要用泛型

6-6 前后端分页功能整合

  • 要用响应式变量必须加上.value,
onMounted() => {
	handleQuery({
		page:1,
		size:pagination.value.pageSize
	})
}

真正传到后端的是上述的page、size,这个必须和后端的PageReq的两个参数要一致才会将前端的参数自动映射到EbookReq req这个类里来;

  • 表格分页组件内置了一些属性:current、 、pageSize等
  • 前边介绍过前端分页,它本来是成功的,就算后端不写分页,前端功能本来就是正常的,它是一个前端分页(逻辑分页),所以一定要确认一下,后端也做了分页
  • 看日志非常重要
  • 因为handleQuery可能被很多地方调用到,它传过来的参数可能各式各样,都不太一样。所以我们一般会展开来,如下,不管params里面有多少参数,这里只会用到这两个参数。
/**
     * 数据查询
     **/
<!-- 下面params是传过来的变量-->
<!-- any 表示它是一个任何类型都可以,其实是一个json对象 -->
const handleQuery = (params: any) => {
	loding.value = true;
	axios.get("/ebook/list",{
<!--下面的params不是传过来的参数-->
		params: {
			page: params.page,
			page: params.size
		}
	}).then((response) => {
        loading.value = false;
        const data = response.data;
        ebooks.value = data.content.list;
}

另一种方式:

<!-- 下面params是传过来的变量-->
const handleQuery = (params: any) => {
	loding.value = true;
	axios.get("/ebook/list?page=" + params.page + "&size=" + params.size).then...
}

6-6 制作电子书表单

  • Ctrl + D 复制当前行并粘贴

  • edit 方法无参数,可用下边两种方法表示:

    <a-button type="primary" @click="edit">编辑</a-button>
    
    <a-button type="primary" @click="edit()">编辑</a-button>
    
  • edit 方法有参数表示方法

    <a-button type="primary" @click="edit(record)">编辑</a-button>
    

6-8完成电子书编辑功能

  • Alt + Shift + 上/下:可将当前行上下移动

  • @RequestBody 注解对应的是json方式和(POST)提交,就像ebook用的是content-type:application/json 需要用@RequestBody才能接收到。同样是POST提交,以form方式提交(Content-Type: application/x-www-form-urlencoded)就不需要任何注解。

    POST http://localhost:8880/ebook/save
    Content-Type: application/json
    {}
    
    public CommonResp save(@RequestBody EbookSaveReq ebookSaveReq) {}
    
  • EbookSaveReq类和EbookQueryResp类内容相同,这种设计方法会使类变得很多,但是比较灵活,后面加参数校验时可以体现出来。在6-11中

  • 如果定义的类带有泛型,new时也要带有泛型,否则会new失败且不会报错

      public class CommonResp<T> {} //声明类
    
      CommonResp resp = new CommonResp<>();  //new对象
    

6-9 雪花算法与新增功能

  • 雪花算法:时间戳,数据中心,机器中心,序列号
  • @Resource是jdk自带的,@Autoeired是Spring自带的
  • id几种算法:自增、uuid、雪花算法
  • Integer类默认值是null,
  • 要在实体Ebook类中把数据库中 not null 值 赋值set为 0 不用 default 0 因为mbyatis生成的代码是全字段的,所以insert 或 update都是带全字段的 如果insert SQL里写了字段,且给的值是null,这种情况就会存null,default 就失效了
  • 雪花算法得到的ID较长,传到前端后,精度丢失,修改不了数据 解决方法:增加统一配置类JacksonConfig.java,将Long类型转成String,再传给前端

6-10 增加删除电子书功能

  • 重要的业务操作,如删除、审批等,一定要有确认动作

6-11 集成Validation做参数核验

  • SpringBoot很多功能都是通过注解来完成
  • 在git记录上按Ctrl + D 对比修改内容
  • 6-8提到的好处:在request里添加一个注解,不会去影响response。虽然两个类里面属性相同,但是我们对请求类可以增加一些特殊的校验,而返回的类,就没有这些注解了。
  • 选中代码,按Alt + Shift + 上 代码上移

6-12 电子书管理功能优化

setup(){
    const param = ref(); //定义一个响应式变量
    param.value = {}; //初始给它一个空变量,否则会报错
}
  • js对象复制:将json对象转为json字符串,再转回json对象

7-2分类表设计与代码生成

  • 代码生成器生成的四个类(Category、CategoryExample、CategoryMapper.java、CategoryMapper.xml)不要动,这样我们后面有新的扩展,会增加一些新的字段的话,可以重新去生成。

7-3完成分类基本增删改查功能

Ctrl + Y 删除选中的内容

7-4分类表格显示优化

  • 加上: 表示变量,不加表示属性

     <a-table
              :columns="columns"
              :data-source="level1"
              :row-key="record => record.id"
              :pagination="false"
          >
    
  • 树形数据展示,查看Ant Design Vue—>Table表格—>树形数据展示

7-5 分类编辑功能优化

  1. <a-selection-option v-for="c in level1" :key="c.id" :value="c.id" :disabled="category.id === c.id">
    	{{c.name}}
    </a-selection-option>
    
    • 如果是在属性里边用变量,我们直接用变量就可以了;

    • 如果是在HTML,就是属性外面,在节点中间要去使用这个变量的话,应该用两个大括号

  • Ctrl + Shift + Z :代码重做(就是回到Ctrl+Z回退之前)

7-6 电子书增加分类管理功能

  • 点击编辑保存之后,列表数据遍了,再次点击编辑之后,返回的内容还是前边儿没有编辑之前的内容(即Action列的变量没有变),是因为“Action”这一列按钮对应的渲染没有变

7-8 点击二级分类菜单显示电子书

const = constant 不变的、永恒的

let 表示变量


第八章 文档管理功能开发


8-1 本章介绍

  • 重点学习无限极树的管理功能设计&富文本编辑框的使用
  • 文档表设计与代码生成
  • 按照分类管理的代码,复制出一套文档树管理
  • 关于无限极树的增删改查功能开发
  • 文档内容保存与显示:富文本框的使用
  • 首页点击某个电子书时,跳转文档页面,显示文档树
  • 点击某个文档时,加载文档内容

8-2 文档表设计与代码生成

8-3 完成文档表基本增删改查功能

8-4 使用树形选择组件选择父节点

/**
     * 将某节点及其子孙节点全部置为disabled
     */
    const setDisable = (treeSelectData: any, id: any) => {
      // console.log(treeSelectData, id);
      // 遍历数组,即遍历某一层节点
      for (let i = 0; i < treeSelectData.length; i++) {
        const node = treeSelectData[i];
        if (node.id === id) {
          // 如果当前节点就是目标节点
          console.log("disabled", node);
          // 将目标节点设置为disabled
          node.disabled = true;

          // 遍历所有子节点,将所有子节点全部都加上disabled
          const children = node.children;
          if (Tool.isNotEmpty(children)) {
            for (let j = 0; j < children.length; j++) {
              setDisable(children, children[j].id)
            }
          }
        } else {
          // 如果当前节点不是目标节点,则到其子节点再找找看。
          const children = node.children;
          if (Tool.isNotEmpty(children)) {
            setDisable(children, id);
          }
        }
      }
    };
	/**
     * 编辑
     */
    const edit = (record: any) => {
      modalVisible.value = true;
      doc.value = Tool.copy(record);

      // 不能选择当前节点及其所有子孙节点,作为父节点,会使树断开
      treeSelectData.value = Tool.copy(level1.value);
      setDisable(treeSelectData.value, record.id);

      // 为选择树添加一个"无"
      treeSelectData.value.unshift({id: 0, name: '无'});
    };

    /**
     * 新增
     */
    const add = () => {
      modalVisible.value = true;
      doc.value = {};

      treeSelectData.value = Tool.copy(level1.value) || [];

      // 为选择树添加一个"无"
      treeSelectData.value.unshift({id: 0, name: '无'});
    };

8-5 Vue页面参数传递完成新增文档功能

 <router-link :to="'/admin/doc?ebookId=' + record.id">

8-6 增加删除文档功能

tips:

  • 程序设计小技巧,将复杂的算法放到前端来做,减少服务器压力
  • 先设计方案,再做技术调研,验证方案可行性,最后才开始开发
  • 有些业务很复杂时,把一个大SQL拆成多次按主键操作,反而性能更高

8-7 集成富文本插件wangeditor

npm iwangeditor@4.6.3 --save
import E from "wangeditor"
const editor = new E("#div1")
editor.create()
  • 初始的时候,没有modal这块标签(即文档表单),放入到sutUp中渲染不出来;根本就没有这样一个元素,所以初始的时候我们写 #ID 去获取这个选择器的时候,选择不到任何元素根本原因:html上没有content元素,只要html有content元素,即使不可见,也能被渲染出来

  • 加入到onMount中仍旧显示不出来,考虑到modal初始是隐藏的,会不会是因为隐藏而找不到content?

  • 添加到add()和edit()中也不显示;考虑到加载modal是很耗时的,在modal还没加载出来的时候editor.reate()就已经执行了,所以需要异步执行;

  • 需要异步执行时,要想到setTimeout

    setTimeout(function(){
    	editor.create();
    },100);
    

8-8 文档内容表设计与代码生成

8-9 文档管理页面布局修改

// 将富文本的层级调低
editor.config.zIndex = 0;
<a-table
              v-if="level1.length > 0"  // 只有level1的长度大于0, level1是动态的
              :columns="columns"
              :data-source="level1"
              :row-key="record => record.id"
              :pagination="false"
              :defaultExpandAllRows="true" // 展开才生效, 这个属性不是动态监听数据的变化的,它是初始的时候执行一次,后边就不在执行。如果在初始的时候,level1还没有数据就没有展开效果了,所以加上上边v-if那一句话,是有数据了,采取展示这个表格,此时这个属性就又作用了
          >
    
    ... 
    
    const level1 = ref(); // 初始化的时候为null
    level1.value = []; // 初始化一个空数组,否则当为null时,上边的level1.length就会出错了

8-10 文档内容的保存与显示

doc.value.content = editor.txt.html();报 Property 'content' does not exist on type '{}'. 错误,是因为在定义的时候定义错了定义成了 const doc= ref({}); 这样是写了一个空对象;可以初始给它复制一个空对象,如:

const doc = ref();
doc.value = {};

这样就可以了

8-11 文档内容的显示

8-12 文档页面功能开发

  • 新加的样式,不要影响页面其它内容,给目标区域加个特定的样式名字,如课程中用的 class=“wangeditor”
  • 使用!important提高样式的优先级
  • style里加了scoped属性,表示里面的属性只在当前页面有用,不加scoped的话,则是全局 起作用

第九章 用户管理&单点登录


9-1 本章介绍

  • 用户管理,用户登录,登录校验(界面+接口)
  • 用户管理功能
    • 用户表设计与持久层代码生成
    • 基本的增删改查
    • 用户名不能重复
    • 关于密码的两层加密处理:加密传输+加密存储
    • 重置密码
  • 登录功能
    • 前端登录界面
    • 后端登录接口
    • 登录成功后的处理
    • 退出登录

9-2 用户表设计与持久层代码生成

9-3 完成用户表基本增删改查功能

9-4 用户名重复交验与自定义异常

使用!!绕过类型检验

:disabled="user.id" // 会报Expected Boolean, got Number with ...

修改成

:disabled="!!user.id"
// 更新用户,是用户名不可修改
user.setLoginName(null); // 设置接收用户名为空,防止被人绕过前段
userMapper.updateByPremaryKeySelective(user);// 只修改有改动的值

9-5 关于密码的两层加密处理

tips:如果要用到第三方的js里边的一些变量,需要在使用的地方定义一下,告诉它这些变量是存在的。

declare let hexMd5: any;

9-6 增加重置密码功能

v-if和i-show都可以用来显示或者隐藏某个元素;v-show只是不显示,适用于动态变化;而v-if会不加载(删掉),初始的时候就判断好是隐藏还是显示。

9-7 单点登录token与JWT介绍

单点登录系统有可能只是提供接口,也可能包括页面

要使用JWT需要引入额外的依赖包

  • token + redis:token是无意义的(通用的做法)
  • JWT:token是有意义的,加密的,包含业务信息,一般是用户信息,可以被解出来(很多也是把token放到redis里边)

9-8 登录功能开发

@NotNull : 会校验null

NotEmpty : 会校验null 和“”

9-9 登录成功处理并集成vuex-1

使用Redis,需要先引入redisTemplate

redisTemplate.opsForValue().set(token, userLoginResp, 3600*24, TimeUnit.SECONDS)

token随着用户登录信息返回给前端

把一个类放入到rides里边,需要序列化(implements Serializable )或者直接转化成字符串JSONObject.toJSONString(userLoginResp);DUBBO是RPC框架,就用到了序列化技术,比如:从A应用的类传到B应用去执行

@Autowired自动注入注解 实际上调用Impl的具体实现 但是当一个接口的方法,对应多个实现的时候,怎么区分到底注入哪一个呢 答案是@Qualifier注解和@Resource注解

@Qualifier注解的用处:当一个接口有多个实现的时候,为了指名具体调用哪个类的实现

@Resource注解:可以通过 byName命名 和 byType类型的方式注入, 默认先按 byName的方式进行匹配,如果匹配不到,再按 byType的方式进行匹配。 可以为 @Service和@Resource 添加 name 这个属性来区分不同的实现

9-10 登录成功处理并集成VUEX-2

vuex :全局响应式变量

vuex封装sessionStorage,各页面/组件只知道vuex,不需要知道sessionStorage

index.ts
declare let SessionStorage: any;
const USER = "USER"
const store = createStore({
    state:{
        user: SessionStorage.get(USER) || {}  // 避免空指针异常
    },
	// 同步
	mutations: {
		setUser(state, user){ // user为传进来的值
			state.user = user
		}
	}
	// 异步
	actions: {

	}
	modules:{

	}
});

export default store;


store.commit("setUser",user.value);  // 会自动调用上边的mutations方法
  • computed用来监控自己定义的变量,如果一个响应式变量是要根据某个变量的变化而计算得来,可以使用computed

9-11 增加退出登录功能

  • 目标:将token置为失效
  • 后端增加退出登录接口,退出后,清除redis用户信息
  • 前端增加退出登录按钮,退出后,清除前端用户信息

9-12 后端接口增加登录校验

  • 未登录时,管理菜单要隐藏
  • 对路由做判断,防止用户通过手敲url访问管理页面

9-13 前端接口增加登录校验

  • 后端校验:必须,防止别人绕过界面直接调用接口
  • 前端校验:非必要,可减少服务器压力

9-14 用户密码初始化

第十章 阅读量和点赞量

10-1 章节介绍

  • 更新阅读数
  • 更新点赞数
  • 更新电子书的文档数、阅读数、点赞数
  • 有文档被点赞时,前端可以收到通知
  • SpringBoot异步化、WebSocket、RocketMQ

10-2 文档阅读数更新

10-3 文档点赞功能开发

10-4 电子书信息更新方案调研

更新方式:

  • 实时更新

    • 优点:准确型高
    • 缺点:改动的地方多
  • 定时批量更新

    • 优点: 改动的地方少
    • 缺点:数据实时性差

10-5 SpringBoot定时任务实例

定时框架Quartz

10-6 完成电子书信息定时更新功能

10-7 日志流水号的使用

logback 增加自定义参数

可以给定时任务添加线程池

在线程开始的地方,给LOG_ID赋值

10-8 WebSocket使用实例

  • 功能:网站通知,点赞时,前端收到通知
  • 定时轮训&被动通知
@Component
@ServerEndpoint("/ws/{token}") 
publi class WebSocketServer{
    // 注意上边的注解,练习时由于忘了写注解导致链接WebSocket出错
} 

10-9 完成点赞通知功能

SpringBoot异步化使用

两个功能代码写在一条线上,会互相影响,可以使用异步线程让两个功能走两条线

10-10 使用异步化解耦点赞通知功能

易犯错误:

  • 认为启动没问题,注解也加了,异步化就完成了,异步化注解 @Asyn要加在方法上;
  • 认为功能调试没问题,异步化就完成了(通过日志号的线程号查看是否为同一个)
  • 要想一步话成功, @Asyn 注解的异步方法,要写到另一个类里边去,不能跟调用的地方在同一个类里边  很多新手认为加上注解,异步化就成功了,测试起来也没有发现什么异常

WebSocketServer.java 单一职责原则

WsService类要能被注入到其他类,前提是一定要能被扫描到该类,记得添加@Service注解

同时对两张表有增删改的操作,就要考虑加事务,否则会造成数据不准确。当然也有不加事务的场景,不能一概而论

同一个类里A调用B,B加事务注解不生效

10-11 使用MQ解耦点赞通知功能

异步有可能方法内部耗时较长,容易出错

当线程的数量超过线程池的最大线程数量(容量),后边的请求就会变成同步,业务量很大的话,就会有可能让线程越来越多,一直到把我们整个服务器塞满,就会影响到原有的业务。所以就引入了MQ,和Redis一样,MQ是一个中间件,需要单独安装。常用的有RocketMQ、Kafka、RabbitMQ等

  • 安装RocketMQ,配置环境变量ROCKETMQ_HOME 跟地址(路径,不要到bin路径),启动 mqnamesrv.cmd(相当于注册中心,管理mqbroker)
  • 再开启一个窗口: mqbroker.cmd -n 127.0.0.1:9876 autoCreateTopicEnable=true
    • topic 就是主体,不同的业务使用不同的主体(比如:客户端<发送方>可以往一个主题里面发送一个消息,服务端<接收方/消费方>可以去监听这个主题) 加上后边的参数,SpringBoot 可发送任意的topic到RocketMQ,否则需要在RocketMQ里先创建好Topic,为了生产环境的安全,一般是在RocketMQ先创建好Topic,使用阿里云的一个Topic
  • 引入依赖,配置地址
  • 在使用的地方注入RocketMQTemplate 类
  • 消费端一般会写到另一个专门监听MQ的应用,案例中,只有一个应用,就写到了一起
  • 消费端注解: @RocketMQMessageListener(consumerGroup = "default", topic = "VOTE_TOPIC")记得添加@Service注解,使能够被扫描到
  • 通常情况下:发送方跟接收方是两个不同的应用,也有项目放在同一个应用,自发自收
  • 上边启动的两个窗口叫做服务端,客服端分为两个:一个是发送方,另一个是消费方

流程:点赞—>发送MQ—>消费MQ—>推送WS

本章内容都被注释掉了,只是学习,本应用可以用异步进行处理,没必要引入MQ

第十一章 知识库功能开发

11-1 章节介绍

统计数据收集与Echarts报表

  • 确认报表统计方案
    • 统计维度
    • 数据收集
    • 数据展示
  • 复杂SQL的编写、Echarts报表的使用

11-2 报表统计方案的探讨

  • 统计维度:

    • 统计数值:总阅读数、总点赞数、今日阅读数、今日点赞数、今日预计阅读数、今日预计阅读增长
    • 统计报表:30天阅读/点赞趋势图、文档阅读量排名(热门文章)、文章点赞量排名
  • 业务表统计: 所有报表数据都是从业务表直接获取

    • 优点: 实时性好(数据准确)
    • 缺点:对业务表性能有影响
  • 中间表统计:定时将业务表数据汇总到中间表,报表数据从中间表获取

    • 优点: 性能好、可实现多功能统计
    • 缺点: 工作量大、步骤多容易出错

11-3 电子书快照表设计

概念: 快照

11-4 电子书快照收集脚本脚本编写

  • 从业务表收集数据的SQL尽量简单,不要影响业务表性能

  • 快照分成两部分:

    • 总量:总阅读数、总点赞数
    • 增量:今日阅读数、今日点赞数

    凌晨1点,把所有的电子书都insert一条快照、后边每小时都是update后四个字段。

    字段添加唯一键(unique)不影响java代码

11-6 完成电子书快照功能

mybatis默认一次只能执行一个sql , 我们可以通过修改链接,增加一个配置项,让它可以执行多个sql。即在application.properties中的数据库地址中添加

spring.datasource.url=jdbc:mysql://ip/datatable?...&allowMultiQueries=true

11-7 首页统计数值功能开发

  • 统计数值:总阅读数、总点赞数、今日阅读数、今日点赞数、今日预计阅读数、今日预计阅读增长
  • 后端获取统计数值接口开发
  • 前端统计数值组件展示
  • 今天预计 = 今天到目前的阅读量 / (当前时间点占一天的百分比)

11-8 Echarts的集成与使用示例

11-9 30天趋势图功能开发

@JsonFormat(pattern="MM-dd", timezone = "GMT+8")
private Date date;

11-10 网站优化

加载完index.html后,vue相关的内容还没初始化,所以界面会显示

...
这块内容。加载完vue后,vue会将这块替换成页面内容。

第十二章 项目部署

12-1 章节介绍

  • 购买RDS配置生产数据库
  • 购买ECS,安装JDK、nginx
  • SpringBoot项目发布,多环境配置
  • Vue项目发布
  • 域名配置
  • ESC镜像

12-2 RDS购买与配置

ECS和RDS要在同一地域,这样可以用内网链接,否则只能用外网链接

12-3 ECS购买预配置

12-4 配置IDEA链接ECS

12-5 JDK的安装与配置

12-6 后端Java项目发布

  • 多环境的启动

    • 创建配置文件
    • 编辑configuration 在VM options:中 -Dspring.profiles.actice=prod
    • 启动成功,过滤profile这个单词,多环境常见
    • 修改打包完成后的名字:在pom的标签下添加${artifactId}
  • nohub可以让要执行的指令后台运行

  • deploy.sh 文件内的~,表示的是当前用户的目录

  • 启动deploy.sh会报错,是因为windows下的换行和linux的换行符不一致,需要在linux下vim deploy.sh 设置,按Esc 输入: set ff=unix 回车,这样就转换了换行符了,这样再保存,就不会再报错,执行:sh deploy.sh

  • 查看日志:tail -100f trace.log

  • 如果日志较多,可以查看日志的跟踪号: grep "跟踪号" trace.log即可过滤出需要的信息

12-7 nginx安装与配置

  • 安装命令:yum install nginx
  • 安装完成之后,修改/etc/nginx/nginx.conf的user改为root,改成使用root用户来启动nginx
  • 使用命令行 service nginx start 启动nginx服务
  • 查看是否启动成功,使用命令行:curl http://IP地址

12-8 前端Vue项目发布

  • 点击npm的build-prod生成静态文件在dist路径下,然后把静态文件上传到服务器:在root目录创建web目录 把文件放到web里(地址在web.conf有映射)
  • 编辑web.conf文件,其中 try_files $uri $uri/ /index.html; 解决页面刷新后空白的问题;server_name 修改成自己的ip
  • 放入到/etc/nginx/conf.d文件夹下,能引入的原因是因为在nginx.conf配置文件有引入该文件夹 include /etc/nginx/conf.d/*.conf
  • 执行 nginx -s reload

12-9 域名准备

12-10 nginx配置域名

mysql 8版本 中数据导入mysql5版本

1.通过文本编辑器搜索“ utf8mb4_0900_ai_ci” 批量替换成 “ utf8_general_ci ”

2.将“ utf8mb4 ” 批量替换成 " utf8 "

3.保存后,再次进行导入sql文件即可。

空文件

简介

Spring Boot + Vue3 前后端分离 实战 wiki 知识库系统 展开 收起
取消

发行版

暂无发行版

贡献者

全部

近期动态

不能加载更多了
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/huxiaoyisheng/wiki.git
git@gitee.com:huxiaoyisheng/wiki.git
huxiaoyisheng
wiki
wiki
master

搜索帮助