Browse Source

权限更新/标签、用户添加uniqueToken校验

master
La123123 1 month ago
parent
commit
7ebe942bfa
  1. 16
      im-admin/ruoyi-im/src/main/java/org/dromara/im/controller/ImUserController.java
  2. 2
      im-admin/ruoyi-im/src/main/java/org/dromara/im/controller/ImUserLabelController.java
  3. 14
      im-admin/ruoyi-im/src/main/java/org/dromara/im/mapper/ImUserMapper.java
  4. 70
      im-admin/ruoyi-im/src/main/java/org/dromara/im/service/impl/ImUserGroupServiceImpl.java
  5. 117
      im-admin/ruoyi-im/src/main/java/org/dromara/im/service/impl/ImUserServiceImpl.java
  6. 31
      im-admin/ruoyi-im/src/main/resources/updateSql.sql

16
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<ImUserVo> 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<ImUserVo> 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<Long> 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<BatchUpdateUserDto> list) {

2
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<ImUserLabelVo> list = imUserLabelService.queryList(bo);
ExcelUtil.exportExcel(list, "用户分组", ImUserLabelVo.class, response);

14
im-admin/ruoyi-im/src/main/java/org/dromara/im/mapper/ImUserMapper.java

@ -29,4 +29,18 @@ public interface ImUserMapper extends BaseMapperPlus<ImUser, ImUserVo> {
"ORDER BY date ASC")
List<Map<String, Object>> 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<Map<String, Object>> getDailyRegistrationCount(@Param("days") Integer days, @Param("uniqueToken") String uniqueToken);
}

70
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<ImUserGroup> 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<ImUserGroup> queryAllList() {
return baseMapper.selectList(null);
LambdaQueryWrapper<ImUserGroup> lqw = Wrappers.lambdaQuery();
if(!LoginHelper.isSuperAdmin()) {
LambdaQueryWrapperHelper.appendToken(lqw, ImUserGroup::getUniqueToken);
}
return baseMapper.selectList(lqw);
}
private LambdaQueryWrapper<ImUserGroup> buildQueryWrapper(ImUserGroupBo bo) {
@ -86,6 +105,11 @@ public class ImUserGroupServiceImpl implements IImUserGroupService {
LambdaQueryWrapper<ImUserGroup> 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<ImUserGroup> 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<Long> ids, Boolean isValid) {
if(isValid){
//TODO 做一些业务上的校验,判断是否需要校验
if (ids == null || ids.isEmpty()) {
return false;
}
}
LambdaUpdateWrapper<ImUserGroup> 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;
}
}

117
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<ImUser> 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<ImUser> 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<ImUser> 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<ImUser> 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<ImUser> 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<ImUser> 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<ImUser> 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<ImUser> 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<ImUser> 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<Long> ids) {
baseMapper.deleteByIds(ids);
LambdaUpdateWrapper<ImUser> 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<ImUser> 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<ImUser> 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<ImUser> temp = new ArrayList<>();
for(BatchUpdateUserDto dto : list) {
ImUser user = new ImUser();

31
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);
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);
Loading…
Cancel
Save