1624 字
8 分钟
伙伴匹配项目总结
2023-08-17

第一部分#

  1. 使用vite进行项目的初始化,node版本需要与其匹配,可以下载nvm来进行node版本的管理。

    yarn create vite
    

    image-20240124122542894

    image-20240124122706606

  2. 使用vant进行前端页面的布局不限于导航栏,tab栏的搭建。

    image-20240124122904208

  3. 设计数据库表,分析需要哪些字段。

    image-20240124125441196

  4. 用户表的添加tag字段

    1. 关联表:
      1. 优点:查询灵活,可以正查,反查
      2. 缺点:需要多建一个表,多维护一个表
      3. 尽量减少关联查询。
    2. 往用户表中添加json字符串来补充tag字段。
      1. 优点:查询方便,不用新建表,标签是固有属性节省开发成本。
      2. 如果性能低可以用缓存。
    3. 具体情况根据需求具体分析,选择合适的。
  5. 开发后端接口

    • 搜索标签

      1. 允许用户传入多个标签,多个标签都存在才搜索出来,and。代码举例:like ‘%Java%’ and like ‘%C++%’。
      2. 允许用户传入多个标签,有任何一个标签存在就能搜索出来 or。代码举例:like ‘%Java%’ or like ‘%C++%’。
    • 两种方式

      1. SQL查询(实现简单,可以通过拆分查询进一步优化)
      2. 内存查询(灵活,可以通过并发进一步优化)
    • 选择场景

      1. 如果参数可以分析,根据用户的参数去选择查询方式,比如标签数。
      2. 如果参数不可分析,并且数据库连接足够、内存空间足够,可以并发同时查询,谁先返回用谁。
      3. 还可以SQL查询与内存计算相结合,比如先用SQL过滤掉部分Tag。
    • 并行流和串行流

      1. 串行流 Stream ,并行流 parallelStream
      2. 并行流parallelStream缺点:parallelStream使用公共线程池,如果某一个方法耗时特别长,那么慢慢的整个线程池会都交给该方法,就没有多余的线程分配给其他任务了。
    • tag的判空

      • Optional可选类,可以减少判断的分支

      • tempTagNameSet = Optional.ofNullable(tempTagNameSet).orElse(new HashSet<>());
        
  6. 解析JSON字符串

    1. 名词解释

      • 序列化:java对象转换成json
      • 反序列化:把json转为java对象
    2. java和json序列化库举例:

      • 标签的string转json,利用gson库实现

      • Set<String> tempTagNameSet = gson.fromJson(tagsStr, new TypeToken<Set<User>>(){}.getType());
        

第二部分#

  1. 前端整合路由

    • vue-router引入

      官方文档

    • main.ts

      import {createApp} from 'vue'
      import {Button, Icon, NavBar, Tabbar, TabbarItem} from 'vant';
      import App from './App.vue'
      import * as VueRouter from 'vue-router'
      import routes from "./config/route.ts";
      
      const app = createApp(App);
      
      app.use(Button);
      app.use(NavBar);
      app.use(Tabbar);
      app.use(TabbarItem);
      app.use(Icon)
      
      const router = VueRouter.createRouter({
          history: VueRouter.createWebHashHistory(),
          routes,
      })
      
      app.use(router)
      
      app.mount('#app')
      
    • route.ts

      import Index from "../pages/Index.vue";
      import Team from "../pages/Team.vue";
      
      const routes = [
          { path: '/', component: Index },
          { path: '/team', component: Team },
          { path: '/user', component: Team },
      ]
      
      export default routes;
      
    • BasicLayout.vue

      <template>
        <van-nav-bar title="标题" left-text="返回" left-arrow>
          <template #right>
            <van-icon name="search" size="18"/>
          </template>
        </van-nav-bar>
        <div id="content">
          <router-view/>
        </div>
        <van-tabbar route @change="onChange">
          <van-tabbar-item to="/" icon="home-o" name="index">主页</van-tabbar-item>
          <van-tabbar-item to="/team" icon="search" name="team">队伍</van-tabbar-item>
          <van-tabbar-item to="/user" icon="friends-o" name="user">个人</van-tabbar-item>
        </van-tabbar>
      </template>
      
  2. 搜索页面开发

    • flat和flatmap

      JavaScript 数组展平方法: flat() 和 flatMap()

    • 搜索的方法,展平tagList标签列表(逻辑有问题,仅用于相关方法学习)

      const onSearch = () => {
        activeIds.value = tagList
          .flatMap((parentTag) => parentTag.children)
          .filter((item) => item.text.contain(searchText.value));
      };
      
    • 同上,正确方法

      //标签列表
      const originTagList = [
        {
          text: "浙江",
          children: [
            { text: "杭州", id: "杭州" },
            { text: "温州", id: "温州" },
          ],
        },
        {
          text: "江苏",
          children: [
            { text: "南京", id: "南京" },
            { text: "无锡", id: "无锡" },
            { text: "徐州", id: "徐州" },
          ],
        },
      ];
      
      let tagList = ref(originTagList);
      
      const onSearch = () => {
        tagList.value = originTagList.map((parentTag) => {
          const tempChildren = [...parentTag.children];
          const tempParentTag = { ...parentTag };
          tempParentTag.children = tempChildren.filter((item) =>
            item.text.includes(searchText.value),
          );
          return tempParentTag;
        });
      };
      //清空搜索框
      const onCancel = () => {
        searchText.value = "";
        tagList.value = originTagList;
      };
      
  3. 个人信息页面

    • 定义user的用户类型

      /**
       * 用户类型
       */
      export type userType = {
        id: number;
        username: string;
        userAccount: string;
        avatarUrl?: string;
        gender: number;
        phone: string;
        email: string;
        userStatus: number;
        userRole: number;
        fantasyCode: string;
        tags: string[];
        createTime: Date;
      };
      
    • 编写前端页面

  4. 用户编辑页面

    • 定义一个传三个参数的点击事件

      @click="toEdit('avatarUrl', '头像', user.avatarUrl)"
      
    • 具体方法

      const toEdit = (editKey: string, editName: string, currentValue: string) => {
        router.push({
          path: "/user/edit",
          query: {
            editKey,
            editName,
            currentValue,
          },
        });
      };
      
    • 用户编辑页面定义传递过来的参数

      const route = useRoute();
      const editUser = ref({
        editKey: route.query.editKey,
        editName: route.query.editName,
        currentValue: route.query.currentValue,
      });
      
    • 采用模板动态展示

      <van-form @submit="onSubmit">
        <van-cell-group inset>
          <van-field
            v-model="editUser.currentValue"
            :name="editUser.editKey"
            :label="editUser.editName"
            :placeholder="`请输入${editUser.editName}`"
          />
        </van-cell-group>
        <div style="margin: 16px">
          <van-button round block type="primary" native-type="submit">
            提交
          </van-button>
        </div>
      </van-form>
      
  5. 页面展示

    • 个人信息

      image-20240602172541352

    • 修改信息

      image-20240602172622972

    • 搜索

      image-20240602172722742

