若依单体项目,目前的会话信息通过
ehcache
存储在本地,不方便集群会话管理,如果你需要将ehcache
替换为redis
来实现集群会话管理。下面详细介绍一下整合步骤。
删除ruoyi-common模块下的pom.xml中的shiro-ehcache依赖
ruoyi-common模块下的pom.xml文件里添加整合依赖
<!-- shiro整合redis -->
<dependency>
<groupId>org.crazycake</groupId>
<artifactId>shiro-redis</artifactId>
<version>3.3.1</version>
<exclusions>
<exclusion>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- springboot整合redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
ruoyi-admin模块下的application-druid.yml文件中添加redis配置
# 数据源配置
spring:
# redis配置
redis:
database: 0
host: 127.0.0.1
port: 6379
password:
timeout: 6000ms # 连接超时时长(毫秒)
lettuce:
pool:
max-active: 1000 # 连接池最大连接数(使用负值表示没有限制)
max-wait: -1ms # 连接池最大阻塞等待时间(使用负值表示没有限制)
max-idle: 10 # 连接池中的最大空闲连接
min-idle: 5 # 连接池中的最小空闲连接
ruoyi-framework\src\main\java\com\ruoyi\framework\shiro\service\SysShiroService.java
ruoyi-framework\src\main\java\com\ruoyi\framework\shiro\session\OnlineSessionDAO.java
ruoyi-framework\src\main\java\com\ruoyi\framework\shiro\web\filter\online\OnlineSessionFilter.java
ruoyi-framework\src\main\java\com\ruoyi\framework\shiro\web\filter\sync\SyncOnlineSessionFilter.java
ruoyi-framework\src\main\java\com\ruoyi\framework\shiro\web\session\OnlineWebSessionManager.java
ruoyi-framework\src\main\java\com\ruoyi\framework\shiro\web\session\SpringSessionValidationScheduler.java
ruoyi-system\src\main\java\com\ruoyi\system\mapper\SysUserOnlineMapper.java
ruoyi-system\src\main\java\com\ruoyi\system\service\ISysUserOnlineService.java
ruoyi-system\src\main\java\com\ruoyi\system\service\impl\SysUserOnlineServiceImpl.java
ruoyi-admin\src\main\resources\ehcache\ehcache-shiro.xml
ruoyi-system\src\main\resources\mapper\system\SysUserOnlineMapper.xml
ruoyi-admin\src\main\java\com\ruoyi\web\controller\monitor\SysUserOnlineController.java
package com.ruoyi.web.controller.monitor;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Deque;
import java.util.Iterator;
import java.util.List;
import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.subject.support.DefaultSubjectContext;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.constant.ShiroConstants;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.ShiroUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.system.domain.SysUserOnline;
/**
* 在线用户监控
*
* @author ruoyi
*/
@Controller
@RequestMapping("/monitor/online")
public class SysUserOnlineController extends BaseController
{
private String prefix = "monitor/online";
@Autowired
private RedisSessionDAO redisSessionDAO;
@RequiresPermissions("monitor:online:view")
@GetMapping()
public String online()
{
return prefix + "/online";
}
@RequiresPermissions("monitor:online:list")
@PostMapping("/list")
@ResponseBody
public TableDataInfo list(SysUserOnline userOnline)
{
String ipaddr = userOnline.getIpaddr();
String loginName = userOnline.getLoginName();
TableDataInfo rspData = new TableDataInfo();
Collection<Session> sessions = redisSessionDAO.getActiveSessions();
Iterator<Session> it = sessions.iterator();
List<SysUserOnline> sessionList = new ArrayList<SysUserOnline>();
while (it.hasNext())
{
SysUserOnline user = getSession(it.next());
if (StringUtils.isNotNull(user))
{
if (StringUtils.isNotEmpty(ipaddr) && StringUtils.isNotEmpty(loginName))
{
if (StringUtils.equals(ipaddr, user.getIpaddr())
&& StringUtils.equals(loginName, user.getLoginName()))
{
sessionList.add(user);
}
}
else if (StringUtils.isNotEmpty(ipaddr))
{
if (StringUtils.equals(ipaddr, user.getIpaddr()))
{
sessionList.add(user);
}
}
else if (StringUtils.isNotEmpty(loginName))
{
if (StringUtils.equals(loginName, user.getLoginName()))
{
sessionList.add(user);
}
}
else
{
sessionList.add(user);
}
}
}
rspData.setRows(sessionList);
rspData.setTotal(sessionList.size());
return rspData;
}
@RequiresPermissions(value = { "monitor:online:batchForceLogout", "monitor:online:forceLogout" }, logical = Logical.OR)
@Log(title = "在线用户", businessType = BusinessType.FORCE)
@PostMapping("/batchForceLogout")
@ResponseBody
public AjaxResult batchForceLogout(@RequestBody List<SysUserOnline> sysUserOnlines)
{
for (SysUserOnline userOnline : sysUserOnlines)
{
String sessionId = userOnline.getSessionId();
String loginName = userOnline.getLoginName();
if (sessionId.equals(ShiroUtils.getSessionId()))
{
return error("当前登录用户无法强退");
}
redisSessionDAO.delete(redisSessionDAO.readSession(sessionId));
removeUserCache(loginName, sessionId);
}
return success();
}
private SysUserOnline getSession(Session session)
{
Object obj = session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
if (null == obj)
{
return null;
}
if (obj instanceof SimplePrincipalCollection)
{
SimplePrincipalCollection spc = (SimplePrincipalCollection) obj;
obj = spc.getPrimaryPrincipal();
if (null != obj && obj instanceof SysUser)
{
SysUser sysUser = (SysUser) obj;
SysUserOnline userOnline = new SysUserOnline();
userOnline.setSessionId(session.getId().toString());
userOnline.setLoginName(sysUser.getLoginName());
if (StringUtils.isNotNull(sysUser.getDept()) && StringUtils.isNotEmpty(sysUser.getDept().getDeptName()))
{
userOnline.setDeptName(sysUser.getDept().getDeptName());
}
userOnline.setIpaddr(session.getHost());
userOnline.setStartTimestamp(session.getStartTimestamp());
userOnline.setLastAccessTime(session.getLastAccessTime());
return userOnline;
}
}
return null;
}
public void removeUserCache(String loginName, String sessionId)
{
Cache<String, Deque<Serializable>> cache = SpringUtils.getBean(RedisCacheManager.class).getCache(ShiroConstants.SYS_USERCACHE);
Deque<Serializable> deque = cache.get(loginName);
if (StringUtils.isEmpty(deque) || deque.size() == 0)
{
return;
}
deque.remove(sessionId);
cache.put(loginName, deque);
}
}
ruoyi-admin\src\main\resources\templates\monitor\online\online.html
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
<th:block th:include="include :: header('在线用户列表')" />
</head>
<body class="gray-bg">
<div class="container-div">
<div class="row">
<div class="col-sm-12 search-collapse">
<form id="online-form">
<div class="select-list">
<ul>
<li>
登录地址:<input type="text" name="ipaddr"/>
</li>
<li>
登录名称:<input type="text" name="loginName"/>
</li>
<li>
<a class="btn btn-primary btn-rounded btn-sm" onclick=".table.search()"><i class="fa fa-search"></i> 搜索</a>
<a class="btn btn-warning btn-rounded btn-sm" onclick=".form.reset()"><i class="fa fa-refresh"></i> 重置</a>
</li>
</ul>
</div>
</form>
</div>
<div class="btn-group-sm" id="toolbar" role="group">
<a class="btn btn-danger multiple disabled" onclick="javascript:batchForceLogout()" shiro:hasPermission="monitor:online:batchForceLogout">
<i class="fa fa-sign-out"></i> 强退
</a>
</div>
<div class="col-sm-12 select-table table-striped">
<table id="bootstrap-table"></table>
</div>
</div>
</div>
<th:block th:include="include :: footer" />
<th:block th:include="include :: bootstrap-table-export-js" />
<script th:inline="javascript">
var forceFlag = [[{@permission.hasPermi('monitor:online:forceLogout')}]];
var prefix = ctx + "monitor/online";(function() {
var options = {
sidePagination: "client",
uniqueId: "sessionId",
url: prefix + "/list",
exportUrl: prefix + "/export",
sortName: "lastAccessTime",
sortOrder: "desc",
showExport: true,
escape: true,
columns: [{
checkbox: true
},
{
title: "序号",
formatter: function (value, row, index) {
return .table.serialNumber(index);
}
},
{
field: 'sessionId',
title: '会话编号',
formatter: function(value, row, index) {
return.table.tooltip(value);
}
},
{
field: 'loginName',
title: '登录名称',
sortable: true
},
{
field: 'deptName',
title: '部门名称'
},
{
field: 'ipaddr',
title: '主机'
},
{
field: 'startTimestamp',
title: '登录时间',
sortable: true
},
{
field: 'lastAccessTime',
title: '最后访问时间',
sortable: true
},
{
title: '操作',
align: 'center',
formatter: function(value, row, index) {
var msg = '<a class="btn btn-danger btn-xs ' + forceFlag + '" href="javascript:void(0)" onclick="forceLogout(\'' + row.sessionId + '\',\'' + row.loginName + '\')"><i class="fa fa-sign-out"></i>强退</a> ';
return msg;
}
}]
};
.table.init(options);
});
// 单条强退
function forceLogout(sessionId, loginName) {.modal.confirm("确定要强制选中用户下线吗?", function() {
var data = [];
var session = {};
session.sessionId = sessionId;
session.loginName = loginName;
data.push(session);
.ajax({
url: prefix + "/batchForceLogout",
method: 'POST',
data: JSON.stringify(data),
headers: {
'Content-Type': 'application/json;charset=utf8'
},
dataType: "json",
beforeSend: function() {.modal.loading("正在处理中,请稍后...");
},
success: function(result) {
if (typeof callback == "function") {
callback(result);
}
.operate.ajaxSuccess(result);
}
});
})
}
// 批量强退
function batchForceLogout() {
var rows =.table.selectColumns("sessionId");
if (rows.length == 0) {
.modal.alertWarning("请选择要强退的用户");
return;
}.modal.confirm("确认要强退选中的" + rows.length + "条数据吗?", function() {
var data = [];
.map(("#" + table.options.id).bootstrapTable('getSelections'), function (row) {
var session = {};
session.sessionId = row.sessionId;
session.loginName = row.loginName;
data.push(session);
});
.ajax({
url: prefix + "/batchForceLogout",
method: 'POST',
data: JSON.stringify(data),
headers: {
'Content-Type': 'application/json;charset=utf8'
},
dataType: "json",
beforeSend: function() {.modal.loading("正在处理中,请稍后...");
},
success: function(result) {
if (typeof callback == "function") {
callback(result);
}
$.operate.ajaxSuccess(result);
}
});
});
}
</script>
</body>
</html>
ruoyi-common\src\main\java\com\ruoyi\common\core\redis\RedisCache.java
package com.ruoyi.common.core.redis;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundSetOperations;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
/**
* spring redis 工具类
*
* @author ruoyi
**/
@SuppressWarnings(value = { "unchecked", "rawtypes" })
@Component
public class RedisCache
{
@Autowired
public RedisTemplate redisTemplate;
/**
* 缓存基本的对象,Integer、String、实体类等
*
* @param key 缓存的键值
* @param value 缓存的值
*/
public <T> void setCacheObject(final String key, final T value)
{
redisTemplate.opsForValue().set(key, value);
}
/**
* 根据key获取缓存中的val+1
*
* @param key 缓存的值
* @return 数值
*/
public Long increment(final String key)
{
return redisTemplate.boundValueOps(key).increment();
}
/**
* 缓存基本的对象,Integer、String、实体类等
*
* @param key 缓存的键值
* @param value 缓存的值
* @param timeout 时间
* @param timeUnit 时间颗粒度
*/
public <T> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit)
{
redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
}
/**
* 设置有效时间
*
* @param key Redis键
* @param timeout 超时时间
* @return true=设置成功;false=设置失败
*/
public boolean expire(final String key, final long timeout)
{
return expire(key, timeout, TimeUnit.SECONDS);
}
/**
* 设置有效时间
*
* @param key Redis键
* @param timeout 超时时间
* @param unit 时间单位
* @return true=设置成功;false=设置失败
*/
public boolean expire(final String key, final long timeout, final TimeUnit unit)
{
return redisTemplate.expire(key, timeout, unit);
}
/**
* 获得缓存的基本对象。
*
* @param key 缓存键值
* @return 缓存键值对应的数据
*/
public <T> T getCacheObject(final String key)
{
ValueOperations<String, T> operation = redisTemplate.opsForValue();
return operation.get(key);
}
/**
* 删除单个对象
*
* @param key
*/
public boolean deleteObject(final String key)
{
return redisTemplate.delete(key);
}
/**
* 删除集合对象
*
* @param collection 多个对象
* @return
*/
public long deleteObject(final Collection collection)
{
return redisTemplate.delete(collection);
}
/**
* 缓存List数据
*
* @param key 缓存的键值
* @param dataList 待缓存的List数据
* @return 缓存的对象
*/
public <T> long setCacheList(final String key, final List<T> dataList)
{
Long count = redisTemplate.opsForList().rightPushAll(key, dataList);
return count == null ? 0 : count;
}
/**
* 获得缓存的list对象
*
* @param key 缓存的键值
* @return 缓存键值对应的数据
*/
public <T> List<T> getCacheList(final String key)
{
return redisTemplate.opsForList().range(key, 0, -1);
}
/**
* 缓存Set
*
* @param key 缓存键值
* @param dataSet 缓存的数据
* @return 缓存数据的对象
*/
public <T> BoundSetOperations<String, T> setCacheSet(final String key, final Set<T> dataSet)
{
BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key);
Iterator<T> it = dataSet.iterator();
while (it.hasNext())
{
setOperation.add(it.next());
}
return setOperation;
}
/**
* 获得缓存的set
*
* @param key
* @return
*/
public <T> Set<T> getCacheSet(final String key)
{
return redisTemplate.opsForSet().members(key);
}
/**
* 缓存Map
*
* @param key
* @param dataMap
*/
public <T> void setCacheMap(final String key, final Map<String, T> dataMap)
{
if (dataMap != null) {
redisTemplate.opsForHash().putAll(key, dataMap);
}
}
/**
* 获得缓存的Map
*
* @param key
* @return
*/
public <T> Map<String, T> getCacheMap(final String key)
{
return redisTemplate.opsForHash().entries(key);
}
/**
* 往Hash中存入数据
*
* @param key Redis键
* @param hKey Hash键
* @param value 值
*/
public <T> void setCacheMapValue(final String key, final String hKey, final T value)
{
redisTemplate.opsForHash().put(key, hKey, value);
}
/**
* 获取Hash中的数据
*
* @param key Redis键
* @param hKey Hash键
* @return Hash中的对象
*/
public <T> T getCacheMapValue(final String key, final String hKey)
{
HashOperations<String, String, T> opsForHash = redisTemplate.opsForHash();
return opsForHash.get(key, hKey);
}
/**
* 获取多个Hash中的数据
*
* @param key Redis键
* @param hKeys Hash键集合
* @return Hash对象集合
*/
public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys)
{
return redisTemplate.opsForHash().multiGet(key, hKeys);
}
/**
* 获得缓存的基本对象列表
*
* @param pattern 字符串前缀
* @return 对象列表
*/
public Collection<String> keys(final String pattern)
{
return redisTemplate.keys(pattern);
}
}
ruoyi-common\src\main\java\com\ruoyi\common\utils\CacheUtils.java
package com.ruoyi.common.utils;
import java.util.Iterator;
import java.util.Set;
import org.apache.shiro.cache.Cache;
import org.crazycake.shiro.RedisCache;
import org.crazycake.shiro.RedisCacheManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.ruoyi.common.utils.spring.SpringUtils;
/**
* Cache工具类
*
* @author ruoyi
*/
public class CacheUtils
{
private static Logger logger = LoggerFactory.getLogger(CacheUtils.class);
private static RedisCacheManager cacheManager = SpringUtils.getBean(RedisCacheManager.class);
private static final String SYS_CACHE = "sys-cache";
/**
* 获取SYS_CACHE缓存
*
* @param key
* @return
*/
public static Object get(String key)
{
return get(SYS_CACHE, key);
}
/**
* 获取SYS_CACHE缓存
*
* @param key
* @param defaultValue
* @return
*/
public static Object get(String key, Object defaultValue)
{
Object value = get(key);
return value != null ? value : defaultValue;
}
/**
* 写入SYS_CACHE缓存
*
* @param key
* @return
*/
public static void put(String key, Object value)
{
put(SYS_CACHE, key, value);
}
/**
* 从SYS_CACHE缓存中移除
*
* @param key
* @return
*/
public static void remove(String key)
{
remove(SYS_CACHE, key);
}
/**
* 获取缓存
*
* @param cacheName
* @param key
* @return
*/
public static Object get(String cacheName, String key)
{
return getCache(cacheName).get(getKey(key));
}
/**
* 获取缓存
*
* @param cacheName
* @param key
* @param defaultValue
* @return
*/
public static Object get(String cacheName, String key, Object defaultValue)
{
Object value = get(cacheName, getKey(key));
return value != null ? value : defaultValue;
}
/**
* 写入缓存
*
* @param cacheName
* @param key
* @param value
*/
public static void put(String cacheName, String key, Object value)
{
getCache(cacheName).put(getKey(key), value);
}
/**
* 从缓存中移除
*
* @param cacheName
* @param key
*/
public static void remove(String cacheName, String key)
{
getCache(cacheName).remove(getKey(key));
}
/**
* 从缓存中移除所有
*
* @param cacheName
*/
public static void removeAll(String cacheName)
{
RedisCache<String, Object> cache = getCache(cacheName);
Set<String> keys = cache.keys();
for (Iterator<String> it = keys.iterator(); it.hasNext();)
{
cache.remove(it.next());
}
logger.info("清理缓存: {} => {}", cacheName, keys);
}
/**
* 从缓存中移除指定key
*
* @param keys
*/
public static void removeByKeys(Set<String> keys)
{
removeByKeys(SYS_CACHE, keys);
}
/**
* 从缓存中移除指定key
*
* @param cacheName
* @param keys
*/
public static void removeByKeys(String cacheName, Set<String> keys)
{
for (Iterator<String> it = keys.iterator(); it.hasNext();)
{
remove(it.next());
}
logger.info("清理缓存: {} => {}", cacheName, keys);
}
/**
* 获取缓存键名
*
* @param key
* @return
*/
private static String getKey(String key)
{
return key;
}
/**
* 获得一个Cache,没有则显示日志。
*
* @param cacheName
* @return
*/
public static RedisCache<String, Object> getCache(String cacheName)
{
Cache<String, Object> cache = cacheManager.getCache(cacheName);
if (cache == null)
{
throw new RuntimeException("当前系统中没有定义“" + cacheName + "”这个缓存。");
}
return (RedisCache<String, Object>) cache;
}
}
ruoyi-common\src\main\java\com\ruoyi\common\utils\DictUtils.java
package com.ruoyi.common.utils;
import java.util.Collection;
import java.util.List;
import org.springframework.stereotype.Component;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.entity.SysDictData;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.spring.SpringUtils;
/**
* 字典工具类
*
* @author ruoyi
*/
@Component
public class DictUtils
{
/**
* 分隔符
*/
public static final String SEPARATOR = ",";
/**
* 设置字典缓存
*
* @param key 参数键
* @param dictDatas 字典数据列表
*/
public static void setDictCache(String key, List<SysDictData> dictDatas)
{
SpringUtils.getBean(RedisCache.class).setCacheObject(getCacheKey(key), dictDatas);
}
/**
* 获取字典缓存
*
* @param key 参数键
* @return dictDatas 字典数据列表
*/
public static List<SysDictData> getDictCache(String key)
{
Object cacheObj = SpringUtils.getBean(RedisCache.class).getCacheObject(getCacheKey(key));
if (StringUtils.isNotNull(cacheObj))
{
List<SysDictData> DictDatas = StringUtils.cast(cacheObj);
return DictDatas;
}
return null;
}
/**
* 根据字典类型和字典值获取字典标签
*
* @param dictType 字典类型
* @param dictValue 字典值
* @return 字典标签
*/
public static String getDictLabel(String dictType, String dictValue)
{
return getDictLabel(dictType, dictValue, SEPARATOR);
}
/**
* 根据字典类型和字典标签获取字典值
*
* @param dictType 字典类型
* @param dictLabel 字典标签
* @return 字典值
*/
public static String getDictValue(String dictType, String dictLabel)
{
return getDictValue(dictType, dictLabel, SEPARATOR);
}
/**
* 根据字典类型和字典值获取字典标签
*
* @param dictType 字典类型
* @param dictValue 字典值
* @param separator 分隔符
* @return 字典标签
*/
public static String getDictLabel(String dictType, String dictValue, String separator)
{
StringBuilder propertyString = new StringBuilder();
List<SysDictData> datas = getDictCache(dictType);
if (StringUtils.containsAny(separator, dictValue) && StringUtils.isNotEmpty(datas))
{
for (SysDictData dict : datas)
{
for (String value : dictValue.split(separator))
{
if (value.equals(dict.getDictValue()))
{
propertyString.append(dict.getDictLabel() + separator);
break;
}
}
}
}
else
{
for (SysDictData dict : datas)
{
if (dictValue.equals(dict.getDictValue()))
{
return dict.getDictLabel();
}
}
}
return StringUtils.stripEnd(propertyString.toString(), separator);
}
/**
* 根据字典类型和字典标签获取字典值
*
* @param dictType 字典类型
* @param dictLabel 字典标签
* @param separator 分隔符
* @return 字典值
*/
public static String getDictValue(String dictType, String dictLabel, String separator)
{
StringBuilder propertyString = new StringBuilder();
List<SysDictData> datas = getDictCache(dictType);
if (StringUtils.containsAny(separator, dictLabel) && StringUtils.isNotEmpty(datas))
{
for (SysDictData dict : datas)
{
for (String label : dictLabel.split(separator))
{
if (label.equals(dict.getDictLabel()))
{
propertyString.append(dict.getDictValue() + separator);
break;
}
}
}
}
else
{
for (SysDictData dict : datas)
{
if (dictLabel.equals(dict.getDictLabel()))
{
return dict.getDictValue();
}
}
}
return StringUtils.stripEnd(propertyString.toString(), separator);
}
/**
* 删除指定字典缓存
*
* @param key 字典键
*/
public static void removeDictCache(String key)
{
SpringUtils.getBean(RedisCache.class).deleteObject(getCacheKey(key));
}
/**
* 清空字典缓存
*/
public static void clearDictCache()
{
Collection<String> keys = SpringUtils.getBean(RedisCache.class).keys(Constants.SYS_DICT_KEY + "*");
SpringUtils.getBean(RedisCache.class).deleteObject(keys);
}
/**
* 获取cache name
*
* @return 缓存名
*/
public static String getCacheName()
{
return Constants.SYS_DICT_CACHE;
}
/**
* 设置cache key
*
* @param configKey 参数键
* @return 缓存键key
*/
public static String getCacheKey(String configKey)
{
return Constants.SYS_DICT_KEY + configKey;
}
}
ruoyi-framework\src\main\java\com\ruoyi\framework\config\FastJson2JsonRedisSerializer.java
package com.ruoyi.framework.config;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.TypeFactory;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;
import com.alibaba.fastjson.parser.ParserConfig;
import org.springframework.util.Assert;
import java.nio.charset.Charset;
/**
* Redis使用FastJson序列化
*
* @author ruoyi
*/
public class FastJson2JsonRedisSerializer<T> implements RedisSerializer<T>
{
@SuppressWarnings("unused")
private ObjectMapper objectMapper = new ObjectMapper();
public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
private Class<T> clazz;
static
{
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
}
public FastJson2JsonRedisSerializer(Class<T> clazz)
{
super();
this.clazz = clazz;
}
@Override
public byte[] serialize(T t) throws SerializationException
{
if (t == null)
{
return new byte[0];
}
return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET);
}
@Override
public T deserialize(byte[] bytes) throws SerializationException
{
if (bytes == null || bytes.length <= 0)
{
return null;
}
String str = new String(bytes, DEFAULT_CHARSET);
return JSON.parseObject(str, clazz);
}
public void setObjectMapper(ObjectMapper objectMapper)
{
Assert.notNull(objectMapper, "'objectMapper' must not be null");
this.objectMapper = objectMapper;
}
protected JavaType getJavaType(Class<?> clazz)
{
return TypeFactory.defaultInstance().constructType(clazz);
}
}
ruoyi-framework\src\main\java\com\ruoyi\framework\config\RedisConfig.java
package com.ruoyi.framework.config;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
/**
* redis配置
*
* @author ruoyi
*/
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport
{
@Bean
@SuppressWarnings(value = { "unchecked", "rawtypes" })
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory)
{
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class);
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
serializer.setObjectMapper(mapper);
template.setValueSerializer(serializer);
// 使用StringRedisSerializer来序列化和反序列化redis的key值
template.setKeySerializer(new StringRedisSerializer());
template.afterPropertiesSet();
return template;
}
}
ruoyi-framework\src\main\java\com\ruoyi\framework\manager\factory\AsyncFactory.java
package com.ruoyi.framework.manager.factory;
import java.util.TimerTask;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.utils.AddressUtils;
import com.ruoyi.common.utils.LogUtils;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.ShiroUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.system.domain.SysLogininfor;
import com.ruoyi.system.domain.SysOperLog;
import com.ruoyi.system.service.ISysOperLogService;
import com.ruoyi.system.service.impl.SysLogininforServiceImpl;
import eu.bitwalker.useragentutils.UserAgent;
/**
* 异步工厂(产生任务用)
*
* @author liuhulu
*
*/
public class AsyncFactory
{
private static final Logger sys_user_logger = LoggerFactory.getLogger("sys-user");
/**
* 操作日志记录
*
* @param operLog 操作日志信息
* @return 任务task
*/
public static TimerTask recordOper(final SysOperLog operLog)
{
return new TimerTask()
{
@Override
public void run()
{
// 远程查询操作地点
operLog.setOperLocation(AddressUtils.getRealAddressByIP(operLog.getOperIp()));
SpringUtils.getBean(ISysOperLogService.class).insertOperlog(operLog);
}
};
}
/**
* 记录登录信息
*
* @param username 用户名
* @param status 状态
* @param message 消息
* @param args 列表
* @return 任务task
*/
public static TimerTask recordLogininfor(final String username, final String status, final String message, final Object... args)
{
final UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent"));
final String ip = ShiroUtils.getIp();
return new TimerTask()
{
@Override
public void run()
{
String address = AddressUtils.getRealAddressByIP(ip);
StringBuilder s = new StringBuilder();
s.append(LogUtils.getBlock(ip));
s.append(address);
s.append(LogUtils.getBlock(username));
s.append(LogUtils.getBlock(status));
s.append(LogUtils.getBlock(message));
// 打印信息到日志
sys_user_logger.info(s.toString(), args);
// 获取客户端操作系统
String os = userAgent.getOperatingSystem().getName();
// 获取客户端浏览器
String browser = userAgent.getBrowser().getName();
// 封装对象
SysLogininfor logininfor = new SysLogininfor();
logininfor.setLoginName(username);
logininfor.setIpaddr(ip);
logininfor.setLoginLocation(address);
logininfor.setBrowser(browser);
logininfor.setOs(os);
logininfor.setMsg(message);
// 日志状态
if (StringUtils.equalsAny(status, Constants.LOGIN_SUCCESS, Constants.LOGOUT, Constants.REGISTER))
{
logininfor.setStatus(Constants.SUCCESS);
}
else if (Constants.LOGIN_FAIL.equals(status))
{
logininfor.setStatus(Constants.FAIL);
}
// 插入数据
SpringUtils.getBean(SysLogininforServiceImpl.class).insertLogininfor(logininfor);
}
};
}
}
ruoyi-framework\src\main\java\com\ruoyi\framework\manager\ShutdownManager.java
package com.ruoyi.framework.manager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.annotation.PreDestroy;
/**
* 确保应用退出时能关闭后台线程
*
* @author ruoyi
*/
@Component
public class ShutdownManager
{
private static final Logger logger = LoggerFactory.getLogger("sys-user");
@PreDestroy
public void destroy()
{
shutdownAsyncManager();
}
/**
* 停止异步执行任务
*/
private void shutdownAsyncManager()
{
try
{
logger.info("====关闭后台任务任务线程池====");
AsyncManager.me().shutdown();
}
catch (Exception e)
{
logger.error(e.getMessage(), e);
}
}
}
ruoyi-framework\src\main\java\com\ruoyi\framework\shiro\service\SysPasswordService.java
package com.ruoyi.framework.shiro.service;
import java.util.concurrent.TimeUnit;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.exception.user.UserPasswordNotMatchException;
import com.ruoyi.common.exception.user.UserPasswordRetryLimitExceedException;
import com.ruoyi.common.utils.MessageUtils;
import com.ruoyi.framework.manager.AsyncManager;
import com.ruoyi.framework.manager.factory.AsyncFactory;
/**
* 登录密码方法
*
* @author ruoyi
*/
@Component
public class SysPasswordService
{
@Autowired
private RedisCache redisCache;
@Value(value = "${user.password.maxRetryCount}")
private String maxRetryCount;
/**
* 登录记录 cache key
*/
private final String SYS_LOGINRECORDCACHE_KEY = "sys_loginRecordCache:";
/**
* 设置cache key
*
* @param loginName 登录名
* @return 缓存键key
*/
private String getCacheKey(String loginName)
{
return SYS_LOGINRECORDCACHE_KEY + loginName;
}
public void validate(SysUser user, String password)
{
String loginName = user.getLoginName();
Integer retryCount = redisCache.getCacheObject(getCacheKey(loginName));
if (retryCount == null)
{
retryCount = 0;
redisCache.setCacheObject(getCacheKey(loginName), retryCount, 10, TimeUnit.MINUTES);
}
if (retryCount >= Integer.valueOf(maxRetryCount).intValue())
{
AsyncManager.me().execute(AsyncFactory.recordLogininfor(loginName, Constants.LOGIN_FAIL, MessageUtils.message("user.password.retry.limit.exceed", maxRetryCount)));
throw new UserPasswordRetryLimitExceedException(Integer.valueOf(maxRetryCount).intValue());
}
if (!matches(user, password))
{
retryCount = retryCount + 1;
AsyncManager.me().execute(AsyncFactory.recordLogininfor(loginName, Constants.LOGIN_FAIL, MessageUtils.message("user.password.retry.limit.count", retryCount)));
redisCache.setCacheObject(getCacheKey(loginName), retryCount, 10, TimeUnit.MINUTES);
throw new UserPasswordNotMatchException();
}
else
{
clearLoginRecordCache(loginName);
}
}
public boolean matches(SysUser user, String newPassword)
{
return user.getPassword().equals(encryptPassword(user.getLoginName(), newPassword, user.getSalt()));
}
public void clearLoginRecordCache(String loginName)
{
redisCache.deleteObject(getCacheKey(loginName));
}
public String encryptPassword(String loginName, String password, String salt)
{
return new Md5Hash(loginName + password + salt).toHex();
}
}
ruoyi-framework\src\main\java\com\ruoyi\framework\shiro\web\filter\LogoutFilter.java
package com.ruoyi.framework.shiro.web.filter;
import java.io.Serializable;
import java.util.Deque;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.session.SessionException;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.constant.ShiroConstants;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.utils.MessageUtils;
import com.ruoyi.common.utils.ShiroUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.framework.manager.AsyncManager;
import com.ruoyi.framework.manager.factory.AsyncFactory;
/**
* 退出过滤器
*
* @author ruoyi
*/
public class LogoutFilter extends org.apache.shiro.web.filter.authc.LogoutFilter
{
private static final Logger log = LoggerFactory.getLogger(LogoutFilter.class);
/**
* 退出后重定向的地址
*/
private String loginUrl;
private Cache<String, Deque<Serializable>> cache;
public String getLoginUrl()
{
return loginUrl;
}
public void setLoginUrl(String loginUrl)
{
this.loginUrl = loginUrl;
}
@Override
protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception
{
try
{
Subject subject = getSubject(request, response);
String redirectUrl = getRedirectUrl(request, response, subject);
try
{
SysUser user = ShiroUtils.getSysUser();
if (StringUtils.isNotNull(user))
{
String loginName = user.getLoginName();
// 记录用户退出日志
AsyncManager.me().execute(AsyncFactory.recordLogininfor(loginName, Constants.LOGOUT, MessageUtils.message("user.logout.success")));
// 清理缓存
removeUserCache(loginName, ShiroUtils.getSessionId());
}
// 退出登录
subject.logout();
}
catch (SessionException ise)
{
log.error("logout fail.", ise);
}
issueRedirect(request, response, redirectUrl);
}
catch (Exception e)
{
log.error("Encountered session exception during logout. This can generally safely be ignored.", e);
}
return false;
}
public void removeUserCache(String loginName, String sessionId)
{
Deque<Serializable> deque = cache.get(loginName);
if (StringUtils.isEmpty(deque) || deque.size() == 0)
{
return;
}
deque.remove(sessionId);
cache.put(loginName, deque);
}
/**
* 退出跳转URL
*/
@Override
protected String getRedirectUrl(ServletRequest request, ServletResponse response, Subject subject)
{
String url = getLoginUrl();
if (StringUtils.isNotEmpty(url))
{
return url;
}
return super.getRedirectUrl(request, response, subject);
}
// 设置Cache的key的前缀
public void setCacheManager(CacheManager cacheManager)
{
// 必须和ehcache缓存配置中的缓存name一致
this.cache = cacheManager.getCache(ShiroConstants.SYS_USERCACHE);
}
}
ruoyi-framework\src\main\java\com\ruoyi\framework\web\service\CacheService.java
package com.ruoyi.framework.web.service;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import org.apache.shiro.session.mgt.SimpleSession;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.subject.support.DefaultSubjectContext;
import org.crazycake.shiro.IRedisManager;
import org.crazycake.shiro.exception.SerializationException;
import org.crazycake.shiro.serializer.ObjectSerializer;
import org.crazycake.shiro.serializer.StringSerializer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.redis.RedisCache;
/**
* 缓存操作处理
*
* @author ruoyi
*/
@Service
public class CacheService
{
@Autowired
private RedisCache redisCache;
@Autowired
private IRedisManager redisManager;
private final String DEFAULT_SESSION_KEY_PREFIX = "shiro:session:";
/**
* 获取所有缓存名称
*
* @return 缓存列表
*/
public String[] getCacheNames()
{
String[] cacheNames = { "shiro:session", "shiro:cache:sys-authCache", "shiro:cache:sys-userCache", "sys_dict",
"sys_config", "sys_loginRecordCache" };
return cacheNames;
}
/**
* 根据缓存名称获取所有键名
*
* @param cacheName 缓存名称
* @return 键名列表
*/
public Set<String> getCacheKeys(String cacheName)
{
Set<String> tmpKeys = new HashSet<String>();
Collection<String> cacheKeys = redisCache.keys(cacheName + ":*");
for (String cacheKey : cacheKeys)
{
tmpKeys.add(cacheKey);
}
return tmpKeys;
}
/**
* 根据缓存名称和键名获取内容值
*
* @param cacheName 缓存名称
* @param cacheKey 键名
* @return 键值
*/
public Object getCacheValue(String cacheName, String cacheKey)
{
if (cacheKey.contains(DEFAULT_SESSION_KEY_PREFIX))
{
try
{
SimpleSession simpleSession = (SimpleSession) new ObjectSerializer().deserialize(redisManager.get(new StringSerializer().serialize(cacheKey)));
Object obj = simpleSession.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
if (null == obj)
{
return "未登录会话";
}
if (obj instanceof SimplePrincipalCollection)
{
SimplePrincipalCollection spc = (SimplePrincipalCollection) obj;
obj = spc.getPrimaryPrincipal();
if (null != obj && obj instanceof SysUser)
{
SysUser sysUser = (SysUser) obj;
return sysUser;
}
}
return obj;
}
catch (SerializationException e)
{
return null;
}
}
return redisCache.getCacheObject(cacheKey);
}
/**
* 根据名称删除缓存信息
*
* @param cacheName 缓存名称
*/
public void clearCacheName(String cacheName)
{
Collection<String> cacheKeys = redisCache.keys(cacheName + ":*");
redisCache.deleteObject(cacheKeys);
}
/**
* 根据名称和键名删除缓存信息
*
* @param cacheName 缓存名称
* @param cacheKey 键名
*/
public void clearCacheKey(String cacheName, String cacheKey)
{
redisCache.deleteObject(cacheKey);
}
/**
* 清理所有缓存
*/
public void clearAll()
{
Collection<String> cacheKeys = redisCache.keys("*");
redisCache.deleteObject(cacheKeys);
}
}
ruoyi-system\src\main\java\com\ruoyi\system\service\impl\SysConfigServiceImpl.java
package com.ruoyi.system.service.impl;
import java.util.Collection;
import java.util.List;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.core.text.Convert;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.domain.SysConfig;
import com.ruoyi.system.mapper.SysConfigMapper;
import com.ruoyi.system.service.ISysConfigService;
/**
* 参数配置 服务层实现
*
* @author ruoyi
*/
@Service
public class SysConfigServiceImpl implements ISysConfigService
{
@Autowired
private SysConfigMapper configMapper;
@Autowired
private RedisCache redisCache;
/**
* 项目启动时,初始化参数到缓存
*/
@PostConstruct
public void init()
{
loadingConfigCache();
}
/**
* 查询参数配置信息
*
* @param configId 参数配置ID
* @return 参数配置信息
*/
@Override
public SysConfig selectConfigById(Long configId)
{
SysConfig config = new SysConfig();
config.setConfigId(configId);
return configMapper.selectConfig(config);
}
/**
* 根据键名查询参数配置信息
*
* @param configKey 参数key
* @return 参数键值
*/
@Override
public String selectConfigByKey(String configKey)
{
String configValue = Convert.toStr(redisCache.getCacheObject(getCacheKey(configKey)));
if (StringUtils.isNotEmpty(configValue))
{
return configValue;
}
SysConfig config = new SysConfig();
config.setConfigKey(configKey);
SysConfig retConfig = configMapper.selectConfig(config);
if (StringUtils.isNotNull(retConfig))
{
redisCache.setCacheObject(getCacheKey(configKey), retConfig.getConfigValue());
return retConfig.getConfigValue();
}
return StringUtils.EMPTY;
}
/**
* 查询参数配置列表
*
* @param config 参数配置信息
* @return 参数配置集合
*/
@Override
public List<SysConfig> selectConfigList(SysConfig config)
{
return configMapper.selectConfigList(config);
}
/**
* 新增参数配置
*
* @param config 参数配置信息
* @return 结果
*/
@Override
public int insertConfig(SysConfig config)
{
int row = configMapper.insertConfig(config);
if (row > 0)
{
redisCache.setCacheObject(getCacheKey(config.getConfigKey()), config.getConfigValue());
}
return row;
}
/**
* 修改参数配置
*
* @param config 参数配置信息
* @return 结果
*/
@Override
public int updateConfig(SysConfig config)
{
int row = configMapper.updateConfig(config);
if (row > 0)
{
redisCache.setCacheObject(getCacheKey(config.getConfigKey()), config.getConfigValue());
}
return row;
}
/**
* 批量删除参数配置对象
*
* @param ids 需要删除的数据ID
* @return 结果
*/
@Override
public void deleteConfigByIds(String ids)
{
Long[] configIds = Convert.toLongArray(ids);
for (Long configId : configIds)
{
SysConfig config = selectConfigById(configId);
if (StringUtils.equals(UserConstants.YES, config.getConfigType()))
{
throw new ServiceException(String.format("内置参数【%1$s】不能删除 ", config.getConfigKey()));
}
configMapper.deleteConfigById(configId);
redisCache.deleteObject(getCacheKey(config.getConfigKey()));
}
}
/**
* 加载参数缓存数据
*/
@Override
public void loadingConfigCache()
{
List<SysConfig> configsList = configMapper.selectConfigList(new SysConfig());
for (SysConfig config : configsList)
{
redisCache.setCacheObject(getCacheKey(config.getConfigKey()), config.getConfigValue());
}
}
/**
* 清空参数缓存数据
*/
@Override
public void clearConfigCache()
{
Collection<String> keys = redisCache.keys(Constants.SYS_CONFIG_KEY + "*");
redisCache.deleteObject(keys);
}
/**
* 重置参数缓存数据
*/
@Override
public void resetConfigCache()
{
clearConfigCache();
loadingConfigCache();
}
/**
* 校验参数键名是否唯一
*
* @param config 参数配置信息
* @return 结果
*/
@Override
public String checkConfigKeyUnique(SysConfig config)
{
Long configId = StringUtils.isNull(config.getConfigId()) ? -1L : config.getConfigId();
SysConfig info = configMapper.checkConfigKeyUnique(config.getConfigKey());
if (StringUtils.isNotNull(info) && info.getConfigId().longValue() != configId.longValue())
{
return UserConstants.CONFIG_KEY_NOT_UNIQUE;
}
return UserConstants.CONFIG_KEY_UNIQUE;
}
/**
* 设置cache key
*
* @param configKey 参数键
* @return 缓存键key
*/
private String getCacheKey(String configKey)
{
return Constants.SYS_CONFIG_KEY + configKey;
}
}
ruoyi-system\src\main\java\com\ruoyi\system\service\impl\SysDictTypeServiceImpl.java
package com.ruoyi.system.service.impl;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.domain.Ztree;
import com.ruoyi.common.core.domain.entity.SysDictData;
import com.ruoyi.common.core.domain.entity.SysDictType;
import com.ruoyi.common.core.text.Convert;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.DictUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.mapper.SysDictDataMapper;
import com.ruoyi.system.mapper.SysDictTypeMapper;
import com.ruoyi.system.service.ISysDictTypeService;
/**
* 字典 业务层处理
*
* @author ruoyi
*/
@Service
public class SysDictTypeServiceImpl implements ISysDictTypeService
{
@Autowired
private SysDictTypeMapper dictTypeMapper;
@Autowired
private SysDictDataMapper dictDataMapper;
/**
* 项目启动时,初始化字典到缓存
*/
@PostConstruct
public void init()
{
loadingDictCache();
}
/**
* 根据条件分页查询字典类型
*
* @param dictType 字典类型信息
* @return 字典类型集合信息
*/
@Override
public List<SysDictType> selectDictTypeList(SysDictType dictType)
{
return dictTypeMapper.selectDictTypeList(dictType);
}
/**
* 根据所有字典类型
*
* @return 字典类型集合信息
*/
@Override
public List<SysDictType> selectDictTypeAll()
{
return dictTypeMapper.selectDictTypeAll();
}
/**
* 根据字典类型查询字典数据
*
* @param dictType 字典类型
* @return 字典数据集合信息
*/
@Override
public List<SysDictData> selectDictDataByType(String dictType)
{
List<SysDictData> dictDatas = DictUtils.getDictCache(dictType);
if (StringUtils.isNotEmpty(dictDatas))
{
return dictDatas;
}
dictDatas = dictDataMapper.selectDictDataByType(dictType);
if (StringUtils.isNotEmpty(dictDatas))
{
DictUtils.setDictCache(dictType, dictDatas);
return dictDatas;
}
return null;
}
/**
* 根据字典类型ID查询信息
*
* @param dictId 字典类型ID
* @return 字典类型
*/
@Override
public SysDictType selectDictTypeById(Long dictId)
{
return dictTypeMapper.selectDictTypeById(dictId);
}
/**
* 根据字典类型查询信息
*
* @param dictType 字典类型
* @return 字典类型
*/
@Override
public SysDictType selectDictTypeByType(String dictType)
{
return dictTypeMapper.selectDictTypeByType(dictType);
}
/**
* 批量删除字典类型
*
* @param ids 需要删除的数据
* @return 结果
*/
@Override
public void deleteDictTypeByIds(String ids)
{
Long[] dictIds = Convert.toLongArray(ids);
for (Long dictId : dictIds)
{
SysDictType dictType = selectDictTypeById(dictId);
if (dictDataMapper.countDictDataByType(dictType.getDictType()) > 0)
{
throw new ServiceException(String.format("%1$s已分配,不能删除", dictType.getDictName()));
}
dictTypeMapper.deleteDictTypeById(dictId);
DictUtils.removeDictCache(dictType.getDictType());
}
}
/**
* 加载字典缓存数据
*/
public void loadingDictCache()
{
List<SysDictType> dictTypeList = dictTypeMapper.selectDictTypeAll();
for (SysDictType dict : dictTypeList)
{
List<SysDictData> dictDatas = dictDataMapper.selectDictDataByType(dict.getDictType());
DictUtils.setDictCache(dict.getDictType(), dictDatas);
}
}
/**
* 清空字典缓存数据
*/
public void clearDictCache()
{
DictUtils.clearDictCache();
}
/**
* 重置字典缓存数据
*/
public void resetDictCache()
{
clearDictCache();
loadingDictCache();
}
/**
* 新增保存字典类型信息
*
* @param dict 字典类型信息
* @return 结果
*/
@Override
public int insertDictType(SysDictType dict)
{
int row = dictTypeMapper.insertDictType(dict);
if (row > 0)
{
DictUtils.setDictCache(dict.getDictType(), null);
}
return row;
}
/**
* 修改保存字典类型信息
*
* @param dict 字典类型信息
* @return 结果
*/
@Override
@Transactional
public int updateDictType(SysDictType dict)
{
SysDictType oldDict = dictTypeMapper.selectDictTypeById(dict.getDictId());
dictDataMapper.updateDictDataType(oldDict.getDictType(), dict.getDictType());
int row = dictTypeMapper.updateDictType(dict);
if (row > 0)
{
List<SysDictData> dictDatas = dictDataMapper.selectDictDataByType(dict.getDictType());
DictUtils.setDictCache(dict.getDictType(), dictDatas);
}
return row;
}
/**
* 校验字典类型称是否唯一
*
* @param dict 字典类型
* @return 结果
*/
@Override
public String checkDictTypeUnique(SysDictType dict)
{
Long dictId = StringUtils.isNull(dict.getDictId()) ? -1L : dict.getDictId();
SysDictType dictType = dictTypeMapper.checkDictTypeUnique(dict.getDictType());
if (StringUtils.isNotNull(dictType) && dictType.getDictId().longValue() != dictId.longValue())
{
return UserConstants.DICT_TYPE_NOT_UNIQUE;
}
return UserConstants.DICT_TYPE_UNIQUE;
}
/**
* 查询字典类型树
*
* @param dictType 字典类型
* @return 所有字典类型
*/
@Override
public List<Ztree> selectDictTree(SysDictType dictType)
{
List<Ztree> ztrees = new ArrayList<Ztree>();
List<SysDictType> dictList = dictTypeMapper.selectDictTypeList(dictType);
for (SysDictType dict : dictList)
{
if (UserConstants.DICT_NORMAL.equals(dict.getStatus()))
{
Ztree ztree = new Ztree();
ztree.setId(dict.getDictId());
ztree.setName(transDictName(dict));
ztree.setTitle(dict.getDictType());
ztrees.add(ztree);
}
}
return ztrees;
}
public String transDictName(SysDictType dictType)
{
StringBuffer sb = new StringBuffer();
sb.append("(" + dictType.getDictName() + ")");
sb.append(" " + dictType.getDictType());
return sb.toString();
}
}
整合完毕之后,测试验证一下reids是否集成成功,在线用户,缓存监控等功能是否正常。
powered by kaifamiao