初始化项目

pull/3/head
fengshuonan 2020-12-11 18:18:48 +08:00
commit 1bfabf1ffa
647 changed files with 35793 additions and 0 deletions

40
.gitignore vendored Normal file
View File

@ -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/

131
README.md Normal file
View File

@ -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、businessapi为对其他模块暴露的接口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工具类lombokfastjson强依赖其他框架不强依赖
## 规则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 像小白一样思考,像专家一样行动

18
kernel-a-rule/README.md Normal file
View File

@ -0,0 +1,18 @@
整个框架每个模块需要遵守的规范
每个开发人员也需要遵守的规范
## 业务异常统一用ServiceException类
## 每个模块的异常枚举要在exception.enums包下维护
## 异常枚举分三类
异常分为三类第一类用户操作异常的枚举以UserExceptionEnum结尾
第二类是系统业务逻辑异常枚举以BusinessExceptionEnum结尾
第三类是第三方调用异常枚举以ThirdExceptionEnum结尾
## 每个模块设立独立的模块异常,方便区分各个模块的异常

83
kernel-a-rule/pom.xml Normal file
View File

@ -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>

View File

@ -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();
}

View File

@ -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);
}

View File

@ -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";
}

View File

@ -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 = "根结点";
}

View File

@ -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;
}
}

View File

@ -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.
GunsAPACHE 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;
}
}

View File

@ -0,0 +1,33 @@
package cn.stylefeng.roses.kernel.rule.enums;
import lombok.Getter;
/**
* del_flagchar(1)YN
*
* @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;
}
}

View File

@ -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.
GunsAPACHE 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();
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -0,0 +1,35 @@
package cn.stylefeng.roses.kernel.rule.pojo.dict;
import lombok.Data;
/**
* id,name,codepojo
* <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;
}

View File

@ -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 {
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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;
/**
* HttpHttpServletRequestHttpServletResponse
*
* @author fengshuonan
* @date 2020/10/15 17:38
*/
@Slf4j
public class HttpServletUtil {
/**
* ip
*/
private static final String LOCAL_IP = "127.0.0.1";
/**
* ipipv6
*/
private static final String LOCAL_REMOTE_HOST = "0:0:0:0:0:0:0:1";
/**
* httpheader
*/
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>
* ipv6127.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;
}
}
/**
* httpclient ip
*
* @param request http
* @param ipGeoApi ipapi
* @param ipGeoAppCode ipappCode
* @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;
}
/**
* httpUserAgent
* <p>
* UserAgent
* <p>
* headernull
*
* @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;
}
}
}

11
kernel-d-auth/README.md Normal file
View File

@ -0,0 +1,11 @@
# 权限模块
## 系统需要认证的情况下使用
## auth包含认证和鉴权
认证和鉴权的区别:
认证校验你能否登录系统认证的过程是校验token的过程
鉴权校验你有系统的哪些权限,鉴权的过程是校验角色是否包含某些接口的权限

View File

@ -0,0 +1 @@
权限模块api

View File

@ -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>

View File

@ -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-tokenfalse-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);
}

View File

@ -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 tokennull
* @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 truefalse
* @author fengshuonan
* @date 2020/10/17 11:02
*/
boolean hasLogin();
/**
* 访
*
* @param requestUri url: /userInfo/list
* @return 访truefalse
* @author fengshuonan
* @date 2020/10/17 11:03
*/
boolean hasPermission(String requestUri);
/**
*
*
* @param roleCode
* @return truefalse
* @author fengshuonan
* @date 2020/10/17 11:04
*/
boolean hasRole(String roleCode);
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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_";
/**
* httptokenheader
*/
String DEFAULT_AUTH_HEADER_NAME = "Authorization";
/**
* httptokenparam
*/
String DEFAULT_AUTH_PARAM_NAME = "token";
/**
*
*/
String DEFAULT_PASSWORD = "123456";
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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, ',');
}
}
/**
* session3600
* <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);
}
/**
* tokenheader
*
* @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);
}
/**
* tokenheader
*
* @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);
}
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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;
/**
* SexEnumM-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;
/**
* DictMap
*/
private Dict otherInfos;
}

View File

@ -0,0 +1 @@
认证和鉴权的sdk

View File

@ -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>

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}
}
}

View File

@ -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;
/**
* tokentoken
* <p>
* token访
* <p>
* key LOGGED_USERID_PREFIX + id
* <p>
* tokenset
*/
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());
}
}
/**
* tokenkey
*
* @author fengshuonan
* @date 2020/10/21 15:09
*/
private String getTokenKey(String token) {
return LOGGED_TOKEN_PREFIX + token;
}
/**
* idkey
*
* @author fengshuonan
* @date 2020/10/21 15:10
*/
private String getUserIdKey(Long userId) {
return LOGGED_USERID_PREFIX + userId;
}
}

