编程学习圈(一)项目初始化,通过标签搜索用户

移动端网站

用户可以快速找到志同道合(比如目标相同,发展阶段相同,当前状态相同)的朋友组队学习


需求分析

用户之间怎么寻找(匹配)?

通过标签。用户要有标签,标签要有分类,比如,学习方向:Java / c++。年级:大一 / 大二

有了标签,用户通过标签主动搜索

找到了伙伴,可以一起组队

  • 创建队伍
  • 邀请伙伴
  • 解散队伍
  • 根据标签查询队伍
  • 加入队伍

允许用户修改标签

首页推荐标签相似的伙伴


技术栈

前端:

  • Vue3
  • Vant3,移动端组件库
  • Vite2,打包工具
  • Axios,请求

后端

  • Spring Boot、Mybatis-Plus

  • Redis

  • MySQL

  • Swagger + Knife4j


项目计划(1)

前端初始化

设计和开发前端基础布局

数据库表设计

后端初始化

搭建增删改查

开发后端:通过标签搜索用户


前端初始化

用脚手架初始化项目

  • Vue CLI:https://cli.vuejs.org/zh/
  • Vite:https://cn.vitejs.dev/guide/(此项目选择vite)

整合移动端组件库 Vant。Vant 提供了按需引入,可以减少代码体积

https://vant-contrib.gitee.io/vant/v3/#/zh-CN/quickstart#fang-fa-er.-an-xu-yin-ru-zu-jian-yang-shi

所有命令

npm init vite@latest
npm install
npm i vant
npm i unplugin-vue-components -D

前端的基础布局

设计

NavBar 导航栏:

  • 当前页面名称
  • 搜索

内容展示

TabBar 标签栏:

  • 主页

  • 队伍

  • 我的

开发

很多页面要复用组件/样式,重复写很麻烦,不利于维护,所以抽象一个通用的布局(Layout)


在src目录下新建一个layouts目录,新建一个BasicLayout.vue文件,将文档中的代码复制到BasicLayout中,稍作修改

NavBar导航栏:https://vant-contrib.gitee.io/vant/v3/#/zh-CN/nav-bar#slots

Tabbar 标签栏:https://vant-contrib.gitee.io/vant/v3/#/zh-CN/tabbar

在src目录下新建一个pages目录,新建三个文件Index.vue、Team.vue、User.vue。

我们点击不同的 tab栏,active 会发生变化,v-model是双向绑定的, 通过判断 active 的值来加载相应的页面

<script setup lang="ts">
import {ref} from "vue";
import Index from "../pages/Index.vue"
import Team from "../pages/Team.vue";
import User from "../pages/User.vue";
const onClickLeft = () => alert('左');
const onClickRight = () => alert('右');

const active = ref('index');
const onChange = (active: string) => console.log(active);
</script>

<template>
    <van-nav-bar
        title="标题"
        left-arrow
        @click-left="onClickLeft"
        @click-right="onClickRight"
    >
        <template #right>
            <van-icon name="search" size="18"/>
        </template>
    </van-nav-bar>

    <div id="content">
        <template v-if="active === 'index'">
            <Index />
        </template>
        <template v-if="active === 'team'">
            <Team />
        </template>
        <template v-if="active === 'user'">
            <User />
        </template>
    </div>

    <van-tabbar v-model="active" @change="onChange">
        <van-tabbar-item icon="home-o" name="index">主页</van-tabbar-item>
        <van-tabbar-item icon="search" name="team">队伍</van-tabbar-item>
        <van-tabbar-item icon="friends-o" name="user">我的</van-tabbar-item>
    </van-tabbar>

</template>

<style scoped>
</style>

image-20240419202623283


数据库设计

标签表

分析标签有哪些,分类

性别:男、女

方向:Java、C++

正在学:Spring

阶段:小学、初中、高中、大一、大二、大三、大四、待业、已就业、研一、研二、研三