第三部分#

  1. 后端整合接口文档

    • 什么是接口文档?

      • 写接口信息的文档,每条信息包括
        • 请求参数
        • 相应参数
          • 错误码
        • 接口地址
        • 接口名称
        • 请求类型
        • 请求格式
        • 备注
      • 谁用接口文档?一般是后端或者负责人提供,后端前端都要使用。
    • 为什么需要接口文档

      • 有个书面内容(背书或者归档),便于大家参考和查阅,便于沉淀和维护,拒绝口口相传
      • 接口文档便于前端和后端开发对接,前后端联调介质。后端=>接口文档<=前端
      • 好的接口文档支持在线调试、在线测试,可以作为工具提高我们的测试开发效率。
    • 如何做接口文档

      • 手写(比如腾讯文档,Markdown笔记)
      • 自动化接口文档生成:自动根据项目代码生成完整的文档或在线调试的网页。Swagger、Postman(侧重接口管理);apifox、apipost、eolink(国产)
    • Swagger原理

      1. 自定义Swagger配置类

      2. 定义需要生成接口文档的代码位置(Controller)

        注:线上环境不要把接口暴露出去。

      3. 可以通过在controller方法上添加@Api、@ApiImplicitParam(name=“name”,value=“姓名”,require=true)、@ApiOperation(value=“向客人问好”)等注解来自定义生成接口描述信息。

  2. 看上了网页信息怎么抓取到

    • 分析网站是如何获取这些信息的,哪个接口?

      curl "https://api.zsxq.com/v2/hashtags/28855251481421/topics?count=20" ^
        -H "accept: application/json, text/plain, */*" ^
        -H "accept-language: zh-CN,zh;q=0.9" ^
        -H "origin: https://wx.zsxq.com" ^
        -H "priority: u=1, i" ^
        -H "referer: https://wx.zsxq.com/" ^
        -H ^"sec-ch-ua: ^\^"Google Chrome^\^";v=^\^"125^\^", ^\^"Chromium^\^";v=^\^"125^\^", ^\^"Not.A/Brand^\^";v=^\^"24^\^"^" ^
        -H "sec-ch-ua-mobile: ?0" ^
        -H ^"sec-ch-ua-platform: ^\^"Windows^\^"^" ^
        -H "sec-fetch-dest: empty" ^
        -H "sec-fetch-mode: cors" ^
        -H "sec-fetch-site: same-site" ^
        -H "user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36" ^
      
    • 用程序去调用接口(JAVA/Python)。

    • 处理(清洗)一下数据,就可以写入数据库。

  3. 流程

    • 从excal导入用户数据,判重。easy excal
    • 抓取写了自我介绍的同学的信息,提取出用户昵称,用户唯一id自我介绍信息‘。
    • 从自我介绍中提取信息,写入数据库中。
  4. EasyExcal

    两种读取方式

    • 确定表头:建立对象和表格形成映射。
    • 不确定表头:每一行的数据映射为Map<String,Object>
伙伴匹配项目总结
https://lym0518.cn/posts/partnermatching/
作者
TeFantasy
发布于
2023-08-17
许可协议
CC BY-NC-SA 4.0