Pre Merge pull request !373 from 守望/base

This commit is contained in:
守望 2023-06-20 04:25:05 +00:00 committed by Gitee
commit 048567a5c3
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
13 changed files with 786 additions and 35 deletions

View File

@ -0,0 +1,128 @@
package org.dromara.common.json.utils;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
@Slf4j
public class JacksonJsonUtil {
private static ObjectMapper OBJECT_MAPPER = new ObjectMapper();
static {
//对象的所有字段全部列入
OBJECT_MAPPER.setSerializationInclusion(JsonInclude.Include.ALWAYS);
//取消默认转换timesstamps(时间戳)形式
OBJECT_MAPPER.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
//忽略空bean转json错误
OBJECT_MAPPER.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
//忽略在json字符串中存在在java类中不存在字段防止错误
OBJECT_MAPPER.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
}
public static <T> String objToJson(T obj) {
if (obj == null) {
return null;
}
try {
return obj instanceof String ? (String)obj : OBJECT_MAPPER.writeValueAsString(obj);
} catch (Exception e) {
log.error("obj To json is error" , e);
return null;
}
}
/**
* 返回格式化好的json串
* @param obj
* @param <T>
* @return
*/
public static <T> String objToJsonPretty(T obj) {
if (obj == null) {
return null;
}
try {
return obj instanceof String ? (String)obj : OBJECT_MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(obj);
} catch (Exception e) {
log.warn("obj To json pretty is error", e);
return null;
}
}
public static <T> T json2Object(String json, Class<T> clazz) {
if (StringUtils.isEmpty(json) || clazz == null) {
return null;
}
try {
return clazz.equals(String.class) ? (T)json : OBJECT_MAPPER.readValue(json, clazz);
} catch (Exception e) {
log.warn("json To obj is error", e);
return null;
}
}
/**
* 通过 TypeReference 处理List<User>这类多泛型问题
* @param json
* @param typeReference
* @param <T>
* @return
*/
public static <T> T json2Object(String json, TypeReference typeReference) {
if (StringUtils.isEmpty(json) || typeReference == null) {
return null;
}
try {
return (T)(typeReference.getType().equals(String.class) ? json : OBJECT_MAPPER.readValue(json, typeReference));
} catch (Exception e) {
log.warn("json To obj is error", e);
return null;
}
}
/**
* 通过jackson 的javatype 来处理多泛型的转换
* @param json
* @param collectionClazz
* @param elements
* @param <T>
* @return
*/
public static <T> T json2Object(String json, Class<?> collectionClazz, Class<?>...elements) {
JavaType javaType = OBJECT_MAPPER.getTypeFactory().constructParametricType(collectionClazz, elements);
try {
return OBJECT_MAPPER.readValue(json, javaType);
} catch (Exception e) {
log.warn("json To obj is error", e);
return null;
}
}
public static Map toLinkedHashMap(String jsonStr) {
Map<String, String> data = null;
try {
data = OBJECT_MAPPER.readValue(jsonStr, new TypeReference<LinkedHashMap<String, String>>() {});
} catch (IOException e) {
e.printStackTrace();
}
return data;
}
}

View File

@ -8,6 +8,7 @@ import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.dromara.common.core.factory.YmlPropertySourceFactory; import org.dromara.common.core.factory.YmlPropertySourceFactory;
import org.dromara.common.mybatis.core.mapper.MySqlInjector;
import org.dromara.common.mybatis.handler.InjectionMetaObjectHandler; import org.dromara.common.mybatis.handler.InjectionMetaObjectHandler;
import org.dromara.common.mybatis.interceptor.PlusDataPermissionInterceptor; import org.dromara.common.mybatis.interceptor.PlusDataPermissionInterceptor;
import org.mybatis.spring.annotation.MapperScan; import org.mybatis.spring.annotation.MapperScan;
@ -82,6 +83,14 @@ public class MybatisPlusConfig {
return new DefaultIdentifierGenerator(NetUtil.getLocalhost()); return new DefaultIdentifierGenerator(NetUtil.getLocalhost());
} }
/**
* 全量更新
* @return
*/
@Bean
public MySqlInjector sqlInjector() {
return new MySqlInjector();
}
/** /**
* PaginationInnerInterceptor 分页插件自动识别数据库类型 * PaginationInnerInterceptor 分页插件自动识别数据库类型
* https://baomidou.com/pages/97710a/ * https://baomidou.com/pages/97710a/

View File

@ -30,7 +30,7 @@ import java.util.stream.Collectors;
* @since 2021-05-13 * @since 2021-05-13
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public interface BaseMapperPlus<T, V> extends BaseMapper<T> { public interface BaseMapperPlus<T, V> extends CommonMapper<T> {
Log log = LogFactory.getLog(BaseMapperPlus.class); Log log = LogFactory.getLog(BaseMapperPlus.class);

View File

@ -0,0 +1,32 @@
package org.dromara.common.mybatis.core.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* @Author warden
* @Date 2023/1/17 17:37
*/
public interface CommonMapper<T> extends BaseMapper<T> {
/**
* 全量插入,等价于insert
* {@link com.baomidou.mybatisplus.extension.injector.methods.InsertBatchSomeColumn}
*
* @param entityList
* @return
*/
int insertBatchSomeColumn(List<T> entityList);
/**
* 全量更新不忽略null字段等价于update
* 解决mybatis-plus会自动忽略null字段不更新
* {@link com.baomidou.mybatisplus.extension.injector.methods.AlwaysUpdateSomeColumnById}
*
* @param entity
* @return
*/
int alwaysUpdateSomeColumnById(@Param(Constants.ENTITY) T entity);
}

