刷题系统重构--添加vip功能
c端系统往往都具有vip功能作为主要盈利点,那咱们的刷题微服务系统肯定也该有,但是系统设计的时候就没有vip系统,今天构思了一下如何设计VIP功能同时减少代码原有改动。
1.首先,我拥有一张题目信息表,和对应题目类型的答案表,为了不改动原有基础,我们新增
首先,根据原料的题目表建一张vip题目表,可以用来只存储vip题目,或者0,1区分vip题目,重构了一下新增题目,加了一项添加题目的vip字段的添加
,这样,在不影响原有代码的基础上,我们便有了vip题目的设计,而且这个方法我们选择了题目列表可获取,题目详情不可获取,不影响原有题目浏览页面观感,同时也利于客户了解到系统题目丰富,只是缺少vip,更加提高了用户的信赖。
所以我们只需要重构题目详情返还页即可,将vip字段返还便可以判断是否vip题目。
这样vip的返还我们就有了
但是后来,我又想到了问题,假如说这样传输的话,非vip用户查询题目详情,其实只需要知道什么题是vip,什么题不需要可以看,但是我这样的基础重构导致题目详细信息依旧会传递给前端,所以也会面临泄露的风险。
于是我重新根据现有代码再次修改了一下方案
用户充值vip或者刷新后会执行getPermissionNow接口获取最新权限存入redis中,之后用户进行vip题目查看时,首先通过gRpc远程调用获取是否vip信息
然后题目不进行详细题目查询将题目id传入VIP表,判断vip字段是否激活,如若不匹配,直接返还vio题目,减少不必要的网络消耗。
由于系统是微服务的形式,用户作为一个单独的模块,那么我们到底是应该怎么来对vip身份进行判断,同时还能保证高并发情景下优化效率呢?
@Override public List<String> getPermissionNow(String userName) { String permissionKeyDel = authPermissionPrefix + "." + userName; String permissionKey = redisUtil.buildKey(authPermissionPrefix, userName); Boolean status = redisUtil.del(permissionKeyDel); // 使用 delete 方法 AuthUser authUser = authUserService.queryByUserName(userName); AuthUserRole authUserRole = authUserRoleDao.queryByUserId(authUser.getId()); AuthRolePermission authRolePermission = new AuthRolePermission(); authRolePermission.setRoleId(authUserRole.getRoleId()); List<AuthRolePermission> rolePermissionList = authRolePermissionService. queryByCondition(authRolePermission); List<Long> permissionIdList = rolePermissionList.stream() .map(AuthRolePermission::getPermissionId).collect(Collectors.toList()); //根据roleId查权限 List<AuthPermission> permissionList = authPermissionService.queryByRoleList(permissionIdList); redisUtil.set(permissionKey, new Gson().toJson(permissionList)); String permissionValue = redisUtil.get(permissionKey); if (StringUtils.isBlank(permissionValue)) { return Collections.emptyList(); } // 从 Redis 中取出的数据类型应为 List<String> List<AuthPermission> permissionList1 = new Gson().fromJson(permissionValue, new TypeToken<List<AuthPermission>>() { }.getType()); List<String> authList = permissionList1.stream().map(AuthPermission::getPermissionKey).collect(Collectors.toList()); return authList; }
rpc调用
@RequestMapping("/permission/getPermissionNow") Result<List<String>> getPermissionNow();
/** * 判断用户是否为vip * @return */ public boolean getUserPermissionNow() { Result result = userFeignService.getPermissionNow(); if (result.getSuccess()) { List<String> permissions = (List<String>) result.getData(); return permissions.stream() .anyMatch(permission -> "subject:vip".equals(permission)); } else { // 处理结果不成功的情况,例如抛出异常或者返回默认值 throw new RuntimeException("获取权限信息失败"); } }
controller判断检索逻辑
//判断用户是不是vip Boolean permission = userRpc.getUserPermissionNow(); if (log.isInfoEnabled()) { log.info("SubjectController.permission:{}", JSON.toJSONString(permission)); } SubjectInfoBO boResult = subjectInfoDomainService.querySubjectInfo(subjectInfoBO); if (boResult.getVip()==1 && !permission){ return Result.fail("请先购买vip"); }
这样,用户只需要赋予其vip权限即可行驶vip功能,同时用户前端无感刷新。
后期也可对vip表新增其他字段,如是否依旧启用,vip几级权限,后续可以尝试将其内容抽象出单独类,进行修改维护。
但是目前此重构方法依旧具有部分问题,高并发场景下,刷新用户权限这段其实是很麻烦也与数据库很多表进行了交互,风险较大,参考更多设计思路,寻找更佳的解决方式以应对高并发场景下用户的vip权限划分