diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000000000000000000000000000000000000..7228855728066974381af8f5fcf084b55bc4625c --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000000000000000000000000000000000000..e79da7eced123ad640ac28b12dd7a80ede958336 --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000000000000000000000000000000000000..4527ab398eaad8badc66bb8b88e29ab64b206d18 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,101 @@ + + + + \ No newline at end of file diff --git a/.idea/interface-common.iml b/.idea/interface-common.iml new file mode 100644 index 0000000000000000000000000000000000000000..78b2cc53b203f0b97534bb1184cdc7b474339fb4 --- /dev/null +++ b/.idea/interface-common.iml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000000000000000000000000000000000000..3ccb27b15ba9e9d42e35dca35e2d1b9de04d2fad --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/smartfox_info.xml b/.idea/smartfox_info.xml new file mode 100644 index 0000000000000000000000000000000000000000..1c2584f990e305e762d6d822c77dbd94ea31c7e4 --- /dev/null +++ b/.idea/smartfox_info.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml new file mode 100644 index 0000000000000000000000000000000000000000..e96534fb27b68192f27f985d3879e173ec77adb8 --- /dev/null +++ b/.idea/uiDesigner.xml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000000000000000000000000000000000000..94a25f7f4cb416c083d265558da75d457237d671 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/README.en.md b/README.en.md index 1197f00c7b7aa2ef773146c741140df1ab1c4de2..7ae86562fc924e8245a3f7bf07ef845a365230e0 100644 --- a/README.en.md +++ b/README.en.md @@ -1,10 +1,10 @@ # interface-common #### Description -通过springboot整合一些工作中常用的技术,提供开源学习 +搭建springBoot通用后台接口服务interface-common(针对于前后端分离项目) #### Software Architecture -Software architecture description + #### Installation diff --git a/interface-common.iml b/interface-common.iml new file mode 100644 index 0000000000000000000000000000000000000000..78b2cc53b203f0b97534bb1184cdc7b474339fb4 --- /dev/null +++ b/interface-common.iml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..d41c9c3a1b7229e901ef6d687979035b0778e87f --- /dev/null +++ b/pom.xml @@ -0,0 +1,358 @@ + + + 4.0.0 + + com.kyrie + interface-common + 1.0-SNAPSHOT + + jar + + + interface-common + interface-common通用接口平台 + + + org.springframework.boot + spring-boot-starter-parent + + 2.1.0.RELEASE + + + + + + UTF-8 + 1.8 + 1.3.6 + 1.0.18 + 3.1.0 + 3.1.0 + 2.22.1 + 3.1.0 + 1.1.1 + 1.9 + 3.4 + 1.3.2 + 3.2.0 + 0.9.1 + + + + + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.boot + spring-boot-starter-web + + + + mysql + mysql-connector-java + + + com.alibaba + druid + ${druid.version} + + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + ${mybatis.version} + + + + commons-codec + commons-codec + ${commons-codec.version} + + + + org.apache.commons + commons-lang3 + ${commons-lang3.version} + + + + + com.alibaba + fastjson + 1.2.8 + + + + + com.google.code.gson + gson + 2.8.5 + + + + ch.qos.logback + logback-classic + 1.2.2 + + + + + org.yaml + snakeyaml + + + + + log4j + log4j + 1.2.17 + + + + + org.projectlombok + lombok + 1.16.16 + provided + + + + + org.apache.shiro + shiro-spring + ${shiro-spring.version} + + + + + com.auth0 + java-jwt + ${java-jwt.version} + + + + io.jsonwebtoken + jjwt + ${jjwt.version} + + + + + org.springframework.boot + spring-boot-starter-data-redis + 1.5.7.RELEASE + + + + + org.springframework.boot + spring-boot-starter-aop + + + org.springframework.boot + spring-boot-starter-logging + + + + + + + org.springframework.kafka + spring-kafka + + + + + + + interface-common + + + + src/main/java + + **/*.xml + + + + + + src/main/resources + true + + application.yml + application-${profileActive}.yml + mapper/**/*.xml + static/** + templates/** + *.xml + *.properties + + + + + + + + + org.apache.maven.plugins + maven-jar-plugin + 3.1.0 + + + + + com.kyrie.Application + ../lib + true + + + + + com/kyrie/** + + + + + + org.springframework.boot + spring-boot-maven-plugin + + ZIP + + + + non-exists + non-exists + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + ${maven.dependency.version} + + + prepare-package + + copy-dependencies + + + target/lib + false + false + true + compile + + + + + + + org.apache.maven.plugins + maven-resources-plugin + ${maven.resources.version} + + + + org.apache.maven.plugins + maven-compiler-plugin + + ${java.version} + ${java.version} + + + + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven.surefire.version} + + true + + + + + + maven-assembly-plugin + ${maven.assembly.version} + + + false + + src/main/assembly/assembly.xml + + + + + make-assembly + package + + single + + + + + + + + + + + + local + + local + + + true + + + + dev + + dev + + + false + + + + uat + + uat + + + false + + + + prod + + prod + + + false + + + + \ No newline at end of file diff --git a/sql/interface-common.sql b/sql/interface-common.sql new file mode 100644 index 0000000000000000000000000000000000000000..3017d345caf231cc367ac00d2f30be655c7c8786 --- /dev/null +++ b/sql/interface-common.sql @@ -0,0 +1,127 @@ +create database interface_common; +use interface_common; +-- 取消外键约束 +SET FOREIGN_KEY_CHECKS=0; + +-- Create table 用户信息表 +DROP TABLE IF EXISTS `t_user_info`; +CREATE TABLE `t_user_info` +( + `seq_id` VARCHAR(64) NOT NULL PRIMARY KEY COMMENT '主键', + `user_no` VARCHAR(100) COMMENT '用户登录工号', + `user_name` VARCHAR(100) COMMENT '用户姓名', + `password` VARCHAR(64) COMMENT '用户密码', + `salt` VARCHAR(25) COMMENT '盐值 ', + `department_id` VARCHAR(100) COMMENT '部门编号', + `role_id` VARCHAR(64) COMMENT '角色编码', + `is_locked` VARCHAR(1) COMMENT '用户账号状态:1-正常、0-锁定', + `create_date` datetime COMMENT '创建时间', + `create_person` varchar(64) COMMENT '创建人', + `if_display` varchar(1) comment '用户账号是否有效:0-无效、1-有效' +) ENGINE=INNODB DEFAULT CHARSET=UTF8 COMMENT='用户信息表'; +-- create index +CREATE INDEX userInfo_userNo ON t_user_info(user_no); +CREATE INDEX userInfo_userName ON t_user_info(user_name); +CREATE INDEX userInfo_roleId ON t_user_info(role_id); +CREATE INDEX userInfo_display ON t_user_info(if_display); +CREATE INDEX userInfo_locked ON t_user_info(is_locked); +CREATE INDEX userInfo_deptId ON t_user_info(department_id); + +INSERT INTO `interface_common`.`t_user_info` (`seq_id`, `user_no`, `user_name`, `password`, `salt`, `department_id`, `role_id`, `is_locked`, `create_date`, `create_person`, `if_display`) +VALUES ('2020040723459081', '200355', '小炑', 'Abcd1234', '0', '999', '1', '1', '2020-04-07 23:59:30', 'admin', '1'); + + +select * from `t_user_role`; + + +-- Create table 用户角色表 +DROP TABLE IF EXISTS `t_user_role`; +CREATE TABLE `t_user_role` +( + `seq_id` VARCHAR(64) NOT NULL PRIMARY KEY COMMENT '主键', + `role_id` VARCHAR(100) COMMENT '角色编码', + `role_name` VARCHAR(100) COMMENT '角色名称', + `role_sign` VARCHAR(100) COMMENT '权限代码', + `create_date` datetime COMMENT '创建时间', + `create_person` varchar(64) COMMENT '创建人', + `if_useable` varchar(1) comment '角色是否有效:0-无效、1-有效' +) ENGINE=INNODB DEFAULT CHARSET=UTF8 COMMENT='用户角色表'; +-- create index +CREATE INDEX userRole_roleId ON t_user_role(role_id); +CREATE INDEX userRole_roleSign ON t_user_role(role_sign); +INSERT INTO `interface_common`.`t_user_role` (`seq_id`, `role_id`, `role_name`, `role_sign`, `create_date`, `create_person`, `if_useable`) VALUES ('2020040723489801', '1', '系统管理员', 'systemAdmin', '2020-04-07 23:49:40', 'admin', '1'); + +-- Create table 登录日志表 +DROP TABLE IF EXISTS `login_log`; +CREATE TABLE `login_log` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `logname` varchar(255) DEFAULT NULL, + `userid` int(11) DEFAULT NULL, + `createtime` datetime DEFAULT NULL, + `state` varchar(255) DEFAULT NULL, + `message` varchar(255) DEFAULT NULL, + `ip` varchar(255) DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE +) ENGINE=InnoDB AUTO_INCREMENT=206 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT; + + +-- Create table 数据字典表 +DROP TABLE IF EXISTS `t_operation_comm_type_val`; +CREATE TABLE `t_operation_comm_type_val` +( + `seq_id` varchar(64) not null primary key COMMENT '主键', + `type_id` varchar(50) COMMENT '类型标识', + `type_name` varchar(50) COMMENT '类型名称', + `val_id` varchar(60) COMMENT '值编号', + `val_name` varchar(4000) COMMENT '值名称', + `ext_val` varchar(4000) COMMENT '扩展值', + `status` varchar(1) COMMENT '状态:0-无效、1-有效', + `order_by` int(4) COMMENT '排序', + `remake` varchar(4000) COMMENT '备注', + `create_date` datetime COMMENT '创建时间', + `create_person` varchar(64) COMMENT '创建人' +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='数据字典表'; +-- create index +CREATE INDEX operationcomm_typeId ON t_operation_comm_type_val(type_id); +CREATE INDEX operationcomm_valId ON t_operation_comm_type_val(val_id); + + +INSERT INTO `interface_common`.`t_operation_comm_type_val` +(`seq_id`, `type_id`, `val_id`, `val_name`, `status`, `order_by`, `remake`, `create_date`, `create_person`) +VALUES ('20200408121728761', 'shiroUrl', 'anon', '/unauthorized;/unauthorized/**;/login', '1', '0', '无会话访问url', '2020-04-08 12:20:20', 'admin'); + +DROP TABLE IF EXISTS `t_trans_log`; +CREATE TABLE `t_trans_log` ( + `logId` varchar(50) NOT NULL COMMENT '日志标识', + `transCode` varchar(200) DEFAULT NULL COMMENT '接口地址', + `createDate` varchar(50) DEFAULT NULL COMMENT '创建时间', + `requestBody` longtext COMMENT '请求体', + `responseBody` longtext COMMENT '响应体', + `transOperator` varchar(50) DEFAULT NULL COMMENT '接口执行人', + `transLogDesc` varchar(100) DEFAULT NULL COMMENT '接口描述', + `transTime` varchar(50) DEFAULT NULL COMMENT '接口耗时', + PRIMARY KEY (`logId`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='接口交易日志表'; +-- create index +CREATE INDEX transLog_operator ON t_trans_log(transOperator); +-- DROP INDEX transLog_operator on t_trans_log; +-- select * from t_trans_log t +-- where t.transOperator = '200355' +-- and t.logId = '6aafb760-e13f-4cea-9f1f-5cd49cff81141587058024331'; + +-- create procedure +delimiter $$ +CREATE procedure SP_EXE_SQL( +sqlstr varchar(1600)) +begin + SET @sql=sqlstr; + PREPARE s1 FROM @sql; + EXECUTE s1; + commit; + DEALLOCATE PREPARE s1; +END$$ +delimiter ; +-- 6aafb760-e13f-4cea-9f1f-5cd49cff81141587058024331 +-- update t_trans_log set transTime = '0' where logId = '6aafb760-e13f-4cea-9f1f-5cd49cff81141587058024331' +call SP_EXE_SQL('update t_trans_log set transTime = ''123'' where logId = ''6aafb760-e13f-4cea-9f1f-5cd49cff81141587058024331'''); +-- drop procedure SP_EXE_SQL; diff --git a/src/bin/restart.sh b/src/bin/restart.sh new file mode 100755 index 0000000000000000000000000000000000000000..13aea275b95d332bb3f0c3594b65b0b82403ab9c --- /dev/null +++ b/src/bin/restart.sh @@ -0,0 +1,21 @@ +#! /bin/shell + +#====================================================================== +# 项目重启shell脚本 +# 先调用shutdown.sh停服 +# 然后调用startup.sh启动服务 +# +# author: kyrie +# date: 2020-04-02 +#====================================================================== + +# 项目名称 +APPLICATION="interface-common" + +# 停服 +echo stop ${APPLICATION} Application... +sh shutdown.sh + +# 启动服务 +echo start ${APPLICATION} Application... +sh startup.sh \ No newline at end of file diff --git a/src/bin/shutdown.sh b/src/bin/shutdown.sh new file mode 100755 index 0000000000000000000000000000000000000000..2d5a3687e9cc530789ae914c6b485a16cb058da2 --- /dev/null +++ b/src/bin/shutdown.sh @@ -0,0 +1,26 @@ +#! /bin/shell + +#====================================================================== +# 项目停服shell脚本 +# 通过项目名称查找到PID +# 然后kill -9 pid +# +# author: kyrie +# date: 2020-04-02 +#====================================================================== + +# 项目名称 +APPLICATION="interface-common" + +# 项目启动jar包名称 +APPLICATION_JAR="${APPLICATION}.jar" + +PID=$(ps -ef | grep "${APPLICATION_JAR}" | grep -v grep | awk '{ print $2 }') +if [[ -z "$PID" ]] +then + echo ${APPLICATION} is already stopped +else + echo kill ${PID} + kill -9 ${PID} + echo ${APPLICATION} stopped successfully +fi \ No newline at end of file diff --git a/src/bin/startup.sh b/src/bin/startup.sh new file mode 100644 index 0000000000000000000000000000000000000000..c17657a5db333cc89caa167a23d73550b4154eb8 --- /dev/null +++ b/src/bin/startup.sh @@ -0,0 +1,125 @@ +#! /bin/shell + +#====================================================================== +# 项目启动shell脚本 +# boot目录: spring boot jar包 +# config目录: 配置文件目录 +# logs目录: 项目运行日志目录 +# logs/interface-common_startup.log: 记录启动日志 +# logs/back目录: 项目运行日志备份目录 +# nohup后台运行 +# +# author: wuxiang +# date: 2020-04-05 +#====================================================================== + +# 项目名称 +APPLICATION="interface-common" + +# 项目启动jar包名称 +APPLICATION_JAR="${APPLICATION}.jar" + +# bin目录绝对路径 +BIN_PATH=$(cd `dirname $0`; pwd) +# 进入bin目录 +cd `dirname $0` +# 返回到上一级项目根目录路径 +cd .. +# 打印项目根目录绝对路径 +# `pwd` 执行系统命令并获得结果 +BASE_PATH=`pwd` + +# 外部配置文件绝对目录,如果是目录需要/结尾,也可以直接指定文件 +# 如果指定的是目录,spring则会读取目录中的所有配置文件 +CONFIG_DIR=${BASE_PATH}"/config/" + +# 项目日志输出绝对路径 +LOG_DIR=${BASE_PATH}"/logs" +LOG_FILE="${APPLICATION}.log" +LOG_PATH="${LOG_DIR}/${LOG_FILE}" +# 日志备份目录 +LOG_BACK_DIR="${LOG_DIR}/back/" + +# 项目启动日志输出绝对路径 +LOG_STARTUP_PATH="${LOG_DIR}/${APPLICATION}_startup.log" + +# 当前时间 +NOW=`date +'%Y-%m-%m-%H-%M-%S'` +NOW_PRETTY=`'date +%Y-%m-%m %H:%M:%S'` + +# 启动日志 +STARTUP_LOG="================================================ ${NOW_PRETTY} ================================================\n" + +# 如果logs文件夹不存在,则创建文件夹 +if [[ ! -d "${LOG_DIR}" ]]; then + mkdir "${LOG_DIR}" +fi + +# 如果logs/back文件夹不存在,则创建文件夹 +if [[ ! -d "${LOG_BACK_DIR}" ]]; then + mkdir "${LOG_BACK_DIR}" +fi + +# 如果项目运行日志存在,则重命名备份 +if [[ -f "${LOG_PATH}" ]]; then + mv ${LOG_PATH} "${LOG_BACK_DIR}/${APPLICATION}_back_${NOW}.log" +fi + +# 创建新的项目运行日志 +echo "" > ${LOG_PATH} + +# 如果项目启动日志不存在,则创建,否则追加 +echo "${STARTUP_LOG}" >> ${LOG_STARTUP_PATH} + +#========================================================================================== +# JVM Configuration +# -Xmx256m:设置JVM最大可用内存为256m,根据项目实际情况而定,建议最小和最大设置成一样。 +# -Xms256m:设置JVM初始内存。此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存 +# -Xmn512m:设置年轻代大小为512m。整个JVM内存大小=年轻代大小 + 年老代大小 + 持久代大小。 +# 持久代一般固定大小为64m,所以增大年轻代,将会减小年老代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8 +# -XX:MetaspaceSize=64m:存储class的内存大小,该值越大触发Metaspace GC的时机就越晚 +# -XX:MaxMetaspaceSize=320m:限制Metaspace增长的上限,防止因为某些情况导致Metaspace无限的使用本地内存,影响到其他程序 +# -XX:-OmitStackTraceInFastThrow:解决重复异常不打印堆栈信息问题 +#========================================================================================== +JAVA_OPT="-server -Xms256m -Xmx256m -Xmn512m -XX:MetaspaceSize=64m -XX:MaxMetaspaceSize=256m" +JAVA_OPT="${JAVA_OPT} -XX:-OmitStackTraceInFastThrow" + +#======================================================= +# 将命令启动相关日志追加到日志文件 +#======================================================= + +# 输出项目名称 +STARTUP_LOG="${STARTUP_LOG}application name: ${APPLICATION}\n" +# 输出jar包名称 +STARTUP_LOG="${STARTUP_LOG}application jar name: ${APPLICATION_JAR}\n" +# 输出项目bin路径 +STARTUP_LOG="${STARTUP_LOG}application bin path: ${BIN_PATH}\n" +# 输出项目根目录 +STARTUP_LOG="${STARTUP_LOG}application root path: ${BASE_PATH}\n" +# 打印日志路径 +STARTUP_LOG="${STARTUP_LOG}application log path: ${LOG_PATH}\n" +# 打印JVM配置 +STARTUP_LOG="${STARTUP_LOG}application JAVA_OPT : ${JAVA_OPT}\n" + + +# 打印启动命令 +STARTUP_LOG="${STARTUP_LOG}application background startup command: nohup java ${JAVA_OPT} -jar ${BASE_PATH}/boot/${APPLICATION_JAR} --spring.config.location=${CONFIG_DIR} > ${LOG_PATH} 2>&1 &\n" + + +#====================================================================== +# 执行启动命令:后台启动项目,并将日志输出到项目根目录下的logs文件夹下 +#====================================================================== +nohup java ${JAVA_OPT} -jar ${BASE_PATH}/boot/${APPLICATION_JAR} --spring.config.location=${CONFIG_DIR} > ${LOG_PATH} 2>&1 & + + +# 进程ID +PID=$(ps -ef | grep "${APPLICATION_JAR}" | grep -v grep | awk '{ print $2 }') +STARTUP_LOG="${STARTUP_LOG}application pid: ${PID}\n" + +# 启动日志追加到启动日志文件中 +echo -e ${STARTUP_LOG} >> ${LOG_STARTUP_PATH} +# 打印启动日志 +echo -e ${STARTUP_LOG} + +# 打印项目日志 +tail -f ${LOG_PATH} diff --git a/src/main/assembly/assembly.xml b/src/main/assembly/assembly.xml new file mode 100644 index 0000000000000000000000000000000000000000..0dfe66d84ab6bff0b6637abc71fc902c54862ff4 --- /dev/null +++ b/src/main/assembly/assembly.xml @@ -0,0 +1,77 @@ + + + + + ${profileActive}-${project.version} + + + + tar.gz + + + + true + + + + + + + ${basedir}/src/bin + bin + 0755 + + **.sh + + + + + + + ${basedir}/target/classes + config + 0644 + + application.yml + application-${profileActive}.yml + mapper/**/*.xml + static/** + templates/** + *.xml + *.properties + *.jks + + + + + + ${basedir}/target/lib + lib + 0755 + + + + + ${basedir}/target + boot + 0755 + + ${project.build.finalName}.jar + + + + + + ${basedir} + + NOTICE + LICENSE + *.md + + + + + \ No newline at end of file diff --git a/src/main/java/com/kyrie/Application.java b/src/main/java/com/kyrie/Application.java new file mode 100644 index 0000000000000000000000000000000000000000..a8002376e5c5c0b99cb58bfa4f476a58ec5f1358 --- /dev/null +++ b/src/main/java/com/kyrie/Application.java @@ -0,0 +1,29 @@ +package com.kyrie; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.web.servlet.ServletComponentScan; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.core.env.ConfigurableEnvironment; + +/** + * @author wuxiang + */ +@SpringBootApplication +@ServletComponentScan +public class Application { + static Logger logger = LoggerFactory.getLogger(Application.class); + + public static void main(String[] args) { + ConfigurableApplicationContext context = SpringApplication.run(Application.class,args); + + ConfigurableEnvironment environment = context.getEnvironment(); + // 当前项目环境 + logger.info("项目当前运行环境: {}",environment.getProperty("spring.profiles.active")); + String port = environment.getProperty("server.port"); + String contextPath = environment.getProperty("server.servlet.context-path"); + logger.info("##########项目启动完成,运行地址为: {} ##########","http://localhost:"+port+contextPath); + } +} diff --git a/src/main/java/com/kyrie/annotation/EnumValidator.java b/src/main/java/com/kyrie/annotation/EnumValidator.java new file mode 100644 index 0000000000000000000000000000000000000000..6da395d8b61201886855c5d88fa966e679d13e1d --- /dev/null +++ b/src/main/java/com/kyrie/annotation/EnumValidator.java @@ -0,0 +1,28 @@ +package com.kyrie.annotation; + +import com.kyrie.aop.EnumValidatorClass; + +import javax.validation.Constraint; +import javax.validation.Payload; +import java.lang.annotation.*; + +/*** + * 描述: 自定义枚举参数校验注解 + * + * @author wuxiang + * @date 2020-04-21 17:12 + */ + +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD,ElementType.METHOD}) +@Constraint(validatedBy = EnumValidatorClass.class) +public @interface EnumValidator { + Class value(); + + String message() default "参数[operateType]不在指定的枚举中"; + + Class[] groups() default { }; + + Class[] payload() default { }; +} diff --git a/src/main/java/com/kyrie/annotation/TransLog.java b/src/main/java/com/kyrie/annotation/TransLog.java new file mode 100644 index 0000000000000000000000000000000000000000..3d71570afaee6fe22337be528a3127d56226c6fd --- /dev/null +++ b/src/main/java/com/kyrie/annotation/TransLog.java @@ -0,0 +1,22 @@ +package com.kyrie.annotation; + +import java.lang.annotation.*; + +/*** + * 描述: 交易日志切面注解 + * + * @author wuxiang + * @date 2020-04-16 15:14 + */ +@Inherited +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface TransLog { + + // 描述 + String description() default ""; + + // 接口交易码 + String transCode(); + +} diff --git a/src/main/java/com/kyrie/aop/EnumValidatorClass.java b/src/main/java/com/kyrie/aop/EnumValidatorClass.java new file mode 100644 index 0000000000000000000000000000000000000000..4985d523e1c152c6c4c73c6fbe8156bc4c7a4b97 --- /dev/null +++ b/src/main/java/com/kyrie/aop/EnumValidatorClass.java @@ -0,0 +1,55 @@ +package com.kyrie.aop; + +import com.kyrie.annotation.EnumValidator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/*** + * 描述: 自定义枚举参数校验注解处理类 + * + * @author wuxiang + * @date 2020-04-21 17:37 + */ +public class EnumValidatorClass implements ConstraintValidator, Annotation { + private Logger log = LoggerFactory.getLogger(this.getClass()); + private List values = new ArrayList<>(); + + @Override + public void initialize(EnumValidator enumValidator) { + Class clz = enumValidator.value(); + Object[] ojects = clz.getEnumConstants(); + try { + Method method = clz.getMethod("getValue"); + if (Objects.isNull(method)) { + throw new Exception(String.format("枚举对象{}缺少字段名为value的字段", + clz.getName())); + } + Object value = null; + for (Object obj : ojects) { + value = method.invoke(obj); + values.add(value); + } + } catch (Exception e) { + log.error("[处理枚举校验异常]", e); + } + } + + + @Override + public Class annotationType() { + return null; + } + + @Override + public boolean isValid(Object value, ConstraintValidatorContext constraintValidatorContext) { + return Objects.isNull(value) || values.contains(value) ? true : false; + } +} diff --git a/src/main/java/com/kyrie/aop/TransLogAop.java b/src/main/java/com/kyrie/aop/TransLogAop.java new file mode 100644 index 0000000000000000000000000000000000000000..d450167ff6f3d734ab00e6e838c40d4f0e551018 --- /dev/null +++ b/src/main/java/com/kyrie/aop/TransLogAop.java @@ -0,0 +1,101 @@ +package com.kyrie.aop; + +import com.alibaba.fastjson.JSON; +import com.kyrie.annotation.TransLog; +import com.kyrie.dto.UserLoginDto; +import com.kyrie.system.mybatis.service.IDefaultService; +import com.kyrie.utils.ConvertUtils; +import com.kyrie.utils.GlobalConstants; +import com.kyrie.utils.IActionContext; +import com.kyrie.vo.TransLogVO; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.AfterReturning; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.aspectj.lang.annotation.Pointcut; +import org.aspectj.lang.reflect.MethodSignature; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.util.UUID; + +/*** + * 描述: 交易日志切面 + * + * @author wuxiang + * @date 2020-04-16 15:15 + */ +@Aspect +@Component +public class TransLogAop { + private Logger logger = LoggerFactory.getLogger(this.getClass()); + + // 线程对象 + private ThreadLocal threadLocal = new ThreadLocal<>(); + + @Resource(name = "defaultService") + private IDefaultService service; + + @Value("${kyrie.loginUrl}") + private String loginUrl; + + // 设置切点 + @Pointcut("@annotation(com.kyrie.annotation.TransLog)") + public void transLogCut(){ + + } + + @Before("transLogCut()") + public void before(JoinPoint point) { + long startTime = System.currentTimeMillis(); + threadLocal.set(startTime); + } + + @AfterReturning(value = "transLogCut()",returning = "rtv") + public void after(JoinPoint point,Object rtv) { + TransLogVO transLog = new TransLogVO(); + // 接口耗时 + long endTime = System.currentTimeMillis(); + String transTime = String.valueOf(endTime - threadLocal.get()); + // 释放线程 + threadLocal.remove(); + transLog.setTransTime(transTime); + + //获取连接点的方法签名对象,在该对象中可以获取到目标方法名,所属类的Class等信息 + MethodSignature signature = (MethodSignature) point.getSignature(); + // 获取注解所属方法相关信息:接口地址、接口描述 + String transCode = signature.getMethod().getAnnotation(TransLog.class).transCode(); + String transDesc = signature.getMethod().getAnnotation(TransLog.class).description(); + transLog.setTransCode(transCode); + transLog.setTransLogDesc(transDesc); + + // 获取请求对象 + Object[] objects = point.getArgs(); + String requestBody = JSON.toJSONString(objects[0]); + transLog.setRequestBody(requestBody); + + // 用户信息,判断当前请求是否为登录请求 + String userNo; + if (loginUrl.equals(transCode)) { + UserLoginDto userLogin = ConvertUtils.stringToBean(requestBody,UserLoginDto.class); + userNo = userLogin.getUserNo(); + } else { + userNo = IActionContext.getUserNo(); + } + transLog.setTransOperator(userNo); + + // 获取响应对象 + String responseBody = JSON.toJSONString(rtv); + transLog.setResponseBody(responseBody); + + // 生成logId + String logId = UUID.randomUUID().toString() + System.currentTimeMillis(); + transLog.setLogId(logId); + // 记录切面日志 + service.insert(GlobalConstants.NAME_SPACE + "insertTransLog",transLog); + } + +} diff --git a/src/main/java/com/kyrie/config/demo.xml b/src/main/java/com/kyrie/config/demo.xml new file mode 100644 index 0000000000000000000000000000000000000000..48d0990d5f5176d6abc54e379fd8b669af3533d4 --- /dev/null +++ b/src/main/java/com/kyrie/config/demo.xml @@ -0,0 +1,19 @@ + + + + + + \ No newline at end of file diff --git a/src/main/java/com/kyrie/config/loginAuthentication.xml b/src/main/java/com/kyrie/config/loginAuthentication.xml new file mode 100644 index 0000000000000000000000000000000000000000..a9e712b55bfa3850baf6cb1e2c111c45a99163ae --- /dev/null +++ b/src/main/java/com/kyrie/config/loginAuthentication.xml @@ -0,0 +1,38 @@ + + + + + + + + \ No newline at end of file diff --git a/src/main/java/com/kyrie/config/operationCommTypeVal.xml b/src/main/java/com/kyrie/config/operationCommTypeVal.xml new file mode 100644 index 0000000000000000000000000000000000000000..13fcd6eff3e29c0297db8ffe1adcb73b3202af61 --- /dev/null +++ b/src/main/java/com/kyrie/config/operationCommTypeVal.xml @@ -0,0 +1,191 @@ + + + + + + + + + update t_operation_comm_type_val t + set + + val_id = #{valId} + + + ,val_name = #{valName} + + + ,ext_val = #{extVal} + + + ,status = #{status} + + + ,order_by = #{orderBy} + + + ,remake = #{remake} + + ,create_date = now() + ,create_person = #{userNo} + where 1=1 + + and type_id = #{typeId} + + + and val_id = #{valId} + + + + + insert into t_operation_comm_type_val( + + seq_id + + + ,type_id + + + ,type_name + + + ,val_id + + + ,val_name + + + ,ext_val + + + ,status + + + ,order_by + + + ,remake + + ,create_date,create_person + ) values ( + + #{seqId} + + + ,#{typeId} + + + ,#{typeName} + + + ,#{valId} + + + ,#{valName} + + + ,#{extVal} + + + ,#{status} + + + ,#{orderBy} + + + ,#{remake} + + ,now(),#{userNo} + ) + + + + delete from t_operation_comm_type_val + where type_id = #{typeId} + and val_id = #{valId} + + + + insert into t_trans_log( + + logId + + + ,transCode + + + ,requestBody + + + ,responseBody + + + ,transOperator + + + ,transLogDesc + + + ,transTime + + ,createDate + ) values( + + #{logId} + + + ,#{transCode} + + + ,#{requestBody} + + + ,#{responseBody} + + + ,#{transOperator} + + + ,#{transLogDesc} + + + ,#{transTime} + + ,now() + ) + + + \ No newline at end of file diff --git a/src/main/java/com/kyrie/controller/DemoController.java b/src/main/java/com/kyrie/controller/DemoController.java new file mode 100644 index 0000000000000000000000000000000000000000..284b238a5961900b3e9ab202cd8cba4f43856caf --- /dev/null +++ b/src/main/java/com/kyrie/controller/DemoController.java @@ -0,0 +1,64 @@ +package com.kyrie.controller; + +import com.alibaba.fastjson.JSON; +import com.google.gson.Gson; +import com.kyrie.annotation.TransLog; +import com.kyrie.dto.LoginLogDto; +import com.kyrie.system.druid.DatasourceSelectHelper; +import com.kyrie.system.mybatis.execption.ServiceException; +import com.kyrie.system.mybatis.service.IDefaultService; +import com.kyrie.utils.ConvertUtils; +import com.kyrie.utils.GlobalConstants; +import com.kyrie.vo.LoginLogVO; +import com.kyrie.vo.ResponseBean; +import org.apache.shiro.authz.annotation.Logical; +import org.apache.shiro.authz.annotation.RequiresRoles; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; +import javax.annotation.Resource; +import javax.validation.Valid; +import java.util.List; + +/** + * 描述:demo controller + * @author wuxiang + * @date 2020-04-19 19:38:00 + */ +@Validated +@RestController +public class DemoController { + private Logger logger = LoggerFactory.getLogger(this.getClass()); + + @Resource(name = "defaultService") + private IDefaultService service; + + @TransLog(transCode = "/test",description = "@测试接口") + @RequestMapping(value = "/test",method = RequestMethod.POST) + @RequiresRoles(logical = Logical.OR, value = {"user","systemAdmin"}) + public ResponseBean> getLog(@Valid @RequestBody LoginLogDto loginLogDto) throws ServiceException { + + logger.info("@demo示例开始执行:{}", JSON.toJSONString(loginLogDto)); + + // 默认初始化的数据源 + List loginLogVOInit = service.selectList(GlobalConstants.NAME_SPACE + "getLoginLog", loginLogDto); + logger.info("初始化加载的数据源查询结果为:{}",JSON.toJSONString(loginLogVOInit)); + + // 设置业务库 + DatasourceSelectHelper.setKey(DatasourceSelectHelper.BUSINESS_DB); + List loginLogVOTwo = service.selectList(GlobalConstants.NAME_SPACE + "getLoginLog", loginLogDto); + logger.info("切换数据源local-2查询结果为:{}",JSON.toJSONString(loginLogVOTwo)); + + + // 设置配置库 + DatasourceSelectHelper.setKey(DatasourceSelectHelper.CONFIG_DB); + List loginLogVOOne = service.selectList(GlobalConstants.NAME_SPACE + "getLoginLog", loginLogDto); + logger.info("切换数据源local-1查询结果为:{}",JSON.toJSONString(loginLogVOOne)); + + return ResponseBean.success(loginLogVOOne, GlobalConstants.SUCCESS_CODE,"@登录日志查询接口请求成功"); + } +} diff --git a/src/main/java/com/kyrie/controller/ErrorController.java b/src/main/java/com/kyrie/controller/ErrorController.java new file mode 100644 index 0000000000000000000000000000000000000000..455ebcf0b7f9aea4a13fd5a875845f1d8560f257 --- /dev/null +++ b/src/main/java/com/kyrie/controller/ErrorController.java @@ -0,0 +1,26 @@ +package com.kyrie.controller; + +import com.kyrie.utils.GlobalConstants; +import com.kyrie.vo.ResponseBean; +import com.kyrie.vo.UserInfoVO; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.bind.annotation.*; + +/*** + * 描述: 处理异常的重定向controller + * + * @author wuxiang + * @date 2020-04-07 23:50 + */ +@RestController +public class ErrorController { + private Logger logger = LoggerFactory.getLogger(this.getClass()); + + @RequestMapping(value = "/500",method = RequestMethod.GET) + public ResponseBean error500 (String errorMsg) { + logger.info("拦截到/500 的错误请求信息为:{}",errorMsg); + return ResponseBean.success(null,GlobalConstants.FAIL_CODE,errorMsg); + + } +} diff --git a/src/main/java/com/kyrie/controller/KafkaProducerController.java b/src/main/java/com/kyrie/controller/KafkaProducerController.java new file mode 100644 index 0000000000000000000000000000000000000000..4b5914b11d40e40a698345db0ac3d05497187c68 --- /dev/null +++ b/src/main/java/com/kyrie/controller/KafkaProducerController.java @@ -0,0 +1,53 @@ +package com.kyrie.controller; + +import com.alibaba.fastjson.JSON; +import com.kyrie.annotation.TransLog; +import com.kyrie.dto.KafkaProducerDto; +import com.kyrie.system.mybatis.execption.ServiceException; +import com.kyrie.utils.GlobalConstants; +import com.kyrie.vo.ResponseBean; +import org.apache.kafka.clients.consumer.ConsumerRecord; +import org.apache.shiro.authz.annotation.Logical; +import org.apache.shiro.authz.annotation.RequiresRoles; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.kafka.annotation.KafkaListener; +import org.springframework.kafka.core.KafkaTemplate; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; +import javax.validation.Valid; + +/** + * 描述:kafka生产者controller + * @author wuxiang + * @date 2020-04-19 19:38:00 + */ +@Validated +@RestController +public class KafkaProducerController { + private Logger logger = LoggerFactory.getLogger(this.getClass()); + + @Autowired + private KafkaTemplate kafkaTemplate; + + @TransLog(transCode = "/produceMsg",description = "@kafka消息发布接口") + @RequestMapping(value = "/produceMsg",method = RequestMethod.POST) + @RequiresRoles(logical = Logical.OR, value = {"user","systemAdmin"}) + public ResponseBean getLog(@Valid @RequestBody KafkaProducerDto kafkaProducerDto) throws ServiceException { + kafkaTemplate.send("topic-kafka-demo", JSON.toJSONString(kafkaProducerDto.getPushContent())); + + return ResponseBean.success(null, GlobalConstants.SUCCESS_CODE,"@kafka消息发布接口"); + } + + @KafkaListener(topics = { "topic-kafka-demo" }) + public void receive(ConsumerRecord record) { + + logger.info("消费得到的消息---key: " + record.key()); + logger.info("消费得到的消息---value: " + record.value().toString()); + + } +} diff --git a/src/main/java/com/kyrie/controller/SqlController.java b/src/main/java/com/kyrie/controller/SqlController.java new file mode 100644 index 0000000000000000000000000000000000000000..4c904af2904a79968682ddd1e0def2b2ff1132aa --- /dev/null +++ b/src/main/java/com/kyrie/controller/SqlController.java @@ -0,0 +1,62 @@ +package com.kyrie.controller; + +import com.alibaba.fastjson.JSON; +import com.kyrie.annotation.TransLog; +import com.kyrie.dto.SqlExecuteDto; +import com.kyrie.system.druid.DatasourceSelectHelper; +import com.kyrie.system.mybatis.service.IDefaultService; +import com.kyrie.utils.GlobalConstants; +import com.kyrie.vo.ResponseBean; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.jdbc.core.SqlParameter; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; +import javax.validation.Valid; +import java.sql.Types; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/*** + * 描述: 动态sql执行 + * + * @author wuxiang + * @date 2020-04-17 15:55 + */ +@Validated +@RestController +public class SqlController { + + private Logger logger = LoggerFactory.getLogger(this.getClass()); + + @Resource(name = "defaultService") + private IDefaultService service; + + @TransLog(transCode = "/sql/execute",description = "@动态sql执行接口") + @RequestMapping(value = "/sql/execute",method = RequestMethod.POST) + public ResponseBean execute(@Valid @RequestBody SqlExecuteDto sqlExecuteDto) { + String sql = sqlExecuteDto.getSql(); + // 格式标准,去除换行符 + sql = sql.replaceAll("#br#",""); + String db = sqlExecuteDto.getDb(); + // 切库 + DatasourceSelectHelper.setKey(db); + + // 组装动态sql参数 + Map map = new HashMap<>(); + map.put("sqlstr",sql); + List sqlParameters = new ArrayList<>(); + sqlParameters.add(new SqlParameter("sqlstr", Types.VARCHAR)); + Map returnMap = service.call("SP_EXE_SQL",false,map,sqlParameters); + logger.info("动态sql执行结果为:{}",returnMap); + + return ResponseBean.success(JSON.toJSONString(returnMap), GlobalConstants.SUCCESS_CODE,"@动态sql执行成功"); + } +} diff --git a/src/main/java/com/kyrie/controller/TOperationController.java b/src/main/java/com/kyrie/controller/TOperationController.java new file mode 100644 index 0000000000000000000000000000000000000000..0a2929fc0b081c824347fad7d0479afd297cbcb3 --- /dev/null +++ b/src/main/java/com/kyrie/controller/TOperationController.java @@ -0,0 +1,77 @@ +package com.kyrie.controller; + +import com.alibaba.druid.util.StringUtils; +import com.kyrie.dto.GetDictionariesDto; +import com.kyrie.dto.OperateDictionariesDto; +import com.kyrie.system.mybatis.execption.ServiceException; +import com.kyrie.utils.GlobalConstants; +import com.kyrie.utils.OperationCommUtil; +import com.kyrie.vo.ResponseBean; +import com.kyrie.vo.TOperationCommTypeVal; +import org.apache.shiro.authz.annotation.Logical; +import org.apache.shiro.authz.annotation.RequiresRoles; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; +import javax.validation.Valid; +import java.util.List; + +/*** + * 描述: 数据字典 + * + * @author wuxiang + * @date 2020-04-07 23:50 + */ +@Validated +@RestController +@RequestMapping("/commVal") +public class TOperationController { + private Logger logger = LoggerFactory.getLogger(this.getClass()); + + @Resource(name = "operationCommUtil") + private OperationCommUtil operationCommUtil; + + /** + * 获取数据字典列表 + */ + @RequestMapping(value = "/getDictionaries", method = RequestMethod.POST) + @RequiresRoles(logical = Logical.OR, value = {"systemAdmin"}) + public ResponseBean> getDictionaries(@Valid @RequestBody GetDictionariesDto getDictionariesDto) throws ServiceException { + String typeId = getDictionariesDto.getTypeId(); + String valId = getDictionariesDto.getValId(); + List list = operationCommUtil.getDictList(typeId,valId); + return ResponseBean.success(list, GlobalConstants.SUCCESS_CODE, "@数据字典查询成功"); + } + + /** + * 修改+新增+删除数据字典 + */ + @RequestMapping(value = "/operateDictionaries", method = RequestMethod.POST) + @RequiresRoles(logical = Logical.OR, value = {"systemAdmin"}) + public ResponseBean> operateDictionaries(@Valid @RequestBody OperateDictionariesDto operateDictionariesDto) throws ServiceException { + String operateType = operateDictionariesDto.getOperateType(); + switch (operateType) { + case "add": + // 新增字典 + operationCommUtil.addDictonaries(operateDictionariesDto); + return ResponseBean.success(null, GlobalConstants.SUCCESS_CODE, "@数据字典添加成功!"); + case "delete": + // 删除字典 + operationCommUtil.delDictonaries(operateDictionariesDto); + return ResponseBean.success(null, GlobalConstants.SUCCESS_CODE, "@数据字典删除成功!"); + case "update": + // 更新字典 + operationCommUtil.updDictonaries(operateDictionariesDto); + return ResponseBean.success(null, GlobalConstants.SUCCESS_CODE, "@数据字典更新成功!"); + default: + // 未匹配到操作类型 + return ResponseBean.fail(null, GlobalConstants.FAIL_CODE, "@数据字典操作失败,失败原因:字典操作类型:" + operateType + "非法!"); + } + } +} diff --git a/src/main/java/com/kyrie/controller/loginController.java b/src/main/java/com/kyrie/controller/loginController.java new file mode 100644 index 0000000000000000000000000000000000000000..151ffd07c64eee93be500a69757744712f2b5785 --- /dev/null +++ b/src/main/java/com/kyrie/controller/loginController.java @@ -0,0 +1,73 @@ +package com.kyrie.controller; + +import com.kyrie.annotation.TransLog; +import com.kyrie.dto.UserLoginDto; +import com.kyrie.security.jwt.JWTUtil; +import com.kyrie.system.druid.DatasourceSelectHelper; +import com.kyrie.system.mybatis.execption.ServiceException; +import com.kyrie.system.mybatis.service.IDefaultService; +import com.kyrie.utils.GlobalConstants; +import com.kyrie.vo.LoginReturnBean; +import com.kyrie.vo.ResponseBean; +import com.kyrie.vo.UserInfoVO; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; +import javax.validation.Valid; + +/*** + * 描述: 登录 + * + * @author wuxiang + * @date 2020-04-07 23:50 + */ +@Validated +@RestController +public class loginController { + private Logger logger = LoggerFactory.getLogger(this.getClass()); + + @Resource(name = "defaultService") + private IDefaultService service; + + + /** + * 用户登录认真接口 + * @param user 用户登录信息 + * @return ResponseBean + * @throws ServiceException + */ + @TransLog(transCode = "/login",description = "@用户登录接口") + @RequestMapping(value = "/login",method = RequestMethod.POST) + public ResponseBean login(@Valid @RequestBody UserLoginDto user) throws ServiceException { + + // 切换配置库 + DatasourceSelectHelper.setKey(DatasourceSelectHelper.CONFIG_DB); + UserInfoVO userDb = service.selectOne(GlobalConstants.NAME_SPACE + "getUserInfo",user); + String password = userDb.getPassword(); + String isLocked = userDb.getIsLocked(); + + String token = ""; + // TODO 当前为测试demo,后续需优化数据库密码及登录校验规则,避免明文校验 + if (StringUtils.isEmpty(password)) { + return ResponseBean.fail(null,GlobalConstants.FAIL_CODE,"用户名或密码不正确!"); + } else if (!password.equals(user.getPassword())) { + return ResponseBean.fail(null,GlobalConstants.FAIL_CODE,"用户名或密码不正确!"); + } else if ("0".equals(isLocked)) { + return ResponseBean.fail(null,GlobalConstants.FAIL_CODE,"账号已被锁定,请联系管理员进行解锁!"); + } else { + token = JWTUtil.createToken(userDb); + } + // 登录成功返回bean + LoginReturnBean loginReturnBean = service.selectOne(GlobalConstants.NAME_SPACE + "loginReturn",user); + loginReturnBean.setToken(token); + return ResponseBean.success(loginReturnBean,GlobalConstants.SUCCESS_CODE,"@用户登录认证成功"); + + } +} diff --git a/src/main/java/com/kyrie/dto/GetDictionariesDto.java b/src/main/java/com/kyrie/dto/GetDictionariesDto.java new file mode 100644 index 0000000000000000000000000000000000000000..8a1318e5002a8997f5e20f0bc707bc61959e38e2 --- /dev/null +++ b/src/main/java/com/kyrie/dto/GetDictionariesDto.java @@ -0,0 +1,24 @@ +package com.kyrie.dto; + +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import java.io.Serializable; + +/*** + * 描述: 获取数据字典信息dto + * + * @author wuxiang + * @date 2020-04-14 11:35 + */ +@Data +public class GetDictionariesDto implements Serializable { + + private static final long serialVersionUID = 1541103894719787272L; + + @NotBlank(message = "数据字典一级类型不能为空") + private String typeId; + + private String valId; + +} diff --git a/src/main/java/com/kyrie/dto/KafkaProducerDto.java b/src/main/java/com/kyrie/dto/KafkaProducerDto.java new file mode 100644 index 0000000000000000000000000000000000000000..e9a4e3fc2c4153d96ca4bb47733ad1b8b1067d03 --- /dev/null +++ b/src/main/java/com/kyrie/dto/KafkaProducerDto.java @@ -0,0 +1,26 @@ +package com.kyrie.dto; + +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import java.io.Serializable; + +/*** + * 描述: kafka消息发布dto + * + * @author wuxiang + * @date 2020-04-23 17:21 + */ +@Data +public class KafkaProducerDto implements Serializable { + + private static final long serialVersionUID = 8663656109959279829L; + @NotBlank(message = "topic不能为空!") + private String topic; + + private Boolean autoFlush; + + private Object pushContent; + + +} diff --git a/src/main/java/com/kyrie/dto/LoginLogDto.java b/src/main/java/com/kyrie/dto/LoginLogDto.java new file mode 100644 index 0000000000000000000000000000000000000000..f9fde64cd9ceb5777557440bc4f4a792dbecfe0f --- /dev/null +++ b/src/main/java/com/kyrie/dto/LoginLogDto.java @@ -0,0 +1,23 @@ +package com.kyrie.dto; + +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import java.io.Serializable; + +/*** + * @author wuxiang + */ +@Data +public class LoginLogDto implements Serializable { + + private static final long serialVersionUID = -4774034279756173029L; + + @NotNull(message = "用户编号[userId]不能为空!") + private Integer userId; + + @NotBlank(message = "日志类型[logType]不能为空!") + private String logType; + +} diff --git a/src/main/java/com/kyrie/dto/OperateDictionariesDto.java b/src/main/java/com/kyrie/dto/OperateDictionariesDto.java new file mode 100644 index 0000000000000000000000000000000000000000..49968d639db773265e356602d5892fa21dbed5ff --- /dev/null +++ b/src/main/java/com/kyrie/dto/OperateDictionariesDto.java @@ -0,0 +1,49 @@ +package com.kyrie.dto; + +import com.kyrie.annotation.EnumValidator; +import com.kyrie.vo.DictOperateEnum; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import java.io.Serializable; + +/*** + * 描述: 数据字典操作实体:新增、删除、更新 + * + * @author wuxiang + * @date 2020-04-14 11:35 + */ +@Data +public class OperateDictionariesDto implements Serializable { + + private static final long serialVersionUID = -8893194005228442084L; + + @NotBlank(message = "数据字典操作类型[operateType]不能为空,add-新增、delete-删除、update-更新") + @EnumValidator(value = DictOperateEnum.class,message = "数据字典操作类型[operateType]传入非法") + private String operateType; + + private String userNo; + + @NotBlank(message = "数据字典一级类型[typeId]不能为空") + private String typeId; + + private String typeName; + + @NotBlank(message = "数据字典二级类型[valId]不能为空") + private String valId; + + private String seqId; + + private String valName; + + private String extVal; + + private String status; + + private Integer orderBy; + + private String remake; + + private String createPerson; + +} diff --git a/src/main/java/com/kyrie/dto/SqlExecuteDto.java b/src/main/java/com/kyrie/dto/SqlExecuteDto.java new file mode 100644 index 0000000000000000000000000000000000000000..1a65ec965849d1c37f86e5124ab53711cb2401fe --- /dev/null +++ b/src/main/java/com/kyrie/dto/SqlExecuteDto.java @@ -0,0 +1,29 @@ +package com.kyrie.dto; + +import com.kyrie.annotation.EnumValidator; +import com.kyrie.vo.DataSourceEnum; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import java.io.Serializable; + +/*** + * 描述: 动态执行sql接口dto类 + * + * @author wuxiang + * @date 2020-04-17 16:00 + */ +@Data +public class SqlExecuteDto implements Serializable { + private static final long serialVersionUID = 2889914619629533018L; + + @NotBlank(message = "SQL不能为空") + private String sql; + + @NotBlank(message = "数据库不能为空") + @EnumValidator(value = DataSourceEnum.class,message = "当前选择的数据库类型[db]非法") + private String db; + + @NotBlank(message = "执行类型不能为空") + private String asyn; +} diff --git a/src/main/java/com/kyrie/dto/UserLoginDto.java b/src/main/java/com/kyrie/dto/UserLoginDto.java new file mode 100644 index 0000000000000000000000000000000000000000..6128714597679548665c6d41c43e9b8ad7ce6401 --- /dev/null +++ b/src/main/java/com/kyrie/dto/UserLoginDto.java @@ -0,0 +1,23 @@ +package com.kyrie.dto; + +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import java.io.Serializable; + +/*** + * 描述: 用户登录dto + * + * @author wuxiang + * @date 2020-04-07 23:56 + */ +@Data +public class UserLoginDto implements Serializable { + private static final long serialVersionUID = 459608697154479803L; + + @NotBlank(message = "用户名不能为空!") + private String userNo; + + @NotBlank(message = "登录密码不能为空!") + private String password; +} diff --git a/src/main/java/com/kyrie/exception/GlobalExceptionHandlerController.java b/src/main/java/com/kyrie/exception/GlobalExceptionHandlerController.java new file mode 100644 index 0000000000000000000000000000000000000000..f4fe334da3927c799f828979b69573356b5aa44a --- /dev/null +++ b/src/main/java/com/kyrie/exception/GlobalExceptionHandlerController.java @@ -0,0 +1,61 @@ +package com.kyrie.exception; + +import com.kyrie.system.mybatis.execption.ServiceException; +import com.kyrie.utils.GlobalConstants; +import com.kyrie.vo.ResponseBean; +import org.apache.shiro.authc.AuthenticationException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +/*** + * 自定义全局controller异常拦截器 + * @author wuxiang + */ +@RestControllerAdvice +public class GlobalExceptionHandlerController { + Logger logger = LoggerFactory.getLogger(GlobalExceptionHandlerController.class); + + /*** + * 拦截参数校验失败异常 + * @param e MethodArgumentNotValidException + * @return ResponseBean + */ + @ExceptionHandler(MethodArgumentNotValidException.class) + @ResponseBody + public ResponseBean methodArgumentNotValidException(MethodArgumentNotValidException e) { + BindingResult bindingResult = e.getBindingResult(); + logger.info("【参数校验失败: {}】",bindingResult.getFieldError().getDefaultMessage()); + return ResponseBean.notValid("", GlobalConstants.FAIL_CODE, bindingResult.getFieldError().getDefaultMessage()); + } + + + /*** + * 数据库操作异常 + * @param e ServiceException + * @return ResponseBean + */ + @ExceptionHandler(ServiceException.class) + @ResponseBody + public ResponseBean ServiceException(ServiceException e) { + logger.error("【数据库操作异常】-- catch ServiceException: ",e); + return ResponseBean.exception("", GlobalConstants.FAIL_CODE, e.getMessage()); + } + + /*** + * token认证失败异常 + * @param e AuthenticationException + * @return ResponseBean + */ + @ExceptionHandler(AuthenticationException.class) + @ResponseBody + public ResponseBean AuthenticationException(AuthenticationException e) { + logger.error("【token认证失败】-- catch AuthenticationException: ",e); + return ResponseBean.exception("", GlobalConstants.FAIL_CODE, e.getMessage()); + } + +} diff --git a/src/main/java/com/kyrie/kafka/KafKaConfiguration.java b/src/main/java/com/kyrie/kafka/KafKaConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..eb2a768267f8108b5ea0019478f6d9e64e3b7f51 --- /dev/null +++ b/src/main/java/com/kyrie/kafka/KafKaConfiguration.java @@ -0,0 +1,45 @@ +package com.kyrie.kafka; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/*** + * 描述: kafka配置读取类 + * + * @author wuxiang + * @date 2020-04-24 13:26 + */ +@ConfigurationProperties(prefix = "interface.kafka") +@Component +public class KafKaConfiguration { + + private String bootstrapServers; + + private KafkaProductConf producer; + + private KafkaConsumerConf consumer; + + public String getBootstrapServers() { + return bootstrapServers; + } + + public void setBootstrapServers(String bootstrapServers) { + this.bootstrapServers = bootstrapServers; + } + + public KafkaProductConf getProducer() { + return producer; + } + + public void setProducer(KafkaProductConf producer) { + this.producer = producer; + } + + public KafkaConsumerConf getConsumer() { + return consumer; + } + + public void setConsumer(KafkaConsumerConf consumer) { + this.consumer = consumer; + } +} diff --git a/src/main/java/com/kyrie/kafka/KafkaConfig.java b/src/main/java/com/kyrie/kafka/KafkaConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..969fbb5c0def4394688362eff7392c10431ab90c --- /dev/null +++ b/src/main/java/com/kyrie/kafka/KafkaConfig.java @@ -0,0 +1,128 @@ +package com.kyrie.kafka; + +import org.apache.kafka.clients.consumer.ConsumerConfig; +import org.apache.kafka.clients.producer.ProducerConfig; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory; +import org.springframework.kafka.config.KafkaListenerContainerFactory; +import org.springframework.kafka.core.*; +import org.springframework.kafka.listener.ConcurrentMessageListenerContainer; + +import java.util.HashMap; +import java.util.Map; + +/*** + * 描述: kafka配置类 + * + * @author wuxiang + * @date 2020-04-24 13:43 + */ +@Configuration +public class KafkaConfig { + + @Autowired + private KafKaConfiguration kafKaConfiguration; + + /** + * 生产者配置 + */ + public Map producerConfigs() { + + Map props = new HashMap<>(); + // 集群的服务器地址 + props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, kafKaConfiguration.getBootstrapServers()); + // 消息缓存 + props.put(ProducerConfig.BUFFER_MEMORY_CONFIG, kafKaConfiguration.getProducer().getBufferMemory()); + // 生产者空间不足时,send()被阻塞的时间,默认60s + props.put(ProducerConfig.MAX_BLOCK_MS_CONFIG, kafKaConfiguration.getProducer().getMaxBlock()); + // 生产者重试次数 + props.put(ProducerConfig.RETRIES_CONFIG, kafKaConfiguration.getProducer().getRetries()); + // 指定ProducerBatch(消息累加器中BufferPool中的)可复用大小 + props.put(ProducerConfig.BATCH_SIZE_CONFIG, kafKaConfiguration.getProducer().getBatchSize()); + // 生产者会在ProducerBatch被填满或者等待超过LINGER_MS_CONFIG时发送 + props.put(ProducerConfig.LINGER_MS_CONFIG, kafKaConfiguration.getProducer().getLinger()); + // key 和 value 的序列化 + props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringSerializer"); + props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, + "org.apache.kafka.common.serialization.StringSerializer"); + // 客户端id + props.put(ProducerConfig.CLIENT_ID_CONFIG, kafKaConfiguration.getProducer().getClientId()); + + return props; + } + + /** + * 生产者工厂 + */ + @Bean + public ProducerFactory producerFactory() { + return new DefaultKafkaProducerFactory<>(producerConfigs()); + } + + /** + * kafkaTemplate 设置生产者监听 + */ + @Bean + public KafkaTemplate kafkaTemplate() { + KafkaTemplate kafkaTemplate = new KafkaTemplate<>(producerFactory()); + kafkaTemplate.setProducerListener(new KafkaProducerListener()); + return kafkaTemplate; + } + + /**######################################## kafka-consumer-config ########################################*/ + + /** + * 消费者配置 + * @return + */ + public Map consumerConfigs() { + + Map props = new HashMap(); + + props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, kafKaConfiguration.getBootstrapServers()); + // 消费者组 + props.put(ConsumerConfig.GROUP_ID_CONFIG, kafKaConfiguration.getConsumer().getGroupId()); + // 自动位移提交 + props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, kafKaConfiguration.getConsumer().getEnableAutoCommit()); + // 自动位移提交间隔时间 + props.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, kafKaConfiguration.getConsumer().getAutoCommitInterval()); + // 消费组失效超时时间 + props.put(ConsumerConfig.SESSION_TIMEOUT_MS_CONFIG, kafKaConfiguration.getConsumer().getSessionTimeout()); + // 设置心跳时间(默认为3000) + props.put(ConsumerConfig.HEARTBEAT_INTERVAL_MS_CONFIG,kafKaConfiguration.getConsumer().getHeartbeatInterval()); + // 位移丢失和位移越界后的恢复起始位置 + props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, kafKaConfiguration.getConsumer().getAutoOffsetReset()); + // key 和 value 的反序列化 + props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, + "org.apache.kafka.common.serialization.StringDeserializer"); + props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, + "org.apache.kafka.common.serialization.StringDeserializer"); + + return props; + } + + /** + * 消费者工厂 + */ + @Bean + public ConsumerFactory consumerFactory() { + return new DefaultKafkaConsumerFactory<>(consumerConfigs()); + } + + /** + * 消费者监听器 + */ + @Bean + public KafkaListenerContainerFactory> kafkaListenerContainerFactory() { + + ConcurrentKafkaListenerContainerFactory factory = new ConcurrentKafkaListenerContainerFactory<>(); + // 设置消费者工厂 + factory.setConsumerFactory(consumerFactory()); + // 要创建的消费者数量(10 个线程并发处理) + factory.setConcurrency(10); + + return factory; + } +} diff --git a/src/main/java/com/kyrie/kafka/KafkaConsumerConf.java b/src/main/java/com/kyrie/kafka/KafkaConsumerConf.java new file mode 100644 index 0000000000000000000000000000000000000000..0da38e14e6b122fd8b8c9c907c6a64c70e469a6d --- /dev/null +++ b/src/main/java/com/kyrie/kafka/KafkaConsumerConf.java @@ -0,0 +1,31 @@ +package com.kyrie.kafka; + +import lombok.Data; + +/*** + * 描述: kafka消费者conf + * + * @author wuxiang + * @date 2020-04-24 13:29 + */ +@Data +public class KafkaConsumerConf { + + // 消费组id + private String groupId; + + // 自动位移提交 + private Boolean enableAutoCommit; + + // 自动位移提交间隔时间 + private Integer autoCommitInterval; + + // 消费组失效超时时间 + private Integer sessionTimeout; + + // 位移丢失和位移越界后的恢复起始位置 + private String autoOffsetReset; + + // 心跳时间 + private String heartbeatInterval; +} diff --git a/src/main/java/com/kyrie/kafka/KafkaProducerListener.java b/src/main/java/com/kyrie/kafka/KafkaProducerListener.java new file mode 100644 index 0000000000000000000000000000000000000000..2742e4b8795370e2b124c9eb8584790de0ca4060 --- /dev/null +++ b/src/main/java/com/kyrie/kafka/KafkaProducerListener.java @@ -0,0 +1,32 @@ +package com.kyrie.kafka; + +import org.apache.kafka.clients.producer.RecordMetadata; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.kafka.support.ProducerListener; + +/*** + * 描述: kafka生产者发送回调监听器 + * + * @author wuxiang + * @date 2020-04-23 16:27 + */ +public class KafkaProducerListener implements ProducerListener { + private Logger logger = LoggerFactory.getLogger(this.getClass()); + + /** + * kafka消息发送成功回调方法 + */ + @Override + public void onSuccess(String topic, Integer partition, Object key, Object value, RecordMetadata recordMetadata) { + logger.info("kafka send ok: [topic:{},partition:{},key:{},value:{},recordMetadata:{}]",topic,partition,key,value,recordMetadata); + } + + /** + * kafka消息发送失败回调方法 + */ + @Override + public void onError(String topic, Integer partition, Object key, Object value, Exception exception) { + logger.info("kafka send error: [topic:{},partition:{},key:{},value:{},exception:{}]",topic,partition,key,value,exception); + } +} diff --git a/src/main/java/com/kyrie/kafka/KafkaProductConf.java b/src/main/java/com/kyrie/kafka/KafkaProductConf.java new file mode 100644 index 0000000000000000000000000000000000000000..cbc0e2de5c1e3d0500c56d50612ec47d1f852bb4 --- /dev/null +++ b/src/main/java/com/kyrie/kafka/KafkaProductConf.java @@ -0,0 +1,25 @@ +package com.kyrie.kafka; + +import lombok.Data; + +/*** + * 描述: kafka生产者conf + * + * @author wuxiang + * @date 2020-04-24 13:29 + */ +@Data +public class KafkaProductConf { + + private Integer bufferMemory; + + private Integer maxBlock; + + private Integer retries; + + private Integer batchSize; + + private Integer linger; + + private String clientId; +} diff --git a/src/main/java/com/kyrie/redis/EnabledRedisConfiguration.java b/src/main/java/com/kyrie/redis/EnabledRedisConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..80ca37d7498116285cf70c9bdc272862fda80194 --- /dev/null +++ b/src/main/java/com/kyrie/redis/EnabledRedisConfiguration.java @@ -0,0 +1,68 @@ +package com.kyrie.redis; + +import java.util.HashSet; +import java.util.Set; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import redis.clients.jedis.HostAndPort; +import redis.clients.jedis.JedisCluster; +import redis.clients.jedis.JedisPoolConfig; + +/*** + * 描述:redis集群配置类 + * @author wuxiang + * + * @date 2020-03-26 + */ +@Configuration +@EnableConfigurationProperties(RedisProperty.class) +public class EnabledRedisConfiguration { + + private static final Logger logger = LoggerFactory.getLogger(EnabledRedisConfiguration.class); + + @Autowired + RedisProperty property; +// @Autowired(required=false) +// Cryptography cryptography; + @Bean + public JedisCluster jedisCluster(){ + Set hostAndPortSet = new HashSet(3); + for(Redis redis : property.getJedis()) { + hostAndPortSet.add(new HostAndPort(redis.getHost(), redis.getPort())); + } +// String dev = System.getProperty("spring.profiles.active"); + String passwd = property.getPasswd(); + logger.info("redis集群密码为: {}",passwd); +// if(!(dev == null || "local".equals(dev))) {//生产测试环境密码需解密 +// passwd = cryptography.decrypt(passwd); +// } + return new JedisCluster(hostAndPortSet, property.getConnectionTimeout(), property.getSoTimeout(), property.getMaxAttempts(), + passwd, jedisPoolConfig()); + } + + @Bean + public JedisPoolConfig jedisPoolConfig() { + JedisPoolConfig jedisPoolConfig = new JedisPoolConfig(); + jedisPoolConfig.setMaxIdle(property.getMaxIdle()); + jedisPoolConfig.setMaxTotal(property.getMaxTotal()); + jedisPoolConfig.setMinIdle(property.getMinIdel()); + jedisPoolConfig.setTestOnBorrow(property.isTestOnBorrow()); + jedisPoolConfig.setTestOnReturn(property.isTestOnReturn()); + jedisPoolConfig.setTestWhileIdle(property.isTestOnIdle()); + jedisPoolConfig.setTimeBetweenEvictionRunsMillis(property.getTimeBetweenEvictionRunsMillis()); + jedisPoolConfig.setNumTestsPerEvictionRun(property.getNumTestsPerEvictionRun()); + return jedisPoolConfig; + } + +// @Bean +// @ConditionalOnMissingBean +// @Profile({"dev","pro","uat","ft","sit","pre"}) +// public Cryptography cryptography(){ +// return new Base64Cryptography(); +// } +} diff --git a/src/main/java/com/kyrie/redis/Redis.java b/src/main/java/com/kyrie/redis/Redis.java new file mode 100644 index 0000000000000000000000000000000000000000..54c7bdaf00e8fad8fd9291da1dc61811bd564452 --- /dev/null +++ b/src/main/java/com/kyrie/redis/Redis.java @@ -0,0 +1,28 @@ +package com.kyrie.redis; + +import java.io.Serializable; + +/** + * 描述:redis集群properties映射 + */ +public class Redis implements Serializable{ + private static final long serialVersionUID = -6649543716294491680L; + private String host; + private int port; + + public void setHost(String host) { + this.host = host; + } + + public void setPort(int port) { + this.port = port; + } + + public String getHost() { + return host; + } + + public int getPort() { + return port; + } +} diff --git a/src/main/java/com/kyrie/redis/RedisConfig.java b/src/main/java/com/kyrie/redis/RedisConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..5d58c96b7d7fb65f446f26813ad4fe737bcdfa01 --- /dev/null +++ b/src/main/java/com/kyrie/redis/RedisConfig.java @@ -0,0 +1,123 @@ +package com.kyrie.redis; +// +//import java.net.UnknownHostException; +//import java.util.HashSet; +//import java.util.Set; +// +//import org.springframework.beans.factory.annotation.Value; +//import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +//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.Jackson2JsonRedisSerializer; +//import org.springframework.data.redis.serializer.StringRedisSerializer; +//import com.fasterxml.jackson.annotation.JsonAutoDetect; +//import com.fasterxml.jackson.annotation.PropertyAccessor; +//import com.fasterxml.jackson.databind.ObjectMapper; +///** +// * redis配置类--单机版 +// */ +////@Configuration +////public class RedisConfig { +//// +//// @Bean(name="interfaceRedisTemplate") +//// @SuppressWarnings("all") +//// public RedisTemplate redisTemplate(RedisConnectionFactory factory) { +//// RedisTemplate template = new RedisTemplate(); +//// template.setConnectionFactory(factory); +//// Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); +//// ObjectMapper om = new ObjectMapper(); +//// om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); +//// om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); +//// jackson2JsonRedisSerializer.setObjectMapper(om); +//// StringRedisSerializer stringRedisSerializer = new StringRedisSerializer(); +//// // key采用String的序列化方式 +//// template.setKeySerializer(stringRedisSerializer); +//// // hash的key也采用String的序列化方式 +//// template.setHashKeySerializer(stringRedisSerializer); +//// // value序列化方式采用jackson +//// template.setValueSerializer(jackson2JsonRedisSerializer); +//// // hash的value序列化方式采用jackson +//// template.setHashValueSerializer(jackson2JsonRedisSerializer); +//// template.afterPropertiesSet(); +//// return template; +//// } +////} +// +//import redis.clients.jedis.HostAndPort; +//import redis.clients.jedis.JedisCluster; +//import redis.clients.jedis.JedisPoolConfig; +// +///** +// * redis-集群配置 +// */ +//@Configuration +//@ConditionalOnClass({JedisCluster.class}) +//public class RedisConfig { +// @Value("${spring.redis.cluster.nodes}") +// private String clusterNodes; +// @Value("${spring.redis.timeout}") +// private int timeout; +// @Value("${spring.redis.pool.max-idle}") +// private int maxIdle; +// @Value("${spring.redis.pool.max-wait}") +// private long maxWaitMillis; +// @Value("${spring.redis.commandTimeout}") +// private int commandTimeout; +// +// @Value("${spring.redis.password}") +// private String password; +// +// // 出现异常最大重试次数 +// private static int maxAttempts = 3; +// +// // 返回值的超时时间 +// private static int soTimeout = 5000; +// +// @Bean +// public JedisCluster getJedisCluster() { +// String[] cNodes = clusterNodes.split(","); +// Set nodes =new HashSet<>(); +// //分割出集群节点 +// for(String node : cNodes) { +// String[] hp = node.split(":"); +// nodes.add(new HostAndPort(hp[0],Integer.parseInt(hp[1]))); +// } +// JedisPoolConfig jedisPoolConfig =new JedisPoolConfig(); +// jedisPoolConfig.setMaxIdle(maxIdle); +// jedisPoolConfig.setMaxWaitMillis(maxWaitMillis); +// +// //创建集群对象 +//// JedisCluster jedisCluster = new JedisCluster(nodes,commandTimeout); +// // return new JedisCluster(nodes,commandTimeout,jedisPoolConfig); +// +// return new JedisCluster(nodes, commandTimeout, soTimeout, maxAttempts, password, jedisPoolConfig); +// } +// +// /** +// * 设置数据存入redis 的序列化方式 +// *
redisTemplate序列化默认使用的jdkSerializeable,存储二进制字节码,导致key会出现乱码,所以自定义 +// *序列化类 +// * +// * @paramredisConnectionFactory +// */ +// @Bean(name="interfaceRedisTemplate") +// public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException { +// RedisTemplate redisTemplate = new RedisTemplate<>(); +// redisTemplate.setConnectionFactory(redisConnectionFactory); +// Jackson2JsonRedisSerializer jackson2JsonRedisSerializer =new Jackson2JsonRedisSerializer(Object.class); +// ObjectMapper objectMapper =new ObjectMapper(); +// objectMapper.setVisibility(PropertyAccessor.ALL,JsonAutoDetect.Visibility.ANY); +// objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); +// jackson2JsonRedisSerializer.setObjectMapper(objectMapper); +// +// redisTemplate.setValueSerializer(jackson2JsonRedisSerializer); +// redisTemplate.setKeySerializer(new StringRedisSerializer()); +// +// redisTemplate.afterPropertiesSet(); +// +// return redisTemplate; +// } +// +//} diff --git a/src/main/java/com/kyrie/redis/RedisProperty.java b/src/main/java/com/kyrie/redis/RedisProperty.java new file mode 100644 index 0000000000000000000000000000000000000000..de45a68928b8703f5a0975607ccfa20aa5e9fa5b --- /dev/null +++ b/src/main/java/com/kyrie/redis/RedisProperty.java @@ -0,0 +1,194 @@ +package com.kyrie.redis; + +import java.io.Serializable; +import java.util.List; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +@SuppressWarnings("serial") +//@PropertySource("classpath:config/redis.yml") +@ConfigurationProperties(prefix = "interface.redis") +public class RedisProperty implements Serializable{ + /** + * The default value for the {@code maxTotal} configuration attribute. + */ + private static final int DEFAULT_MAX_TOTAL = 8; + + /** + * The default value for the {@code maxIdle} configuration attribute. + */ + private static final int DEFAULT_MAX_IDLE = 8; + + /** + * The default value for the {@code minIdle} configuration attribute. + */ + private static final int DEFAULT_MIN_IDLE = 0; + /** + * The default value for the {@code testOnBorrow} configuration attribute. + */ + private static final boolean DEFAULT_TEST_ON_BORROW = false; + + /** + * The default value for the {@code testOnReturn} configuration attribute. + */ + private static final boolean DEFAULT_TEST_ON_RETURN = false; + + /** + * The default value for the {@code testWhileIdle} configuration attribute. + */ + private static final boolean DEFAULT_TEST_WHILE_IDLE = false; + /** + * The default value for the {@code timeBetweenEvictionRunsMillis} + * configuration attribute. + */ + private static final long DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS = -1L; + /** + * The default value for the {@code numTestsPerEvictionRun} configuration + * attribute. + */ + private static final int DEFAULT_NUM_TESTS_PER_EVICTION_RUN = 3; + /** + * The default value for the {@code soTimeout} configuration attribute. + */ + private static final int DEFAULT_SO_TIMEOUT = 5000; + /** + * The default value for the {@code connectionTimeout} configuration attribute. + */ + private static final int DEFAULT_CONNECTION_TIMEOUT = 3000; + /** + * The default value for the {@code connectionTimeout} configuration attribute. + */ + private static final int DEFAULT_MAX_ATTEMPTS = 2; + /**连接命令执行超时时间,默认为5秒,单位为毫秒*/ + private int soTimeout = DEFAULT_SO_TIMEOUT; + /**获取连接的超时时间,默认为3秒,单位毫秒*/ + private int connectionTimeout = DEFAULT_CONNECTION_TIMEOUT; + /**命令失败时,最大重试次数,默认为2次*/ + private int maxAttempts = DEFAULT_MAX_ATTEMPTS; + /**最大连接数,默认值为8,如果赋值为-1,则表示不限制**/ + private int maxTotal = DEFAULT_MAX_TOTAL; + /**最大空闲连接数,默认值为8**/ + private int maxIdle = DEFAULT_MAX_IDLE; + /**最小空闲连接数,默认为0**/ + private int minIdel = DEFAULT_MIN_IDLE; + /**从连接池中获取连接时是否进行有效性检查*/ + private boolean testOnBorrow = DEFAULT_TEST_ON_BORROW; + /**在连接空闲时进行是否进行有效性检查*/ + private boolean testOnIdle = DEFAULT_TEST_WHILE_IDLE; + /**连接归还到连接池时是否进行有效性检查 */ + private boolean testOnReturn = DEFAULT_TEST_ON_RETURN; + /**空闲连接的检测线程,检测周期时间,单位为毫秒; 如果为负值则不进行检查,默认为-1 */ + private long timeBetweenEvictionRunsMillis = DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS; + /**检测线程每次检测连接的个数,默认为3,为负数时全部检查*/ + private int numTestsPerEvictionRun = DEFAULT_NUM_TESTS_PER_EVICTION_RUN; + /**redis密码*/ + private String passwd; + /**redis集群地址*/ + private List jedis; + + public void setSoTimeout(int soTimeout) { + this.soTimeout = soTimeout; + } + + public void setConnectionTimeout(int connectionTimeout) { + this.connectionTimeout = connectionTimeout; + } + + public void setMaxAttempts(int maxAttempts) { + this.maxAttempts = maxAttempts; + } + + public void setPasswd(String passwd) { + this.passwd = passwd; + } + + public int getSoTimeout() { + return soTimeout; + } + + public int getConnectionTimeout() { + return connectionTimeout; + } + + public String getPasswd() { + return passwd; + } + + public int getMaxAttempts() { + return maxAttempts; + } + + public void setJedis(List jedis) { + this.jedis = jedis; + } + + public List getJedis() { + return jedis; + } + + public int getMaxTotal() { + return maxTotal; + } + + public void setMaxTotal(int maxTotal) { + this.maxTotal = maxTotal; + } + + public int getMaxIdle() { + return maxIdle; + } + + public void setMaxIdle(int maxIdle) { + this.maxIdle = maxIdle; + } + + public int getMinIdel() { + return minIdel; + } + + public void setMinIdel(int minIdel) { + this.minIdel = minIdel; + } + + public boolean isTestOnBorrow() { + return testOnBorrow; + } + + public void setTestOnBorrow(boolean testOnBorrow) { + this.testOnBorrow = testOnBorrow; + } + + public boolean isTestOnIdle() { + return testOnIdle; + } + + public void setTestOnIdle(boolean testOnIdle) { + this.testOnIdle = testOnIdle; + } + + public boolean isTestOnReturn() { + return testOnReturn; + } + + public void setTestOnReturn(boolean testOnReturn) { + this.testOnReturn = testOnReturn; + } + + public long getTimeBetweenEvictionRunsMillis() { + return timeBetweenEvictionRunsMillis; + } + + public void setTimeBetweenEvictionRunsMillis(long timeBetweenEvictionRunsMillis) { + this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis; + } + + public int getNumTestsPerEvictionRun() { + return numTestsPerEvictionRun; + } + + public void setNumTestsPerEvictionRun(int numTestsPerEvictionRun) { + this.numTestsPerEvictionRun = numTestsPerEvictionRun; + } + + +} diff --git a/src/main/java/com/kyrie/security/interceptor/InterceptorConfigurer.java b/src/main/java/com/kyrie/security/interceptor/InterceptorConfigurer.java new file mode 100644 index 0000000000000000000000000000000000000000..e2b6b33e8b1a3fe7a6db0df783163fa7093fd25e --- /dev/null +++ b/src/main/java/com/kyrie/security/interceptor/InterceptorConfigurer.java @@ -0,0 +1,44 @@ +package com.kyrie.security.interceptor; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +/*** + * 描述: 配置安全请求拦截器 + * + * @author wuxiang + * @date 2020-04-12 11:55 + */ +@Configuration +public class InterceptorConfigurer implements WebMvcConfigurer { + + @Value("${shiro.anonMappings}") + private String anonMappings; + + /** + * 配置静态资源 + */ + /*public void addResourceHandlers(ResourceHandlerRegistry registry) { + registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/"); + registry.addResourceHandler("/templates/**").addResourceLocations("classpath:/templates/"); + super.addResourceHandlers(registry); + }*/ + + @Autowired + private UltraViresInterceptor ultraViresInterceptor; + + @Override + public void addInterceptors(InterceptorRegistry registry) { + // addPathPatterns 用于添加拦截规则 + // excludePathPatterns 用于排除拦截 + String[] excludePathPatterns = anonMappings.split(";"); + // 地址拦截器 + registry.addInterceptor(ultraViresInterceptor) + .addPathPatterns("/**") + .excludePathPatterns(excludePathPatterns); + } + +} diff --git a/src/main/java/com/kyrie/security/interceptor/RequestWrapper.java b/src/main/java/com/kyrie/security/interceptor/RequestWrapper.java new file mode 100644 index 0000000000000000000000000000000000000000..5c708a7a8149db3958dfc05d95a5823e5589eb5c --- /dev/null +++ b/src/main/java/com/kyrie/security/interceptor/RequestWrapper.java @@ -0,0 +1,93 @@ +package com.kyrie.security.interceptor; + + +import javax.servlet.ReadListener; +import javax.servlet.ServletInputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; +import java.io.*; + +/*** + * 描述: 复制HttpServletRequest请求对象,便于读取参数 + * + * @author wuxiang + * @date 2020-04-12 11:28 + */ +public class RequestWrapper extends HttpServletRequestWrapper { + private final String body; + + public RequestWrapper(HttpServletRequest request) { + super(request); + StringBuilder stringBuilder = new StringBuilder(); + BufferedReader bufferedReader = null; + InputStream inputStream = null; + try { + inputStream = request.getInputStream(); + if (inputStream != null) { + bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); + char[] charBuffer = new char[128]; + int bytesRead = -1; + while ((bytesRead = bufferedReader.read(charBuffer)) > 0) { + stringBuilder.append(charBuffer, 0, bytesRead); + } + } else { + stringBuilder.append(""); + } + } catch (IOException ex) { + + } finally { + if (inputStream != null) { + try { + inputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + if (bufferedReader != null) { + try { + bufferedReader.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + body = stringBuilder.toString(); + } + + @Override + public ServletInputStream getInputStream() throws IOException { + final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes()); + ServletInputStream servletInputStream = new ServletInputStream() { + @Override + public boolean isFinished() { + return false; + } + + @Override + public boolean isReady() { + return false; + } + + @Override + public void setReadListener(ReadListener readListener) { + } + + @Override + public int read() throws IOException { + return byteArrayInputStream.read(); + } + }; + return servletInputStream; + + } + + @Override + public BufferedReader getReader() throws IOException { + return new BufferedReader(new InputStreamReader(this.getInputStream())); + } + + public String getBody() { + return this.body; + } + +} diff --git a/src/main/java/com/kyrie/security/interceptor/RequestWrapperFilter.java b/src/main/java/com/kyrie/security/interceptor/RequestWrapperFilter.java new file mode 100644 index 0000000000000000000000000000000000000000..c02828ffc3b8d840c7a661fe199b3daba837bad4 --- /dev/null +++ b/src/main/java/com/kyrie/security/interceptor/RequestWrapperFilter.java @@ -0,0 +1,43 @@ +package com.kyrie.security.interceptor; + +import org.springframework.stereotype.Component; + +import javax.servlet.*; +import javax.servlet.annotation.WebFilter; +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; + + +/*** + * 描述: 传递request + * + * @author wuxiang + * @date 2020-04-12 12:13 + */ +@Component +@WebFilter(urlPatterns = "/**",filterName = "RequestWrapperFilter") +public class RequestWrapperFilter implements Filter { + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + + } + + @Override + public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { + ServletRequest requestWrapper = null; + if(servletRequest instanceof HttpServletRequest) { + requestWrapper = new RequestWrapper((HttpServletRequest) servletRequest); + } + if(requestWrapper == null) { + filterChain.doFilter(servletRequest, servletResponse); + } else { + filterChain.doFilter(requestWrapper, servletResponse); + } + } + + @Override + public void destroy() { + + } +} diff --git a/src/main/java/com/kyrie/security/interceptor/UltraViresInterceptor.java b/src/main/java/com/kyrie/security/interceptor/UltraViresInterceptor.java new file mode 100644 index 0000000000000000000000000000000000000000..57ed79cd4385ed6aa2ab383516670d35e3aa502b --- /dev/null +++ b/src/main/java/com/kyrie/security/interceptor/UltraViresInterceptor.java @@ -0,0 +1,83 @@ +package com.kyrie.security.interceptor; + +import com.alibaba.fastjson.JSON; +import com.kyrie.security.jwt.JWTUtil; +import com.kyrie.utils.ConvertUtils; +import com.kyrie.utils.GlobalConstants; +import com.kyrie.vo.UserInfoVO; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.ModelAndView; +import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.net.URLEncoder; +import java.util.Map; + +/*** + * 描述: 安全校验拦截器(判断是否为越权访问) + * + * @author wuxiang + * @date 2020-04-12 11:59 + */ +@Component("ultraViresInterceptor") +public class UltraViresInterceptor extends HandlerInterceptorAdapter { + private Logger logger = LoggerFactory.getLogger(this.getClass()); + + @Value("${shiro.anonMappings}") + private String anonMappings; + + // 拦截前处理 + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) { + logger.info("########## 执行自定义安全校验拦截器 ##########"); + try { + RequestWrapper requestWrapper = new RequestWrapper(request); + // 获取@RequestBody注解参数和post请求参数 + String body = requestWrapper.getBody(); + String requestUri = request.getRequestURI(); + logger.info("当前请求uri:{},拦截到的请求信息为:{}",requestUri,body); + + if (anonMappings.indexOf(requestUri) > -1) { + // 请求不做拦截 + return true; + } + if (StringUtils.isEmpty(body)) { + response.sendRedirect("/500?errorMsg=" + URLEncoder.encode(GlobalConstants.CROSS_ACCESS_MSG,"UTF-8")); + return false; + } + Map requestMap = JSON.parseObject(body); + // 从token中获取用户信息 + UserInfoVO userInfo = ConvertUtils.stringToBean(JWTUtil.getUserInfo(request.getHeader(GlobalConstants.SECURITY_TOKEN)),UserInfoVO.class); + String userNoFromToken = userInfo.getUserNo(); + String userNoFromRequest = (String) requestMap.get(GlobalConstants.BUSINESS_REQUEST_ID); + + if (StringUtils.isEmpty(userNoFromRequest) || !userNoFromToken.equals(userNoFromRequest)) { + response.sendRedirect("/500?errorMsg=" + URLEncoder.encode(GlobalConstants.CROSS_ACCESS_MSG,"UTF-8")); + return false; + } + return true; + + } catch (Exception e) { + logger.error("安全识别请求拦截器内部异常", e); + return false; + } + } + + // 拦截后处理 + @Override + public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, + ModelAndView modelAndView) throws Exception { + + } + + // 全部完成后处理 + @Override + public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, + Object o, Exception e) throws Exception { + + } +} \ No newline at end of file diff --git a/src/main/java/com/kyrie/security/jwt/JWTFilter.java b/src/main/java/com/kyrie/security/jwt/JWTFilter.java new file mode 100644 index 0000000000000000000000000000000000000000..0f084cd5fbe4e6c7d8239947baff4d2e99922b10 --- /dev/null +++ b/src/main/java/com/kyrie/security/jwt/JWTFilter.java @@ -0,0 +1,106 @@ +package com.kyrie.security.jwt; + +import com.kyrie.utils.GlobalConstants; +import org.apache.commons.lang3.StringUtils; +import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.RequestMethod; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.net.URLEncoder; + +/*** + * 描述: 自定义JWTFilter过滤器,对token进行认证处理 + * 所有需要认证的请求都会到当前过滤器中,配置为anon无认证的请求不会到该过滤器 + * + * @author wuxiang + * @date 2020-04-07 10:51 + */ +public class JWTFilter extends BasicHttpAuthenticationFilter { + private Logger logger = LoggerFactory.getLogger(this.getClass()); + + /*** + * 持有token进行认证 + */ + @Override + protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) { + // 始终返回false来执行onAccessDenied方法 + return false; + } + + + /** + * 当isAccessAllowed返回false时会执行该方法 + * 重定向到/500进行controller处理返回响应结果信息 + * @param request ServletRequest + * @param response ServletResponse + * @throws Exception + */ + @Override + protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception { + HttpServletResponse httpResponse = (HttpServletResponse) response; + // 获取请求头上的token信息 + HttpServletRequest httpRequest = (HttpServletRequest) request; + String token = httpRequest.getHeader(GlobalConstants.SECURITY_TOKEN); + + if (StringUtils.isEmpty(token)) { + // 针对error的中文错误信息重定向后会乱码的解决方案,对errorMsg进行编码即可 + httpResponse.sendRedirect("/500?errorMsg=" + URLEncoder.encode(GlobalConstants.TOKEN_LOSS_MSG,"UTF-8")); + return false; + } + + // 执行token认证 + try { + // 委托 realm 进行登录认证 + JWTToken jwtToken = new JWTToken(token); + getSubject(request,response).login(jwtToken); + return true; + } catch (Exception e) { + responseError(response, e.getMessage()); + return false; + } + } + + /*** + * 添加跨域支持 + * @param request ServletRequest + * @param response ServletResponse + * @return boolean + * @throws Exception + */ + @Override + protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception { + HttpServletRequest httpRequest = (HttpServletRequest) request; + HttpServletResponse httpResponse = (HttpServletResponse) response; + + httpResponse.setHeader("Access-control-Allow-Origin", httpRequest.getHeader("Origin")); + httpResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE"); + httpResponse.setHeader("Access-Control-Allow-Headers", httpRequest.getHeader("Access-Control-Request-Headers")); + + // 跨域时会首先发送一个option请求,这里我们给option请求直接返回正常状态 + if (httpRequest.getMethod().equals(RequestMethod.OPTIONS.name())) { + httpResponse.setStatus(HttpStatus.OK.value()); + return false; + } + return super.preHandle(request, response); + } + + /** + * 将非法请求跳转到 /500?errorMsg=xxx + */ + private void responseError(ServletResponse response, String message) { + try { + HttpServletResponse httpServletResponse = (HttpServletResponse) response; + //设置编码,否则中文字符在重定向时会变为空字符串 + httpServletResponse.sendRedirect("/500?errorMsg=" + URLEncoder.encode(message, "UTF-8")); + + } catch (IOException e) { + logger.error(e.getMessage()); + } + } +} diff --git a/src/main/java/com/kyrie/security/jwt/JWTToken.java b/src/main/java/com/kyrie/security/jwt/JWTToken.java new file mode 100644 index 0000000000000000000000000000000000000000..6a7463486d7e9dde835cd371e684f18046048dae --- /dev/null +++ b/src/main/java/com/kyrie/security/jwt/JWTToken.java @@ -0,0 +1,28 @@ +package com.kyrie.security.jwt; + +import org.apache.shiro.authc.AuthenticationToken; + +/*** + * 描述: 对JWT token进行扩展 + * + * @author wuxiang + * @date 2020-04-07 12:55 + */ +public class JWTToken implements AuthenticationToken { + + private String token; + + public JWTToken (String token) { + this.token = token; + } + + @Override + public Object getPrincipal() { + return token; + } + + @Override + public Object getCredentials() { + return token; + } +} diff --git a/src/main/java/com/kyrie/security/jwt/JWTUtil.java b/src/main/java/com/kyrie/security/jwt/JWTUtil.java new file mode 100644 index 0000000000000000000000000000000000000000..15fed5856ed559945d694b502691ea76703badd5 --- /dev/null +++ b/src/main/java/com/kyrie/security/jwt/JWTUtil.java @@ -0,0 +1,81 @@ +package com.kyrie.security.jwt; + +import com.alibaba.fastjson.JSON; +import com.auth0.jwt.JWT; +import com.auth0.jwt.JWTVerifier; +import com.auth0.jwt.algorithms.Algorithm; +import com.auth0.jwt.exceptions.JWTDecodeException; +import com.auth0.jwt.interfaces.DecodedJWT; +import com.kyrie.utils.GlobalConstants; +import com.kyrie.utils.OperationCommUtil; +import com.kyrie.utils.SpringContextBean; +import com.kyrie.vo.UserInfoVO; +import java.io.UnsupportedEncodingException; +import java.util.Date; + +/*** + * 描述: JWTUtil工具类 + * + * @author wuxiang + * @date 2020-04-07 14:44 + */ +public class JWTUtil { + + private static OperationCommUtil operationCommUtil = SpringContextBean.getBean(OperationCommUtil.class); + + /** + * 生成 jwtToken 签名 + */ + public static String createToken(UserInfoVO user) { + try { + long EXPIRE_TIME = Long.valueOf(operationCommUtil.getDictValue(GlobalConstants.TYPE_2097,GlobalConstants.TYPE_2097_JWTTOKEN_EXPIRE_TIME,"900000")); + String SECRET = operationCommUtil.getDictValue(GlobalConstants.TYPE_2097,GlobalConstants.TYPE_2097_SECRET,"kyrie.com.cm"); + + Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME); + Algorithm algorithm = Algorithm.HMAC256(SECRET); + + // 附带username信息 + return JWT.create() + .withClaim(GlobalConstants.JWT_CLAIM, JSON.toJSONString(user)) + //到期时间 + .withExpiresAt(date) + //创建一个新的JWT,并使用给定的算法进行标记 + .sign(algorithm); + } catch (UnsupportedEncodingException e) { + return null; + } + } + + /** + * 校验 token 是否正确 + */ + public static boolean verify(String token, String user) { + try { + String SECRET = operationCommUtil.getDictValue(GlobalConstants.TYPE_2097,GlobalConstants.TYPE_2097_SECRET,"kyrie.com.cm"); + Algorithm algorithm = Algorithm.HMAC256(SECRET); + //在token中附带了username信息 + JWTVerifier verifier = JWT.require(algorithm) + .withClaim(GlobalConstants.JWT_CLAIM, user) + .build(); + //验证 token + verifier.verify(token); + return true; + } catch (Exception exception) { + return false; + } + } + + /** + * 获得token中的信息 + */ + public static String getUserInfo(String token) { + try { + DecodedJWT jwt = JWT.decode(token); + return jwt.getClaim(GlobalConstants.JWT_CLAIM).asString(); + } catch (JWTDecodeException e) { + return null; + } + } + + +} diff --git a/src/main/java/com/kyrie/security/shiro/ShiroConfig.java b/src/main/java/com/kyrie/security/shiro/ShiroConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..fc9abe792cd2fabc900eedcc7ab83c2a97631c4e --- /dev/null +++ b/src/main/java/com/kyrie/security/shiro/ShiroConfig.java @@ -0,0 +1,125 @@ +package com.kyrie.security.shiro; + +import com.kyrie.security.jwt.JWTFilter; +import com.kyrie.system.mybatis.service.IDefaultService; +import com.kyrie.utils.GlobalConstants; +import com.kyrie.vo.TOperationCommTypeVal; +import org.apache.commons.lang3.StringUtils; +import org.apache.shiro.mgt.DefaultSessionStorageEvaluator; +import org.apache.shiro.mgt.DefaultSubjectDAO; +import org.apache.shiro.mgt.SecurityManager; +import org.apache.shiro.spring.LifecycleBeanPostProcessor; +import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor; +import org.apache.shiro.spring.web.ShiroFilterFactoryBean; +import org.apache.shiro.web.mgt.DefaultWebSecurityManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import javax.servlet.Filter; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; + +/*** + * 描述: Shiro配置类 + * + * @author wuxiang + * @date 2020-04-07 10:38 + */ +@Configuration +public class ShiroConfig { + + private Logger logger = LoggerFactory.getLogger(this.getClass()); + + @Value("${shiro.anonMappings}") + private String anonMappings; + + /** + * 注入ShiroFilterFactoryBean + * @param securityManager SecurityManager + * @return ShiroFilterFactoryBean + */ + @Bean + public ShiroFilterFactoryBean factory (SecurityManager securityManager) { + ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean(); + + // 添加jwt过滤器 + Map filterMap = new LinkedHashMap<>(); + + filterMap.put("jwt", new JWTFilter()); + + factoryBean.setFilters(filterMap); + factoryBean.setSecurityManager(securityManager); + + // 设置无权限时跳转的URL + factoryBean.setUnauthorizedUrl("/500?errorMsg=" + GlobalConstants.LIMITED_REQUEST); + + // 设置请求URL规则配置 + Map fileterRuleMap = new HashMap<>(); + // 设置所有的请求都要通过自定义的jwt过滤器 + fileterRuleMap.put("/**","jwt"); + + logger.info("无认证mappings:{}",anonMappings); + if (StringUtils.isNotEmpty(anonMappings)) { + String[] mappingArr = anonMappings.split(";"); + for (int i = 0; i < mappingArr.length; i++) { + fileterRuleMap.put(mappingArr[i],"anon"); + } + } + + // 将规则添加到ShiroFilterFactoryBean中 + factoryBean.setFilterChainDefinitionMap(fileterRuleMap); + + return factoryBean; + } + + /** + * 注入securityManager + * + */ + @Bean + public SecurityManager securityManager (ShiroRealm shiroRealm) { + DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); + + // 设置自定义的Realm + securityManager.setRealm(shiroRealm); + + // 关闭shiro自带的session + DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO(); + DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator(); + defaultSessionStorageEvaluator.setSessionStorageEnabled(false); + subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator); + securityManager.setSubjectDAO(subjectDAO); + return securityManager; + } + + /** + * 添加注解支持 + */ + @Bean + public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() { + DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator(); + // 强制使用cglib,防止重复代理和可能引起代理出错的问题 + defaultAdvisorAutoProxyCreator.setProxyTargetClass(true); + return defaultAdvisorAutoProxyCreator; + } + + @Bean + public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) { + AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor(); + advisor.setSecurityManager(securityManager); + return advisor; + } + + /** + * 此处将shiro生命周期设置为静态,才能使用配置文件属性 + */ + @Bean + public static LifecycleBeanPostProcessor lifecycleBeanPostProcessor() { + return new LifecycleBeanPostProcessor(); + } +} diff --git a/src/main/java/com/kyrie/security/shiro/ShiroPropertity.java b/src/main/java/com/kyrie/security/shiro/ShiroPropertity.java new file mode 100644 index 0000000000000000000000000000000000000000..aa8c61adfd40f578d63cc7bd402239aea7f5f52d --- /dev/null +++ b/src/main/java/com/kyrie/security/shiro/ShiroPropertity.java @@ -0,0 +1,19 @@ +package com.kyrie.security.shiro; + +import lombok.Data; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +/*** + * 描述: 获取mappings配置信息 + * + * @author wuxiang + * @date 2020-04-08 13:34 + */ +@Data +@Component +public class ShiroPropertity { + + @Value("${shiro.anonMappings}") + private String mappings; +} diff --git a/src/main/java/com/kyrie/security/shiro/ShiroRealm.java b/src/main/java/com/kyrie/security/shiro/ShiroRealm.java new file mode 100644 index 0000000000000000000000000000000000000000..82736e36761c12cf150b65c723e47648470cb326 --- /dev/null +++ b/src/main/java/com/kyrie/security/shiro/ShiroRealm.java @@ -0,0 +1,98 @@ +package com.kyrie.security.shiro; + +import com.kyrie.security.jwt.JWTToken; +import com.kyrie.security.jwt.JWTUtil; +import com.kyrie.system.mybatis.service.IDefaultService; +import com.kyrie.utils.ConvertUtils; +import com.kyrie.vo.UserInfoVO; +import org.apache.commons.lang3.StringUtils; +import org.apache.shiro.authc.AuthenticationException; +import org.apache.shiro.authc.AuthenticationInfo; +import org.apache.shiro.authc.AuthenticationToken; +import org.apache.shiro.authc.SimpleAuthenticationInfo; +import org.apache.shiro.authz.AuthorizationInfo; +import org.apache.shiro.authz.SimpleAuthorizationInfo; +import org.apache.shiro.realm.AuthorizingRealm; +import org.apache.shiro.subject.PrincipalCollection; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; + +/*** + * 描述: 自定义ShiroRealm + * + * @author wuxiang + * @date 2020-04-07 14:06 + */ +@Component +public class ShiroRealm extends AuthorizingRealm { + + private Logger logger = LoggerFactory.getLogger(this.getClass()); + + @Resource(name = "defaultService") + private IDefaultService service; + + /** + * 重写此方法 + * 标识这个Realm是专门用来验证JwtToken,不负责验证其他的token(UsernamePasswordToken) + * @param token AuthenticationToken + * @return boolean + */ + @Override + public boolean supports(AuthenticationToken token) { + //这个token就是从过滤器中传入的jwtToken + return token instanceof JWTToken; + } + + /** + * 用户身份认证 + * @param authenticationToken AuthenticationToken + * @return AuthenticationInfo + * @throws AuthenticationException + */ + @Override + protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { + /*logger.info("##########执行用户身份认证##########");*/ + // 获取token + String token = (String) authenticationToken.getCredentials(); + // 通过token获取用户信息 + String user = JWTUtil.getUserInfo(token); + + if (StringUtils.isEmpty(user) || !JWTUtil.verify(token,user)) { + throw new AuthenticationException("token认证失败!"); + } + + // 执行数据库认证,即双重认证用户身份,防止管理员中途更改用户信息导致用户未重新登录,更改信息不生效 + // TODO 存在问题:中途管理员修改密码怎么办? + /*UserInfoVO userInfoVO = service.selectOne(NAME_SPACE + "getUserInfo",userNo); + if (null == userInfoVO) { + throw new AuthenticationException("用户信息不存在或者被更改,请重新登录"); + }*/ + + return new SimpleAuthenticationInfo(token,token,"ShiroRealm"); + } + + /** + * 用户权限认证 + * @param principals PrincipalCollection + * @return AuthorizationInfo + */ + @Override + protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { + /*logger.info("##########执行用户权限认证##########");*/ + // 获取token中的用户实体信息 + UserInfoVO userInfo = ConvertUtils.stringToBean(JWTUtil.getUserInfo(principals.toString()),UserInfoVO.class); + + SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(); + String role = userInfo.getRoleSign(); + if (StringUtils.isNotEmpty(role)) { + simpleAuthorizationInfo.addRole(role); + } + /*logger.info("#####当前用户: {},的角色权限为: {}", userInfo.getUserNo(), role);*/ + return simpleAuthorizationInfo; + } + + +} diff --git a/src/main/java/com/kyrie/system/druid/DatasourceAutoConfiguration.java b/src/main/java/com/kyrie/system/druid/DatasourceAutoConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..96d6c0da20a2eac10d8a1d1c07ecd1ef61f8fc1c --- /dev/null +++ b/src/main/java/com/kyrie/system/druid/DatasourceAutoConfiguration.java @@ -0,0 +1,181 @@ +package com.kyrie.system.druid; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.sql.DataSource; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import com.alibaba.druid.pool.DruidDataSource; +import com.alibaba.fastjson.JSON; + +/** + * 不同配置加载不同的数据源,非开发环境还需进行密文解密操作 + * single 加载单一数据源, + * routing 加载带多数据源通过dsKey与数据源名称进行匹配,选择执行sql的数据源 + * routingwriteread 除路由功能后,还有读写分离的能力,通过prefix属性来区分读写操作 + * @author wuxiang + * + */ +@Configuration +@EnableConfigurationProperties(value={DatasourceProperties.class,DatasourceProperty.class}) +public class DatasourceAutoConfiguration { + + private Logger logger = LoggerFactory.getLogger(DatasourceAutoConfiguration.class); + + @Autowired + DatasourceProperties datasourceProperties; + + @Autowired + DatasourceProperty datasourceProperty; + + /** + * 是否开启SQL拦截数据同步 + */ + @Value("${spring.datasource.sqlAop.isUse:false}") + boolean isUseSqlAop; + /** + * 解密器 + */ +// @Autowired(required=false) +// Cryptography cryptography; + + + @Value("${spring.datasource.isRemoveAbandoned:false}") + boolean isRemoveAbandoned; + + /** + * 初始化数据源 + * @return 数据源 + */ + @Bean + public DataSource datasource() { + logger.info("数据源连接池信息如下:{}", JSON.toJSONString(datasourceProperties)); + logger.info("SQL AOP 状态: {}", isUseSqlAop); + // 判断所选用的数据源类型 + if (DatasourceProperty.DATASOURCE_TYPE_SINGLE.equals(datasourceProperty.getType())) { + logger.info("亲,你选择的是single数据源,配置信息如下 :{}", JSON.toJSONString(getNodeByName(datasourceProperty.getName()))); + return createDatasource(datasourceProperties, getNodeByName(datasourceProperty.getName()), false); + } else if (DatasourceProperty.DATASOURCE_TYPE_ROUTING.equals(datasourceProperty.getType())) { + MultipleDataSource mds = new MultipleDataSource(); + Map dsMap = new HashMap<>(); + logger.info("亲,你选择的是routing数据源,配置信息如下 :"); + for(Node node : getNodeByName(datasourceProperty.getName().split(","))) { + logger.info("node name:{}, properties:{}", node.getName(), JSON.toJSONString(node)); + dsMap.put(node.getName(), createDatasource(datasourceProperties, node, false)); + } + mds.setTargetDataSources(dsMap); + String defaultName = dsMap.keySet().toArray(new String[0])[0]; + logger.info("choose routing and default datasource is {}", defaultName); + mds.setDefaultTargetDataSource(dsMap.get(defaultName)); + return mds; + } else if(DatasourceProperty.DATASOURCE_TYPE_ROUTING_READWRITE.equals(datasourceProperty.getType())) { + MultipleDataSource mds = new MultipleDataSource(); + Map dsMap = new HashMap(); + logger.info("亲,你选择的是routingreadwrite数据源,配置信息如下 :"); + for(Node node : getNodeByName(datasourceProperty.getName().split(","))) { + dsMap.put(node.getName(), createDatasource(datasourceProperties, node, false)); + logger.info("node name:{}, properties:{}", node.getName(), JSON.toJSONString(node)); + Node dsp = node.getSlave(); + if(dsp != null) { + logger.info("node's slave name:{}, properties:{}", dsp.getName(), JSON.toJSONString(dsp)); + dsMap.put(node.getName() + "-slave", createDatasource(datasourceProperties, dsp, false)); + } + } + mds.setTargetDataSources(dsMap); + String defaultName = dsMap.keySet().toArray(new String[0])[0]; + logger.info("choose routingreadwrite and default datasource is {}", defaultName); + mds.setDefaultTargetDataSource(dsMap.get(defaultName)); + return mds; + } else { + logger.error("亲,你的配置[{}]有问题,请仔细检查下数据源类型是否为[single,routing,routingreadwrite]中的一种", datasourceProperty.getType()); + throw new RuntimeException("亲,你的配置["+ datasourceProperty.getType() + "]有问题,请仔细检查下数据源类型是否为[single,routing,routingreadwrite]中的一种"); + } + } + + /** + * 通过配置生成druid数据源 + * @return 数据源 + */ + private DataSource createDatasource(DatasourceProperties datasourceProperties, Node node,boolean isJta) { + DruidDataSource datasource = new DruidDataSource(); + datasource.setUrl(node.getUrl()); + datasource.setUsername(node.getUsername()); + datasource.setDriverClassName(node.getDriverClassName()); + String passwd = node.getPassword(); + /*Node node = datasourceProperties.getNodes().get(0); + + DruidDataSource datasource = new DruidDataSource(); + datasource.setUrl(node.getUrl()); + datasource.setUsername(node.getUsername()); + datasource.setDriverClassName(node.getDriverClassName()); + String passwd = node.getPassword();*/ + // 后续对于生产环境需要进行数据库密码加密处理 + /*String dev = System.getProperty("spring.profiles.active"); + if(!(dev==null || "local".equals(dev))) {//生产测试环境密码需解密 + passwd = cryptography.decrypt(passwd); + }*/ + datasource.setPassword(passwd); + //configuration + datasource.setInitialSize(datasourceProperties.getInitialSize()); + datasource.setMinIdle(datasourceProperties.getMinIdle()); + datasource.setMaxActive(datasourceProperties.getMaxActive()); + datasource.setMaxWait(datasourceProperties.getMaxWait()); + datasource.setTimeBetweenEvictionRunsMillis(datasourceProperties.getTimeBetweenEvictionRunsMillis()); + datasource.setMinEvictableIdleTimeMillis(datasourceProperties.getMinEvictableIdleTimeMillis()); + datasource.setValidationQuery(datasourceProperties.getValidationQuery()); + datasource.setTestWhileIdle(datasourceProperties.isTestWhileIdle()); + datasource.setTestOnBorrow(datasourceProperties.isTestOnBorrow()); + datasource.setTestOnReturn(datasourceProperties.isTestOnReturn()); + datasource.setPoolPreparedStatements(datasourceProperties.isPoolPreparedStatements()); + datasource.setMaxPoolPreparedStatementPerConnectionSize(datasourceProperties.getMaxPoolPreparedStatementPerConnectionSize()); + datasource.setConnectionProperties(datasourceProperties.getConnectionProperties()); + /*datasource.setProxyFilters(druidSqlFilter());*/ + if(this.isRemoveAbandoned) { + datasource.setRemoveAbandoned(datasourceProperties.isRemoveAbandoned()); + datasource.setRemoveAbandonedTimeoutMillis(datasourceProperties.getRemoveAbandonedTimeoutMillis()); + datasource.setLogAbandoned(true); + logger.info("开启removeAbandoned模式,获取连接后在{}毫秒内不执行完的SQL将会被取消", datasourceProperties.getRemoveAbandonedTimeoutMillis()); + } + try { + datasource.setFilters(datasourceProperties.getFilters()); + datasource.init(); + logger.info("datasource name:[{}] url:[{}] username:[{}] inited successfully!", node.getName(), node.getUrl(), node.getUsername()); + } catch (SQLException e) { + logger.error("datasource inited failed", e); + } catch (Exception e) { + logger.error("db password decrypt failed"); + } + return datasource; + } + + private Node getNodeByName(String name) { + Node temp = null; + List nodes = datasourceProperties.getNodes(); + for(Node node : nodes) { + if(name.equals(node.getName())) { + temp = node; + } + } + return temp; + } + + private List getNodeByName(String[] names) { + List temp = new ArrayList(names.length); + for(String name : names) { + temp.add(getNodeByName(name)); + } + return temp; + } + + +} + diff --git a/src/main/java/com/kyrie/system/druid/DatasourceProperties.java b/src/main/java/com/kyrie/system/druid/DatasourceProperties.java new file mode 100644 index 0000000000000000000000000000000000000000..df56008dea8647bffaa4d77e85fc953a1804edf6 --- /dev/null +++ b/src/main/java/com/kyrie/system/druid/DatasourceProperties.java @@ -0,0 +1,203 @@ +package com.kyrie.system.druid; + +import java.io.Serializable; +import java.util.List; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * 读取数据源配置信息类 + * @author wuxiang + * @since jdk1.8.0 + * @version 1.0.1 + */ +@SuppressWarnings("serial") +@ConfigurationProperties(prefix = "spring.datasource") +public class DatasourceProperties implements Serializable { + /** + * 初始化连接数 + */ + private int initialSize; + /** + * 最小连接数 + */ + private int minIdle; + /** + * 最大连接数 + */ + private int maxActive; + /** + * 连接连接时最大等待时间 + */ + private int maxWait; + /** + * 连接在池内空闲时,检查周期时间,单位毫秒 + */ + private int timeBetweenEvictionRunsMillis; + /** + * 连接在池中空闲时最小生存的时间,单位是毫秒 + */ + private int minEvictableIdleTimeMillis; + /** + * 检查连接是否有效的SQL语句 + */ + private String validationQuery; + /** + * 连接空闲时是否检查连接是否可用 + */ + private boolean testWhileIdle; + /** + * 获取连接内是否检查连接是否可用 + */ + private boolean testOnBorrow; + /** + * 归还连接时是否检查连接是否可用 + */ + private boolean testOnReturn; + /** + * 是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭 + */ + private boolean poolPreparedStatements; + /** + * preparedStatement最大缓存个数 + */ + private int maxPoolPreparedStatementPerConnectionSize; + /** + * 属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有: + * stat : 监控统计用的filter: + * log4j :日志用的filter + * wall: 防御sql注入的filter + */ + private String filters; + /** + * 物理连接初始化的时候执行的sql + */ + private String connectionProperties; + /** + * 数据库节点配置集合 + */ + private List nodes; + /** + * 是否开启获取连接后不归还主动关闭 + */ + private boolean removeAbandoned; + /** + * 连接租期最长时间 + */ + private Long removeAbandonedTimeoutMillis; + + + public boolean isRemoveAbandoned() { + return removeAbandoned; + } + + + public void setRemoveAbandoned(Boolean removeAbandoned) { + this.removeAbandoned = removeAbandoned; + } + + + public void setRemoveAbandonedTimeoutMillis(Long removeAbandonedTimeoutMillis) { + this.removeAbandonedTimeoutMillis = removeAbandonedTimeoutMillis; + } + + + public Long getRemoveAbandonedTimeoutMillis() { + return removeAbandonedTimeoutMillis; + } + + public List getNodes() { + return nodes; + } + public void setNodes(List nodes) { + this.nodes = nodes; + } + public int getInitialSize() { + return initialSize; + } + public void setInitialSize(int initialSize) { + this.initialSize = initialSize; + } + public int getMinIdle() { + return minIdle; + } + public void setMinIdle(int minIdle) { + this.minIdle = minIdle; + } + public int getMaxActive() { + return maxActive; + } + public void setMaxActive(int maxActive) { + this.maxActive = maxActive; + } + public int getMaxWait() { + return maxWait; + } + public void setMaxWait(int maxWait) { + this.maxWait = maxWait; + } + public int getTimeBetweenEvictionRunsMillis() { + return timeBetweenEvictionRunsMillis; + } + public void setTimeBetweenEvictionRunsMillis(int timeBetweenEvictionRunsMillis) { + this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis; + } + public int getMinEvictableIdleTimeMillis() { + return minEvictableIdleTimeMillis; + } + public void setMinEvictableIdleTimeMillis(int minEvictableIdleTimeMillis) { + this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis; + } + public String getValidationQuery() { + return validationQuery; + } + public void setValidationQuery(String validationQuery) { + this.validationQuery = validationQuery; + } + public boolean isTestWhileIdle() { + return testWhileIdle; + } + public void setTestWhileIdle(boolean testWhileIdle) { + this.testWhileIdle = testWhileIdle; + } + public boolean isTestOnBorrow() { + return testOnBorrow; + } + public void setTestOnBorrow(boolean testOnBorrow) { + this.testOnBorrow = testOnBorrow; + } + public boolean isTestOnReturn() { + return testOnReturn; + } + public void setTestOnReturn(boolean testOnReturn) { + this.testOnReturn = testOnReturn; + } + public boolean isPoolPreparedStatements() { + return poolPreparedStatements; + } + public void setPoolPreparedStatements(boolean poolPreparedStatements) { + this.poolPreparedStatements = poolPreparedStatements; + } + public int getMaxPoolPreparedStatementPerConnectionSize() { + return maxPoolPreparedStatementPerConnectionSize; + } + public void setMaxPoolPreparedStatementPerConnectionSize(int maxPoolPreparedStatementPerConnectionSize) { + this.maxPoolPreparedStatementPerConnectionSize = maxPoolPreparedStatementPerConnectionSize; + } + public String getFilters() { + return filters; + } + public void setFilters(String filters) { + this.filters = filters; + } + public String getConnectionProperties() { + return connectionProperties; + } + public void setConnectionProperties(String connectionProperties) { + this.connectionProperties = connectionProperties; + } + + + +} + diff --git a/src/main/java/com/kyrie/system/druid/DatasourceProperty.java b/src/main/java/com/kyrie/system/druid/DatasourceProperty.java new file mode 100644 index 0000000000000000000000000000000000000000..2c4cad85b38008372ed923f626774dea9fa393d7 --- /dev/null +++ b/src/main/java/com/kyrie/system/druid/DatasourceProperty.java @@ -0,0 +1,69 @@ +package com.kyrie.system.druid; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +import java.io.Serializable; + +/*** + * 描述: 读取数据源类型配置文件 + * + * @author wuxiang + * @date 2020-04-17 00:33 + */ +@SuppressWarnings("serial") +@ConfigurationProperties(prefix = "kyrie.datasource-config") +public class DatasourceProperty implements Serializable { + /** + * 单一数据源 + */ + public static final String DATASOURCE_TYPE_SINGLE = "single"; + /** + * 路由数据源 + */ + public static final String DATASOURCE_TYPE_ROUTING = "routing"; + /** + * 路由数据源+读写分离 + */ + public static final String DATASOURCE_TYPE_ROUTING_READWRITE = "routingreadwrite"; + /** + * 分布式事务数据源 + */ + public static final String DATASOURCE_TYPE_JTA = "jta"; + /** + * 数据源类型(single,routing,routing&readwrite)三种 + */ + private String type; + /** + * 需要获取的数据源配置,如有多个请用逗号(,)分隔 + */ + private String name; + /** + * 如果数据源类型为routingreadwrite,则此属性会有效,以此前缀来区分service操作是否为写操作 + * 多个前缀请用逗号(,)分隔 + */ + private String prefix; + + public void setPrefix(String prefix) { + this.prefix = prefix; + } + + public String getPrefix() { + return prefix; + } + + public void setType(String type) { + this.type = type; + } + + public String getType() { + return type; + } + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return name; + } +} diff --git a/src/main/java/com/kyrie/system/druid/DatasourceSelectHelper.java b/src/main/java/com/kyrie/system/druid/DatasourceSelectHelper.java new file mode 100644 index 0000000000000000000000000000000000000000..2ec2d3ea1b173d29fdf3869f92bce0c1294f6aeb --- /dev/null +++ b/src/main/java/com/kyrie/system/druid/DatasourceSelectHelper.java @@ -0,0 +1,36 @@ +package com.kyrie.system.druid; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/*** + * 描述: 路由数据源切换 + * + * @author wuxiang + * @date 2020-04-17 00:24 + */ +public class DatasourceSelectHelper { + static final Logger LOGGER = LoggerFactory.getLogger(DatasourceSelectHelper.class); + + // 数据源类型 + public static final String BUSINESS_DB = "business"; + + public static final String CONFIG_DB = "config"; + + public static final String ROUTE_DB = "route"; + + public static final String REPORT_DB = "report"; + + public static final String ARC_DB = "arc"; + + private static final ThreadLocal DATASOURCEKEY = new ThreadLocal(); + + public static String getKey() { + return DATASOURCEKEY.get(); + } + + public static void setKey(String key) { + DATASOURCEKEY.set(key); + LOGGER.info("datasource dsKey switch to ######{}#####", key); + } +} diff --git a/src/main/java/com/kyrie/system/druid/MultipleDataSource.java b/src/main/java/com/kyrie/system/druid/MultipleDataSource.java new file mode 100644 index 0000000000000000000000000000000000000000..f35770eeef86088205b58905e4a360545e30b6fd --- /dev/null +++ b/src/main/java/com/kyrie/system/druid/MultipleDataSource.java @@ -0,0 +1,17 @@ +package com.kyrie.system.druid; + +import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; + +/*** + * 描述: 路由多数据源 + * + * @author wuxiang + * @date 2020-04-17 00:23 + */ +public class MultipleDataSource extends AbstractRoutingDataSource { + + @Override + protected Object determineCurrentLookupKey() { + return DatasourceSelectHelper.getKey(); + } +} diff --git a/src/main/java/com/kyrie/system/druid/MybatisConfig.java b/src/main/java/com/kyrie/system/druid/MybatisConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..42476188d245c212c2bbe2c35d74354c0838a37b --- /dev/null +++ b/src/main/java/com/kyrie/system/druid/MybatisConfig.java @@ -0,0 +1,64 @@ +package com.kyrie.system.druid; + +import org.apache.ibatis.session.SqlSessionFactory; +import org.mybatis.spring.SqlSessionFactoryBean; +import org.mybatis.spring.SqlSessionTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.datasource.DataSourceTransactionManager; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.annotation.EnableTransactionManagement; +import org.springframework.transaction.annotation.TransactionManagementConfigurer; + +import javax.sql.DataSource; + +/*** + * @author wuxiang + * 初始化sqlSessionFactory、sqlSession、JdbcTemplate + */ +@Configuration +@EnableTransactionManagement +public class MybatisConfig implements TransactionManagementConfigurer { + + /** + * 指定mapper位置 + */ + @Value("${mybatis.location}") + private String mapperLocation; + + @Autowired + private DataSource dataSource; + + @Bean(name = "sqlSessionFactory") + public SqlSessionFactory sqlSessionFactory() throws Exception { + SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); + sqlSessionFactoryBean.setDataSource(dataSource); + PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); + sqlSessionFactoryBean.setConfigLocation(resolver.getResource(mapperLocation)); + + return sqlSessionFactoryBean.getObject(); + } + + @Bean(name = "sqlSession") + public SqlSessionTemplate sqlSession(@Qualifier(value = "sqlSessionFactory") SqlSessionFactory sqlSessionFactory) { + return new SqlSessionTemplate(sqlSessionFactory); + } + + @Bean + @Override + public PlatformTransactionManager annotationDrivenTransactionManager() { + return new DataSourceTransactionManager(dataSource); + } + + @Bean + public JdbcTemplate jdbcTemplate(DataSource dataSource){ + return new JdbcTemplate(dataSource); + } + + +} diff --git a/src/main/java/com/kyrie/system/druid/Node.java b/src/main/java/com/kyrie/system/druid/Node.java new file mode 100644 index 0000000000000000000000000000000000000000..e4f8d00d43dfdcea5001a69e1b0e97f12d905971 --- /dev/null +++ b/src/main/java/com/kyrie/system/druid/Node.java @@ -0,0 +1,24 @@ +package com.kyrie.system.druid; + +import lombok.Data; + +import java.io.Serializable; + +/** + * 数据库的节点配置 + * 包括驱动类、数据库名、数据库用户名、数据库密码以及从库信息 + * 数据库密码除开发环境为明文,其它环境都为密文 + * @author wuxiang + */ +@SuppressWarnings("serial") +@Data +public class Node implements Serializable{ + private String driverClassName; + private String name; + private String url; + private String username; + private String password; + private Node slave; + +} + diff --git a/src/main/java/com/kyrie/system/mybatis/dao/StoredProcedureHandler.java b/src/main/java/com/kyrie/system/mybatis/dao/StoredProcedureHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..d7e02ecc28a7d7bca4a20947b65897c795fd36cf --- /dev/null +++ b/src/main/java/com/kyrie/system/mybatis/dao/StoredProcedureHandler.java @@ -0,0 +1,28 @@ +package com.kyrie.system.mybatis.dao; + +import javax.sql.DataSource; + +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.object.StoredProcedure; + +/*** + * @author wuxiang + * @date 2020-04-06 + */ +public class StoredProcedureHandler extends StoredProcedure { + public StoredProcedureHandler() { + } + + public StoredProcedureHandler(DataSource ds) { + this(); + setDataSource(ds); + } + + public StoredProcedureHandler(DataSource ds, String sql) { + super(ds, sql); + } + + public StoredProcedureHandler(JdbcTemplate jdbcTemplate, String name) { + super(jdbcTemplate, name); + } +} diff --git a/src/main/java/com/kyrie/system/mybatis/execption/ServiceException.java b/src/main/java/com/kyrie/system/mybatis/execption/ServiceException.java new file mode 100644 index 0000000000000000000000000000000000000000..1a034d22fcf5b3ed13ef142398f68f55c15d4552 --- /dev/null +++ b/src/main/java/com/kyrie/system/mybatis/execption/ServiceException.java @@ -0,0 +1,36 @@ +package com.kyrie.system.mybatis.execption; + +/*** + * @author wuxiang + * @date 2020-04-06 + * + * mybatis通用异常处理类 + */ +public class ServiceException extends RuntimeException { + private String messageCode; + private String message; + + private static final long serialVersionUID = -8705167646414506373L; + + public ServiceException() { + } + + public ServiceException(String message) { + super(message); + } + + public ServiceException(Throwable cause) { + super(cause); + } + + public ServiceException(String message, Throwable cause) { + super(message, cause); + this.message = message; + } + + public ServiceException(Throwable cause, String messageCode, String message) { + super("[" + messageCode + "]" + message, cause); + this.messageCode = messageCode; + } +} + diff --git a/src/main/java/com/kyrie/system/mybatis/service/IDefaultService.java b/src/main/java/com/kyrie/system/mybatis/service/IDefaultService.java new file mode 100644 index 0000000000000000000000000000000000000000..d1e29b79af20816fbff59e53f59dde90f6799fe3 --- /dev/null +++ b/src/main/java/com/kyrie/system/mybatis/service/IDefaultService.java @@ -0,0 +1,135 @@ +package com.kyrie.system.mybatis.service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import com.kyrie.system.mybatis.execption.ServiceException; +import org.springframework.jdbc.core.BatchPreparedStatementSetter; +import org.springframework.jdbc.core.ParameterizedPreparedStatementSetter; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.jdbc.core.SqlOutParameter; +import org.springframework.jdbc.core.SqlParameter; + +/*** + * @author wuxiang + * @date 2020-04-06 + * + * mybatis通用数据库访问方法 + */ +public abstract interface IDefaultService { + public abstract int insert(String paramString, Object paramObject) + throws ServiceException; + + + public abstract int delete(String paramString, Object paramObject) + throws ServiceException; + + + public abstract int update(String paramString, Object paramObject) + throws ServiceException; + + + public abstract List selectList(String paramString) + throws ServiceException; + + public abstract List selectList(String paramString, Object paramObject) + throws ServiceException; + + public abstract List selectList(String paramString, Object paramObject, int paramInt1, int paramInt2) + throws ServiceException; + + public abstract T selectOne(String paramString, Object paramObject) + throws ServiceException; + + public abstract Object[] getCommonPaged(String paramString1, String paramString2, Object paramObject, int paramInt1, int paramInt2) + throws ServiceException; + + public abstract void batchUpdate(String paramString, List paramList, int[] paramArrayOfInt) + throws ServiceException; + + public abstract int[] batchUpdate(String... paramVarArgs) + throws ServiceException; + + public abstract void batchUpdate(String paramString, List paramList) + throws ServiceException; + + public abstract void batchUpdate(String paramString, BatchPreparedStatementSetter paramBatchPreparedStatementSetter) + throws ServiceException; + + public abstract void batchUpdate(String paramString, Collection paramCollection, int paramInt, ParameterizedPreparedStatementSetter paramParameterizedPreparedStatementSetter) + throws ServiceException; + + public abstract List> queryList(String paramString, List paramList, int[] paramArrayOfInt) + throws ServiceException; + + public abstract List queryList(String paramString, List paramList, int[] paramArrayOfInt, Class paramClass) + throws ServiceException; + + public abstract List> queryList(String paramString) + throws ServiceException; + + public abstract List queryList(String paramString, Class paramClass) + throws ServiceException; + + public abstract List> queryList(String paramString, Object... paramVarArgs) + throws ServiceException; + + public abstract List queryList(String paramString, Class paramClass, Object... paramVarArgs) + throws ServiceException; + + public abstract List queryList(String paramString, List paramList, Class paramClass) + throws ServiceException; + + public abstract List queryList(String paramString, List paramList, RowMapper paramRowMapper) + throws ServiceException; + + public abstract T queryOneReocrd(String paramString, Class paramClass) + throws ServiceException; + + public abstract T queryOneReocrd(String paramString, RowMapper paramRowMapper) + throws ServiceException; + + public abstract T queryOneReocrd(String paramString, List paramList, Class paramClass) + throws ServiceException; + + public abstract T queryOneReocrd(String paramString, List paramList, RowMapper paramRowMapper) + throws ServiceException; + + public abstract T queryOneReocrd(String paramString, List paramList, int[] paramArrayOfInt, Class paramClass) + throws ServiceException; + + public abstract T queryOneReocrd(String paramString, List paramList, int[] paramArrayOfInt, RowMapper paramRowMapper) + throws ServiceException; + + public abstract int update(String paramString) + throws ServiceException; + + public abstract int updateJdbc(String paramString, Object... paramVarArgs) + throws ServiceException; + + public abstract int update(String paramString, List paramList) + throws ServiceException; + + public abstract int update(String paramString, List paramList, int[] paramArrayOfInt) + throws ServiceException; + + public abstract Map call(String paramString, boolean paramBoolean, Map paramMap, List paramList, List paramList1) + throws ServiceException; + + public abstract Map call(String paramString, boolean paramBoolean, Map paramMap, List paramList) + throws ServiceException; + + public abstract String keyCreate(String paramString, Character paramCharacter) + throws ServiceException; + + public abstract long getSeqByName(String paramString) + throws ServiceException; + + public abstract String getIdBySeq(Character paramCharacter, long paramLong) + throws ServiceException; + + public abstract String keyCreatePad15Bit(String paramString, Character paramCharacter) + throws ServiceException; +} + diff --git a/src/main/java/com/kyrie/system/mybatis/service/impl/DefaultService.java b/src/main/java/com/kyrie/system/mybatis/service/impl/DefaultService.java new file mode 100644 index 0000000000000000000000000000000000000000..f383b8ab3651823cce2334e9d953e9dd457808aa --- /dev/null +++ b/src/main/java/com/kyrie/system/mybatis/service/impl/DefaultService.java @@ -0,0 +1,525 @@ +package com.kyrie.system.mybatis.service.impl; + +import java.text.SimpleDateFormat; +import java.util.Collection; +import java.util.Date; +import java.util.List; +import java.util.Map; +import com.kyrie.system.mybatis.dao.StoredProcedureHandler; +import com.kyrie.system.mybatis.service.IDefaultService; +import com.kyrie.system.mybatis.execption.ServiceException; +import org.apache.commons.lang3.time.FastDateFormat; +import org.apache.ibatis.session.RowBounds; +import org.apache.ibatis.session.SqlSession; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DataAccessException; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.jdbc.core.BatchPreparedStatementSetter; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.ParameterizedPreparedStatementSetter; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.jdbc.core.SqlOutParameter; +import org.springframework.jdbc.core.SqlParameter; +import org.springframework.jdbc.object.StoredProcedure; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; + +/*** + * @author wuxiang + * @date 2020-04-06 + * 封装mybatis数据库通用方法 + */ +@Service("defaultService") +public class DefaultService implements IDefaultService { + @Resource(name = "sqlSession") + private SqlSession session; + + @Autowired + JdbcTemplate jdbcTemplate; + + public static final int BATCH_SIZE = 5000; + + private IDefaultService fallback = null; + + private List> fallbackExceptions = null; + + public static final long INITVAL_12 = 100000000000L; + + public static final long INITVAL_13 = 1000000000000L; + + public static final long INITVAL_7 = 1000000L; + + public static final long INITVAL_8 = 10000000L; + + /*public void setJdbcTemplate(@Autowired JdbcTemplate jdbcTemplate) { + this.jdbcTemplate = jdbcTemplate; + }*/ + + @Override + public int insert(String statementName, Object param) + throws ServiceException { + try { + return this.session.insert(statementName, param); + } catch (Exception e) { + throw new ServiceException(" 插入失败", e); + } + } + + @Override + public void batchUpdate(String sql, List batchArgs, int[] argTypes) throws ServiceException { + try { + int fromIndex = 0; + int toIndex = 5000; + while (fromIndex != batchArgs.size()) { + if (toIndex > batchArgs.size()) { + toIndex = batchArgs.size(); + } + this.jdbcTemplate.batchUpdate(sql, batchArgs.subList(fromIndex, toIndex), argTypes); + fromIndex = toIndex; + toIndex += 5000; + if (toIndex > batchArgs.size()) { + toIndex = batchArgs.size(); + } + } + } catch (Exception e) { + throw new ServiceException(" 批量更新失败", e); + } + } + + @Override + public int delete(String statementName, Object param) throws ServiceException { + try { + return this.session.delete(statementName, param); + } catch (Exception e) { + throw new ServiceException(" 删除失败", e); + } + } + + @Override + public int update(String statementName, Object param) throws ServiceException { + try { + return this.session.update(statementName, param); + } catch (Exception e) { + throw new ServiceException(" 更新失败", e); + } + } + + @Override + public List selectList(String statementName) throws ServiceException { + try { + return this.session.selectList(statementName); + } catch (Exception e) { + if (this.fallbackExceptions != null) { + for (Class c : this.fallbackExceptions) { + if (c.isAssignableFrom(e.getClass())) { + return this.fallback.selectList(statementName); + } + } + } + throw new ServiceException(" 查询失败", e); + } + } + @Override + public List> queryList(String sql, List args, int[] types) { + try { + return this.jdbcTemplate.queryForList(sql, args != null ? args.toArray() : null, types); + } catch (DataAccessException dex) { + throw new ServiceException(" 查询失败", dex); + } + } + @Override + public List queryList(String sql, List args, int[] types, Class clazz) { + try { + return this.jdbcTemplate.queryForList(sql, args != null ? args.toArray() : null, types, clazz); + } catch (DataAccessException dex) { + throw new ServiceException(" 查询失败", dex); + } + } + @Override + public List> queryList(String sql) { + try { + return this.jdbcTemplate.queryForList(sql); + } catch (DataAccessException dex) { + throw new ServiceException(" 查询失败", dex); + } + } + @Override + public List queryList(String sql, Class clazz) { + try { + return this.jdbcTemplate.queryForList(sql, clazz); + } catch (DataAccessException dex) { + throw new ServiceException(" 查询失败", dex); + } + } + @Override + public List> queryList(String sql, Object... args) { + try { + return this.jdbcTemplate.queryForList(sql, args); + } catch (DataAccessException dex) { + throw new ServiceException("查询失败", dex); + } + } + @Override + public List queryList(String sql, Class clazz, Object... args) { + try { + return this.jdbcTemplate.queryForList(sql, clazz, args); + } catch (DataAccessException dex) { + throw new ServiceException("查询失败", dex); + } + } + @Override + public List queryList(String sql, List args, Class clazz) { + try { + return this.jdbcTemplate.queryForList(sql, args != null ? args.toArray() : null, clazz); + } catch (DataAccessException dex) { + throw new ServiceException("查询失败", dex); + } + } + @Override + public List queryList(String sql, List args, RowMapper rowMapper) + throws ServiceException { + try { + return this.jdbcTemplate.query(sql, args != null ? args.toArray() : null, rowMapper); + } catch (DataAccessException dex) { + throw new ServiceException("查询失败", dex); + } + } + @Override + public T queryOneReocrd(String sql, Class clazz) { + try { + return (T) this.jdbcTemplate.queryForObject(sql, clazz); + } catch (EmptyResultDataAccessException dex) { + return null; + } catch (DataAccessException dex) { + throw new ServiceException("查询失败", dex); + } + } + @Override + public T queryOneReocrd(String sql, RowMapper rowMapper) { + try { + return (T) this.jdbcTemplate.queryForObject(sql, rowMapper); + } catch (EmptyResultDataAccessException dex) { + return null; + } catch (DataAccessException dex) { + throw new ServiceException("查询失败", dex); + } + } + @Override + public T queryOneReocrd(String sql, List args, Class clazz) { + try { + return (T) this.jdbcTemplate.queryForObject(sql, args != null ? args.toArray() : null, clazz); + } catch (EmptyResultDataAccessException dex) { + return null; + } catch (DataAccessException dex) { + throw new ServiceException("查询失败", dex); + } + } + @Override + public T queryOneReocrd(String sql, List args, RowMapper rowMapper) { + try { + return (T) this.jdbcTemplate.queryForObject(sql, args != null ? args.toArray() : null, rowMapper); + } catch (EmptyResultDataAccessException dex) { + return null; + } catch (DataAccessException dex) { + throw new ServiceException("查询失败", dex); + } + } + @Override + public T queryOneReocrd(String sql, List args, int[] argTypes, Class clazz) { + try { + return (T) this.jdbcTemplate.queryForObject(sql, args != null ? args.toArray() : null, argTypes, clazz); + } catch (EmptyResultDataAccessException dex) { + return null; + } catch (DataAccessException dex) { + throw new ServiceException("查询失败", dex); + } + } + @Override + public T queryOneReocrd(String sql, List args, int[] argTypes, RowMapper rowMapper) { + try { + return (T) this.jdbcTemplate.queryForObject(sql, args != null ? args.toArray() : null, argTypes, rowMapper); + } catch (EmptyResultDataAccessException dex) { + return null; + } catch (DataAccessException dex) { + throw new ServiceException("查询失败", dex); + } + } + @Override + public int update(String sql) { + try { + return this.jdbcTemplate.update(sql); + } catch (DataAccessException dex) { + throw new ServiceException("更新失败", dex); + } + } + @Override + public int updateJdbc(String sql, Object... args) { + try { + return this.jdbcTemplate.update(sql, args); + } catch (DataAccessException dex) { + throw new ServiceException("更新失败", dex); + } + } + @Override + public int update(String sql, List args) { + try { + return this.jdbcTemplate.update(sql, args != null ? args.toArray() : null); + } catch (DataAccessException dex) { + throw new ServiceException("更新失败", dex); + } + } + @Override + public int update(String sql, List args, int[] argTypes) { + try { + return this.jdbcTemplate.update(sql, args != null ? args.toArray() : null, argTypes); + } catch (DataAccessException dex) { + throw new ServiceException("更新失败", dex); + } + } + @Override + public Map call(String name, boolean isFunction, Map procedureParamMap, List sqlInParameters, List sqlOutParameter) { + try { + StoredProcedure storedProcedure = new StoredProcedureHandler(this.jdbcTemplate, name); + if ((sqlInParameters != null) && (!sqlInParameters.isEmpty())) { + for (SqlParameter sqlParameter : sqlInParameters) { + storedProcedure.declareParameter(sqlParameter); + } + } + if ((sqlOutParameter != null) && (!sqlOutParameter.isEmpty())) { + for (SqlOutParameter sqlParameter : sqlOutParameter) { + storedProcedure.declareParameter(sqlParameter); + } + } + storedProcedure.setFunction(isFunction); + storedProcedure.compile(); + return storedProcedure.execute(procedureParamMap); + } catch (DataAccessException dex) { + throw new ServiceException("存过执行失败", dex); + } + } + @Override + public Map call(String name, boolean isFunction, Map procedureParamMap, List sqlParameters) + throws ServiceException { + try { + StoredProcedure storedProcedure = new StoredProcedureHandler(this.jdbcTemplate, name); + if ((sqlParameters != null) && (!sqlParameters.isEmpty())) { + for (SqlParameter sqlParameter : sqlParameters) { + storedProcedure.declareParameter(sqlParameter); + } + } + storedProcedure.setFunction(isFunction); + storedProcedure.compile(); + return storedProcedure.execute(procedureParamMap); + } catch (DataAccessException dex) { + throw new ServiceException("存过执行失败", dex); + } + } + @Override + public List selectList(String statementName, Object param, int offset, int limit) + throws ServiceException { + try { + if (limit < 0) { + return selectList(statementName, param); + } + return this.session.selectList(statementName, param, new RowBounds(offset, limit)); + } catch (Exception e) { + if (this.fallbackExceptions != null) { + for (Class c : this.fallbackExceptions) { + if (c.isAssignableFrom(e.getClass())) { + return this.fallback.selectList(statementName, param, offset, limit); + } + } + } + throw new ServiceException("查询失败", e); + } + } + @Override + public List selectList(String statementName, Object param) + throws ServiceException { + try { + return this.session.selectList(statementName, param); + } catch (Exception e) { + if (this.fallbackExceptions != null) { + for (Class c : this.fallbackExceptions) { + if (c.isAssignableFrom(e.getClass())) { + return this.fallback.selectList(statementName, param); + } + } + } + throw new ServiceException("查询失败", e); + } + } + @Override + public T selectOne(String statementName, Object param) + throws ServiceException { + try { + return (T) this.session.selectOne(statementName, param); + } catch (Exception e) { + if (this.fallbackExceptions != null) { + for (Class c : this.fallbackExceptions) { + if (c.isAssignableFrom(e.getClass())) { + return (T) this.fallback.selectOne(statementName, param); + } + } + } + throw new ServiceException("查询失败", e); + } + } + @Override + public Object[] getCommonPaged(String queryStatement, String countStatement, Object param, int offset, int limit) + throws ServiceException { + try { + Object[] obj = new Object[2]; + obj[0] = selectList(queryStatement, param, offset, limit); + obj[1] = selectOne(countStatement, param); + return obj; + } catch (Exception e) { + if (this.fallbackExceptions != null) { + for (Class c : this.fallbackExceptions) { + if (c.isAssignableFrom(e.getClass())) { + return this.fallback.getCommonPaged(queryStatement, countStatement, param, offset, limit); + } + } + } + throw new ServiceException("查询失败", e); + } + } + + + @Override + public int[] batchUpdate(String... sql) + throws ServiceException { + try { + return this.jdbcTemplate.batchUpdate(sql); + } catch (Exception e) { + throw new ServiceException("批量更新失败", e); + } + } + @Override + public void batchUpdate(String sql, List batchArgs) + throws ServiceException { + try { + int fromIndex = 0; + int toIndex = 5000; + while (fromIndex < batchArgs.size()) { + if (toIndex > batchArgs.size()) { + toIndex = batchArgs.size(); + } + this.jdbcTemplate.batchUpdate(sql, batchArgs.subList(fromIndex, toIndex)); + fromIndex = toIndex; + toIndex += 5000; + } + } catch (Exception e) { + throw new ServiceException("批量更新失败", e); + } + } + @Override + public void batchUpdate(String sql, BatchPreparedStatementSetter pss) + throws ServiceException { + try { + this.jdbcTemplate.batchUpdate(sql, pss); + } catch (Exception e) { + throw new ServiceException("批量更新失败", e); + } + } + @Override + public void batchUpdate(String sql, Collection batchArgs, int batchSize, ParameterizedPreparedStatementSetter ppss) + throws ServiceException { + try { + this.jdbcTemplate.batchUpdate(sql, batchArgs, batchSize, ppss); + } catch (Exception e) { + throw new ServiceException("批量更新失败", e); + } + } + @Override + public String keyCreate(String sequence, Character prefix) + throws ServiceException { + try { + Long seqId = (Long) this.jdbcTemplate.queryForObject("select " + sequence + ".nextval from dual", Long.class); + return format20bit(prefix, seqId.longValue()); + } catch (Exception e) { + throw new ServiceException("seqId创建失败", e); + } + } + @Override + public String keyCreatePad15Bit(String sequence, Character prefix) + throws ServiceException { + try { + Long seqId = (Long) this.jdbcTemplate.queryForObject("select " + sequence + ".nextval from dual", Long.class); + return format15bit(prefix, seqId.longValue()); + } catch (Exception e) { + throw new ServiceException("seqId创建失败", e); + } + } + + private String format(long initval, long number) { + return String.valueOf(initval + number).substring(1); + } + + public static final FastDateFormat df = FastDateFormat.getInstance("yyyyMMdd"); + + private String format20bit(Character prefix, long number) { + return getIdBySeq(prefix == null ? "" : prefix.toString(), String.valueOf(number)); + } + + public static String getIdBySeq(String seqId) { + StringBuffer seqBuf = new StringBuffer(); + if ((seqId != null) && (seqId.length() > 0) && (seqId.length() < 13)) { + SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd"); + seqBuf.append(df.format(new Date())); + for (int i = 0; i < 12 - seqId.length(); i++) { + seqBuf.append("0"); + } + seqBuf.append(seqId); + } else if ((seqId != null) && (seqId.length() > 12)) { + SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd"); + seqBuf.append(df.format(new Date())); + seqBuf.append(seqId.substring(seqId.length() - 12)); + } + return seqBuf.toString(); + } + + public static String getIdBySeq(String prefix, String seqId) { + StringBuffer seqBuf = new StringBuffer(prefix); + if ((seqId != null) && (seqId.length() > 0) && (seqId.length() + prefix.length() < 13)) { + SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd"); + seqBuf.append(df.format(new Date())); + for (int i = 0; i < 12 - prefix.length() - seqId.length(); i++) { + seqBuf.append("0"); + } + seqBuf.append(seqId); + } else if ((seqId != null) && (seqId.length() + prefix.length() > 12)) { + SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd"); + seqBuf.append(df.format(new Date())); + seqBuf.append(seqId.substring(seqId.length() + prefix.length() - 12)); + } + return seqBuf.toString(); + } + + private String format15bit(Character prefix, long number) { + number %= (prefix == null ? 10000000L : 1000000L); + String num = format(prefix == null ? 10000000L : 1000000L, number); + String datestr = df.format(new Date()); + return (prefix == null ? "" : prefix) + datestr + num; + } + @Override + public long getSeqByName(String sequence) + throws ServiceException { + try { + return ((Long) this.jdbcTemplate.queryForObject("select " + sequence + ".nextval from dual", Long.class)).longValue(); + } catch (Exception e) { + throw new ServiceException("seqId创建失败", e); + } + } + @Override + public String getIdBySeq(Character prefix, long number) + throws ServiceException { + try { + return format20bit(prefix, number); + } catch (Exception e) { + throw new ServiceException("seqId创建失败", e); + } + } +} + diff --git a/src/main/java/com/kyrie/utils/ConvertUtils.java b/src/main/java/com/kyrie/utils/ConvertUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..e4592394fe91d3f1033ed52e77fb7e4fd3c461b7 --- /dev/null +++ b/src/main/java/com/kyrie/utils/ConvertUtils.java @@ -0,0 +1,47 @@ +package com.kyrie.utils; + +import com.alibaba.fastjson.JSON; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +/*** + * 描述: 类型转换工具类 + * + * @author wuxiang + * @date 2020-04-12 14:43 + */ +public class ConvertUtils { + + /** + * 把一个字符串转换成bean对象(by FastJson) + * @param str String + * @param T + * @return T + */ + public static T stringToBean(String str, Class clazz) { + if(str == null || str.length() <= 0 || clazz == null) { + return null; + } + if(clazz == int.class || clazz == Integer.class) { + return (T)Integer.valueOf(str); + }else if(clazz == String.class) { + return (T)str; + }else if(clazz == long.class || clazz == Long.class) { + return (T)Long.valueOf(str); + }else { + return JSON.toJavaObject(JSON.parseObject(str), clazz); + } + } + + /** + * Object类型转换为json String类型(by Gson) + * @param object Object + * @return String + */ + public static String toJson(Object object) { + GsonBuilder gsonBuilder = new GsonBuilder(); + gsonBuilder.setPrettyPrinting(); + Gson gson = gsonBuilder.create(); + return gson.toJson(object); + } +} diff --git a/src/main/java/com/kyrie/utils/GlobalConstants.java b/src/main/java/com/kyrie/utils/GlobalConstants.java new file mode 100644 index 0000000000000000000000000000000000000000..b7af75169be065000fff639108138a89733bd686 --- /dev/null +++ b/src/main/java/com/kyrie/utils/GlobalConstants.java @@ -0,0 +1,70 @@ +package com.kyrie.utils; + +/*** + * 描述: 全局常量配置类 + * + * @author wuxiang + * @date 2020-04-06 16:31 + */ +public class GlobalConstants { + + // 接口成功调用resultCode + public static final String SUCCESS_CODE = "1"; + + // 接口成功失败resultCode + public static final String FAIL_CODE = "0"; + + // 无认证数据字典类型 + public static final String ANON_TYPE_CODE = "shiroUrl"; + + // 无认证数据字典值编码 + public static final String ANON_VAL_CODE = "anno"; + + // jwt claim + public static final String JWT_CLAIM = "user"; + + // 安全token + public static final String SECURITY_TOKEN = "token"; + + // 越权访问错误信息 + public static final String CROSS_ACCESS_MSG = "请求被拦截,拦截原因:当前请求存在越权访问的安全风险。"; + + // token认证失败 + public static final String TOKEN_LOSS_MSG = "token认证失败,失败原因:token丢失。请重新登录。"; + + // 受限制请求提示 + public static final String LIMITED_REQUEST = "受限制的请求,请核实用户权限!"; + + // 用户访问标识userNo,不带此标识会被拦截掉 + public static final String BUSINESS_REQUEST_ID = "userNo"; + + // redis缓存失效时间 + public static final int REDIS_EXPIRE_TIME = 300;// redis缓存失效时间 + + // 全局数据库mapper目录 + public static final String NAME_SPACE = "com.kyrie.config."; + + // 全局数据字典配置 + public static final String TYPE_2097 = "2097"; + + // jwt token 失效时间 + public static final String TYPE_2097_JWTTOKEN_EXPIRE_TIME = "jwtToken_expire_time"; + + // jwt token 密钥 + public static final String TYPE_2097_SECRET = "secretCode"; + + /**################################## kafka相关常量配置 ##################################*/ + public final static String AUTO_FLUSH_NAME = "autoFlush"; + public final static String TOPIC_NAME = "topic"; + public final static String KEY_SERIALIZER = "key-serializer"; + public final static String VALUE_SERIALIZER = "value-serializer"; + + + /**################################## kafka相关常量配置 ##################################*/ + + + + + + +} diff --git a/src/main/java/com/kyrie/utils/IActionContext.java b/src/main/java/com/kyrie/utils/IActionContext.java new file mode 100644 index 0000000000000000000000000000000000000000..1b337373cc4023a30cbcab350a8483f8c5a67ef8 --- /dev/null +++ b/src/main/java/com/kyrie/utils/IActionContext.java @@ -0,0 +1,30 @@ +package com.kyrie.utils; + +import com.kyrie.security.jwt.JWTUtil; +import com.kyrie.vo.UserInfoVO; +import org.apache.shiro.SecurityUtils; + +/*** + * 描述: 全局获取当前用户信息类 + * + * @author wuxiang + * @date 2020-04-16 15:53 + */ +public class IActionContext { + + /** + * 获取用户姓名 + */ + public static String getUserNo() { + return getUser().getUserNo(); + } + + /** + * 获取用户信息 + */ + public static UserInfoVO getUser() { + UserInfoVO principal = ConvertUtils.stringToBean( + JWTUtil.getUserInfo(SecurityUtils.getSubject().getPrincipal().toString()), UserInfoVO.class); + return principal; + } +} diff --git a/src/main/java/com/kyrie/utils/OperationCommUtil.java b/src/main/java/com/kyrie/utils/OperationCommUtil.java new file mode 100644 index 0000000000000000000000000000000000000000..0d8d346857a14c70ee5caf2a64a3b62612545418 --- /dev/null +++ b/src/main/java/com/kyrie/utils/OperationCommUtil.java @@ -0,0 +1,108 @@ +package com.kyrie.utils; + +import com.alibaba.druid.util.StringUtils; +import com.kyrie.dto.OperateDictionariesDto; +import com.kyrie.system.mybatis.execption.ServiceException; +import com.kyrie.system.mybatis.service.IDefaultService; +import com.kyrie.vo.TOperationCommTypeVal; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import redis.clients.jedis.JedisCluster; +import javax.annotation.Resource; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +/*** + * 描述: 数据字典缓存操作工具类 + * + * @author wuxiang + * @date 2020-04-14 11:04 + */ +@Component("operationCommUtil") +public class OperationCommUtil { + + private Logger logger = LoggerFactory.getLogger(this.getClass()); + + @Autowired + private JedisCluster jedisCluster; + + @Resource(name = "defaultService") + private IDefaultService defaultService; + + + /** + * 获取缓存列表:通过typeId+valId查询 + */ + public List getDictList(String typeId,String valId) throws ServiceException { + return getDictionariesFromDb(typeId,valId); + } + + /** + * 从数据库中获取字典信息 + */ + private List getDictionariesFromDb(String typeId, String valId) throws ServiceException { + Map map = new HashMap<>(); + map.put("typeId",typeId); + map.put("valId",valId); + List list = defaultService.selectList(GlobalConstants.NAME_SPACE + "getDictionariesFromDb",map); + return list; + } + + /** + * 从数据库中获取字典值 + */ + private String getValueFromDb(String typeId, String valId) throws ServiceException { + Map map = new HashMap<>(); + map.put("typeId",typeId); + map.put("valId",valId); + return defaultService.selectOne(GlobalConstants.NAME_SPACE + "getValueFromDb",map); + } + + + /** + * 获取缓存字典值valName + */ + public String getDictValue(String typeId, String valId, String defaultValue) { + // 首先取缓存 + String valueName = jedisCluster.get(typeId +":" + valId); + if (!StringUtils.isEmpty(valueName)) { + return valueName; + } + // 缓存没有就从数据库获取 + valueName = getValueFromDb(typeId,valId); + if (!StringUtils.isEmpty(valueName)) { + jedisCluster.set(typeId+":"+valId,valueName); + } else { + valueName = defaultValue; + } + return valueName; + } + + /** + * 新增数据字典 + */ + public int addDictonaries(OperateDictionariesDto operateDictionariesDto) throws ServiceException { + String seqId = UUID.randomUUID().toString(); + operateDictionariesDto.setSeqId(seqId); + return defaultService.insert(GlobalConstants.NAME_SPACE + "addDictonaries", operateDictionariesDto); + } + + /** + * 删除数据字典 + */ + public int delDictonaries(OperateDictionariesDto operateDictionariesDto) throws ServiceException { + return defaultService.insert(GlobalConstants.NAME_SPACE + "delDictonaries", operateDictionariesDto); + } + + /** + * 更新数据字典 + */ + public int updDictonaries(OperateDictionariesDto operateDictionariesDto) throws ServiceException { + return defaultService.update(GlobalConstants.NAME_SPACE + "updDictonaries", operateDictionariesDto); + } + +} diff --git a/src/main/java/com/kyrie/utils/RequestUtil.java b/src/main/java/com/kyrie/utils/RequestUtil.java new file mode 100644 index 0000000000000000000000000000000000000000..3c0fc9d1317567be6194ff32e102e854c69482aa --- /dev/null +++ b/src/main/java/com/kyrie/utils/RequestUtil.java @@ -0,0 +1,63 @@ +package com.kyrie.utils; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; +/*** + * 描述: + * + * @author wuxiang + * @date 2020-04-09 23:54 + */ +public class RequestUtil { + + /** + * 从request中获得参数Map,并返回可读的Map + */ + public static Map getParameterMap(HttpServletRequest request){ + //参数Map + Map properties = request.getParameterMap(); + //返回值Map + Map returnMap = new HashMap(); + Iterator it = properties.keySet().iterator(); + String name = ""; + String value = ""; + while(it.hasNext()){ + String key = it.next(); + Object valueObj = properties.get(key); + if(null == valueObj){ + value = ""; + }else if(valueObj instanceof String[]){ + String[] values = (String[]) valueObj; + for(int i = 0; i trimParameter(Map param){ + //返回值Map + Map returnMap = new HashMap(); + Iterator it = param.keySet().iterator(); + String value = ""; + while(it.hasNext()){ + String key = it.next(); + Object valueObj = param.get(key); + if(null == valueObj){ + value = ""; + }else{ + value = valueObj.toString().trim(); + } + returnMap.put(key, value); + } + return returnMap; + } +} + diff --git a/src/main/java/com/kyrie/utils/SpringContextBean.java b/src/main/java/com/kyrie/utils/SpringContextBean.java new file mode 100644 index 0000000000000000000000000000000000000000..49aa10f2410e883c9aae252d684bd0c51af4d3ec --- /dev/null +++ b/src/main/java/com/kyrie/utils/SpringContextBean.java @@ -0,0 +1,10 @@ +package com.kyrie.utils; + +import org.springframework.context.annotation.DependsOn; + +@DependsOn("springContextHolder") +public class SpringContextBean { + public static T getBean(Class clazz){ + return (T)SpringContextHolder.getBean(clazz); + } +} diff --git a/src/main/java/com/kyrie/utils/SpringContextHolder.java b/src/main/java/com/kyrie/utils/SpringContextHolder.java new file mode 100644 index 0000000000000000000000000000000000000000..5c2c1e90220e2460ae2b27e3f6ebae4e741bf91e --- /dev/null +++ b/src/main/java/com/kyrie/utils/SpringContextHolder.java @@ -0,0 +1,46 @@ +package com.kyrie.utils; + +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.stereotype.Component; + +/*** + * 全局上下文 + * @author wuxiang + * + */ +@Component("springContextHolder") +public class SpringContextHolder implements ApplicationContextAware{ + + private static ApplicationContext application; + + @Override + public void setApplicationContext(ApplicationContext application) throws BeansException { + SpringContextHolder.application = application; + } + + @SuppressWarnings("unchecked") + public static T getBean(String name){ + AssertApplication(); + return (T)application.getBean(name); + } + + public static T getBean(Class clazz){ + AssertApplication(); + return (T)application.getBean(clazz); + } + + public void setApplicaiont(ApplicationContext applicaiont) { + SpringContextHolder.application = applicaiont; + } + + + + private static void AssertApplication(){ + if (application == null) { + throw new RuntimeException("SpringContextHolder中application为空,请检查是否注入"); + } + } + +} diff --git a/src/main/java/com/kyrie/vo/DataSourceEnum.java b/src/main/java/com/kyrie/vo/DataSourceEnum.java new file mode 100644 index 0000000000000000000000000000000000000000..5afac67e86a38c23d840199e6f6a06eb98eaf466 --- /dev/null +++ b/src/main/java/com/kyrie/vo/DataSourceEnum.java @@ -0,0 +1,46 @@ +package com.kyrie.vo; + +/*** + * 描述: 数据源枚举类 + * + * @author wuxiang + * @date 2020-04-21 17:43 + */ +public enum DataSourceEnum { + + /** + * 业务库 + */ + BUSINESS_DB("business"), + + /** + * 配置库 + */ + CONFIG_DB("config"), + + /** + * 路由库 + */ + ROUTE_DB("route"), + + /** + * 报表库 + */ + REPORT_DB("report"), + + /** + * 归档库 + */ + ARC_DB("arc"); + + private String value; + + DataSourceEnum(String value) { + this.value = value; + } + + public String getValue(){ + return value; + } + +} diff --git a/src/main/java/com/kyrie/vo/DictOperateEnum.java b/src/main/java/com/kyrie/vo/DictOperateEnum.java new file mode 100644 index 0000000000000000000000000000000000000000..4b1de3efd64cab1a852a216ae64ca45f152d9d3a --- /dev/null +++ b/src/main/java/com/kyrie/vo/DictOperateEnum.java @@ -0,0 +1,36 @@ +package com.kyrie.vo; + +/*** + * 描述: 数据字典操作方式枚举类 + * + * @author wuxiang + * @date 2020-04-21 17:43 + */ +public enum DictOperateEnum { + + /** + * 新增字典 + */ + DICT_ADD("add"), + + /** + * 删除字典 + */ + DICT_DEL("delete"), + + /** + * 更新字典 + */ + DICT_UPD("update"); + + private String value; + + DictOperateEnum(String value) { + this.value = value; + } + + public String getValue(){ + return value; + } + +} diff --git a/src/main/java/com/kyrie/vo/LoginLogVO.java b/src/main/java/com/kyrie/vo/LoginLogVO.java new file mode 100644 index 0000000000000000000000000000000000000000..b373e70d45b1765a0a370f6e30d3f7ac2da9f53f --- /dev/null +++ b/src/main/java/com/kyrie/vo/LoginLogVO.java @@ -0,0 +1,31 @@ +package com.kyrie.vo; + +import lombok.Data; + +import java.io.Serializable; + +/*** + * 描述: + * + * @author wuxiang + * @date 2020-04-06 16:38 + */ +@Data +public class LoginLogVO implements Serializable { + + private static final long serialVersionUID = 7253053491456999442L; + + private String createTime; + + private String logType; + + private String ip; + + private Integer id; + + private String state; + + private String message; + + private Integer userId; +} diff --git a/src/main/java/com/kyrie/vo/LoginReturnBean.java b/src/main/java/com/kyrie/vo/LoginReturnBean.java new file mode 100644 index 0000000000000000000000000000000000000000..f45d4c569511f0a7ff474809a23d301b92b8d10c --- /dev/null +++ b/src/main/java/com/kyrie/vo/LoginReturnBean.java @@ -0,0 +1,30 @@ +package com.kyrie.vo; + +import lombok.Data; + +import java.io.Serializable; + +/*** + * 描述: 用户登录返回信息VO + * + * @author wuxiang + * @date 2020-04-07 14:32 + */ +@Data +public class LoginReturnBean implements Serializable { + + + private static final long serialVersionUID = -6511915232912132173L; + private String userNo; + + private String userName; + + private String departmentId; + + private String roleSign; + + private String roleName; + + private String token; + +} diff --git a/src/main/java/com/kyrie/vo/ResponseBean.java b/src/main/java/com/kyrie/vo/ResponseBean.java new file mode 100644 index 0000000000000000000000000000000000000000..7a96799f4a3476a41285f228b14356c827bd2ac9 --- /dev/null +++ b/src/main/java/com/kyrie/vo/ResponseBean.java @@ -0,0 +1,90 @@ +package com.kyrie.vo; + +/*** + * 描述: 接口通用返回VO + * + * @author wuxiang + * @date 2020-04-06 16:29:30 + */ +public class ResponseBean { + + private String resultCode; + + private String resultMsg; + + private T result; + + private ResponseBean (T result, String resultCode, String resultMsg) { + this.result = result; + this.resultCode = resultCode; + this.resultMsg = resultMsg; + } + + /*** + * 参数校验失败的回调方法 + * @param result 返回数据vo + * @param resultCode 返回resultCode + * @param resultMsg 返回resultMsg + * @return + */ + public static ResponseBean notValid (T result, String resultCode, String resultMsg) { + return new ResponseBean(result, resultCode, resultMsg); + } + + /*** + * 数据库异常的回调方法 + * @param result 返回数据vo + * @param resultCode 返回resultCode + * @param resultMsg 返回resultMsg + * @return + */ + public static ResponseBean exception (T result, String resultCode, String resultMsg) { + return new ResponseBean(result, resultCode, resultMsg); + } + + /*** + * 成功的回调方法 + * @param result 返回数据vo + * @param resultCode 返回resultCode + * @param resultMsg 返回resultMsg + * @return + */ + public static ResponseBean success (T result, String resultCode, String resultMsg) { + return new ResponseBean(result, resultCode, resultMsg); + } + + /*** + * 失败的回调方法 + * @param result 返回数据vo + * @param resultCode 返回resultCode + * @param resultMsg 返回resultMsg + * @return + */ + public static ResponseBean fail (T result, String resultCode, String resultMsg) { + return new ResponseBean(result, resultCode, resultMsg); + } + + public String getResultCode() { + return resultCode; + } + + public void setResultCode(String resultCode) { + this.resultCode = resultCode; + } + + public String getResultMsg() { + return resultMsg; + } + + public void setResultMsg(String resultMsg) { + this.resultMsg = resultMsg; + } + + public T getResult() { + return result; + } + + public void setResult(T result) { + this.result = result; + } +} diff --git a/src/main/java/com/kyrie/vo/TOperationCommTypeVal.java b/src/main/java/com/kyrie/vo/TOperationCommTypeVal.java new file mode 100644 index 0000000000000000000000000000000000000000..263470bab49504ad45bb123c2439e3dfd1c11629 --- /dev/null +++ b/src/main/java/com/kyrie/vo/TOperationCommTypeVal.java @@ -0,0 +1,41 @@ +package com.kyrie.vo; + +import lombok.Data; + +import java.io.Serializable; + +/*** + * 描述: 数据字典实体VO + * + * @author wuxiang + * @date 2020-04-08 12:25 + */ +@Data +public class TOperationCommTypeVal implements Serializable { + + private static final long serialVersionUID = 88339235806548762L; + + private String seqId; + + private String typeId; + + private String typeName; + + private String valId; + + private String valName; + + private String extVal; + + private String status; + + private Integer orderBy; + + private String reamke; + + private String createDate; + + private String createPerson; + + +} diff --git a/src/main/java/com/kyrie/vo/TransLogVO.java b/src/main/java/com/kyrie/vo/TransLogVO.java new file mode 100644 index 0000000000000000000000000000000000000000..0937491b84db60d71e69416f675142ca7556da33 --- /dev/null +++ b/src/main/java/com/kyrie/vo/TransLogVO.java @@ -0,0 +1,30 @@ +package com.kyrie.vo; + +import lombok.Data; + +import java.io.Serializable; + +/*** + * 描述: + * + * @author wuxiang + * @date 2020-04-16 17:34 + */ +@Data +public class TransLogVO implements Serializable { + private static final long serialVersionUID = -2509600590043868481L; + + private String logId; + + private String transCode; + + private String requestBody; + + private String responseBody; + + private String transOperator; + + private String transLogDesc; + + private String transTime; +} diff --git a/src/main/java/com/kyrie/vo/UserInfoVO.java b/src/main/java/com/kyrie/vo/UserInfoVO.java new file mode 100644 index 0000000000000000000000000000000000000000..9fc73e14e2c434cbc1c6600f481dd994a22fa489 --- /dev/null +++ b/src/main/java/com/kyrie/vo/UserInfoVO.java @@ -0,0 +1,44 @@ +package com.kyrie.vo; + +import lombok.Data; + +import java.io.Serializable; + +/*** + * 描述: 用户信息VO + * + * @author wuxiang + * @date 2020-04-07 14:32 + */ +@Data +public class UserInfoVO implements Serializable { + + private static final long serialVersionUID = -8323890249190589223L; + + private String seqId; + + private String userNo; + + private String userName; + + private String password; + + private String salt; + + private String departmentId; + + private String roleSign; + + private String roleName; + + private String isLocked; + + private String ifDisplay; + + private String createDate; + + private String createPerson; + + private String token; + +} diff --git a/src/main/java/com/kyrie/vo/UserRoleVO.java b/src/main/java/com/kyrie/vo/UserRoleVO.java new file mode 100644 index 0000000000000000000000000000000000000000..5bad8e0abac411f932b031b79dc6d945b4dc8afe --- /dev/null +++ b/src/main/java/com/kyrie/vo/UserRoleVO.java @@ -0,0 +1,31 @@ +package com.kyrie.vo; + +import lombok.Data; + +import java.io.Serializable; + +/*** + * 描述: 用户角色VO + * + * @author wuxiang + * @date 2020-04-07 23:21 + */ +@Data +public class UserRoleVO implements Serializable { + + private static final long serialVersionUID = 5860763071247277860L; + + private String seqId; + + private String roleId; + + private String roleSign; + + private String roleName; + + private String createDate; + + private String createPerson; + + private String ifUseAble; +} diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml new file mode 100644 index 0000000000000000000000000000000000000000..69bd04ce97b08db6d4959db275fc94fc1b13f4e4 --- /dev/null +++ b/src/main/resources/application-dev.yml @@ -0,0 +1,47 @@ +server: + servlet: + context-path: / + port: 31001 + + +spring: + #数据库的配置 + datasource: + initialSize: 5 + minIdle: 5 + maxActive: 20 + maxWait: 60000 + timeBetweenEvictionRunsMillis: 60000 + minEvictableIdleTimeMillis: 300000 + validationQuery: SELECT 'x' FROM DUAL + testWhileIdle: true + testOnBorrow: false + testOnReturn: false + poolPreparedStatements: true + maxOpenPreparedStatements: 20 + filters: stat,wall,log4j + connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000 + nodes[0]: + name: dev-1 #dev数据库1 + driver-class-name: com.mysql.jdbc.Driver + url: jdbc:mysql://127.0.0.1:3306/edxapp?useUnicode=true&characterEncoding=utf-8 + username: root + password: CSpa2s_is + nodes[1]: + name: dev-2 #dev数据库2 + driver-class-name: com.mysql.jdbc.Driver + url: jdbc:mysql://127.0.0.1:3306/edxapp?useUnicode=true&characterEncoding=utf-8 + username: root + password: CSpa2s_is + nodes[2]: + name: dev-3 #dev数据库3 + driver-class-name: com.mysql.jdbc.Driver + url: jdbc:mysql://127.0.0.1:3306/edxapp?useUnicode=true&characterEncoding=utf-8 + username: root + password: CSpa2s_is + +#使用assembly打包后的配置文件在config目录下,因此需要指定在config目录下 +logging: + config: config/logback-spring.xml +mybatis: + location: file:config/mybatis-config.xml #dev、uat、prod环境使用文件读取config目录下的配置文件 \ No newline at end of file diff --git a/src/main/resources/application-local.yml b/src/main/resources/application-local.yml new file mode 100644 index 0000000000000000000000000000000000000000..2d2813a5382f2820a515209f9b0e27fc8b5598d1 --- /dev/null +++ b/src/main/resources/application-local.yml @@ -0,0 +1,110 @@ +server: + servlet: + context-path: / + port: 31006 + +#数据源加载类型:routing-路由多数据源、single-单数据源,name:表示当前类型的数据源可以切换哪些数据库,prefix:表示可进行的数据库操作 +kyrie: + datasource-config: + type: routing + name: config,business + prefix: add,update,sumbit,save,delete,cancel,insert + +#下方为单一数据源single配置 +#kyrie: +# datasource-config: +# type: single +# name: config + +spring: + datasource: + initialSize: 5 + minIdle: 5 + maxActive: 20 + maxWait: 60000 + timeBetweenEvictionRunsMillis: 60000 + minEvictableIdleTimeMillis: 300000 + validationQuery: SELECT 'x' FROM DUAL + testWhileIdle: true + testOnBorrow: false + testOnReturn: false + poolPreparedStatements: true + maxOpenPreparedStatements: 20 + filters: stat,wall,log4j + connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000 + nodes[0]: + name: config #配置库 + driver-class-name: com.mysql.jdbc.Driver + url: jdbc:mysql://127.0.0.1:3306/interface_common?useUnicode=true&characterEncoding=utf-8 + username: root + password: CSpa2s_is + nodes[1]: + name: business #业务库 + driver-class-name: com.mysql.jdbc.Driver + url: jdbc:mysql://127.0.0.1:3306/edxapp?useUnicode=true&characterEncoding=utf-8 + username: root + password: CSpa2s_is + nodes[2]: + name: route #路由库 + driver-class-name: com.mysql.jdbc.Driver + url: jdbc:mysql://127.0.0.1:3306/edxapp?useUnicode=true&characterEncoding=utf-8 + username: root + password: CSpa2s_is + nodes[3]: + name: report #报表库 + driver-class-name: com.mysql.jdbc.Driver + url: jdbc:mysql://127.0.0.1:3306/edxapp?useUnicode=true&characterEncoding=utf-8 + username: root + password: CSpa2s_is + nodes[4]: + name: arc #归档库 + driver-class-name: com.mysql.jdbc.Driver + url: jdbc:mysql://127.0.0.1:3306/edxapp?useUnicode=true&characterEncoding=utf-8 + username: root + password: CSpa2s_is + +#使用assembly打包后的配置文件在config目录下,因此需要指定在config目录下 +logging: + config: classpath:logback-spring.xml +mybatis: + location: classpath:mybatis-config.xml + +interface: + redis: #redis集群配置 + soTimeout: 2000 + connectionTimeout: 2000 + maxAttempts: 3 + passwd: CSpa2s_is + maxTotal: 10 + maxIdle: 10 + minIdel: 0 + testOnBorrow: false + testOnIdle: true + testOnReturn: false + timeBetweenEvictionRunsMillis: 60000 + numTestsPerEvictionRun: -1 + jedis[0]: + host: 127.0.0.1 + port: 6379 + jedis[1]: + host: 127.0.0.1 + port: 6380 + jedis[2]: + host: 127.0.0.1 + port: 6381 + kafka: #kafka集群配置 + bootstrap-servers: http://127.0.0.1:9092,http://127.0.0.1:9093,http://127.0.0.1:9094 + producer: + buffer-memory: 40960 + max-block: 6000 + retries: 0 + batch-size: 4096 + linger: 1 + client-id: producer.client.id.kafka + consumer: + group-id: ci-data + enable-auto-commit: true + auto-commit-interval: 100 + session-timeout: 9000 + auto-offset-reset: latest + heartbeat-interval: 3000 #心跳时间应设置为session-timeout的1/3 \ No newline at end of file diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml new file mode 100644 index 0000000000000000000000000000000000000000..7b0ca431290442a2b4786f9534deb04bc866cb75 --- /dev/null +++ b/src/main/resources/application-prod.yml @@ -0,0 +1,47 @@ +server: + servlet: + context-path: / + port: 31001 + + +spring: + #数据库的配置 + datasource: + initialSize: 5 + minIdle: 5 + maxActive: 20 + maxWait: 60000 + timeBetweenEvictionRunsMillis: 60000 + minEvictableIdleTimeMillis: 300000 + validationQuery: SELECT 'x' FROM DUAL + testWhileIdle: true + testOnBorrow: false + testOnReturn: false + poolPreparedStatements: true + maxOpenPreparedStatements: 20 + filters: stat,wall,log4j + connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000 + nodes[0]: + name: uat-1 #uat数据库1 + driver-class-name: com.mysql.jdbc.Driver + url: jdbc:mysql://127.0.0.1:3306/edxapp?useUnicode=true&characterEncoding=utf-8 + username: root + password: CSpa2s_is + nodes[1]: + name: uat-2 #uat数据库2 + driver-class-name: com.mysql.jdbc.Driver + url: jdbc:mysql://127.0.0.1:3306/edxapp?useUnicode=true&characterEncoding=utf-8 + username: root + password: CSpa2s_is + nodes[2]: + name: uat-3 #uat数据库3 + driver-class-name: com.mysql.jdbc.Driver + url: jdbc:mysql://127.0.0.1:3306/edxapp?useUnicode=true&characterEncoding=utf-8 + username: root + password: CSpa2s_is + +#使用assembly打包后的配置文件在config目录下,因此需要指定在config目录下 +logging: + config: config/logback-spring.xml +mybatis: + location: file:config/mybatis-config.xml #uat、prod环境使用文件读取config目录下的配置文件 \ No newline at end of file diff --git a/src/main/resources/application-uat.yml b/src/main/resources/application-uat.yml new file mode 100644 index 0000000000000000000000000000000000000000..7b0ca431290442a2b4786f9534deb04bc866cb75 --- /dev/null +++ b/src/main/resources/application-uat.yml @@ -0,0 +1,47 @@ +server: + servlet: + context-path: / + port: 31001 + + +spring: + #数据库的配置 + datasource: + initialSize: 5 + minIdle: 5 + maxActive: 20 + maxWait: 60000 + timeBetweenEvictionRunsMillis: 60000 + minEvictableIdleTimeMillis: 300000 + validationQuery: SELECT 'x' FROM DUAL + testWhileIdle: true + testOnBorrow: false + testOnReturn: false + poolPreparedStatements: true + maxOpenPreparedStatements: 20 + filters: stat,wall,log4j + connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000 + nodes[0]: + name: uat-1 #uat数据库1 + driver-class-name: com.mysql.jdbc.Driver + url: jdbc:mysql://127.0.0.1:3306/edxapp?useUnicode=true&characterEncoding=utf-8 + username: root + password: CSpa2s_is + nodes[1]: + name: uat-2 #uat数据库2 + driver-class-name: com.mysql.jdbc.Driver + url: jdbc:mysql://127.0.0.1:3306/edxapp?useUnicode=true&characterEncoding=utf-8 + username: root + password: CSpa2s_is + nodes[2]: + name: uat-3 #uat数据库3 + driver-class-name: com.mysql.jdbc.Driver + url: jdbc:mysql://127.0.0.1:3306/edxapp?useUnicode=true&characterEncoding=utf-8 + username: root + password: CSpa2s_is + +#使用assembly打包后的配置文件在config目录下,因此需要指定在config目录下 +logging: + config: config/logback-spring.xml +mybatis: + location: file:config/mybatis-config.xml #uat、prod环境使用文件读取config目录下的配置文件 \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 0000000000000000000000000000000000000000..a2adbbc0f6f14069b93ec7abab6381e9e528e771 --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,13 @@ +spring: +#环境自动激活 + profiles: + active: @profileActive@ + #JPA + jpa: + show-sql: true + +shiro: + anonMappings: /login;/500/**;/error + +kyrie: + loginUrl: /login \ No newline at end of file diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml new file mode 100755 index 0000000000000000000000000000000000000000..e9dc39ba0f66e12cefa8e4a571852cc3e645f5c4 --- /dev/null +++ b/src/main/resources/logback-spring.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + ${CONSOLE_LOG_PATTERN} + utf8 + + + + /Users/wuxiang/Desktop/applog/interface-common/interface-common.log + + /Users/wuxiang/Desktop/applog/interface-common/interface-common.%d{yyyy-MM-dd}.%i.log.zip + 200MB + 10 + 2GB + + + %d %5p | %t | %logger{25} - %msg%n + utf-8 + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/mybatis-config.xml b/src/main/resources/mybatis-config.xml new file mode 100644 index 0000000000000000000000000000000000000000..8446ab2ba04c45a462e8ead1f5c677be5cf8542e --- /dev/null +++ b/src/main/resources/mybatis-config.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file