View File

@ -0,0 +1,33 @@
package org.dromara.common.mybatis.core.mapper;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.extension.injector.methods.AlwaysUpdateSomeColumnById;
import com.baomidou.mybatisplus.extension.injector.methods.InsertBatchSomeColumn;
import java.util.List;
/**
* 自定义Sql注入
*
* @author nieqiurong 2018/8/11 20:23.
*/
public class MySqlInjector extends DefaultSqlInjector {
@Override
public List<AbstractMethod> getMethodList(Class<?> mapperClass, TableInfo tableInfo) {
List<AbstractMethod> methodList = super.getMethodList(mapperClass, tableInfo);
/**
* 以下 3 个为内置选装件
* 2 个支持字段筛选函数
*/
// : 不要指定了 update 填充的字段
methodList.add(new InsertBatchSomeColumn(i -> i.getFieldFill() != FieldFill.UPDATE));
// 标识了INSERT的字段不更新createTime createBy)
methodList.add(new AlwaysUpdateSomeColumnById(i -> i.getFieldFill() != FieldFill.INSERT));
return methodList;
}
}

View File

@ -0,0 +1,167 @@
package org.dromara.common.mybatis.core.utils;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.domain.model.LoginUser;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.common.satoken.utils.LoginHelper;
import java.lang.reflect.Field;
import java.util.*;
/**
* @Author warden
* @Date 2022/12/8 14:56
*/
@Slf4j
public class BeanUtil {
public static final String ADD = "ADD";
public static final String REMOVE = "REMOVE";
public static final String NORMAL = "NORMAL";
/**
* 比对两条记录数据库字段是否相同相同返回true不同返回false
*
* @param compare
* @param current
* @return
*/
public static boolean diffObject(Object compare, Object current) {
List<Field> fieldList = new ArrayList<>();
Field[] allFields = ReflectUtil.getFieldsDirectly(compare.getClass(), true);
String[] dontCompareFields = {"createBy", "createTime", "updateBy", "updateTime", "params", "searchValue", "serialVersionUID"};
List<String> dCList = new ArrayList<>();
dCList.addAll(Arrays.asList(dontCompareFields));
for (Field field : allFields) {
if (dCList.contains(field.getName())) {
continue;
}
TableField tableField = field.getAnnotation(TableField.class);
if (tableField == null || tableField.exist()) {
fieldList.add(field);
}
}
for (Field field : fieldList) {
try {
//抑制Java对其的检查
field.setAccessible(true);
//获取 object field 所代表的属性值
Object comp = field.get(compare);
Object curr = field.get(current);
if (comp != null && curr != null) {
if (!comp.toString().equals(curr.toString())) {
return false;
}
} else {
if ((comp == null && curr != null) || (comp != null && curr == null)) {
return false;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
return true;
}
/**
* 比较原List与现List的差异返回Map
* Map的三个KEY值ADD(新增)\REMOVE删除\NORMAL相同
*
* @param compareList 需要比较的list(页面传过来的
* @param currList 当前的list当前数据库中的
* @return
*/
public static Map<String, Set<Long>> diffList(Set<Long> compareList, Set<Long> currList) {
if (CollectionUtil.isEmpty(compareList) && CollectionUtil.isEmpty(currList)) {
return null;
}
Map<String, Set<Long>> result = new HashMap();
result.put(ADD, new HashSet<Long>());
result.put(REMOVE, new HashSet<Long>());
result.put(NORMAL, new HashSet<Long>());
if (CollectionUtil.isEmpty(compareList)) {
result.put(REMOVE, currList);
} else if (CollectionUtil.isEmpty(currList)) {
result.put(ADD, compareList);
} else {
for (Long comStr : compareList) {
if (currList.contains(comStr)) {
result.get(NORMAL).add(comStr);
} else {
result.get(ADD).add(comStr);
}
}
for (Long currStr : currList) {
if (compareList.contains(currStr)) {
result.get(NORMAL).add(currStr);
} else {
result.get(REMOVE).add(currStr);
}
}
}
return result;
}
public static Map diffList(List<Long> compareList, List<Long> currList) {
Set<Long> compareSet = new HashSet<>();
Set<Long> currSet = new HashSet<>();
if (CollectionUtil.isNotEmpty(compareList)) {
for (Long val : compareList) {
compareSet.add(val);
}
}
if (CollectionUtil.isNotEmpty(currList)) {
for (Long val : currList) {
currSet.add(val);
}
}
return diffList(compareSet, currSet);
}
public static void fillValue(Object object) {
if (object instanceof BaseEntity) {
BaseEntity baseEntity = (BaseEntity) object;
Date current = ObjectUtil.isNotNull(baseEntity.getCreateTime())
? baseEntity.getCreateTime() : new Date();
baseEntity.setCreateTime(current);
baseEntity.setUpdateTime(current);
Long userId = baseEntity.getCreateBy() != null
? baseEntity.getCreateBy() : getLoginUserId();
// 当前已登录 创建人为空 则填充
baseEntity.setCreateBy(userId);
// 当前已登录 更新人为空 则填充
baseEntity.setUpdateBy(userId);
}
}
/**
* 获取登录用户名
*/
private static Long getLoginUserId() {
LoginUser loginUser;
try {
loginUser = LoginHelper.getLoginUser();
} catch (Exception e) {
log.warn("自动注入警告 => 用户未登录");
return null;
}
return loginUser.getUserId();
}
}

View File

@ -0,0 +1,286 @@
package org.dromara.common.mybatis.core.utils;
import cn.hutool.core.util.ReflectUtil;
import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
import org.dromara.common.mybatis.core.mapper.CommonMapper;
import java.util.*;
/**
* 比对列表数据用于判断差异并保存到数据库中
* 全量保存
*/
public class SaveDiffUtil {
// 获取mybatisplus 的ID生成器
private final static IdentifierGenerator IDENTIFIER_GENERATOR = SpringUtils.getBean("idGenerator");
public static Map<String, Set<Long>> saveFormDiff(List formList, List oriList, BaseMapper mapper) {
return saveFormDiff(formList, oriList, mapper, null, null);
}
public static Map<String, Set<Long>> saveFormDiff(List formList, List oriList, BaseMapper mapper, Boolean exeUpdate) {
return saveFormDiff(formList, oriList, mapper, exeUpdate, null);
}
/**
* 需要处理手工修改的记录
*
* @param formList 表单提交的数据
* @param oriList 数据库数据
* @param mapper 处理实体的mapper
* @param exeUpdate 是否执行更新
* @param updateAll 更新操作是否全字段更新
* @throws Exception
*/
public static Map<String, Set<Long>> saveFormDiff(List formList, List oriList, BaseMapper mapper,
Boolean exeUpdate, Boolean updateAll) {
if (exeUpdate == null) {
exeUpdate = false;
}
if (updateAll == null) {
updateAll = false;
}
if (formList == null) {
formList = new ArrayList();
}
Map<String, Set<Long>> result = getFormDiff(formList, oriList);
if (result != null) {
Set<Long> addList = result.get(BeanUtil.ADD);
Set<Long> removeList = result.get(BeanUtil.REMOVE);
Set<Long> updateList = result.get(BeanUtil.NORMAL);
// 添加的对象
if (addList != null && addList.size() > 0) {
List addObjList = new ArrayList();
for (Long id : addList) {
Iterator it = formList.iterator();
while (it.hasNext()) {
Object obj = it.next();
Long objId = (Long) ReflectUtil.getFieldValue(obj, "id");
if (id.equals(objId)) {
addObjList.add(obj);
// it.remove(); 不可删除需要返回
break;
}
}
}
if (addObjList != null && addObjList.size() > 0) {
// List list = cn.hutool.core.bean.BeanUtil.copyToList(addObjList, clz);
((BaseMapperPlus) mapper).insertBatch(addObjList);
}
}
if (removeList != null && removeList.size() > 0) {
mapper.deleteBatchIds(removeList);
}
if (exeUpdate && updateList != null && updateList.size() > 0) {
List updateObjList = new ArrayList();
for (Long id : updateList) {
Object compareObj = findObj(formList, id);
Object currObj = findObj(oriList, id);
// 比对数据库字段是否有修改
boolean diff = BeanUtil.diffObject(compareObj, currObj);
if (!diff) {
updateObjList.add(compareObj);
}
}
if (updateObjList.size() > 0) {
for (Object obj : updateObjList) {
if (updateAll) {
// 全量更新
((CommonMapper) mapper).alwaysUpdateSomeColumnById(obj);
} else {
// 更新不为空
mapper.updateById(obj);
}
}
}
}
}
return result;
}
public static Map<String, Set<Long>> getFormDiff(List formList, List oriList) {
// 需要比较的
List<Long> compareList = new ArrayList<>();
// 原始数据
List<Long> currList = new ArrayList<>();
updateObject(formList, compareList);
updateObject(oriList, currList);
Map<String, Set<Long>> result = BeanUtil.diffList(compareList, currList);
return result;
}
private static void updateObject(List objList, List targetList, String idFieldName) {
if (idFieldName == null) {
idFieldName = "id";
}
for (Object obj : objList) {
Object val = ReflectUtil.getFieldValue(obj, idFieldName);
if (val != null) {
targetList.add(val);
} else {
Long uuid = IDENTIFIER_GENERATOR.nextId(obj).longValue();
targetList.add(uuid);
ReflectUtil.setFieldValue(obj, "id", uuid);
}
}
}
private static void updateObject(List objList, List targetList) {
updateObject(objList, targetList, null);
}
/**
* 根据查询的属性找对象的key并赋值到目标列表中
*
* @param oriList
* @param targetList
* @param searchFieldName
* @param idFieldName
*/
public static void updateObject(List oriList, List targetList, String searchFieldName, String idFieldName) {
if (oriList == null) {
return;
}
for (Object oriObj : oriList) {
Object searchStr = ReflectUtil.getFieldValue(oriObj, searchFieldName);
if (searchStr != null) {
for (Object tarObj : targetList) {
Object tarStr = ReflectUtil.getFieldValue(tarObj, searchFieldName);
if (searchStr.equals(tarStr)) {
Object idStr = ReflectUtil.getFieldValue(oriObj, idFieldName);
if (idStr != null) {
ReflectUtil.setFieldValue(tarObj, idFieldName, idStr);
}
continue;
}
}
}
}
}
/**
* 根据属性及属性值从列表中查找对象
*
* @param objList
* @param idFieldName
* @param keyValue
* @return
*/
private static Object findObj(List objList, String idFieldName, Object keyValue) {
if (idFieldName == null) {
idFieldName = "id";
}
Object result = null;
for (Object obj : objList) {
Object val = ReflectUtil.getFieldValue(obj, idFieldName);
if (keyValue.equals(val)) {
result = obj;
break;
}
}
return result;
}
private static Object findObj(List objList, Object keyValue) {
return findObj(objList, null, keyValue);
}
// /**
// * 修改关系记录根据linkID+pkId添加和删除
// */
// /**
// * @param currentUser 当前用户
// * @param formList 表单提交的数据
// * @param oriList 数据库中的数据
// * @param clz 要插入表中的对象对应的类
// * @param findId 根据哪个属性查找传入的formListoriList
// * @param pkIdName 根据哪个属性删除添加关系记录
// * @param linkIdName 外键字段名称
// * @param linkId 外键字段值
// * @param mapper
// * @throws Exception
// */
// public static void saveRelaDiff(
// List formList,
// List oriList,
// Class clz,
// String findId,
// String pkIdName,
// String linkIdName,
// String linkId,
// BaseMapper mapper) throws Exception {
// // 需要比较的
// List<String> compareList = new ArrayList<>();
// // 原始数据
// List<String> currList = new ArrayList<>();
//
// updateObject(formList, compareList, findId);
// updateObject(oriList, currList, findId);
//
// Map<String, Set<String>> result = BeanUtil.diffList(compareList, currList);
//
// if (result != null) {
// Set<String> addList = result.get(BeanUtil.ADD);
// Set<String> removeList = result.get(BeanUtil.REMOVE);
//
// //删除数据
// if (removeList != null && removeList.size() > 0) {
// for (String removeId : removeList) {
// Object obj = clz.newInstance();
// ReflectUtil.setFieldValue(obj, "delFlag", DelFlag.DELETE);
//
// Example remExample = new Example(clz);
// Example.Criteria remCriteria = remExample.createCriteria();
// //获取删除标记为正常的记录
// remCriteria.andEqualTo(linkIdName, linkId);
// remCriteria.andEqualTo(pkIdName, removeId);
// remCriteria.andNotEqualTo("delFlag", DelFlag.DELETE);
// mapper.updateByExampleSelective(obj, remExample);
// }
// }
//
// //添加资源
// if (addList != null && addList.size() > 0) {
// List addObjList = new ArrayList();
// for (String addId : addList) {
// Object obj = clz.newInstance();
// ReflectUtil.setFieldValue(obj, linkIdName, linkId);
// ReflectUtil.setFieldValue(obj, pkIdName, addId);
//
// BeanUtil.setCreateUser(currentUser, obj);
// BeanUtil.setUpdateUser(currentUser, obj);
//
// addObjList.add(obj);
// }
//
// if (addObjList.size() > 0) {
// mapper.batchInsert(addObjList);
// }
// }
// }
// }
}

View File

@ -12,6 +12,15 @@ import org.dromara.common.tenant.helper.TenantHelper;
*/ */
public class TenantKeyPrefixHandler extends KeyPrefixHandler { public class TenantKeyPrefixHandler extends KeyPrefixHandler {
// 用于获取指定租户下的缓存
private static final ThreadLocal<String> TENANT_ID = new ThreadLocal<>();
public static void setTenantId(String tenantId) {
if (StringUtils.isNotEmpty(tenantId)) {
TENANT_ID.set(tenantId);
}
}
public TenantKeyPrefixHandler(String keyPrefix) { public TenantKeyPrefixHandler(String keyPrefix) {
super(keyPrefix); super(keyPrefix);
} }
@ -27,7 +36,12 @@ public class TenantKeyPrefixHandler extends KeyPrefixHandler {
if (StringUtils.contains(name, GlobalConstants.GLOBAL_REDIS_KEY)) { if (StringUtils.contains(name, GlobalConstants.GLOBAL_REDIS_KEY)) {
return super.map(name); return super.map(name);
} }
String tenantId = TenantHelper.getTenantId();
String tenantId = TENANT_ID.get();
TENANT_ID.remove();
if (tenantId == null) {
tenantId = TenantHelper.getTenantId();
}
if (StringUtils.startsWith(name, tenantId)) { if (StringUtils.startsWith(name, tenantId)) {
// 如果存在则直接返回 // 如果存在则直接返回
return super.map(name); return super.map(name);

View File

@ -4,22 +4,18 @@ import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.exception.NotLoginException; import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.bean.BeanUtil;
import org.dromara.common.core.constant.CacheConstants; import lombok.RequiredArgsConstructor;
import org.dromara.common.core.constant.GlobalConstants;
import org.dromara.common.core.domain.R; import org.dromara.common.core.domain.R;
import org.dromara.common.core.domain.dto.UserOnlineDTO; import org.dromara.common.core.domain.dto.UserOnlineDTO;
import org.dromara.common.core.utils.StreamUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.log.annotation.Log; import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType; import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.redis.utils.RedisUtils;
import org.dromara.common.web.core.BaseController; import org.dromara.common.web.core.BaseController;
import org.dromara.system.domain.SysUserOnline; import org.dromara.system.domain.SysUserOnline;
import lombok.RequiredArgsConstructor; import org.dromara.system.domain.bo.SysOnlineUserBo;
import org.dromara.system.service.ISysUserOnlineService;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@ -33,6 +29,8 @@ import java.util.List;
@RequestMapping("/monitor/online") @RequestMapping("/monitor/online")
public class SysUserOnlineController extends BaseController { public class SysUserOnlineController extends BaseController {
private final ISysUserOnlineService onlineService;
/** /**
* 获取在线用户监控列表 * 获取在线用户监控列表
* *
@ -42,33 +40,12 @@ public class SysUserOnlineController extends BaseController {
@SaCheckPermission("monitor:online:list") @SaCheckPermission("monitor:online:list")
@GetMapping("/list") @GetMapping("/list")
public TableDataInfo<SysUserOnline> list(String ipaddr, String userName) { public TableDataInfo<SysUserOnline> list(String ipaddr, String userName) {
// 获取所有未过期的 token SysOnlineUserBo userBo = new SysOnlineUserBo();
List<String> keys = StpUtil.searchTokenValue("", 0, -1, false); userBo.setIpaddr(ipaddr);
List<UserOnlineDTO> userOnlineDTOList = new ArrayList<>(); userBo.setUserName(userName);
for (String key : keys) { List<UserOnlineDTO> userOnlineDTOList = this.onlineService.getOnlineUsers(null, userBo);
String token = StringUtils.substringAfterLast(key, ":"); // 倒序排序
// 如果已经过期则跳过
if (StpUtil.stpLogic.getTokenActivityTimeoutByToken(token) < -1) {
continue;
}
userOnlineDTOList.add(RedisUtils.getCacheObject(CacheConstants.ONLINE_TOKEN_KEY + token));
}
if (StringUtils.isNotEmpty(ipaddr) && StringUtils.isNotEmpty(userName)) {
userOnlineDTOList = StreamUtils.filter(userOnlineDTOList, userOnline ->
StringUtils.equals(ipaddr, userOnline.getIpaddr()) &&
StringUtils.equals(userName, userOnline.getUserName())
);
} else if (StringUtils.isNotEmpty(ipaddr)) {
userOnlineDTOList = StreamUtils.filter(userOnlineDTOList, userOnline ->
StringUtils.equals(ipaddr, userOnline.getIpaddr())
);
} else if (StringUtils.isNotEmpty(userName)) {
userOnlineDTOList = StreamUtils.filter(userOnlineDTOList, userOnline ->
StringUtils.equals(userName, userOnline.getUserName())
);
}
Collections.reverse(userOnlineDTOList); Collections.reverse(userOnlineDTOList);
userOnlineDTOList.removeAll(Collections.singleton(null));
List<SysUserOnline> userOnlineList = BeanUtil.copyToList(userOnlineDTOList, SysUserOnline.class); List<SysUserOnline> userOnlineList = BeanUtil.copyToList(userOnlineDTOList, SysUserOnline.class);
return TableDataInfo.build(userOnlineList); return TableDataInfo.build(userOnlineList);
} }

View File

@ -0,0 +1,14 @@
package org.dromara.system.domain.bo;
import lombok.Data;
/**
* @Author warden
* @Date 2023/6/19 17:34
*/
@Data
public class SysOnlineUserBo {
private String ipaddr;
private String userName;
// private String tenantId;
}

View File

@ -0,0 +1,13 @@
package org.dromara.system.service;
import org.dromara.common.core.domain.dto.UserOnlineDTO;
import org.dromara.system.domain.bo.SysOnlineUserBo;
import java.util.List;
/**
* @Author warden
*/
public interface ISysUserOnlineService {
List<UserOnlineDTO> getOnlineUsers(String tenantId, SysOnlineUserBo userBo);
}

View File

@ -1,6 +1,8 @@
package org.dromara.system.service.impl; package org.dromara.system.service.impl;
import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.secure.BCrypt; import cn.dev33.satoken.secure.BCrypt;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.convert.Convert; import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil; import cn.hutool.core.util.RandomUtil;
@ -11,6 +13,7 @@ import lombok.RequiredArgsConstructor;
import org.dromara.common.core.constant.CacheNames; import org.dromara.common.core.constant.CacheNames;
import org.dromara.common.core.constant.Constants; import org.dromara.common.core.constant.Constants;
import org.dromara.common.core.constant.TenantConstants; import org.dromara.common.core.constant.TenantConstants;
import org.dromara.common.core.domain.dto.UserOnlineDTO;
import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.MapstructUtils; import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.SpringUtils; import org.dromara.common.core.utils.SpringUtils;
@ -18,10 +21,12 @@ import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.system.domain.*; import org.dromara.system.domain.*;
import org.dromara.system.domain.bo.SysOnlineUserBo;
import org.dromara.system.domain.bo.SysTenantBo; import org.dromara.system.domain.bo.SysTenantBo;
import org.dromara.system.domain.vo.SysTenantVo; import org.dromara.system.domain.vo.SysTenantVo;
import org.dromara.system.mapper.*; import org.dromara.system.mapper.*;
import org.dromara.system.service.ISysTenantService; import org.dromara.system.service.ISysTenantService;
import org.dromara.system.service.ISysUserOnlineService;
import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable; import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -52,6 +57,7 @@ public class SysTenantServiceImpl implements ISysTenantService {
private final SysDictTypeMapper dictTypeMapper; private final SysDictTypeMapper dictTypeMapper;
private final SysDictDataMapper dictDataMapper; private final SysDictDataMapper dictDataMapper;
private final SysConfigMapper configMapper; private final SysConfigMapper configMapper;
private final ISysUserOnlineService onlineService;
/** /**
* 查询租户 * 查询租户
@ -261,6 +267,17 @@ public class SysTenantServiceImpl implements ISysTenantService {
@CacheEvict(cacheNames = CacheNames.SYS_TENANT, key = "#bo.tenantId") @CacheEvict(cacheNames = CacheNames.SYS_TENANT, key = "#bo.tenantId")
@Override @Override
public int updateTenantStatus(SysTenantBo bo) { public int updateTenantStatus(SysTenantBo bo) {
// 停用租户时要将租户下的所有在线用户全部剔除避免依然有权限操作
if (Constants.FAIL.equals(bo.getStatus())) {
List<UserOnlineDTO> userOnlineDTOList = this.onlineService.getOnlineUsers(bo.getTenantId(), null);
for (UserOnlineDTO online : userOnlineDTOList) {
try {
StpUtil.kickoutByTokenValue(online.getTokenId());
} catch (NotLoginException ignored) {
}
}
}
SysTenant tenant = MapstructUtils.convert(bo, SysTenant.class); SysTenant tenant = MapstructUtils.convert(bo, SysTenant.class);
return baseMapper.updateById(tenant); return baseMapper.updateById(tenant);
} }

View File

@ -0,0 +1,61 @@
package org.dromara.system.service.impl;
import cn.dev33.satoken.stp.StpUtil;
import org.dromara.common.core.constant.CacheConstants;
import org.dromara.common.core.domain.dto.UserOnlineDTO;
import org.dromara.common.core.utils.StreamUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.redis.utils.RedisUtils;
import org.dromara.common.tenant.handle.TenantKeyPrefixHandler;
import org.dromara.system.domain.bo.SysOnlineUserBo;
import org.dromara.system.service.ISysUserOnlineService;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service
public class SysUserOnlineServiceImpl implements ISysUserOnlineService {
@Override
public List<UserOnlineDTO> getOnlineUsers(String tenantId, SysOnlineUserBo userBo) {
String ipaddr = userBo == null ? null : userBo.getIpaddr();
String userName = userBo == null ? null : userBo.getUserName();
// 获取所有未过期的 token
List<String> keys = StpUtil.searchTokenValue("", 0, -1, false);
List<UserOnlineDTO> userOnlineDTOList = new ArrayList<>();
for (String key : keys) {
String token = StringUtils.substringAfterLast(key, ":");
// 如果已经过期则跳过
if (StpUtil.stpLogic.getTokenActivityTimeoutByToken(token) < -1) {
continue;
}
TenantKeyPrefixHandler.setTenantId(tenantId);
UserOnlineDTO online = RedisUtils.getCacheObject(CacheConstants.ONLINE_TOKEN_KEY + token);
if (online != null) {
userOnlineDTOList.add(online);
}
}
// // 清空为空的数据
// userOnlineDTOList.removeAll(Collections.singleton(null));
if (StringUtils.isNotEmpty(ipaddr) && StringUtils.isNotEmpty(userName)) {
userOnlineDTOList = StreamUtils.filter(userOnlineDTOList, userOnline ->
StringUtils.equals(ipaddr, userOnline.getIpaddr()) &&
StringUtils.equals(userName, userOnline.getUserName())
);
} else if (StringUtils.isNotEmpty(ipaddr)) {
userOnlineDTOList = StreamUtils.filter(userOnlineDTOList, userOnline ->
StringUtils.equals(ipaddr, userOnline.getIpaddr())
);
} else if (StringUtils.isNotEmpty(userName)) {
userOnlineDTOList = StreamUtils.filter(userOnlineDTOList, userOnline ->
StringUtils.equals(userName, userOnline.getUserName())
);
}
return userOnlineDTOList;
}
}