柚见十三期(优化)

前端优化

加载匹配功能与加载骨架特效

骨架屏 : vant-skeleton

index.vue中

/**  * 加载数据  */  
const loadData = async () => {  let userListData;  loading.value = true;  //心动模式  if (isMatchMode.value){  const  num = 10;//推荐人数  userListData = await myAxios.get('user/match',{  params: {  num,  },  })  .then(function (response) {  console.log('/user/match succeed',response);  return response?.data;  })  .catch(function (error) {  console.log('/user/match error',error);  showFailToast('请求失败');  });  }  else {  //不开启推荐模式,默认全部查询  //普通用户使用分页查询todo 并没有实现  userListData = await  myAxios.get('/user/recommend',{  params: {  pageSize: 8,  pageNum: 1,  },  })  .then(function (response) {  console.log('/user/recommend succeed', response);  return response?.data?.records;  })  .catch(function (error) {  console.log('/user/recommends error',error);  showFailToast('请求失败');  });  }  if (userListData){  userListData.forEach((user: userType) =>{  if (user.tags){  user.tags = JSON.parse(user.tags);  }  })  userList.value = userListData;  }  loading.value = false;  
}  //`watchEffect`函数用于在响应式数据发生变化时执行副作用(side effect)操作。在这个例子中,当数据发生变化时,会调用`loadData()`函数来加载数据。watchEffect(() =>{  loadData();  
})

在这里插入图片描述

前端导航标题

router :控制路由跳转
route: 获取路由信息

解决:使用 router.beforeEach,根据要跳转页面的 url 路径 匹配 config/routes 配置的 title 字段

在这里插入图片描述

在这里插入图片描述

//标题  
const DEFAULT_TITLE='柚见'  
const title=ref(DEFAULT_TITLE);  
/**  * 根据路由切换标题  */  
router.beforeEach((to,from)=>{  const toPath=to.path;  const route=routes.find((r)=>{  return toPath==r.path;  })  title.value=route.title ?? DEFAULT_TITLE;  })

重定向到登录页

根据后端返回的未登录的错误码,重定向

全局响应拦截器中修改
在这里插入图片描述

发现页面一直不能正常跳转
切换hash模式
不同的历史模式 | Vue Router (vuejs.org)
在这里插入图片描述

登录成功后自动跳转回之前页面

在这里插入图片描述

添加创建队伍按钮

添加样式

在这里插入图片描述

引入样式

在这里插入图片描述

在这里插入图片描述

换成vant库中的+
在这里插入图片描述

<!--  创建队伍按钮-->  <van-button type="primary" @click="doJoinTeam" class="add-button" icon="plus">  </van-button>

队伍操作按钮权限控制

更新队伍:仅创建人可见

 v-if="team.userId === currentUser?.id"

解散队伍:仅创建人可见

 v-if="team.userId === currentUser?.id"

加入队伍: 仅非队伍创建人、且未加入队伍的人可见
退出队伍:创建人不可见,仅已加入队伍的人可见

后端修改

仅加入队伍和创建队伍的人能看到队伍操作按钮(listTeam 接口要能获取我加入的队伍状态) ✔

方案 1:前端查询我加入了哪些队伍列表,然后判断每个队伍 id 是否在列表中(前端要多发一次请求)

方案 2:在后端去做上述事情(推荐)

加密队伍与公开队伍的展示

前端修改

Tab 标签页 - Vant 4 (gitee.io)

<van-tabs v-model:active="activeName">  <van-tab title="公开队伍" name="public"></van-tab>  <van-tab title="加密队伍" name="secret"></van-tab>  
</van-tabs>

在这里插入图片描述

