mirror of https://github.com/jeecgboot/jeecg-boot
zhangdaiscott
2 years ago
275 changed files with 7011 additions and 40277 deletions
@ -0,0 +1,152 @@
|
||||
import {getRefPromise} from '@/utils/util' |
||||
|
||||
/** JModal 的拖拽混入 */ |
||||
export default { |
||||
data() { |
||||
return { |
||||
// 拖动配置
|
||||
dragSettings: { |
||||
// 上次拖动top记录
|
||||
top: null, |
||||
// 上次拖动left记录
|
||||
left: null, |
||||
wrapEl: null, |
||||
dragEl: null, |
||||
headerEl: null, |
||||
}, |
||||
} |
||||
}, |
||||
watch: { |
||||
visible() { |
||||
if (!this.visible || !this.draggable) { |
||||
return |
||||
} |
||||
this.handleDrag() |
||||
}, |
||||
draggable() { |
||||
if (!this.visible || !this.draggable) { |
||||
return |
||||
} |
||||
this.handleDrag() |
||||
}, |
||||
}, |
||||
methods: { |
||||
async handleDrag() { |
||||
let modalRef = await getRefPromise(this, 'modal') |
||||
const dragWraps = modalRef.$el.querySelectorAll('.ant-modal-wrap') |
||||
let wrapEl = dragWraps[0] |
||||
if (!wrapEl) return |
||||
this.dragSettings.wrapEl = wrapEl |
||||
this.dragSettings.dragEl = wrapEl.querySelector('.ant-modal') |
||||
this.dragSettings.headerEl = wrapEl.querySelector('.ant-modal-header') |
||||
const display = getStyle(wrapEl, 'display') |
||||
const draggable = wrapEl.getAttribute('data-drag') |
||||
if (display !== 'none') { |
||||
// 拖拽位置
|
||||
if (draggable === null || this.destroyOnClose) { |
||||
this.enableDrag(wrapEl) |
||||
} |
||||
} |
||||
}, |
||||
/** 启用拖拽 */ |
||||
enableDrag() { |
||||
let {wrapEl, dragEl, headerEl} = this.dragSettings |
||||
if (!wrapEl) return |
||||
wrapEl.setAttribute('data-drag', this.draggable) |
||||
if (!headerEl || !dragEl || !this.draggable) return |
||||
|
||||
// 还原上一次移动的位置
|
||||
this.resetModalPosition() |
||||
|
||||
headerEl.style.cursor = 'move' |
||||
headerEl.onmousedown = (e) => { |
||||
if (!e) return |
||||
// 鼠标按下,计算当前元素距离可视区的距离
|
||||
const disX = e.clientX |
||||
const disY = e.clientY |
||||
const screenWidth = document.body.clientWidth // body当前宽度
|
||||
const screenHeight = document.documentElement.clientHeight // 可见区域高度(应为body高度,可某些环境下无法获取)
|
||||
|
||||
const dragElWidth = dragEl.offsetWidth // 对话框宽度
|
||||
const dragElHeight = dragEl.offsetHeight // 对话框高度
|
||||
|
||||
const minDragElLeft = dragEl.offsetLeft |
||||
|
||||
const maxDragElLeft = screenWidth - dragEl.offsetLeft - dragElWidth |
||||
const minDragElTop = dragEl.offsetTop |
||||
const maxDragElTop = screenHeight - dragEl.offsetTop - dragElHeight |
||||
// 获取到的值带px 正则匹配替换
|
||||
const domLeft = getStyle(dragEl, 'left') |
||||
const domTop = getStyle(dragEl, 'top') |
||||
let styL = +domLeft |
||||
let styT = +domTop |
||||
|
||||
// 注意在ie中 第一次获取到的值为组件自带50% 移动之后赋值为px
|
||||
if (domLeft.includes('%')) { |
||||
styL = +document.body.clientWidth * (+domLeft.replace(/%/g, '') / 100) |
||||
styT = +document.body.clientHeight * (+domTop.replace(/%/g, '') / 100) |
||||
} else { |
||||
styL = +domLeft.replace(/px/g, '') |
||||
styT = +domTop.replace(/px/g, '') |
||||
} |
||||
|
||||
document.onmousemove = (e) => { |
||||
// 全屏时不触发移动方法
|
||||
if (this.innerFullscreen) { |
||||
return |
||||
} |
||||
// 通过事件委托,计算移动的距离
|
||||
let left = e.clientX - disX |
||||
let top = e.clientY - disY |
||||
|
||||
// 边界处理
|
||||
if (-left > minDragElLeft) { |
||||
left = -minDragElLeft |
||||
} else if (left > maxDragElLeft) { |
||||
left = maxDragElLeft |
||||
} |
||||
|
||||
if (-top > minDragElTop) { |
||||
top = -minDragElTop |
||||
} else if (top > maxDragElTop) { |
||||
top = maxDragElTop |
||||
} |
||||
|
||||
this.setModalPosition(top + styT, left + styL) |
||||
} |
||||
|
||||
document.onmouseup = () => { |
||||
document.onmousemove = null |
||||
document.onmouseup = null |
||||
} |
||||
} |
||||
}, |
||||
|
||||
/** |
||||
* 移动弹窗位置 |
||||
* @param top 顶部位置 |
||||
* @param left 左侧位置 |
||||
* @param remember 是否记录位置,默认 true |
||||
*/ |
||||
setModalPosition(top, left, remember = true) { |
||||
// 记录移动位置
|
||||
if (remember) { |
||||
this.dragSettings.top = top |
||||
this.dragSettings.left = left |
||||
} |
||||
// 移动当前元素
|
||||
this.dragSettings.dragEl.style.cssText += `;left:${left}px;top:${top}px;` |
||||
}, |
||||
/** |
||||
* 将弹窗移动到上次记录的位置 |
||||
*/ |
||||
resetModalPosition() { |
||||
this.setModalPosition(this.dragSettings.top, this.dragSettings.left, false) |
||||
}, |
||||
|
||||
}, |
||||
} |
||||
|
||||
function getStyle(dom, attr) { |
||||
return getComputedStyle(dom)[attr] |
||||
} |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,16 +0,0 @@
|
||||
-- ---------------------------- |
||||
-- Table structure for sys_role_index |
||||
-- ---------------------------- |
||||
CREATE TABLE `sys_role_index` ( |
||||
`id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, |
||||
`role_code` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '角色编码', |
||||
`url` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '路由地址', |
||||
`priority` int(11) NULL DEFAULT 0 COMMENT '优先级', |
||||
`status` varchar(2) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '0' COMMENT '状态0:无效 1:有效', |
||||
`create_by` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '创建人登录名称', |
||||
`create_time` datetime NULL DEFAULT NULL COMMENT '创建日期', |
||||
`update_by` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '更新人登录名称', |
||||
`update_time` datetime NULL DEFAULT NULL COMMENT '更新日期', |
||||
`sys_org_code` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '所属部门', |
||||
PRIMARY KEY (`id`) USING BTREE |
||||
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '角色首页表' ROW_FORMAT = Dynamic; |
@ -0,0 +1,22 @@
|
||||
|
||||
ALTER TABLE `sys_user` |
||||
MODIFY COLUMN `org_code` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '登录会话的机构编码' AFTER `phone`; |
||||
|
||||
ALTER TABLE `sys_role_index` |
||||
ADD COLUMN `component` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '组件' AFTER `url`, |
||||
ADD COLUMN `is_route` tinyint(1) NULL DEFAULT 1 COMMENT '是否路由菜单: 0:不是 1:是(默认值1)' AFTER `component`; |
||||
|
||||
ALTER TABLE `jeecg_order_main` |
||||
ADD COLUMN `bpm_status` varchar(3) NULL COMMENT '流程状态' AFTER `update_time`; |
||||
|
||||
UPDATE `sys_dict_item` SET `dict_id` = '4f69be5f507accea8d5df5f11346181a', `item_text` = '文本', `item_value` = '1', `description` = '', `sort_order` = 1, `status` = 1, `create_by` = 'admin', `create_time` = '2023-02-28 10:50:36', `update_by` = 'admin', `update_time` = '2022-07-04 16:29:21' WHERE `id` = '222705e11ef0264d4214affff1fb4ff9'; |
||||
UPDATE `sys_dict_item` SET `dict_id` = '4f69be5f507accea8d5df5f11346181a', `item_text` = '富文本', `item_value` = '2', `description` = '', `sort_order` = 2, `status` = 1, `create_by` = 'admin', `create_time` = '2031-02-28 10:50:44', `update_by` = 'admin', `update_time` = '2022-07-04 16:29:30' WHERE `id` = '6a7a9e1403a7943aba69e54ebeff9762'; |
||||
delete from sys_dict_item where id in ('1199607547704647681', '8bccb963e1cd9e8d42482c54cc609ca2'); |
||||
update sys_sms_template set template_type = '2' where template_type='4'; |
||||
update sys_sms_template set template_type = '1' where template_type='3'; |
||||
|
||||
ALTER TABLE `sys_sms_template` |
||||
ADD COLUMN `use_status` varchar(1) NULL COMMENT '是否使用中 1是0否' AFTER `update_by`; |
||||
|
||||
ALTER TABLE `sys_sms` |
||||
MODIFY COLUMN `es_type` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '发送方式:参考枚举MessageTypeEnum' AFTER `es_title`; |
@ -0,0 +1,20 @@
|
||||
package org.jeecg.common.aspect.annotation; |
||||
|
||||
import java.lang.annotation.*; |
||||
|
||||
/** |
||||
* 动态table切换 |
||||
* |
||||
* @author :zyf |
||||
* @date:2020-04-25 |
||||
*/ |
||||
@Target(ElementType.METHOD) |
||||
@Retention(RetentionPolicy.RUNTIME) |
||||
@Documented |
||||
public @interface DynamicTable { |
||||
/** |
||||
* 需要动态解析的表名 |
||||
* @return |
||||
*/ |
||||
String value(); |
||||
} |
@ -0,0 +1,16 @@
|
||||
package org.jeecg.common.constant; |
||||
|
||||
/** |
||||
* 动态切换表配置常量 |
||||
* |
||||
* @author: scott |
||||
* @date: 2022年04月25日 22:30 |
||||
*/ |
||||
public class DynamicTableConstant { |
||||
/** |
||||
* 角色首页配置表 |
||||
* vue2表名: sys_role_index |
||||
* vue3表名: sys_role_index_vue3 |
||||
*/ |
||||
public static final String SYS_ROLE_INDEX = "sys_role_index"; |
||||
} |
@ -0,0 +1,68 @@
|
||||
package org.jeecg.common.constant.enums; |
||||
|
||||
import org.jeecg.common.system.annotation.EnumDict; |
||||
import org.jeecg.common.system.vo.DictModel; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
|
||||
/** |
||||
* 消息类型 |
||||
* @author: jeecg-boot |
||||
*/ |
||||
@EnumDict("messageType") |
||||
public enum MessageTypeEnum { |
||||
|
||||
XT("system", "系统消息"), |
||||
YJ("email", "邮件消息"), |
||||
DD("dingtalk", "钉钉消息"), |
||||
QYWX("wechat_enterprise", "企业微信"); |
||||
|
||||
MessageTypeEnum(String type, String note){ |
||||
this.type = type; |
||||
this.note = note; |
||||
} |
||||
|
||||
/** |
||||
* 消息类型 |
||||
*/ |
||||
String type; |
||||
|
||||
/** |
||||
* 类型说明 |
||||
*/ |
||||
String note; |
||||
|
||||
public String getNote() { |
||||
return note; |
||||
} |
||||
|
||||
public void setNote(String note) { |
||||
this.note = note; |
||||
} |
||||
|
||||
public String getType() { |
||||
return type; |
||||
} |
||||
|
||||
public void setType(String type) { |
||||
this.type = type; |
||||
} |
||||
|
||||
|
||||
/** |
||||
* 获取字典数据 |
||||
* @return |
||||
*/ |
||||
public static List<DictModel> getDictList(){ |
||||
List<DictModel> list = new ArrayList<>(); |
||||
DictModel dictModel = null; |
||||
for(MessageTypeEnum e: MessageTypeEnum.values()){ |
||||
dictModel = new DictModel(); |
||||
dictModel.setValue(e.getType()); |
||||
dictModel.setText(e.getNote()); |
||||
list.add(dictModel); |
||||
} |
||||
return list; |
||||
} |
||||
} |
@ -0,0 +1,20 @@
|
||||
package org.jeecg.common.desensitization.annotation; |
||||
|
||||
import java.lang.annotation.*; |
||||
|
||||
/** |
||||
* 解密注解 |
||||
* |
||||
* 在方法上定义 将方法返回对象中的敏感字段 解密,需要注意的是,如果没有加密过,解密会出问题,返回原字符串 |
||||
*/ |
||||
@Documented |
||||
@Retention(RetentionPolicy.RUNTIME) |
||||
@Target({ElementType.METHOD}) |
||||
public @interface SensitiveDecode { |
||||
|
||||
/** |
||||
* 指明需要脱敏的实体类class |
||||
* @return |
||||
*/ |
||||
Class entity() default Object.class; |
||||
} |
@ -0,0 +1,20 @@
|
||||
package org.jeecg.common.desensitization.annotation; |
||||
|
||||
import java.lang.annotation.*; |
||||
|
||||
/** |
||||
* 加密注解 |
||||
* |
||||
* 在方法上声明 将方法返回对象中的敏感字段 加密/格式化 |
||||
*/ |
||||
@Documented |
||||
@Retention(RetentionPolicy.RUNTIME) |
||||
@Target({ElementType.METHOD}) |
||||
public @interface SensitiveEncode { |
||||
|
||||
/** |
||||
* 指明需要脱敏的实体类class |
||||
* @return |
||||
*/ |
||||
Class entity() default Object.class; |
||||
} |
@ -0,0 +1,21 @@
|
||||
package org.jeecg.common.desensitization.annotation; |
||||
|
||||
|
||||
import org.jeecg.common.desensitization.enums.SensitiveEnum; |
||||
|
||||
import java.lang.annotation.*; |
||||
|
||||
/** |
||||
* 在字段上定义 标识字段存储的信息是敏感的 |
||||
*/ |
||||
@Documented |
||||
@Retention(RetentionPolicy.RUNTIME) |
||||
@Target(ElementType.FIELD) |
||||
public @interface SensitiveField { |
||||
|
||||
/** |
||||
* 不同类型处理不同 |
||||
* @return |
||||
*/ |
||||
SensitiveEnum type() default SensitiveEnum.ENCODE; |
||||
} |
@ -0,0 +1,81 @@
|
||||
package org.jeecg.common.desensitization.aspect; |
||||
|
||||
import lombok.extern.slf4j.Slf4j; |
||||
import org.aspectj.lang.ProceedingJoinPoint; |
||||
import org.aspectj.lang.annotation.Around; |
||||
import org.aspectj.lang.annotation.Aspect; |
||||
import org.aspectj.lang.annotation.Pointcut; |
||||
import org.aspectj.lang.reflect.MethodSignature; |
||||
import org.jeecg.common.desensitization.annotation.SensitiveDecode; |
||||
import org.jeecg.common.desensitization.annotation.SensitiveEncode; |
||||
import org.jeecg.common.desensitization.util.SensitiveInfoUtil; |
||||
import org.springframework.stereotype.Component; |
||||
|
||||
import java.lang.reflect.Method; |
||||
import java.util.List; |
||||
|
||||
/** |
||||
* 敏感数据切面处理类 |
||||
* @Author taoYan |
||||
* @Date 2022/4/20 17:45 |
||||
**/ |
||||
@Slf4j |
||||
@Aspect |
||||
@Component |
||||
public class SensitiveDataAspect { |
||||
|
||||
/** |
||||
* 定义切点Pointcut |
||||
*/ |
||||
@Pointcut("@annotation(org.jeecg.common.desensitization.annotation.SensitiveEncode) || @annotation(org.jeecg.common.desensitization.annotation.SensitiveDecode)") |
||||
public void sensitivePointCut() { |
||||
} |
||||
|
||||
@Around("sensitivePointCut()") |
||||
public Object around(ProceedingJoinPoint point) throws Throwable { |
||||
// 处理结果
|
||||
Object result = point.proceed(); |
||||
if(result == null){ |
||||
return result; |
||||
} |
||||
Class resultClass = result.getClass(); |
||||
log.debug(" resultClass = {}" , resultClass); |
||||
|
||||
if(resultClass.isPrimitive()){ |
||||
//是基本类型 直接返回 不需要处理
|
||||
return result; |
||||
} |
||||
// 获取方法注解信息:是哪个实体、是加密还是解密
|
||||
boolean isEncode = true; |
||||
Class entity = null; |
||||
MethodSignature methodSignature = (MethodSignature) point.getSignature(); |
||||
Method method = methodSignature.getMethod(); |
||||
SensitiveEncode encode = method.getAnnotation(SensitiveEncode.class); |
||||
if(encode==null){ |
||||
SensitiveDecode decode = method.getAnnotation(SensitiveDecode.class); |
||||
if(decode!=null){ |
||||
entity = decode.entity(); |
||||
isEncode = false; |
||||
} |
||||
}else{ |
||||
entity = encode.entity(); |
||||
} |
||||
|
||||
long startTime=System.currentTimeMillis(); |
||||
if(resultClass.equals(entity) || entity.equals(Object.class)){ |
||||
// 方法返回实体和注解的entity一样,如果注解没有申明entity属性则认为是(方法返回实体和注解的entity一样)
|
||||
SensitiveInfoUtil.handlerObject(result, isEncode); |
||||
} else if(result instanceof List){ |
||||
// 方法返回List<实体>
|
||||
SensitiveInfoUtil.handleList(result, entity, isEncode); |
||||
}else{ |
||||
// 方法返回一个对象
|
||||
SensitiveInfoUtil.handleNestedObject(result, entity, isEncode); |
||||
} |
||||
long endTime=System.currentTimeMillis(); |
||||
log.info((isEncode ? "加密操作," : "解密操作,") + "Aspect程序耗时:" + (endTime - startTime) + "ms"); |
||||
|
||||
return result; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,55 @@
|
||||
package org.jeecg.common.desensitization.enums; |
||||
|
||||
/** |
||||
* 敏感字段信息类型 |
||||
*/ |
||||
public enum SensitiveEnum { |
||||
|
||||
|
||||
/** |
||||
* 加密 |
||||
*/ |
||||
ENCODE, |
||||
|
||||
/** |
||||
* 中文名 |
||||
*/ |
||||
CHINESE_NAME, |
||||
|
||||
/** |
||||
* 身份证号 |
||||
*/ |
||||
ID_CARD, |
||||
|
||||
/** |
||||
* 座机号 |
||||
*/ |
||||
FIXED_PHONE, |
||||
|
||||
/** |
||||
* 手机号 |
||||
*/ |
||||
MOBILE_PHONE, |
||||
|
||||
/** |
||||
* 地址 |
||||
*/ |
||||
ADDRESS, |
||||
|
||||
/** |
||||
* 电子邮件 |
||||
*/ |
||||
EMAIL, |
||||
|
||||
/** |
||||
* 银行卡 |
||||
*/ |
||||
BANK_CARD, |
||||
|
||||
/** |
||||
* 公司开户银行联号 |
||||
*/ |
||||
CNAPS_CODE; |
||||
|
||||
|
||||
} |
@ -0,0 +1,362 @@
|
||||
package org.jeecg.common.desensitization.util; |
||||
|
||||
import lombok.extern.slf4j.Slf4j; |
||||
import org.jeecg.common.desensitization.annotation.SensitiveField; |
||||
import org.jeecg.common.desensitization.enums.SensitiveEnum; |
||||
import org.jeecg.common.util.encryption.AesEncryptUtil; |
||||
import org.jeecg.common.util.oConvertUtils; |
||||
|
||||
import java.lang.reflect.Field; |
||||
import java.lang.reflect.ParameterizedType; |
||||
import java.util.Collections; |
||||
import java.util.List; |
||||
|
||||
/** |
||||
* 敏感信息处理工具类 |
||||
* @author taoYan |
||||
* @date 2022/4/20 18:01 |
||||
**/ |
||||
@Slf4j |
||||
public class SensitiveInfoUtil { |
||||
|
||||
/** |
||||
* 处理嵌套对象 |
||||
* @param obj 方法返回值 |
||||
* @param entity 实体class |
||||
* @param isEncode 是否加密(true: 加密操作 / false:解密操作) |
||||
* @throws IllegalAccessException |
||||
*/ |
||||
public static void handleNestedObject(Object obj, Class entity, boolean isEncode) throws IllegalAccessException { |
||||
Field[] fields = obj.getClass().getDeclaredFields(); |
||||
for (Field field : fields) { |
||||
if(field.getType().isPrimitive()){ |
||||
continue; |
||||
} |
||||
if(field.getType().equals(entity)){ |
||||
// 对象里面是实体
|
||||
field.setAccessible(true); |
||||
Object nestedObject = field.get(obj); |
||||
handlerObject(nestedObject, isEncode); |
||||
break; |
||||
}else{ |
||||
// 对象里面是List<实体>
|
||||
if(field.getGenericType() instanceof ParameterizedType){ |
||||
ParameterizedType pt = (ParameterizedType)field.getGenericType(); |
||||
if(pt.getRawType().equals(List.class)){ |
||||
if(pt.getActualTypeArguments()[0].equals(entity)){ |
||||
field.setAccessible(true); |
||||
Object nestedObject = field.get(obj); |
||||
handleList(nestedObject, entity, isEncode); |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 处理Object |
||||
* @param obj 方法返回值 |
||||
* @param isEncode 是否加密(true: 加密操作 / false:解密操作) |
||||
* @return |
||||
* @throws IllegalAccessException |
||||
*/ |
||||
public static Object handlerObject(Object obj, boolean isEncode) throws IllegalAccessException { |
||||
log.debug(" obj --> "+ obj.toString()); |
||||
long startTime=System.currentTimeMillis(); |
||||
if (oConvertUtils.isEmpty(obj)) { |
||||
return obj; |
||||
} |
||||
// 判断是不是一个对象
|
||||
Field[] fields = obj.getClass().getDeclaredFields(); |
||||
for (Field field : fields) { |
||||
boolean isSensitiveField = field.isAnnotationPresent(SensitiveField.class); |
||||
if(isSensitiveField){ |
||||
// 必须有SensitiveField注解 才作处理
|
||||
if(field.getType().isAssignableFrom(String.class)){ |
||||
//必须是字符串类型 才作处理
|
||||
field.setAccessible(true); |
||||
String realValue = (String) field.get(obj); |
||||
if(realValue==null || "".equals(realValue)){ |
||||
continue; |
||||
} |
||||
SensitiveField sf = field.getAnnotation(SensitiveField.class); |
||||
if(isEncode==true){ |
||||
//加密
|
||||
String value = SensitiveInfoUtil.getEncodeData(realValue, sf.type()); |
||||
field.set(obj, value); |
||||
}else{ |
||||
//解密只处理 encode类型的
|
||||
if(sf.type().equals(SensitiveEnum.ENCODE)){ |
||||
String value = SensitiveInfoUtil.getDecodeData(realValue); |
||||
field.set(obj, value); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
//long endTime=System.currentTimeMillis();
|
||||
//log.info((isEncode ? "加密操作," : "解密操作,") + "当前程序耗时:" + (endTime - startTime) + "ms");
|
||||
return obj; |
||||
} |
||||
|
||||
/** |
||||
* 处理 List<实体> |
||||
* @param obj |
||||
* @param entity |
||||
* @param isEncode(true: 加密操作 / false:解密操作) |
||||
*/ |
||||
public static void handleList(Object obj, Class entity, boolean isEncode){ |
||||
List list = (List)obj; |
||||
if(list.size()>0){ |
||||
Object first = list.get(0); |
||||
if(first.getClass().equals(entity)){ |
||||
for(int i=0; i<list.size(); i++){ |
||||
Object temp = list.get(i); |
||||
try { |
||||
handlerObject(temp, isEncode); |
||||
} catch (IllegalAccessException e) { |
||||
e.printStackTrace(); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
/** |
||||
* 处理数据 获取解密后的数据 |
||||
* @param data |
||||
* @return |
||||
*/ |
||||
public static String getDecodeData(String data){ |
||||
String result = null; |
||||
try { |
||||
result = AesEncryptUtil.desEncrypt(data); |
||||
} catch (Exception exception) { |
||||
log.warn("数据解密错误,原数据:"+data); |
||||
} |
||||
//解决debug模式下,加解密失效导致中文被解密变成空的问题
|
||||
if(oConvertUtils.isEmpty(result) && oConvertUtils.isNotEmpty(data)){ |
||||
result = data; |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
/** |
||||
* 处理数据 获取加密后的数据 或是格式化后的数据 |
||||
* @param data 字符串 |
||||
* @param sensitiveEnum 类型 |
||||
* @return 处理后的字符串 |
||||
*/ |
||||
public static String getEncodeData(String data, SensitiveEnum sensitiveEnum){ |
||||
String result; |
||||
switch (sensitiveEnum){ |
||||
case ENCODE: |
||||
try { |
||||
result = AesEncryptUtil.encrypt(data); |
||||
} catch (Exception exception) { |
||||
log.error("数据加密错误", exception.getMessage()); |
||||
result = data; |
||||
} |
||||
break; |
||||
case CHINESE_NAME: |
||||
result = chineseName(data); |
||||
break; |
||||
case ID_CARD: |
||||
result = idCardNum(data); |
||||
break; |
||||
case FIXED_PHONE: |
||||
result = fixedPhone(data); |
||||
break; |
||||
case MOBILE_PHONE: |
||||
result = mobilePhone(data); |
||||
break; |
||||
case ADDRESS: |
||||
result = address(data, 3); |
||||
break; |
||||
case EMAIL: |
||||
result = email(data); |
||||
break; |
||||
case BANK_CARD: |
||||
result = bankCard(data); |
||||
break; |
||||
case CNAPS_CODE: |
||||
result = cnapsCode(data); |
||||
break; |
||||
default: |
||||
result = data; |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
|
||||
/** |
||||
* [中文姓名] 只显示第一个汉字,其他隐藏为2个星号 |
||||
* @param fullName 全名 |
||||
* @return <例子:李**> |
||||
*/ |
||||
private static String chineseName(String fullName) { |
||||
if (oConvertUtils.isEmpty(fullName)) { |
||||
return ""; |
||||
} |
||||
return formatRight(fullName, 1); |
||||
} |
||||
|
||||
/** |
||||
* [中文姓名] 只显示第一个汉字,其他隐藏为2个星号 |
||||
* @param familyName 姓 |
||||
* @param firstName 名 |
||||
* @return <例子:李**> |
||||
*/ |
||||
private static String chineseName(String familyName, String firstName) { |
||||
if (oConvertUtils.isEmpty(familyName) || oConvertUtils.isEmpty(firstName)) { |
||||
return ""; |
||||
} |
||||
return chineseName(familyName + firstName); |
||||
} |
||||
|
||||
/** |
||||
* [身份证号] 显示最后四位,其他隐藏。共计18位或者15位。 |
||||
* @param id 身份证号 |
||||
* @return <例子:*************5762> |
||||
*/ |
||||
private static String idCardNum(String id) { |
||||
if (oConvertUtils.isEmpty(id)) { |
||||
return ""; |
||||
} |
||||
return formatLeft(id, 4); |
||||
|
||||
} |
||||
|
||||
/** |
||||
* [固定电话] 后四位,其他隐藏 |
||||
* @param num 固定电话 |
||||
* @return <例子:****1234> |
||||
*/ |
||||
private static String fixedPhone(String num) { |
||||
if (oConvertUtils.isEmpty(num)) { |
||||
return ""; |
||||
} |
||||
return formatLeft(num, 4); |
||||
} |
||||
|
||||
/** |
||||
* [手机号码] 前三位,后四位,其他隐藏 |
||||
* @param num 手机号码 |
||||
* @return <例子:138******1234> |
||||
*/ |
||||
private static String mobilePhone(String num) { |
||||
if (oConvertUtils.isEmpty(num)) { |
||||
return ""; |
||||
} |
||||
int len = num.length(); |
||||
if(len<11){ |
||||
return num; |
||||
} |
||||
return formatBetween(num, 3, 4); |
||||
} |
||||
|
||||
/** |
||||
* [地址] 只显示到地区,不显示详细地址;我们要对个人信息增强保护 |
||||
* @param address 地址 |
||||
* @param sensitiveSize 敏感信息长度 |
||||
* @return <例子:北京市海淀区****> |
||||
*/ |
||||
private static String address(String address, int sensitiveSize) { |
||||
if (oConvertUtils.isEmpty(address)) { |
||||
return ""; |
||||
} |
||||
int len = address.length(); |
||||
if(len<sensitiveSize){ |
||||
return address; |
||||
} |
||||
return formatRight(address, sensitiveSize); |
||||
} |
||||
|
||||
/** |
||||
* [电子邮箱] 邮箱前缀仅显示第一个字母,前缀其他隐藏,用星号代替,@及后面的地址显示 |
||||
* @param email 电子邮箱 |
||||
* @return <例子:g**@163.com> |
||||
*/ |
||||
private static String email(String email) { |
||||
if (oConvertUtils.isEmpty(email)) { |
||||
return ""; |
||||
} |
||||
int index = email.indexOf("@"); |
||||
if (index <= 1){ |
||||
return email; |
||||
} |
||||
String begin = email.substring(0, 1); |
||||
String end = email.substring(index); |
||||
String stars = "**"; |
||||
return begin + stars + end; |
||||
} |
||||
|
||||
/** |
||||
* [银行卡号] 前六位,后四位,其他用星号隐藏每位1个星号 |
||||
* @param cardNum 银行卡号 |
||||
* @return <例子:6222600**********1234> |
||||
*/ |
||||
private static String bankCard(String cardNum) { |
||||
if (oConvertUtils.isEmpty(cardNum)) { |
||||
return ""; |
||||
} |
||||
return formatBetween(cardNum, 6, 4); |
||||
} |
||||
|
||||
/** |
||||
* [公司开户银行联号] 公司开户银行联行号,显示前两位,其他用星号隐藏,每位1个星号 |
||||
* @param code 公司开户银行联号 |
||||
* @return <例子:12********> |
||||
*/ |
||||
private static String cnapsCode(String code) { |
||||
if (oConvertUtils.isEmpty(code)) { |
||||
return ""; |
||||
} |
||||
return formatRight(code, 2); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* 将右边的格式化成* |
||||
* @param str 字符串 |
||||
* @param reservedLength 保留长度 |
||||
* @return 格式化后的字符串 |
||||
*/ |
||||
private static String formatRight(String str, int reservedLength){ |
||||
String name = str.substring(0, reservedLength); |
||||
String stars = String.join("", Collections.nCopies(str.length()-reservedLength, "*")); |
||||
return name + stars; |
||||
} |
||||
|
||||
/** |
||||
* 将左边的格式化成* |
||||
* @param str 字符串 |
||||
* @param reservedLength 保留长度 |
||||
* @return 格式化后的字符串 |
||||
*/ |
||||
private static String formatLeft(String str, int reservedLength){ |
||||
int len = str.length(); |
||||
String show = str.substring(len-reservedLength); |
||||
String stars = String.join("", Collections.nCopies(len-reservedLength, "*")); |
||||
return stars + show; |
||||
} |
||||
|
||||
/** |
||||
* 将中间的格式化成* |
||||
* @param str 字符串 |
||||
* @param beginLen 开始保留长度 |
||||
* @param endLen 结尾保留长度 |
||||
* @return 格式化后的字符串 |
||||
*/ |
||||
private static String formatBetween(String str, int beginLen, int endLen){ |
||||
int len = str.length(); |
||||
String begin = str.substring(0, beginLen); |
||||
String end = str.substring(len-endLen); |
||||
String stars = String.join("", Collections.nCopies(len-beginLen-endLen, "*")); |
||||
return begin + stars + end; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,19 @@
|
||||
package org.jeecg.common.system.annotation; |
||||
|
||||
import java.lang.annotation.*; |
||||
|
||||
/** |
||||
* 将枚举类转化成字典数据 |
||||
* @Author taoYan |
||||
* @Date 2022/7/8 10:34 |
||||
**/ |
||||
@Target(ElementType.TYPE) |
||||
@Retention(RetentionPolicy.RUNTIME) |
||||
@Documented |
||||
public @interface EnumDict { |
||||
|
||||
/** |
||||
* 作为字典数据的唯一编码 |
||||
*/ |
||||
String value() default ""; |
||||
} |
@ -0,0 +1,111 @@
|
||||
package org.jeecg.common.system.util; |
||||
|
||||
import lombok.extern.slf4j.Slf4j; |
||||
import org.jeecg.common.system.annotation.EnumDict; |
||||
import org.jeecg.common.system.vo.DictModel; |
||||
import org.jeecg.common.util.oConvertUtils; |
||||
import org.springframework.core.io.Resource; |
||||
import org.springframework.core.io.support.PathMatchingResourcePatternResolver; |
||||
import org.springframework.core.io.support.ResourcePatternResolver; |
||||
import org.springframework.core.type.classreading.CachingMetadataReaderFactory; |
||||
import org.springframework.core.type.classreading.MetadataReader; |
||||
import org.springframework.core.type.classreading.MetadataReaderFactory; |
||||
import org.springframework.util.ClassUtils; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.HashMap; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
|
||||
/** |
||||
* 资源加载工具类 |
||||
* @Author taoYan |
||||
* @Date 2022/7/8 10:40 |
||||
**/ |
||||
@Slf4j |
||||
public class ResourceUtil { |
||||
|
||||
|
||||
/** |
||||
* 枚举字典数据 |
||||
*/ |
||||
private final static Map<String, List<DictModel>> enumDictData = new HashMap<>(5); |
||||
|
||||
/** |
||||
* 所有java类 |
||||
*/ |
||||
private final static String CLASS_PATTERN="/**/*.class"; |
||||
|
||||
/** |
||||
* 包路径 org.jeecg |
||||
*/ |
||||
private final static String BASE_PACKAGE = "org.jeecg"; |
||||
|
||||
/** |
||||
* 枚举类中获取字典数据的方法名 |
||||
*/ |
||||
private final static String METHOD_NAME = "getDictList"; |
||||
|
||||
/** |
||||
* 获取枚举类对应的字典数据 SysDictServiceImpl#queryAllDictItems() |
||||
* @return |
||||
*/ |
||||
public static Map<String, List<DictModel>> getEnumDictData(){ |
||||
if(enumDictData.keySet().size()>0){ |
||||
return enumDictData; |
||||
} |
||||
ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver(); |
||||
String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + ClassUtils.convertClassNameToResourcePath(BASE_PACKAGE) + CLASS_PATTERN; |
||||
try { |
||||
Resource[] resources = resourcePatternResolver.getResources(pattern); |
||||
MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(resourcePatternResolver); |
||||
for (Resource resource : resources) { |
||||
MetadataReader reader = readerFactory.getMetadataReader(resource); |
||||
String classname = reader.getClassMetadata().getClassName(); |
||||
Class<?> clazz = Class.forName(classname); |
||||
EnumDict enumDict = clazz.getAnnotation(EnumDict.class); |
||||
if (enumDict != null) { |
||||
EnumDict annotation = clazz.getAnnotation(EnumDict.class); |
||||
String key = annotation.value(); |
||||
if(oConvertUtils.isNotEmpty(key)){ |
||||
List<DictModel> list = (List<DictModel>) clazz.getDeclaredMethod(METHOD_NAME).invoke(null); |
||||
enumDictData.put(key, list); |
||||
} |
||||
} |
||||
} |
||||
}catch (Exception e){ |
||||
log.error("获取枚举类字典数据异常", e.getMessage()); |
||||
// e.printStackTrace();
|
||||
} |
||||
return enumDictData; |
||||
} |
||||
|
||||
/** |
||||
* 用于后端字典翻译 SysDictServiceImpl#queryManyDictByKeys(java.util.List, java.util.List) |
||||
* @param dictCodeList |
||||
* @param keys |
||||
* @return |
||||
*/ |
||||
public static Map<String, List<DictModel>> queryManyDictByKeys(List<String> dictCodeList, List<String> keys){ |
||||
if(enumDictData.keySet().size()==0){ |
||||
getEnumDictData(); |
||||
} |
||||
Map<String, List<DictModel>> map = new HashMap<>(); |
||||
for (String code : enumDictData.keySet()) { |
||||
if(dictCodeList.indexOf(code)>=0){ |
||||
List<DictModel> dictItemList = enumDictData.get(code); |
||||
for(DictModel dm: dictItemList){ |
||||
String value = dm.getValue(); |
||||
if(keys.indexOf(value)>=0){ |
||||
List<DictModel> list = new ArrayList<>(); |
||||
list.add(new DictModel(value, dm.getText())); |
||||
map.put(code,list); |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
return map; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,35 @@
|
||||
package org.jeecg.config.filter; |
||||
|
||||
import org.jeecg.common.constant.CommonConstant; |
||||
import org.jeecg.config.sign.util.BodyReaderHttpServletRequestWrapper; |
||||
|
||||
import javax.servlet.*; |
||||
import javax.servlet.http.HttpServletRequest; |
||||
import java.io.IOException; |
||||
|
||||
/** |
||||
* 针对post请求,将HttpServletRequest包一层 保留body里的参数 |
||||
* @Author taoYan |
||||
* @Date 2022/4/25 19:19 |
||||
**/ |
||||
public class RequestBodyReserveFilter implements Filter { |
||||
|
||||
@Override |
||||
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { |
||||
ServletRequest requestWrapper = null; |
||||
|
||||
if(servletRequest instanceof HttpServletRequest) { |
||||
HttpServletRequest req = (HttpServletRequest) servletRequest; |
||||
// POST请求类型,才获取POST请求体
|
||||
if(CommonConstant.HTTP_POST.equals(req.getMethod())){ |
||||
requestWrapper = new BodyReaderHttpServletRequestWrapper(req); |
||||
} |
||||
} |
||||
|
||||
if(requestWrapper == null) { |
||||
filterChain.doFilter(servletRequest, servletResponse); |
||||
} else { |
||||
filterChain.doFilter(requestWrapper, servletResponse); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,52 @@
|
||||
package org.jeecg.config.filter; |
||||
|
||||
import lombok.extern.slf4j.Slf4j; |
||||
import org.jeecg.common.api.CommonAPI; |
||||
import org.jeecg.common.util.RedisUtil; |
||||
import org.jeecg.common.util.SpringContextUtils; |
||||
import org.jeecg.common.util.TokenUtils; |
||||
|
||||
import javax.servlet.*; |
||||
import javax.servlet.http.HttpServletRequest; |
||||
import javax.servlet.http.HttpServletResponse; |
||||
import java.io.IOException; |
||||
|
||||
/** |
||||
* websocket 前端将token放到子协议里传入 与后端建立连接时需要用到http协议,此处用于校验token的有效性 |
||||
* @Author taoYan |
||||
* @Date 2022/4/21 17:01 |
||||
**/ |
||||
@Slf4j |
||||
public class WebsocketFilter implements Filter { |
||||
|
||||
private static final String TOKEN_KEY = "Sec-WebSocket-Protocol"; |
||||
|
||||
private static CommonAPI commonApi; |
||||
|
||||
private static RedisUtil redisUtil; |
||||
|
||||
@Override |
||||
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { |
||||
if (commonApi == null) { |
||||
commonApi = SpringContextUtils.getBean(CommonAPI.class); |
||||
} |
||||
if (redisUtil == null) { |
||||
redisUtil = SpringContextUtils.getBean(RedisUtil.class); |
||||
} |
||||
HttpServletRequest request = (HttpServletRequest)servletRequest; |
||||
String token = request.getHeader(TOKEN_KEY); |
||||
|
||||
log.info("websocket连接 Token安全校验,Path = {},token:{}", request.getRequestURI(), token); |
||||
|
||||
try { |
||||
TokenUtils.verifyToken(token, commonApi, redisUtil); |
||||
} catch (Exception exception) { |
||||
log.error("websocket连接校验失败,{},token:{}", exception.getMessage(), token); |
||||
return; |
||||
} |
||||
HttpServletResponse response = (HttpServletResponse)servletResponse; |
||||
response.setHeader(TOKEN_KEY, token); |
||||
filterChain.doFilter(servletRequest, servletResponse); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,62 @@
|
||||
package org.jeecg.config.mybatis; |
||||
|
||||
import cn.hutool.core.util.ObjectUtil; |
||||
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; |
||||
import java.util.concurrent.ConcurrentHashMap; |
||||
|
||||
|
||||
/** |
||||
* @Description: 本地线程变量存储工具类 |
||||
* @author: lsq |
||||
* @date: 2022年03月25日 11:42 |
||||
*/ |
||||
public class ThreadLocalDataHelper { |
||||
/** |
||||
* 线程的本地变量 |
||||
*/ |
||||
private static final ThreadLocal<ConcurrentHashMap> REQUEST_DATA = new ThreadLocal<>(); |
||||
|
||||
/** |
||||
* 存储本地参数 |
||||
*/ |
||||
private static final ConcurrentHashMap DATA_MAP = new ConcurrentHashMap<>(); |
||||
|
||||
/** |
||||
* 设置请求参数 |
||||
* |
||||
* @param key 参数key |
||||
* @param value 参数值 |
||||
*/ |
||||
public static void put(String key, Object value) { |
||||
if(ObjectUtil.isNotEmpty(value)) { |
||||
DATA_MAP.put(key, value); |
||||
REQUEST_DATA.set(DATA_MAP); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 获取请求参数值 |
||||
* |
||||
* @param key 请求参数 |
||||
* @return |
||||
*/ |
||||
public static <T> T get(String key) { |
||||
ConcurrentHashMap dataMap = REQUEST_DATA.get(); |
||||
if (CollectionUtils.isNotEmpty(dataMap)) { |
||||
return (T) dataMap.get(key); |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
/** |
||||
* 获取请求参数 |
||||
* |
||||
* @return 请求参数 MAP 对象 |
||||
*/ |
||||
public static void clear() { |
||||
DATA_MAP.clear(); |
||||
REQUEST_DATA.remove(); |
||||
} |
||||
|
||||
} |
||||
|
@ -0,0 +1,55 @@
|
||||
package org.jeecg.config.mybatis.aspect; |
||||
|
||||
import org.aspectj.lang.ProceedingJoinPoint; |
||||
import org.aspectj.lang.annotation.Around; |
||||
import org.aspectj.lang.annotation.Aspect; |
||||
import org.aspectj.lang.annotation.Pointcut; |
||||
import org.aspectj.lang.reflect.MethodSignature; |
||||
import org.jeecg.common.aspect.annotation.DynamicTable; |
||||
import org.jeecg.common.constant.CommonConstant; |
||||
import org.jeecg.common.util.SpringContextUtils; |
||||
import org.jeecg.config.mybatis.ThreadLocalDataHelper; |
||||
import org.springframework.stereotype.Component; |
||||
|
||||
import javax.servlet.http.HttpServletRequest; |
||||
import java.lang.reflect.Method; |
||||
|
||||
/** |
||||
* 动态table切换 切面处理 |
||||
* |
||||
* @author :zyf |
||||
* @date:2020-04-25 |
||||
*/ |
||||
@Aspect |
||||
@Component |
||||
public class DynamicTableAspect { |
||||
|
||||
|
||||
/** |
||||
* 定义切面拦截切入点 |
||||
*/ |
||||
@Pointcut("@annotation(org.jeecg.common.aspect.annotation.DynamicTable)") |
||||
public void dynamicTable() { |
||||
} |
||||
|
||||
|
||||
@Around("dynamicTable()") |
||||
public Object around(ProceedingJoinPoint point) throws Throwable { |
||||
MethodSignature signature = (MethodSignature) point.getSignature(); |
||||
Method method = signature.getMethod(); |
||||
DynamicTable dynamicTable = method.getAnnotation(DynamicTable.class); |
||||
HttpServletRequest request = SpringContextUtils.getHttpServletRequest(); |
||||
//获取前端传递的版本标记
|
||||
String version = request.getHeader(CommonConstant.VERSION); |
||||
//存储版本号到本地线程变量
|
||||
ThreadLocalDataHelper.put(CommonConstant.VERSION, version); |
||||
//存储表名到本地线程变量
|
||||
ThreadLocalDataHelper.put(CommonConstant.DYNAMIC_TABLE_NAME, dynamicTable.value()); |
||||
//执行方法
|
||||
Object result = point.proceed(); |
||||
//清空本地变量
|
||||
ThreadLocalDataHelper.clear(); |
||||
return result; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,55 @@
|
||||
package org.jeecg.config.mybatis.interceptor; |
||||
|
||||
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder; |
||||
import lombok.extern.slf4j.Slf4j; |
||||
import org.apache.commons.lang3.StringUtils; |
||||
import org.springframework.web.servlet.HandlerInterceptor; |
||||
import org.springframework.web.servlet.ModelAndView; |
||||
|
||||
import javax.servlet.http.HttpServletRequest; |
||||
import javax.servlet.http.HttpServletResponse; |
||||
|
||||
/** |
||||
* 动态数据源切换拦截器 |
||||
* |
||||
* 测试:拦截参数,自动切换数据源 |
||||
* 未来规划:后面通过此机制,实现多租户切换数据源功能 |
||||
* @author zyf |
||||
*/ |
||||
@Slf4j |
||||
public class DynamicDatasourceInterceptor implements HandlerInterceptor { |
||||
|
||||
/** |
||||
* 在请求处理之前进行调用(Controller方法调用之前) |
||||
*/ |
||||
@Override |
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { |
||||
String requestURI = request.getRequestURI(); |
||||
log.info("经过多数据源Interceptor,当前路径是{}", requestURI); |
||||
//获取动态数据源名称
|
||||
String dsName = request.getParameter("dsName"); |
||||
String dsKey = "master"; |
||||
if (StringUtils.isNotEmpty(dsName)) { |
||||
dsKey = dsName; |
||||
} |
||||
DynamicDataSourceContextHolder.push(dsKey); |
||||
return true; |
||||
} |
||||
|
||||
/** |
||||
* 请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后) |
||||
*/ |
||||
@Override |
||||
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) { |
||||
|
||||
} |
||||
|
||||
/** |
||||
* 在整个请求结束之后被调用,也就是在DispatcherServlet 渲染了对应的视图之后执行(主要是用于进行资源清理工作) |
||||
*/ |
||||
@Override |
||||
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { |
||||
DynamicDataSourceContextHolder.clear(); |
||||
} |
||||
|
||||
} |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue