伙伴匹配系统(四)


开发笔记

1.本期目标计划

  1. 页面和功能开发
    1. 搜索页面
    2. 用户信息
    3. 用户修改页面
  2. 改造用户中心,把单机登录改为分布式 session 登录
  3. 标签的整理、细节的优化

2.前端页面跳转传值

  1. query => url searchParams,url 后附加参数,传递的值长度有限
  2. vuex(全局状态管理),搜索页将关键词塞到状态中,搜索结果页从状态取值

3.Session 共享

种 session 的时候注意范围,cookie.domain 比如两个域名: aaa.yupi.com bbb.yupi.com 如果要共享 cookie,可以种一个更高层的公共域名,比如 yupi.com

4.为什么服务器 A 登录后,请求发到服务器 B,不认识该用户?

用户在 A 登录,所以 session(用户登录信息)存在了 A 上 结果请求 B 时,B 没有用户信息,所以不认识。 image.png 解决方案:共享存储 ,而不是把数据放到单台服务器的内存中 image.png 如何共享存储?

  1. Redis(基于内存的 K / V 数据库)此处选择 Redis,因为用户信息读取 / 是否登录的判断极其频繁 ,Redis 基于内存,读写性能很高,简单的数据单机 qps 5w - 10w
  2. MySQL
  3. 文件服务器 ceph

Session 共享实现

5. 安装 Redis

官网:https://redis.io/ windows 下载: Redis 5.0.14 下载: 链接:https://pan.baidu.com/s/1XcsAIrdeesQAyQU2lE3cOg 提取码:vkoi redis 管理工具 quick redis:https://quick123.net/

  1. 引入 redis,能够操作 redis:
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-redis -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    <version>2.6.4</version>
</dependency>
  1. 引入 spring-session 和 redis 的整合,使得自动将 session 存储到 redis 中:
<!-- https://mvnrepository.com/artifact/org.springframework.session/spring-session-data-redis -->
<dependency>
    <groupId>org.springframework.session</groupId>
    <artifactId>spring-session-data-redis</artifactId>
    <version>2.6.3</version>
</dependency>
  1. 修改 spring-session 存储配置 spring.session.store-type默认是 none,表示存储在单台服务器store-type: redis,表示从 redis 读写 session

JWT 的优缺点:https://zhuanlan.zhihu.com/p/108999941

6.todo 待优化

前端:动态展示页面标题、微调格式

一、页面和功能开发

1.搜索页面

(1).新建UserResultPage.vue,创建页面,同时别忘了在路由里引入这个页面

(2).优化SearchPage页面,添加一个搜索按钮,来实现点击提交选中的标签到UserResultPage页面

  <div style="padding: 16px">
    <van-button block type="primary" @click="doSearchResult">搜索</van-button>
  </div>

import {useRouter} from 'vue-router';
const router = useRouter();

const doSearchResult = () => {
  router.push({
    path: '/user/list',
    query: {
      tags: activeIds.value
    }
  })
}

ps:千万别忘了引入useRouter和定义router常量,我这边没引入但是不报错(比较迷惑人) 显示如下: image.png 点击搜索按钮,可观察到路径跳转会带有参数 image.png

(3).完善UserResultPage页面,来显示用户的信息,依旧从vant的组件库寻找合适的组件

因为用户信息中要包括个人简介,而我们用户中心的数据中并未包含这一字段,现在去添加 具体操作就不演示了,修改表之后DDL语句改变如下 image.png 修改数据库之后还并未对后端对应数据库的内容做相应的增加。偷个懒,等下次修改后端时再修改 进入前端的用户对象的规范,也添加这一字段 image.png 现在正式进入修改页面环节,在vant文档中复制商品卡片组件 image.png image.png 复制到UserResultPage页面并修改如下:

<template>
  <van-card
      :desc="mockUser.profile"
      :title="`${mockUser.userName} (${mockUser.planetCode})`"
      :thumb="mockUser.avatar"
  >
    <template #tags>
      <van-tag plain type="danger" v-for="tag in user.tags" style="margin-right: 8px; margin-top: 8px" >
        {{tag}}
      </van-tag>
    </template>
    <template #footer>
      <van-button size="mini">联系我</van-button>
    </template>
  </van-card>
</template>
<script setup>
import {useRoute} from "vue-router";
import {ref} from "vue";

const route = useRoute();
const {tags} = route.query;

const mockUser = ref({
  id: 1,
  userName: 'tianber',
  userAccount: 'tianber',
  profile: 'tianber的个人简介,全栈工程师,加油加油加油!!!!!',
  gender: '男',
  phone: '234234',
  email: '324242342@qq.com',
  planetCode: '981',
  avatar: 'https://img1.baidu.com/it/u=1645832847,2375824523&fm=253&fmt=auto&app=138&f=JPEG?w=480&h=480',
  createTime: new Date().toDateString()
})
</script>

<style scoped>

</style>

启动,在search路径下选择标签点击搜索,成功跳转到UserResultPage页面,显示如下: image.png

(4).前端页面已开发完成,现在开发后端,和前端进行对接

对接后端接口,在controller层编写代码

//根据标签查询用户
@GetMapping("/search/tags")
public BaseResponse<List<User>> searchUsersByTags(@RequestParam(required = false) List<String> tagNameList){
    if (CollectionUtils.isEmpty(tagNameList)){
        return ResultUtils.error(ErrorCode.PARAMS_ERROR);
    }
    List<User> userList = userService.searchUsersByTags(tagNameList);
    return ResultUtils.success(userList);
}

