diff --git a/im-admin/ruoyi-im/src/main/java/org/dromara/im/controller/ImUserController.java b/im-admin/ruoyi-im/src/main/java/org/dromara/im/controller/ImUserController.java index 221eebe..ac7e2d8 100644 --- a/im-admin/ruoyi-im/src/main/java/org/dromara/im/controller/ImUserController.java +++ b/im-admin/ruoyi-im/src/main/java/org/dromara/im/controller/ImUserController.java @@ -63,7 +63,7 @@ public class ImUserController extends BaseController { */ @SaCheckPermission("im:user:export") @Log(title = "用户", businessType = BusinessType.EXPORT) - @PostMapping("/export") + //@PostMapping("/export") public void export(ImUserBo bo, HttpServletResponse response) { List list = userService.queryList(bo); ExcelUtil.exportExcel(list, "用户", ImUserVo.class, response); @@ -148,7 +148,7 @@ public class ImUserController extends BaseController { /** * 查询客服列表 */ - @SaCheckPermission("im:user:list") + @SaCheckPermission("im:user:listCustomer") @GetMapping("/listCustomer") public TableDataInfo listCustomer(ImUserBo bo, PageQuery pageQuery) { System.out.println("ImUserBo" + bo); @@ -159,7 +159,7 @@ public class ImUserController extends BaseController { /** * 新增客服(isCustomer-2) */ - @SaCheckPermission("im:user:list") + @SaCheckPermission("im:user:addCustomer") @PostMapping("/addCustomer") public R addCustomer(@RequestBody ImUser user) { // 昵称默认跟用户名保持一致 @@ -195,7 +195,7 @@ public class ImUserController extends BaseController { /** * 删除客服 */ - @SaCheckPermission("im:user:list") + @SaCheckPermission("im:user:removeCustomer") @PostMapping("/removeCustomer") public R removeCustomer(@RequestBody List ids) { @@ -207,7 +207,7 @@ public class ImUserController extends BaseController { /** * 修改客服 */ - @SaCheckPermission("im:user:list") + @SaCheckPermission("im:user:editCustomer") @PostMapping("/editCustomer") public R editCustomer(@RequestBody ImUser user) { @@ -223,9 +223,9 @@ public class ImUserController extends BaseController { } /** - * 修改客服 + * 重置客服密码 */ - @SaCheckPermission("im:user:list") + @SaCheckPermission("im:user:resetPwdCustomer") @PostMapping("/resetPwdCustomer") public R resetPwdCustomer(@RequestBody ImUser user) { Long id = user.getId(); @@ -247,7 +247,7 @@ public class ImUserController extends BaseController { /** * 批量修改用户信息(标签和群组) */ - @SaCheckPermission("im:user:edit") + @SaCheckPermission("im:user:editGroupAndLabel") @Log(title = "用户", businessType = BusinessType.UPDATE) @PostMapping("/updateBatchUser") public R updateBatchUser(@RequestBody List list) { diff --git a/im-admin/ruoyi-im/src/main/java/org/dromara/im/controller/ImUserLabelController.java b/im-admin/ruoyi-im/src/main/java/org/dromara/im/controller/ImUserLabelController.java index fb85e16..b42c5cb 100644 --- a/im-admin/ruoyi-im/src/main/java/org/dromara/im/controller/ImUserLabelController.java +++ b/im-admin/ruoyi-im/src/main/java/org/dromara/im/controller/ImUserLabelController.java @@ -51,7 +51,7 @@ public class ImUserLabelController extends BaseController { */ @SaCheckPermission("im:userLabel:export") @Log(title = "用户分组", businessType = BusinessType.EXPORT) - @PostMapping("/export") + //@PostMapping("/export") public void export(ImUserLabelBo bo, HttpServletResponse response) { List list = imUserLabelService.queryList(bo); ExcelUtil.exportExcel(list, "用户分组", ImUserLabelVo.class, response); diff --git a/im-admin/ruoyi-im/src/main/java/org/dromara/im/mapper/ImUserMapper.java b/im-admin/ruoyi-im/src/main/java/org/dromara/im/mapper/ImUserMapper.java index 99e018b..de37ed0 100644 --- a/im-admin/ruoyi-im/src/main/java/org/dromara/im/mapper/ImUserMapper.java +++ b/im-admin/ruoyi-im/src/main/java/org/dromara/im/mapper/ImUserMapper.java @@ -29,4 +29,18 @@ public interface ImUserMapper extends BaseMapperPlus { "ORDER BY date ASC") List> getDailyRegistrationCount(@Param("days") Integer days); + /** + * 按天统计用户注册数量(带uniqueToken条件) + * @param days 统计天数 + * @param uniqueToken 唯一标识 + * @return 统计结果 + */ + @Select("SELECT DATE(created_time) as date, COUNT(*) as count " + + "FROM im_user " + + "WHERE created_time >= DATE_SUB(CURDATE(), INTERVAL #{days} DAY) " + + "AND unique_token = #{uniqueToken} " + + "GROUP BY DATE(created_time) " + + "ORDER BY date ASC") + List> getDailyRegistrationCount(@Param("days") Integer days, @Param("uniqueToken") String uniqueToken); + } diff --git a/im-admin/ruoyi-im/src/main/java/org/dromara/im/service/impl/ImUserGroupServiceImpl.java b/im-admin/ruoyi-im/src/main/java/org/dromara/im/service/impl/ImUserGroupServiceImpl.java index c6de38d..ca74968 100644 --- a/im-admin/ruoyi-im/src/main/java/org/dromara/im/service/impl/ImUserGroupServiceImpl.java +++ b/im-admin/ruoyi-im/src/main/java/org/dromara/im/service/impl/ImUserGroupServiceImpl.java @@ -1,5 +1,6 @@ package org.dromara.im.service.impl; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import org.dromara.common.core.utils.MapstructUtils; import org.dromara.common.core.utils.StringUtils; import org.dromara.common.mybatis.core.page.TableDataInfo; @@ -9,7 +10,10 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.dynamic.datasource.annotation.DS; import lombok.RequiredArgsConstructor; +import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.im.constant.ImConstant; +import org.dromara.im.service.IImAgentService; +import org.dromara.im.util.LambdaQueryWrapperHelper; import org.springframework.stereotype.Service; import org.dromara.im.domain.bo.ImUserGroupBo; import org.dromara.im.domain.vo.ImUserGroupVo; @@ -34,6 +38,8 @@ public class ImUserGroupServiceImpl implements IImUserGroupService { private final ImUserGroupMapper baseMapper; + private final IImAgentService imAgentService; + /** * 查询用户分组 * @@ -42,7 +48,14 @@ public class ImUserGroupServiceImpl implements IImUserGroupService { */ @Override public ImUserGroupVo queryById(Long id){ - return baseMapper.selectVoById(id); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(ImUserGroup::getId, id); + + if(!LoginHelper.isSuperAdmin()) { + LambdaQueryWrapperHelper.appendToken(lqw, ImUserGroup::getUniqueToken); + } + + return baseMapper.selectVoOne(lqw); } /** @@ -78,7 +91,13 @@ public class ImUserGroupServiceImpl implements IImUserGroupService { */ @Override public List queryAllList() { - return baseMapper.selectList(null); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + + if(!LoginHelper.isSuperAdmin()) { + LambdaQueryWrapperHelper.appendToken(lqw, ImUserGroup::getUniqueToken); + } + + return baseMapper.selectList(lqw); } private LambdaQueryWrapper buildQueryWrapper(ImUserGroupBo bo) { @@ -86,6 +105,11 @@ public class ImUserGroupServiceImpl implements IImUserGroupService { LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); lqw.like(StringUtils.isNotBlank(bo.getGroupName()), ImUserGroup::getGroupName, bo.getGroupName()); lqw.eq(bo.getSort() != null, ImUserGroup::getSort, bo.getSort()); + + if(!LoginHelper.isSuperAdmin()) { + LambdaQueryWrapperHelper.appendToken(lqw, ImUserGroup::getUniqueToken); + } + return lqw; } @@ -98,6 +122,14 @@ public class ImUserGroupServiceImpl implements IImUserGroupService { @Override public Boolean insertByBo(ImUserGroupBo bo) { ImUserGroup add = MapstructUtils.convert(bo, ImUserGroup.class); + + // 如果不是超级管理员,则设置 uniqueToken + if(!LoginHelper.isSuperAdmin()) { + if (add != null) { + add.setUniqueToken(imAgentService.getTokenByUserId()); + } + } + validEntityBeforeSave(add); boolean flag = baseMapper.insert(add) > 0; if (flag) { @@ -115,8 +147,23 @@ public class ImUserGroupServiceImpl implements IImUserGroupService { @Override public Boolean updateByBo(ImUserGroupBo bo) { ImUserGroup update = MapstructUtils.convert(bo, ImUserGroup.class); - validEntityBeforeSave(update); - return baseMapper.updateById(update) > 0; + if (update == null) { + return false; + } + + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); + updateWrapper.eq(ImUserGroup::getId, update.getId()); + + if(!LoginHelper.isSuperAdmin()) { + // 使用当前用户的 token 而不是从 bo 中获取,防止篡改 + updateWrapper.eq(ImUserGroup::getUniqueToken, imAgentService.getTokenByUserId()); + } + + updateWrapper.set(ImUserGroup::getGroupName, update.getGroupName()); + updateWrapper.set(ImUserGroup::getSort, update.getSort()); + updateWrapper.set(ImUserGroup::getRemark, update.getRemark()); + + return baseMapper.update(null, updateWrapper) > 0; } /** @@ -136,8 +183,19 @@ public class ImUserGroupServiceImpl implements IImUserGroupService { @Override public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { if(isValid){ - //TODO 做一些业务上的校验,判断是否需要校验 + if (ids == null || ids.isEmpty()) { + return false; + } + } + + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); + updateWrapper.in(ImUserGroup::getId, ids); + + if(!LoginHelper.isSuperAdmin()) { + // 添加 uniqueToken 条件,确保只能删除当前用户的记录 + updateWrapper.eq(ImUserGroup::getUniqueToken, imAgentService.getTokenByUserId()); } - return baseMapper.deleteByIds(ids) > 0; + + return baseMapper.delete(updateWrapper) > 0; } } diff --git a/im-admin/ruoyi-im/src/main/java/org/dromara/im/service/impl/ImUserServiceImpl.java b/im-admin/ruoyi-im/src/main/java/org/dromara/im/service/impl/ImUserServiceImpl.java index 0998853..70352cc 100644 --- a/im-admin/ruoyi-im/src/main/java/org/dromara/im/service/impl/ImUserServiceImpl.java +++ b/im-admin/ruoyi-im/src/main/java/org/dromara/im/service/impl/ImUserServiceImpl.java @@ -12,6 +12,7 @@ import org.apache.logging.log4j.util.Strings; import org.dromara.common.core.utils.StringUtils; import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.im.constant.ImConstant; import org.dromara.im.constant.ImRedisKey; import org.dromara.im.domain.ImUser; @@ -22,10 +23,13 @@ import org.dromara.im.domain.dto.ImUserUnbanDto; import org.dromara.im.domain.vo.ImUserVo; import org.dromara.im.mapper.ImUserMapper; import org.dromara.im.mq.ImRedisMQTemplate; +import org.dromara.im.service.IImAgentService; import org.dromara.im.service.IImUserService; +import org.dromara.im.util.LambdaQueryWrapperHelper; import org.springframework.stereotype.Service; import java.util.*; +import java.util.stream.Collectors; /** * 用户Service业务层处理 @@ -40,6 +44,7 @@ public class ImUserServiceImpl implements IImUserService { private final ImRedisMQTemplate redisMQTemplate; private final ImUserMapper baseMapper; + private final IImAgentService imAgentService; /** * 查询用户 @@ -49,7 +54,14 @@ public class ImUserServiceImpl implements IImUserService { */ @Override public ImUserVo queryById(Long id){ - return baseMapper.selectVoById(id); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(ImUser::getId, id); + + if(!LoginHelper.isSuperAdmin()) { + LambdaQueryWrapperHelper.appendToken(lqw, ImUser::getUniqueToken); + } + + return baseMapper.selectVoOne(lqw); } /** @@ -84,6 +96,11 @@ public class ImUserServiceImpl implements IImUserService { public void ban(ImUserBanDto dto) { LambdaUpdateWrapper wrapper = Wrappers.lambdaUpdate(); wrapper.eq(ImUser::getId, dto.getId()); + + if(!LoginHelper.isSuperAdmin()) { + wrapper.eq(ImUser::getUniqueToken, imAgentService.getTokenByUserId()); + } + wrapper.set(ImUser::getIsBanned, true); wrapper.set(ImUser::getReason, dto.getReason()); baseMapper.update(wrapper); @@ -95,6 +112,11 @@ public class ImUserServiceImpl implements IImUserService { public void unban(ImUserUnbanDto dto) { LambdaUpdateWrapper wrapper = Wrappers.lambdaUpdate(); wrapper.eq(ImUser::getId, dto.getId()); + + if(!LoginHelper.isSuperAdmin()) { + wrapper.eq(ImUser::getUniqueToken, imAgentService.getTokenByUserId()); + } + wrapper.set(ImUser::getIsBanned, false); wrapper.set(ImUser::getReason, Strings.EMPTY); baseMapper.update(wrapper); @@ -106,6 +128,10 @@ public class ImUserServiceImpl implements IImUserService { wrapper.like(StringUtils.isNotBlank(bo.getUserName()), ImUser::getUserName, bo.getUserName()); wrapper.like(StringUtils.isNotBlank(bo.getNickName()), ImUser::getNickName, bo.getNickName()); + if(!LoginHelper.isSuperAdmin()) { + LambdaQueryWrapperHelper.appendToken(wrapper, ImUser::getUniqueToken); + } + // 处理标签 ID 查询,支持多个标签 ID 的 AND 匹配 if (StringUtils.isNotBlank(bo.getLabelIds())) { String[] labelArray = bo.getLabelIds().split(","); @@ -136,6 +162,11 @@ public class ImUserServiceImpl implements IImUserService { if(StrUtil.isNotEmpty(name)){ queryWrapper.like(ImUser::getUserName, name); } + + if(!LoginHelper.isSuperAdmin()) { + LambdaQueryWrapperHelper.appendToken(queryWrapper, ImUser::getUniqueToken); + } + queryWrapper.select(ImUser::getId, ImUser::getUserName); queryWrapper.orderByDesc(ImUser::getId); queryWrapper.last("limit 10"); @@ -147,6 +178,11 @@ public class ImUserServiceImpl implements IImUserService { // 取出用户名或昵称匹配的前10条 LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(); queryWrapper.in(ImUser::getId, ids); + + if(!LoginHelper.isSuperAdmin()) { + LambdaQueryWrapperHelper.appendToken(queryWrapper, ImUser::getUniqueToken); + } + return baseMapper.selectVoList(queryWrapper); } @@ -161,6 +197,13 @@ public class ImUserServiceImpl implements IImUserService { if (days == null || days <= 0) { days = 7; // 默认统计最近7天 } + + // 如果不是超级管理员,则使用带uniqueToken的方法 + if(!LoginHelper.isSuperAdmin()) { + String uniqueToken = imAgentService.getTokenByUserId(); + return baseMapper.getDailyRegistrationCount(days, uniqueToken); + } + return baseMapper.getDailyRegistrationCount(days); } @@ -171,7 +214,13 @@ public class ImUserServiceImpl implements IImUserService { */ @Override public Long getTotalUserCount() { - return baseMapper.selectCount(null); + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + + if(!LoginHelper.isSuperAdmin()) { + LambdaQueryWrapperHelper.appendToken(wrapper, ImUser::getUniqueToken); + } + + return baseMapper.selectCount(wrapper); } @@ -184,6 +233,11 @@ public class ImUserServiceImpl implements IImUserService { public Long getDailyActiveUserCount() { LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); wrapper.ge(ImUser::getLastLoginTime, DateUtils.addDays(new Date(), -1)); + + if(!LoginHelper.isSuperAdmin()) { + LambdaQueryWrapperHelper.appendToken(wrapper, ImUser::getUniqueToken); + } + return baseMapper.selectCount(wrapper); } @@ -196,6 +250,11 @@ public class ImUserServiceImpl implements IImUserService { public Long getWeeklyActiveUserCount() { LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); wrapper.ge(ImUser::getLastLoginTime, DateUtils.addDays(new Date(), -7)); + + if(!LoginHelper.isSuperAdmin()) { + LambdaQueryWrapperHelper.appendToken(wrapper, ImUser::getUniqueToken); + } + return baseMapper.selectCount(wrapper); } @@ -208,11 +267,22 @@ public class ImUserServiceImpl implements IImUserService { public Long getMonthlyActiveUserCount() { LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); wrapper.ge(ImUser::getLastLoginTime, DateUtils.addDays(new Date(), -30)); + + if(!LoginHelper.isSuperAdmin()) { + LambdaQueryWrapperHelper.appendToken(wrapper, ImUser::getUniqueToken); + } + return baseMapper.selectCount(wrapper); } @Override public void save(ImUser user) { + // 如果不是超级管理员,则设置 uniqueToken + if(!LoginHelper.isSuperAdmin()) { + if (user != null) { + user.setUniqueToken(imAgentService.getTokenByUserId()); + } + } baseMapper.insert(user); } @@ -220,6 +290,11 @@ public class ImUserServiceImpl implements IImUserService { public ImUser findUserByUserName(String userName) { LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); wrapper.eq(ImUser::getUserName, userName); + + if(!LoginHelper.isSuperAdmin()) { + LambdaQueryWrapperHelper.appendToken(wrapper, ImUser::getUniqueToken); + } + return baseMapper.selectOne(wrapper); } @@ -233,12 +308,36 @@ public class ImUserServiceImpl implements IImUserService { @Override public void removeCustomer(List ids) { - baseMapper.deleteByIds(ids); + LambdaUpdateWrapper wrapper = Wrappers.lambdaUpdate(); + wrapper.in(ImUser::getId, ids); + + if(!LoginHelper.isSuperAdmin()) { + wrapper.eq(ImUser::getUniqueToken, imAgentService.getTokenByUserId()); + } + + baseMapper.delete(wrapper); } @Override public void updateCustomerById(ImUser bo) { - baseMapper.updateById(bo); + if (bo == null) { + return; + } + + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); + updateWrapper.eq(ImUser::getId, bo.getId()); + + if(!LoginHelper.isSuperAdmin()) { + updateWrapper.eq(ImUser::getUniqueToken, imAgentService.getTokenByUserId()); + } + + updateWrapper.set(ImUser::getNickName, bo.getNickName()); + updateWrapper.set(ImUser::getLabelIds, bo.getLabelIds()); + updateWrapper.set(ImUser::getGroupIds, bo.getGroupIds()); + updateWrapper.set(ImUser::getHeadImage, bo.getHeadImage()); + updateWrapper.set(ImUser::getHeadImageThumb, bo.getHeadImageThumb()); + + baseMapper.update(null, updateWrapper); } @Override @@ -253,6 +352,16 @@ public class ImUserServiceImpl implements IImUserService { } } + //不是超级管理员,则校验 token 是否一致 + if(!LoginHelper.isSuperAdmin()) { + List vaildList = baseMapper.selectByIds(list.stream().map(BatchUpdateUserDto::getId).collect(Collectors.toList())); + String uniqueToken = imAgentService.getTokenByUserId(); + for(ImUser user : vaildList) { + if(!uniqueToken.equals(user.getUniqueToken())) + return; + } + } + List temp = new ArrayList<>(); for(BatchUpdateUserDto dto : list) { ImUser user = new ImUser(); diff --git a/im-admin/ruoyi-im/src/main/resources/updateSql.sql b/im-admin/ruoyi-im/src/main/resources/updateSql.sql index b611475..c37c7ad 100644 --- a/im-admin/ruoyi-im/src/main/resources/updateSql.sql +++ b/im-admin/ruoyi-im/src/main/resources/updateSql.sql @@ -60,4 +60,33 @@ ADD INDEX idx_token_id (unique_token, id); ALTER TABLE im_user_label ADD COLUMN unique_token VARCHAR(100) COMMENT '唯一标识token' AFTER id, ADD INDEX idx_unique_token (unique_token), -ADD INDEX idx_token_id (unique_token, id); \ No newline at end of file +ADD INDEX idx_token_id (unique_token, id); + +-- 2026.4.8 + +-- 1. 先删除所有表的 unique_token 相关索引和字段 +ALTER TABLE im_group DROP INDEX uk_unique_token, DROP INDEX idx_token_id, DROP COLUMN unique_token; +ALTER TABLE im_group_message DROP INDEX uk_unique_token, DROP INDEX idx_token_id, DROP COLUMN unique_token; +ALTER TABLE im_private_message DROP INDEX uk_unique_token, DROP INDEX idx_token_id, DROP COLUMN unique_token; +ALTER TABLE im_user DROP INDEX uk_unique_token, DROP INDEX idx_token_id, DROP COLUMN unique_token; + +-- 2. 重新添加字段和普通索引 +ALTER TABLE im_group + ADD COLUMN unique_token VARCHAR(100) COMMENT '标识token' AFTER id, + ADD INDEX idx_unique_token (unique_token), + ADD INDEX idx_token_id (unique_token, id); + +ALTER TABLE im_group_message + ADD COLUMN unique_token VARCHAR(100) COMMENT '标识token' AFTER id, + ADD INDEX idx_unique_token (unique_token), + ADD INDEX idx_token_id (unique_token, id); + +ALTER TABLE im_private_message + ADD COLUMN unique_token VARCHAR(100) COMMENT '标识token' AFTER id, + ADD INDEX idx_unique_token (unique_token), + ADD INDEX idx_token_id (unique_token, id); + +ALTER TABLE im_user + ADD COLUMN unique_token VARCHAR(100) COMMENT '代理标识token' AFTER id, + ADD INDEX idx_unique_token (unique_token), + ADD INDEX idx_token_id (unique_token, id); \ No newline at end of file