View File

@ -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;
/**
* tokentoken
* <p>
* token访
* <p>
* key LOGGED_USERID_PREFIX + id
* <p>
* tokenset
*/
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);
}
}
/**
* tokenkey
*
* @author fengshuonan
* @date 2020/10/21 15:09
*/
private String getTokenKey(String token) {
return LOGGED_TOKEN_PREFIX + token;
}
/**
* idkey
*
* @author fengshuonan
* @date 2020/10/21 15:10
*/
private String getUserIdKey(Long userId) {
return LOGGED_USERID_PREFIX + userId;
}
}

View File

@ -0,0 +1 @@
认证和鉴权的spring boot自动加载模块

View File

@ -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>

View File

@ -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));
}
}

View File

@ -0,0 +1,2 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
cn.stylefeng.roses.kernel.auth.starter.GunsAuthAutoConfiguration

35
kernel-d-auth/pom.xml Normal file
View File

@ -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>

7
kernel-d-cache/README.md Normal file
View File

@ -0,0 +1,7 @@
系统配置表模块
本模块意在建立一块系统配置内存空间,保存一些系统常用的配置,通过在线管理这些配置,可实时维护这些配置
多模块之间可通过config-api提供的接口进行配置的共享和读取
在新增一个配置的时候优先考虑存到config模块再考虑往application.yml中存

View File

@ -0,0 +1,3 @@
缓存模块,提供一些缓存接口
缓存模块为了给系统提供缓存功能,更快的执行业务

View File

@ -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>

View File

@ -0,0 +1,104 @@
package cn.stylefeng.roses.kernel.cache.api;
import java.util.Collection;
import java.util.Map;
/**
*
* <p>
* cacheclass
*
* @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);
/**
* keycommon 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();
/**
* keyvalue
*
* @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();
}

View File

@ -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 = ":";
}

View File

@ -0,0 +1 @@
系统配置表的实现,基于数据库存储

View File

@ -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>

View File

@ -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;
}
}

View File

@ -0,0 +1 @@
系统配置表的实现,基于数据库存储

View File

@ -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>

View File

@ -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;
}
}

35
kernel-d-cache/pom.xml Normal file
View File

@ -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>

View File

@ -0,0 +1,7 @@
系统配置表模块
本模块意在建立一块系统配置内存空间,保存一些系统常用的配置,通过在线管理这些配置,可实时维护这些配置
多模块之间可通过config-api提供的接口进行配置的共享和读取
在新增一个配置的时候优先考虑存到config模块再考虑往application.yml中存

View File

@ -0,0 +1 @@
配置模块的api模块

View File

@ -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>

View File

@ -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_configcode
* @param clazz
* @return
* @throws ConfigException
* @author stylefeng
* @date 2020/6/20 22:03
*/
<T> T getConfigValue(String configCode, Class<T> clazz) throws ConfigException;
/**
* confignull
*
* @param configCode sys_configcode
* @param clazz
* @return
* @author stylefeng
* @date 2020/6/20 22:03
*/
<T> T getConfigValueNullable(String configCode, Class<T> clazz);
/**
* config
*
* @param configCode sys_configcode
* @param clazz
* @param defaultValue
* @return
* @author stylefeng
* @date 2020/6/20 22:03
*/
<T> T getSysConfigValueWithDefault(String configCode, Class<T> clazz, T defaultValue);
}

View File

@ -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";
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1 @@
系统配置表在线维护模块,可以在线管理配置

View File

@ -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>

View File

@ -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();
}
}

View File

@ -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.
GunsAPACHE 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;
}

View File

@ -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>
* springconstants
* <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);
}
}
}

View File

@ -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> {
}

View File

@ -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>

View File

@ -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.
GunsAPACHE 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;
}

View File

@ -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.
GunsAPACHE 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);
}

View File

@ -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;
}
}

View File

@ -0,0 +1 @@
系统配置表的实现,基于数据库存储

View File

@ -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>

View File

@ -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;
}
}
}

View File

@ -0,0 +1 @@
配置模块的spring boot自动加载模块

View File

@ -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>

View File

@ -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 {
}

View File

@ -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

36
kernel-d-config/pom.xml Normal file
View File

@ -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>

3
kernel-d-db/README.md Normal file
View File

@ -0,0 +1,3 @@
跟数据库交互的框架
公司约定用mybatis-plus为核心框架如无特殊情况不允许切换dao框架

View File

@ -0,0 +1 @@
数据库api模块

View File

@ -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>

View File

@ -0,0 +1,43 @@
package cn.stylefeng.roses.kernel.db.api;
import java.util.Set;
/**
* apisql
*
* @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);
}

View File

@ -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";
}

View File

@ -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";
}

View File

@ -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