debug启动项目,去knife4j接口操作,确保已经登录,传两个参数,回后端看看是否获取参数 image.pngimage.png 带空参数请求 image.png 是我们自己写的报错(请求参数错误)成功!现在回前端对接后端

(5).开发前端接口

首先要在前端引入axios 终端输入

yarn add axios

在src目录下新建plugins包和myAxios.ts,复制axios文档中如下代码,并修改整理如下: image.pngimage.png

import axios from "axios";

// Set config defaults when creating the instance
const myAxios = axios.create({
    baseURL: 'http://localhost:8080/api',
});

// 添加请求拦截器
myAxios.interceptors.request.use(function (config) {
    console.log("我要发送请求了,",config)
    return config;
}, function (error) {
    // 对请求错误做些什么
    return Promise.reject(error);
});

// 添加响应拦截器
myAxios.interceptors.response.use(function (response) {
    // 对响应数据做点什么
    console.log("我收到你的响应了,",response)
    return response;
}, function (error) {
    // 对响应错误做点什么
    return Promise.reject(error);
});

export default myAxios;

在userSearchPage页面新增如下代码用来页面挂载

onMounted(() => {
    // Make a request for a user with a given ID
    myAxios.get('/user/search/tags',{
        params: {
            tagNameList: tags
        }
    })
    .then(function (response) {
        console.log('/user/search/tags succeed',response);
        Toast.success('请求成功');
    })
    .catch(function (error) {
        console.error('/user/search/tags error',error);
        Toast.fail('请求失败');
    })
})

image.png 跨域了,我们在后端解决这个问题 image.png 踩坑处:这边一开始不要带有参数请求,不然会一直显示跨域(也不知道是什么原因造成的,希望有大佬解答) 再次运行(刷新) 发现路径后面带有的参数不合规范,带有[]。我们这边可以引入qs去用于参数序列化,处理发送请求的参数 image.png image.png **踩坑处:这边又踩了大坑,可能由于axios的版本问题,会报错:Uncaught (in promise) ** {message: ‘options must be an object’, name: ‘AxiosError’, code: ‘ERR_BAD_OPTION_VALUE’, stack: ‘AxiosError: options must be an object\n at Objec…ji.com/static/js/chunk-libs.c096185b.js:42:41367)’}…………. axios版本如果是比较新的,要按照上图所示写,再次刷新成功获取 image.png debug启动,打个断点,再次刷新,可以在后端观察到成功获取前端的参数 image.png 现在要将数据库对接上,不再使用假数据,首先往数据库里添加假数据(尽量详细) 修改onMounted,修改如下

const userList = ref([]);//存放用户列表

onMounted ( async ()=>{     //异步调用
  const userListData = await myAxios.get('/user/search/tags',{
    params: {
      tagNameList: tags
    },
    paramsSerializer: {
      serialize: function(params){
        // return qs.stringify(params,{arrayFormat:'repeat'})
        return qs.stringify(params,{indices: false})
      }
    }
  })
      .then(function (response){
        console.log('/user/search/tags succeed',response);
        Toast.success('请求成功!');
        return response.data?.data;  //返回数据  ?.可选链操作符,避免数据为null或undefined时报错
      })
      .catch(function (error){
        console.error('/user/search/tags error',error);
        Toast.fail('请求失败!');
      })
  // console.log(userListData)
  if(userListData){
    userListData.forEach(user => {
      if(user.tags){
        user.tags = JSON.parse(user.tags)
      }
    })
    userList.value = userListData;
  }
})

image.png forEach函数会显示没有这个函数,写成userListData?.data.forEach编译不会报错但运行会报错,所以不要管编译报错,照着鱼皮的写。 踩坑处:注意数据库里的逻辑删除是不是1(所以数据尽量多写点),我这边由于没仔细看,“男”标签就两个,一个是被逻辑删除了,结果只显示一个,还以为循环出错,查了好久。。。 显示如下: image.pngimage.png

二、改造用户中心,把单机登录改为分布式 session 登录

(1).安装redis和管理工具quickredis

Redis 5.0.14 下载: 链接:https://pan.baidu.com/s/1XcsAIrdeesQAyQU2lE3cOg 提取码:vkoi redis 管理工具 quick redis:https://quick123.net/

(2).在springboot里引入redis,能够操作redis

<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-redis -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    <version>2.6.4</version>
</dependency>

(3).引入 spring-session 和 redis 的整合,使得自动将 session 存储到 redis 中:

<!-- https://mvnrepository.com/artifact/org.springframework.session/spring-session-data-redis -->
<dependency>
    <groupId>org.springframework.session</groupId>
    <artifactId>spring-session-data-redis</artifactId>
    <version>2.6.3</version>
</dependency>

修改 spring-session 存储配置 spring.session.store-type 默认是 none,表示存储在单台服务器 store-type: redis,表示从 redis 读写 session ,配置如下: image.png

(4).测试session共享

为了模拟多服务器,我们需要打包项目,在另一个端口启动,这里是8081 先打包,后在target目录下打开终端运行下面的代码

java -jar .\user-center-backend-0.0.1-SNAPSHOT.jar --server.port=8081

运行,成功启动8080和8081端口的knife4j接口进行操作,先在8080端口登录并获取当前登录用户信息 image.png 在8081端口查看当前登录用户信息,也能查询到 image.png 使用QuickRedis查看session是否存入redis中 image.png

over !!!!!!!!!!!


Author: qwq小小舒
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint policy. If reproduced, please indicate source qwq小小舒 !
  TOC