巧用set集合去重导致菜单列表显示顺序不一致

业务情景:
根据用户标识userId查询菜单列表。首先查询redis是否有数据,有数据取出,无数据在MySQL中查询,并将结果保存到redis

代码一

package top.lookupstar.services.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import top.lookupstar.constant.ManagerConstants;
import top.lookupstar.domain.SysMenu;
import top.lookupstar.mapper.SysMenuMapper;
import top.lookupstar.services.SysMenuService;

import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

@Service
public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> implements SysMenuService {
    @Resource
    private SysMenuMapper sysMenuMapper;
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public Set<SysMenu> querySysMenuListByUserId(Long userId) {
        // 从redis中查询菜单权限
        String jsonStr = stringRedisTemplate.opsForValue().get(ManagerConstants.LOGON_SYS_MENU_PREFIX + userId);
        // 判断是否有值
        Set<SysMenu> menus = null;
        if (StringUtils.hasText(jsonStr)) {
            // 有值,将json格式菜单权限字符串转换为对象
            List<SysMenu> sysMenu = JSONObject.parseArray(jsonStr, SysMenu.class);
            // 将List集合转换成Set集合
            menus = sysMenu.stream().collect(Collectors.toSet());
        } else {
            // 没有,去查询数据库.并将结果存放到redis
            Set<SysMenu> sysMenus = sysMenuMapper.selectSysMenuByUserId(userId);
            // 将set集合转换为树结构 过滤转换
            menus = transitionTree(sysMenus, 0L);
            stringRedisTemplate.opsForValue().set(ManagerConstants.LOGON_SYS_MENU_PREFIX + userId, JSONObject.toJSONString(menus));
        }

        return menus;
    }

    /**
     * 集合转换树
     * @param menus
     * @param pid
     * @return
     */
    private Set<SysMenu> transitionTree(Set<SysMenu> menus, Long pid) {
        // 未知菜单深度
        Set<SysMenu> roots = menus.stream()
                .filter(m -> m.getParentId().equals(pid))
                .collect(Collectors.toSet());
        // 获取字节点集合
        roots.forEach(r -> {
            Set<SysMenu> child = transitionTree(menus, r.getMenuId());
            r.setList(child);
        });
        return roots;
    }
}

代码二

    @Override
    public Set<SysMenu> querySysMenuListByUserId(Long userId) {
        // 从redis中查询菜单权限
        String jsonStr = stringRedisTemplate.opsForValue().get(ManagerConstants.LOGON_SYS_MENU_PREFIX + userId);
        // 判断是否有值
        Set<SysMenu> menus = null;
        if (StringUtils.hasText(jsonStr)) {
            // 有值,将json格式菜单权限字符串转换为对象
            List<SysMenu> sysMenu = JSONObject.parseArray(jsonStr, SysMenu.class);
            // 将List集合转换成Set集合
            menus = sysMenu.stream().collect(Collectors.toSet());
        } else {
            // 没有,去查询数据库.并将结果存放到redis
            menus = sysMenuMapper.selectSysMenuByUserId(userId);
            // 将set集合转换为树结构 过滤转换
            stringRedisTemplate.opsForValue().set(ManagerConstants.LOGON_SYS_MENU_PREFIX + userId, JSONObject.toJSONString(menus));
        }
        return transitionTree(menus, 0L);
    }

起因:前端显示菜单顺序异常。
初次调用接口查询到的数据显示顺序正常,再次调用接口查询,返回的是redis里面的数据,前端显示顺序不符合预期,排序字段没有生效。

原因分析:
代码一,从redis查询到的记录先是从JSON字符串转换成list集合,此时数据顺序依然是转换成树结构的顺序,list转换set顺序变化。
前端接收到数据并没有按照ordernum字段的数字排序显示,而是直接显示后端数据导致显示结果异常。

解决方案1:
代码二所示,将从MySQL查询的结果,不转换存入redis中,转换树结构最后执行。这样每次返回的数据顺序不变
解决方案2:
将querySysMenuListByUserId返回的集合转换为list集合,并按照orderNum字段的数字大小排序。
解决方案3:
后端代码不变,前端对接收到的数据排序

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值