柚见十三期(优化)

前端优化

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

骨架屏 : 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;


  • 24
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
4.5javaEE_SSH_SqlServer企业车辆管理系统设计与实现(源码+数据库sql+lun文+视频齐全);javaEE_SSH_SqlServer企业车辆管理系统设计与实现(源码+数据库sql+lun文+视频齐全); javaEE_SSH_SqlServer企业车辆管理系统设计与实现(源码+数据库sql+lun文+视频齐全); javaEE_SSH_SqlServer企业车辆管理系统设计与实现(源码+数据库sql+lun文+视频齐全); javaEE_SSH_SqlServer企业车辆管理系统设计与实现(源码+数据库sql+lun文+视频齐全);javaEE_SSH_SqlServer企业车辆管理系统设计与实现(源码+数据库sql+lun文+视频齐全);javaEE_SSH_SqlServer企业车辆管理系统设计与实现(源码+数据库sql+lun文+视频齐全);javaEE_SSH_SqlServer企业车辆管理系统设计与实现(源码+数据库sql+lun文+视频齐全);javaEE_SSH_SqlServer企业车辆管理系统设计与实现(源码+数据库sql+lun文+视频齐全);javaEE_SSH_SqlServer企业车辆管理系统设计与实现(源码+数据库sql+lun文+视频齐全);javaEE_SSH_SqlServer企业车辆管理系统设计与实现(源码+数据库sql+lun文+视频齐全);javaEE_SSH_SqlServer企业车辆管理系统设计与实现(源码+数据库sql+lun文+视频齐全);javaEE_SSH_SqlServer企业车辆管理系统设计与实现(源码+数据库sql+lun文+视频齐全);javaEE_SSH_SqlServer企业车辆管理系统设计与实现(源码+数据库sql+lun文+视频齐全);javaEE_SSH_SqlServer企业车辆管理系统设计与实现(源码+数据库sql+lun文+视频齐全);javaEE_SSH_SqlServer企业车辆管理系统设计与实现(源码+数据库sql+lun文+视频齐全);

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值