const onTabChange=(name)=>{  if(name==='public')  {  //查询公开队伍  listTeam()  }else if(name === 'secret'){  //查询加密队伍  listTeam(' ',2);  }  console.log(name)  }
//只有管理员和本人才能查看非私有的房间  
if (!isManager && !statusEnum.equals(TeamStatusEnum.PUBLIC) && !statusEnum.equals(TeamStatusEnum.SECRET)) {  
throw new BusinessException(ErrorCode.NO_AUTH);  
}

点击加入加密队伍后,弹窗密码跳出

在这里插入图片描述

<script setup lang="ts">  
import {TeamType} from "../models/team";  
import {teamStatusEnum} from "../constants/team.ts";  
import myAxios from "../plungins/myAxios.js";  
import {showFailToast, showSuccessToast} from "vant";  
import {onMounted, ref} from "vue";  
import {getCurrentUser} from "../services/user.ts";  
import {useRouter} from "vue-router";  const  router=useRouter();  
const password=ref('');  
//加入该队伍的id  
const joinId=ref(0)  
const showPwd=ref(false)  
interface TeamCardListProps{  teamList: TeamType[];  
}  const props= withDefaults(defineProps<TeamCardListProps>(),{  //@ts-ignore  teamList: [] as TeamType[]  
});  //获得当前用户  
const currentUser=ref()  
onMounted(async ()=>{  const res=await getCurrentUser()  currentUser.value=res  
})  //加入队伍之前---------------------------------------------------------------------  
const preJoinTeam=(team : TeamType)=>{  joinId.value=team.id;  if(team.status === 2)  {  //打开加密弹窗  showPwd.value=true;  }  else if(team.status === 0)  {  doJoinTeam();  }  
}  
const doJoinCancel = () => {  joinId.value = 0;  password.value = '';  
}  
//点击公开队伍,加入队伍------------------------------------------------------------  
const doJoinTeam=async()=>{  if(!joinId.value)  {  return ;  }  const res=await myAxios.post('/team/join',{  teamId:joinId.value,  password:password.value  })  if(res?.code===0){  showSuccessToast("加入成功")  doJoinCancel();  }else{  showFailToast("加入失败\n"+(res.description ? `${res.description}`: ''))  }  
}  
//跳转到更新页---------------------------------------------------------------------  
const doUpdateTeam=({id}: { id: any })=>{  router.push({  path:'/team/update',  query:{  id  }  })  
}  
//点击退出队伍-------------------------------------------------------------------  
const doQuitTeam=async (id)=>{  const res=await myAxios.post('/team/quit',{  teamId:id  })  if(res?.code===0){  showSuccessToast("操作成功")  }else{  showFailToast("操作失败\n"+(res.description ? `${res.description}`: ''))  }  }  
//点击解散队伍-------------------------------------------------------------------------  
const doDeleteTeam=async ({id}: { id: any })=>{  const res=await myAxios.post('/team/delete',{  id  })  if(res?.code===0){  showSuccessToast("操作成功")  }else{  showFailToast("操作失败\n"+(res.description ? `${res.description}`: ''))  }  
}  </script>

已加入队伍人数

<div>  {{ `已加入人数 : ${team.hasJoinNum} / ` + team.maxNum }}  
</div>

后端优化

队伍按钮权限控制

在这里插入图片描述

在这里插入图片描述

@GetMapping("/list")  
public BaseResponse<List<TeamUserVO>> listTeams(TeamQuery teamQuery,HttpServletRequest request) {  if (teamQuery == null) {  
throw new BusinessException(ErrorCode.PARAMS_ERROR);  
}  
boolean isManager = userService.isManager(request);  
User loginUser = userService.getLoginUser(request);  // 1、查询队伍列表  
List<TeamUserVO> teamList = teamService.listTeams(teamQuery, isManager);  
final List<Integer> teamIdList = teamList.stream().map(TeamUserVO::getId).collect(Collectors.toList());  
// 2、判断当前用户是否已加入队伍  
QueryWrapper<UserTeam> userTeamQueryWrapper = new QueryWrapper<>();  
try {  
userTeamQueryWrapper.eq("userId", loginUser.getId());  
userTeamQueryWrapper.in("teamId", teamIdList);  
List<UserTeam> userTeamList = userTeamService.list(userTeamQueryWrapper);  
// 已加入的队伍 id 集合  
Set<Integer> hasJoinTeamIdSet = userTeamList.stream().map(UserTeam::getTeamId).collect(Collectors.toSet());  
teamList.forEach(team -> {  
boolean hasJoin = hasJoinTeamIdSet.contains(team.getId());  
team.setHasJoin(hasJoin);  
});  
} catch (Exception e) {}  return ResultUtils.success(teamList);  
}

查询已加入队伍的人数

//3.查询已加入队伍数  
QueryWrapper<UserTeam> userTeamQueryWrapper2 = new QueryWrapper<>();  
userTeamQueryWrapper.in("teamId", teamIdList);  
List<UserTeam> userTeamList2 = userTeamService.list(userTeamQueryWrapper2);  //队伍id => userId集合  
Map<Integer, List<UserTeam>> teamIdUserTeamList = userTeamList2.stream().collect(Collectors.groupingBy(UserTeam::getTeamId));  
teamList.forEach(team ->  
team.setHasJoinNum(teamIdUserTeamList.getOrDefault(team.getId(), new ArrayList<>()).size())  
);

多次快速点击加入队伍,重复加入队伍

只要我们点的足够快,就可以在同一时间内往数据库插入多条同样的数据,所以这里我们使用分布式锁(推荐)使用两把锁,一把锁锁队伍,一把锁锁用户(实现较难,不推荐)

之前的代码

//该用户已加入的队伍数量不能超过5个  
int userId = loginUser.getId();  
// 只有一个线程能获取到锁  
RLock lock = redissonClient.getLock("youjian:join_team");  QueryWrapper<UserTeam> userTeamQueryWrapper = new QueryWrapper<>();  
userTeamQueryWrapper.eq("userId",userId);  
int hasJoinNum = (int) userTeamService.count(userTeamQueryWrapper);  
if (hasJoinNum >= 5){  
throw new BusinessException(ErrorCode.PARAMS_ERROR,"最多创建和加入5个队伍");  
}  //不能重复加入已加入的队伍  
userTeamQueryWrapper = new QueryWrapper<>();  
userTeamQueryWrapper.eq("userId",userId);  
userTeamQueryWrapper.eq("teamId",teamId);  
int hasUserJoinTeam = (int) userTeamService.count(userTeamQueryWrapper);  
if (hasUserJoinTeam > 0){  
throw new BusinessException(ErrorCode.PARAMS_ERROR,"用户已加入该队伍");  
}  
//不能加入已满队伍  
userTeamQueryWrapper = new QueryWrapper<>();  
userTeamQueryWrapper.eq("teamId",teamId);  
int teamHasJoinNum = (int) userTeamService.count(userTeamQueryWrapper);  
if (teamHasJoinNum >= team.getMaxNum()){  
throw new BusinessException(ErrorCode.PARAMS_ERROR,"队伍已满");  
}  //3.加入,修改队伍信息  
UserTeam userTeam = new UserTeam();  
userTeam.setUserId(userId);  
userTeam.setTeamId(teamId);  
userTeam.setJoinTime(new Date());  
return userTeamService.save(userTeam);

修改后

@Resource  
private RedissonClient redissonClient;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/543473.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

3.2_5 内存映射文件

文章目录 3.2_5 内存映射文件&#xff08;一&#xff09;传统的文件访问方式&#xff08;二&#xff09;内存映射文件&#xff08;Memory-Mapped Files&#xff09; 总结 3.2_5 内存映射文件 &#xff08;一&#xff09;传统的文件访问方式 磁盘的存储是以块为单位的&#xff0…

数据分析-Pandas如何观测数据的中心趋势度

数据分析-Pandas如何观测数据的中心趋势度 数据分析和处理中&#xff0c;难免会遇到各种数据&#xff0c;那么数据呈现怎样的规律呢&#xff1f;不管金融数据&#xff0c;风控数据&#xff0c;营销数据等等&#xff0c;莫不如此。如何通过图示展示数据的规律&#xff1f; 数据…

公众号留言功能恢复了,你的开通了吗?

了解公众号的人都知道&#xff0c;腾讯在2018年3月宣布暂停新注册公众号的留言功能&#xff0c;这之后注册的公众号都不具备留言功能。 这成了很多号主运营人的一块心病&#xff0c;也包括我。 没有留言&#xff0c;就好似一个人玩单机游戏&#xff0c;无法与读者互动&#xff…

柚见第十二期(随机匹配)

随机匹配 目的 为了帮大家更快地发现和自己兴趣相同的朋友 问题 匹配 1 个还是匹配多个&#xff1f; 答&#xff1a;匹配多个&#xff0c;并且按照匹配的相似度从高到低排序 怎么匹配&#xff1f;&#xff08;根据什么匹配&#xff09; 答&#xff1a;标签 tags 还可以根据 us…

经典机器学习模型(一)感知机模型

经典机器学习模型(一)感知机模型 感知机可以说是一个相当重要的机器学习基础模型&#xff0c;是神经网络和支持向量机的基础。 感知机是一个二分类的线性分类模型&#xff0c;之所以说是线性&#xff0c;是因为它的模型是线性形式的。 从《统计学习方法》中&#xff0c;我们…

【蓝桥杯】线段树

一.线段树 1.定义&#xff1a; 线段树是算法竞赛中常用的用来维护 区间信息 的数据结构。 线段树可以在O(logN) 的时间复杂度内实现单点修改、区间修改、区间查询&#xff08;区间求和&#xff0c;求区间最大值&#xff0c;求区间最小值&#xff09;等操作。 2.结构&#x…

【论文阅读】Improved Denoising Diffusion Probabilistic Models

Improved Denoising Diffusion Probabilistic Models 文章目录 Improved Denoising Diffusion Probabilistic Models概述Improving the Log-likelihoodLearning ∑ θ ( x t , t ) \sum_{\theta}(x_{t}, t) ∑θ​(xt​,t)Improving the Noise ScheduleReducing Gradient Nois…

基于Pnpm + Turborepo + QianKun的微前端+Monorepo实践

基于Pnpm Turborepo QianKun的微前端Monorepo实践 背景 微前端一般都会涉及多个代码库&#xff0c;很多时候要一个一个代码库地去开发维护和运行&#xff0c;很不方便&#xff0c;这种时候引入Monorepo搭配微前端就能很好地解决这种问题&#xff0c;一个代码库就可以完成整…

Spring Cloud Alibaba微服务从入门到进阶(三)(Spring Cloud Alibaba)

Spring Cloud Alibaba是spring Cloud的子项目 Spring Cloud Alibaba的主要组件&#xff08;红框内是开源的&#xff09; Spring Cloud是快速构建分布式系统的工具集&#xff0c; Spring Cloud提供了很多分布式功能 Spring Cloud常用子项目 项目整合 Spring Cloud Alibaba …

友塔游戏测试开发笔面经验

题目一 给定任意非负整数M&#xff0c;判断其能否表达为 M 2 ^a 2 ^b(a和b为非负整数)&#xff0c;若可以输出a和b&#xff0c;若不能输出-1&#xff1b; 例如&#xff1a; 输入&#xff1a;6 输出: “1 2” 分析&#xff1a; void findAB(int M){} 为解决问题的主函数 …

优选算法[1]

目录 1.双指针&#xff1b; 2.滑动窗口&#xff1b; 3.二分查找&#xff1b; 4.前缀和&#xff1b; 1.双指针&#xff1b; 包括对撞指针和快慢指针(一般用来循环&#xff09;&#xff1b; 题目类型&#xff1a;移动零&#xff0c;复写零&#xff0c;快乐数&#xff0c;盛…

【每日力扣】235. 二叉搜索树的最近公共祖先与39. 组合总和问题描述

&#x1f525; 个人主页: 黑洞晓威 &#x1f600;你不必等到非常厉害&#xff0c;才敢开始&#xff0c;你需要开始&#xff0c;才会变的非常厉害。 235. 二叉搜索树的最近公共祖先 给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。 百度百科中最近公共祖先的定义…