个人特质:乐观、焦虑、平静、单身、有对象

当前目标:考研、春招、秋招、社招、考公、转行、跳槽



id,bigint,主键

标签名,varchar,(非空,唯一索引)

上传标签的用户,userId,bigint(如果要根据 userId 查已上传标签的话,最好加上,普通索引)

父标签id,parentId,bigint(这里有点像emp表,员工的上司)

是否为父标签,isParent,tinyint(布尔不够灵活)

创建时间,createTime,datetime

更新时间,updateTime,datetime

是否删除,isDelete,tinyint(0,1)



我们的需求中,需要查询所有标签,及其父标签,也就是标签分组

查询所有标签,能得到父标签id,根据这个id进行分组,可以查出所有子标签


标签不应该写死,如果是固定写死的,直接在后端用枚举就可以了,没必要建表

用户的标签是会改变的,比如用户正在学spring,之后可能学redis。让用户可以自定义标签

CREATE TABLE `tag`  (
  `id` tinyint UNSIGNED NOT NULL AUTO_INCREMENT,
  `tag_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '标签名称',
  `user_id` bigint UNSIGNED NULL DEFAULT NULL COMMENT '用户id',
  `parent_id` bigint UNSIGNED NULL DEFAULT NULL COMMENT '父标签id',
  `is_parent` tinyint UNSIGNED NULL DEFAULT NULL COMMENT '0 - 不是,1 - 是父标签',
  `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  `is_deleted` tinyint UNSIGNED NOT NULL DEFAULT 0 COMMENT '是否删除(0否,1是)',
  PRIMARY KEY (`id`) USING BTREE,
  UNIQUE INDEX `uniIdx_tag_name`(`tag_name` ASC) USING BTREE,
  INDEX `idx_user_id`(`user_id` ASC) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

用户表

用户要有标签,两种方案

  • 方案一:直接在用户表加 tags 字段,[“java”,“男”],存json字符串

    优点:查询方便,不用新建关联表

    比如查询 id 为111用户的所有标签

    select tags from user where id = 111
    

    比如查询有”java“标签的用户,可以用like

    -- 查询"java"标签下的用户
    SELECT * FROM user WHERE tags LIKE '%java%'
    

    标签放在用户表也很合理,是用户的固有属性(用户画像)

    比如要查用户列表,要先查关系表,拿到这些用户的标签id,用标签id再去标签表里查询(很麻烦

  • 方案二:加一个关联表,记录用户和标签的关系

    关联表的应用场景:正查,反查。比如查询某个用户有多少标签,查询某个标签下有多少用户

    缺点:要多建一个表,多维护一个表

    根据标签查用户方便,但是如果查询某个用户的所用标签,就要关联查询

用户量不多的时候,方案一的 like 查询不会比方案二的关联查询慢多少

如果用户量增多,可以用缓存,标签不是经常变化的数据

企业大项目开发中,尽量减少关联查询,因为影响拓展性

最终选择方案一

CREATE TABLE `user`  (
  `id` tinyint UNSIGNED NOT NULL AUTO_INCREMENT,
  `user_account` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '账号',
  `user_password` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '密码',
  `nickname` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '昵称',
  `avatar_url` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '头像',
  `gender` tinyint UNSIGNED NULL DEFAULT NULL COMMENT '性别',
  `profile` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '个人简介',
  `email` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '邮箱',
  `phone` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '电话号码',
  `is_normal` tinyint UNSIGNED NOT NULL DEFAULT 1 COMMENT '用户状态(0不正常,1正常)',
  `user_role` tinyint UNSIGNED NOT NULL DEFAULT 0 COMMENT '用户角色(user/admin)',
  `tags` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '标签json列表',
  `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  `is_deleted` tinyint UNSIGNED NOT NULL DEFAULT 0 COMMENT '是否删除(0否,1是)',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 9 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

后端初始化

创建springboot项目,引入一些依赖。关于Java版本,boot3.0以上的必须用17。以下的用8

spring-boot-starter-web

spring-boot-starter-test

lombok

commons-lang3

gson

redis

mybatis-plus-spring-boot3-starter

mysql

搭建增删改查

application.yml 配置数据源,idea连接数据源,MybatisX生成代码,写增删改查接口

封装通用返回类
自定义错误码
封装返回工具类
自定义异常
封装全局异常处理



需求:注册

  1. 接收数据,UserRegisterRequest:用户名,密码,确认密码(重复输入密码)
  2. 校验数据
    1. dto对象非空,里面的参数非空
    2. 账号要大于4位,小于12位
    3. 密码要大于8位,小于20位
    4. 账号不包含特殊字符
    5. 密码和确认密码相同
    6. 账号在数据库中不存在
  3. 存储数据,返回注册结果
    1. 密码加密
    2. 插入数据库,返回用户id


需求:登录

  1. 接收数据,UserLoginRequest:用户名,密码
  2. 校验数据
    1. dto对象非空,里面的参数非空
    2. 账号要大于4位,小于12位
    3. 密码要大于8位,小于20位
    4. 账号不包含特殊字符
    5. 密码和确认密码相同
    6. 账号在数据库中不存在
  3. 查询数据,返回登录结果
    1. 密码加密
    2. 根据账号和加密之后的密码查询用户是否存在
    3. 用户信息脱敏,隐藏敏感信息,防止数据库字段泄露
    4. 保存用户登录态(session)
    5. 返回脱敏后的用户信息

需求:获取当前登录用户和退出登录

getAttribute

removeAttribute


需求:通过标签搜索用户


方式一:SQL查询

  1. 接收数据,用户选择多个标签进行搜索,List,标签列表

  2. 校验数据,List集合是否为null,或者是一个空的集合

  3. 查询数据

    -- 比如查询标签有 java 和 男 的用户
    select * from user where tags like%”java“%and tags like%%;
    

    使用mybatis-plus条件构造器,

    循环标签列表,构建查询条件,.like()

  4. 返回数据,把查询到的多个用户,进行脱敏返回,List

方式二:内存查询

  1. 方法的参数、返回值不变

  2. 校验数据,List集合是否为null,或者是一个空的集合

  3. 查询数据。先把所有用户查出来。在内存中判断是否有包含要求的标签

    1. 遍历所有用户。拿到每个用户的tags的值(json 字符串),

    2. 判断该字符串是否null,排除标签为null的用户

    3. 然后 json 格式字符串转成 Set,一个集合临时存一个用户的所有标签

      这里用List还是Set?选择Set。[“java”,“男”,“大一”]—>Set

      序列化库选择:

      gson,谷歌,√

      fastjson,阿里

      jackson

      kryo

    4. 遍历标签名列表,如果该用户的所有标签,包含,用户传过来的所有标签,就符合条件

      比如:A{1,2,3,4} B{1,3} B是A的子集,就符合条件

  4. 返回数据。对于符合条件的用户,通过 map 方法将其映射为脱敏用户,然后通过 collect 方法收集到一个新的列表中,返回。

测试哪种方式更快

有bug,要找java标签的用户,但java和javascript标签的用户都被找出来了。在模糊查询的时候,标签外面加双引号

userQueryWrapper.like("tags", "\"" + tagName + "\"");

利用 System.currentTimeMillis() 测试时间,发现差不多,注意数据库第一次连接是很耗时的。目前因为数据量太少,测试结果没有参考性,之后再测试。

两种方式如何选择?

  • 可以测试查询时间和参数的关系,比如用户选择的标签数量达到多少的时候,两种方式谁更快

  • 内存查询可以通过并发进一步优化,所以如果能保证数据库连接和内存足够,可以并发同时查询,谁先返回用谁

  • SQL查询和内存查询相结合,比如先用SQL过滤掉部分tag

  • 29
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值