mirror of https://gitee.com/stylefeng/roses
初始化项目
commit
1bfabf1ffa
|
@ -0,0 +1,40 @@
|
|||
*.class
|
||||
|
||||
# Package Files #
|
||||
*.jar
|
||||
*.war
|
||||
*.ear
|
||||
target/
|
||||
|
||||
# eclipse
|
||||
.settings/
|
||||
.classpath
|
||||
.project
|
||||
logs/
|
||||
|
||||
# idea
|
||||
.idea/
|
||||
*.iml
|
||||
|
||||
*velocity.log*
|
||||
|
||||
### STS ###
|
||||
.apt_generated
|
||||
.factorypath
|
||||
.springBeans
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
*.iws
|
||||
*.ipr
|
||||
|
||||
### NetBeans ###
|
||||
nbproject/private/
|
||||
build/
|
||||
nbbuild/
|
||||
dist/
|
||||
nbdist/
|
||||
.nb-gradle/
|
||||
|
||||
*.log
|
||||
tmp/
|
|
@ -0,0 +1,131 @@
|
|||
## Roses核心包
|
||||
|
||||
基于《Java开发手册(嵩山版)》
|
||||
|
||||
### 规则1 模块的分类(a类,d类,o类,s类,p类)
|
||||
|
||||
> a类排名第一,Advanced,为全模块的规则,本公司所有的代码都需要遵守的规则,包含枚举,异常,基础类等
|
||||
|
||||
> d类排名第二,Development,给开发人员用的快速开发工具,方便快速开发,例如日志,邮件,短信,缓存等
|
||||
|
||||
> o类排名第三,Operations,偏运维类的封装,例如监控,调用链记录,内网转发模块
|
||||
|
||||
> s类排名第四,Service,偏应用功能的封装,例如用户管理,角色管理,公司管理,每个模块是一个独立的业务
|
||||
|
||||
> p类排名第五,Pattern,设计模式或业务解决方案,例如高并发的解决方案,海量数据存储方案等
|
||||
|
||||
### 规则2 模块的建设标准
|
||||
|
||||
> 2.1 模块建立的基本思想是封装重用的代码,提高开发效率
|
||||
|
||||
> 2.2 由团队核心成员评估批准后进行模块的编写,模块的编写要遵守第3条规定
|
||||
|
||||
### 规则3 模块设计思想
|
||||
|
||||
> 3.1 每个模块内部分三类子模块
|
||||
|
||||
分别是api、sdk、business,api为对其他模块暴露的接口,sdk是对核心功能的封装,business是带业务逻辑的封装
|
||||
|
||||
以短信模块kernel-d-sms为例,sms-api模块是接口模块,是短信功能提供的所有接口。
|
||||
|
||||
sms-sdk-aliyun模块是阿里云短信的sdk封装。
|
||||
|
||||
sms-sdk-tencent模块是腾讯云短信的sdk封装。
|
||||
|
||||
sms-business-validation模块是带短信验证功能(业务)的模块。
|
||||
|
||||
api、sdk、business为三类模块,不是三个,一般api模块仅一个,sdk和business类模块可以无限拓展。
|
||||
|
||||
> 3.2 依赖接口不依赖实现
|
||||
|
||||
模块与模块之间的调用,通过api模块来调用(例如sms-api),而不直接依赖他的实现(sms-sdk或sms-business),具体的实现由business模块决定或者由具体项目决定。
|
||||
|
||||
> 3.3 每个模块要详细编写readme
|
||||
|
||||
每个kernel模块要编写对应的readme文档
|
||||
|
||||
每个kernel的子模块也要写清楚readme文档
|
||||
|
||||
> 3.4 所有api的实现都装入spring容器中,使用api时通过@Resource注入api
|
||||
|
||||
同一个项目,一个api的实现可以有两个,需要通过@Resource(name = "xxx")指定资源的名字。
|
||||
|
||||
### 规则4 模块中任何类均可拓展
|
||||
|
||||
利用@Primary注解来替换已经装载的spring容器中的bean
|
||||
|
||||
## 规则5 每个模块要有一个常量类
|
||||
|
||||
常量类用来存放模块名称和异常枚举的步进值,如果本模块异常较多,可以存放多个步进值
|
||||
|
||||
```java
|
||||
public interface RuleConstants {
|
||||
|
||||
/**
|
||||
* 规则模块的名称
|
||||
*/
|
||||
String RULE_MODULE_NAME = "kernel-a-rule";
|
||||
|
||||
/**
|
||||
* 异常枚举的步进值
|
||||
*/
|
||||
String RULE_EXCEPTION_STEP_CODE = "00";
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
## 规则6 每个模块要有一个异常类
|
||||
|
||||
异常类要集成ServiceException
|
||||
|
||||
```java
|
||||
public class DaoException extends ServiceException {
|
||||
|
||||
public DaoException(AbstractExceptionEnum exception) {
|
||||
super(DbConstants.DB_MODULE_NAME, exception);
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
## 规则7 强依赖
|
||||
|
||||
项目基于spring boot架构,所以对spring boot强依赖,另外对hutool工具类,lombok,fastjson强依赖,其他框架不强依赖
|
||||
|
||||
## 规则8 expander包是对配置表的拓展
|
||||
|
||||
kernel-d-config模块只负责对系统配置的初始化,新增,删除等操作,不进行对某个具体配置的维护,各个模块需要配置拓展属性时,在各个模块的api模块建立expander包维护
|
||||
|
||||
## 规则9 business可以依赖sdk层,sdk层可依赖api层,反之不行
|
||||
|
||||
## 规则10 高模块可依赖低模块的api,反之不行
|
||||
|
||||
s类的api模块可以依赖d类的api,反之不行,防止出现互相依赖(循环依赖)的情况
|
||||
|
||||
## 规则11 Bean的装配,尽量在类的构造函数,不要在类的内部用@Resource或者@Autowired
|
||||
|
||||
构造函数装配更灵活,如果直接用@Resource则会交给spring去装配,spring会去找到容器中的相关bean,不如手动的灵活
|
||||
|
||||
多出现在装配的是接口的情况,如果接口有多个实现,很明显用构造函数去装配更合适
|
||||
|
||||
## 规则12 pojo的分包结构
|
||||
|
||||
pojo下可以分为request(控制器层请求参数的封装),response(控制器层响应参数的封装),param(其他类下参数的封装)
|
||||
|
||||
其中request包下的类以Request结尾,response包下的类以Response结尾,param包下的类以Param结尾
|
||||
|
||||
request包下的类一般会加上参数校验注解,参数校验用的hibernate validator注解
|
||||
|
||||
一般情况,直接用实体返回,减少一些pojo的书写,复杂的返回对象还是要单独封装pojo
|
||||
|
||||
## 规则13 表的设计
|
||||
|
||||
表名不要用缩写,用全拼单词
|
||||
|
||||
排序字段用decimal带两位小数点,这样往里边插入数据的时候,不用改别人的排序,就可以通过小数来插入了,如果两位不够用的时候,还可以扩充为3位等等
|
||||
|
||||
表设计中,不要用mysql的关键字作为字段和表名
|
||||
|
||||
## 规则14 pom中的注释要写清楚,为什么引用这个模块写到每个依赖上边
|
||||
|
||||
## 规则15 像小白一样思考,像专家一样行动
|
|
@ -0,0 +1,18 @@
|
|||
整个框架每个模块需要遵守的规范
|
||||
|
||||
每个开发人员也需要遵守的规范
|
||||
|
||||
## 业务异常统一用ServiceException类
|
||||
|
||||
## 每个模块的异常枚举要在exception.enums包下维护
|
||||
|
||||
## 异常枚举分三类
|
||||
|
||||
异常分为三类,第一类用户操作异常的枚举以UserExceptionEnum结尾
|
||||
|
||||
第二类是系统业务逻辑异常,枚举以BusinessExceptionEnum结尾
|
||||
|
||||
第三类是第三方调用异常,枚举以ThirdExceptionEnum结尾
|
||||
|
||||
## 每个模块设立独立的模块异常,方便区分各个模块的异常
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>cn.stylefeng.roses</groupId>
|
||||
<artifactId>roses-kernel</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>kernel-a-rule</artifactId>
|
||||
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- spring boot -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- hutool -->
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- lombok -->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!--json解析-->
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>fastjson</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!--web-->
|
||||
<!--HttpServletContext获取http请求上下文会用到-->
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!--spring-web-->
|
||||
<!--HttpServletContext获取http请求上下文会用到-->
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>${project.artifactId}</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<version>3.0.1</version>
|
||||
<configuration>
|
||||
<attach>true</attach>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>compile</phase>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
|
@ -0,0 +1,33 @@
|
|||
package cn.stylefeng.roses.kernel.rule.abstracts;
|
||||
|
||||
/**
|
||||
* 异常枚举格式规范,每个异常枚举都要实现这个接口
|
||||
* <p>
|
||||
* 为了在抛出ServiceException的时候规范抛出的内容
|
||||
* <p>
|
||||
* ServiceException抛出时必须为本接口的实现类
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/14 21:41
|
||||
*/
|
||||
public interface AbstractExceptionEnum {
|
||||
|
||||
/**
|
||||
* 获取异常的状态码
|
||||
*
|
||||
* @return 状态码
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/14 21:42
|
||||
*/
|
||||
String getErrorCode();
|
||||
|
||||
/**
|
||||
* 获取给用户提示信息
|
||||
*
|
||||
* @return 提示信息
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/14 21:42
|
||||
*/
|
||||
String getUserTip();
|
||||
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
package cn.stylefeng.roses.kernel.rule.abstracts;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 树形节点的抽象接口
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/15 14:31
|
||||
*/
|
||||
public interface AbstractTreeNode {
|
||||
|
||||
/**
|
||||
* 获取节点id
|
||||
*
|
||||
* @return 节点的id标识
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/15 15:28
|
||||
*/
|
||||
String getNodeId();
|
||||
|
||||
/**
|
||||
* 获取节点父id
|
||||
*
|
||||
* @return 父节点的id
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/15 15:28
|
||||
*/
|
||||
String getNodeParentId();
|
||||
|
||||
/**
|
||||
* 设置children
|
||||
*
|
||||
* @param childrenNodes 设置节点的子节点
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/15 15:28
|
||||
*/
|
||||
void setChildrenNodes(List childrenNodes);
|
||||
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
package cn.stylefeng.roses.kernel.rule.constants;
|
||||
|
||||
/**
|
||||
* 规则模块的常量
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/16 11:25
|
||||
*/
|
||||
public interface RuleConstants {
|
||||
|
||||
/**
|
||||
* 用户端操作异常的错误码分类编号
|
||||
*/
|
||||
String USER_OPERATION_ERROR_TYPE_CODE = "A";
|
||||
|
||||
/**
|
||||
* 业务执行异常的错误码分类编号
|
||||
*/
|
||||
String BUSINESS_ERROR_TYPE_CODE = "B";
|
||||
|
||||
/**
|
||||
* 第三方调用异常的错误码分类编号
|
||||
*/
|
||||
String THIRD_ERROR_TYPE_CODE = "C";
|
||||
|
||||
/**
|
||||
* 一级宏观码标识,宏观码标识代表一类错误码的统称
|
||||
*/
|
||||
String FIRST_LEVEL_WIDE_CODE = "0001";
|
||||
|
||||
/**
|
||||
* 请求成功的返回码
|
||||
*/
|
||||
String SUCCESS_CODE = "00000";
|
||||
|
||||
/**
|
||||
* 请求成功的返回信息
|
||||
*/
|
||||
String SUCCESS_MESSAGE = "请求成功";
|
||||
|
||||
/**
|
||||
* 规则模块的名称
|
||||
*/
|
||||
String RULE_MODULE_NAME = "kernel-a-rule";
|
||||
|
||||
/**
|
||||
* 异常枚举的步进值
|
||||
*/
|
||||
String RULE_EXCEPTION_STEP_CODE = "01";
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package cn.stylefeng.roses.kernel.rule.constants;
|
||||
|
||||
/**
|
||||
* 构建树有关的常量
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/15 15:50
|
||||
*/
|
||||
public interface TreeConstants {
|
||||
|
||||
/**
|
||||
* 根节点Id
|
||||
*/
|
||||
String ROOT_TREE_NODE_ID = "-1";
|
||||
|
||||
/**
|
||||
* 根节点名称
|
||||
*/
|
||||
String ROOT_TREE_NODE_NAME = "根结点";
|
||||
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
package cn.stylefeng.roses.kernel.rule.enums;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 性别的枚举
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/17 10:01
|
||||
*/
|
||||
@Getter
|
||||
public enum SexEnum {
|
||||
|
||||
/**
|
||||
* 男
|
||||
*/
|
||||
M("M", "男"),
|
||||
|
||||
/**
|
||||
* 女
|
||||
*/
|
||||
F("F", "女");
|
||||
|
||||
private final String code;
|
||||
|
||||
private final String message;
|
||||
|
||||
SexEnum(String code, String message) {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据code获取枚举
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/29 18:59
|
||||
*/
|
||||
public static SexEnum codeToEnum(String code) {
|
||||
if (null != code) {
|
||||
for (SexEnum e : SexEnum.values()) {
|
||||
if (e.getCode().equals(code)) {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
Copyright [2020] [https://www.stylefeng.cn]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
Guns采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
|
||||
|
||||
1.请不要删除和修改根目录下的LICENSE文件。
|
||||
2.请不要删除和修改Guns源码头部的版权声明。
|
||||
3.请保留源码和相关描述文件的项目出处,作者声明等。
|
||||
4.分发源码时候,请注明软件出处 https://gitee.com/stylefeng/guns-separation
|
||||
5.在修改包名,模块名称,项目代码等时,请注明软件出处 https://gitee.com/stylefeng/guns-separation
|
||||
6.若您的项目无法满足以上几点,可申请商业授权,获取Guns商业授权许可,请在官网购买授权,地址为 https://www.stylefeng.cn
|
||||
*/
|
||||
package cn.stylefeng.roses.kernel.rule.enums;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 公共状态,一般用来表示开启和关闭
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/14 21:31
|
||||
*/
|
||||
@Getter
|
||||
public enum StatusEnum {
|
||||
|
||||
/**
|
||||
* 启用
|
||||
*/
|
||||
ENABLE(1, "启用"),
|
||||
|
||||
/**
|
||||
* 禁用
|
||||
*/
|
||||
DISABLE(2, "禁用");
|
||||
|
||||
private final Integer code;
|
||||
|
||||
private final String message;
|
||||
|
||||
StatusEnum(Integer code, String message) {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据code获取枚举
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/29 18:59
|
||||
*/
|
||||
public static StatusEnum codeToEnum(Integer code) {
|
||||
if (null != code) {
|
||||
for (StatusEnum e : StatusEnum.values()) {
|
||||
if (e.getCode().equals(code)) {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
package cn.stylefeng.roses.kernel.rule.enums;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 是或否的枚举,一般用在数据库字段,例如del_flag字段,char(1),填写Y或N
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/4/13 22:59
|
||||
*/
|
||||
@Getter
|
||||
public enum YesOrNotEnum {
|
||||
|
||||
/**
|
||||
* 是
|
||||
*/
|
||||
Y("Y", "是"),
|
||||
|
||||
/**
|
||||
* 否
|
||||
*/
|
||||
N("N", "否");
|
||||
|
||||
private final String code;
|
||||
|
||||
private final String message;
|
||||
|
||||
YesOrNotEnum(String code, String message) {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
Copyright [2020] [https://www.stylefeng.cn]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
Guns采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
|
||||
|
||||
1.请不要删除和修改根目录下的LICENSE文件。
|
||||
2.请不要删除和修改Guns源码头部的版权声明。
|
||||
3.请保留源码和相关描述文件的项目出处,作者声明等。
|
||||
4.分发源码时候,请注明软件出处 https://gitee.com/stylefeng/guns-separation
|
||||
5.在修改包名,模块名称,项目代码等时,请注明软件出处 https://gitee.com/stylefeng/guns-separation
|
||||
6.若您的项目无法满足以上几点,可申请商业授权,获取Guns商业授权许可,请在官网购买授权,地址为 https://www.stylefeng.cn
|
||||
*/
|
||||
package cn.stylefeng.roses.kernel.rule.exception.base;
|
||||
|
||||
import cn.stylefeng.roses.kernel.rule.abstracts.AbstractExceptionEnum;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import static cn.stylefeng.roses.kernel.rule.constants.RuleConstants.RULE_MODULE_NAME;
|
||||
|
||||
/**
|
||||
* 所有业务异常的基类
|
||||
* <p>
|
||||
* 在抛出异常时候,务必带上AbstractExceptionEnum枚举
|
||||
* <p>
|
||||
* 业务异常分为三种
|
||||
* <p>
|
||||
* 第一种是用户端操作的异常,例如用户输入参数为空,用户输入账号密码不正确
|
||||
* <p>
|
||||
* 第二种是当前系统业务逻辑出错,例如系统执行出错,磁盘空间不足
|
||||
* <p>
|
||||
* 第三种是第三方系统调用出错,例如文件服务调用失败,RPC调用超时
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/15 9:07
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class ServiceException extends RuntimeException {
|
||||
|
||||
/**
|
||||
* 错误码
|
||||
*/
|
||||
private String errorCode;
|
||||
|
||||
/**
|
||||
* 返回给用户的提示信息
|
||||
*/
|
||||
private String userTip;
|
||||
|
||||
/**
|
||||
* 异常的模块名称
|
||||
*/
|
||||
private String moduleName;
|
||||
|
||||
/**
|
||||
* 根据模块名,错误码,用户提示直接抛出异常
|
||||
*/
|
||||
public ServiceException(String moduleName, String errorCode, String userTip) {
|
||||
super(userTip);
|
||||
this.errorCode = moduleName;
|
||||
this.moduleName = errorCode;
|
||||
this.userTip = userTip;
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果要直接抛出ServiceException,可以用这个构造函数
|
||||
*/
|
||||
public ServiceException(String moduleName, AbstractExceptionEnum exception) {
|
||||
super(exception.getUserTip());
|
||||
this.moduleName = moduleName;
|
||||
this.errorCode = exception.getErrorCode();
|
||||
this.userTip = exception.getUserTip();
|
||||
}
|
||||
|
||||
/**
|
||||
* 不建议直接抛出ServiceException,因为这样无法确认是哪个模块抛出的异常
|
||||
* <p>
|
||||
* 建议使用业务异常时,都抛出自己模块的异常类
|
||||
*/
|
||||
public ServiceException(AbstractExceptionEnum exception) {
|
||||
super(exception.getUserTip());
|
||||
this.moduleName = RULE_MODULE_NAME;
|
||||
this.errorCode = exception.getErrorCode();
|
||||
this.userTip = exception.getUserTip();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package cn.stylefeng.roses.kernel.rule.exception.enums.defaults;
|
||||
|
||||
import cn.stylefeng.roses.kernel.rule.abstracts.AbstractExceptionEnum;
|
||||
import lombok.Getter;
|
||||
|
||||
import static cn.stylefeng.roses.kernel.rule.constants.RuleConstants.BUSINESS_ERROR_TYPE_CODE;
|
||||
import static cn.stylefeng.roses.kernel.rule.constants.RuleConstants.FIRST_LEVEL_WIDE_CODE;
|
||||
|
||||
/**
|
||||
* 系统执行出错,业务本身逻辑问题导致的错误(一级宏观码)
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/15 17:18
|
||||
*/
|
||||
@Getter
|
||||
public enum DefaultBusinessExceptionEnum implements AbstractExceptionEnum {
|
||||
|
||||
/**
|
||||
* 系统执行出错(一级宏观错误码)
|
||||
*/
|
||||
SYSTEM_RUNTIME_ERROR(BUSINESS_ERROR_TYPE_CODE + FIRST_LEVEL_WIDE_CODE, "系统执行出错,请检查系统运行状况");
|
||||
|
||||
/**
|
||||
* 错误编码
|
||||
*/
|
||||
private final String errorCode;
|
||||
|
||||
/**
|
||||
* 提示用户信息
|
||||
*/
|
||||
private final String userTip;
|
||||
|
||||
DefaultBusinessExceptionEnum(String errorCode, String userTip) {
|
||||
this.errorCode = errorCode;
|
||||
this.userTip = userTip;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package cn.stylefeng.roses.kernel.rule.exception.enums.defaults;
|
||||
|
||||
import cn.stylefeng.roses.kernel.rule.abstracts.AbstractExceptionEnum;
|
||||
import lombok.Getter;
|
||||
|
||||
import static cn.stylefeng.roses.kernel.rule.constants.RuleConstants.FIRST_LEVEL_WIDE_CODE;
|
||||
import static cn.stylefeng.roses.kernel.rule.constants.RuleConstants.THIRD_ERROR_TYPE_CODE;
|
||||
|
||||
/**
|
||||
* 表示错误来源于第三方服务,比如 CDN 服务出错,消息投递超时等问题
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/15 17:31
|
||||
*/
|
||||
@Getter
|
||||
public enum DefaultThirdExceptionEnum implements AbstractExceptionEnum {
|
||||
|
||||
/**
|
||||
* 调用第三方服务出错(一级宏观错误码)
|
||||
*/
|
||||
THIRD_PARTY_ERROR(THIRD_ERROR_TYPE_CODE + FIRST_LEVEL_WIDE_CODE, "第三方调用出现错误");
|
||||
|
||||
/**
|
||||
* 错误编码
|
||||
*/
|
||||
private final String errorCode;
|
||||
|
||||
/**
|
||||
* 提示用户信息
|
||||
*/
|
||||
private final String userTip;
|
||||
|
||||
DefaultThirdExceptionEnum(String errorCode, String userTip) {
|
||||
this.errorCode = errorCode;
|
||||
this.userTip = userTip;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package cn.stylefeng.roses.kernel.rule.exception.enums.defaults;
|
||||
|
||||
import cn.stylefeng.roses.kernel.rule.abstracts.AbstractExceptionEnum;
|
||||
import lombok.Getter;
|
||||
|
||||
import static cn.stylefeng.roses.kernel.rule.constants.RuleConstants.FIRST_LEVEL_WIDE_CODE;
|
||||
import static cn.stylefeng.roses.kernel.rule.constants.RuleConstants.USER_OPERATION_ERROR_TYPE_CODE;
|
||||
|
||||
/**
|
||||
* 源于用户操作的异常枚举,比如参数错误,用户安装版本过低,用户支付超时等问题
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/15 17:31
|
||||
*/
|
||||
@Getter
|
||||
public enum DefaultUserExceptionEnum implements AbstractExceptionEnum {
|
||||
|
||||
/**
|
||||
* 用户端错误(一级宏观错误码)
|
||||
*/
|
||||
USER_OPERATION_ERROR(USER_OPERATION_ERROR_TYPE_CODE + FIRST_LEVEL_WIDE_CODE, "执行失败,请检查操作是否正常");
|
||||
|
||||
/**
|
||||
* 错误编码
|
||||
*/
|
||||
private final String errorCode;
|
||||
|
||||
/**
|
||||
* 提示用户信息
|
||||
*/
|
||||
private final String userTip;
|
||||
|
||||
DefaultUserExceptionEnum(String errorCode, String userTip) {
|
||||
this.errorCode = errorCode;
|
||||
this.userTip = userTip;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package cn.stylefeng.roses.kernel.rule.exception.enums.http;
|
||||
|
||||
import cn.stylefeng.roses.kernel.rule.abstracts.AbstractExceptionEnum;
|
||||
import lombok.Getter;
|
||||
|
||||
import static cn.stylefeng.roses.kernel.rule.constants.RuleConstants.BUSINESS_ERROR_TYPE_CODE;
|
||||
import static cn.stylefeng.roses.kernel.rule.constants.RuleConstants.RULE_EXCEPTION_STEP_CODE;
|
||||
|
||||
/**
|
||||
* servlet相关业务异常
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/15 17:39
|
||||
*/
|
||||
@Getter
|
||||
public enum ServletExceptionEnum implements AbstractExceptionEnum {
|
||||
|
||||
/**
|
||||
* 获取不到http context异常
|
||||
*/
|
||||
HTTP_CONTEXT_ERROR(BUSINESS_ERROR_TYPE_CODE + RULE_EXCEPTION_STEP_CODE + "01", "获取不到http context,请确认当前请求是http请求");
|
||||
|
||||
/**
|
||||
* 错误编码
|
||||
*/
|
||||
private final String errorCode;
|
||||
|
||||
/**
|
||||
* 提示用户信息
|
||||
*/
|
||||
private final String userTip;
|
||||
|
||||
ServletExceptionEnum(String errorCode, String userTip) {
|
||||
this.errorCode = errorCode;
|
||||
this.userTip = userTip;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
package cn.stylefeng.roses.kernel.rule.factory;
|
||||
|
||||
import cn.stylefeng.roses.kernel.rule.abstracts.AbstractTreeNode;
|
||||
import cn.stylefeng.roses.kernel.rule.factory.base.AbstractTreeBuildFactory;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 默认递归工具类,用于遍历有父子关系的节点,例如菜单树,字典树等等
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2018/7/25 下午5:59
|
||||
*/
|
||||
@Data
|
||||
public class DefaultTreeBuildFactory<T extends AbstractTreeNode> implements AbstractTreeBuildFactory<T> {
|
||||
|
||||
/**
|
||||
* 顶级节点的父节点id(默认-1)
|
||||
*/
|
||||
private String rootParentId = "-1";
|
||||
|
||||
public DefaultTreeBuildFactory() {
|
||||
|
||||
}
|
||||
|
||||
public DefaultTreeBuildFactory(String rootParentId) {
|
||||
this.rootParentId = rootParentId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<T> doTreeBuild(List<T> nodes) {
|
||||
|
||||
// 将每个节点的构造一个子树
|
||||
for (T treeNode : nodes) {
|
||||
this.buildChildNodes(nodes, treeNode, new ArrayList<>());
|
||||
}
|
||||
|
||||
// 只保留上级是根节点的节点,也就是只留下所有一级节点
|
||||
ArrayList<T> results = new ArrayList<>();
|
||||
for (T node : nodes) {
|
||||
if (node.getNodeParentId().equals(rootParentId)) {
|
||||
results.add(node);
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询子节点的集合
|
||||
*
|
||||
* @param totalNodes 所有节点的集合
|
||||
* @param node 被查询节点的id
|
||||
* @param childNodeLists 被查询节点的子节点集合
|
||||
*/
|
||||
private void buildChildNodes(List<T> totalNodes, T node, List<T> childNodeLists) {
|
||||
if (totalNodes == null || node == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<T> nodeSubLists = getSubChildsLevelOne(totalNodes, node);
|
||||
|
||||
if (nodeSubLists.size() == 0) {
|
||||
|
||||
} else {
|
||||
for (T nodeSubList : nodeSubLists) {
|
||||
buildChildNodes(totalNodes, nodeSubList, new ArrayList<>());
|
||||
}
|
||||
}
|
||||
|
||||
childNodeLists.addAll(nodeSubLists);
|
||||
node.setChildrenNodes(childNodeLists);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取子一级节点的集合
|
||||
*
|
||||
* @param list 所有节点的集合
|
||||
* @param node 被查询节点的model
|
||||
* @author fengshuonan
|
||||
*/
|
||||
private List<T> getSubChildsLevelOne(List<T> list, T node) {
|
||||
List<T> nodeList = new ArrayList<>();
|
||||
for (T nodeItem : list) {
|
||||
if (nodeItem.getNodeParentId().equals(node.getNodeId())) {
|
||||
nodeList.add(nodeItem);
|
||||
}
|
||||
}
|
||||
return nodeList;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package cn.stylefeng.roses.kernel.rule.factory;
|
||||
|
||||
import cn.stylefeng.roses.kernel.rule.pojo.tree.DefaultTreeNode;
|
||||
|
||||
import static cn.stylefeng.roses.kernel.rule.constants.TreeConstants.ROOT_TREE_NODE_ID;
|
||||
import static cn.stylefeng.roses.kernel.rule.constants.TreeConstants.ROOT_TREE_NODE_NAME;
|
||||
|
||||
/**
|
||||
* 创建树节点的工厂类
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/15 15:51
|
||||
*/
|
||||
public class TreeNodeFactory {
|
||||
|
||||
/**
|
||||
* 创建一个根节点
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/15 15:52
|
||||
*/
|
||||
public static DefaultTreeNode createRootNode() {
|
||||
DefaultTreeNode root = new DefaultTreeNode();
|
||||
root.setChecked(false);
|
||||
root.setId(ROOT_TREE_NODE_ID);
|
||||
root.setName(ROOT_TREE_NODE_NAME);
|
||||
root.setOpen(true);
|
||||
root.setPId(null);
|
||||
return root;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package cn.stylefeng.roses.kernel.rule.factory.base;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 树构建的抽象类,定义构建tree的基本步骤
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2018/7/25 下午5:59
|
||||
*/
|
||||
public interface AbstractTreeBuildFactory<T> {
|
||||
|
||||
/**
|
||||
* 树节点构建整体过程
|
||||
*
|
||||
* @param nodes 被处理的节点集合
|
||||
* @return 被处理后的节点集合(带树形结构了)
|
||||
* @author fengshuonan
|
||||
* @date 2018/7/26 上午9:45
|
||||
*/
|
||||
List<T> doTreeBuild(List<T> nodes);
|
||||
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package cn.stylefeng.roses.kernel.rule.pojo.dict;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 只包含id,name,code三个字段的pojo
|
||||
* <p>
|
||||
* 一般用在获取某个业务的下拉列表的返回bean
|
||||
* <p>
|
||||
* 例如,返回用户下拉列表,只需返回用户id和姓名即可
|
||||
* <p>
|
||||
* 例如,返回角色下拉列表,只需返回角色id和角色名称
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/11/21 16:53
|
||||
*/
|
||||
@Data
|
||||
public class SimpleDict {
|
||||
|
||||
/**
|
||||
* id
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 名称
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 编码
|
||||
*/
|
||||
private String code;
|
||||
|
||||
}
|
|
@ -0,0 +1,132 @@
|
|||
package cn.stylefeng.roses.kernel.rule.pojo.request;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 请求基类,所有接口请求可继承此类
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/14 18:12
|
||||
*/
|
||||
@Data
|
||||
public class BaseRequest implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 开始时间
|
||||
*/
|
||||
private String searchBeginTime;
|
||||
|
||||
/**
|
||||
* 结束时间
|
||||
*/
|
||||
private String searchEndTime;
|
||||
|
||||
/**
|
||||
* 分页:每页大小(默认20)
|
||||
*/
|
||||
private Integer pageSize;
|
||||
|
||||
/**
|
||||
* 分页:第几页(从1开始)
|
||||
*/
|
||||
private Integer pageNo;
|
||||
|
||||
/**
|
||||
* 排序字段
|
||||
*/
|
||||
private String orderBy;
|
||||
|
||||
/**
|
||||
* 正序或者倒序排列(asc 或 desc)
|
||||
*/
|
||||
private String sortBy;
|
||||
|
||||
/**
|
||||
* 其他参数(如有需要)
|
||||
*/
|
||||
private Map<String, Object> otherParams;
|
||||
|
||||
/**
|
||||
* 参数校验分组:分页
|
||||
*/
|
||||
public @interface page {
|
||||
}
|
||||
|
||||
/**
|
||||
* 参数校验分组:查询所有
|
||||
*/
|
||||
public @interface list {
|
||||
}
|
||||
|
||||
/**
|
||||
* 参数校验分组:增加
|
||||
*/
|
||||
public @interface add {
|
||||
}
|
||||
|
||||
/**
|
||||
* 参数校验分组:编辑
|
||||
*/
|
||||
public @interface edit {
|
||||
}
|
||||
|
||||
/**
|
||||
* 参数校验分组:删除
|
||||
*/
|
||||
public @interface delete {
|
||||
}
|
||||
|
||||
/**
|
||||
* 参数校验分组:详情
|
||||
*/
|
||||
public @interface detail {
|
||||
}
|
||||
|
||||
/**
|
||||
* 参数校验分组:导出
|
||||
*/
|
||||
public @interface export {
|
||||
}
|
||||
|
||||
/**
|
||||
* 参数校验分组:修改状态
|
||||
*/
|
||||
public @interface updateStatus {
|
||||
}
|
||||
|
||||
/**
|
||||
* 预留组1,用来给特殊业务的参数校验用
|
||||
*/
|
||||
public @interface groupOne {
|
||||
}
|
||||
|
||||
/**
|
||||
* 预留组2,用来给特殊业务的参数校验用
|
||||
*/
|
||||
public @interface groupTwo {
|
||||
}
|
||||
|
||||
/**
|
||||
* 预留组3,用来给特殊业务的参数校验用
|
||||
*/
|
||||
public @interface groupThree {
|
||||
}
|
||||
|
||||
/**
|
||||
* 预留组4,用来给特殊业务的参数校验用
|
||||
*/
|
||||
public @interface groupFour {
|
||||
}
|
||||
|
||||
/**
|
||||
* 预留组5,用来给特殊业务的参数校验用
|
||||
*/
|
||||
public @interface groupFive {
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package cn.stylefeng.roses.kernel.rule.pojo.response;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 请求失败的结果包装类
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/16 16:26
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class ErrorResponseData extends ResponseData {
|
||||
|
||||
/**
|
||||
* 异常的具体类名称
|
||||
*/
|
||||
private String exceptionClazz;
|
||||
|
||||
public ErrorResponseData(String code, String message) {
|
||||
super(Boolean.FALSE, code, message, null);
|
||||
}
|
||||
|
||||
ErrorResponseData(String code, String message, Object object) {
|
||||
super(Boolean.FALSE, code, message, object);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
package cn.stylefeng.roses.kernel.rule.pojo.response;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* http响应结果封装
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/17 17:33
|
||||
*/
|
||||
@Data
|
||||
public class ResponseData {
|
||||
|
||||
/**
|
||||
* 请求是否成功
|
||||
*/
|
||||
private Boolean success;
|
||||
|
||||
/**
|
||||
* 响应状态码
|
||||
*/
|
||||
private String code;
|
||||
|
||||
/**
|
||||
* 响应信息
|
||||
*/
|
||||
private String message;
|
||||
|
||||
/**
|
||||
* 响应对象
|
||||
*/
|
||||
private Object data;
|
||||
|
||||
public ResponseData() {
|
||||
}
|
||||
|
||||
public ResponseData(Boolean success, String code, String message, Object data) {
|
||||
this.success = success;
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package cn.stylefeng.roses.kernel.rule.pojo.response;
|
||||
|
||||
import cn.stylefeng.roses.kernel.rule.constants.RuleConstants;
|
||||
|
||||
/**
|
||||
* 响应成功的封装类
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/16 16:23
|
||||
*/
|
||||
public class SuccessResponseData extends ResponseData {
|
||||
|
||||
public SuccessResponseData() {
|
||||
super(Boolean.TRUE, RuleConstants.SUCCESS_CODE, RuleConstants.SUCCESS_MESSAGE, null);
|
||||
}
|
||||
|
||||
public SuccessResponseData(Object object) {
|
||||
super(Boolean.TRUE, RuleConstants.SUCCESS_CODE, RuleConstants.SUCCESS_MESSAGE, object);
|
||||
}
|
||||
|
||||
public SuccessResponseData(String code, String message, Object object) {
|
||||
super(Boolean.TRUE, code, message, object);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
package cn.stylefeng.roses.kernel.rule.pojo.tree;
|
||||
|
||||
import cn.stylefeng.roses.kernel.rule.abstracts.AbstractTreeNode;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 默认树节点的封装
|
||||
* <p>
|
||||
* 默认的根节点id是-1,名称是根节点
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/15 14:39
|
||||
*/
|
||||
@Data
|
||||
public class DefaultTreeNode implements AbstractTreeNode {
|
||||
|
||||
/**
|
||||
* 节点id
|
||||
*/
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 父节点id
|
||||
*/
|
||||
private String pId;
|
||||
|
||||
/**
|
||||
* 节点名称
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 是否打开节点
|
||||
*/
|
||||
private Boolean open;
|
||||
|
||||
/**
|
||||
* 是否被选中
|
||||
*/
|
||||
private Boolean checked = false;
|
||||
|
||||
/**
|
||||
* 排序,越小越靠前
|
||||
*/
|
||||
private BigDecimal sort;
|
||||
|
||||
/**
|
||||
* 子节点
|
||||
*/
|
||||
private List<DefaultTreeNode> children;
|
||||
|
||||
@Override
|
||||
public String getNodeId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNodeParentId() {
|
||||
return pId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChildrenNodes(List childrenNodes) {
|
||||
this.children = childrenNodes;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
package cn.stylefeng.roses.kernel.rule.util;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.aop.framework.AdvisedSupport;
|
||||
import org.springframework.aop.framework.AopProxy;
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
/**
|
||||
* 获取代理原始对象的工具
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/19 16:20
|
||||
*/
|
||||
@Slf4j
|
||||
public class AopTargetUtils {
|
||||
|
||||
/**
|
||||
* 获取代理对象的原始对象
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/19 16:21
|
||||
*/
|
||||
public static Object getTarget(Object proxy) {
|
||||
|
||||
// 不是代理对象,直接返回参数对象
|
||||
if (!AopUtils.isAopProxy(proxy)) {
|
||||
return proxy;
|
||||
}
|
||||
|
||||
// 判断是否是jdk还是cglib代理的对象
|
||||
try {
|
||||
if (AopUtils.isJdkDynamicProxy(proxy)) {
|
||||
return getJdkDynamicProxyTargetObject(proxy);
|
||||
} else {
|
||||
return getCglibProxyTargetObject(proxy);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("获取代理对象异常", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取cglib代理的对象
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/19 16:21
|
||||
*/
|
||||
private static Object getCglibProxyTargetObject(Object proxy) throws Exception {
|
||||
Field h = proxy.getClass().getDeclaredField("CGLIB$CALLBACK_0");
|
||||
h.setAccessible(true);
|
||||
Object dynamicAdvisedInterceptor = h.get(proxy);
|
||||
Field advised = dynamicAdvisedInterceptor.getClass().getDeclaredField("advised");
|
||||
advised.setAccessible(true);
|
||||
return ((AdvisedSupport) advised.get(dynamicAdvisedInterceptor)).getTargetSource().getTarget();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取jdk代理的对象
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/19 16:22
|
||||
*/
|
||||
private static Object getJdkDynamicProxyTargetObject(Object proxy) throws Exception {
|
||||
Field h = proxy.getClass().getSuperclass().getDeclaredField("h");
|
||||
h.setAccessible(true);
|
||||
AopProxy aopProxy = (AopProxy) h.get(proxy);
|
||||
Field advised = aopProxy.getClass().getDeclaredField("advised");
|
||||
advised.setAccessible(true);
|
||||
return ((AdvisedSupport) advised.get(aopProxy)).getTargetSource().getTarget();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,162 @@
|
|||
package cn.stylefeng.roses.kernel.rule.util;
|
||||
|
||||
import cn.hutool.core.net.NetUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.extra.servlet.ServletUtil;
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import cn.hutool.http.useragent.UserAgent;
|
||||
import cn.hutool.http.useragent.UserAgentUtil;
|
||||
import cn.stylefeng.roses.kernel.rule.exception.enums.http.ServletExceptionEnum;
|
||||
import com.alibaba.fastjson.JSONPath;
|
||||
import cn.stylefeng.roses.kernel.rule.exception.base.ServiceException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 保存Http请求的上下文,在任何地方快速获取HttpServletRequest和HttpServletResponse
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/15 17:38
|
||||
*/
|
||||
@Slf4j
|
||||
public class HttpServletUtil {
|
||||
|
||||
/**
|
||||
* 本机ip地址
|
||||
*/
|
||||
private static final String LOCAL_IP = "127.0.0.1";
|
||||
|
||||
/**
|
||||
* 本机ip地址的ipv6地址
|
||||
*/
|
||||
private static final String LOCAL_REMOTE_HOST = "0:0:0:0:0:0:0:1";
|
||||
|
||||
/**
|
||||
* 获取用户浏览器信息的http请求header
|
||||
*/
|
||||
private static final String USER_AGENT_HTTP_HEADER = "User-Agent";
|
||||
|
||||
/**
|
||||
* 获取当前请求的request对象
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/15 17:48
|
||||
*/
|
||||
public static HttpServletRequest getRequest() {
|
||||
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
||||
if (requestAttributes == null) {
|
||||
throw new ServiceException(ServletExceptionEnum.HTTP_CONTEXT_ERROR);
|
||||
} else {
|
||||
return requestAttributes.getRequest();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前请求的response对象
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/15 17:48
|
||||
*/
|
||||
public static HttpServletResponse getResponse() {
|
||||
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
||||
if (requestAttributes == null) {
|
||||
throw new ServiceException(ServletExceptionEnum.HTTP_CONTEXT_ERROR);
|
||||
} else {
|
||||
return requestAttributes.getResponse();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取客户端ip
|
||||
* <p>
|
||||
* 如果获取不到或者获取到的是ipv6地址,都返回127.0.0.1
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/26 14:09
|
||||
*/
|
||||
public static String getRequestClientIp(HttpServletRequest request) {
|
||||
if (ObjectUtil.isEmpty(request)) {
|
||||
return LOCAL_IP;
|
||||
} else {
|
||||
String remoteHost = ServletUtil.getClientIP(request);
|
||||
return LOCAL_REMOTE_HOST.equals(remoteHost) ? LOCAL_IP : remoteHost;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据http请求的client ip定位城市等信息
|
||||
*
|
||||
* @param request http请求封装
|
||||
* @param ipGeoApi 阿里云ip定位api接口
|
||||
* @param ipGeoAppCode 阿里云ip定位appCode
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/26 14:10
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static String calcClientIpAddress(HttpServletRequest request, String ipGeoApi, String ipGeoAppCode) {
|
||||
|
||||
// 如果获取不到,返回 "-"
|
||||
String resultJson = "-";
|
||||
|
||||
// 请求阿里云定位接口需要传的header的名称
|
||||
String requestApiHeader = "Authorization";
|
||||
|
||||
// 获取客户端的ip地址
|
||||
String ip = getRequestClientIp(request);
|
||||
|
||||
// 如果是本地ip或局域网ip,则直接不查询
|
||||
if (ObjectUtil.isEmpty(ip) || NetUtil.isInnerIP(ip)) {
|
||||
return resultJson;
|
||||
}
|
||||
|
||||
// 判断定位api和appCode是否为空
|
||||
if (ObjectUtil.hasEmpty(ipGeoApi, ipGeoAppCode)) {
|
||||
return resultJson;
|
||||
}
|
||||
|
||||
try {
|
||||
if (ObjectUtil.isAllNotEmpty(ipGeoApi, ipGeoAppCode)) {
|
||||
String jsonPath = "$['data']['country','region','city','isp']";
|
||||
String appCodeSymbol = "APPCODE";
|
||||
HttpRequest http = HttpUtil.createGet(String.format(ipGeoApi, ip));
|
||||
http.header(requestApiHeader, appCodeSymbol + " " + ipGeoAppCode);
|
||||
resultJson = http.timeout(3000).execute().body();
|
||||
resultJson = String.join("", (List<String>) JSONPath.read(resultJson, jsonPath));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error(">>> 根据ip定位异常,具体信息为:{}", e.getMessage());
|
||||
}
|
||||
return resultJson;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据http请求获取UserAgent信息
|
||||
* <p>
|
||||
* UserAgent信息包含浏览器的版本,客户端操作系统等信息
|
||||
* <p>
|
||||
* 没有相关header被解析,则返回null
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/28 9:14
|
||||
*/
|
||||
public static UserAgent getUserAgent(HttpServletRequest request) {
|
||||
|
||||
// 获取http header的内容
|
||||
String userAgentStr = ServletUtil.getHeaderIgnoreCase(request, USER_AGENT_HTTP_HEADER);
|
||||
|
||||
// 如果http header内容不为空,则解析这个字符串获取UserAgent对象
|
||||
if (ObjectUtil.isNotEmpty(userAgentStr)) {
|
||||
return UserAgentUtil.parse(userAgentStr);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
# 权限模块
|
||||
|
||||
## 系统需要认证的情况下使用
|
||||
|
||||
## auth包含认证和鉴权
|
||||
|
||||
认证和鉴权的区别:
|
||||
|
||||
认证校验你能否登录系统,认证的过程是校验token的过程
|
||||
|
||||
鉴权校验你有系统的哪些权限,鉴权的过程是校验角色是否包含某些接口的权限
|
|
@ -0,0 +1 @@
|
|||
权限模块api
|
|
@ -0,0 +1,52 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>cn.stylefeng.roses</groupId>
|
||||
<artifactId>kernel-d-auth</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>auth-api</artifactId>
|
||||
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!--config模块的api-->
|
||||
<!--权限相关的配置要放到容器里-->
|
||||
<dependency>
|
||||
<groupId>cn.stylefeng.roses</groupId>
|
||||
<artifactId>config-api</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>${project.artifactId}</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<version>3.0.1</version>
|
||||
<configuration>
|
||||
<attach>true</attach>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>compile</phase>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,81 @@
|
|||
package cn.stylefeng.roses.kernel.auth.api;
|
||||
|
||||
import cn.stylefeng.roses.kernel.auth.api.exception.AuthException;
|
||||
import cn.stylefeng.roses.kernel.auth.api.pojo.auth.LoginRequest;
|
||||
import cn.stylefeng.roses.kernel.auth.api.pojo.auth.LoginResponse;
|
||||
|
||||
/**
|
||||
* 认证服务的接口,包括基本的登录退出操作和校验token等操作
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/26 14:41
|
||||
*/
|
||||
public interface AuthServiceApi {
|
||||
|
||||
/**
|
||||
* 常规登录操作
|
||||
*
|
||||
* @param loginRequest 登录的请求
|
||||
* @return token 一般为jwt token
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/26 14:41
|
||||
*/
|
||||
LoginResponse login(LoginRequest loginRequest);
|
||||
|
||||
/**
|
||||
* 登录(直接用账号登录),一般用在第三方登录
|
||||
*
|
||||
* @param username 账号
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/26 14:40
|
||||
*/
|
||||
LoginResponse loginWithUserName(String username);
|
||||
|
||||
/**
|
||||
* 当前登录人退出登录
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/19 14:16
|
||||
*/
|
||||
void logout();
|
||||
|
||||
/**
|
||||
* 移除某个token,也就是退出某个用户
|
||||
*
|
||||
* @param token 某个用户的登录token
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/19 14:16
|
||||
*/
|
||||
void logoutWithToken(String token);
|
||||
|
||||
/**
|
||||
* 校验token
|
||||
*
|
||||
* @param token 某个用户的登录token
|
||||
* @throws AuthException 认证异常,如果token错误或过期,会有相关的异常抛出
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/19 14:16
|
||||
*/
|
||||
void validateToken(String token) throws AuthException;
|
||||
|
||||
/**
|
||||
* 获取token是否正确
|
||||
*
|
||||
* @param token 某个用户的登录token
|
||||
* @return true-token正确,false-token错误
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/19 14:16
|
||||
*/
|
||||
boolean getTokenFlag(String token);
|
||||
|
||||
/**
|
||||
* 校验用户访问的url是否认证通过
|
||||
*
|
||||
* @param token 用户登陆的token
|
||||
* @param requestUrl 被校验的url
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/22 16:03
|
||||
*/
|
||||
void checkAuth(String token, String requestUrl);
|
||||
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
package cn.stylefeng.roses.kernel.auth.api;
|
||||
|
||||
import cn.stylefeng.roses.kernel.auth.api.exception.AuthException;
|
||||
import cn.stylefeng.roses.kernel.auth.api.pojo.login.LoginUser;
|
||||
|
||||
/**
|
||||
* 当前登陆用户相关的一系列方法
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/17 10:27
|
||||
*/
|
||||
public interface LoginUserApi {
|
||||
|
||||
/**
|
||||
* 获取当前登陆用户的token
|
||||
* <p>
|
||||
* 如果获取不到,返回null
|
||||
*
|
||||
* @return 当前用户的token或null
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/17 11:05
|
||||
*/
|
||||
String getToken();
|
||||
|
||||
/**
|
||||
* 获取当前登陆用户
|
||||
* <p>
|
||||
* 如果获取不到当前登陆用户会抛出 AuthException
|
||||
*
|
||||
* @return 当前登陆用户信息
|
||||
* @throws AuthException 权限异常
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/17 10:27
|
||||
*/
|
||||
LoginUser getLoginUser() throws AuthException;
|
||||
|
||||
/**
|
||||
* 获取当前登陆用户
|
||||
* <p>
|
||||
* 如果获取不到当前登陆用户返回null
|
||||
*
|
||||
* @return 当前登录用户信息
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/17 11:00
|
||||
*/
|
||||
LoginUser getLoginUserNullable();
|
||||
|
||||
/**
|
||||
* 获取是否是超级管理员的标识
|
||||
*
|
||||
* @return true-是超级管理员,false-不是超级管理员
|
||||
* @author fengshuonan
|
||||
* @date 2020/11/4 15:45
|
||||
*/
|
||||
boolean getSuperAdminFlag();
|
||||
|
||||
/**
|
||||
* 判断当前用户是否登录
|
||||
*
|
||||
* @return 是否登录,true是,false否
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/17 11:02
|
||||
*/
|
||||
boolean hasLogin();
|
||||
|
||||
/**
|
||||
* 判断当前登录用户是否有某资源的访问权限
|
||||
*
|
||||
* @param requestUri 请求的url,例如: /userInfo/list
|
||||
* @return 是否有访问权限,true是,false否
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/17 11:03
|
||||
*/
|
||||
boolean hasPermission(String requestUri);
|
||||
|
||||
/**
|
||||
* 判断当前登录用户是否包含某个角色
|
||||
*
|
||||
* @param roleCode 角色编码
|
||||
* @return 是否包含该角色,true是,false否
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/17 11:04
|
||||
*/
|
||||
boolean hasRole(String roleCode);
|
||||
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package cn.stylefeng.roses.kernel.auth.api;
|
||||
|
||||
import cn.stylefeng.roses.kernel.auth.api.exception.AuthException;
|
||||
|
||||
/**
|
||||
* 权限相关的服务接口
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/19 14:24
|
||||
*/
|
||||
public interface PermissionServiceApi {
|
||||
|
||||
/**
|
||||
* 校验当前用户是否有某个接口的权限
|
||||
* <p>
|
||||
* 只要认证不通过,则会抛出异常
|
||||
*
|
||||
* @param token 用户登陆的token
|
||||
* @param requestUrl 被校验的url
|
||||
* @throws AuthException 认证失败的异常信息
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/19 14:50
|
||||
*/
|
||||
void checkPermission(String token, String requestUrl) throws AuthException;
|
||||
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
package cn.stylefeng.roses.kernel.auth.api;
|
||||
|
||||
import cn.stylefeng.roses.kernel.auth.api.pojo.login.LoginUser;
|
||||
|
||||
/**
|
||||
* 用户会话管理
|
||||
* <p>
|
||||
* 会话指的是用户登录后和服务器一直保持一个交互状态的维护
|
||||
* <p>
|
||||
* 会话具有时效性,反之,当用户不再访问系统的时候,会话应该自动失效
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/19 16:47
|
||||
*/
|
||||
public interface SessionManagerApi {
|
||||
|
||||
/**
|
||||
* 创建会话
|
||||
*
|
||||
* @param token 用户登录的token
|
||||
* @param loginUser 登录的用户
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/19 16:47
|
||||
*/
|
||||
void createSession(String token, LoginUser loginUser);
|
||||
|
||||
/**
|
||||
* 通过token获取会话
|
||||
*
|
||||
* @param token 用户token
|
||||
* @return token对应用户的详细信息
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/19 16:48
|
||||
*/
|
||||
LoginUser getSession(String token);
|
||||
|
||||
/**
|
||||
* 根据token删除一个会话
|
||||
*
|
||||
* @param token 用户token
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/19 16:48
|
||||
*/
|
||||
void removeSession(String token);
|
||||
|
||||
/**
|
||||
* 删除用户所有的会话,但除了参数传的token的会话
|
||||
* <p>
|
||||
* 用在单端登录中,一个账号只能在一个浏览器登录
|
||||
*
|
||||
* @param token 用户token
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/21 16:18
|
||||
*/
|
||||
void removeSessionExcludeToken(String token);
|
||||
|
||||
/**
|
||||
* 判断一个token是否还存在会话状态
|
||||
*
|
||||
* @param token 用户token
|
||||
* @return true-存在会话,false-不存在会话或者失效了
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/19 16:49
|
||||
*/
|
||||
boolean haveSession(String token);
|
||||
|
||||
/**
|
||||
* 刷新会话的过期时间,刷新后用户当前的过期时间将重置
|
||||
*
|
||||
* @param token 用户的token
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/19 16:50
|
||||
*/
|
||||
void refreshSession(String token);
|
||||
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package cn.stylefeng.roses.kernel.auth.api.constants;
|
||||
|
||||
/**
|
||||
* auth,鉴权模块的常量
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/16 11:05
|
||||
*/
|
||||
public interface AuthConstants {
|
||||
|
||||
/**
|
||||
* auth模块的名称
|
||||
*/
|
||||
String AUTH_MODULE_NAME = "kernel-d-auth";
|
||||
|
||||
/**
|
||||
* 异常枚举的步进值
|
||||
*/
|
||||
String AUTH_EXCEPTION_STEP_CODE = "03";
|
||||
|
||||
/**
|
||||
* 登录用户的缓存前缀
|
||||
*/
|
||||
String LOGGED_TOKEN_PREFIX = "LOGGED_TOKEN_";
|
||||
|
||||
/**
|
||||
* 登录用户id的缓存前缀
|
||||
*/
|
||||
String LOGGED_USERID_PREFIX = "LOGGED_USERID_";
|
||||
|
||||
/**
|
||||
* 默认http请求携带token的header名称
|
||||
*/
|
||||
String DEFAULT_AUTH_HEADER_NAME = "Authorization";
|
||||
|
||||
/**
|
||||
* 获取http请求携带token的param的名称
|
||||
*/
|
||||
String DEFAULT_AUTH_PARAM_NAME = "token";
|
||||
|
||||
/**
|
||||
* 默认密码
|
||||
*/
|
||||
String DEFAULT_PASSWORD = "123456";
|
||||
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package cn.stylefeng.roses.kernel.auth.api.context;
|
||||
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import cn.stylefeng.roses.kernel.auth.api.LoginUserApi;
|
||||
|
||||
/**
|
||||
* 快速获取当前登陆用户的一系列操作方法,具体实现在Spring容器中查找
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/17 10:30
|
||||
*/
|
||||
public class LoginContext {
|
||||
|
||||
public static LoginUserApi me() {
|
||||
return SpringUtil.getBean(LoginUserApi.class);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
package cn.stylefeng.roses.kernel.auth.api.enums;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.stylefeng.roses.kernel.auth.api.exception.AuthException;
|
||||
import lombok.Getter;
|
||||
|
||||
import static cn.stylefeng.roses.kernel.auth.api.exception.enums.AuthExceptionEnum.DATA_SCOPE_ERROR;
|
||||
|
||||
/**
|
||||
* 数据范围类型枚举,数据范围的值越小,数据权限越小
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/11/5 15:22
|
||||
*/
|
||||
@Getter
|
||||
public enum DataScopeTypeEnum {
|
||||
|
||||
/**
|
||||
* 仅本人数据
|
||||
*/
|
||||
SELF(10, "仅本人数据"),
|
||||
|
||||
/**
|
||||
* 本部门数据
|
||||
*/
|
||||
DEPT(20, "本部门数据"),
|
||||
|
||||
/**
|
||||
* 本部门及以下数据
|
||||
*/
|
||||
DEPT_WITH_CHILD(30, "本部门及以下数据"),
|
||||
|
||||
/**
|
||||
* 指定部门数据
|
||||
*/
|
||||
DEFINE(40, "指定部门数据"),
|
||||
|
||||
/**
|
||||
* 全部数据
|
||||
*/
|
||||
ALL(50, "全部数据");
|
||||
|
||||
private final Integer code;
|
||||
|
||||
private final String message;
|
||||
|
||||
DataScopeTypeEnum(Integer code, String message) {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据code获取枚举
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/29 18:59
|
||||
*/
|
||||
public static DataScopeTypeEnum codeToEnum(Integer code) {
|
||||
if (null != code) {
|
||||
for (DataScopeTypeEnum dataScopeTypeEnum : DataScopeTypeEnum.values()) {
|
||||
if (dataScopeTypeEnum.getCode().equals(code)) {
|
||||
return dataScopeTypeEnum;
|
||||
}
|
||||
}
|
||||
}
|
||||
String userTip = StrUtil.format(DATA_SCOPE_ERROR.getUserTip(), code);
|
||||
throw new AuthException(DATA_SCOPE_ERROR, userTip);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package cn.stylefeng.roses.kernel.auth.api.exception;
|
||||
|
||||
import cn.stylefeng.roses.kernel.auth.api.constants.AuthConstants;
|
||||
import cn.stylefeng.roses.kernel.rule.abstracts.AbstractExceptionEnum;
|
||||
import cn.stylefeng.roses.kernel.rule.exception.base.ServiceException;
|
||||
|
||||
/**
|
||||
* 认证类异常
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/15 15:59
|
||||
*/
|
||||
public class AuthException extends ServiceException {
|
||||
|
||||
public AuthException(String errorCode, String userTip) {
|
||||
super(AuthConstants.AUTH_MODULE_NAME, errorCode, userTip);
|
||||
}
|
||||
|
||||
public AuthException(AbstractExceptionEnum exception, String userTip) {
|
||||
super(AuthConstants.AUTH_MODULE_NAME, exception.getErrorCode(), userTip);
|
||||
}
|
||||
|
||||
public AuthException(AbstractExceptionEnum exception) {
|
||||
super(AuthConstants.AUTH_MODULE_NAME, exception);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
package cn.stylefeng.roses.kernel.auth.api.exception.enums;
|
||||
|
||||
import cn.stylefeng.roses.kernel.auth.api.constants.AuthConstants;
|
||||
import cn.stylefeng.roses.kernel.rule.abstracts.AbstractExceptionEnum;
|
||||
import cn.stylefeng.roses.kernel.rule.constants.RuleConstants;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 认证相关异常
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/16 10:53
|
||||
*/
|
||||
@Getter
|
||||
public enum AuthExceptionEnum implements AbstractExceptionEnum {
|
||||
|
||||
/**
|
||||
* 认证异常
|
||||
*/
|
||||
AUTH_ERROR(RuleConstants.BUSINESS_ERROR_TYPE_CODE + AuthConstants.AUTH_EXCEPTION_STEP_CODE + "01", "认证失败,请检查您的操作是否正确"),
|
||||
|
||||
/**
|
||||
* 登陆时,账号或密码为空
|
||||
*/
|
||||
PARAM_EMPTY(RuleConstants.USER_OPERATION_ERROR_TYPE_CODE + AuthConstants.AUTH_EXCEPTION_STEP_CODE + "02", "登陆失败,账号或密码参数为空"),
|
||||
|
||||
/**
|
||||
* 账号或密码错误
|
||||
*/
|
||||
USERNAME_PASSWORD_ERROR(RuleConstants.USER_OPERATION_ERROR_TYPE_CODE + AuthConstants.AUTH_EXCEPTION_STEP_CODE + "03", "账号或密码错误"),
|
||||
|
||||
/**
|
||||
* 用户状态异常,可能被禁用可能被冻结,用StrUtil.format()格式化
|
||||
*/
|
||||
USER_STATUS_ERROR(RuleConstants.BUSINESS_ERROR_TYPE_CODE + AuthConstants.AUTH_EXCEPTION_STEP_CODE + "04", "当前用户被{},请检查用户状态是否正常"),
|
||||
|
||||
/**
|
||||
* 登陆失败,账号参数为空
|
||||
*/
|
||||
ACCOUNT_IS_BLANK(RuleConstants.USER_OPERATION_ERROR_TYPE_CODE + AuthConstants.AUTH_EXCEPTION_STEP_CODE + "05", "登陆失败,账号参数为空"),
|
||||
|
||||
/**
|
||||
* 获取token失败
|
||||
*/
|
||||
TOKEN_GET_ERROR(RuleConstants.USER_OPERATION_ERROR_TYPE_CODE + AuthConstants.AUTH_EXCEPTION_STEP_CODE + "06", "获取token失败,请检查header和param中是否传递了用户token"),
|
||||
|
||||
/**
|
||||
* 获取资源为空
|
||||
*/
|
||||
RESOURCE_DEFINITION_ERROR(RuleConstants.BUSINESS_ERROR_TYPE_CODE + AuthConstants.AUTH_EXCEPTION_STEP_CODE + "07", "获取资源为空,请检查当前请求url是否存在对应的ResourceDefinition"),
|
||||
|
||||
/**
|
||||
* 获取不到token对应的用户信息,请检查登录是否过期
|
||||
*/
|
||||
TOKEN_ERROR(RuleConstants.BUSINESS_ERROR_TYPE_CODE + AuthConstants.AUTH_EXCEPTION_STEP_CODE + "08", "获取不到token对应的用户信息,请检查登录是否过期"),
|
||||
|
||||
/**
|
||||
* 权限校验失败,请检查用户是否有该资源的权限
|
||||
*/
|
||||
PERMISSION_RES_VALIDATE_ERROR(RuleConstants.BUSINESS_ERROR_TYPE_CODE + AuthConstants.AUTH_EXCEPTION_STEP_CODE + "09", "权限校验失败,请检查用户是否有该资源的权限"),
|
||||
|
||||
/**
|
||||
* 数据范围类型转化异常
|
||||
*/
|
||||
DATA_SCOPE_ERROR(RuleConstants.BUSINESS_ERROR_TYPE_CODE + AuthConstants.AUTH_EXCEPTION_STEP_CODE + "10", "数据范围类型转化异常,数据范围类型为:{}"),
|
||||
|
||||
/**
|
||||
* 权限校验失败,只有超级管理员可以授权所有数据
|
||||
*/
|
||||
ONLY_SUPER_ERROR(RuleConstants.BUSINESS_ERROR_TYPE_CODE + AuthConstants.AUTH_EXCEPTION_STEP_CODE + "11", "权限校验失败,只有超级管理员可以授权所有数据");
|
||||
|
||||
/**
|
||||
* 错误编码
|
||||
*/
|
||||
private final String errorCode;
|
||||
|
||||
/**
|
||||
* 提示用户信息
|
||||
*/
|
||||
private final String userTip;
|
||||
|
||||
AuthExceptionEnum(String errorCode, String userTip) {
|
||||
this.errorCode = errorCode;
|
||||
this.userTip = userTip;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
package cn.stylefeng.roses.kernel.auth.api.expander;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.stylefeng.roses.kernel.config.api.context.ConfigContext;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.stylefeng.roses.kernel.auth.api.constants.AuthConstants.*;
|
||||
|
||||
/**
|
||||
* 权限相关配置快速获取
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/17 16:10
|
||||
*/
|
||||
public class AuthConfigExpander {
|
||||
|
||||
/**
|
||||
* 获取不被权限控制的url
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/17 16:12
|
||||
*/
|
||||
public static List<String> getNoneSecurityConfig() {
|
||||
String noneSecurityUrls = ConfigContext.me().getSysConfigValueWithDefault("SYS_NONE_SECURITY_URLS", String.class, "");
|
||||
if (StrUtil.isEmpty(noneSecurityUrls)) {
|
||||
return new ArrayList<>();
|
||||
} else {
|
||||
return StrUtil.split(noneSecurityUrls, ',');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取session过期时间,默认3600秒
|
||||
* <p>
|
||||
* 在这个时段内不操作,会将用户踢下线,从新登陆
|
||||
* <p>
|
||||
* 关于记住我功能,如果开启了记住我功能,这个参数
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/20 9:32
|
||||
*/
|
||||
public static Long getSessionExpiredSeconds() {
|
||||
return ConfigContext.me().getSysConfigValueWithDefault("SYS_SESSION_EXPIRED_SECONDS", Long.class, 3600L);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取单账号单端登录的开关
|
||||
* <p>
|
||||
* 单账号单端登录为限制一个账号多个浏览器登录
|
||||
*
|
||||
* @return true-开启单端限制,false-关闭单端限制
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/21 14:31
|
||||
*/
|
||||
public static boolean getSingleAccountLoginFlag() {
|
||||
return ConfigContext.me().getSysConfigValueWithDefault("SYS_SINGLE_ACCOUNT_LOGIN_FLAG", Boolean.class, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取携带token的header头的名称
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/22 14:11
|
||||
*/
|
||||
public static String getAuthTokenHeaderName() {
|
||||
return ConfigContext.me().getSysConfigValueWithDefault("SYS_AUTH_HEADER_NAME", String.class, DEFAULT_AUTH_HEADER_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取携带token的header头的名称
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/22 14:11
|
||||
*/
|
||||
public static String getAuthTokenParamName() {
|
||||
return ConfigContext.me().getSysConfigValueWithDefault("SYS_AUTH_PARAM_NAME", String.class, DEFAULT_AUTH_PARAM_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取默认密码
|
||||
*
|
||||
* @author luojie
|
||||
* @date 2020/11/6 10:05
|
||||
*/
|
||||
public static String getDefaultPassWord() {
|
||||
return ConfigContext.me().getSysConfigValueWithDefault("SYS_DEFAULT_PASSWORD", String.class, DEFAULT_PASSWORD);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package cn.stylefeng.roses.kernel.auth.api.pojo.auth;
|
||||
|
||||
import cn.stylefeng.roses.kernel.rule.pojo.request.BaseRequest;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 登录的请求参数
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/19 14:02
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class LoginRequest extends BaseRequest {
|
||||
|
||||
/**
|
||||
* 账号
|
||||
*/
|
||||
private String account;
|
||||
|
||||
/**
|
||||
* 密码
|
||||
*/
|
||||
private String password;
|
||||
|
||||
/**
|
||||
* 记住我
|
||||
*/
|
||||
private Boolean rememberMe;
|
||||
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package cn.stylefeng.roses.kernel.auth.api.pojo.auth;
|
||||
|
||||
import cn.stylefeng.roses.kernel.auth.api.pojo.login.LoginUser;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 登录操作的响应结果
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/19 14:17
|
||||
*/
|
||||
@Data
|
||||
public class LoginResponse {
|
||||
|
||||
/**
|
||||
* 登录人的信息
|
||||
*/
|
||||
private LoginUser loginUser;
|
||||
|
||||
/**
|
||||
* 登录人的token
|
||||
*/
|
||||
private String token;
|
||||
|
||||
public LoginResponse(LoginUser loginUser, String token) {
|
||||
this.loginUser = loginUser;
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
package cn.stylefeng.roses.kernel.auth.api.pojo.login;
|
||||
|
||||
import cn.hutool.core.lang.Dict;
|
||||
import cn.stylefeng.roses.kernel.auth.api.enums.DataScopeTypeEnum;
|
||||
import cn.stylefeng.roses.kernel.rule.pojo.dict.SimpleDict;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 登录用户信息
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/17 9:58
|
||||
*/
|
||||
@Data
|
||||
public class LoginUser implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 用户主键id
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 账号
|
||||
*/
|
||||
private String account;
|
||||
|
||||
/**
|
||||
* 姓名
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 公司/组织id
|
||||
*/
|
||||
private Long organizationId;
|
||||
|
||||
/**
|
||||
* 头像(图片最终访问的url)
|
||||
*/
|
||||
private String avatar;
|
||||
|
||||
/**
|
||||
* 生日
|
||||
*/
|
||||
private Date birthday;
|
||||
|
||||
/**
|
||||
* 性别(具体SexEnum枚举类)(M-男,F-女)
|
||||
*/
|
||||
private String sex;
|
||||
|
||||
/**
|
||||
* 邮箱
|
||||
*/
|
||||
private String email;
|
||||
|
||||
/**
|
||||
* 手机
|
||||
*/
|
||||
private String mobilePhone;
|
||||
|
||||
/**
|
||||
* 固定电话
|
||||
*/
|
||||
private String tel;
|
||||
|
||||
/**
|
||||
* 超级管理员标识,true-是超级管理员
|
||||
*/
|
||||
private Boolean superAdmin;
|
||||
|
||||
/**
|
||||
* 用户数据范围类型的集合
|
||||
*/
|
||||
private Set<DataScopeTypeEnum> dataScopeTypes;
|
||||
|
||||
/**
|
||||
* 用户数据范围(userId的集合)
|
||||
* <p>
|
||||
* 用户能看哪些用户数据的权限
|
||||
*/
|
||||
private Set<Long> userIdDataScope;
|
||||
|
||||
/**
|
||||
* 组织机构数据范围(组织架构id的集合)
|
||||
* <p>
|
||||
* 用户能看哪些组织机构数据的信息
|
||||
*/
|
||||
private Set<Long> organizationIdDataScope;
|
||||
|
||||
/**
|
||||
* 具备应用信息
|
||||
*/
|
||||
private Set<SimpleDict> apps;
|
||||
|
||||
/**
|
||||
* 角色信息
|
||||
*/
|
||||
private Set<SimpleDict> roles;
|
||||
|
||||
/**
|
||||
* 可用资源集合
|
||||
*/
|
||||
private Set<String> resourceUrls;
|
||||
|
||||
/**
|
||||
* 其他信息,Dict为Map的拓展
|
||||
*/
|
||||
private Dict otherInfos;
|
||||
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
认证和鉴权的sdk
|
|
@ -0,0 +1,72 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>cn.stylefeng.roses</groupId>
|
||||
<artifactId>kernel-d-auth</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>auth-sdk</artifactId>
|
||||
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!--auth本模块的api-->
|
||||
<dependency>
|
||||
<groupId>cn.stylefeng.roses</groupId>
|
||||
<artifactId>auth-api</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<!--资源模块的api-->
|
||||
<!--权限校验等功能,需要用到资源模块的接口-->
|
||||
<dependency>
|
||||
<groupId>cn.stylefeng.roses</groupId>
|
||||
<artifactId>scanner-api</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<!--system业务模块的api-->
|
||||
<!--登录和鉴权需要用到用户相关的接口-->
|
||||
<dependency>
|
||||
<groupId>cn.stylefeng.roses</groupId>
|
||||
<artifactId>system-api</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<!--jwt模块的api-->
|
||||
<!--token用的jwt token-->
|
||||
<dependency>
|
||||
<groupId>cn.stylefeng.roses</groupId>
|
||||
<artifactId>jwt-api</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<!--web模块-->
|
||||
<!--web获取token的操作需要从http header中取-->
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!--redis的依赖-->
|
||||
<!--session manager可以用redis,可以用内存的-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,186 @@
|
|||
package cn.stylefeng.roses.kernel.auth.auth;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.crypto.digest.BCrypt;
|
||||
import cn.stylefeng.roses.kernel.auth.api.AuthServiceApi;
|
||||
import cn.stylefeng.roses.kernel.auth.api.SessionManagerApi;
|
||||
import cn.stylefeng.roses.kernel.auth.api.context.LoginContext;
|
||||
import cn.stylefeng.roses.kernel.auth.api.exception.AuthException;
|
||||
import cn.stylefeng.roses.kernel.auth.api.exception.enums.AuthExceptionEnum;
|
||||
import cn.stylefeng.roses.kernel.auth.api.expander.AuthConfigExpander;
|
||||
import cn.stylefeng.roses.kernel.auth.api.pojo.auth.LoginRequest;
|
||||
import cn.stylefeng.roses.kernel.auth.api.pojo.auth.LoginResponse;
|
||||
import cn.stylefeng.roses.kernel.auth.api.pojo.login.LoginUser;
|
||||
import cn.stylefeng.roses.kernel.jwt.api.context.JwtContext;
|
||||
import cn.stylefeng.roses.kernel.jwt.api.exception.JwtException;
|
||||
import cn.stylefeng.roses.kernel.jwt.api.pojo.payload.DefaultJwtPayload;
|
||||
import cn.stylefeng.roses.kernel.resource.api.pojo.resource.ResourceDefinition;
|
||||
import cn.stylefeng.roses.kernel.resource.api.pojo.resource.ResourceUrlParam;
|
||||
import cn.stylefeng.roses.kernel.rule.util.HttpServletUtil;
|
||||
import cn.stylefeng.roses.kernel.system.ResourceServiceApi;
|
||||
import cn.stylefeng.roses.kernel.system.UserServiceApi;
|
||||
import cn.stylefeng.roses.kernel.system.enums.UserStatusEnum;
|
||||
import cn.stylefeng.roses.kernel.system.pojo.user.UserLoginInfoDTO;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Date;
|
||||
|
||||
import static cn.stylefeng.roses.kernel.auth.api.exception.enums.AuthExceptionEnum.RESOURCE_DEFINITION_ERROR;
|
||||
import static cn.stylefeng.roses.kernel.auth.api.exception.enums.AuthExceptionEnum.TOKEN_ERROR;
|
||||
|
||||
/**
|
||||
* 认证服务的实现
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/20 10:25
|
||||
*/
|
||||
@Service
|
||||
public class AuthServiceImpl implements AuthServiceApi {
|
||||
|
||||
/**
|
||||
* 用于操作缓存时候加锁
|
||||
*/
|
||||
private static final Object SESSION_OPERATE_LOCK = new Object();
|
||||
|
||||
@Resource
|
||||
private UserServiceApi userServiceApi;
|
||||
|
||||
@Resource
|
||||
private SessionManagerApi sessionManagerApi;
|
||||
|
||||
@Resource
|
||||
private ResourceServiceApi resourceServiceApi;
|
||||
|
||||
@Override
|
||||
public LoginResponse login(LoginRequest loginRequest) {
|
||||
return loginAction(loginRequest, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoginResponse loginWithUserName(String username) {
|
||||
LoginRequest loginRequest = new LoginRequest();
|
||||
loginRequest.setAccount(username);
|
||||
return loginAction(new LoginRequest(), false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logout() {
|
||||
String token = LoginContext.me().getToken();
|
||||
logoutWithToken(token);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logoutWithToken(String token) {
|
||||
// 清除token缓存的用户信息
|
||||
sessionManagerApi.removeSession(token);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validateToken(String token) throws AuthException {
|
||||
try {
|
||||
JwtContext.me().validateTokenWithException(token);
|
||||
} catch (JwtException e) {
|
||||
throw new AuthException(e.getErrorCode(), e.getUserTip());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getTokenFlag(String token) {
|
||||
return JwtContext.me().validateToken(token);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkAuth(String token, String requestUrl) {
|
||||
|
||||
// 获取url对应的资源信息ResourceDefinition
|
||||
ResourceUrlParam resourceUrlReq = new ResourceUrlParam();
|
||||
resourceUrlReq.setUrl(requestUrl);
|
||||
ResourceDefinition resourceDefinition = resourceServiceApi.getResourceByUrl(resourceUrlReq);
|
||||
|
||||
// 获取token对应的用户信息
|
||||
LoginUser session = sessionManagerApi.getSession(token);
|
||||
if (session == null) {
|
||||
throw new AuthException(TOKEN_ERROR);
|
||||
}
|
||||
|
||||
// 资源为空,直接响应异常,禁止用户访问
|
||||
if (resourceDefinition == null) {
|
||||
throw new AuthException(RESOURCE_DEFINITION_ERROR);
|
||||
}
|
||||
|
||||
// 检查接口是否需要鉴权
|
||||
Boolean requiredLogin = resourceDefinition.getRequiredLogin();
|
||||
|
||||
// 需要鉴权,则判断token是否过期
|
||||
if (requiredLogin) {
|
||||
this.validateToken(token);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录的真正业务逻辑
|
||||
*
|
||||
* @param loginRequest 登录参数
|
||||
* @param validatePassword 是否校验密码,true-校验密码,false-不会校验密码
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/21 16:59
|
||||
*/
|
||||
private LoginResponse loginAction(LoginRequest loginRequest, Boolean validatePassword) {
|
||||
|
||||
// 1.参数为空校验
|
||||
if (validatePassword) {
|
||||
if (loginRequest == null || StrUtil.hasBlank(loginRequest.getAccount(), loginRequest.getPassword())) {
|
||||
throw new AuthException(AuthExceptionEnum.PARAM_EMPTY);
|
||||
}
|
||||
} else {
|
||||
if (loginRequest == null || StrUtil.hasBlank(loginRequest.getAccount())) {
|
||||
throw new AuthException(AuthExceptionEnum.ACCOUNT_IS_BLANK);
|
||||
}
|
||||
}
|
||||
|
||||
// 2.获取用户密码的加密值和用户的状态
|
||||
UserLoginInfoDTO userValidateInfo = userServiceApi.getUserLoginInfo(loginRequest.getAccount());
|
||||
|
||||
// 3.校验用户密码是否正确(BCrypt算法)
|
||||
if (validatePassword) {
|
||||
if (ObjectUtil.isEmpty(userValidateInfo.getUserPasswordHexed()) || !BCrypt.checkpw(loginRequest.getPassword(), userValidateInfo.getUserPasswordHexed())) {
|
||||
throw new AuthException(AuthExceptionEnum.USERNAME_PASSWORD_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
// 4.校验用户是否异常(不是正常状态)
|
||||
if (!UserStatusEnum.ENABLE.getCode().equals(userValidateInfo.getUserStatus())) {
|
||||
String userTip = StrUtil.format(AuthExceptionEnum.USER_STATUS_ERROR.getErrorCode(), UserStatusEnum.getCodeMessage(userValidateInfo.getUserStatus()));
|
||||
throw new AuthException(AuthExceptionEnum.USER_STATUS_ERROR.getErrorCode(), userTip);
|
||||
}
|
||||
|
||||
// 5.获取LoginUser,用于用户的缓存
|
||||
LoginUser loginUser = userValidateInfo.getLoginUser();
|
||||
|
||||
// 6.生成用户的token
|
||||
DefaultJwtPayload defaultJwtPayload = new DefaultJwtPayload(loginUser.getId(), loginUser.getAccount(), loginRequest.getRememberMe());
|
||||
String jwtToken = JwtContext.me().generateTokenDefaultPayload(defaultJwtPayload);
|
||||
|
||||
synchronized (SESSION_OPERATE_LOCK) {
|
||||
|
||||
// 7.缓存用户信息,创建会话
|
||||
sessionManagerApi.createSession(jwtToken, loginUser);
|
||||
|
||||
// 8.如果开启了单账号单端在线,则踢掉已经上线的该用户
|
||||
if (AuthConfigExpander.getSingleAccountLoginFlag()) {
|
||||
sessionManagerApi.removeSessionExcludeToken(jwtToken);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 9.更新用户登录时间和ip
|
||||
String ip = HttpServletUtil.getRequestClientIp(HttpServletUtil.getRequest());
|
||||
userServiceApi.updateUserLoginInfo(loginUser.getId(), new Date(), ip);
|
||||
|
||||
// 10.组装返回结果
|
||||
return new LoginResponse(loginUser, jwtToken);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,142 @@
|
|||
package cn.stylefeng.roses.kernel.auth.auth;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.stylefeng.roses.kernel.auth.api.LoginUserApi;
|
||||
import cn.stylefeng.roses.kernel.auth.api.SessionManagerApi;
|
||||
import cn.stylefeng.roses.kernel.auth.api.exception.AuthException;
|
||||
import cn.stylefeng.roses.kernel.auth.api.expander.AuthConfigExpander;
|
||||
import cn.stylefeng.roses.kernel.auth.api.pojo.login.LoginUser;
|
||||
import cn.stylefeng.roses.kernel.rule.pojo.dict.SimpleDict;
|
||||
import cn.stylefeng.roses.kernel.rule.util.HttpServletUtil;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.Set;
|
||||
|
||||
import static cn.stylefeng.roses.kernel.auth.api.exception.enums.AuthExceptionEnum.TOKEN_GET_ERROR;
|
||||
|
||||
/**
|
||||
* 当前登陆用户的接口实现
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/21 18:09
|
||||
*/
|
||||
@Service
|
||||
public class LoginUserImpl implements LoginUserApi {
|
||||
|
||||
@Resource
|
||||
private SessionManagerApi sessionManagerApi;
|
||||
|
||||
@Override
|
||||
public String getToken() {
|
||||
|
||||
// 获取当前http请求
|
||||
HttpServletRequest request = HttpServletUtil.getRequest();
|
||||
|
||||
// 优先从param参数中获取token
|
||||
String parameterToken = request.getParameter(AuthConfigExpander.getAuthTokenParamName());
|
||||
|
||||
// 不为空则直接返回param的token
|
||||
if (StrUtil.isNotBlank(parameterToken)) {
|
||||
return parameterToken;
|
||||
}
|
||||
|
||||
// 从header中获取token
|
||||
String authToken = request.getHeader(AuthConfigExpander.getAuthTokenHeaderName());
|
||||
if (StrUtil.isNotBlank(authToken)) {
|
||||
return authToken;
|
||||
}
|
||||
|
||||
// 获取不到token,直接告诉用户
|
||||
throw new AuthException(TOKEN_GET_ERROR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoginUser getLoginUser() throws AuthException {
|
||||
|
||||
// 获取用户的token
|
||||
String token = getToken();
|
||||
|
||||
// 获取session中该token对应的用户
|
||||
LoginUser session = sessionManagerApi.getSession(token);
|
||||
|
||||
// session为空抛出异常
|
||||
if (session == null) {
|
||||
throw new AuthException(TOKEN_GET_ERROR);
|
||||
}
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoginUser getLoginUserNullable() {
|
||||
|
||||
// 获取用户的token
|
||||
String token = null;
|
||||
try {
|
||||
token = getToken();
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 获取session中该token对应的用户
|
||||
return sessionManagerApi.getSession(token);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getSuperAdminFlag() {
|
||||
LoginUser loginUser = getLoginUser();
|
||||
return loginUser.getSuperAdmin();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasLogin() {
|
||||
|
||||
// 获取用户的token
|
||||
String token = null;
|
||||
try {
|
||||
token = getToken();
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 获取是否在会话中有
|
||||
return sessionManagerApi.haveSession(token);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(String requestUri) {
|
||||
|
||||
LoginUser loginUser = getLoginUser();
|
||||
|
||||
Set<String> resourceUrls = loginUser.getResourceUrls();
|
||||
|
||||
if (resourceUrls != null && resourceUrls.size() > 0) {
|
||||
return resourceUrls.contains(requestUri);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasRole(String roleCode) {
|
||||
|
||||
LoginUser loginUser = getLoginUser();
|
||||
|
||||
Set<SimpleDict> roles = loginUser.getRoles();
|
||||
|
||||
if (roles != null && roles.size() > 0) {
|
||||
for (SimpleDict role : roles) {
|
||||
if (role.getCode().equals(roleCode)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
package cn.stylefeng.roses.kernel.auth.permission;
|
||||
|
||||
import cn.stylefeng.roses.kernel.auth.api.PermissionServiceApi;
|
||||
import cn.stylefeng.roses.kernel.auth.api.SessionManagerApi;
|
||||
import cn.stylefeng.roses.kernel.auth.api.exception.AuthException;
|
||||
import cn.stylefeng.roses.kernel.auth.api.pojo.login.LoginUser;
|
||||
import cn.stylefeng.roses.kernel.resource.api.pojo.resource.ResourceDefinition;
|
||||
import cn.stylefeng.roses.kernel.resource.api.pojo.resource.ResourceUrlParam;
|
||||
import cn.stylefeng.roses.kernel.system.ResourceServiceApi;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Set;
|
||||
|
||||
import static cn.stylefeng.roses.kernel.auth.api.exception.enums.AuthExceptionEnum.*;
|
||||
|
||||
/**
|
||||
* 权限相关的service
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/22 15:49
|
||||
*/
|
||||
@Service
|
||||
public class PermissionServiceImpl implements PermissionServiceApi {
|
||||
|
||||
@Resource
|
||||
private ResourceServiceApi resourceServiceApi;
|
||||
|
||||
@Resource
|
||||
private SessionManagerApi sessionManagerApi;
|
||||
|
||||
@Override
|
||||
public void checkPermission(String token, String requestUrl) {
|
||||
|
||||
// 获取token对应的用户信息
|
||||
LoginUser session = sessionManagerApi.getSession(token);
|
||||
if (session == null) {
|
||||
throw new AuthException(TOKEN_ERROR);
|
||||
}
|
||||
|
||||
// 获取url对应的资源信息ResourceDefinition
|
||||
ResourceUrlParam resourceUrlReq = new ResourceUrlParam();
|
||||
resourceUrlReq.setUrl(requestUrl);
|
||||
ResourceDefinition resourceDefinition = resourceServiceApi.getResourceByUrl(resourceUrlReq);
|
||||
|
||||
// 资源为空,直接响应异常,禁止用户访问
|
||||
if (resourceDefinition == null) {
|
||||
throw new AuthException(RESOURCE_DEFINITION_ERROR);
|
||||
}
|
||||
|
||||
// 检查接口是否需要权限验证
|
||||
Boolean requiredPermission = resourceDefinition.getRequiredPermission();
|
||||
|
||||
// 需要权限认证,验证用户有没有当前url的权限
|
||||
if (requiredPermission) {
|
||||
Set<String> resourceUrls = session.getResourceUrls();
|
||||
if (resourceUrls == null || resourceUrls.size() == 0) {
|
||||
throw new AuthException(PERMISSION_RES_VALIDATE_ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,146 @@
|
|||
package cn.stylefeng.roses.kernel.auth.session;
|
||||
|
||||
|
||||
import cn.hutool.cache.impl.TimedCache;
|
||||
import cn.stylefeng.roses.kernel.auth.api.SessionManagerApi;
|
||||
import cn.stylefeng.roses.kernel.auth.api.pojo.login.LoginUser;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static cn.stylefeng.roses.kernel.auth.api.constants.AuthConstants.LOGGED_TOKEN_PREFIX;
|
||||
import static cn.stylefeng.roses.kernel.auth.api.constants.AuthConstants.LOGGED_USERID_PREFIX;
|
||||
|
||||
/**
|
||||
* 基于内存的会话管理
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2019-09-28-14:43
|
||||
*/
|
||||
public class MemoryCacheSessionManager implements SessionManagerApi {
|
||||
|
||||
/**
|
||||
* 登录用户缓存
|
||||
* <p>
|
||||
* key是 LOGGED_TOKEN_PREFIX + 用户的token
|
||||
*/
|
||||
private final TimedCache<String, LoginUser> loginUserCache;
|
||||
|
||||
/**
|
||||
* 用户token的缓存,这个缓存用来存储一个用户的所有token
|
||||
* <p>
|
||||
* 没开启单端限制的话,一个用户可能有多个token,因为一个用户可能在多个地点或打开多个浏览器访问系统
|
||||
* <p>
|
||||
* key是 LOGGED_USERID_PREFIX + 用户id
|
||||
* <p>
|
||||
* 这个缓存应该定时刷新下,因为有过期token的用户,所以这个里边的值set得清理
|
||||
*/
|
||||
private final Map<String, Set<String>> loginTokenCache = new HashMap<>();
|
||||
|
||||
public MemoryCacheSessionManager(TimedCache<String, LoginUser> loginUserCache) {
|
||||
this.loginUserCache = loginUserCache;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createSession(String token, LoginUser loginUser) {
|
||||
|
||||
// 装配用户信息的缓存
|
||||
loginUserCache.put(getTokenKey(token), loginUser);
|
||||
|
||||
// 装配用户token的缓存
|
||||
String userIdKey = getUserIdKey(loginUser.getId());
|
||||
Set<String> theUserTokens = loginTokenCache.get(userIdKey);
|
||||
if (theUserTokens == null) {
|
||||
HashSet<String> tempUserTokens = new HashSet<>();
|
||||
tempUserTokens.add(token);
|
||||
loginTokenCache.put(userIdKey, tempUserTokens);
|
||||
} else {
|
||||
theUserTokens.add(token);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoginUser getSession(String token) {
|
||||
return loginUserCache.get(getTokenKey(token));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeSession(String token) {
|
||||
|
||||
String tokenKey = getTokenKey(token);
|
||||
LoginUser loginUser = loginUserCache.get(tokenKey);
|
||||
|
||||
// 删除用户id对应token的缓存
|
||||
if (loginUser != null) {
|
||||
Long userId = loginUser.getId();
|
||||
Set<String> userTokens = loginTokenCache.get(getUserIdKey(userId));
|
||||
if (userTokens != null) {
|
||||
userTokens.remove(token);
|
||||
|
||||
// 如果删除后size为0,则把整个key删掉
|
||||
if (userTokens.size() == 0) {
|
||||
loginTokenCache.remove(getUserIdKey(userId));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 删除用户信息的缓存
|
||||
loginUserCache.remove(getTokenKey(token));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeSessionExcludeToken(String token) {
|
||||
|
||||
// 获取token对应的会话
|
||||
LoginUser session = this.getSession(token);
|
||||
|
||||
// 如果会话为空,直接返回
|
||||
if (session == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取用户id
|
||||
Long userId = session.getId();
|
||||
|
||||
// 设置用户id对应的token列表为参数token
|
||||
HashSet<String> tokenSet = new HashSet<>();
|
||||
tokenSet.add(token);
|
||||
loginTokenCache.put(getUserIdKey(userId), tokenSet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean haveSession(String token) {
|
||||
return loginUserCache.containsKey(getTokenKey(token));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refreshSession(String token) {
|
||||
LoginUser loginUser = loginUserCache.get(getTokenKey(token));
|
||||
if (loginUser != null) {
|
||||
loginUserCache.put(LOGGED_TOKEN_PREFIX + token, loginUser, loginUserCache.timeout());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取token的缓存key
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/21 15:09
|
||||
*/
|
||||
private String getTokenKey(String token) {
|
||||
return LOGGED_TOKEN_PREFIX + token;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户id的缓存key
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/21 15:10
|
||||
*/
|
||||
private String getUserIdKey(Long userId) {
|
||||
return LOGGED_USERID_PREFIX + userId;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,159 @@
|
|||
package cn.stylefeng.roses.kernel.auth.session;
|
||||
|
||||
import com.alibaba.fastjson.parser.ParserConfig;
|
||||
import cn.stylefeng.roses.kernel.auth.api.SessionManagerApi;
|
||||
import cn.stylefeng.roses.kernel.auth.api.pojo.login.LoginUser;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static cn.stylefeng.roses.kernel.auth.api.constants.AuthConstants.LOGGED_TOKEN_PREFIX;
|
||||
import static cn.stylefeng.roses.kernel.auth.api.constants.AuthConstants.LOGGED_USERID_PREFIX;
|
||||
|
||||
/**
|
||||
* 基于redis的会话管理
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2019-09-28-14:43
|
||||
*/
|
||||
public class RedisSessionManager implements SessionManagerApi {
|
||||
|
||||
/**
|
||||
* 登录用户缓存
|
||||
* <p>
|
||||
* key是 LOGGED_TOKEN_PREFIX + 用户的token
|
||||
*/
|
||||
private final RedisTemplate<String, LoginUser> loginUserRedisTemplate;
|
||||
|
||||
/**
|
||||
* 用户token的缓存,这个缓存用来存储一个用户的所有token
|
||||
* <p>
|
||||
* 没开启单端限制的话,一个用户可能有多个token,因为一个用户可能在多个地点或打开多个浏览器访问系统
|
||||
* <p>
|
||||
* key是 LOGGED_USERID_PREFIX + 用户id
|
||||
* <p>
|
||||
* 这个缓存应该定时刷新下,因为有过期token的用户,所以这个里边的值set得清理
|
||||
*/
|
||||
private final RedisTemplate<String, Set<String>> loginTokenRedisTemplate;
|
||||
|
||||
/**
|
||||
* session的超时时间
|
||||
*/
|
||||
private final Long sessionExpiredSeconds;
|
||||
|
||||
public RedisSessionManager(RedisTemplate<String, LoginUser> loginUserRedisTemplate, RedisTemplate<String, Set<String>> loginTokenRedisTemplate, Long sessionExpiredSeconds) {
|
||||
this.loginUserRedisTemplate = loginUserRedisTemplate;
|
||||
this.loginTokenRedisTemplate = loginTokenRedisTemplate;
|
||||
this.sessionExpiredSeconds = sessionExpiredSeconds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createSession(String token, LoginUser loginUser) {
|
||||
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
|
||||
|
||||
// 装配用户信息的缓存
|
||||
loginUserRedisTemplate.opsForValue().set(getTokenKey(token), loginUser, sessionExpiredSeconds, TimeUnit.SECONDS);
|
||||
|
||||
// 装配用户token的缓存
|
||||
String userIdKey = getUserIdKey(loginUser.getId());
|
||||
Set<String> theUserTokens = loginTokenRedisTemplate.opsForValue().get(userIdKey);
|
||||
if (theUserTokens == null) {
|
||||
HashSet<String> tempUserTokens = new HashSet<>();
|
||||
tempUserTokens.add(token);
|
||||
loginTokenRedisTemplate.opsForValue().set(userIdKey, tempUserTokens);
|
||||
} else {
|
||||
theUserTokens.add(token);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoginUser getSession(String token) {
|
||||
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
|
||||
return loginUserRedisTemplate.opsForValue().get(getTokenKey(token));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeSession(String token) {
|
||||
|
||||
String tokenKey = getTokenKey(token);
|
||||
LoginUser loginUser = loginUserRedisTemplate.opsForValue().get(tokenKey);
|
||||
|
||||
// 删除用户id对应token的缓存
|
||||
if (loginUser != null) {
|
||||
Long userId = loginUser.getId();
|
||||
Set<String> userTokens = loginTokenRedisTemplate.opsForValue().get(getUserIdKey(userId));
|
||||
if (userTokens != null) {
|
||||
userTokens.remove(token);
|
||||
|
||||
// 如果删除后size为0,则把整个key删掉
|
||||
if (userTokens.size() == 0) {
|
||||
loginUserRedisTemplate.delete(getUserIdKey(userId));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 删除用户信息的缓存
|
||||
loginUserRedisTemplate.delete(getTokenKey(token));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeSessionExcludeToken(String token) {
|
||||
|
||||
// 获取token对应的会话
|
||||
LoginUser session = this.getSession(token);
|
||||
|
||||
// 如果会话为空,直接返回
|
||||
if (session == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取用户id
|
||||
Long userId = session.getId();
|
||||
|
||||
// 设置用户id对应的token列表为参数token
|
||||
HashSet<String> tokenSet = new HashSet<>();
|
||||
tokenSet.add(token);
|
||||
loginTokenRedisTemplate.opsForValue().set(getUserIdKey(userId), tokenSet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean haveSession(String token) {
|
||||
Boolean flag = loginUserRedisTemplate.hasKey(getTokenKey(token));
|
||||
if (flag == null) {
|
||||
return false;
|
||||
} else {
|
||||
return flag;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refreshSession(String token) {
|
||||
LoginUser loginUser = loginUserRedisTemplate.boundValueOps(getTokenKey(token)).get();
|
||||
if (loginUser != null) {
|
||||
loginUserRedisTemplate.boundValueOps(getTokenKey(token)).expire(sessionExpiredSeconds, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取token的缓存key
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/21 15:09
|
||||
*/
|
||||
private String getTokenKey(String token) {
|
||||
return LOGGED_TOKEN_PREFIX + token;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户id的缓存key
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/21 15:10
|
||||
*/
|
||||
private String getUserIdKey(Long userId) {
|
||||
return LOGGED_USERID_PREFIX + userId;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
认证和鉴权的spring boot自动加载模块
|
|
@ -0,0 +1,51 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>cn.stylefeng.roses</groupId>
|
||||
<artifactId>kernel-d-auth</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>auth-spring-boot-starter</artifactId>
|
||||
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!--auth本模块的sdk-->
|
||||
<dependency>
|
||||
<groupId>cn.stylefeng.roses</groupId>
|
||||
<artifactId>auth-sdk</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>${project.artifactId}</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<version>3.0.1</version>
|
||||
<configuration>
|
||||
<attach>true</attach>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>compile</phase>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,35 @@
|
|||
package cn.stylefeng.roses.kernel.auth.starter;
|
||||
|
||||
import cn.hutool.cache.CacheUtil;
|
||||
import cn.stylefeng.roses.kernel.auth.api.SessionManagerApi;
|
||||
import cn.stylefeng.roses.kernel.auth.api.expander.AuthConfigExpander;
|
||||
import cn.stylefeng.roses.kernel.auth.session.MemoryCacheSessionManager;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* 认证和鉴权模块的自动配置
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/11/30 22:16
|
||||
*/
|
||||
@Configuration
|
||||
public class GunsAuthAutoConfiguration {
|
||||
|
||||
/**
|
||||
* 默认的session缓存为内存缓存,方便启动
|
||||
* <p>
|
||||
* 如需替换请在项目中增加一个SessionManagerApi Bean即可
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/11/30 22:17
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(SessionManagerApi.class)
|
||||
public SessionManagerApi sessionManagerApi() {
|
||||
Long sessionExpiredSeconds = AuthConfigExpander.getSessionExpiredSeconds();
|
||||
return new MemoryCacheSessionManager(CacheUtil.newTimedCache(sessionExpiredSeconds * 1000));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
cn.stylefeng.roses.kernel.auth.starter.GunsAuthAutoConfiguration
|
|
@ -0,0 +1,35 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>cn.stylefeng.roses</groupId>
|
||||
<artifactId>roses-kernel</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>kernel-d-auth</artifactId>
|
||||
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<modules>
|
||||
<module>auth-api</module>
|
||||
<module>auth-sdk</module>
|
||||
<module>auth-spring-boot-starter</module>
|
||||
</modules>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- 开发规则 -->
|
||||
<dependency>
|
||||
<groupId>cn.stylefeng.roses</groupId>
|
||||
<artifactId>kernel-a-rule</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,7 @@
|
|||
系统配置表模块
|
||||
|
||||
本模块意在建立一块系统配置内存空间,保存一些系统常用的配置,通过在线管理这些配置,可实时维护这些配置
|
||||
|
||||
多模块之间可通过config-api提供的接口,进行配置的共享和读取
|
||||
|
||||
在新增一个配置的时候,优先考虑存到config模块,再考虑往application.yml中存
|
|
@ -0,0 +1,3 @@
|
|||
缓存模块,提供一些缓存接口
|
||||
|
||||
缓存模块为了给系统提供缓存功能,更快的执行业务
|
|
@ -0,0 +1,42 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>cn.stylefeng.roses</groupId>
|
||||
<artifactId>kernel-d-cache</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>cache-api</artifactId>
|
||||
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<version>3.0.1</version>
|
||||
<configuration>
|
||||
<attach>true</attach>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>compile</phase>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
104
kernel-d-cache/cache-api/src/main/java/cn/stylefeng/roses/kernel/cache/api/CacheOperatorApi.java
vendored
Normal file
104
kernel-d-cache/cache-api/src/main/java/cn/stylefeng/roses/kernel/cache/api/CacheOperatorApi.java
vendored
Normal file
|
@ -0,0 +1,104 @@
|
|||
package cn.stylefeng.roses.kernel.cache.api;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 缓存操作的基础接口,可以实现不同种缓存实现
|
||||
* <p>
|
||||
* 泛型为cache的值类class类型
|
||||
*
|
||||
* @author stylefeng
|
||||
* @date 2020/7/8 22:02
|
||||
*/
|
||||
public interface CacheOperatorApi<T> {
|
||||
|
||||
/**
|
||||
* 添加缓存
|
||||
*
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
* @author stylefeng
|
||||
* @date 2020/7/8 22:06
|
||||
*/
|
||||
void put(String key, T value);
|
||||
|
||||
/**
|
||||
* 添加缓存(带过期时间,单位是秒)
|
||||
*
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
* @param timeoutSeconds 过期时间,单位秒
|
||||
* @author stylefeng
|
||||
* @date 2020/7/8 22:07
|
||||
*/
|
||||
void put(String key, T value, Long timeoutSeconds);
|
||||
|
||||
/**
|
||||
* 通过缓存key获取缓存
|
||||
*
|
||||
* @param key 键
|
||||
* @return 值
|
||||
* @author stylefeng
|
||||
* @date 2020/7/8 22:08
|
||||
*/
|
||||
T get(String key);
|
||||
|
||||
/**
|
||||
* 删除缓存
|
||||
*
|
||||
* @param key 键,多个
|
||||
* @author stylefeng
|
||||
* @date 2020/7/8 22:09
|
||||
*/
|
||||
void remove(String... key);
|
||||
|
||||
/**
|
||||
* 判断某个key值是否存在于缓存
|
||||
*
|
||||
* @param key 缓存的键
|
||||
* @return true-存在,false-不存在
|
||||
* @author fengshuonan
|
||||
* @date 2020/11/20 16:50
|
||||
*/
|
||||
boolean contains(String key);
|
||||
|
||||
/**
|
||||
* 获得缓存的所有key列表(不带common prefix的)
|
||||
*
|
||||
* @return key列表
|
||||
* @author stylefeng
|
||||
* @date 2020/7/8 22:11
|
||||
*/
|
||||
Collection<String> getAllKeys();
|
||||
|
||||
/**
|
||||
* 获得缓存的所有值列表
|
||||
*
|
||||
* @return 值列表
|
||||
* @author stylefeng
|
||||
* @date 2020/7/8 22:11
|
||||
*/
|
||||
Collection<T> getAllValues();
|
||||
|
||||
/**
|
||||
* 获取所有的key,value
|
||||
*
|
||||
* @return 键值map
|
||||
* @author stylefeng
|
||||
* @date 2020/7/8 22:11
|
||||
*/
|
||||
Map<String, T> getAllKeyValues();
|
||||
|
||||
/**
|
||||
* 通用缓存的前缀,用于区分不同业务
|
||||
* <p>
|
||||
* 如果带了前缀,所有的缓存在添加的时候,key都会带上这个前缀
|
||||
*
|
||||
* @return 缓存前缀
|
||||
* @author stylefeng
|
||||
* @date 2020/7/9 11:06
|
||||
*/
|
||||
String getCommonKeyPrefix();
|
||||
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package cn.stylefeng.roses.kernel.cache.api.constants;
|
||||
|
||||
/**
|
||||
* 缓存模块的常量
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/22 16:55
|
||||
*/
|
||||
public interface CacheConstants {
|
||||
|
||||
/**
|
||||
* 缓存模块的名称
|
||||
*/
|
||||
String CACHE_MODULE_NAME = "kernel-d-cache";
|
||||
|
||||
/**
|
||||
* 缓存模块的异常步进值
|
||||
*/
|
||||
String CACHE_EXCEPTION_STEP_CODE = "07";
|
||||
|
||||
/**
|
||||
* 缓存的分割符号
|
||||
*/
|
||||
String CACHE_DELIMITER = ":";
|
||||
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
系统配置表的实现,基于数据库存储
|
|
@ -0,0 +1,29 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>cn.stylefeng.roses</groupId>
|
||||
<artifactId>kernel-d-cache</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>cache-sdk-memory</artifactId>
|
||||
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!--cache模块的api-->
|
||||
<dependency>
|
||||
<groupId>cn.stylefeng.roses</groupId>
|
||||
<artifactId>cache-api</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,86 @@
|
|||
package cn.stylefeng.roses.kernel.cache;
|
||||
|
||||
import cn.hutool.cache.impl.CacheObj;
|
||||
import cn.hutool.cache.impl.TimedCache;
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.stylefeng.roses.kernel.cache.api.CacheOperatorApi;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 基于内存的缓存封装
|
||||
*
|
||||
* @author stylefeng
|
||||
* @date 2020/7/9 10:09
|
||||
*/
|
||||
public abstract class AbstractMemoryCacheOperator<T> implements CacheOperatorApi<T> {
|
||||
|
||||
private final TimedCache<String, T> timedCache;
|
||||
|
||||
public AbstractMemoryCacheOperator(TimedCache<String, T> timedCache) {
|
||||
this.timedCache = timedCache;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(String key, T value) {
|
||||
timedCache.put(getCommonKeyPrefix() + key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(String key, T value, Long timeoutSeconds) {
|
||||
timedCache.put(getCommonKeyPrefix() + key, value, timeoutSeconds * 1000);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get(String key) {
|
||||
// 如果用户在超时前调用了get(key)方法,会重头计算起始时间,false的作用就是不从头算
|
||||
return timedCache.get(getCommonKeyPrefix() + key, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(String... key) {
|
||||
if (key.length > 0) {
|
||||
for (String itemKey : key) {
|
||||
timedCache.remove(getCommonKeyPrefix() + itemKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(String key) {
|
||||
return timedCache.containsKey(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> getAllKeys() {
|
||||
Iterator<CacheObj<String, T>> cacheObjIterator = timedCache.cacheObjIterator();
|
||||
ArrayList<String> keys = CollectionUtil.newArrayList();
|
||||
while (cacheObjIterator.hasNext()) {
|
||||
// 去掉缓存key的common prefix前缀
|
||||
String key = cacheObjIterator.next().getKey();
|
||||
keys.add(StrUtil.removePrefix(key, getCommonKeyPrefix()));
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<T> getAllValues() {
|
||||
Iterator<CacheObj<String, T>> cacheObjIterator = timedCache.cacheObjIterator();
|
||||
ArrayList<T> values = CollectionUtil.newArrayList();
|
||||
while (cacheObjIterator.hasNext()) {
|
||||
values.add(cacheObjIterator.next().getValue());
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, T> getAllKeyValues() {
|
||||
Collection<String> allKeys = this.getAllKeys();
|
||||
HashMap<String, T> results = CollectionUtil.newHashMap();
|
||||
for (String key : allKeys) {
|
||||
results.put(key, this.get(key));
|
||||
}
|
||||
return results;
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
系统配置表的实现,基于数据库存储
|
|
@ -0,0 +1,45 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>cn.stylefeng.roses</groupId>
|
||||
<artifactId>kernel-d-cache</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>cache-sdk-redis</artifactId>
|
||||
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!--缓存模块的api-->
|
||||
<dependency>
|
||||
<groupId>cn.stylefeng.roses</groupId>
|
||||
<artifactId>cache-api</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- redis,使用jedis客户端排除lettuce -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>io.lettuce</groupId>
|
||||
<artifactId>lettuce-core</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>redis.clients</groupId>
|
||||
<artifactId>jedis</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,85 @@
|
|||
package cn.stylefeng.roses.kernel.cache;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.stylefeng.roses.kernel.cache.api.CacheOperatorApi;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
||||
/**
|
||||
* 基于redis的缓存封装
|
||||
*
|
||||
* @author stylefeng
|
||||
* @date 2020/7/9 10:09
|
||||
*/
|
||||
public abstract class AbstractRedisCacheOperator<T> implements CacheOperatorApi<T> {
|
||||
|
||||
private final RedisTemplate<String, T> redisTemplate;
|
||||
|
||||
public AbstractRedisCacheOperator(RedisTemplate<String, T> redisTemplate) {
|
||||
this.redisTemplate = redisTemplate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(String key, T value) {
|
||||
redisTemplate.boundValueOps(getCommonKeyPrefix() + key).set(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(String key, T value, Long timeoutSeconds) {
|
||||
redisTemplate.boundValueOps(getCommonKeyPrefix() + key).set(value, timeoutSeconds, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get(String key) {
|
||||
return redisTemplate.boundValueOps(getCommonKeyPrefix() + key).get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(String... key) {
|
||||
ArrayList<String> keys = CollectionUtil.toList(key);
|
||||
List<String> withPrefixKeys = keys.stream().map(i -> getCommonKeyPrefix() + i).collect(Collectors.toList());
|
||||
redisTemplate.delete(withPrefixKeys);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(String key) {
|
||||
T value = redisTemplate.boundValueOps(getCommonKeyPrefix() + key).get();
|
||||
return value != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> getAllKeys() {
|
||||
Set<String> keys = redisTemplate.keys(getCommonKeyPrefix() + "*");
|
||||
if (keys != null) {
|
||||
// 去掉缓存key的common prefix前缀
|
||||
return keys.stream().map(key -> StrUtil.removePrefix(key, getCommonKeyPrefix())).collect(Collectors.toSet());
|
||||
} else {
|
||||
return CollectionUtil.newHashSet();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<T> getAllValues() {
|
||||
Set<String> keys = redisTemplate.keys(getCommonKeyPrefix() + "*");
|
||||
if (keys != null) {
|
||||
return redisTemplate.opsForValue().multiGet(keys);
|
||||
} else {
|
||||
return CollectionUtil.newArrayList();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, T> getAllKeyValues() {
|
||||
Collection<String> allKeys = this.getAllKeys();
|
||||
HashMap<String, T> results = CollectionUtil.newHashMap();
|
||||
for (String key : allKeys) {
|
||||
results.put(key, this.get(key));
|
||||
}
|
||||
return results;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>cn.stylefeng.roses</groupId>
|
||||
<artifactId>roses-kernel</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>kernel-d-cache</artifactId>
|
||||
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<modules>
|
||||
<module>cache-api</module>
|
||||
<module>cache-sdk-memory</module>
|
||||
<module>cache-sdk-redis</module>
|
||||
</modules>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- 开发规则 -->
|
||||
<dependency>
|
||||
<groupId>cn.stylefeng.roses</groupId>
|
||||
<artifactId>kernel-a-rule</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,7 @@
|
|||
系统配置表模块
|
||||
|
||||
本模块意在建立一块系统配置内存空间,保存一些系统常用的配置,通过在线管理这些配置,可实时维护这些配置
|
||||
|
||||
多模块之间可通过config-api提供的接口,进行配置的共享和读取
|
||||
|
||||
在新增一个配置的时候,优先考虑存到config模块,再考虑往application.yml中存
|
|
@ -0,0 +1 @@
|
|||
配置模块的api模块
|
|
@ -0,0 +1,42 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>cn.stylefeng.roses</groupId>
|
||||
<artifactId>kernel-d-config</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>config-api</artifactId>
|
||||
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<version>3.0.1</version>
|
||||
<configuration>
|
||||
<attach>true</attach>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>compile</phase>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
|
@ -0,0 +1,101 @@
|
|||
package cn.stylefeng.roses.kernel.config.api;
|
||||
|
||||
import cn.stylefeng.roses.kernel.config.api.exception.ConfigException;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 系统配置表相关的api
|
||||
* <p>
|
||||
* 系统配置表的实现可以用内存,数据库或redis
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/17 10:27
|
||||
*/
|
||||
public interface ConfigApi {
|
||||
|
||||
/**
|
||||
* 初始化配置表中的所有配置
|
||||
*
|
||||
* @param configs 配置表的所有配置
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/17 14:11
|
||||
*/
|
||||
void initConfig(Map<String, Object> configs);
|
||||
|
||||
/**
|
||||
* 获取配置表中所有配置
|
||||
*
|
||||
* @return 系统配置表中所有的配置
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/17 14:07
|
||||
*/
|
||||
Map<String, Object> getAllConfigs();
|
||||
|
||||
/**
|
||||
* 获取所有配置的名称集合
|
||||
*
|
||||
* @return 所有配置的名称
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/17 14:31
|
||||
*/
|
||||
Set<String> getAllConfigKeys();
|
||||
|
||||
/**
|
||||
* 往配置表中添加一个配置
|
||||
* <p>
|
||||
* 如果有某个配置,则会覆盖某个配置
|
||||
*
|
||||
* @param key 配置标识
|
||||
* @param value 配置具体值
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/17 14:14
|
||||
*/
|
||||
void putConfig(String key, Object value);
|
||||
|
||||
/**
|
||||
* 删除一个配置项
|
||||
*
|
||||
* @param key 配置名称
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/17 18:45
|
||||
*/
|
||||
void deleteConfig(String key);
|
||||
|
||||
/**
|
||||
* 获取config表中的配置,如果为空,抛出异常
|
||||
*
|
||||
* @param configCode 变量名称,对应sys_config表中的code
|
||||
* @param clazz 返回变量值的类型
|
||||
* @return 配置的值
|
||||
* @throws ConfigException 如果值为空抛出异常会
|
||||
* @author stylefeng
|
||||
* @date 2020/6/20 22:03
|
||||
*/
|
||||
<T> T getConfigValue(String configCode, Class<T> clazz) throws ConfigException;
|
||||
|
||||
/**
|
||||
* 获取config表中的配置,如果为空,返回null
|
||||
*
|
||||
* @param configCode 变量名称,对应sys_config表中的code
|
||||
* @param clazz 返回变量值的类型
|
||||
* @return 配置的值
|
||||
* @author stylefeng
|
||||
* @date 2020/6/20 22:03
|
||||
*/
|
||||
<T> T getConfigValueNullable(String configCode, Class<T> clazz);
|
||||
|
||||
/**
|
||||
* 获取config表中的配置,如果为空返回默认值
|
||||
*
|
||||
* @param configCode 变量名称,对应sys_config表中的code
|
||||
* @param clazz 返回变量值的类型
|
||||
* @param defaultValue 如果结果为空返回此默认值
|
||||
* @return 配置的值
|
||||
* @author stylefeng
|
||||
* @date 2020/6/20 22:03
|
||||
*/
|
||||
<T> T getSysConfigValueWithDefault(String configCode, Class<T> clazz, T defaultValue);
|
||||
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package cn.stylefeng.roses.kernel.config.api.constants;
|
||||
|
||||
/**
|
||||
* 系统配置表的常量
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/16 11:05
|
||||
*/
|
||||
public interface ConfigConstants {
|
||||
|
||||
/**
|
||||
* config模块的名称
|
||||
*/
|
||||
String CONFIG_MODULE_NAME = "kernel-d-config";
|
||||
|
||||
/**
|
||||
* 异常枚举的步进值
|
||||
*/
|
||||
String CONFIG_EXCEPTION_STEP_CODE = "04";
|
||||
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
package cn.stylefeng.roses.kernel.config.api.context;
|
||||
|
||||
import cn.stylefeng.roses.kernel.config.api.ConfigApi;
|
||||
import cn.stylefeng.roses.kernel.config.api.exception.ConfigException;
|
||||
|
||||
import static cn.stylefeng.roses.kernel.config.api.exception.enums.ConfigExceptionEnum.CONFIG_CONTAINER_IS_NULL;
|
||||
|
||||
/**
|
||||
* 系统配置表相关的api
|
||||
* <p>
|
||||
* 系统配置表默认由数据库实现,可实现在线管理,也可以拓展redis等实现
|
||||
* <p>
|
||||
* 使用之前请调用setConfigApi初始化
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/17 10:27
|
||||
*/
|
||||
public class ConfigContext {
|
||||
|
||||
private static ConfigApi CONFIG_API = null;
|
||||
|
||||
/**
|
||||
* 获取config操作接口
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/17 14:30
|
||||
*/
|
||||
public static ConfigApi me() {
|
||||
if (CONFIG_API == null) {
|
||||
throw new ConfigException(CONFIG_CONTAINER_IS_NULL);
|
||||
}
|
||||
return CONFIG_API;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置config api的实现
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/12/4 14:35
|
||||
*/
|
||||
public static void setConfigApi(ConfigApi configApi) {
|
||||
CONFIG_API = configApi;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package cn.stylefeng.roses.kernel.config.api.exception;
|
||||
|
||||
import cn.stylefeng.roses.kernel.config.api.constants.ConfigConstants;
|
||||
import cn.stylefeng.roses.kernel.rule.abstracts.AbstractExceptionEnum;
|
||||
import cn.stylefeng.roses.kernel.rule.exception.base.ServiceException;
|
||||
|
||||
/**
|
||||
* 系统配置表的异常
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/15 15:59
|
||||
*/
|
||||
public class ConfigException extends ServiceException {
|
||||
|
||||
public ConfigException(String errorCode, String userTip) {
|
||||
super(ConfigConstants.CONFIG_MODULE_NAME, errorCode, userTip);
|
||||
}
|
||||
|
||||
public ConfigException(AbstractExceptionEnum exception) {
|
||||
super(ConfigConstants.CONFIG_MODULE_NAME, exception);
|
||||
}
|
||||
|
||||
public ConfigException(AbstractExceptionEnum exception, String userTip) {
|
||||
super(ConfigConstants.CONFIG_MODULE_NAME, exception.getErrorCode(), userTip);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
package cn.stylefeng.roses.kernel.config.api.exception.enums;
|
||||
|
||||
import cn.stylefeng.roses.kernel.config.api.constants.ConfigConstants;
|
||||
import cn.stylefeng.roses.kernel.rule.abstracts.AbstractExceptionEnum;
|
||||
import cn.stylefeng.roses.kernel.rule.constants.RuleConstants;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 系统配置表相关的异常枚举
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/16 10:53
|
||||
*/
|
||||
@Getter
|
||||
public enum ConfigExceptionEnum implements AbstractExceptionEnum {
|
||||
|
||||
/**
|
||||
* 数据库操作未知异常
|
||||
*/
|
||||
DAO_ERROR(RuleConstants.BUSINESS_ERROR_TYPE_CODE + ConfigConstants.CONFIG_EXCEPTION_STEP_CODE + "01", "配置表信息操作异常"),
|
||||
|
||||
/**
|
||||
* 系统配置表不存在该配置
|
||||
* <p>
|
||||
* 使用时候,用StrUtil.format()将配置名称带上
|
||||
*/
|
||||
CONFIG_NOT_EXIST(RuleConstants.BUSINESS_ERROR_TYPE_CODE + ConfigConstants.CONFIG_EXCEPTION_STEP_CODE + "02", "系统配置表不存在该配置,请检查该配置是否存在,配置名称:{}"),
|
||||
|
||||
/**
|
||||
* 系统配置表获取值时,强转类型异常
|
||||
* <p>
|
||||
* 使用时候,用StrUtil.format()将配置名称带上
|
||||
*/
|
||||
CONVERT_ERROR(RuleConstants.BUSINESS_ERROR_TYPE_CODE + ConfigConstants.CONFIG_EXCEPTION_STEP_CODE + "03", "获取系统配置值时,强转类型异常,配置名称:{},配置值:{},转化类型:{}"),
|
||||
|
||||
/**
|
||||
* 获取不到application.yml中的数据库配置
|
||||
*/
|
||||
APP_DB_CONFIG_ERROR(RuleConstants.BUSINESS_ERROR_TYPE_CODE + ConfigConstants.CONFIG_EXCEPTION_STEP_CODE + "04", "获取不到application.yml中的数据库配置,无法从数据库加载系统配置表"),
|
||||
|
||||
/**
|
||||
* 初始化系统配置表失败,找不到com.mysql.cj.jdbc.Driver驱动类
|
||||
*/
|
||||
CLASS_NOT_FOUND_ERROR(RuleConstants.BUSINESS_ERROR_TYPE_CODE + ConfigConstants.CONFIG_EXCEPTION_STEP_CODE + "06", "初始化系统配置表失败,找不到com.mysql.cj.jdbc.Driver驱动类"),
|
||||
|
||||
/**
|
||||
* 初始化系统配置表失败,执行查询语句失败
|
||||
*/
|
||||
CONFIG_SQL_EXE_ERROR(RuleConstants.BUSINESS_ERROR_TYPE_CODE + ConfigConstants.CONFIG_EXCEPTION_STEP_CODE + "07", "初始化系统配置表失败,执行查询语句失败"),
|
||||
|
||||
/**
|
||||
* 系统参数配置编码重复
|
||||
*/
|
||||
CONFIG_CODE_REPEAT(RuleConstants.BUSINESS_ERROR_TYPE_CODE + ConfigConstants.CONFIG_EXCEPTION_STEP_CODE + "08", "系统参数配置编码重复,请检查code参数"),
|
||||
|
||||
/**
|
||||
* 删除失败,不能删除系统参数
|
||||
*/
|
||||
CONFIG_SYS_CAN_NOT_DELETE(RuleConstants.BUSINESS_ERROR_TYPE_CODE + ConfigConstants.CONFIG_EXCEPTION_STEP_CODE + "09", "删除失败,不能删除系统参数"),
|
||||
|
||||
/**
|
||||
* 配置容器是空,请先初始化配置容器
|
||||
*/
|
||||
CONFIG_CONTAINER_IS_NULL(RuleConstants.BUSINESS_ERROR_TYPE_CODE + ConfigConstants.CONFIG_EXCEPTION_STEP_CODE + "10", "配置容器为空,请先初始化配置容器,请调用ConfigContext.setConfigApi()初始化");
|
||||
|
||||
/**
|
||||
* 错误编码
|
||||
*/
|
||||
private final String errorCode;
|
||||
|
||||
/**
|
||||
* 提示用户信息
|
||||
*/
|
||||
private final String userTip;
|
||||
|
||||
ConfigExceptionEnum(String errorCode, String userTip) {
|
||||
this.errorCode = errorCode;
|
||||
this.userTip = userTip;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
系统配置表在线维护模块,可以在线管理配置
|
|
@ -0,0 +1,59 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>cn.stylefeng.roses</groupId>
|
||||
<artifactId>kernel-d-config</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>config-business</artifactId>
|
||||
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!--系统配置表的sdk-->
|
||||
<dependency>
|
||||
<groupId>cn.stylefeng.roses</groupId>
|
||||
<artifactId>config-sdk-db</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<!--资源api模块-->
|
||||
<!--用在资源控制器,资源扫描上-->
|
||||
<dependency>
|
||||
<groupId>cn.stylefeng.roses</groupId>
|
||||
<artifactId>scanner-api</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<!--参数校验模块-->
|
||||
<!--用在控制器,参数校验-->
|
||||
<dependency>
|
||||
<groupId>cn.stylefeng.roses</groupId>
|
||||
<artifactId>validator-api</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<!--数据库sdk-->
|
||||
<!--数据库dao框架-->
|
||||
<dependency>
|
||||
<groupId>cn.stylefeng.roses</groupId>
|
||||
<artifactId>db-sdk-mp</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<!--web模块-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,101 @@
|
|||
package cn.stylefeng.roses.kernel.config.modular.controller;
|
||||
|
||||
import cn.stylefeng.roses.kernel.config.modular.param.SysConfigParam;
|
||||
import cn.stylefeng.roses.kernel.config.modular.service.SysConfigService;
|
||||
import cn.stylefeng.roses.kernel.resource.api.annotation.ApiResource;
|
||||
import cn.stylefeng.roses.kernel.resource.api.annotation.GetResource;
|
||||
import cn.stylefeng.roses.kernel.resource.api.annotation.PostResource;
|
||||
import cn.stylefeng.roses.kernel.rule.pojo.response.ResponseData;
|
||||
import cn.stylefeng.roses.kernel.rule.pojo.response.SuccessResponseData;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
|
||||
/**
|
||||
* 参数配置控制器
|
||||
*
|
||||
* @author stylefeng
|
||||
* @date 2020/4/13 22:46
|
||||
*/
|
||||
@RestController
|
||||
@ApiResource(name = "参数配置控制器")
|
||||
public class SysConfigController {
|
||||
|
||||
@Resource
|
||||
private SysConfigService sysConfigService;
|
||||
|
||||
/**
|
||||
* 分页查询配置列表
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/4/14 11:10
|
||||
*/
|
||||
@GetResource(name = "分页查询配置列表", path = "/sysConfig/page")
|
||||
public ResponseData page(SysConfigParam sysConfigParam) {
|
||||
return new SuccessResponseData(sysConfigService.page(sysConfigParam));
|
||||
}
|
||||
|
||||
/**
|
||||
* 系统参数配置列表
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/4/14 11:10
|
||||
*/
|
||||
@GetResource(name = "系统参数配置列表", path = "/sysConfig/list")
|
||||
public ResponseData list(SysConfigParam sysConfigParam) {
|
||||
return new SuccessResponseData(sysConfigService.list(sysConfigParam));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查看系统参数配置
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/4/14 11:12
|
||||
*/
|
||||
@GetResource(name = "查看系统参数配置", path = "/sysConfig/detail")
|
||||
public ResponseData detail(@Validated(SysConfigParam.detail.class) SysConfigParam sysConfigParam) {
|
||||
return new SuccessResponseData(sysConfigService.detail(sysConfigParam));
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加系统参数配置
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/4/14 11:11
|
||||
*/
|
||||
@PostResource(name = "添加系统参数配置", path = "/sysConfig/add")
|
||||
public ResponseData add(@RequestBody @Validated(SysConfigParam.add.class) SysConfigParam sysConfigParam) {
|
||||
sysConfigService.add(sysConfigParam);
|
||||
return new SuccessResponseData();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除系统参数配置
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/4/14 11:11
|
||||
*/
|
||||
@PostResource(name = "删除系统参数配置", path = "/sysConfig/delete")
|
||||
public ResponseData delete(@RequestBody @Validated(SysConfigParam.delete.class) SysConfigParam sysConfigParam) {
|
||||
sysConfigService.delete(sysConfigParam);
|
||||
return new SuccessResponseData();
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑系统参数配置
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/4/14 11:11
|
||||
*/
|
||||
@PostResource(name = "编辑系统参数配置", path = "/sysConfig/edit")
|
||||
public ResponseData edit(@RequestBody @Validated(SysConfigParam.edit.class) SysConfigParam sysConfigParam) {
|
||||
sysConfigService.edit(sysConfigParam);
|
||||
return new SuccessResponseData();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
Copyright [2020] [https://www.stylefeng.cn]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
Guns采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
|
||||
|
||||
1.请不要删除和修改根目录下的LICENSE文件。
|
||||
2.请不要删除和修改Guns源码头部的版权声明。
|
||||
3.请保留源码和相关描述文件的项目出处,作者声明等。
|
||||
4.分发源码时候,请注明软件出处 https://gitee.com/stylefeng/guns-separation
|
||||
5.在修改包名,模块名称,项目代码等时,请注明软件出处 https://gitee.com/stylefeng/guns-separation
|
||||
6.若您的项目无法满足以上几点,可申请商业授权,获取Guns商业授权许可,请在官网购买授权,地址为 https://www.stylefeng.cn
|
||||
*/
|
||||
package cn.stylefeng.roses.kernel.config.modular.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import cn.stylefeng.roses.kernel.db.api.pojo.entity.BaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 参数配置
|
||||
* </p>
|
||||
*
|
||||
* @author stylefeng
|
||||
* @date 2019/6/20 13:44
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@TableName("sys_config")
|
||||
public class SysConfig extends BaseEntity {
|
||||
|
||||
/**
|
||||
* 主键
|
||||
*/
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 名称
|
||||
*/
|
||||
@TableField("name")
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 编码
|
||||
*/
|
||||
@TableField("code")
|
||||
private String code;
|
||||
|
||||
/**
|
||||
* 属性值
|
||||
*/
|
||||
@TableField("value")
|
||||
private String value;
|
||||
|
||||
/**
|
||||
* 是否是系统参数(Y-是,N-否)
|
||||
*/
|
||||
@TableField("sys_flag")
|
||||
private String sysFlag;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
@TableField("remark")
|
||||
private String remark;
|
||||
|
||||
/**
|
||||
* 状态(字典 1正常 2停用)
|
||||
*/
|
||||
@TableField("status")
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 常量所属分类的编码,来自于“常量的分类”字典
|
||||
*/
|
||||
@TableField("group_code")
|
||||
private String groupCode;
|
||||
|
||||
/**
|
||||
* 是否删除(Y-已删除,N-未删除)
|
||||
*/
|
||||
@TableField("del_flag")
|
||||
private String delFlag;
|
||||
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
package cn.stylefeng.roses.kernel.config.modular.listener;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.db.DbUtil;
|
||||
import cn.hutool.db.Entity;
|
||||
import cn.hutool.db.handler.EntityListHandler;
|
||||
import cn.hutool.db.sql.SqlExecutor;
|
||||
import cn.stylefeng.roses.kernel.config.ConfigContainer;
|
||||
import cn.stylefeng.roses.kernel.config.api.context.ConfigContext;
|
||||
import cn.stylefeng.roses.kernel.config.api.exception.ConfigException;
|
||||
import cn.stylefeng.roses.kernel.config.api.exception.enums.ConfigExceptionEnum;
|
||||
import cn.stylefeng.roses.kernel.rule.enums.StatusEnum;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.context.event.ApplicationContextInitializedEvent;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.stylefeng.roses.kernel.config.api.exception.enums.ConfigExceptionEnum.APP_DB_CONFIG_ERROR;
|
||||
|
||||
/**
|
||||
* 初始化系统配置表
|
||||
* <p>
|
||||
* 当spring装配好配置后,就去数据库读constants
|
||||
* <p>
|
||||
*
|
||||
* @author stylefeng
|
||||
* @date 2020/6/6 23:39
|
||||
*/
|
||||
@Slf4j
|
||||
public class ConfigInitListener implements ApplicationListener<ApplicationContextInitializedEvent>, Ordered {
|
||||
|
||||
private static final String CONFIG_LIST_SQL = "select code,value from sys_config where status = ?";
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return Ordered.HIGHEST_PRECEDENCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(ApplicationContextInitializedEvent applicationContextInitializedEvent) {
|
||||
|
||||
// 如果是配置中心的上下文略过,spring cloud环境environment会读取不到
|
||||
ConfigurableApplicationContext applicationContext = applicationContextInitializedEvent.getApplicationContext();
|
||||
if (applicationContext instanceof AnnotationConfigApplicationContext) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 初始化Config Api
|
||||
ConfigContext.setConfigApi(new ConfigContainer());
|
||||
|
||||
ConfigurableEnvironment environment = applicationContextInitializedEvent.getApplicationContext().getEnvironment();
|
||||
|
||||
// 获取数据库连接配置
|
||||
String dataSourceUrl = environment.getProperty("spring.datasource.url");
|
||||
String dataSourceUsername = environment.getProperty("spring.datasource.username");
|
||||
String dataSourcePassword = environment.getProperty("spring.datasource.password");
|
||||
|
||||
// 如果有为空的配置,终止执行
|
||||
if (ObjectUtil.hasEmpty(dataSourceUrl, dataSourceUsername, dataSourcePassword)) {
|
||||
throw new ConfigException(APP_DB_CONFIG_ERROR);
|
||||
}
|
||||
|
||||
Connection conn = null;
|
||||
try {
|
||||
Class.forName("com.mysql.cj.jdbc.Driver");
|
||||
assert dataSourceUrl != null;
|
||||
conn = DriverManager.getConnection(dataSourceUrl, dataSourceUsername, dataSourcePassword);
|
||||
|
||||
// 获取sys_config表的数据
|
||||
List<Entity> entityList = SqlExecutor.query(conn, CONFIG_LIST_SQL, new EntityListHandler(), StatusEnum.ENABLE.getCode());
|
||||
|
||||
// 将查询到的参数配置添加到缓存
|
||||
if (ObjectUtil.isNotEmpty(entityList)) {
|
||||
entityList.forEach(sysConfig -> ConfigContext.me().putConfig(sysConfig.getStr("code"), sysConfig.getStr("value")));
|
||||
}
|
||||
} catch (ClassNotFoundException e) {
|
||||
log.error(">>> 初始化系统配置表失败,找不到com.mysql.cj.jdbc.Driver类", e);
|
||||
throw new ConfigException(ConfigExceptionEnum.CLASS_NOT_FOUND_ERROR);
|
||||
} catch (SQLException sqlException) {
|
||||
log.error(">>> 初始化系统配置表失败,执行查询语句失败", sqlException);
|
||||
throw new ConfigException(ConfigExceptionEnum.CONFIG_SQL_EXE_ERROR);
|
||||
} finally {
|
||||
DbUtil.close(conn);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package cn.stylefeng.roses.kernel.config.modular.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import cn.stylefeng.roses.kernel.config.modular.entity.SysConfig;
|
||||
|
||||
/**
|
||||
* 系统参数配置 Mapper 接口
|
||||
*
|
||||
* @author stylefeng
|
||||
* @date 2019/6/20 13:44
|
||||
*/
|
||||
public interface SysConfigMapper extends BaseMapper<SysConfig> {
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="cn.stylefeng.roses.kernel.config.modular.mapper.SysConfigMapper">
|
||||
|
||||
|
||||
</mapper>
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
Copyright [2020] [https://www.stylefeng.cn]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
Guns采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
|
||||
|
||||
1.请不要删除和修改根目录下的LICENSE文件。
|
||||
2.请不要删除和修改Guns源码头部的版权声明。
|
||||
3.请保留源码和相关描述文件的项目出处,作者声明等。
|
||||
4.分发源码时候,请注明软件出处 https://gitee.com/stylefeng/guns-separation
|
||||
5.在修改包名,模块名称,项目代码等时,请注明软件出处 https://gitee.com/stylefeng/guns-separation
|
||||
6.若您的项目无法满足以上几点,可申请商业授权,获取Guns商业授权许可,请在官网购买授权,地址为 https://www.stylefeng.cn
|
||||
*/
|
||||
package cn.stylefeng.roses.kernel.config.modular.param;
|
||||
|
||||
import cn.stylefeng.roses.kernel.rule.pojo.request.BaseRequest;
|
||||
import cn.stylefeng.roses.kernel.validator.validators.flag.FlagValue;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* 系统参数配置参数
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/4/14 10:18
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class SysConfigParam extends BaseRequest {
|
||||
|
||||
/**
|
||||
* 主键
|
||||
*/
|
||||
@NotNull(message = "id不能为空,请检查id参数", groups = {edit.class, delete.class, detail.class})
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 名称
|
||||
*/
|
||||
@NotBlank(message = "名称不能为空,请检查name参数", groups = {add.class, edit.class})
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 编码
|
||||
*/
|
||||
@NotBlank(message = "编码不能为空,请检查code参数", groups = {add.class, edit.class})
|
||||
private String code;
|
||||
|
||||
/**
|
||||
* 值
|
||||
*/
|
||||
@NotBlank(message = "值不能为空,请检查value参数", groups = {add.class, edit.class})
|
||||
private String value;
|
||||
|
||||
/**
|
||||
* 是否是系统参数(Y-是,N-否)
|
||||
*/
|
||||
@NotBlank(message = "是否是系统参数不能为空,请检查sysFlag参数", groups = {add.class, edit.class})
|
||||
@FlagValue(message = "是否是系统参数格式错误,正确格式应该Y或者N,请检查sysFlag参数", groups = {add.class, edit.class})
|
||||
private String sysFlag;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
/**
|
||||
* 常量所属分类的编码,来自于“常量的分类”字典
|
||||
*/
|
||||
@NotBlank(message = "值不能为空,请检查value参数", groups = {add.class, edit.class})
|
||||
private String groupCode;
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
Copyright [2020] [https://www.stylefeng.cn]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
Guns采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
|
||||
|
||||
1.请不要删除和修改根目录下的LICENSE文件。
|
||||
2.请不要删除和修改Guns源码头部的版权声明。
|
||||
3.请保留源码和相关描述文件的项目出处,作者声明等。
|
||||
4.分发源码时候,请注明软件出处 https://gitee.com/stylefeng/guns-separation
|
||||
5.在修改包名,模块名称,项目代码等时,请注明软件出处 https://gitee.com/stylefeng/guns-separation
|
||||
6.若您的项目无法满足以上几点,可申请商业授权,获取Guns商业授权许可,请在官网购买授权,地址为 https://www.stylefeng.cn
|
||||
*/
|
||||
package cn.stylefeng.roses.kernel.config.modular.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import cn.stylefeng.roses.kernel.config.modular.entity.SysConfig;
|
||||
import cn.stylefeng.roses.kernel.config.modular.param.SysConfigParam;
|
||||
import cn.stylefeng.roses.kernel.db.api.pojo.page.PageResult;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 系统参数配置service接口
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/4/14 11:14
|
||||
*/
|
||||
public interface SysConfigService extends IService<SysConfig> {
|
||||
|
||||
/**
|
||||
* 查询系统参数配置
|
||||
*
|
||||
* @param sysConfigParam 查询参数
|
||||
* @return 查询分页结果
|
||||
* @author fengshuonan
|
||||
* @date 2020/4/14 11:14
|
||||
*/
|
||||
PageResult<SysConfig> page(SysConfigParam sysConfigParam);
|
||||
|
||||
/**
|
||||
* 查询系统参数配置
|
||||
*
|
||||
* @param sysConfigParam 查询参数
|
||||
* @return 系统参数配置列表
|
||||
* @author fengshuonan
|
||||
* @date 2020/4/14 11:14
|
||||
*/
|
||||
List<SysConfig> list(SysConfigParam sysConfigParam);
|
||||
|
||||
/**
|
||||
* 查看系统参数配置
|
||||
*
|
||||
* @param sysConfigParam 查看参数
|
||||
* @return 系统参数配置
|
||||
* @author fengshuonan
|
||||
* @date 2020/4/14 11:15
|
||||
*/
|
||||
SysConfig detail(SysConfigParam sysConfigParam);
|
||||
|
||||
/**
|
||||
* 添加系统参数配置
|
||||
*
|
||||
* @param sysConfigParam 添加参数
|
||||
* @author fengshuonan
|
||||
* @date 2020/4/14 11:14
|
||||
*/
|
||||
void add(SysConfigParam sysConfigParam);
|
||||
|
||||
/**
|
||||
* 删除系统参数配置
|
||||
*
|
||||
* @param sysConfigParam 删除参数
|
||||
* @author fengshuonan
|
||||
* @date 2020/4/14 11:15
|
||||
*/
|
||||
void delete(SysConfigParam sysConfigParam);
|
||||
|
||||
/**
|
||||
* 编辑系统参数配置
|
||||
*
|
||||
* @param sysConfigParam 编辑参数
|
||||
* @author fengshuonan
|
||||
* @date 2020/4/14 11:15
|
||||
*/
|
||||
void edit(SysConfigParam sysConfigParam);
|
||||
|
||||
}
|
|
@ -0,0 +1,157 @@
|
|||
package cn.stylefeng.roses.kernel.config.modular.service.impl;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.stylefeng.roses.kernel.config.modular.mapper.SysConfigMapper;
|
||||
import cn.stylefeng.roses.kernel.config.modular.service.SysConfigService;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import cn.stylefeng.roses.kernel.config.api.context.ConfigContext;
|
||||
import cn.stylefeng.roses.kernel.config.api.exception.ConfigException;
|
||||
import cn.stylefeng.roses.kernel.config.api.exception.enums.ConfigExceptionEnum;
|
||||
import cn.stylefeng.roses.kernel.config.modular.entity.SysConfig;
|
||||
import cn.stylefeng.roses.kernel.config.modular.param.SysConfigParam;
|
||||
import cn.stylefeng.roses.kernel.db.api.factory.PageFactory;
|
||||
import cn.stylefeng.roses.kernel.db.api.factory.PageResultFactory;
|
||||
import cn.stylefeng.roses.kernel.db.api.pojo.page.PageResult;
|
||||
import cn.stylefeng.roses.kernel.rule.enums.StatusEnum;
|
||||
import cn.stylefeng.roses.kernel.rule.enums.YesOrNotEnum;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static cn.stylefeng.roses.kernel.config.api.exception.enums.ConfigExceptionEnum.CONFIG_SYS_CAN_NOT_DELETE;
|
||||
|
||||
|
||||
/**
|
||||
* 系统参数配置service接口实现类
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/4/14 11:16
|
||||
*/
|
||||
@Service
|
||||
public class SysConfigServiceImpl extends ServiceImpl<SysConfigMapper, SysConfig> implements SysConfigService {
|
||||
|
||||
@Override
|
||||
public PageResult<SysConfig> page(SysConfigParam sysConfigParam) {
|
||||
LambdaQueryWrapper<SysConfig> wrapper = createWrapper(sysConfigParam);
|
||||
Page<SysConfig> page = this.page(PageFactory.defaultPage(), wrapper);
|
||||
return PageResultFactory.createPageResult(page);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SysConfig> list(SysConfigParam sysConfigParam) {
|
||||
LambdaQueryWrapper<SysConfig> wrapper = createWrapper(sysConfigParam);
|
||||
return this.list(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SysConfig detail(SysConfigParam sysConfigParam) {
|
||||
return this.querySysConfig(sysConfigParam);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(SysConfigParam sysConfigParam) {
|
||||
|
||||
// 1.构造实体
|
||||
SysConfig sysConfig = new SysConfig();
|
||||
BeanUtil.copyProperties(sysConfigParam, sysConfig);
|
||||
sysConfig.setStatus(StatusEnum.ENABLE.getCode());
|
||||
|
||||
// 2.保存到库中
|
||||
this.save(sysConfig);
|
||||
|
||||
// 3.添加对应context
|
||||
ConfigContext.me().putConfig(sysConfigParam.getCode(), sysConfigParam.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(SysConfigParam sysConfigParam) {
|
||||
|
||||
// 1.根据id获取常量
|
||||
SysConfig sysConfig = this.querySysConfig(sysConfigParam);
|
||||
|
||||
// 2.不能删除系统参数
|
||||
if (YesOrNotEnum.Y.getCode().equals(sysConfig.getSysFlag())) {
|
||||
throw new ConfigException(CONFIG_SYS_CAN_NOT_DELETE);
|
||||
}
|
||||
|
||||
// 3.设置状态为已删除
|
||||
sysConfig.setStatus(StatusEnum.DISABLE.getCode());
|
||||
this.updateById(sysConfig);
|
||||
|
||||
// 4.删除对应context
|
||||
ConfigContext.me().deleteConfig(sysConfigParam.getCode());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void edit(SysConfigParam sysConfigParam) {
|
||||
|
||||
// 1.根据id获取常量信息
|
||||
SysConfig sysConfig = this.querySysConfig(sysConfigParam);
|
||||
|
||||
// 2.请求参数转化为实体
|
||||
BeanUtil.copyProperties(sysConfigParam, sysConfig);
|
||||
// 不能修改状态,用修改状态接口修改状态
|
||||
sysConfig.setStatus(null);
|
||||
|
||||
// 3.更新记录
|
||||
this.updateById(sysConfig);
|
||||
|
||||
// 4.更新对应常量context
|
||||
ConfigContext.me().putConfig(sysConfigParam.getCode(), sysConfigParam.getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取系统参数配置
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/4/14 11:19
|
||||
*/
|
||||
private SysConfig querySysConfig(SysConfigParam sysConfigParam) {
|
||||
SysConfig sysConfig = this.getById(sysConfigParam.getId());
|
||||
if (ObjectUtil.isEmpty(sysConfig)) {
|
||||
String userTip = StrUtil.format(ConfigExceptionEnum.CONFIG_NOT_EXIST.getUserTip(), "id: " + sysConfigParam.getId());
|
||||
throw new ConfigException(ConfigExceptionEnum.CONFIG_NOT_EXIST, userTip);
|
||||
}
|
||||
return sysConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建wrapper
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/11/6 10:16
|
||||
*/
|
||||
private LambdaQueryWrapper<SysConfig> createWrapper(SysConfigParam sysConfigParam) {
|
||||
LambdaQueryWrapper<SysConfig> queryWrapper = new LambdaQueryWrapper<>();
|
||||
|
||||
if (ObjectUtil.isNotNull(sysConfigParam)) {
|
||||
// 如果名称不为空,则带上名称搜素搜条件
|
||||
if (ObjectUtil.isNotEmpty(sysConfigParam.getName())) {
|
||||
queryWrapper.like(SysConfig::getName, sysConfigParam.getName());
|
||||
}
|
||||
|
||||
// 如果常量编码不为空,则带上常量编码搜素搜条件
|
||||
if (ObjectUtil.isNotEmpty(sysConfigParam.getCode())) {
|
||||
queryWrapper.like(SysConfig::getCode, sysConfigParam.getCode());
|
||||
}
|
||||
|
||||
// 如果分类编码不为空,则带上分类编码
|
||||
if (ObjectUtil.isNotEmpty(sysConfigParam.getGroupCode())) {
|
||||
queryWrapper.eq(SysConfig::getGroupCode, sysConfigParam.getGroupCode());
|
||||
}
|
||||
}
|
||||
|
||||
// 查询未删除的
|
||||
queryWrapper.ne(SysConfig::getDelFlag, YesOrNotEnum.Y.getCode());
|
||||
|
||||
// 按类型升序排列,同类型的排在一起
|
||||
queryWrapper.orderByDesc(SysConfig::getGroupCode);
|
||||
|
||||
return queryWrapper;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
系统配置表的实现,基于数据库存储
|
|
@ -0,0 +1,29 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>cn.stylefeng.roses</groupId>
|
||||
<artifactId>kernel-d-config</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>config-sdk-db</artifactId>
|
||||
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!--config模块的api-->
|
||||
<dependency>
|
||||
<groupId>cn.stylefeng.roses</groupId>
|
||||
<artifactId>config-api</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,105 @@
|
|||
package cn.stylefeng.roses.kernel.config;
|
||||
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.lang.Dict;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.stylefeng.roses.kernel.config.api.ConfigApi;
|
||||
import cn.stylefeng.roses.kernel.config.api.exception.ConfigException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static cn.stylefeng.roses.kernel.config.api.exception.enums.ConfigExceptionEnum.CONFIG_NOT_EXIST;
|
||||
import static cn.stylefeng.roses.kernel.config.api.exception.enums.ConfigExceptionEnum.CONVERT_ERROR;
|
||||
|
||||
/**
|
||||
* 系统配置表的实现类
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/17 14:56
|
||||
*/
|
||||
@Slf4j
|
||||
public class ConfigContainer implements ConfigApi {
|
||||
|
||||
/**
|
||||
* 系统配置的Map缓存
|
||||
*/
|
||||
private static final Dict CONFIG_CONTAINER = Dict.create();
|
||||
|
||||
@Override
|
||||
public void initConfig(Map<String, Object> configs) {
|
||||
if (configs == null || configs.size() == 0) {
|
||||
return;
|
||||
}
|
||||
CONFIG_CONTAINER.putAll(configs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getAllConfigs() {
|
||||
return CONFIG_CONTAINER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getAllConfigKeys() {
|
||||
return CONFIG_CONTAINER.keySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putConfig(String key, Object value) {
|
||||
CONFIG_CONTAINER.put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteConfig(String key) {
|
||||
CONFIG_CONTAINER.remove(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T getConfigValue(String configCode, Class<T> clazz) throws ConfigException {
|
||||
String configValue = CONFIG_CONTAINER.getStr(configCode);
|
||||
if (ObjectUtil.isEmpty(configValue)) {
|
||||
String format = StrUtil.format(CONFIG_NOT_EXIST.getUserTip(), configCode);
|
||||
log.error(format);
|
||||
throw new ConfigException(CONFIG_NOT_EXIST.getErrorCode(), format);
|
||||
} else {
|
||||
try {
|
||||
return Convert.convert(clazz, configValue);
|
||||
} catch (Exception e) {
|
||||
String format = StrUtil.format(CONVERT_ERROR.getUserTip(), configCode, configValue, clazz.toString());
|
||||
log.error(format);
|
||||
throw new ConfigException(CONVERT_ERROR.getErrorCode(), format);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T getConfigValueNullable(String configCode, Class<T> clazz) {
|
||||
String configValue = CONFIG_CONTAINER.getStr(configCode);
|
||||
if (ObjectUtil.isEmpty(configValue)) {
|
||||
String format = StrUtil.format(CONFIG_NOT_EXIST.getUserTip(), configCode);
|
||||
log.error(format);
|
||||
return null;
|
||||
} else {
|
||||
try {
|
||||
return Convert.convert(clazz, configValue);
|
||||
} catch (Exception e) {
|
||||
String format = StrUtil.format(CONVERT_ERROR.getUserTip(), configCode, configValue, clazz.toString());
|
||||
log.error(format);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T getSysConfigValueWithDefault(String configCode, Class<T> clazz, T defaultValue) {
|
||||
T value = this.getConfigValueNullable(configCode, clazz);
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
配置模块的spring boot自动加载模块
|
|
@ -0,0 +1,51 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>cn.stylefeng.roses</groupId>
|
||||
<artifactId>kernel-d-config</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>config-spring-boot-starter</artifactId>
|
||||
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!--config包含的业务模块-->
|
||||
<dependency>
|
||||
<groupId>cn.stylefeng.roses</groupId>
|
||||
<artifactId>config-business</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>${project.artifactId}</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<version>3.0.1</version>
|
||||
<configuration>
|
||||
<attach>true</attach>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>compile</phase>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,15 @@
|
|||
package cn.stylefeng.roses.kernel.config.starter;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* 系统配置模块的自动配置类
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/11/30 22:24
|
||||
*/
|
||||
@Configuration
|
||||
public class GunsSysConfigAutoConfiguration {
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
cn.stylefeng.roses.kernel.config.starter.GunsSysConfigAutoConfiguration
|
||||
org.springframework.context.ApplicationListener=\
|
||||
cn.stylefeng.roses.kernel.config.modular.listener.ConfigInitListener
|
|
@ -0,0 +1,36 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>cn.stylefeng.roses</groupId>
|
||||
<artifactId>roses-kernel</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>kernel-d-config</artifactId>
|
||||
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<modules>
|
||||
<module>config-api</module>
|
||||
<module>config-sdk-db</module>
|
||||
<module>config-business</module>
|
||||
<module>config-spring-boot-starter</module>
|
||||
</modules>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- 开发规则 -->
|
||||
<dependency>
|
||||
<groupId>cn.stylefeng.roses</groupId>
|
||||
<artifactId>kernel-a-rule</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,3 @@
|
|||
跟数据库交互的框架
|
||||
|
||||
公司约定用mybatis-plus为核心框架,如无特殊情况,不允许切换dao框架
|
|
@ -0,0 +1 @@
|
|||
数据库api模块
|
|
@ -0,0 +1,63 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>cn.stylefeng.roses</groupId>
|
||||
<artifactId>kernel-d-db</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>db-api</artifactId>
|
||||
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- mybatis-plus dao框架 -->
|
||||
<!-- 基础实体,分页参数和结果的创建,需要用到mp的注解和类 -->
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- servlet库 -->
|
||||
<!-- PageFactory类会从http请求获取分页参数 -->
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!--数据库连接池,项目默认采用druid,用别的连接池可以排除这个包-->
|
||||
<!--druid数据源的创建,需要用到本包-->
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>druid</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<version>3.0.1</version>
|
||||
<configuration>
|
||||
<attach>true</attach>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>compile</phase>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
|
@ -0,0 +1,43 @@
|
|||
package cn.stylefeng.roses.kernel.db.api;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 数据库操作的api,用于快速进行sql操作并获取结果
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/11/4 14:43
|
||||
*/
|
||||
public interface DbOperatorApi {
|
||||
|
||||
/**
|
||||
* 返回SelectCount SQL执行的结果
|
||||
*
|
||||
* @param sql 带有select count()相关语句的sql
|
||||
* @param args sql中的参数
|
||||
* @return sql执行的结果,取第一行第一个数字
|
||||
* @author fengshuonan
|
||||
* @date 2020/11/4 14:43
|
||||
*/
|
||||
int selectCount(String sql, Object... args);
|
||||
|
||||
/**
|
||||
* 获取某个表,某条数据的所有子列表 TODO 测试
|
||||
* <p>
|
||||
* 本方法用在带有层级关系的表,并且有 "pids" 类似的字段
|
||||
* <p>
|
||||
* 通过 like 操作可以查询到该条数据的所有子数据
|
||||
* <p>
|
||||
* pids的组成规范必须是[0],[xxx],[xxx]
|
||||
*
|
||||
* @param tableName 表名称,例如sys_user
|
||||
* @param parentIdsFieldName 父级ids的字段名
|
||||
* @param keyFieldName 主键id的字段名
|
||||
* @param keyFieldValue 主键id的值
|
||||
* @return keyFieldValue的值
|
||||
* @author fengshuonan
|
||||
* @date 2020/11/5 17:32
|
||||
*/
|
||||
Set<Long> findSubListByParentId(String tableName, String parentIdsFieldName, String keyFieldName, Long keyFieldValue);
|
||||
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package cn.stylefeng.roses.kernel.db.api.constants;
|
||||
|
||||
/**
|
||||
* 数据库模块的常量
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/16 11:05
|
||||
*/
|
||||
public interface DbConstants {
|
||||
|
||||
/**
|
||||
* db模块的名称
|
||||
*/
|
||||
String DB_MODULE_NAME = "kernel-d-db";
|
||||
|
||||
/**
|
||||
* 异常枚举的步进值
|
||||
*/
|
||||
String DB_EXCEPTION_STEP_CODE = "02";
|
||||
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package cn.stylefeng.roses.kernel.db.api.constants;
|
||||
|
||||
/**
|
||||
* 数据库常用字段的枚举
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/16 17:07
|
||||
*/
|
||||
public interface DbFieldConstants {
|
||||
|
||||
/**
|
||||
* 主键id的字段名
|
||||
*/
|
||||
String ID = "id";
|
||||
|
||||
/**
|
||||
* 创建用户的字段名
|
||||
*/
|
||||
String CREATE_USER = "createUser";
|
||||
|
||||
/**
|
||||
* 创建时间的字段名
|
||||
*/
|
||||
String CREATE_TIME = "createTime";
|
||||
|
||||
/**
|
||||
* 更新用户的字段名
|
||||
*/
|
||||
String UPDATE_USER = "updateUser";
|
||||
|
||||
/**
|
||||
* 更新时间的字段名
|
||||
*/
|
||||
String UPDATE_TIME = "updateTime";
|
||||
|
||||
/**
|
||||
* 删除标记的字段名
|
||||
*/
|
||||
String DEL_FLAG = "delFlag";
|
||||
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package cn.stylefeng.roses.kernel.db.api.context;
|
||||
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import cn.stylefeng.roses.kernel.db.api.DbOperatorApi;
|
||||
|
||||
/**
|
||||
* 获取sql操作器
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2020/11/4 15:07
|
||||
*/
|
||||
public class DbOperatorContext {
|
||||
|
||||
public static DbOperatorApi me() {
|
||||
return SpringUtil.getBean(DbOperatorApi.class);
|
||||
}
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue