Discord.js等级角色奖励:获取数组中低于阈值的最高等级对应角色
Discord.js 机器人等级奖励角色发放逻辑修复
问题背景
开发Discord.js等级体系机器人时,需实现以下逻辑:用户升级达到指定等级里程碑时,为其发放对应奖励角色,同时移除此前持有的低阶段奖励角色。
初始版本代码仅支持奖励等级连续(相邻奖励等级差为1,如1级升2级)的场景,当奖励配置存在等级断层(如从4级升到5级、3/4级无对应奖励)时,程序会因找不到待移除的旧角色变量直接崩溃。
原有代码缺陷分析
初始版本直接通过「当前等级-1」匹配旧角色,完全未考虑奖励等级不连续的场景:
memberData.level = 4; levelUpRoles = [ // 用户自定义的等级-角色映射配置 {role: 'id1', level: 1}, {role: 'id2', level: 2}, {role: 'id3', level: 5}, {role: 'id4', level: 20}, {role: 'id5', level: 10}, ] let newLvl = levelUpRoles.find(object => object.level === memberData.level); // 缺陷点:直接匹配等级-1的配置,等级断层时prevLvl为undefined let prevLvl = levelUpRoles.find(object => object.level === memberData.level - 1); if (newLvl) { target.roles.add(newLvl.role).catch(console.error); // prevLvl为undefined时读取.role属性,直接触发类型错误崩溃 target.roles.remove(prevLvl.role).catch(console.error); }
后续调整的版本仍存在两个致命问题:
- 多余的
slice(0,-1)操作会错误截断符合要求的最高等级低阶角色 slice(-1)返回值是长度为1的数组,而非单个角色对象,直接读取.role属性依然会得到undefined
调整后的问题代码如下:
// 缺陷点:多余截断逻辑,且最终返回数组而非单个角色对象 let prevLvl = levelUpRoles.filter(object => object.level < memberData.level).sort(function (a, b) {return a.level - b.level}).slice(0,-1).slice(-1); let newLvl = levelUpRoles.find(object => object.level === memberData.level); if (newLvl) { console.log(newLvl); target.roles.add(newLvl.role).catch(console.error); console.log(prevLvl); if (prevLvl) { // prevLvl是数组,此处.role取值无效 target.roles.remove(prevLvl.role).catch(console.error); } }
正确实现方案
获取待移除旧角色的核心逻辑:
- 先对等级角色配置做升序排序,规避用户自定义配置顺序混乱的问题
- 筛选出所有等级要求低于当前用户等级的奖励配置
- 取筛选结果中等级最高的配置,即为距离当前等级最近的上一阶段奖励角色
- 增加空值校验,仅当确实存在旧奖励角色时才执行移除操作,避免空值报错
最终可稳定运行的代码:
// 拷贝原数组后排序,避免修改原数组顺序影响其他业务逻辑 const sortedLevelRoles = [...levelUpRoles].sort((a, b) => a.level - b.level); // 匹配当前等级对应的新奖励角色 const newReward = sortedLevelRoles.find(item => item.level === memberData.level); if (newReward) { // 授予新的里程碑角色 target.roles.add(newReward.role).catch(console.error); // 筛选所有低于当前等级的奖励,取最高等级的作为待移除旧角色 const lowerLevelRewards = sortedLevelRoles.filter(item => item.level < memberData.level); const oldReward = lowerLevelRewards.pop(); // pop直接取数组最后一项,即最高等级的低阶奖励 if (oldReward) { target.roles.remove(oldReward.role).catch(console.error); } }
逻辑验证
基于示例配置测试多场景:
- 用户首次升级到1级:低于1级的奖励为空,不执行移除操作,仅授予1级角色,无报错
- 用户从4级升到5级:新奖励为5级角色
id3,筛选出低于5级的最高奖励为2级角色id2,移除id2、授予id3,符合预期 - 用户从9级升到10级:新奖励为10级角色
id5,低于10级的最高奖励为5级角色id3,移除id3、授予id5,符合预期 - 用户从19级升到20级:新奖励为20级角色
id4,低于20级的最高奖励为10级角色id5,移除id5、授予id4,符合预期
内容的提问来源于stack exchange,提问作者Gleyveon
相关产品推荐
相关产品推荐

