This commit is contained in:
2025-11-28 16:17:55 +08:00
commit 3b097b5f63
480 changed files with 117778 additions and 0 deletions
+21
View File
@@ -0,0 +1,21 @@
# http://editorconfig.org
root = true
# 空格替代Tab缩进在各种编辑工具下效果一致
[*]
indent_style = space
indent_size = 4
charset = utf-8
end_of_line = lf
trim_trailing_whitespace = true
insert_final_newline = true
[*.java]
indent_style = tab
[*.{json,yml}]
indent_size = 2
[*.md]
insert_final_newline = false
trim_trailing_whitespace = false
+27
View File
@@ -0,0 +1,27 @@
# maven #
target
logs
# windows #
Thumbs.db
# Mac #
.DS_Store
# eclipse #
.settings
.project
.classpath
.log
*.class
# idea #
.idea
*.iml
# Package Files #
*.jar
*.war
*.ear
/target
+15
View File
@@ -0,0 +1,15 @@
FROM bladex/alpine-java:openjdk17_cn_slim
LABEL maintainer="bladejava@qq.com"
RUN mkdir -p /blade
WORKDIR /blade
EXPOSE 8800
COPY ./target/blade-api.jar ./app.jar
ENTRYPOINT ["java", "--add-opens", "java.base/java.lang=ALL-UNNAMED", "--add-opens", "java.base/java.lang.reflect=ALL-UNNAMED", "-Djava.security.egd=file:/dev/./urandom", "-jar", "app.jar"]
CMD ["--spring.profiles.active=test"]
+35
View File
@@ -0,0 +1,35 @@
BladeX商业授权许可协议
一、 知识产权:
BladeX系列产品知识产权归上海布雷德科技有限公司独立所有
二、 许可:
1. 在您完全接受并遵守本协议的基础上,本协议授予您使用BladeX的某些权利和非独占性许可。
2. 本协议中,将本产品使用用途分为"专业版用途"和"企业版用途"。
3. "专业版用途"定义:指个人在非团体机构中出于任何合法目的使用本产品(任何目的包括商业目的或非盈利目的)。
4. "企业版用途"定义:指拥有合法执照的团体机构(例如公司企业、政府、学校、军队、医院、社会团体等各类组织)(不包含集团,若集团使用则需为各个子公司分别购买企业授权)出于任何合法目的使用本产品(任何目的包括商业目的或非盈利目的)。
5. 若您不能以拥有合法执照的团体机构名义购买企业版,则视为个人名义购买,仅可行使专业版用途。在遵守此协议的前提下,后续有一次机会将企业版授权免费绑定至法人为购买人的新公司,并从专业版用途转为企业版用途。
三、 约束和限制:
1. 本产品只能由您为本协议许可的目的而使用,您不得透露给任何第三方;
2. 从本产品取得的任何信息、软件、产品或服务,您不得对其进行修改、改编或基于以上内容创建同种类别的衍生产品并售卖。
3. 您不得对本产品以及与之关联的商业授权进行发布、出租、销售、分销、抵押、转让、许可或发放子许可证。
4. 本产品商业授权版可能包含一些独立功能或特性,这些功能只有在您购买商业授权后才可以使用。在未取得商业授权的情况下,您不得使用、尝试使用或复制这些授权版独立功能。
5. 若您的客户要求以源码方式交付软件,需缴纳企业版授权费用,否则本产品部分不得提供源码。
四、 不得用于非法或禁止的用途:
您在使用本产品或服务时,不得将本产品产品或服务用于任何非法用途或本协议条款、条件和声明禁止的用途。
五、 免责说明:
1. 本产品按"现状"授予许可,您须自行承担使用本产品的风险。BladeX团队不对此提供任何明示、暗示或任何其它形式的担保和表示。在任何情况下,对于因使用或无法使用本软件而导致的任何损失(包括但不仅限于商业利润损失、业务中断或业务信息丢失),BladeX团队无需向您或任何第三方负责,即使BladeX团队已被告知可能会造成此类损失。在任何情况下, BladeX团队均不就任何直接的、间接的、附带的、后果性的、特别的、惩戒性的和处罚性的损害赔偿承担任何责任,无论该主张是基于保证、合同、侵权(包括疏忽)或是基于其他原因作出。
2. 本产品可能内置有第三方服务,您应自行评估使用这些第三方服务的风险,由使用此类第三方服务而产生的纠纷,全部责任由您自行承担。
3. BladeX团队不对使用本产品构建的网站中任何信息内容以及导致的任何版权纠纷、法律争议和后果承担任何责任,全部责任由您自行承担。
4. BladeX团队可能会经常提供产品更新或升级,但BladeX团队没有为根据本协议许可的产品提供维护或更新的责任。
5. BladeX团队可能会按照官方制定的答疑规则为您进行答疑,但BladeX团队没有为根据本协议许可的产品提供技术支持的义务或责任。
六、 权利和所有权的保留:
BladeX团队保留所有未在本协议中明确授予您的所有权利。BladeX团队保留随时更新本协议的权利,并只需公示于对应产品项目的LICENSE文件,无需征得您的事先同意且无需另行通知,更新后的内容应于公示即时生效。您可以随时访问产品地址并查阅最新版许可条款,在更新生效后您继续使用本产品则被视作您已接受了新的条款。
七、 协议终止
1. 您一旦开始复制、下载、安装或者使用本产品,即被视为完全理解并接受本协议的各项条款,在享有上述条款授予的许可权力同时,也受到相关的约束和限制,本协议许可范围以外的行为,将直接违反本协议并构成侵权。
2. 一旦您违反本协议的条款,BladeX团队随时可能终止本协议、收回许可和授权,并要求您承担相应法律和经济责任。
+43
View File
@@ -0,0 +1,43 @@
## 版权声明
* BladeX是一个商业化软件,系列产品知识产权归**上海布雷德科技有限公司**独立所有
* 您一旦开始复制、下载、安装或者使用本产品,即被视为完全理解并接受本协议的各项条款
* 更多详情请看:[BladeX商业授权许可协议](https://license.bladex.cn)
## 答疑流程
>1. 遇到问题或Bug
>2. 业务型问题打断点调试尝试找出问题所在
>3. 系统型问题通过百度、谷歌、社区查找解决方案
>4. 未解决问题则进入技术社区进行发帖提问:[https://sns.bladex.cn](https://sns.bladex.cn)
>5. 将帖子地址发至商业群,特别简单三言两语就能描述清楚的也可在答疑时间内发至商业群提问
>6. 发帖的时候一定要描述清楚,详细描述遇到问题的**重现步骤**、**报错详细信息**、**相关代码与逻辑**、**使用软件版本**以及**操作系统版本**,否则随意发帖提问将会提高我们的答疑难度。
## 答疑时间
* 工作日:9:00 ~ 17:00 提供答疑,周末、节假日休息,暂停答疑
* 请勿**私聊提问**,以免被其他用户的消息覆盖从而无法获得答疑
* 答疑时间外遇到问题可以将问题发帖至[技术社区](https://sns.bladex.cn),我们后续会逐个回复
## 授权范围
* 专业版:只可用于**个人学习**及**个人私活**项目,不可用于公司或团队,不可泄露给任何第三方
* 企业版:可用于**企业名下**的任何项目,企业版员工在**未购买**专业版授权前,只授权开发**所在授权企业名下**的项目,**不得将BladeX用于个人私活**
* 共同遵守:若甲方需要您提供项目源码,则需代为甲方购买BladeX企业授权,甲方购买后续的所有项目都无需再次购买授权
## 商用权益
* ✔️ 遵守[商业协议](https://license.bladex.cn)的前提下,将BladeX系列产品用于授权范围内的商用项目,并上线运营
* ✔️ 遵守[商业协议](https://license.bladex.cn)的前提下,不限制项目数,不限制服务器数
* ✔️ 遵守[商业协议](https://license.bladex.cn)的前提下,将自行编写的业务代码申请软件著作权
## 何为侵权
* ❌ 不遵守商业协议,私自销售商业源码
* ❌ 以任何理由将BladeX源码用于申请软件著作权
* ❌ 将商业源码以任何途径任何理由泄露给未授权的单位或个人
* ❌ 开发完毕项目,没有为甲方购买企业授权,向甲方提供了BladeX代码
* ❌ 基于BladeX拓展研发与BladeX有竞争关系的衍生框架,并将其开源或销售
## 侵权后果
* 情节较轻:第一次发现警告处理
* 情节较重:封禁账号,踢出商业群,并保留追究法律责任的权利
* 情节严重:与本地律师事务所合作,以公司名义起诉侵犯计算机软件著作权
## 举报有奖
* 向官方提供有用线索并成功捣毁盗版个人或窝点,将会看成果给予 500~10000 不等的现金奖励
* 官方唯一指定QQ1272154962
+3668
View File
File diff suppressed because it is too large Load Diff
+27
View File
@@ -0,0 +1,27 @@
## 一、调整内存:max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]elasticsearch用户拥有的内存权限太小,至少需要262144)
#### 1.修改配置sysctl.conf
[root@localhost ~]# vi /etc/sysctl.conf
#### 2.添加下面配置:
vm.max_map_count=262144
#### 3.重新加载:
[root@localhost ~]# sysctl -p
#### 4.最后重新启动elasticsearch,即可启动成功。
## 二、Docker 命令自动补全
#### 1.安装依赖工具bash-complete
[root@localhost ~]# yum install -y bash-completion
[root@localhost ~]# source /usr/share/bash-completion/completions/docker
[root@localhost ~]# source /usr/share/bash-completion/bash_completion
## 三、将本文件夹内的文件拷贝至服务器
#### 1.对sh脚本赋予执行权限
#### 2.执行 ./deploy.sh
#### 3.等待服务启动完毕即可
#### 4.卸载执行 ./undeploy.sh
+88
View File
@@ -0,0 +1,88 @@
#./bin/bash
# 定义颜色
BLUE_COLOR="\033[36m"
RED_COLOR="\033[31m"
GREEN_COLOR="\033[32m"
VIOLET_COLOR="\033[35m"
RES="\033[0m"
echo -e "${BLUE_COLOR}# ######################################################################${RES}"
echo -e "${BLUE_COLOR}# Docker ELK Deploy Script #${RES}"
echo -e "${BLUE_COLOR}# ######################################################################${RES}"
# 创建目录
echo -e "${BLUE_COLOR}---> create [elasticsearch]directory start.${RES}"
if [ ! -d "./elasticsearch/" ]; then
mkdir -p ./elasticsearch/master/conf ./elasticsearch/master/data ./elasticsearch/master/logs \
./elasticsearch/slave1/conf ./elasticsearch/slave1/data ./elasticsearch/slave1/logs \
./elasticsearch/slave2/conf ./elasticsearch/slave2/data ./elasticsearch/slave2/logs
fi
echo -e "${RED_COLOR}---> create [kibana]directory start.${RES}"
if [ ! -d "./kibana/" ]; then
mkdir -p ./kibana/conf ./kibana/logs
fi
echo -e "${GREEN_COLOR}---> create [logstash]directory start.${RES}"
if [ ! -d "./logstash/" ]; then
mkdir -p ./logstash/conf ./logstash/logs
fi
echo -e "${GREEN_COLOR}---> create [filebeat]directory start.${RES}"
if [ ! -d "./filebeat/" ]; then
mkdir -p ./filebeat/conf ./filebeat/logs ./filebeat/data
fi
echo -e "${VIOLET_COLOR}---> create [nginx]directory start.${RES}"
if [ ! -d "./nginx/" ]; then
mkdir -p ./nginx/conf ./nginx/logs ./nginx/www
fi
echo -e "${BLUE_COLOR}===> create directory success.${RES}"
# 目录授权(data/logs 都要授读/写权限)
echo -e "${BLUE_COLOR}---> directory authorize start.${RES}"
if [ -d "./elasticsearch/" ]; then
chmod 777 ./elasticsearch/master/data/ ./elasticsearch/master/logs/ \
./elasticsearch/slave1/data/ ./elasticsearch/slave1/logs/ \
./elasticsearch/slave2/data/ ./elasticsearch/slave2/logs
fi
if [ -d "./filebeat/" ]; then
chmod 777 ./filebeat/data/ ./filebeat/logs/
fi
echo -e "${BLUE_COLOR}===> directory authorize success.${RES}"
# 移动配置文件
echo -e "${BLUE_COLOR}---> move [elasticsearch]config file start.${RES}"
if [ -f "./es-master.yml" ] && [ -f "./es-slave1.yml" ] && [ -f "./es-slave2.yml" ]; then
mv ./es-master.yml ./elasticsearch/master/conf
mv ./es-slave1.yml ./elasticsearch/slave1/conf
mv ./es-slave2.yml ./elasticsearch/slave2/conf
fi
echo -e "${RED_COLOR}---> move [kibana]config file start.${RES}"
if [ -f "./kibana.yml" ]; then
mv ./kibana.yml ./kibana/conf
fi
echo -e "${GREEN_COLOR}---> move [logstash]config file start.${RES}"
if [ -f "./logstash.yml" ] && [ -f "./logstash-filebeat.conf" ]; then
mv ./logstash-filebeat.conf ./logstash/conf
mv ./logstash.yml ./logstash/conf
fi
echo -e "${GREEN_COLOR}---> move [filebeat]config file start.${RES}"
if [ -f "./filebeat.yml" ]; then
mv ./filebeat.yml ./filebeat/conf
fi
echo -e "${VIOLET_COLOR}---> move [nginx]config file start.${RES}"
if [ -f "./nginx.conf" ]; then
mv ./nginx.conf ./nginx/conf
fi
echo -e "${BLUE_COLOR}===> move config files success.${RES}"
echo -e "${GREEN_COLOR}>>>>>>>>>>>>>>>>>> The End <<<<<<<<<<<<<<<<<<${RES}"
# 部署项目
echo -e "${BLUE_COLOR}==================> Docker deploy Start <==================${RES}"
docker-compose up --build -d
+115
View File
@@ -0,0 +1,115 @@
version: "3"
services:
es-master:
container_name: es-master
hostname: es-master
image: elasticsearch:7.1.1
restart: always
ports:
- 9200:9200
- 9300:9300
volumes:
- ./elasticsearch/master/conf/es-master.yml:/usr/share/elasticsearch/config/elasticsearch.yml
- ./elasticsearch/master/data:/usr/share/elasticsearch/data
- ./elasticsearch/master/logs:/usr/share/elasticsearch/logs
environment:
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
es-slave1:
container_name: es-slave1
image: elasticsearch:7.1.1
restart: always
ports:
- 9201:9200
- 9301:9300
volumes:
- ./elasticsearch/slave1/conf/es-slave1.yml:/usr/share/elasticsearch/config/elasticsearch.yml
- ./elasticsearch/slave1/data:/usr/share/elasticsearch/data
- ./elasticsearch/slave1/logs:/usr/share/elasticsearch/logs
environment:
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
es-slave2:
container_name: es-slave2
image: elasticsearch:7.1.1
restart: always
ports:
- 9202:9200
- 9302:9300
volumes:
- ./elasticsearch/slave2/conf/es-slave2.yml:/usr/share/elasticsearch/config/elasticsearch.yml
- ./elasticsearch/slave2/data:/usr/share/elasticsearch/data
- ./elasticsearch/slave2/logs:/usr/share/elasticsearch/logs
environment:
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
es-head:
container_name: es-head
image: mobz/elasticsearch-head:5
restart: always
ports:
- 9100:9100
depends_on:
- es-master
- es-slave1
- es-slave2
kibana:
container_name: kibana
hostname: kibana
image: kibana:7.1.1
restart: always
ports:
- 5601:5601
volumes:
- ./kibana/conf/kibana.yml:/usr/share/kibana/config/kibana.yml
environment:
- elasticsearch.hosts=http://es-master:9200
depends_on:
- es-master
- es-slave1
- es-slave2
filebeat:
# 容器名称
container_name: filebeat
# 主机名称
hostname: filebeat
# 镜像
image: docker.elastic.co/beats/filebeat:7.1.1
# 重启机制
restart: always
# 持久化挂载
volumes:
- ./filebeat/conf/filebeat.yml:/usr/share/filebeat/filebeat.yml
# 映射到容器中[作为数据源]
- ./logs:/home/project/elk/logs
- ./filebeat/logs:/usr/share/filebeat/logs
- ./filebeat/data:/usr/share/filebeat/data
# 将指定容器连接到当前连接,可以设置别名,避免ip方式导致的容器重启动态改变的无法连接情况
links:
- logstash
ports:
- 9000:9000
# 依赖服务[可无]
depends_on:
- es-master
- es-slave1
- es-slave2
logstash:
container_name: logstash
hostname: logstash
image: logstash:7.1.1
command: logstash -f ./conf/logstash-filebeat.conf
restart: always
volumes:
# 映射到容器中
- ./logstash/conf/logstash-filebeat.conf:/usr/share/logstash/conf/logstash-filebeat.conf
- ./logstash/conf/logstash.yml:/usr/share/logstash/config/logstash.yml
ports:
- 5044:5044
depends_on:
- es-master
- es-slave1
- es-slave2
+28
View File
@@ -0,0 +1,28 @@
# 集群名称
cluster.name: es-cluster
# 节点名称
node.name: es-master
# 是否可以成为master节点
node.master: true
# 是否允许该节点存储数据,默认开启
node.data: false
# 网络绑定
network.host: 0.0.0.0
# 设置对外服务的http端口
http.port: 9200
# 设置节点间交互的tcp端口
transport.port: 9300
# 集群发现
discovery.seed_hosts:
- es-master
- es-slave1
- es-slave2
# 手动指定可以成为 mater 的所有节点的 name 或者 ip,这些配置将会在第一次选举中进行计算
cluster.initial_master_nodes:
- es-master
# 支持跨域访问
http.cors.enabled: true
http.cors.allow-origin: "*"
# 安全认证
xpack.security.enabled: false
#http.cors.allow-headers: "Authorization"
+28
View File
@@ -0,0 +1,28 @@
# 集群名称
cluster.name: es-cluster
# 节点名称
node.name: es-slave1
# 是否可以成为master节点
node.master: true
# 是否允许该节点存储数据,默认开启
node.data: true
# 网络绑定
network.host: 0.0.0.0
# 设置对外服务的http端口
http.port: 9201
# 设置节点间交互的tcp端口
#transport.port: 9301
# 集群发现
discovery.seed_hosts:
- es-master
- es-slave1
- es-slave2
# 手动指定可以成为 mater 的所有节点的 name 或者 ip,这些配置将会在第一次选举中进行计算
cluster.initial_master_nodes:
- es-master
# 支持跨域访问
http.cors.enabled: true
http.cors.allow-origin: "*"
# 安全认证
xpack.security.enabled: false
#http.cors.allow-headers: "Authorization"
+28
View File
@@ -0,0 +1,28 @@
# 集群名称
cluster.name: es-cluster
# 节点名称
node.name: es-slave2
# 是否可以成为master节点
node.master: true
# 是否允许该节点存储数据,默认开启
node.data: true
# 网络绑定
network.host: 0.0.0.0
# 设置对外服务的http端口
http.port: 9202
# 设置节点间交互的tcp端口
#transport.port: 9302
# 集群发现
discovery.seed_hosts:
- es-master
- es-slave1
- es-slave2
# 手动指定可以成为 mater 的所有节点的 name 或者 ip,这些配置将会在第一次选举中进行计算
cluster.initial_master_nodes:
- es-master
# 支持跨域访问
http.cors.enabled: true
http.cors.allow-origin: "*"
# 安全认证
xpack.security.enabled: false
#http.cors.allow-headers: "Authorization"
+37
View File
@@ -0,0 +1,37 @@
filebeat.inputs:
- type: log
enabled: true
paths:
# 当前目录下的所有.log文件
- /home/project/elk/logs/*.log
multiline.pattern: ^\[
multiline.negate: true
multiline.match: after
- type: tcp
enabled: true
max_message_size: 10MiB
host: "0.0.0.0:9000"
filebeat.config.modules:
path: ${path.config}/modules.d/*.yml
reload.enabled: false
setup.template.settings:
index.number_of_shards: 1
setup.dashboards.enabled: false
setup.kibana:
host: "http://kibana:5601"
# 不直接传输至ES
#output.elasticsearch:
# hosts: ["http://es-master:9200"]
# index: "filebeat-%{[beat.version]}-%{+yyyy.MM.dd}"
output.logstash:
hosts: ["logstash:5044"]
processors:
- add_host_metadata: ~
- add_cloud_metadata: ~
+8
View File
@@ -0,0 +1,8 @@
# 服务端口
server.port: 5601
# 服务IP
server.host: "0.0.0.0"
# ES
elasticsearch.hosts: ["http://es-master:9200"]
# 汉化
i18n.locale: "zh-CN"
@@ -0,0 +1,23 @@
input {
# 来源beats
beats {
# 端口
port => "5044"
}
}
# 分析、过滤插件,可以多个
filter {
grok {
match => { "message" => "%{COMBINEDAPACHELOG}"}
}
geoip {
source => "clientip"
}
}
output {
# 选择elasticsearch
elasticsearch {
hosts => ["http://es-master:9200"]
index => "%{[@metadata][beat]}-%{[@metadata][version]}-%{+YYYY.MM.dd}"
}
}
+8
View File
@@ -0,0 +1,8 @@
# 服务IP
http.host: "0.0.0.0"
# ES
xpack.monitoring.elasticsearch.hosts: [ "http://es-master:9200" ]
xpack.monitoring.enabled: true
xpack.management.enabled: false
+16
View File
@@ -0,0 +1,16 @@
#./bin/bash
# 定义颜色
BLUE_COLOR="\033[36m"
RED_COLOR="\033[31m"
GREEN_COLOR="\033[32m"
VIOLET_COLOR="\033[35m"
RES="\033[0m"
echo -e "${BLUE_COLOR}# ######################################################################${RES}"
echo -e "${BLUE_COLOR}# Docker ELK UnDeploy Script #${RES}"
echo -e "${BLUE_COLOR}# ######################################################################${RES}"
# 部署项目
echo -e "${BLUE_COLOR}==================> Docker UnDeploy Start <==================${RES}"
docker-compose stop
docker-compose rm
+1
View File
@@ -0,0 +1 @@
java -Xms1024m -Xmx1024m -jar app.jar
+76
View File
@@ -0,0 +1,76 @@
#!/bin/bash
#设置jar文件名
APP_NAME=app.jar
#使用说明,用来提示输入参数
usage() {
echo "Usage: sh 执行脚本.sh [start|stop|restart|status]"
exit 1
}
#检查程序是否在运行
is_exist(){
pid=`ps -ef|grep $APP_NAME|grep -v grep|awk '{print $2}' `
#如果不存在返回1,存在返回0
if [ -z "${pid}" ]; then
return 1
else
return 0
fi
}
#启动方法
start(){
is_exist
if [ $? -eq "0" ]; then
echo "${APP_NAME} is already running. pid=${pid} ."
else
nohup java -Xms1024m -Xmx1024m -jar $APP_NAME > /dev/null 2>&1 &
fi
}
#停止方法
stop(){
is_exist
if [ $? -eq "0" ]; then
kill -9 $pid
else
echo "${APP_NAME} is not running"
fi
}
#输出运行状态
status(){
is_exist
if [ $? -eq "0" ]; then
echo "${APP_NAME} is running. Pid is ${pid}"
else
echo "${APP_NAME} is NOT running."
fi
}
#重启
restart(){
stop
start
}
#根据输入参数,选择执行对应方法,不输入则执行使用说明
case "$1" in
"start")
start
;;
"stop")
stop
;;
"status")
status
;;
"restart")
restart
;;
*)
usage
;;
esac
+687
View File
@@ -0,0 +1,687 @@
Table: blade_attach(附件表)
| Field | Type | Null | Key | Default | Remarks |
| ------------- | ------------- | ---- | --- | ------- | ------- |
| id | BIGINT(20) | NO | PRI | | 主键 |
| tenant_id | VARCHAR(12) | YES | | 000000 | 租户ID |
| link | VARCHAR(1000) | YES | | | 附件地址 |
| domain_url | VARCHAR(500) | YES | | | 附件域名 |
| name | VARCHAR(500) | YES | | | 附件名称 |
| original_name | VARCHAR(500) | YES | | | 附件原名 |
| extension | VARCHAR(12) | YES | | | 附件拓展名 |
| attach_size | BIGINT(20) | YES | | | 附件大小 |
| create_user | BIGINT(20) | YES | | | 创建人 |
| create_dept | BIGINT(20) | YES | | | 创建部门 |
| create_time | DATETIME(19) | YES | | | 创建时间 |
| update_user | BIGINT(20) | YES | | | 修改人 |
| update_time | DATETIME(19) | YES | | | 修改时间 |
| status | INT(10) | YES | | | 状态 |
| is_deleted | INT(10) | YES | | | 是否已删除 |
Table: blade_client(客户端表)
| Field | Type | Null | Key | Default | Remarks |
| ----------------------- | ------------- | ---- | --- | ------- | ------- |
| id | BIGINT(20) | NO | PRI | | 主键 |
| client_id | VARCHAR(48) | NO | | | 客户端id |
| client_secret | VARCHAR(256) | NO | | | 客户端密钥 |
| resource_ids | VARCHAR(256) | YES | | | 资源集合 |
| scope | VARCHAR(256) | NO | | | 授权范围 |
| authorized_grant_types | VARCHAR(256) | NO | | | 授权类型 |
| web_server_redirect_uri | VARCHAR(256) | YES | | | 回调地址 |
| authorities | VARCHAR(256) | YES | | | 权限 |
| access_token_validity | INT(10) | NO | | | 令牌过期秒数 |
| refresh_token_validity | INT(10) | NO | | | 刷新令牌过期秒数|
| additional_information | VARCHAR(4096) | YES | | | 附加说明 |
| autoapprove | VARCHAR(256) | YES | | | 自动授权 |
| create_user | BIGINT(20) | YES | | | 创建人 |
| create_dept | BIGINT(20) | YES | | | 创建部门 |
| create_time | DATETIME(19) | YES | | | 创建时间 |
| update_user | BIGINT(20) | YES | | | 修改人 |
| update_time | DATETIME(19) | YES | | | 修改时间 |
| status | INT(10) | NO | | | 状态 |
| is_deleted | INT(10) | NO | | | 是否已删除 |
Table: blade_code(代码生成表)
| Field | Type | Null | Key | Default | Remarks |
| ------------- | ------------- | ---- | --- | ------- | ------- |
| id | BIGINT(20) | NO | PRI | | 主键 |
| model_id | BIGINT(20) | YES | | | 数据模型主键 |
| service_name | VARCHAR(64) | YES | | | 服务名称 |
| code_name | VARCHAR(64) | YES | | | 模块名称 |
| table_name | VARCHAR(64) | YES | | | 表名 |
| table_prefix | VARCHAR(64) | YES | | | 表前缀 |
| pk_name | VARCHAR(32) | YES | | | 主键名 |
| package_name | VARCHAR(500) | YES | | | 后端包名 |
| template_type | VARCHAR(32) | YES | | | 模版类型 |
| author | VARCHAR(32) | YES | | | 作者信息 |
| sub_model_id | VARCHAR(32) | YES | | | 子表模型主键 |
| sub_fk_id | VARCHAR(32) | YES | | | 子表绑定外键 |
| tree_id | VARCHAR(32) | YES | | | 树主键字段 |
| tree_pid | VARCHAR(32) | YES | | | 树父主键字段 |
| tree_name | VARCHAR(64) | YES | | | 树名称字段 |
| base_mode | INT(10) | YES | | | 基础业务模式 |
| wrap_mode | INT(10) | YES | | | 包装器模式 |
| feign_mode | INT(10) | YES | | | 远程调用模式 |
| code_style | VARCHAR(32) | YES | | | 代码风格 |
| api_path | VARCHAR(2000) | YES | | | 后端路径 |
| web_path | VARCHAR(2000) | YES | | | 前端路径 |
| is_deleted | INT(10) | YES | | 0 | 是否已删除 |
Table: blade_datasource(数据源配置表)
| Field | Type | Null | Key | Default | Remarks |
|-----------------|--------------| ---- | --- | ------- |------------|
| id | BIGINT(20) | NO | PRI | | 主键 |
| category | INT(2) | YES | | | 数据源类型 |
| name | VARCHAR(100) | YES | | | 名称 |
| driver_class | VARCHAR(100) | YES | | | 驱动类 |
| url | VARCHAR(500) | YES | | | 连接地址 |
| username | VARCHAR(50) | YES | | | 用户名 |
| password | VARCHAR(50) | YES | | | 密码 |
| sharding_config | LONGTEXT | YES | | | 分库分表配置 |
| remark | VARCHAR(255) | YES | | | 备注 |
| create_user | BIGINT(20) | YES | | | 创建人 |
| create_dept | BIGINT(20) | YES | | | 创建部门 |
| create_time | DATETIME(19) | YES | | | 创建时间 |
| update_user | BIGINT(20) | YES | | | 修改人 |
| update_time | DATETIME(19) | YES | | | 修改时间 |
| status | INT(10) | YES | | | 状态 |
| is_deleted | INT(10) | YES | | | 是否已删除 |
Table: blade_dept(机构表)
| Field | Type | Null | Key | Default | Remarks |
| ------------- | ------------- | ---- | --- | ------- | ------- |
| id | BIGINT(20) | NO | PRI | | 主键 |
| tenant_id | VARCHAR(12) | YES | | 000000 | 租户ID |
| parent_id | BIGINT(20) | YES | | 0 | 父主键 |
| ancestors | VARCHAR(2000) | YES | | | 祖级列表 |
| dept_category | INT(10) | YES | | | 部门类型 |
| dept_name | VARCHAR(45) | YES | | | 部门名 |
| full_name | VARCHAR(45) | YES | | | 部门全称 |
| sort | INT(10) | YES | | | 排序 |
| remark | VARCHAR(255) | YES | | | 备注 |
| is_deleted | INT(10) | YES | | 0 | 是否已删除 |
Table: blade_dict(字典表)
| Field | Type | Null | Key | Default | Remarks |
| ---------- | ------------ | ---- | --- | ------- | ------- |
| id | BIGINT(20) | NO | PRI | | 主键 |
| parent_id | BIGINT(20) | YES | | 0 | 父主键 |
| code | VARCHAR(255) | YES | | | 字典码 |
| dict_key | VARCHAR(255) | YES | | | 字典值 |
| dict_value | VARCHAR(255) | YES | | | 字典名称 |
| sort | INT(10) | YES | | | 排序 |
| remark | VARCHAR(255) | YES | | | 字典备注 |
| is_sealed | INT(10) | YES | | 0 | 是否已封存 |
| is_deleted | INT(10) | YES | | 0 | 是否已删除 |
Table: blade_dict_biz(业务字典表)
| Field | Type | Null | Key | Default | Remarks |
| ---------- | ------------ | ---- | --- | ------- | ------- |
| id | BIGINT(20) | NO | PRI | | 主键 |
| tenant_id | VARCHAR(12) | YES | | | 租户ID |
| parent_id | BIGINT(20) | YES | | 0 | 父主键 |
| code | VARCHAR(255) | YES | | | 字典码 |
| dict_key | VARCHAR(255) | YES | | | 字典值 |
| dict_value | VARCHAR(255) | YES | | | 字典名称 |
| sort | INT(10) | YES | | | 排序 |
| remark | VARCHAR(255) | YES | | | 字典备注 |
| is_sealed | INT(10) | YES | | 0 | 是否已封存 |
| is_deleted | INT(10) | YES | | 0 | 是否已删除 |
Table: blade_log_api(接口日志表)
| Field | Type | Null | Key | Default | Remarks |
| ------------ | ------------- | ---- | --- | ----------------- | ------- |
| id | BIGINT(20) | NO | PRI | | 编号 |
| tenant_id | VARCHAR(12) | YES | | 000000 | 租户ID |
| service_id | VARCHAR(32) | YES | | | 服务ID |
| server_host | VARCHAR(255) | YES | | | 服务器名 |
| server_ip | VARCHAR(255) | YES | | | 服务器IP地址 |
| env | VARCHAR(255) | YES | | | 服务器环境 |
| type | CHAR(1) | YES | | 1 | 日志类型 |
| title | VARCHAR(255) | YES | | | 日志标题 |
| method | VARCHAR(10) | YES | | | 操作方式 |
| request_uri | VARCHAR(255) | YES | | | 请求URI |
| user_agent | VARCHAR(1000) | YES | | | 用户代理 |
| remote_ip | VARCHAR(255) | YES | | | 操作IP地址 |
| method_class | VARCHAR(255) | YES | | | 方法类 |
| method_name | VARCHAR(255) | YES | | | 方法名 |
| params | TEXT(65535) | YES | | | 操作提交的数据 |
| time | VARCHAR(64) | YES | | | 执行时间 |
| create_by | VARCHAR(64) | YES | | | 创建者 |
| create_time | DATETIME(19) | YES | | CURRENT_TIMESTAMP | 创建时间 |
Table: blade_log_error(错误日志表)
| Field | Type | Null | Key | Default | Remarks |
| -------------- | ------------- | ---- | --- | ----------------- | ------- |
| id | BIGINT(20) | NO | PRI | | 编号 |
| tenant_id | VARCHAR(12) | YES | | 000000 | 租户ID |
| service_id | VARCHAR(32) | YES | | | 服务ID |
| server_host | VARCHAR(255) | YES | | | 服务器名 |
| server_ip | VARCHAR(255) | YES | | | 服务器IP地址 |
| env | VARCHAR(255) | YES | | | 系统环境 |
| method | VARCHAR(10) | YES | | | 操作方式 |
| request_uri | VARCHAR(255) | YES | | | 请求URI |
| user_agent | VARCHAR(1000) | YES | | | 用户代理 |
| stack_trace | TEXT(65535) | YES | | | 堆栈 |
| exception_name | VARCHAR(255) | YES | | | 异常名 |
| message | TEXT(65535) | YES | | | 异常信息 |
| line_number | INT(10) | YES | | | 错误行数 |
| remote_ip | VARCHAR(255) | YES | | | 操作IP地址 |
| method_class | VARCHAR(255) | YES | | | 方法类 |
| file_name | VARCHAR(1000) | YES | | | 文件名 |
| method_name | VARCHAR(255) | YES | | | 方法名 |
| params | TEXT(65535) | YES | | | 操作提交的数据 |
| create_by | VARCHAR(64) | YES | | | 创建者 |
| create_time | DATETIME(19) | YES | | CURRENT_TIMESTAMP | 创建时间 |
Table: blade_log_usual(通用日志表)
| Field | Type | Null | Key | Default | Remarks |
| ------------ | ------------- | ---- | --- | ----------------- | ------- |
| id | BIGINT(20) | NO | PRI | | 编号 |
| tenant_id | VARCHAR(12) | YES | | 000000 | 租户ID |
| service_id | VARCHAR(32) | YES | | | 服务ID |
| server_host | VARCHAR(255) | YES | | | 服务器名 |
| server_ip | VARCHAR(255) | YES | | | 服务器IP地址 |
| env | VARCHAR(255) | YES | | | 系统环境 |
| log_level | VARCHAR(10) | YES | | | 日志级别 |
| log_id | VARCHAR(100) | YES | | | 日志业务id |
| log_data | TEXT(65535) | YES | | | 日志数据 |
| method | VARCHAR(10) | YES | | | 操作方式 |
| request_uri | VARCHAR(255) | YES | | | 请求URI |
| remote_ip | VARCHAR(255) | YES | | | 操作IP地址 |
| method_class | VARCHAR(255) | YES | | | 方法类 |
| method_name | VARCHAR(255) | YES | | | 方法名 |
| user_agent | VARCHAR(1000) | YES | | | 用户代理 |
| params | TEXT(65535) | YES | | | 操作提交的数据 |
| create_by | VARCHAR(64) | YES | | | 创建者 |
| create_time | DATETIME(19) | YES | | CURRENT_TIMESTAMP | 创建时间 |
Table: blade_menu(菜单表)
| Field | Type | Null | Key | Default | Remarks |
| ---------- | ------------ | ---- | --- | ------- | ------- |
| id | BIGINT(20) | NO | PRI | | 主键 |
| parent_id | BIGINT(20) | YES | | 0 | 父级菜单 |
| code | VARCHAR(255) | YES | | | 菜单编号 |
| name | VARCHAR(255) | YES | | | 菜单名称 |
| alias | VARCHAR(255) | YES | | | 菜单别名 |
| path | VARCHAR(255) | YES | | | 请求地址 |
| source | VARCHAR(255) | YES | | | 菜单资源 |
| sort | INT(10) | YES | | | 排序 |
| category | INT(10) | YES | | | 菜单类型 |
| action | INT(10) | YES | | 0 | 操作按钮类型 |
| is_open | INT(10) | YES | | 1 | 是否打开新页面 |
| remark | VARCHAR(255) | YES | | | 备注 |
| is_deleted | INT(10) | YES | | 0 | 是否已删除 |
Table: blade_model(数据模型表)
| Field | Type | Null | Key | Default | Remarks |
| ------------- | ------------- | ---- | --- | ------- | ------- |
| id | BIGINT(20) | NO | PRI | | 主键 |
| datasource_id | BIGINT(20) | YES | | | 数据源主键 |
| model_name | VARCHAR(50) | YES | | | 模型名称 |
| model_code | VARCHAR(50) | YES | | | 模型编号 |
| model_table | VARCHAR(100) | YES | | | 物理表名 |
| model_class | VARCHAR(100) | YES | | | 模型类名 |
| model_remark | VARCHAR(500) | YES | | | 模型备注 |
| create_user | BIGINT(20) | YES | | | 创建人 |
| create_dept | BIGINT(20) | YES | | | 创建部门 |
| create_time | DATETIME(19) | YES | | | 创建时间 |
| update_user | BIGINT(20) | YES | | | 修改人 |
| update_time | DATETIME(19) | YES | | | 修改时间 |
| status | INT(10) | YES | | | 状态 |
| is_deleted | INT(10) | YES | | | 是否已删除 |
Table: blade_model_prototype(数据原型表)
| Field | Type | Null | Key | Default | Remarks |
| -------------- | ------------- | ---- | --- | ------- | ------- |
| id | BIGINT(20) | NO | PRI | | 主键 |
| model_id | BIGINT(20) | YES | | | 模型主键 |
| jdbc_name | VARCHAR(50) | YES | | | 物理列名 |
| jdbc_type | VARCHAR(20) | YES | | | 物理类型 |
| jdbc_comment | VARCHAR(500) | YES | | | 注释说明 |
| property_type | VARCHAR(20) | YES | | | 实体类型 |
| property_entity| VARCHAR(500) | YES | | | 实体类型引用|
| property_name | VARCHAR(50) | YES | | | 实体列名 |
| is_list | INT(10) | YES | | | 列表显示 |
| is_form | INT(10) | YES | | | 表单显示 |
| is_row | INT(10) | YES | | | 独占一行 |
| component_type | VARCHAR(50) | YES | | | 组件类型 |
| dict_code | VARCHAR(50) | YES | | | 字典编码 |
| is_required | INT(10) | YES | | | 是否必填 |
| is_query | INT(10) | YES | | | 查询配置 |
| query_type | VARCHAR(50) | YES | | | 查询配置 |
| create_user | BIGINT(20) | YES | | | 创建人 |
| create_dept | BIGINT(20) | YES | | | 创建部门 |
| create_time | DATETIME(19) | YES | | | 创建时间 |
| update_user | BIGINT(20) | YES | | | 修改人 |
| update_time | DATETIME(19) | YES | | | 修改时间 |
| status | INT(10) | YES | | | 状态 |
| is_deleted | INT(10) | YES | | | 是否已删除 |
Table: blade_notice(通知公告表)
| Field | Type | Null | Key | Default | Remarks |
| ------------ | ------------- | ---- | --- | ------- | ------- |
| id | BIGINT(20) | NO | PRI | | 主键 |
| tenant_id | VARCHAR(12) | YES | | 000000 | 租户ID |
| title | VARCHAR(255) | YES | | | 标题 |
| category | INT(10) | YES | | | 类型 |
| release_time | DATETIME(19) | YES | | | 发布时间 |
| content | VARCHAR(2000) | YES | | | 内容 |
| create_user | BIGINT(20) | YES | | | 创建人 |
| create_dept | BIGINT(20) | YES | | | 创建部门 |
| create_time | DATETIME(19) | YES | | | 创建时间 |
| update_user | BIGINT(20) | YES | | | 修改人 |
| update_time | DATETIME(19) | YES | | | 修改时间 |
| status | INT(10) | YES | | | 状态 |
| is_deleted | INT(10) | YES | | | 是否已删除 |
Table: blade_oss(对象存储表)
| Field | Type | Null | Key | Default | Remarks |
|--------------------| ------------ | ---- | --- | ------- | ------- |
| id | BIGINT(20) | NO | PRI | | 主键 |
| tenant_id | VARCHAR(12) | YES | | 000000 | 租户ID |
| category | INT(10) | YES | | | 分类 |
| oss_code | VARCHAR(32) | YES | | | 资源编号 |
| endpoint | VARCHAR(255) | YES | | | 资源地址 |
| transform_endpoint | VARCHAR(255) | YES | | | 外网资源地址 |
| access_key | VARCHAR(255) | YES | | | accessKey|
| secret_key | VARCHAR(255) | YES | | | secretKey|
| bucket_name | VARCHAR(255) | YES | | | 空间名 |
| app_id | VARCHAR(255) | YES | | | 应用ID |
| region | VARCHAR(255) | YES | | | 地域简称 |
| remark | VARCHAR(255) | YES | | | 备注 |
| create_user | BIGINT(20) | YES | | | 创建人 |
| create_dept | BIGINT(20) | YES | | | 创建部门 |
| create_time | DATETIME(19) | YES | | | 创建时间 |
| update_user | BIGINT(20) | YES | | | 修改人 |
| update_time | DATETIME(19) | YES | | | 修改时间 |
| status | INT(10) | YES | | | 状态 |
| is_deleted | INT(10) | YES | | 0 | 是否已删除 |
Table: blade_param(参数表)
| Field | Type | Null | Key | Default | Remarks |
| ----------- | ------------ | ---- | --- | ------- | ------- |
| id | BIGINT(20) | NO | PRI | | 主键 |
| param_name | VARCHAR(255) | YES | | | 参数名 |
| param_key | VARCHAR(255) | YES | | | 参数键 |
| param_value | VARCHAR(255) | YES | | | 参数值 |
| remark | VARCHAR(255) | YES | | | 备注 |
| create_user | BIGINT(20) | YES | | | 创建人 |
| create_dept | BIGINT(20) | YES | | | 创建部门 |
| create_time | DATETIME(19) | YES | | | 创建时间 |
| update_user | BIGINT(20) | YES | | | 修改人 |
| update_time | DATETIME(19) | YES | | | 修改时间 |
| status | INT(10) | YES | | | 状态 |
| is_deleted | INT(10) | YES | | 0 | 是否已删除 |
Table: blade_post(岗位表)
| Field | Type | Null | Key | Default | Remarks |
| ----------- | ------------ | ---- | --- | ------- | ------- |
| id | BIGINT(20) | NO | PRI | | 主键 |
| tenant_id | VARCHAR(12) | YES | | 000000 | 租户ID |
| category | INT(10) | YES | | | 岗位类型 |
| post_code | VARCHAR(12) | YES | | | 岗位编号 |
| post_name | VARCHAR(64) | YES | | | 岗位名称 |
| sort | INT(10) | YES | | | 岗位排序 |
| remark | VARCHAR(255) | YES | | | 岗位描述 |
| create_user | BIGINT(20) | YES | | | 创建人 |
| create_dept | BIGINT(20) | YES | | | 创建部门 |
| create_time | DATETIME(19) | YES | | | 创建时间 |
| update_user | BIGINT(20) | YES | | | 修改人 |
| update_time | DATETIME(19) | YES | | | 修改时间 |
| status | INT(10) | YES | | | 状态 |
| is_deleted | INT(10) | YES | | | 是否已删除 |
Table: blade_process_leave(流程请假业务表)
| Field | Type | Null | Key | Default | Remarks |
| --------------------- | ------------ | ---- | --- | ------- | ------- |
| id | BIGINT(20) | NO | PRI | | 编号 |
| process_definition_id | VARCHAR(64) | YES | | | 流程定义主键 |
| process_instance_id | VARCHAR(64) | YES | | | 流程实例主键 |
| start_time | DATETIME(19) | YES | | | 开始时间 |
| end_time | DATETIME(19) | YES | | | 结束时间 |
| reason | VARCHAR(255) | YES | | | 请假理由 |
| task_user | VARCHAR(255) | YES | | | 第一级审批人 |
| apply_time | DATETIME(19) | YES | | | 申请时间 |
| create_user | BIGINT(20) | YES | | | 创建人 |
| create_dept | BIGINT(20) | YES | | | 创建部门 |
| create_time | DATETIME(19) | YES | | | 创建时间 |
| update_user | BIGINT(20) | YES | | | 修改人 |
| update_time | DATETIME(19) | YES | | | 修改时间 |
| status | INT(10) | YES | | | 状态 |
| is_deleted | INT(10) | YES | | | 是否已删除 |
Table: blade_region(行政区划表)
| Field | Type | Null | Key | Default | Remarks |
| ------------- | ------------ | ---- | --- | ------- | ------- |
| code | VARCHAR(12) | NO | PRI | | 区划编号 |
| parent_code | VARCHAR(12) | YES | | | 父区划编号 |
| ancestors | VARCHAR(255) | YES | | | 祖区划编号 |
| name | VARCHAR(32) | YES | | | 区划名称 |
| province_code | VARCHAR(12) | YES | | | 省级区划编号 |
| province_name | VARCHAR(32) | YES | | | 省级名称 |
| city_code | VARCHAR(12) | YES | | | 市级区划编号 |
| city_name | VARCHAR(32) | YES | | | 市级名称 |
| district_code | VARCHAR(12) | YES | | | 区级区划编号 |
| district_name | VARCHAR(32) | YES | | | 区级名称 |
| town_code | VARCHAR(12) | YES | | | 镇级区划编号 |
| town_name | VARCHAR(32) | YES | | | 镇级名称 |
| village_code | VARCHAR(12) | YES | | | 村级区划编号 |
| village_name | VARCHAR(32) | YES | | | 村级名称 |
| region_level | INT(10) | YES | | | 层级 |
| sort | INT(10) | YES | | | 排序 |
| remark | VARCHAR(255) | YES | | | 备注 |
Table: blade_report_file(报表文件表)
| Field | Type | Null | Key | Default | Remarks |
| ----------- | -------------------- | ---- | --- | ------- | ------- |
| id | BIGINT(20) | NO | PRI | | 主键 |
| name | VARCHAR(100) | NO | | | 文件名 |
| content | MEDIUMBLOB(16777215) | YES | | | 文件内容 |
| create_time | DATETIME(19) | YES | | | 创建时间 |
| update_time | DATETIME(19) | YES | | | 更新时间 |
| is_deleted | INT(10) | YES | | 0 | 是否已删除 |
Table: blade_role(角色表)
| Field | Type | Null | Key | Default | Remarks |
| ---------- | ------------ | ---- | --- | ------- | ------- |
| id | BIGINT(20) | NO | PRI | | 主键 |
| tenant_id | VARCHAR(12) | YES | | 000000 | 租户ID |
| parent_id | BIGINT(20) | YES | | 0 | 父主键 |
| role_name | VARCHAR(255) | YES | | | 角色名 |
| sort | INT(10) | YES | | | 排序 |
| role_alias | VARCHAR(255) | YES | | | 角色别名 |
| is_deleted | INT(10) | YES | | 0 | 是否已删除 |
Table: blade_role_menu(角色菜单关联表)
| Field | Type | Null | Key | Default | Remarks |
| ------- | ---------- | ---- | --- | ------- | ------- |
| id | BIGINT(20) | NO | PRI | | 主键 |
| menu_id | BIGINT(20) | YES | | | 菜单id |
| role_id | BIGINT(20) | YES | | | 角色id |
Table: blade_role_scope(角色数据权限关联表)
| Field | Type | Null | Key | Default | Remarks |
| -------------- | ---------- | ---- | --- | ------- | ------- |
| id | BIGINT(20) | NO | PRI | | 主键 |
| scope_category | INT(10) | YES | | | 权限类型(1:数据权限、2:接口权限)|
| scope_id | BIGINT(20) | YES | | | 权限id |
| role_id | BIGINT(20) | YES | | | 角色id |
Table: blade_scope_api(接口权限表)
| Field | Type | Null | Key | Default | Remarks |
| ------------- | ------------ | ---- | --- | ------- | ------- |
| id | BIGINT(20) | NO | PRI | | 主键 |
| menu_id | BIGINT(20) | YES | | | 菜单主键 |
| resource_code | VARCHAR(255) | YES | | | 资源编号 |
| scope_name | VARCHAR(255) | YES | | | 接口权限名 |
| scope_path | VARCHAR(255) | YES | | | 接口权限地址 |
| scope_type | INT(10) | YES | | | 接口权限类型 |
| remark | VARCHAR(255) | YES | | | 接口权限备注 |
| create_user | BIGINT(20) | YES | | | 创建人 |
| create_dept | BIGINT(20) | YES | | | 创建部门 |
| create_time | DATETIME(19) | YES | | | 创建时间 |
| update_user | BIGINT(20) | YES | | | 修改人 |
| update_time | DATETIME(19) | YES | | | 修改时间 |
| status | INT(10) | YES | | | 状态 |
| is_deleted | INT(10) | YES | | | 是否已删除 |
Table: blade_scope_data(数据权限表)
| Field | Type | Null | Key | Default | Remarks |
| ------------- | ------------- | ---- | --- | ------- | ------- |
| id | BIGINT(20) | NO | PRI | | 主键 |
| menu_id | BIGINT(20) | YES | | | 菜单主键 |
| resource_code | VARCHAR(255) | YES | | | 资源编号 |
| scope_name | VARCHAR(255) | YES | | | 数据权限名称 |
| scope_field | VARCHAR(255) | YES | | | 数据权限字段 |
| scope_class | VARCHAR(500) | YES | | | 数据权限类名 |
| scope_column | VARCHAR(255) | YES | | | 数据权限字段 |
| scope_type | INT(10) | YES | | | 数据权限类型 |
| scope_value | VARCHAR(2000) | YES | | | 数据权限值域 |
| remark | VARCHAR(255) | YES | | | 数据权限备注 |
| create_user | BIGINT(20) | YES | | | 创建人 |
| create_dept | BIGINT(20) | YES | | | 创建部门 |
| create_time | DATETIME(19) | YES | | | 创建时间 |
| update_user | BIGINT(20) | YES | | | 修改人 |
| update_time | DATETIME(19) | YES | | | 修改时间 |
| status | INT(10) | YES | | | 状态 |
| is_deleted | INT(10) | YES | | | 是否已删除 |
Table: blade_sms(短信配置表)
| Field | Type | Null | Key | Default | Remarks |
| ----------- | ------------ | ---- | --- | ------- | ------- |
| id | BIGINT(20) | NO | PRI | | 主键 |
| tenant_id | VARCHAR(12) | YES | | 000000 | 租户ID |
| category | INT(10) | YES | | | 分类 |
| sms_code | VARCHAR(32) | YES | | | 资源编号 |
| template_id | VARCHAR(64) | YES | | | 模板ID |
| access_key | VARCHAR(255) | YES | | | accessKey|
| secret_key | VARCHAR(255) | YES | | | secretKey|
| region_id | VARCHAR(255) | YES | | | regionId|
| sign_name | VARCHAR(64) | YES | | | 短信签名 |
| remark | VARCHAR(255) | YES | | | 备注 |
| create_user | BIGINT(20) | YES | | | 创建人 |
| create_dept | BIGINT(20) | YES | | | 创建部门 |
| create_time | DATETIME(19) | YES | | | 创建时间 |
| update_user | BIGINT(20) | YES | | | 修改人 |
| update_time | DATETIME(19) | YES | | | 修改时间 |
| status | INT(10) | YES | | | 状态 |
| is_deleted | INT(10) | YES | | 0 | 是否已删除 |
Table: blade_tenant(租户表)
| Field | Type | Null | Key | Default | Remarks |
| -------------- | ------------- | ---- | --- | ------- | ------- |
| id | BIGINT(20) | NO | PRI | | 主键 |
| tenant_id | VARCHAR(12) | YES | | 000000 | 租户ID |
| tenant_name | VARCHAR(50) | NO | | | 租户名称 |
| domain_url | VARCHAR(255) | YES | | | 域名地址 |
| background_url | VARCHAR(1000) | YES | | | 系统背景 |
| linkman | VARCHAR(20) | YES | | | 联系人 |
| contact_number | VARCHAR(20) | YES | | | 联系电话 |
| address | VARCHAR(255) | YES | | | 联系地址 |
| account_number | INT(10) | YES | | -1 | 账号额度 |
| expire_time | DATETIME(19) | YES | | | 过期时间 |
| datasource | BIGINT(20) | YES | | | 数据源ID |
| license_key | VARCHAR(1000) | YES | | | 授权码 |
| create_user | BIGINT(20) | YES | | | 创建人 |
| create_dept | BIGINT(20) | YES | | | 创建部门 |
| create_time | DATETIME(19) | YES | | | 创建时间 |
| update_user | BIGINT(20) | YES | | | 修改人 |
| update_time | DATETIME(19) | YES | | | 修改时间 |
| status | INT(10) | YES | | | 状态 |
| is_deleted | INT(10) | YES | | 0 | 是否已删除 |
Table: blade_top_menu(顶部菜单表)
| Field | Type | Null | Key | Default | Remarks |
| ----------- | ------------ | ---- | --- | ------- | ------- |
| id | BIGINT(20) | NO | PRI | | 主键 |
| tenant_id | VARCHAR(12) | YES | | | 租户id |
| code | VARCHAR(255) | YES | | | 顶部菜单编号 |
| name | VARCHAR(255) | YES | | | 顶部菜单名 |
| source | VARCHAR(255) | YES | | | 顶部菜单资源 |
| sort | INT(10) | YES | | | 顶部菜单排序 |
| create_user | BIGINT(20) | YES | | | 创建人 |
| create_dept | BIGINT(20) | YES | | | 创建部门 |
| create_time | DATETIME(19) | YES | | | 创建时间 |
| update_user | BIGINT(20) | YES | | | 修改人 |
| update_time | DATETIME(19) | YES | | | 修改时间 |
| status | INT(10) | YES | | | 状态 |
| is_deleted | INT(10) | YES | | | 是否已删除 |
Table: blade_top_menu_setting(顶部菜单配置表)
| Field | Type | Null | Key | Default | Remarks |
| ----------- | ---------- | ---- | --- | ------- | ------- |
| id | BIGINT(20) | NO | PRI | | 主键 |
| top_menu_id | BIGINT(20) | YES | | | 顶部菜单主键 |
| menu_id | BIGINT(20) | YES | | | 菜单主键 |
Table: blade_user(用户表)
| Field | Type | Null | Key | Default | Remarks |
| ----------- | ------------- | ---- | --- | ------- | ------- |
| id | BIGINT(20) | NO | PRI | | 主键 |
| tenant_id | VARCHAR(12) | YES | | 000000 | 租户ID |
| code | VARCHAR(12) | YES | | | 用户编号 |
| user_type | SMALLINT(5) | YES | | | 用户平台 |
| account | VARCHAR(45) | YES | | | 账号 |
| password | VARCHAR(45) | YES | | | 密码 |
| name | VARCHAR(20) | YES | | | 昵称 |
| real_name | VARCHAR(10) | YES | | | 真名 |
| avatar | VARCHAR(500) | YES | | | 头像 |
| email | VARCHAR(45) | YES | | | 邮箱 |
| phone | VARCHAR(45) | YES | | | 手机 |
| birthday | DATETIME(19) | YES | | | 生日 |
| sex | SMALLINT(5) | YES | | | 性别 |
| role_id | VARCHAR(1000) | YES | | | 角色id |
| dept_id | VARCHAR(1000) | YES | | | 部门id |
| post_id | VARCHAR(1000) | YES | | | 岗位id |
| create_user | BIGINT(20) | YES | | | 创建人 |
| create_dept | BIGINT(20) | YES | | | 创建部门 |
| create_time | DATETIME(19) | YES | | | 创建时间 |
| update_user | BIGINT(20) | YES | | | 修改人 |
| update_time | DATETIME(19) | YES | | | 修改时间 |
| status | INT(10) | YES | | | 状态 |
| is_deleted | INT(10) | YES | | 0 | 是否已删除 |
Table: blade_user_dept(用户部门表)
| Field | Type | Null | Key | Default | Remarks |
| ------- | ---------- | ---- | --- | ------- | ------- |
| id | BIGINT(20) | NO | PRI | | 主键 |
| user_id | BIGINT(20) | YES | | 0 | 用户ID |
| dept_id | BIGINT(20) | YES | | 0 | 部门ID |
Table: blade_user_oauth(用户第三方认证表)
| Field | Type | Null | Key | Default | Remarks |
| --------- | ------------- | ---- | --- | ------- | ------- |
| id | BIGINT(20) | NO | PRI | | 主键 |
| tenant_id | VARCHAR(12) | YES | | | 租户ID |
| uuid | VARCHAR(64) | YES | | | 第三方系统用户ID|
| user_id | BIGINT(20) | YES | | | 用户ID |
| username | VARCHAR(32) | YES | | | 账号 |
| nickname | VARCHAR(64) | YES | | | 用户名 |
| avatar | VARCHAR(1000) | YES | | | 头像 |
| blog | VARCHAR(50) | YES | | | 应用主页 |
| company | VARCHAR(255) | YES | | | 公司名 |
| location | VARCHAR(255) | YES | | | 地址 |
| email | VARCHAR(255) | YES | | | 邮件 |
| remark | VARCHAR(255) | YES | | | 备注 |
| gender | VARCHAR(16) | YES | | | 性别 |
| source | VARCHAR(16) | YES | | | 来源 |
Table: blade_user_web(用户平台拓展表)
| Field | Type | Null | Key | Default | Remarks |
| ------- | ---------- | ---- | --- | ------- | ------- |
| id | BIGINT(20) | NO | PRI | | 主键 |
| user_id | BIGINT(20) | YES | | 0 | 用户ID |
| user_ext | VARCHAR(255) | YES | | | 用户拓展信息 |
Table: blade_user_app(用户平台拓展表)
| Field | Type | Null | Key | Default | Remarks |
| ------- | ---------- | ---- | --- | ------- | ------- |
| id | BIGINT(20) | NO | PRI | | 主键 |
| user_id | BIGINT(20) | YES | | 0 | 用户ID |
| user_ext | VARCHAR(255) | YES | | | 用户拓展信息 |
Table: blade_user_other(用户平台拓展表)
| Field | Type | Null | Key | Default | Remarks |
| ------- | ---------- | ---- | --- | ------- | ------- |
| id | BIGINT(20) | NO | PRI | | 主键 |
| user_id | BIGINT(19) | YES | | 0 | 用户ID |
| user_ext | VARCHAR(255) | YES | | | 用户拓展信息 |
Table: blade_job_server (任务服务表)
| Field | Type | Null | Key | Default | Remarks |
|-------------------|--------------|------|-----|---------|----------------------|
| id | BIGINT(20) | NO | PRI | | 主键 |
| job_server_name | VARCHAR(50) | YES | | NULL | 任务服务名称 |
| job_server_url | VARCHAR(255) | YES | | NULL | 任务服务器地址 |
| job_app_name | VARCHAR(20) | YES | | NULL | 任务应用名称 |
| job_app_password | VARCHAR(100) | YES | | NULL | 任务应用密码 |
| job_remark | VARCHAR(255) | YES | | NULL | 任务备注 |
| create_user | BIGINT(20) | YES | | NULL | 创建人 |
| create_dept | BIGINT(20) | YES | | NULL | 创建部门 |
| create_time | DATETIME | YES | | NULL | 创建时间 |
| update_user | BIGINT(20) | YES | | NULL | 修改人 |
| update_time | DATETIME | YES | | NULL | 修改时间 |
| status | INT(2) | YES | | NULL | 状态 |
| is_deleted | INT(2) | YES | | 0 | 是否已删除 |
Table: blade_job_info (任务信息表)
| Field | Type | Null | Key | Default | Remarks |
|------------------------|------------------------|------|-----|---------|-----------------------------------|
| id | BIGINT(20) | NO | PRI | | 主键 |
| job_server_id | BIGINT(20) | YES | | NULL | 任务服务ID |
| job_id | BIGINT(20) | YES | | NULL | 任务 ID |
| job_name | VARCHAR(50) | YES | | NULL | 任务名称 |
| job_description | VARCHAR(255) | YES | | NULL | 任务描述 |
| job_params | TEXT | YES | | | 任务参数 |
| time_expression_type | INT(2) | YES | | NULL | 时间表达式类型 |
| time_expression | VARCHAR(255) | YES | | NULL | 时间表达式 |
| execute_type | INT(2) | YES | | NULL | 执行类型 |
| processor_type | INT(2) | YES | | NULL | 处理器类型 |
| processor_info | VARCHAR(255) | YES | | NULL | 处理器参数 |
| max_instance_num | INT(11) | YES | | NULL | 最大实例数 |
| concurrency | INT(11) | YES | | NULL | 单机线程并发数 |
| instance_time_limit | BIGINT(20) | YES | | NULL | 任务实例运行时间限制 |
| instance_retry_num | INT(11) | YES | | NULL | 任务实例重试次数 |
| task_retry_num | INT(11) | YES | | NULL | Task 重试次数 |
| min_cpu_cores | DOUBLE | YES | | NULL | 最小可用 CPU 核心数 |
| min_memory_space | DOUBLE | YES | | NULL | 最小内存大小(GB |
| min_disk_space | DOUBLE | YES | | NULL | 最小磁盘大小(GB |
| designated_workers | VARCHAR(255) | YES | | NULL | 指定机器执行 |
| max_worker_count | INT(2) | YES | | NULL | 最大执行机器数量 |
| notify_user_ids | VARCHAR(2000) | YES | | NULL | 接收报警的用户 ID 列表 |
| enable | INT(2) | YES | | NULL | 是否启用该任务 |
| dispatch_strategy | INT(2) | YES | | NULL | 调度策略 |
| lifecycle | VARCHAR(255) | YES | | NULL | 生命周期 |
| alert_threshold | INT(2) | YES | | NULL | 错误阈值 |
| statistic_window_len | INT(2) | YES | | NULL | 统计的窗口长度(s) |
| silence_window_len | INT(2) | YES | | NULL | 沉默时间窗口(s) |
| log_type | INT(2) | YES | | NULL | 日志配置 |
| log_level | INT(2) | YES | | NULL | 日志级别 |
| extra | VARCHAR(255) | YES | | NULL | 扩展字段 |
| create_user | BIGINT(20) | YES | | NULL | 创建人 |
| create_dept | BIGINT(20) | YES | | NULL | 创建部门 |
| create_time | DATETIME | YES | | NULL | 创建时间 |
| update_user | BIGINT(20) | YES | | NULL | 修改人 |
| update_time | DATETIME | YES | | NULL | 修改时间 |
| status | INT(2) | YES | | NULL | 状态 |
| is_deleted | INT(2) | YES | | 0 | 是否已删除 |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
@@ -0,0 +1,16 @@
-- -----------------------------------
-- 修改应用表的授权集合
-- -----------------------------------
UPDATE "BLADEX"."BLADE_CLIENT" SET AUTHORIZED_GRANT_TYPES = 'authorization_code,password,refresh_token,captcha,social,register' where ID < 1123598811738675203;
-- -----------------------------------
-- 删除可能重复的菜单
-- -----------------------------------
DELETE FROM "BLADEX"."BLADE_CLIENT" WHERE "CLIENT_ID" = 'saber3';
DELETE FROM "BLADEX"."BLADE_CLIENT" WHERE "CLIENT_ID" = 'rider';
-- -----------------------------------
-- 新增客户端记录
-- -----------------------------------
INSERT INTO "BLADEX"."BLADE_CLIENT" ("ID", "CLIENT_ID", "CLIENT_SECRET", "RESOURCE_IDS", "SCOPE", "AUTHORIZED_GRANT_TYPES", "WEB_SERVER_REDIRECT_URI", "AUTHORITIES", "ACCESS_TOKEN_VALIDITY", "REFRESH_TOKEN_VALIDITY", "ADDITIONAL_INFORMATION", "AUTOAPPROVE", "CREATE_USER", "CREATE_DEPT", "CREATE_TIME", "UPDATE_USER", "UPDATE_TIME", "STATUS", "IS_DELETED") VALUES ('1123598811738675203', 'saber3', 'saber3_secret', NULL, 'all', 'authorization_code,password,refresh_token,captcha,social,register', 'http://localhost:2888/login', NULL, '3600', '604800', NULL, NULL, '1123598815738675201', '1123598813738675201', '2024-04-01 00:00:00', '1123598815738675201', '2024-04-01 00:00:00', '1', '0');
INSERT INTO "BLADEX"."BLADE_CLIENT" ("ID", "CLIENT_ID", "CLIENT_SECRET", "RESOURCE_IDS", "SCOPE", "AUTHORIZED_GRANT_TYPES", "WEB_SERVER_REDIRECT_URI", "AUTHORITIES", "ACCESS_TOKEN_VALIDITY", "REFRESH_TOKEN_VALIDITY", "ADDITIONAL_INFORMATION", "AUTOAPPROVE", "CREATE_USER", "CREATE_DEPT", "CREATE_TIME", "UPDATE_USER", "UPDATE_TIME", "STATUS", "IS_DELETED") VALUES ('1123598811738675204', 'rider', 'rider_secret', NULL, 'all', 'authorization_code,password,refresh_token,captcha,social,register', 'http://localhost:88', NULL, '3600', '604800', NULL, NULL, '1123598815738675201', '1123598813738675201', '2024-04-01 00:00:00', '1123598815738675201', '2024-04-01 00:00:00', '1', '0');
@@ -0,0 +1,16 @@
-- -----------------------------------
-- 修改应用表saber的访问地址
-- -----------------------------------
UPDATE `blade_client` SET authorized_grant_types = 'authorization_code,password,refresh_token,captcha,social,register' where id < 1123598811738675203;
-- -----------------------------------
-- 删除可能重复的菜单
-- -----------------------------------
DELETE FROM `blade_client` WHERE client_id = 'saber3';
DELETE FROM `blade_client` WHERE client_id = 'rider';
-- -----------------------------------
-- 新增模型设计菜单
-- -----------------------------------
INSERT INTO `blade_client` (`id`, `client_id`, `client_secret`, `resource_ids`, `scope`, `authorized_grant_types`, `web_server_redirect_uri`, `authorities`, `access_token_validity`, `refresh_token_validity`, `additional_information`, `autoapprove`, `create_user`, `create_dept`, `create_time`, `update_user`, `update_time`, `status`, `is_deleted`) VALUES (1123598811738675203, 'saber3', 'saber3_secret', NULL, 'all', 'authorization_code,password,refresh_token,captcha,social,register', 'http://localhost:2888/login', NULL, 3600, 604800, NULL, NULL, 1123598815738675201, 1123598813738675201, '2024-04-01 00:00:00', 1123598815738675201, '2024-04-01 00:00:00', 1, 0);
INSERT INTO `blade_client` (`id`, `client_id`, `client_secret`, `resource_ids`, `scope`, `authorized_grant_types`, `web_server_redirect_uri`, `authorities`, `access_token_validity`, `refresh_token_validity`, `additional_information`, `autoapprove`, `create_user`, `create_dept`, `create_time`, `update_user`, `update_time`, `status`, `is_deleted`) VALUES (1123598811738675204, 'rider', 'rider_secret', NULL, 'all', 'authorization_code,password,refresh_token,captcha,social,register', 'http://localhost:88', NULL, 3600, 604800, NULL, NULL, 1123598815738675201, 1123598813738675201, '2024-04-01 00:00:00', 1123598815738675201, '2024-04-01 00:00:00', 1, 0);
@@ -0,0 +1,16 @@
-- -----------------------------------
-- 修改应用表的授权集合
-- -----------------------------------
UPDATE "BLADEX"."BLADE_CLIENT" SET AUTHORIZED_GRANT_TYPES = 'authorization_code,password,refresh_token,captcha,social,register' where ID < 1123598811738675203;
-- -----------------------------------
-- 删除可能重复的菜单
-- -----------------------------------
DELETE FROM "BLADEX"."BLADE_CLIENT" WHERE "CLIENT_ID" = 'saber3';
DELETE FROM "BLADEX"."BLADE_CLIENT" WHERE "CLIENT_ID" = 'rider';
-- -----------------------------------
-- 新增客户端记录
-- -----------------------------------
INSERT INTO "BLADEX"."BLADE_CLIENT" ("ID", "CLIENT_ID", "CLIENT_SECRET", "RESOURCE_IDS", "SCOPE", "AUTHORIZED_GRANT_TYPES", "WEB_SERVER_REDIRECT_URI", "AUTHORITIES", "ACCESS_TOKEN_VALIDITY", "REFRESH_TOKEN_VALIDITY", "ADDITIONAL_INFORMATION", "AUTOAPPROVE", "CREATE_USER", "CREATE_DEPT", "CREATE_TIME", "UPDATE_USER", "UPDATE_TIME", "STATUS", "IS_DELETED") VALUES ('1123598811738675203', 'saber3', 'saber3_secret', NULL, 'all', 'authorization_code,password,refresh_token,captcha,social,register', 'http://localhost:2888/login', NULL, '3600', '604800', NULL, NULL, '1123598815738675201', '1123598813738675201', TO_DATE('2024-04-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'), '1123598815738675201', TO_DATE('2024-04-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'), '1', '0');
INSERT INTO "BLADEX"."BLADE_CLIENT" ("ID", "CLIENT_ID", "CLIENT_SECRET", "RESOURCE_IDS", "SCOPE", "AUTHORIZED_GRANT_TYPES", "WEB_SERVER_REDIRECT_URI", "AUTHORITIES", "ACCESS_TOKEN_VALIDITY", "REFRESH_TOKEN_VALIDITY", "ADDITIONAL_INFORMATION", "AUTOAPPROVE", "CREATE_USER", "CREATE_DEPT", "CREATE_TIME", "UPDATE_USER", "UPDATE_TIME", "STATUS", "IS_DELETED") VALUES ('1123598811738675204', 'rider', 'rider_secret', NULL, 'all', 'authorization_code,password,refresh_token,captcha,social,register', 'http://localhost:88', NULL, '3600', '604800', NULL, NULL, '1123598815738675201', '1123598813738675201', TO_DATE('2024-04-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'), '1123598815738675201', TO_DATE('2024-04-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'), '1', '0');
@@ -0,0 +1,16 @@
-- -----------------------------------
-- 修改应用表的授权集合
-- -----------------------------------
UPDATE blade_client SET authorized_grant_types = 'authorization_code,password,refresh_token,captcha,social,register' WHERE client_id = 'saber';
-- -----------------------------------
-- 删除可能重复的菜单
-- -----------------------------------
DELETE FROM "blade_client" WHERE "client_id" = 'saber3';
DELETE FROM "blade_client" WHERE "client_id" = 'rider';
-- -----------------------------------
-- 新增客户端记录
-- -----------------------------------
INSERT INTO "blade_client" ("id", "client_id", "client_secret", "resource_ids", "scope", "authorized_grant_types", "web_server_redirect_uri", "authorities", "access_token_validity", "refresh_token_validity", "additional_information", "autoapprove", "create_user", "create_dept", "create_time", "update_user", "update_time", "status", "is_deleted") VALUES (1123598811738675203, 'saber3', 'saber3_secret', NULL, 'all', 'authorization_code,password,refresh_token,captcha,social,register', 'http://localhost:2888/login', NULL, 3600, 604800, NULL, NULL, 1123598815738675201, 1123598813738675201, '2024-04-01 00:00:00', 1123598815738675201, '2024-04-01 00:00:00', 1, 0);
INSERT INTO "blade_client" ("id", "client_id", "client_secret", "resource_ids", "scope", "authorized_grant_types", "web_server_redirect_uri", "authorities", "access_token_validity", "refresh_token_validity", "additional_information", "autoapprove", "create_user", "create_dept", "create_time", "update_user", "update_time", "status", "is_deleted") VALUES (1123598811738675204, 'rider', 'rider_secret', NULL, 'all', 'authorization_code,password,refresh_token,captcha,social,register', 'http://localhost:88', NULL, 3600, 604800, NULL, NULL, 1123598815738675201, 1123598813738675201, '2024-04-01 00:00:00', 1123598815738675201, '2024-04-01 00:00:00', 1, 0);
@@ -0,0 +1,16 @@
-- -----------------------------------
-- 修改应用表saber的访问地址
-- -----------------------------------
UPDATE [dbo].[blade_client] SET [authorized_grant_types] = 'authorization_code,password,refresh_token,captcha,social,register' WHERE id < '1123598811738675203';
-- -----------------------------------
-- 删除可能重复的菜单
-- -----------------------------------
DELETE FROM [dbo].[blade_client] WHERE client_id = 'saber3';
DELETE FROM [dbo].[blade_client] WHERE client_id = 'rider';
-- -----------------------------------
-- 新增模型设计菜单
-- -----------------------------------
INSERT INTO [dbo].[blade_client] ([id], [client_id], [client_secret], [resource_ids], [scope], [authorized_grant_types], [web_server_redirect_uri], [authorities], [access_token_validity], [refresh_token_validity], [additional_information], [autoapprove], [create_user], [create_dept], [create_time], [update_user], [update_time], [status], [is_deleted]) VALUES (1123598811738675203, N'saber3', N'saber3_secret', NULL, N'all', N'authorization_code,password,refresh_token,captcha,social,register', N'http://localhost:2888/login', NULL, 3600, 604800, NULL, NULL, 1123598815738675201, 1123598813738675201, '2024-04-01 00:00:00', 1123598815738675201, '2024-04-01 00:00:00', 1, 0);
INSERT INTO [dbo].[blade_client] ([id], [client_id], [client_secret], [resource_ids], [scope], [authorized_grant_types], [web_server_redirect_uri], [authorities], [access_token_validity], [refresh_token_validity], [additional_information], [autoapprove], [create_user], [create_dept], [create_time], [update_user], [update_time], [status], [is_deleted]) VALUES (1123598811738675204, N'rider', N'rider_secret', NULL, N'all', N'authorization_code,password,refresh_token,captcha,social,register', N'http://localhost:88', NULL, 3600, 604800, NULL, NULL, 1123598815738675201, 1123598813738675201, '2024-04-01 00:00:00', 1123598815738675201, '2024-04-01 00:00:00', 1, 0);
@@ -0,0 +1,16 @@
-- -----------------------------------
-- 修改应用表的授权集合
-- -----------------------------------
UPDATE "BLADE_CLIENT" SET AUTHORIZED_GRANT_TYPES = 'authorization_code,password,refresh_token,captcha,social,register' where ID < 1123598811738675203;
-- -----------------------------------
-- 删除可能重复的菜单
-- -----------------------------------
DELETE FROM "BLADE_CLIENT" WHERE "CLIENT_ID" = 'saber3';
DELETE FROM "BLADE_CLIENT" WHERE "CLIENT_ID" = 'rider';
-- -----------------------------------
-- 新增客户端记录
-- -----------------------------------
INSERT INTO "BLADE_CLIENT" ("ID", "CLIENT_ID", "CLIENT_SECRET", "RESOURCE_IDS", "SCOPE", "AUTHORIZED_GRANT_TYPES", "WEB_SERVER_REDIRECT_URI", "AUTHORITIES", "ACCESS_TOKEN_VALIDITY", "REFRESH_TOKEN_VALIDITY", "ADDITIONAL_INFORMATION", "AUTOAPPROVE", "CREATE_USER", "CREATE_DEPT", "CREATE_TIME", "UPDATE_USER", "UPDATE_TIME", "STATUS", "IS_DELETED") VALUES ('1123598811738675203', 'saber3', 'saber3_secret', NULL, 'all', 'authorization_code,password,refresh_token,captcha,social,register', 'http://localhost:2888/login', NULL, '3600', '604800', NULL, NULL, '1123598815738675201', '1123598813738675201', TO_DATE('2024-04-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'), '1123598815738675201', TO_DATE('2024-04-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'), '1', '0');
INSERT INTO "BLADE_CLIENT" ("ID", "CLIENT_ID", "CLIENT_SECRET", "RESOURCE_IDS", "SCOPE", "AUTHORIZED_GRANT_TYPES", "WEB_SERVER_REDIRECT_URI", "AUTHORITIES", "ACCESS_TOKEN_VALIDITY", "REFRESH_TOKEN_VALIDITY", "ADDITIONAL_INFORMATION", "AUTOAPPROVE", "CREATE_USER", "CREATE_DEPT", "CREATE_TIME", "UPDATE_USER", "UPDATE_TIME", "STATUS", "IS_DELETED") VALUES ('1123598811738675204', 'rider', 'rider_secret', NULL, 'all', 'authorization_code,password,refresh_token,captcha,social,register', 'http://localhost:88', NULL, '3600', '604800', NULL, NULL, '1123598815738675201', '1123598813738675201', TO_DATE('2024-04-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'), '1123598815738675201', TO_DATE('2024-04-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'), '1', '0');
+353
View File
@@ -0,0 +1,353 @@
<?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>
<groupId>org.springblade</groupId>
<artifactId>BladeX-Boot</artifactId>
<packaging>jar</packaging>
<version>4.0.1.RELEASE</version>
<properties>
<bladex.project.id>blade-api</bladex.project.id>
<bladex.project.version>4.0.1.RELEASE</bladex.project.version>
<java.version>17</java.version>
<maven.plugin.version>3.11.0</maven.plugin.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<!-- Docker仓库服务配置 -->
<docker.registry.url>192.168.0.188</docker.registry.url>
<docker.username>admin</docker.username>
<docker.password>admin12345</docker.password>
<docker.namespace>blade</docker.namespace>
<docker.fabric.version>0.42.0</docker.fabric.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springblade.platform</groupId>
<artifactId>blade-bom</artifactId>
<version>${bladex.project.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- Blade -->
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-core-boot</artifactId>
<exclusions>
<exclusion>
<groupId>org.springblade</groupId>
<artifactId>blade-core-cloud</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-core-oauth2</artifactId>
</dependency>
<!-- 多租户字段隔离模式 -->
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-starter-tenant</artifactId>
</dependency>
<!-- 多租户数据库隔离模式则引入此配置 -->
<!--<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-starter-tenant-dynamic</artifactId>
</dependency>-->
<!-- 集成sharding功能则引入此配置 -->
<!--<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-starter-sharding</artifactId>
</dependency>-->
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-starter-http</artifactId>
</dependency>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-starter-api-crypto</artifactId>
</dependency>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-starter-datascope</artifactId>
</dependency>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-starter-develop</artifactId>
</dependency>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-starter-swagger</artifactId>
</dependency>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-starter-excel</artifactId>
</dependency>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-starter-social</artifactId>
</dependency>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-starter-powerjob</artifactId>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-ui</artifactId>
</dependency>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-core-auto</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-core-test</artifactId>
<scope>test</scope>
</dependency>
<!-- 报表 -->
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-starter-report</artifactId>
</dependency>
<!-- 工作流 -->
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-starter-flowable</artifactId>
</dependency>
<!-- 验证码 -->
<dependency>
<groupId>com.github.whvcse</groupId>
<artifactId>easy-captcha</artifactId>
</dependency>
<!-- Mybatis-Plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
</dependency>
<!--Oss-->
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-starter-oss</artifactId>
</dependency>
<!--Sms-->
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-starter-sms</artifactId>
</dependency>
<!--Aws S3-->
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-s3</artifactId>
</dependency>
<!--MinIO-->
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
</dependency>
<!--Alioss-->
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
</dependency>
<!--AliSms-->
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
</dependency>
<!--华为云Obs-->
<dependency>
<groupId>com.huaweicloud</groupId>
<artifactId>esdk-obs-java</artifactId>
</dependency>
<!--腾讯COS-->
<dependency>
<groupId>com.qcloud</groupId>
<artifactId>cos_api</artifactId>
</dependency>
<!--腾讯SMS-->
<dependency>
<groupId>com.github.qcloudsms</groupId>
<artifactId>qcloudsms</artifactId>
</dependency>
<!--QiNiu-->
<dependency>
<groupId>com.qiniu</groupId>
<artifactId>qiniu-java-sdk</artifactId>
</dependency>
<!--YunPian-->
<dependency>
<groupId>com.yunpian.sdk</groupId>
<artifactId>yunpian-java-sdk</artifactId>
</dependency>
<!-- liteflow -->
<!-- <dependency>-->
<!-- <groupId>org.springblade</groupId>-->
<!-- <artifactId>blade-starter-liteflow</artifactId>-->
<!-- </dependency>-->
<!-- Oracle -->
<!--<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc7</artifactId>
</dependency>-->
<!-- PostgreSql -->
<!--<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
</dependency>-->
<!-- SqlServer -->
<!--<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
</dependency>-->
<!-- DaMeng -->
<!--<dependency>
<groupId>com.dameng</groupId>
<artifactId>DmJdbcDriver18</artifactId>
</dependency>-->
<!--YashanDB-->
<!--<dependency>
<groupId>com.yashandb.jdbc</groupId>
<artifactId>yasdb-jdbc</artifactId>
</dependency>-->
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<finalName>${bladex.project.id}</finalName>
<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>3.2.4</version>
<configuration>
<finalName>${project.build.finalName}</finalName>
<excludes>
<!-- 打包的 jar 中排除 lombok -->
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>io.fabric8</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>${docker.fabric.version}</version>
<configuration>
<authConfig>
<username>${docker.username}</username>
<password>${docker.password}</password>
</authConfig>
<registry>${docker.registry.url}</registry>
<images>
<image>
<name>${docker.namespace}/${project.build.finalName}:${project.version}</name>
<alias>${project.name}</alias>
<build>
<dockerFile>${project.basedir}/Dockerfile</dockerFile>
</build>
</image>
</images>
<buildArgs>
<JAR_FILE>${basedir}/target/${project.build.finalName}.jar</JAR_FILE>
</buildArgs>
</configuration>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven.plugin.version}</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<encoding>UTF-8</encoding>
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
</configuration>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>aliyun-repos</id>
<name>Aliyun Public Repository</name>
<url>https://maven.aliyun.com/repository/public</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>bladex</id>
<name>BladeX Release Repository</name>
<url>https://center.javablade.com/api/packages/blade/maven</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>aliyun-plugin</id>
<name>Aliyun Public Plugin</name>
<url>https://maven.aliyun.com/repository/public</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
<distributionManagement>
<repository>
<id>bladex</id>
<name>BladeX Release Repository</name>
<url>https://center.javablade.com/api/packages/blade/maven</url>
</repository>
<snapshotRepository>
<id>bladex</id>
<name>BladeX Snapshot Repository</name>
<url>https://center.javablade.com/api/packages/blade/maven</url>
</snapshotRepository>
</distributionManagement>
</project>
@@ -0,0 +1,26 @@
package org.springblade;
import org.mybatis.spring.annotation.MapperScan;
import org.springblade.common.constant.CommonConstant;
import org.springblade.core.launch.BladeApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
/**
* 启动器
*
* @author Chill
*/
@EnableScheduling
@EnableRedisHttpSession
@SpringBootApplication
public class Application {
public static void main(String[] args) {
BladeApplication.run(CommonConstant.APPLICATION_NAME, Application.class, args);
}
}
@@ -0,0 +1,46 @@
package org.springblade.common.cache;
import org.springblade.core.tool.utils.StringPool;
/**
* 缓存名
*
* @author Chill
*/
public interface CacheNames {
/**
* 返回拼接后的key
*
* @param cacheKey 缓存key
* @param cacheKeyValue 缓存key值
* @return tenantKey
*/
static String cacheKey(String cacheKey, String cacheKeyValue) {
return cacheKey.concat(cacheKeyValue);
}
/**
* 返回租户格式的key
*
* @param tenantId 租户编号
* @param cacheKey 缓存key
* @param cacheKeyValue 缓存key值
* @return tenantKey
*/
static String tenantKey(String tenantId, String cacheKey, String cacheKeyValue) {
return tenantId.concat(StringPool.COLON).concat(cacheKey).concat(cacheKeyValue);
}
/**
* 验证码key
*/
String CAPTCHA_KEY = "blade:auth::blade:captcha:";
/**
* 登录失败key
*/
String USER_FAIL_KEY = "blade:user::blade:fail:";
}
@@ -0,0 +1,101 @@
package org.springblade.common.cache;
import org.springblade.common.enums.DictBizEnum;
import org.springblade.core.cache.utils.CacheUtil;
import org.springblade.core.secure.utils.AuthUtil;
import org.springblade.core.tool.utils.SpringUtil;
import org.springblade.core.tool.utils.StringPool;
import org.springblade.modules.system.pojo.entity.DictBiz;
import org.springblade.modules.system.service.IDictBizService;
import java.util.List;
import static org.springblade.core.cache.constant.CacheConstant.DICT_CACHE;
/**
* 业务字典缓存工具类
*
* @author Chill
*/
public class DictBizCache {
private static final String DICT_ID = "dictBiz:id";
private static final String DICT_VALUE = "dictBiz:value";
private static final String DICT_LIST = "dictBiz:list";
private static final IDictBizService dictService;
static {
dictService = SpringUtil.getBean(IDictBizService.class);
}
/**
* 获取字典实体
*
* @param id 主键
* @return DictBiz
*/
public static DictBiz getById(Long id) {
String keyPrefix = DICT_ID.concat(StringPool.DASH).concat(AuthUtil.getTenantId()).concat(StringPool.COLON);
return CacheUtil.get(DICT_CACHE, keyPrefix, id, () -> dictService.getById(id));
}
/**
* 获取字典值
*
* @param code 字典编号枚举
* @param dictKey Integer型字典键
* @return String
*/
public static String getValue(DictBizEnum code, Integer dictKey) {
return getValue(code.getName(), dictKey);
}
/**
* 获取字典值
*
* @param code 字典编号
* @param dictKey Integer型字典键
* @return String
*/
public static String getValue(String code, Integer dictKey) {
String keyPrefix = DICT_VALUE.concat(StringPool.DASH).concat(AuthUtil.getTenantId()).concat(StringPool.COLON);
return CacheUtil.get(DICT_CACHE, keyPrefix + code + StringPool.COLON, String.valueOf(dictKey), () -> dictService.getValue(code, String.valueOf(dictKey)));
}
/**
* 获取字典值
*
* @param code 字典编号枚举
* @param dictKey String型字典键
* @return String
*/
public static String getValue(DictBizEnum code, String dictKey) {
return getValue(code.getName(), dictKey);
}
/**
* 获取字典值
*
* @param code 字典编号
* @param dictKey String型字典键
* @return String
*/
public static String getValue(String code, String dictKey) {
String keyPrefix = DICT_VALUE.concat(StringPool.DASH).concat(AuthUtil.getTenantId()).concat(StringPool.COLON);
return CacheUtil.get(DICT_CACHE, keyPrefix + code + StringPool.COLON, dictKey, () -> dictService.getValue(code, dictKey));
}
/**
* 获取字典集合
*
* @param code 字典编号
* @return List<DictBiz>
*/
public static List<DictBiz> getList(String code) {
String keyPrefix = DICT_LIST.concat(StringPool.DASH).concat(AuthUtil.getTenantId()).concat(StringPool.COLON);
return CacheUtil.get(DICT_CACHE, keyPrefix, code, () -> dictService.getList(code));
}
}
@@ -0,0 +1,128 @@
package org.springblade.common.cache;
import org.springblade.common.enums.DictEnum;
import org.springblade.core.cache.utils.CacheUtil;
import org.springblade.core.tool.utils.SpringUtil;
import org.springblade.core.tool.utils.StringPool;
import org.springblade.modules.system.pojo.entity.Dict;
import org.springblade.modules.system.service.IDictService;
import java.util.List;
import java.util.Optional;
import static org.springblade.core.cache.constant.CacheConstant.DICT_CACHE;
/**
* 字典缓存工具类
*
* @author Chill
*/
public class DictCache {
private static final String DICT_ID = "dict:id:";
private static final String DICT_KEY = "dict:key:";
private static final String DICT_VALUE = "dict:value:";
private static final String DICT_LIST = "dict:list:";
private static final Boolean TENANT_MODE = Boolean.FALSE;
private static final IDictService dictService;
static {
dictService = SpringUtil.getBean(IDictService.class);
}
/**
* 获取字典实体
*
* @param id 主键
* @return Dict
*/
public static Dict getById(Long id) {
return CacheUtil.get(DICT_CACHE, DICT_ID, id, () -> dictService.getById(id), TENANT_MODE);
}
/**
* 获取字典值
*
* @param code 字典编号枚举
* @param dictValue 字典值
* @return String
*/
public static String getKey(DictEnum code, String dictValue) {
return getKey(code.getName(), dictValue);
}
/**
* 获取字典键
*
* @param code 字典编号
* @param dictValue 字典值
* @return String
*/
public static String getKey(String code, String dictValue) {
return CacheUtil.get(DICT_CACHE, DICT_KEY + code + StringPool.COLON, dictValue, () -> {
List<Dict> list = getList(code);
Optional<String> key = list.stream().filter(
dict -> dict.getDictValue().equalsIgnoreCase(dictValue)
).map(Dict::getDictKey).findFirst();
return key.orElse(StringPool.EMPTY);
}, TENANT_MODE);
}
/**
* 获取字典值
*
* @param code 字典编号枚举
* @param dictKey Integer型字典键
* @return String
*/
public static String getValue(DictEnum code, Integer dictKey) {
return getValue(code.getName(), dictKey);
}
/**
* 获取字典值
*
* @param code 字典编号
* @param dictKey Integer型字典键
* @return String
*/
public static String getValue(String code, Integer dictKey) {
return CacheUtil.get(DICT_CACHE, DICT_VALUE + code + StringPool.COLON, String.valueOf(dictKey), () -> dictService.getValue(code, String.valueOf(dictKey)), TENANT_MODE);
}
/**
* 获取字典值
*
* @param code 字典编号枚举
* @param dictKey String型字典键
* @return String
*/
public static String getValue(DictEnum code, String dictKey) {
return getValue(code.getName(), dictKey);
}
/**
* 获取字典值
*
* @param code 字典编号
* @param dictKey String型字典键
* @return String
*/
public static String getValue(String code, String dictKey) {
return CacheUtil.get(DICT_CACHE, DICT_VALUE + code + StringPool.COLON, dictKey, () -> dictService.getValue(code, dictKey), TENANT_MODE);
}
/**
* 获取字典集合
*
* @param code 字典编号
* @return List<Dict>
*/
public static List<Dict> getList(String code) {
return CacheUtil.get(DICT_CACHE, DICT_LIST, code, () -> dictService.getList(code), TENANT_MODE);
}
}
@@ -0,0 +1,47 @@
package org.springblade.common.cache;
import org.springblade.core.cache.utils.CacheUtil;
import org.springblade.core.tool.utils.SpringUtil;
import org.springblade.modules.system.pojo.entity.Param;
import org.springblade.modules.system.service.IParamService;
import static org.springblade.core.cache.constant.CacheConstant.PARAM_CACHE;
/**
* 参数缓存工具类
*
* @author Chill
*/
public class ParamCache {
private static final String PARAM_ID = "param:id:";
private static final String PARAM_VALUE = "param:value:";
private static final IParamService paramService;
static {
paramService = SpringUtil.getBean(IParamService.class);
}
/**
* 获取参数实体
*
* @param id 主键
* @return Param
*/
public static Param getById(Long id) {
return CacheUtil.get(PARAM_CACHE, PARAM_ID, id, () -> paramService.getById(id));
}
/**
* 获取参数配置
*
* @param paramKey 参数值
* @return String
*/
public static String getValue(String paramKey) {
return CacheUtil.get(PARAM_CACHE, PARAM_VALUE, paramKey, () -> paramService.getValue(paramKey));
}
}
@@ -0,0 +1,42 @@
package org.springblade.common.cache;
import org.springblade.core.cache.utils.CacheUtil;
import org.springblade.core.tool.utils.SpringUtil;
import org.springblade.modules.system.pojo.entity.Region;
import org.springblade.modules.system.service.IRegionService;
import static org.springblade.core.cache.constant.CacheConstant.SYS_CACHE;
/**
* 行政区划缓存工具类
*
* @author Chill
*/
public class RegionCache {
public static final String MAIN_CODE = "00";
public static final int PROVINCE_LEVEL = 1;
public static final int CITY_LEVEL = 2;
public static final int DISTRICT_LEVEL = 3;
public static final int TOWN_LEVEL = 4;
public static final int VILLAGE_LEVEL = 5;
private static final String REGION_CODE = "region:code:";
private static final IRegionService regionService;
static {
regionService = SpringUtil.getBean(IRegionService.class);
}
/**
* 获取行政区划实体
*
* @param code 区划编号
* @return Param
*/
public static Region getByCode(String code) {
return CacheUtil.get(SYS_CACHE, REGION_CODE, code, () -> regionService.getById(code));
}
}
+302
View File
@@ -0,0 +1,302 @@
package org.springblade.common.cache;
import org.springblade.core.cache.utils.CacheUtil;
import org.springblade.core.tool.utils.SpringUtil;
import org.springblade.core.tool.utils.StringPool;
import org.springblade.modules.system.pojo.entity.*;
import org.springblade.modules.system.service.*;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import static org.springblade.core.cache.constant.CacheConstant.SYS_CACHE;
/**
* 系统缓存
*
* @author Chill
*/
public class SysCache {
private static final String MENU_ID = "menu:id:";
private static final String DEPT_ID = "dept:id:";
private static final String DEPT_NAME = "dept:name:";
private static final String DEPT_NAME_FUZZY = "dept:nameFuzzy:";
private static final String DEPT_NAME_ID = "deptName:id:";
private static final String DEPT_NAMES_ID = "deptNames:id:";
private static final String DEPT_CHILD_ID = "deptChild:id:";
private static final String DEPT_CHILDIDS_ID = "deptChildIds:id:";
private static final String POST_ID = "post:id:";
private static final String POST_NAME = "post:name:";
private static final String POST_NAME_FUZZY = "post:nameFuzzy:";
private static final String POST_NAME_ID = "postName:id:";
private static final String POST_NAMES_ID = "postNames:id:";
private static final String ROLE_ID = "role:id:";
private static final String ROLE_NAME = "role:name:";
private static final String ROLE_NAME_ID = "roleName:id:";
private static final String ROLE_NAMES_ID = "roleNames:id:";
private static final String ROLE_ALIAS_ID = "roleAlias:id:";
private static final String ROLE_ALIASES_ID = "roleAliases:id:";
public static final String TENANT_ID = "tenant:id:";
public static final String TENANT_TENANT_ID = "tenant:tenantId:";
public static final String TENANT_PACKAGE_ID = "tenant:packageId:";
private static final IMenuService menuService;
private static final IDeptService deptService;
private static final IPostService postService;
private static final IRoleService roleService;
private static final ITenantService tenantService;
private static final ITenantPackageService tenantPackageService;
static {
menuService = SpringUtil.getBean(IMenuService.class);
deptService = SpringUtil.getBean(IDeptService.class);
postService = SpringUtil.getBean(IPostService.class);
roleService = SpringUtil.getBean(IRoleService.class);
tenantService = SpringUtil.getBean(ITenantService.class);
tenantPackageService = SpringUtil.getBean(ITenantPackageService.class);
}
/**
* 获取菜单
*
* @param id 主键
* @return 菜单
*/
public static Menu getMenu(Long id) {
return CacheUtil.get(SYS_CACHE, MENU_ID, id, () -> menuService.getById(id));
}
/**
* 获取部门
*
* @param id 主键
* @return 部门
*/
public static Dept getDept(Long id) {
return CacheUtil.get(SYS_CACHE, DEPT_ID, id, () -> deptService.getById(id));
}
/**
* 获取部门id
*
* @param tenantId 租户id
* @param deptNames 部门名
* @return 部门id
*/
public static String getDeptIds(String tenantId, String deptNames) {
return CacheUtil.get(SYS_CACHE, DEPT_NAME, tenantId + StringPool.DASH + deptNames, () -> deptService.getDeptIds(tenantId, deptNames));
}
/**
* 获取部门id
*
* @param tenantId 租户id
* @param deptNames 部门名模糊查询
* @return 部门id
*/
public static String getDeptIdsByFuzzy(String tenantId, String deptNames) {
return CacheUtil.get(SYS_CACHE, DEPT_NAME_FUZZY, tenantId + StringPool.DASH + deptNames, () -> deptService.getDeptIdsByFuzzy(tenantId, deptNames));
}
/**
* 获取部门名
*
* @param id 主键
* @return 部门名
*/
public static String getDeptName(Long id) {
return CacheUtil.get(SYS_CACHE, DEPT_NAME_ID, id, () -> deptService.getById(id).getDeptName());
}
/**
* 获取部门名集合
*
* @param deptIds 主键集合
* @return 部门名
*/
public static List<String> getDeptNames(String deptIds) {
return CacheUtil.get(SYS_CACHE, DEPT_NAMES_ID, deptIds, () -> deptService.getDeptNames(deptIds));
}
/**
* 获取子部门集合
*
* @param deptId 主键
* @return 子部门
*/
public static List<Dept> getDeptChild(Long deptId) {
return CacheUtil.get(SYS_CACHE, DEPT_CHILD_ID, deptId, () -> deptService.getDeptChild(deptId));
}
/**
* 获取子部门ID集合
*
* @param deptId 主键
* @return 子部门ID
*/
public static List<Long> getDeptChildIds(Long deptId) {
if (deptId == null) {
return null;
}
List<Long> deptIdList = CacheUtil.get(SYS_CACHE, DEPT_CHILDIDS_ID, deptId, List.class);
if (deptIdList == null) {
deptIdList = new ArrayList<>();
List<Dept> deptChild = getDeptChild(deptId);
if (deptChild != null) {
List<Long> collect = deptChild.stream().map(Dept::getId).collect(Collectors.toList());
deptIdList.addAll(collect);
}
deptIdList.add(deptId);
CacheUtil.put(SYS_CACHE, DEPT_CHILDIDS_ID, deptId, deptIdList);
}
return deptIdList;
}
/**
* 获取岗位
*
* @param id 主键
* @return
*/
public static Post getPost(Long id) {
return CacheUtil.get(SYS_CACHE, POST_ID, id, () -> postService.getById(id));
}
/**
* 获取岗位id
*
* @param tenantId 租户id
* @param postNames 岗位名
* @return
*/
public static String getPostIds(String tenantId, String postNames) {
return CacheUtil.get(SYS_CACHE, POST_NAME, tenantId + StringPool.DASH + postNames, () -> postService.getPostIds(tenantId, postNames));
}
/**
* 获取岗位id
*
* @param tenantId 租户id
* @param postNames 岗位名模糊查询
* @return
*/
public static String getPostIdsByFuzzy(String tenantId, String postNames) {
return CacheUtil.get(SYS_CACHE, POST_NAME_FUZZY, tenantId + StringPool.DASH + postNames, () -> postService.getPostIdsByFuzzy(tenantId, postNames));
}
/**
* 获取岗位名
*
* @param id 主键
* @return 岗位名
*/
public static String getPostName(Long id) {
return CacheUtil.get(SYS_CACHE, POST_NAME_ID, id, () -> postService.getById(id).getPostName());
}
/**
* 获取岗位名集合
*
* @param postIds 主键集合
* @return 岗位名
*/
public static List<String> getPostNames(String postIds) {
return CacheUtil.get(SYS_CACHE, POST_NAMES_ID, postIds, () -> postService.getPostNames(postIds));
}
/**
* 获取角色
*
* @param id 主键
* @return Role
*/
public static Role getRole(Long id) {
return CacheUtil.get(SYS_CACHE, ROLE_ID, id, () -> roleService.getById(id));
}
/**
* 获取角色id
*
* @param tenantId 租户id
* @param roleNames 角色名
* @return
*/
public static String getRoleIds(String tenantId, String roleNames) {
return CacheUtil.get(SYS_CACHE, ROLE_NAME, tenantId + StringPool.DASH + roleNames, () -> roleService.getRoleIds(tenantId, roleNames));
}
/**
* 获取角色名
*
* @param id 主键
* @return 角色名
*/
public static String getRoleName(Long id) {
return CacheUtil.get(SYS_CACHE, ROLE_NAME_ID, id, () -> roleService.getById(id).getRoleName());
}
/**
* 获取角色名集合
*
* @param roleIds 主键集合
* @return 角色名
*/
public static List<String> getRoleNames(String roleIds) {
return CacheUtil.get(SYS_CACHE, ROLE_NAMES_ID, roleIds, () -> roleService.getRoleNames(roleIds));
}
/**
* 获取角色别名
*
* @param id 主键
* @return 角色别名
*/
public static String getRoleAlias(Long id) {
return CacheUtil.get(SYS_CACHE, ROLE_ALIAS_ID, id, () -> roleService.getById(id).getRoleAlias());
}
/**
* 获取角色别名集合
*
* @param roleIds 主键集合
* @return 角色别名
*/
public static List<String> getRoleAliases(String roleIds) {
return CacheUtil.get(SYS_CACHE, ROLE_ALIASES_ID, roleIds, () -> roleService.getRoleAliases(roleIds));
}
/**
* 获取租户
*
* @param id 主键
* @return Tenant
*/
public static Tenant getTenant(Long id) {
return CacheUtil.get(SYS_CACHE, TENANT_ID, id, () -> tenantService.getById(id), Boolean.FALSE);
}
/**
* 获取租户
*
* @param tenantId 租户id
* @return Tenant
*/
public static Tenant getTenant(String tenantId) {
return CacheUtil.get(SYS_CACHE, TENANT_TENANT_ID, tenantId, () -> tenantService.getByTenantId(tenantId), Boolean.FALSE);
}
/**
* 获取租户产品包
*
* @param tenantId 租户id
* @return Tenant
*/
public static TenantPackage getTenantPackage(String tenantId) {
Tenant tenant = getTenant(tenantId);
return CacheUtil.get(SYS_CACHE, TENANT_PACKAGE_ID, tenantId, () -> tenantPackageService.getById(tenant.getPackageId()), Boolean.FALSE);
}
}
@@ -0,0 +1,62 @@
package org.springblade.common.cache;
import org.springblade.core.cache.utils.CacheUtil;
import org.springblade.core.tool.utils.Func;
import org.springblade.core.tool.utils.SpringUtil;
import org.springblade.core.tool.utils.StringPool;
import org.springblade.core.tool.utils.StringUtil;
import org.springblade.modules.system.pojo.entity.User;
import org.springblade.modules.system.service.IUserService;
import static org.springblade.core.cache.constant.CacheConstant.USER_CACHE;
import static org.springblade.core.launch.constant.FlowConstant.TASK_USR_PREFIX;
/**
* 系统缓存
*
* @author Chill
*/
public class UserCache {
private static final String USER_CACHE_ID = "user:id:";
private static final String USER_CACHE_ACCOUNT = "user:account:";
private static final IUserService userService;
static {
userService = SpringUtil.getBean(IUserService.class);
}
/**
* 根据任务用户id获取用户信息
*
* @param taskUserId 任务用户id
* @return
*/
public static User getUserByTaskUser(String taskUserId) {
Long userId = Func.toLong(StringUtil.removePrefix(taskUserId, TASK_USR_PREFIX));
return getUser(userId);
}
/**
* 获取用户
*
* @param userId 用户id
* @return
*/
public static User getUser(Long userId) {
return CacheUtil.get(USER_CACHE, USER_CACHE_ID, userId, () -> userService.getById(userId));
}
/**
* 获取用户
*
* @param tenantId 租户id
* @param account 账号名
* @return
*/
public static User getUser(String tenantId, String account) {
return CacheUtil.get(USER_CACHE, USER_CACHE_ACCOUNT, tenantId + StringPool.DASH + account, () -> userService.userByAccount(tenantId, account));
}
}
@@ -0,0 +1,70 @@
package org.springblade.common.config;
import org.springblade.core.launch.constant.AppConstant;
import org.springblade.core.oauth2.endpoint.OAuth2SocialEndpoint;
import org.springblade.core.oauth2.endpoint.OAuth2TokenEndPoint;
import org.springblade.core.secure.registry.SecureRegistry;
import org.springblade.core.tool.utils.StringPool;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* Blade配置
*
* @author Chill
*/
@Configuration(proxyBeanMethods = false)
public class BladeConfiguration implements WebMvcConfigurer {
/**
* 安全框架配置
*/
@Bean
public SecureRegistry secureRegistry() {
SecureRegistry secureRegistry = new SecureRegistry();
secureRegistry.setEnabled(true);
secureRegistry.excludePathPatterns("/blade-auth/**");
secureRegistry.excludePathPatterns("/blade-system/tenant/info");
secureRegistry.excludePathPatterns("/blade-flow/process/resource-view");
secureRegistry.excludePathPatterns("/blade-flow/process/diagram-view");
secureRegistry.excludePathPatterns("/blade-flow/manager/check-upload");
secureRegistry.excludePathPatterns("/doc.html");
secureRegistry.excludePathPatterns("/swagger-ui.html");
secureRegistry.excludePathPatterns("/static/**");
secureRegistry.excludePathPatterns("/webjars/**");
secureRegistry.excludePathPatterns("/swagger-resources/**");
secureRegistry.excludePathPatterns("/druid/**");
return secureRegistry;
}
/**
* 跨域配置
*/
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/cors/**")
.allowedOriginPatterns("*")
.allowedHeaders("*")
.allowedMethods("*")
.maxAge(3600)
.allowCredentials(true);
}
/**
* 给OAuth2服务端添加前缀
*/
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.addPathPrefix(StringPool.SLASH + AppConstant.APPLICATION_AUTH_NAME,
c -> c.isAnnotationPresent(RestController.class) && (
OAuth2TokenEndPoint.class.equals(c) || OAuth2SocialEndpoint.class.equals(c))
);
}
}
@@ -0,0 +1,28 @@
package org.springblade.common.config;
import lombok.AllArgsConstructor;
import org.springblade.common.handler.BladeScopeModelHandler;
import org.springblade.core.datascope.handler.ScopeModelHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
/**
* Blade处理器自动配置
*
* @author Chill
*/
@Configuration(proxyBeanMethods = false)
@AllArgsConstructor
public class BladeHandlerConfiguration {
private final JdbcTemplate jdbcTemplate;
@Bean
public ScopeModelHandler scopeModelHandler() {
return new BladeScopeModelHandler(jdbcTemplate);
}
}
@@ -0,0 +1,43 @@
package org.springblade.common.config;
import lombok.AllArgsConstructor;
import org.springblade.common.event.ApiLogListener;
import org.springblade.common.event.ErrorLogListener;
import org.springblade.common.event.UsualLogListener;
import org.springblade.core.launch.props.BladeProperties;
import org.springblade.core.launch.server.ServerInfo;
import org.springblade.modules.system.service.ILogService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 日志工具自动配置
*
* @author Chill
*/
@Configuration(proxyBeanMethods = false)
@AllArgsConstructor
public class BladeLogConfiguration {
private final ILogService logService;
private final ServerInfo serverInfo;
private final BladeProperties bladeProperties;
@Bean(name = "apiLogListener")
public ApiLogListener apiLogListener() {
return new ApiLogListener(logService, serverInfo, bladeProperties);
}
@Bean(name = "errorEventListener")
public ErrorLogListener errorEventListener() {
return new ErrorLogListener(logService, serverInfo, bladeProperties);
}
@Bean(name = "usualEventListener")
public UsualLogListener usualEventListener() {
return new UsualLogListener(logService, serverInfo, bladeProperties);
}
}
@@ -0,0 +1,26 @@
package org.springblade.common.config;
import org.springblade.common.filter.PreviewFilter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 演示配置类
*
* @author Chill
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(value = "blade.preview.enabled", havingValue = "true")
public class BladePreviewConfiguration {
/**
* 演示模式配置
*/
@Bean
public PreviewFilter previewFilter() {
return new PreviewFilter();
}
}
@@ -0,0 +1,28 @@
package org.springblade.common.config;
import org.springblade.core.report.datasource.ReportDataSource;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
/**
* 报表配置类
*
* @author Chill
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(value = "report.enabled", havingValue = "true", matchIfMissing = true)
public class BladeReportConfiguration {
/**
* 自定义报表可选数据源
*/
@Bean
public ReportDataSource reportDataSource(DataSource dataSource) {
return new ReportDataSource(dataSource);
}
}
@@ -0,0 +1,46 @@
package org.springblade.common.config;
import lombok.AllArgsConstructor;
import org.springblade.core.launch.constant.AppConstant;
import org.springdoc.core.models.GroupedOpenApi;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Swagger配置类
*
* @author Chill
*/
@Configuration(proxyBeanMethods = false)
@AllArgsConstructor
@ConditionalOnProperty(value = "swagger.enabled", havingValue = "true", matchIfMissing = true)
public class SwaggerConfiguration {
@Bean
public GroupedOpenApi authApi() {
return GroupedOpenApi.builder()
.group("授权模块")
.packagesToScan(AppConstant.BASE_PACKAGES + ".core.oauth2", AppConstant.BASE_PACKAGES + ".modules.auth")
.build();
}
@Bean
public GroupedOpenApi sysApi() {
return GroupedOpenApi.builder()
.group("系统模块")
.packagesToScan(AppConstant.BASE_PACKAGES + ".modules.system", AppConstant.BASE_PACKAGES + ".modules.resource")
.build();
}
@Bean
public GroupedOpenApi flowApi() {
// 创建并返回GroupedOpenApi对象
return GroupedOpenApi.builder()
.group("工作流模块")
.packagesToScan(AppConstant.BASE_PACKAGES + ".flow")
.build();
}
}
@@ -0,0 +1,69 @@
package org.springblade.common.constant;
import org.springblade.core.launch.constant.AppConstant;
/**
* 通用常量
*
* @author Chill
*/
public interface CommonConstant {
/**
* app name
*/
String APPLICATION_NAME = AppConstant.APPLICATION_NAME_PREFIX + "api";
/**
* sword 系统名
*/
String SWORD_NAME = "sword";
/**
* saber 系统名
*/
String SABER_NAME = "saber";
/**
* 顶级父节点id
*/
Long TOP_PARENT_ID = 0L;
/**
* 顶级父节点名称
*/
String TOP_PARENT_NAME = "顶级";
/**
* 未封存状态值
*/
Integer NOT_SEALED_ID = 0;
/**
* 默认密码
*/
String DEFAULT_PASSWORD = "123456";
/**
* 默认密码参数值
*/
String DEFAULT_PARAM_PASSWORD = "account.initPassword";
/**
* 默认排序字段
*/
String SORT_FIELD = "sort";
/**
* 数据权限类型
*/
Integer DATA_SCOPE_CATEGORY = 1;
/**
* 接口权限类型
*/
Integer API_SCOPE_CATEGORY = 2;
}
@@ -0,0 +1,166 @@
package org.springblade.common.constant;
import io.jsonwebtoken.Claims;
/**
* 通用常量信息
*
* @author ruoyi
*/
public class Constants {
/**
* UTF-8 字符集
*/
public static final String UTF8 = "UTF-8";
/**
* GBK 字符集
*/
public static final String GBK = "GBK";
/**
* http请求
*/
public static final String HTTP = "http://";
/**
* https请求
*/
public static final String HTTPS = "https://";
/**
* 通用成功标识
*/
public static final String SUCCESS = "0";
/**
* 通用失败标识
*/
public static final String FAIL = "1";
/**
* 登录成功
*/
public static final String LOGIN_SUCCESS = "Success";
/**
* 注销
*/
public static final String LOGOUT = "Logout";
/**
* 注册
*/
public static final String REGISTER = "Register";
/**
* 登录失败
*/
public static final String LOGIN_FAIL = "Error";
/**
* 验证码 redis key
*/
public static final String CAPTCHA_CODE_KEY = "captcha_codes:";
/**
* 登录用户 redis key
*/
public static final String LOGIN_TOKEN_KEY = "login_tokens:";
/**
* 防重提交 redis key
*/
public static final String REPEAT_SUBMIT_KEY = "repeat_submit:";
/**
* 限流 redis key
*/
public static final String RATE_LIMIT_KEY = "rate_limit:";
/**
* 验证码有效期分钟
*/
public static final Integer CAPTCHA_EXPIRATION = 2;
/**
* 令牌
*/
public static final String TOKEN = "token";
/**
* 令牌前缀
*/
public static final String TOKEN_PREFIX = "Bearer ";
/**
* 令牌前缀
*/
public static final String LOGIN_USER_KEY = "login_user_key";
/**
* 用户ID
*/
public static final String JWT_USERID = "userid";
/**
* 用户名称
*/
public static final String JWT_USERNAME = Claims.SUBJECT;
/**
* 用户头像
*/
public static final String JWT_AVATAR = "avatar";
/**
* 创建时间
*/
public static final String JWT_CREATED = "created";
/**
* 用户权限
*/
public static final String JWT_AUTHORITIES = "authorities";
/**
* 参数管理 cache key
*/
public static final String SYS_CONFIG_KEY = "sys_config:";
/**
* 字典管理 cache key
*/
public static final String SYS_DICT_KEY = "sys_dict:";
/**
* 资源映射路径 前缀
*/
public static final String RESOURCE_PREFIX = "/profile";
/**
* RMI 远程方法调用
*/
public static final String LOOKUP_RMI = "rmi:";
/**
* LDAP 远程方法调用
*/
public static final String LOOKUP_LDAP = "ldap:";
/**
* LDAPS 远程方法调用
*/
public static final String LOOKUP_LDAPS = "ldaps:";
/**
* 定时任务白名单配置仅允许访问的包名如其他需要可以自行添加
*/
public static final String[] JOB_WHITELIST_STR = {"com.ruoyi"};
/**
* 定时任务违规的字符
*/
public static final String[] JOB_ERROR_STR = {"java.net.URL", "javax.naming.InitialContext", "org.yaml.snakeyaml",
"org.springframework", "org.apache", "com.ruoyi.common.utils.file"};
}
@@ -0,0 +1,23 @@
package org.springblade.common.constant;
/**
* 字典常量.
*
* @author zhuangqian
*/
public interface DictConstant {
String SEX_CODE = "sex";
String NOTICE_CODE = "notice";
String MENU_CATEGORY_CODE = "menu_category";
String BUTTON_FUNC_CODE = "button_func";
String YES_NO_CODE = "yes_no";
String FLOW_CATEGORY_CODE = "flow_category";
}
@@ -0,0 +1,78 @@
package org.springblade.common.constant;
import org.springblade.core.launch.constant.AppConstant;
/**
* 启动常量
*
* @author Chill
*/
public interface LauncherConstant {
/**
* sentinel dev 地址
*/
String SENTINEL_DEV_ADDR = "127.0.0.1:8858";
/**
* sentinel prod 地址
*/
String SENTINEL_PROD_ADDR = "10.211.55.5:8858";
/**
* sentinel test 地址
*/
String SENTINEL_TEST_ADDR = "172.30.0.58:8858";
/**
* elk dev 地址
*/
String ELK_DEV_ADDR = "127.0.0.1:9000";
/**
* elk prod 地址
*/
String ELK_PROD_ADDR = "172.30.0.58:9000";
/**
* elk test 地址
*/
String ELK_TEST_ADDR = "172.30.0.58:9000";
/**
* 动态获取sentinel地址
*
* @param profile 环境变量
* @return addr
*/
static String sentinelAddr(String profile) {
switch (profile) {
case (AppConstant.PROD_CODE):
return SENTINEL_PROD_ADDR;
case (AppConstant.TEST_CODE):
return SENTINEL_TEST_ADDR;
default:
return SENTINEL_DEV_ADDR;
}
}
/**
* 动态获取elk地址
*
* @param profile 环境变量
* @return addr
*/
static String elkAddr(String profile) {
switch (profile) {
case (AppConstant.PROD_CODE):
return ELK_PROD_ADDR;
case (AppConstant.TEST_CODE):
return ELK_TEST_ADDR;
default:
return ELK_DEV_ADDR;
}
}
}
@@ -0,0 +1,51 @@
package org.springblade.common.constant;
import java.util.Arrays;
import java.util.List;
/**
* 租户常量
*
* @author Chill
*/
public interface TenantConstant {
/**
* 租户默认密码KEY
*/
String PASSWORD_KEY = "tenant.default.password";
/**
* 租户默认账号额度KEY
*/
String ACCOUNT_NUMBER_KEY = "tenant.default.accountNumber";
/**
* 租户默认菜单集合KEY
*/
String ACCOUNT_MENU_CODE_KEY = "tenant.default.menuCode";
/**
* 租户默认密码
*/
String DEFAULT_PASSWORD = "123456";
/**
* 租户授权码默认16位密钥
*/
String DES_KEY = "0000000000000000";
/**
* 租户默认账号额度
*/
Integer DEFAULT_ACCOUNT_NUMBER = -1;
/**
* 租户默认菜单集合
*/
List<String> MENU_CODES = Arrays.asList(
"desk", "flow", "work", "monitor", "resource", "role", "user", "dept", "dictbiz", "topmenu"
);
}
@@ -0,0 +1,249 @@
package org.springblade.common.constant;
/**
* 用户常量信息
*
* @author ruoyi
*/
public class UserConstants
{
/**
* 平台内系统用户的唯一标志
*/
public static final String SYS_USER = "SYS_USER";
/** 正常状态 */
public static final String NORMAL = "0";
/** 异常状态 */
public static final String EXCEPTION = "1";
/** 用户封禁状态 */
public static final String USER_DISABLE = "1";
/** 角色封禁状态 */
public static final String ROLE_DISABLE = "1";
/** 部门正常状态 */
public static final String DEPT_NORMAL = "0";
/** 部门停用状态 */
public static final String DEPT_DISABLE = "1";
/** 字典正常状态 */
public static final String DICT_NORMAL = "0";
/** 是否为系统默认(是) */
public static final String YES = "Y";
public static final String NO = "N";
/** 是否菜单外链(是) */
public static final String YES_FRAME = "0";
/** 是否菜单外链(否) */
public static final String NO_FRAME = "1";
/** 菜单类型(目录) */
public static final String TYPE_DIR = "M";
/** 菜单类型(菜单) */
public static final String TYPE_MENU = "C";
/** 菜单类型(按钮) */
public static final String TYPE_BUTTON = "F";
/** Layout组件标识 */
public final static String LAYOUT = "Layout";
/** ParentView组件标识 */
public final static String PARENT_VIEW = "ParentView";
/** InnerLink组件标识 */
public final static String INNER_LINK = "InnerLink";
public final static String WEBSOCKET_HEARTBEAT = "-heartbeat-";
/** 校验返回结果码 */
public final static String UNIQUE = "0";
public final static String NOT_UNIQUE = "1";
/**
* 用户名长度限制
*/
public static final int USERNAME_MIN_LENGTH = 2;
public static final int USERNAME_MAX_LENGTH = 20;
/**
* 密码长度限制
*/
public static final int PASSWORD_MIN_LENGTH = 5;
public static final int PASSWORD_MAX_LENGTH = 20;
/**
* 各种业务单据的内定自动编码规则标识
*/
public static final String ITEM_TYPE_CODE ="ITEM_TYPE_CODE";
public static final String ITEM_CODE ="ITEM_CODE";
public static final String MACHINERY_TYPE_CODE="MACHINERY_TYPE_CODE";
public static final String TASK_CODE="TASK_CODE";
public static final String DEFECT_CODE = "DEFECT_CODE";
public static final String SN_CODE = "SN_CODE";
public static final String TRANS_ORDER_CODE ="TRANS_ORDER_CODE";
public static final String ITEMRECPT_CODE ="ITEMRECPT_CODE"; //物料采购入库
public static final String WM_RTVENDOR_CODE ="WM_RTVENDOR_CODE";//退回供应商
public static final String ISSUE_CODE ="ISSUE_CODE"; //生产领料
public static final String RTISSUE_CODE ="RTISSUE_CODE"; //生产退料
public static final String PRODUCTRECPT_CODE ="PRODUCTRECPT_CODE"; //产品入库
public static final String PRODUCTSALSE_CODE ="PRODUCTSALSE_CODE"; //销售出库
public static final String RTSALSE_CODE ="RTSALSE_CODE"; //销售退货
public static final String TRANSFER_CODE ="TRANSFER_CODE"; //移库
public static final String STOCKTAKING_CODE ="STOCKTAKING_CODE"; //盘库单
public static final String FEEDBACK_CODE ="FEEDBACK_CODE"; //报工单
/**
* 单据的状态类型
*/
public static final String ORDER_STATUS_PREPARE="PREPARE";
public static final String ORDER_STATUS_CONFIRMED="CONFIRMED";
public static final String ORDER_STATUS_APPROVING="APPROVING";
public static final String ORDER_STATUS_APPROVED="APPROVED";
public static final String ORDER_STATUS_FINISHED="FINISHED";
/**
* 维护类型
*/
public static final String MAINTEN_TYPE_REGULAR="REGULAR";
public static final String MAINTEN_TYPE_USAGE="USAGE";
/**
* 甘特图中的TASK类型
*/
public static final String GANTT_TASK_TYPE_TASK="task";
public static final String GANTT_TASK_TYPE_PROJECT="project";
/**
* 报表相关
*/
public static final String REPORT_PRINT_TYPE ="print";
public static final String REPORT_PDF_TYPE ="pdf";
public static final String REPORT_EXCEL_TYPE ="excel";
public static final String REPORT_WORD_TYPE ="word";
public static final String REPORT_JASPER_PATH="reports/jasper/";
/**
* 库存事务类型
*/
public static final String TRANSACTION_TYPE_ITEM_RECPT = "ITEM_RECPT"; //原材料接收入库
public static final String TRANSACTION_TYPE_ITEM_RTV = "ITEM_RTV"; //原材料退回供应商
public static final String TRANSACTION_TYPE_ITEM_ISSUE_OUT = "ITEM_ISSUE_OUT"; //生产领用-出库事务
public static final String TRANSACTION_TYPE_ITEM_ISSUE_IN = "ITEM_ISSUE_IN"; //生产领用-入库事务
public static final String TRANSACTION_TYPE_OUTSOURCE_ISSUE_OUT ="OUTSOURCE_ISSUE_OUT"; //外协领用-出库事务
public static final String TRANSACTION_TYPE_OUTSOURCE_RECPT_IN ="OUTSOURCE_RECPT_IN"; //外协入库-入库事务
public static final String TRANSACTION_TYPE_ITEM_RT_ISSUE_OUT = "ITEM_RT_ISSUE_OUT"; //生产退料-出库事务
public static final String TRANSACTION_TYPE_ITEM_RT_ISSUE_IN = "ITEM_RT_ISSUE_IN"; //生产退料-入库事务
public static final String TRANSACTION_TYPE_WAREHOUSE_TRANS_OUT = "TRANS_OUT"; //移库,移出
public static final String TRANSACTION_TYPE_WAREHOUSE_TRANS_IN = "TRANS_IN"; //移库,移入
public static final String TRANSACTION_TYPE_ITEM_CONSUME = "ITEM_CONSUME";//物料生产消耗
public static final String TRANSACTION_TYPE_PRODUCT_PRODUCE = "PRODUCT_PRODUCE";//产品生产
public static final String TRANSACTION_TYPE_PRODUCT_RECPT_OUT = "PRODUCT_RECPT_OUT"; //产品入库-出库事务
public static final String TRANSACTION_TYPE_PRODUCT_RECPT_IN = "PRODUCT_RECPT_IN"; //产品入库-入库事务
public static final String TRANSACTION_TYPE_PRODUCT_ISSUE = "PRODUCT_SALSE"; //销售出库
public static final String TRANSACTION_TYPE_PRODUCT_RS = "PRODUCT_RT"; //销售退货
/**
* 轮班方式
*/
public static final String CAL_SHIFT_TYPE_SINGLE="SINGLE";
public static final String CAL_SHIFT_TYPE_TWO="SHIFT_TWO";
public static final String CAL_SHIFT_TYPE_THREE="SHIFT_THREE";
public static final String CAL_SHIFT_NAME_DAY="白班";
public static final String CAL_SHIFT_NAME_NIGHT="夜班";
public static final String CAL_SHIFT_NAME_MID="中班";
public static final String CAL_SHIFT_METHOD_QUARTER="QUARTER";
public static final String CAL_SHIFT_METHOD_MONTH="MONTH";
public static final String CAL_SHIFT_METHOD_WEEK="WEEK";
public static final String CAL_SHIFT_METHOD_DAY="DAY";
/**
* 排班日历的查询方式
*/
public static final String CAL_QUERY_BY_TYPE="TYPE";
public static final String CAL_QUERY_BY_TEAM="TEAM";
public static final String CAL_QUERY_BY_USER="USER";
/**
* 生产投料单据的类型
*/
public static final String TASK_ISSUE_DOC_TYPE_ISSUE="ISSUE"; //领料单
public static final String TASK_ISSUE_DOC_TYPE_TRANS="TRANS"; //流转单
/**
* 检测单类型这里的类型是大类
* 首检末检等等是过程检验中的子分类
*/
public static final String QC_TYPE_IQC = "IQC"; //来料检验单
public static final String QC_TYPE_IPQC = "IPQC"; //过程检验单
public static final String QC_TYPE_OQC = "OQC"; //出货检验
/**
* 默认线边库对应的仓库库区库位编码
*/
public static final String VIRTUAL_WH ="XBK_VIRTUAL";
public static final String VIRTUAL_WS ="XBKKQ_VIRTUAL";
public static final String VIRTUAL_WA ="XBKKW_VIRTUAL";
/**
* 条码格式
*/
public static final String QR_CODE = "QR_CODE";
public static final String EAN_CODE = "EAN_CODE";
public static final String UPC_CODE = "UPC_CODE";
public static final String CODE39_CODE = "CODE39_CODE";
/**
* 条码类型
*/
public static final String BARCODE_TYPE_ITEM = "ITEM"; //物料
public static final String BARCODE_TYPE_PACKAGE = "PACKAGE"; //装箱单
public static final String BARCODE_TYPE_STOCK = "STOCK"; //库存
public static final String BARCODE_TYPE_MACHINERY = "MACHINERY"; //设备
public static final String BARCODE_TYPE_WORKSTATION = "WORKSTATION"; //工作站
public static final String BARCODE_TYPE_WAREHOUSE = "WAREHOUSE"; //仓库
public static final String BARCODE_TYPE_STORAGELOCATION = "LOCATION"; //库区
public static final String BARCODE_TYPE_STORAGEAREA = "AREA"; //库位
public static final String BARCODE_TYPE_TRANSORDER = "TRANSORDER"; //流转单
public static final String BARCODE_TYPE_CLIENT = "CLIENT"; //客户
public static final String BARCODE_TYPE_VENDOR = "VENDOR"; //供应商
public static final String BARCODE_TYPE_SN = "SN";
/**
* 消息状态
*/
public static final String MESSAGE_STATUS_UNREAD = "UNREAD"; //未读
public static final String MESSAGE_STATUS_READ = "READ";//已读
public static final String MESSAGE_STATUS_PROCEED = "PROCEED";//已处理
/**
* 库存盘点方式
*/
public static final String WM_STOCK_TAKING_TYPE_BLIND = "BLIND"; //盲盘
public static final String WM_STOCK_TAKING_TYPE_OPEN = "OPEN"; //明盘
/**
* 库存盘点结果
*/
public static final String WM_STOCK_TAKING_STATUS_LOSS = "LOSS";// 盘亏
public static final String WM_STOCK_TAKING_STATUS_PROFIT = "PROFIT"; //盘盈
public static final String WM_STOCK_TAKING_STATUS_NORMAL = "NORMAL"; //正常
}
@@ -0,0 +1,44 @@
package org.springblade.common.enums;
public enum CycleMethodMnum {
CYCLE_METHOD_YEAR("YEAR", "按年"),
CYCLE_METHOD_MONTH("MONTH", "按月"),
CYCLE_METHOD_DAY("DAY", "按日"),
CYCLE_METHOD_HOUR("HOUR", "按小时"),
CYCLE_METHOD_MINUTE("MINUTE", "按分钟"),
CYCLE_METHOD_OTHER("OTHER", "其他");
private String code;
private String name;
CycleMethodMnum(String code, String name) {
this.code = code;
this.name = name;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public static CycleMethodMnum getByCode(String code) {
for (CycleMethodMnum value : CycleMethodMnum.values()) {
if (value.getCode().equals(code)) {
return value;
}
}
return CYCLE_METHOD_OTHER;
}
}
@@ -0,0 +1,24 @@
package org.springblade.common.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 业务字典枚举类
*
* @author Chill
*/
@Getter
@AllArgsConstructor
public enum DictBizEnum {
/**
* 测试
*/
TEST("test"),
;
final String name;
}
@@ -0,0 +1,80 @@
package org.springblade.common.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 系统字典枚举类
*
* @author Chill
*/
@Getter
@AllArgsConstructor
public enum DictEnum {
/**
* 性别
*/
SEX("sex"),
/**
* 通知类型
*/
NOTICE("notice"),
/**
* 菜单类型
*/
MENU_CATEGORY("menu_category"),
/**
* 按钮功能
*/
BUTTON_FUNC("button_func"),
/**
* 是否
*/
YES_NO("yes_no"),
/**
* 流程类型
*/
FLOW("flow"),
/**
* 机构类型
*/
ORG_CATEGORY("org_category"),
/**
* 数据权限
*/
DATA_SCOPE_TYPE("data_scope_type"),
/**
* 接口权限
*/
API_SCOPE_TYPE("api_scope_type"),
/**
* 权限类型
*/
SCOPE_CATEGORY("scope_category"),
/**
* 对象存储类型
*/
OSS("oss"),
/**
* 短信服务类型
*/
SMS("sms"),
/**
* 岗位类型
*/
POST_CATEGORY("post_category"),
/**
* 行政区划
*/
REGION("region"),
/**
* 用户平台
*/
USER_TYPE("user_type"),
;
final String name;
}
@@ -0,0 +1,42 @@
package org.springblade.common.enums;
public enum PartTypeEnum {
PART_TYPE_INPUTCHAR("INPUTCHAR", "传入字符", 0),
PART_TYPE_NOWDATE("NOWDATE", "当前日期", 1),
PART_TYPE_FIXCHAR("FIXCHAR", "固定字符", 2),
PART_TYPE_SERIALNO("SERIALNO", "流水号", 3),
PART_TYPE_OTHER("OTHER", "其他", 99);
private final String code;
private final String name;
private final Integer beanIndex;
PartTypeEnum(String code, String name, Integer beanIndex) {
this.code = code;
this.name = name;
this.beanIndex = beanIndex;
}
public String getCode() {
return code;
}
public String getName() {
return name;
}
public Integer getBeanIndex() {
return beanIndex;
}
public static PartTypeEnum getByCode(String code) {
for (PartTypeEnum value : PartTypeEnum.values()) {
if (value.getCode().equals(code)) {
return value;
}
}
return PART_TYPE_OTHER;
}
}
@@ -0,0 +1,45 @@
package org.springblade.common.event;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.launch.props.BladeProperties;
import org.springblade.core.launch.server.ServerInfo;
import org.springblade.core.log.constant.EventConstant;
import org.springblade.core.log.event.ApiLogEvent;
import org.springblade.core.log.model.LogApi;
import org.springblade.core.log.utils.LogAbstractUtil;
import org.springblade.modules.system.service.ILogService;
import org.springframework.context.event.EventListener;
import org.springframework.core.annotation.Order;
import org.springframework.scheduling.annotation.Async;
import java.util.Map;
/**
* 异步监听日志事件
*
* @author Chill
*/
@Slf4j
@AllArgsConstructor
public class ApiLogListener {
private final ILogService logService;
private final ServerInfo serverInfo;
private final BladeProperties bladeProperties;
@Async
@Order
@EventListener(ApiLogEvent.class)
public void saveApiLog(ApiLogEvent event) {
Map<String, Object> source = (Map<String, Object>) event.getSource();
LogApi logApi = (LogApi) source.get(EventConstant.EVENT_LOG);
LogAbstractUtil.addOtherInfoToLog(logApi, bladeProperties, serverInfo);
logService.saveApiLog(logApi);
}
}
@@ -0,0 +1,47 @@
package org.springblade.common.event;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.launch.props.BladeProperties;
import org.springblade.core.launch.server.ServerInfo;
import org.springblade.core.log.constant.EventConstant;
import org.springblade.core.log.event.ErrorLogEvent;
import org.springblade.core.log.model.LogError;
import org.springblade.core.log.utils.LogAbstractUtil;
import org.springblade.modules.system.service.ILogService;
import org.springframework.context.event.EventListener;
import org.springframework.core.annotation.Order;
import org.springframework.scheduling.annotation.Async;
import java.util.Map;
/**
* 异步监听错误日志事件
*
* @author Chill
*/
@Slf4j
@AllArgsConstructor
public class ErrorLogListener {
private final ILogService logService;
private final ServerInfo serverInfo;
private final BladeProperties bladeProperties;
@Async
@Order
@EventListener(ErrorLogEvent.class)
public void saveErrorLog(ErrorLogEvent event) {
try {
Map<String, Object> source = (Map<String, Object>) event.getSource();
LogError logError = (LogError) source.get(EventConstant.EVENT_LOG);
LogAbstractUtil.addOtherInfoToLog(logError, bladeProperties, serverInfo);
logService.saveErrorLog(logError);
} catch (Exception e) {
// 可以根据需要进行更多的异常处理例如发送警报等
log.error("保存错误日志时发生异常", e);
}
}
}
@@ -0,0 +1,43 @@
package org.springblade.common.event;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.launch.props.BladeProperties;
import org.springblade.core.launch.server.ServerInfo;
import org.springblade.core.log.constant.EventConstant;
import org.springblade.core.log.event.UsualLogEvent;
import org.springblade.core.log.model.LogUsual;
import org.springblade.core.log.utils.LogAbstractUtil;
import org.springblade.modules.system.service.ILogService;
import org.springframework.context.event.EventListener;
import org.springframework.core.annotation.Order;
import org.springframework.scheduling.annotation.Async;
import java.util.Map;
/**
* 异步监听日志事件
*
* @author Chill
*/
@Slf4j
@AllArgsConstructor
public class UsualLogListener {
private final ILogService logService;
private final ServerInfo serverInfo;
private final BladeProperties bladeProperties;
@Async
@Order
@EventListener(UsualLogEvent.class)
public void saveUsualLog(UsualLogEvent event) {
Map<String, Object> source = (Map<String, Object>) event.getSource();
LogUsual logUsual = (LogUsual) source.get(EventConstant.EVENT_LOG);
LogAbstractUtil.addOtherInfoToLog(logUsual, bladeProperties, serverInfo);
logService.saveUsualLog(logUsual);
}
}
@@ -0,0 +1,52 @@
package org.springblade.common.filter;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* 演示过滤器
*
* @author Chill
*/
public class PreviewFilter implements Filter {
private static final List<String> KEYS = new ArrayList<>();
static {
KEYS.add("notice");
KEYS.add("process");
KEYS.add("work");
KEYS.add("token");
}
@Override
public void init(FilterConfig filterConfig) {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
String path = httpServletRequest.getServletPath();
String method = httpServletRequest.getMethod();
String get = "GET";
if (method.equals(get) || KEYS.stream().anyMatch(path::contains)) {
filterChain.doFilter(servletRequest, servletResponse);
} else {
throw new RuntimeException("演示环境暂时无法操作!");
}
}
@Override
public void destroy() {
}
}
@@ -0,0 +1,106 @@
package org.springblade.common.handler;
import lombok.RequiredArgsConstructor;
import org.springblade.core.cache.utils.CacheUtil;
import org.springblade.core.datascope.constant.DataScopeConstant;
import org.springblade.core.datascope.handler.ScopeModelHandler;
import org.springblade.core.datascope.model.DataScopeModel;
import org.springblade.core.tool.utils.CollectionUtil;
import org.springblade.core.tool.utils.Func;
import org.springblade.core.tool.utils.StringPool;
import org.springblade.core.tool.utils.StringUtil;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static org.springblade.core.cache.constant.CacheConstant.SYS_CACHE;
/**
* BladeScopeModelHandler
*
* @author Chill
*/
//若开启动态数据源功能则加上@Master注解指定权限数据库为主库
//@Master
@RequiredArgsConstructor
public class BladeScopeModelHandler implements ScopeModelHandler {
private static final String SCOPE_CACHE_CODE = "dataScope:code:";
private static final String SCOPE_CACHE_CLASS = "dataScope:class:";
private static final String DEPT_CACHE_ANCESTORS = "dept:ancestors:";
private static final DataScopeModel SEARCHED_DATA_SCOPE_MODEL = new DataScopeModel(Boolean.TRUE);
private final JdbcTemplate jdbcTemplate;
/**
* 获取数据权限
*
* @param mapperId 数据权限mapperId
* @param roleId 用户角色集合
* @return DataScopeModel
*/
@Override
public DataScopeModel getDataScopeByMapper(String mapperId, String roleId) {
List<Object> args = new ArrayList<>(Collections.singletonList(mapperId));
List<Long> roleIds = Func.toLongList(roleId);
args.addAll(roleIds);
// 增加searched字段防止未配置的参数重复读库导致缓存击穿
// 后续若有新增配置则会清空缓存重新加载
DataScopeModel dataScope = CacheUtil.get(SYS_CACHE, SCOPE_CACHE_CLASS, mapperId + StringPool.COLON + roleId, DataScopeModel.class, Boolean.FALSE);
if (dataScope == null || !dataScope.getSearched()) {
List<DataScopeModel> list = jdbcTemplate.query(DataScopeConstant.dataByMapper(roleIds.size()), args.toArray(), new BeanPropertyRowMapper<>(DataScopeModel.class));
if (CollectionUtil.isNotEmpty(list)) {
dataScope = list.iterator().next();
dataScope.setSearched(Boolean.TRUE);
} else {
dataScope = SEARCHED_DATA_SCOPE_MODEL;
}
CacheUtil.put(SYS_CACHE, SCOPE_CACHE_CLASS, mapperId + StringPool.COLON + roleId, dataScope, Boolean.FALSE);
}
return StringUtil.isNotBlank(dataScope.getResourceCode()) ? dataScope : null;
}
/**
* 获取数据权限
*
* @param code 数据权限资源编号
* @return DataScopeModel
*/
@Override
public DataScopeModel getDataScopeByCode(String code) {
DataScopeModel dataScope = CacheUtil.get(SYS_CACHE, SCOPE_CACHE_CODE, code, DataScopeModel.class, Boolean.FALSE);
// 增加searched字段防止未配置的参数重复读库导致缓存击穿
// 后续若有新增配置则会清空缓存重新加载
if (dataScope == null || !dataScope.getSearched()) {
List<DataScopeModel> list = jdbcTemplate.query(DataScopeConstant.DATA_BY_CODE, new Object[]{code}, new BeanPropertyRowMapper<>(DataScopeModel.class));
if (CollectionUtil.isNotEmpty(list)) {
dataScope = list.iterator().next();
dataScope.setSearched(Boolean.TRUE);
} else {
dataScope = SEARCHED_DATA_SCOPE_MODEL;
}
CacheUtil.put(SYS_CACHE, SCOPE_CACHE_CODE, code, dataScope, Boolean.FALSE);
}
return StringUtil.isNotBlank(dataScope.getResourceCode()) ? dataScope : null;
}
/**
* 获取部门子级
*
* @param deptId 部门id
* @return deptIds
*/
@Override
public List<Long> getDeptAncestors(Long deptId) {
List ancestors = CacheUtil.get(SYS_CACHE, DEPT_CACHE_ANCESTORS, deptId, List.class);
if (CollectionUtil.isEmpty(ancestors)) {
ancestors = jdbcTemplate.queryForList(DataScopeConstant.DATA_BY_DEPT, new Object[]{deptId}, Long.class);
CacheUtil.put(SYS_CACHE, DEPT_CACHE_ANCESTORS, deptId, ancestors);
}
return ancestors;
}
}
@@ -0,0 +1,29 @@
package org.springblade.common.launch;
import org.springblade.common.constant.LauncherConstant;
import org.springblade.core.auto.service.AutoService;
import org.springblade.core.launch.service.LauncherService;
import org.springblade.core.launch.utils.PropsUtil;
import org.springframework.boot.builder.SpringApplicationBuilder;
import java.util.Properties;
/**
* 启动参数拓展
*
* @author smallchil
*/
@AutoService(LauncherService.class)
public class LauncherServiceImpl implements LauncherService {
@Override
public void launcher(SpringApplicationBuilder builder, String appName, String profile, boolean isLocalDev) {
Properties props = System.getProperties();
PropsUtil.setProperty(props, "spring.cloud.sentinel.transport.dashboard", LauncherConstant.sentinelAddr(profile));
PropsUtil.setProperty(props, "spring.datasource.dynamic.enabled", "false");
// 开启elk日志
//PropsUtil.setProperty(props, "blade.log.elk.destination", LauncherConstant.elkAddr(profile));
}
}
@@ -0,0 +1,11 @@
package org.springblade.common.utils;
/**
* 通用工具类
*
* @author Chill
*/
public class CommonUtil {
}
@@ -0,0 +1,187 @@
package org.springblade.common.utils;
import java.lang.management.ManagementFactory;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Date;
import org.apache.commons.lang3.time.DateFormatUtils;
/**
* 时间工具类
*
* @author ruoyi
*/
public class DateUtils extends org.apache.commons.lang3.time.DateUtils
{
public static String YYYY = "yyyy";
public static String YYYY_MM = "yyyy-MM";
public static String YYYY_MM_DD = "yyyy-MM-dd";
public static String YYYYMMDDHHMMSS = "yyyyMMddHHmmss";
public static String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";
private static String[] parsePatterns = {
"yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM",
"yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM",
"yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"};
/**
* 获取当前Date型日期
*
* @return Date() 当前日期
*/
public static Date getNowDate()
{
return new Date();
}
/**
* 获取当前日期, 默认格式为yyyy-MM-dd
*
* @return String
*/
public static String getDate()
{
return dateTimeNow(YYYY_MM_DD);
}
public static final String getTime()
{
return dateTimeNow(YYYY_MM_DD_HH_MM_SS);
}
public static final String dateTimeNow()
{
return dateTimeNow(YYYYMMDDHHMMSS);
}
public static final String dateTimeNow(final String format)
{
return parseDateToStr(format, new Date());
}
public static final String dateTime(final Date date)
{
return parseDateToStr(YYYY_MM_DD, date);
}
public static final String parseDateToStr(final String format, final Date date)
{
return new SimpleDateFormat(format).format(date);
}
public static final Date dateTime(final String format, final String ts)
{
try
{
return new SimpleDateFormat(format).parse(ts);
}
catch (ParseException e)
{
throw new RuntimeException(e);
}
}
/**
* 日期路径 即年// 如2018/08/08
*/
public static final String datePath()
{
Date now = new Date();
return DateFormatUtils.format(now, "yyyy/MM/dd");
}
/**
* 日期路径 即年// 如20180808
*/
public static final String dateTime()
{
Date now = new Date();
return DateFormatUtils.format(now, "yyyyMMdd");
}
/**
* 日期型字符串转化为日期 格式
*/
public static Date parseDate(Object str)
{
if (str == null)
{
return null;
}
try
{
return parseDate(str.toString(), parsePatterns);
}
catch (ParseException e)
{
return null;
}
}
/**
* 获取服务器启动时间
*/
public static Date getServerStartDate()
{
long time = ManagementFactory.getRuntimeMXBean().getStartTime();
return new Date(time);
}
/**
* 计算相差天数
*/
public static int differentDaysByMillisecond(Date date1, Date date2)
{
return Math.abs((int) ((date2.getTime() - date1.getTime()) / (1000 * 3600 * 24)));
}
/**
* 计算两个时间差
*/
public static String getDatePoor(Date endDate, Date nowDate)
{
long nd = 1000 * 24 * 60 * 60;
long nh = 1000 * 60 * 60;
long nm = 1000 * 60;
// long ns = 1000;
// 获得两个时间的毫秒时间差异
long diff = endDate.getTime() - nowDate.getTime();
// 计算差多少天
long day = diff / nd;
// 计算差多少小时
long hour = diff % nd / nh;
// 计算差多少分钟
long min = diff % nd % nh / nm;
// 计算差多少秒//输出结果
// long sec = diff % nd % nh % nm / ns;
return day + "" + hour + "小时" + min + "分钟";
}
/**
* 增加 LocalDateTime ==> Date
*/
public static Date toDate(LocalDateTime temporalAccessor)
{
ZonedDateTime zdt = temporalAccessor.atZone(ZoneId.systemDefault());
return Date.from(zdt.toInstant());
}
/**
* 增加 LocalDate ==> Date
*/
public static Date toDate(LocalDate temporalAccessor)
{
LocalDateTime localDateTime = LocalDateTime.of(temporalAccessor, LocalTime.of(0, 0, 0));
ZonedDateTime zdt = localDateTime.atZone(ZoneId.systemDefault());
return Date.from(zdt.toInstant());
}
}
@@ -0,0 +1,584 @@
package org.springblade.common.utils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import cn.hutool.core.text.StrFormatter;
import org.springblade.common.constant.Constants;
import org.springframework.util.AntPathMatcher;
/**
* 字符串工具类
*
* @author ruoyi
*/
public class StringUtils extends org.apache.commons.lang3.StringUtils
{
/** 空字符串 */
private static final String NULLSTR = "";
/** 下划线 */
private static final char SEPARATOR = '_';
/**
* 获取参数不为空值
*
* @param value defaultValue 要判断的value
* @return value 返回值
*/
public static <T> T nvl(T value, T defaultValue)
{
return value != null ? value : defaultValue;
}
/**
* * 判断一个Collection是否为空 包含ListSetQueue
*
* @param coll 要判断的Collection
* @return true为空 false非空
*/
public static boolean isEmpty(Collection<?> coll)
{
return isNull(coll) || coll.isEmpty();
}
/**
* * 判断一个Collection是否非空包含ListSetQueue
*
* @param coll 要判断的Collection
* @return true非空 false
*/
public static boolean isNotEmpty(Collection<?> coll)
{
return !isEmpty(coll);
}
/**
* * 判断一个对象数组是否为空
*
* @param objects 要判断的对象数组
** @return true为空 false非空
*/
public static boolean isEmpty(Object[] objects)
{
return isNull(objects) || (objects.length == 0);
}
/**
* * 判断一个对象数组是否非空
*
* @param objects 要判断的对象数组
* @return true非空 false
*/
public static boolean isNotEmpty(Object[] objects)
{
return !isEmpty(objects);
}
/**
* * 判断一个Map是否为空
*
* @param map 要判断的Map
* @return true为空 false非空
*/
public static boolean isEmpty(Map<?, ?> map)
{
return isNull(map) || map.isEmpty();
}
/**
* * 判断一个Map是否为空
*
* @param map 要判断的Map
* @return true非空 false
*/
public static boolean isNotEmpty(Map<?, ?> map)
{
return !isEmpty(map);
}
/**
* * 判断一个字符串是否为空串
*
* @param str String
* @return true为空 false非空
*/
public static boolean isEmpty(String str)
{
return isNull(str) || NULLSTR.equals(str.trim());
}
/**
* * 判断一个字符串是否为非空串
*
* @param str String
* @return true非空串 false空串
*/
public static boolean isNotEmpty(String str)
{
return !isEmpty(str);
}
/**
* * 判断一个对象是否为空
*
* @param object Object
* @return true为空 false非空
*/
public static boolean isNull(Object object)
{
return object == null;
}
/**
* * 判断一个对象是否非空
*
* @param object Object
* @return true非空 false
*/
public static boolean isNotNull(Object object)
{
return !isNull(object);
}
/**
* * 判断一个对象是否是数组类型Java基本型别的数组
*
* @param object 对象
* @return true是数组 false不是数组
*/
public static boolean isArray(Object object)
{
return isNotNull(object) && object.getClass().isArray();
}
/**
* 去空格
*/
public static String trim(String str)
{
return (str == null ? "" : str.trim());
}
/**
* 截取字符串
*
* @param str 字符串
* @param start 开始
* @return 结果
*/
public static String substring(final String str, int start)
{
if (str == null)
{
return NULLSTR;
}
if (start < 0)
{
start = str.length() + start;
}
if (start < 0)
{
start = 0;
}
if (start > str.length())
{
return NULLSTR;
}
return str.substring(start);
}
/**
* 截取字符串
*
* @param str 字符串
* @param start 开始
* @param end 结束
* @return 结果
*/
public static String substring(final String str, int start, int end)
{
if (str == null)
{
return NULLSTR;
}
if (end < 0)
{
end = str.length() + end;
}
if (start < 0)
{
start = str.length() + start;
}
if (end > str.length())
{
end = str.length();
}
if (start > end)
{
return NULLSTR;
}
if (start < 0)
{
start = 0;
}
if (end < 0)
{
end = 0;
}
return str.substring(start, end);
}
/**
* 格式化文本, {} 表示占位符<br>
* 此方法只是简单将占位符 {} 按照顺序替换为参数<br>
* 如果想输出 {} 使用 \\转义 { 即可如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可<br>
* <br>
* 通常使用format("this is {} for {}", "a", "b") -> this is a for b<br>
* 转义{} format("this is \\{} for {}", "a", "b") -> this is \{} for a<br>
* 转义\ format("this is \\\\{} for {}", "a", "b") -> this is \a for b<br>
*
* @param template 文本模板被替换的部分用 {} 表示
* @param params 参数值
* @return 格式化后的文本
*/
public static String format(String template, Object... params)
{
if (isEmpty(params) || isEmpty(template))
{
return template;
}
return StrFormatter.format(template, params);
}
/**
* 是否为http(s)://开头
*
* @param link 链接
* @return 结果
*/
public static boolean ishttp(String link)
{
return StringUtils.startsWithAny(link, Constants.HTTP, Constants.HTTPS);
}
/**
* 字符串转set
*
* @param str 字符串
* @param sep 分隔符
* @return set集合
*/
public static final Set<String> str2Set(String str, String sep)
{
return new HashSet<String>(str2List(str, sep, true, false));
}
/**
* 字符串转list
*
* @param str 字符串
* @param sep 分隔符
* @param filterBlank 过滤纯空白
* @param trim 去掉首尾空白
* @return list集合
*/
public static final List<String> str2List(String str, String sep, boolean filterBlank, boolean trim)
{
List<String> list = new ArrayList<String>();
if (StringUtils.isEmpty(str))
{
return list;
}
// 过滤空白字符串
if (filterBlank && StringUtils.isBlank(str))
{
return list;
}
String[] split = str.split(sep);
for (String string : split)
{
if (filterBlank && StringUtils.isBlank(string))
{
continue;
}
if (trim)
{
string = string.trim();
}
list.add(string);
}
return list;
}
/**
* 查找指定字符串是否包含指定字符串列表中的任意一个字符串同时串忽略大小写
*
* @param cs 指定字符串
* @param searchCharSequences 需要检查的字符串数组
* @return 是否包含任意一个字符串
*/
public static boolean containsAnyIgnoreCase(CharSequence cs, CharSequence... searchCharSequences)
{
if (isEmpty(cs) || isEmpty(searchCharSequences))
{
return false;
}
for (CharSequence testStr : searchCharSequences)
{
if (containsIgnoreCase(cs, testStr))
{
return true;
}
}
return false;
}
/**
* 驼峰转下划线命名
*/
public static String toUnderScoreCase(String str)
{
if (str == null)
{
return null;
}
StringBuilder sb = new StringBuilder();
// 前置字符是否大写
boolean preCharIsUpperCase = true;
// 当前字符是否大写
boolean curreCharIsUpperCase = true;
// 下一字符是否大写
boolean nexteCharIsUpperCase = true;
for (int i = 0; i < str.length(); i++)
{
char c = str.charAt(i);
if (i > 0)
{
preCharIsUpperCase = Character.isUpperCase(str.charAt(i - 1));
}
else
{
preCharIsUpperCase = false;
}
curreCharIsUpperCase = Character.isUpperCase(c);
if (i < (str.length() - 1))
{
nexteCharIsUpperCase = Character.isUpperCase(str.charAt(i + 1));
}
if (preCharIsUpperCase && curreCharIsUpperCase && !nexteCharIsUpperCase)
{
sb.append(SEPARATOR);
}
else if ((i != 0 && !preCharIsUpperCase) && curreCharIsUpperCase)
{
sb.append(SEPARATOR);
}
sb.append(Character.toLowerCase(c));
}
return sb.toString();
}
/**
* 是否包含字符串
*
* @param str 验证字符串
* @param strs 字符串组
* @return 包含返回true
*/
public static boolean inStringIgnoreCase(String str, String... strs)
{
if (str != null && strs != null)
{
for (String s : strs)
{
if (str.equalsIgnoreCase(trim(s)))
{
return true;
}
}
}
return false;
}
/**
* 将下划线大写方式命名的字符串转换为驼峰式如果转换前的下划线大写方式命名的字符串为空则返回空字符串 例如HELLO_WORLD->HelloWorld
*
* @param name 转换前的下划线大写方式命名的字符串
* @return 转换后的驼峰式命名的字符串
*/
public static String convertToCamelCase(String name)
{
StringBuilder result = new StringBuilder();
// 快速检查
if (name == null || name.isEmpty())
{
// 没必要转换
return "";
}
else if (!name.contains("_"))
{
// 不含下划线仅将首字母大写
return name.substring(0, 1).toUpperCase() + name.substring(1);
}
// 用下划线将原始字符串分割
String[] camels = name.split("_");
for (String camel : camels)
{
// 跳过原始字符串中开头结尾的下换线或双重下划线
if (camel.isEmpty())
{
continue;
}
// 首字母大写
result.append(camel.substring(0, 1).toUpperCase());
result.append(camel.substring(1).toLowerCase());
}
return result.toString();
}
/**
* 驼峰式命名法 例如user_name->userName
*/
public static String toCamelCase(String s)
{
if (s == null)
{
return null;
}
s = s.toLowerCase();
StringBuilder sb = new StringBuilder(s.length());
boolean upperCase = false;
for (int i = 0; i < s.length(); i++)
{
char c = s.charAt(i);
if (c == SEPARATOR)
{
upperCase = true;
}
else if (upperCase)
{
sb.append(Character.toUpperCase(c));
upperCase = false;
}
else
{
sb.append(c);
}
}
return sb.toString();
}
/**
* 查找指定字符串是否匹配指定字符串列表中的任意一个字符串
*
* @param str 指定字符串
* @param strs 需要检查的字符串数组
* @return 是否匹配
*/
public static boolean matches(String str, List<String> strs)
{
if (isEmpty(str) || isEmpty(strs))
{
return false;
}
for (String pattern : strs)
{
if (isMatch(pattern, str))
{
return true;
}
}
return false;
}
/**
* 判断url是否与规则配置:
* ? 表示单个字符;
* * 表示一层路径内的任意字符串不可跨层级;
* ** 表示任意层路径;
*
* @param pattern 匹配规则
* @param url 需要匹配的url
* @return
*/
public static boolean isMatch(String pattern, String url)
{
AntPathMatcher matcher = new AntPathMatcher();
return matcher.match(pattern, url);
}
@SuppressWarnings("unchecked")
public static <T> T cast(Object obj)
{
return (T) obj;
}
/**
* 数字左边补齐0使之达到指定长度注意如果数字转换为字符串后长度大于size则只保留 最后size个字符
*
* @param num 数字对象
* @param size 字符串指定长度
* @return 返回数字的字符串格式该字符串为指定长度
*/
public static final String padl(final Number num, final int size)
{
return padl(num.toString(), size, '0');
}
/**
* 字符串左补齐如果原始字符串s长度大于size则只保留最后size个字符
*
* @param s 原始字符串
* @param size 字符串指定长度
* @param c 用于补齐的字符
* @return 返回指定长度的字符串由原字符串左补齐或截取得到
*/
public static final String padl(final String s, final int size, final char c)
{
final StringBuilder sb = new StringBuilder(size);
if (s != null)
{
final int len = s.length();
if (s.length() <= size)
{
for (int i = size - len; i > 0; i--)
{
sb.append(c);
}
sb.append(s);
}
else
{
return s.substring(len - size, len);
}
}
else
{
for (int i = size; i > 0; i--)
{
sb.append(c);
}
}
return sb.toString();
}
}
@@ -0,0 +1,141 @@
package org.springblade.job.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.AllArgsConstructor;
import org.springblade.core.boot.ctrl.BladeController;
import org.springblade.core.launch.constant.AppConstant;
import org.springblade.core.mp.support.Condition;
import org.springblade.core.mp.support.Query;
import org.springblade.core.tool.api.R;
import org.springblade.core.tool.utils.Func;
import org.springblade.job.pojo.entity.JobInfo;
import org.springblade.job.service.IJobInfoService;
import org.springblade.job.pojo.vo.JobInfoVO;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
/**
* 任务信息表 控制器
*
* @author BladeX
*/
@RestController
@AllArgsConstructor
@RequestMapping(AppConstant.APPLICATION_JOB_NAME + "/job-info")
@Tag(name = "任务信息表", description = "任务信息表接口")
public class JobInfoController extends BladeController {
private final IJobInfoService jobInfoService;
/**
* 任务信息表 详情
*/
@GetMapping("/detail")
@ApiOperationSupport(order = 1)
@Operation(summary = "详情", description = "传入jobInfo")
public R<JobInfo> detail(JobInfo jobInfo) {
JobInfo detail = jobInfoService.getOne(Condition.getQueryWrapper(jobInfo));
return R.data(detail);
}
/**
* 任务信息表 分页
*/
@GetMapping("/list")
@ApiOperationSupport(order = 2)
@Operation(summary = "分页", description = "传入jobInfo")
public R<IPage<JobInfo>> list(@Parameter(hidden = true) @RequestParam Map<String, Object> jobInfo, Query query) {
IPage<JobInfo> pages = jobInfoService.page(Condition.getPage(query), Condition.getQueryWrapper(jobInfo, JobInfo.class));
return R.data(pages);
}
/**
* 任务信息表 自定义分页
*/
@GetMapping("/page")
@ApiOperationSupport(order = 3)
@Operation(summary = "分页", description = "传入jobInfo")
public R<IPage<JobInfoVO>> page(JobInfoVO jobInfo, Query query) {
IPage<JobInfoVO> pages = jobInfoService.selectJobInfoPage(Condition.getPage(query), jobInfo);
return R.data(pages);
}
/**
* 任务信息表 新增
*/
@PostMapping("/save")
@ApiOperationSupport(order = 4)
@Operation(summary = "新增", description = "传入jobInfo")
public R save(@Valid @RequestBody JobInfo jobInfo) {
return R.status(jobInfoService.save(jobInfo));
}
/**
* 任务信息表 修改
*/
@PostMapping("/update")
@ApiOperationSupport(order = 5)
@Operation(summary = "修改", description = "传入jobInfo")
public R update(@Valid @RequestBody JobInfo jobInfo) {
return R.status(jobInfoService.updateById(jobInfo));
}
/**
* 任务信息表 新增或修改
*/
@PostMapping("/submit")
@ApiOperationSupport(order = 6)
@Operation(summary = "新增或修改", description = "传入jobInfo")
public R submit(@Valid @RequestBody JobInfo jobInfo) {
return R.status(jobInfoService.submitAndSync(jobInfo));
}
/**
* 任务信息表 删除
*/
@PostMapping("/remove")
@ApiOperationSupport(order = 7)
@Operation(summary = "删除", description = "传入ids")
public R remove(@Parameter(description = "主键集合", required = true) @RequestParam String ids) {
return R.status(jobInfoService.removeAndSync(Func.toLongList(ids)));
}
/**
* 任务信息表 变更状态
*/
@PostMapping("/change")
@ApiOperationSupport(order = 8)
@Operation(summary = "变更状态", description = "传入id与status")
public R change(@Parameter(description = "主键", required = true) @RequestParam Long id, @Parameter(description = "是否启用", required = true) @RequestParam Integer enable) {
return R.status(jobInfoService.changeServerJob(id, enable));
}
/**
* 运行服务
*/
@PostMapping("run")
@ApiOperationSupport(order = 9)
@Operation(summary = "运行服务", description = "传入jobInfoId")
public R run(@Parameter(description = "主键", required = true) @RequestParam Long id) {
return R.status(jobInfoService.runServerJob(id));
}
/**
* 任务信息数据同步
*/
@PostMapping("sync")
@ApiOperationSupport(order = 10)
@Operation(summary = "任务信息数据同步", description = "任务信息数据同步")
public R sync() {
return R.status(jobInfoService.sync());
}
}
@@ -0,0 +1,139 @@
package org.springblade.job.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.AllArgsConstructor;
import org.springblade.core.boot.ctrl.BladeController;
import org.springblade.core.launch.constant.AppConstant;
import org.springblade.core.mp.support.Condition;
import org.springblade.core.mp.support.Query;
import org.springblade.core.tool.api.R;
import org.springblade.core.tool.utils.Func;
import org.springblade.core.tool.utils.StringPool;
import org.springblade.job.pojo.entity.JobServer;
import org.springblade.job.service.IJobServerService;
import org.springblade.job.pojo.vo.JobServerVO;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
/**
* 任务服务表 控制器
*
* @author BladeX
*/
@RestController
@AllArgsConstructor
@RequestMapping(AppConstant.APPLICATION_JOB_NAME + "/job-server")
@Tag(name = "任务服务表", description = "任务服务表接口")
public class JobServerController extends BladeController {
private final IJobServerService jobServerService;
/**
* 任务服务表 详情
*/
@GetMapping("/detail")
@ApiOperationSupport(order = 1)
@Operation(summary = "详情", description = "传入jobServer")
public R<JobServer> detail(JobServer jobServer) {
JobServer detail = jobServerService.getOne(Condition.getQueryWrapper(jobServer));
return R.data(detail);
}
/**
* 任务服务表 分页
*/
@GetMapping("/list")
@ApiOperationSupport(order = 2)
@Operation(summary = "分页", description = "传入jobServer")
public R<IPage<JobServer>> list(@Parameter(hidden = true) @RequestParam Map<String, Object> jobServer, Query query) {
IPage<JobServer> pages = jobServerService.page(Condition.getPage(query), Condition.getQueryWrapper(jobServer, JobServer.class));
return R.data(pages);
}
/**
* 任务服务表 自定义分页
*/
@GetMapping("/page")
@ApiOperationSupport(order = 3)
@Operation(summary = "分页", description = "传入jobServer")
public R<IPage<JobServerVO>> page(JobServerVO jobServer, Query query) {
IPage<JobServerVO> pages = jobServerService.selectJobServerPage(Condition.getPage(query), jobServer);
return R.data(pages);
}
/**
* 任务服务表 新增
*/
@PostMapping("/save")
@ApiOperationSupport(order = 4)
@Operation(summary = "新增", description = "传入jobServer")
public R save(@Valid @RequestBody JobServer jobServer) {
return R.status(jobServerService.save(jobServer));
}
/**
* 任务服务表 修改
*/
@PostMapping("/update")
@ApiOperationSupport(order = 5)
@Operation(summary = "修改", description = "传入jobServer")
public R update(@Valid @RequestBody JobServer jobServer) {
return R.status(jobServerService.updateById(jobServer));
}
/**
* 任务服务表 新增或修改
*/
@PostMapping("/submit")
@ApiOperationSupport(order = 6)
@Operation(summary = "新增或修改", description = "传入jobServer")
public R submit(@Valid @RequestBody JobServer jobServer) {
return R.status(jobServerService.submitAndSync(jobServer));
}
/**
* 任务服务表 删除
*/
@PostMapping("/remove")
@ApiOperationSupport(order = 7)
@Operation(summary = "逻辑删除", description = "传入ids")
public R remove(@Parameter(description = "主键集合", required = true) @RequestParam String ids) {
return R.status(jobServerService.deleteLogic(Func.toLongList(ids)));
}
/**
* 应用服务信息 列表
*/
@GetMapping("/select")
@ApiOperationSupport(order = 8)
@Operation(summary = "应用服务信息", description = "应用服务信息")
public R select() {
List<JobServer> list = jobServerService.list();
list.forEach(jobServer -> jobServer.setJobAppName(
jobServer.getJobAppName() + StringPool.COLON + StringPool.SPACE + StringPool.LEFT_BRACKET +
jobServer.getJobServerName() + StringPool.SPACE + StringPool.DASH + StringPool.SPACE + jobServer.getJobServerUrl() + StringPool.RIGHT_BRACKET)
);
return R.data(list);
}
/**
* 任务服务数据同步
*/
@PostMapping("sync")
@ApiOperationSupport(order = 9)
@Operation(summary = "任务服务数据同步", description = "任务服务数据同步")
public R sync() {
jobServerService.list().forEach(jobServerService::sync);
return R.success("同步完毕");
}
}
@@ -0,0 +1,27 @@
package org.springblade.job.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.springblade.job.pojo.entity.JobInfo;
import org.springblade.job.pojo.vo.JobInfoVO;
import java.util.List;
/**
* 任务信息表 Mapper 接口
*
* @author BladeX
*/
public interface JobInfoMapper extends BaseMapper<JobInfo> {
/**
* 自定义分页
*
* @param page
* @param jobInfo
* @return
*/
List<JobInfoVO> selectJobInfoPage(IPage page, JobInfoVO jobInfo);
}
@@ -0,0 +1,52 @@
<?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="org.springblade.job.mapper.JobInfoMapper">
<!-- 通用查询映射结果 -->
<resultMap id="jobInfoResultMap" type="org.springblade.job.pojo.entity.JobInfo">
<result column="id" property="id"/>
<result column="job_server_id" property="jobServerId"/>
<result column="job_id" property="jobId"/>
<result column="job_name" property="jobName"/>
<result column="job_description" property="jobDescription"/>
<result column="job_params" property="jobParams"/>
<result column="time_expression_type" property="timeExpressionType"/>
<result column="time_expression" property="timeExpression"/>
<result column="execute_type" property="executeType"/>
<result column="processor_type" property="processorType"/>
<result column="processor_info" property="processorInfo"/>
<result column="max_instance_num" property="maxInstanceNum"/>
<result column="concurrency" property="concurrency"/>
<result column="instance_time_limit" property="instanceTimeLimit"/>
<result column="instance_retry_num" property="instanceRetryNum"/>
<result column="task_retry_num" property="taskRetryNum"/>
<result column="min_cpu_cores" property="minCpuCores"/>
<result column="min_memory_space" property="minMemorySpace"/>
<result column="min_disk_space" property="minDiskSpace"/>
<result column="designated_workers" property="designatedWorkers"/>
<result column="max_worker_count" property="maxWorkerCount"/>
<result column="notify_user_ids" property="notifyUserIds"/>
<result column="enable" property="enable"/>
<result column="dispatch_strategy" property="dispatchStrategy"/>
<result column="lifecycle" property="lifecycle"/>
<result column="alert_threshold" property="alertThreshold"/>
<result column="statistic_window_len" property="statisticWindowLen"/>
<result column="silence_window_len" property="silenceWindowLen"/>
<result column="log_type" property="logType"/>
<result column="log_level" property="logLevel"/>
<result column="extra" property="extra"/>
<result column="create_user" property="createUser"/>
<result column="create_dept" property="createDept"/>
<result column="create_time" property="createTime"/>
<result column="update_user" property="updateUser"/>
<result column="update_time" property="updateTime"/>
<result column="status" property="status"/>
<result column="is_deleted" property="isDeleted"/>
</resultMap>
<select id="selectJobInfoPage" resultMap="jobInfoResultMap">
select * from blade_job_info where is_deleted = 0
</select>
</mapper>
@@ -0,0 +1,27 @@
package org.springblade.job.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.springblade.job.pojo.entity.JobServer;
import org.springblade.job.pojo.vo.JobServerVO;
import java.util.List;
/**
* 任务服务表 Mapper 接口
*
* @author BladeX
*/
public interface JobServerMapper extends BaseMapper<JobServer> {
/**
* 自定义分页
*
* @param page
* @param jobServer
* @return
*/
List<JobServerVO> selectJobServerPage(IPage page, JobServerVO jobServer);
}
@@ -0,0 +1,27 @@
<?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="org.springblade.job.mapper.JobServerMapper">
<!-- 通用查询映射结果 -->
<resultMap id="jobServerResultMap" type="org.springblade.job.pojo.entity.JobServer">
<result column="id" property="id"/>
<result column="job_server_name" property="jobServerName"/>
<result column="job_server_url" property="jobServerUrl"/>
<result column="job_app_name" property="jobAppName"/>
<result column="job_app_password" property="jobAppPassword"/>
<result column="job_remark" property="jobRemark"/>
<result column="create_user" property="createUser"/>
<result column="create_dept" property="createDept"/>
<result column="create_time" property="createTime"/>
<result column="update_user" property="updateUser"/>
<result column="update_time" property="updateTime"/>
<result column="status" property="status"/>
<result column="is_deleted" property="isDeleted"/>
</resultMap>
<select id="selectJobServerPage" resultMap="jobServerResultMap">
select * from blade_job_server where is_deleted = 0
</select>
</mapper>
@@ -0,0 +1,32 @@
package org.springblade.job.pojo.dto;
import lombok.Data;
import org.springblade.job.pojo.entity.JobInfo;
import org.springblade.job.pojo.entity.JobServer;
import tech.powerjob.client.PowerJobClient;
/**
* 任务数据DTO
*
* @author Chill
*/
@Data
public class JobDTO {
/**
* 任务信息类
*/
private JobInfo jobInfo;
/**
* 任务服务类
*/
private JobServer jobServer;
/**
* 任务客户端类
*/
private PowerJobClient powerJobClient;
}
@@ -0,0 +1,178 @@
package org.springblade.job.pojo.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springblade.core.mp.base.BaseEntity;
import java.io.Serial;
import java.math.BigDecimal;
/**
* 任务信息表 实体类
*
* @author BladeX
*/
@Data
@TableName("blade_job_info")
@EqualsAndHashCode(callSuper = true)
@Schema(description = "任务信息表")
public class JobInfo extends BaseEntity {
@Serial
private static final long serialVersionUID = 1L;
/**
* 任务服务ID
*/
@Schema(description = "任务服务ID")
private Long jobServerId;
/**
* 任务 ID可选null 代表创建任务否则填写需要修改的任务 ID
*/
@Schema(description = "任务 ID,可选,null 代表创建任务,否则填写需要修改的任务 ID")
private Long jobId;
/**
* 任务名称
*/
@Schema(description = "任务名称")
private String jobName;
/**
* 任务描述
*/
@Schema(description = "任务描述")
private String jobDescription;
/**
* 任务参数Processor#process 方法入参 TaskContext 对象的 jobParams 字段
*/
@Schema(description = "任务参数,Processor#process 方法入参 TaskContext 对象的 jobParams 字段")
private String jobParams;
/**
* 时间表达式类型枚举值
*/
@Schema(description = "时间表达式类型,枚举值")
private Integer timeExpressionType;
/**
* 时间表达式填写类型由 timeExpressionType 决定比如 CRON 需要填写 CRON 表达式
*/
@Schema(description = "时间表达式,填写类型由 timeExpressionType 决定,比如 CRON 需要填写 CRON 表达式")
private String timeExpression;
/**
* 执行类型枚举值
*/
@Schema(description = "执行类型,枚举值")
private Integer executeType;
/**
* 处理器类型枚举值
*/
@Schema(description = "处理器类型,枚举值")
private Integer processorType;
/**
* 处理器参数填写类型由 processorType 决定如Java 处理器需要填写全限定类名com.github.kfcfans.oms.processors.demo.MapReduceProcessorDemo
*/
@Schema(description = "处理器参数,填写类型由 processorType 决定,如Java 处理器需要填写全限定类名,如:com.github.kfcfans.oms.processors.demo.MapReduceProcessorDemo")
private String processorInfo;
/**
* 最大实例数该任务同时执行的数量任务和实例就像是类和对象的关系任务被调度执行后被称为实例
*/
@Schema(description = "最大实例数,该任务同时执行的数量(任务和实例就像是类和对象的关系,任务被调度执行后被称为实例)")
private Integer maxInstanceNum;
/**
* 单机线程并发数表示该实例执行过程中每个Worker 使用的线程数量
*/
@Schema(description = "单机线程并发数,表示该实例执行过程中每个Worker 使用的线程数量")
private Integer concurrency;
/**
* 任务实例运行时间限制0 代表无任何限制超时会被打断并判定为执行失败
*/
@Schema(description = "任务实例运行时间限制,0 代表无任何限制,超时会被打断并判定为执行失败")
private Long instanceTimeLimit;
/**
* instanceRetryNum 任务实例重试次数整个任务失败时重试代价大不推荐使用
*/
@Schema(description = "instanceRetryNum 任务实例重试次数,整个任务失败时重试,代价大,不推荐使用")
private Integer instanceRetryNum;
/**
* taskRetryNum Task 重试次数每个子 Task 失败后单独重试代价小推荐使用
*/
@Schema(description = "taskRetryNum Task 重试次数,每个子 Task 失败后单独重试,代价小,推荐使用")
private Integer taskRetryNum;
/**
* minCpuCores 最小可用 CPU 核心数CPU 可用核心数小于该值的 Worker 将不会执行该任务0 代表无任何限制
*/
@Schema(description = "minCpuCores 最小可用 CPU 核心数,CPU 可用核心数小于该值的 Worker 将不会执行该任务,0 代表无任何限制")
private BigDecimal minCpuCores;
/**
* 最小内存大小GB可用内存小于该值的Worker 将不会执行该任务0 代表无任何限制
*/
@Schema(description = "最小内存大小(GB),可用内存小于该值的Worker 将不会执行该任务,0 代表无任何限制")
private BigDecimal minMemorySpace;
/**
* 最小磁盘大小GB可用磁盘空间小于该值的Worker 将不会执行该任务0 代表无任何限制
*/
@Schema(description = "最小磁盘大小(GB),可用磁盘空间小于该值的Worker 将不会执行该任务,0 代表无任何限制")
private BigDecimal minDiskSpace;
/**
* 指定机器执行设置该参数后只有列表中的机器允许执行该任务空代表不指定机器
*/
@Schema(description = "指定机器执行,设置该参数后只有列表中的机器允许执行该任务,空代表不指定机器")
private String designatedWorkers;
/**
* 最大执行机器数量限定调动执行的机器数量0代表无限制
*/
@Schema(description = "最大执行机器数量,限定调动执行的机器数量,0代表无限制")
private Integer maxWorkerCount;
/**
* 接收报警的用户 ID 列表
*/
@Schema(description = "接收报警的用户 ID 列表")
private String notifyUserIds;
/**
* 是否启用该任务未启用的任务不会被调度
*/
@Schema(description = "是否启用该任务,未启用的任务不会被调度")
private Integer enable;
/**
* 调度策略枚举目前支持随机RANDOM 健康度优先HEALTH_FIRST
*/
@Schema(description = "调度策略,枚举,目前支持随机(RANDOM)和 健康度优先(HEALTH_FIRST")
private Integer dispatchStrategy;
/**
* lifecycle 生命周期预留用于指定定时调度任务的生效时间范围
*/
@Schema(description = "lifecycle 生命周期(预留,用于指定定时调度任务的生效时间范围)")
private String lifecycle;
/**
* 错误阈值0代表不限制
*/
@Schema(description = "错误阈值,0代表不限制")
private Integer alertThreshold;
/**
* 统计的窗口长度(s)0代表不限制
*/
@Schema(description = "统计的窗口长度(s)0代表不限制")
private Integer statisticWindowLen;
/**
* 沉默时间窗口(s)0代表不限制
*/
@Schema(description = "沉默时间窗口(s)0代表不限制")
private Integer silenceWindowLen;
/**
* 日志配置
*/
@Schema(description = "日志配置")
private Integer logType;
/**
* 日志配置
*/
@Schema(description = "日志级别")
private Integer logLevel;
/**
* 扩展字段供开发者使用用于功能扩展powerjob 自身不会使用该字段
*/
@Schema(description = "扩展字段(供开发者使用,用于功能扩展,powerjob 自身不会使用该字段)")
private String extra;
}
@@ -0,0 +1,52 @@
package org.springblade.job.pojo.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springblade.core.mp.base.BaseEntity;
import java.io.Serial;
/**
* 任务服务表 实体类
*
* @author BladeX
*/
@Data
@TableName("blade_job_server")
@EqualsAndHashCode(callSuper = true)
@Schema(description = "任务服务表")
public class JobServer extends BaseEntity {
@Serial
private static final long serialVersionUID = 1L;
/**
* 任务服务名称
*/
@Schema(description = "任务服务名称")
private String jobServerName;
/**
* 任务服务器地址
*/
@Schema(description = "任务服务器地址")
private String jobServerUrl;
/**
* 任务应用名称
*/
@Schema(description = "任务应用名称")
private String jobAppName;
/**
* 任务应用密码
*/
@Schema(description = "任务应用密码")
private String jobAppPassword;
/**
* 任务备注
*/
@Schema(description = "任务备注")
private String jobRemark;
}
@@ -0,0 +1,21 @@
package org.springblade.job.pojo.vo;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springblade.job.pojo.entity.JobInfo;
import java.io.Serial;
/**
* 任务信息表 视图实体类
*
* @author BladeX
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class JobInfoVO extends JobInfo {
@Serial
private static final long serialVersionUID = 1L;
}
@@ -0,0 +1,21 @@
package org.springblade.job.pojo.vo;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springblade.job.pojo.entity.JobServer;
import java.io.Serial;
/**
* 任务服务表 视图实体类
*
* @author BladeX
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class JobServerVO extends JobServer {
@Serial
private static final long serialVersionUID = 1L;
}
@@ -0,0 +1,35 @@
package org.springblade.job.processor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import tech.powerjob.worker.core.processor.ProcessResult;
import tech.powerjob.worker.core.processor.TaskContext;
import tech.powerjob.worker.core.processor.sdk.BasicProcessor;
import tech.powerjob.worker.log.OmsLogger;
// 支持 SpringBean 的形式
@Slf4j
@Component
public class ProcessorDemo implements BasicProcessor {
@Override
public ProcessResult process(TaskContext context) {
// 在线日志功能可以直接在控制台查看任务日志非常便捷
OmsLogger omsLogger = context.getOmsLogger();
omsLogger.info("BasicProcessorDemo start to process, current JobParams is {}.", context.getJobParams());
// TaskContext为任务的上下文信息包含了在控制台录入的任务元数据常用字段为
// jobParams任务参数在控制台录入instanceParams任务实例参数通过 OpenAPI 触发的任务实例才可能存在该参数
// 进行实际处理...
log.info("============== ProcessorDemo#process ==============");
log.info("hello blade");
log.info("============== ProcessorDemo#process ==============");
// 返回结果该结果会被持久化到数据库在前端页面直接查看极为方便
return new ProcessResult(true, "result is success");
}
}
@@ -0,0 +1,64 @@
package org.springblade.job.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.springblade.core.mp.base.BaseService;
import org.springblade.job.pojo.entity.JobInfo;
import org.springblade.job.pojo.vo.JobInfoVO;
import java.util.List;
/**
* 任务信息表 服务类
*
* @author BladeX
*/
public interface IJobInfoService extends BaseService<JobInfo> {
/**
* 自定义分页
*
* @param page
* @param jobInfo
* @return
*/
IPage<JobInfoVO> selectJobInfoPage(IPage<JobInfoVO> page, JobInfoVO jobInfo);
/**
* 保存并同步
*
* @return
*/
Boolean submitAndSync(JobInfo jobInfo);
/**
* 删除并同步
*
* @return
*/
Boolean removeAndSync(List<Long> ids);
/**
* 启用禁用服务
*
* @param id 任务服务ID
* @param enable 是否启用
* @return
*/
Boolean changeServerJob(Long id, Integer enable);
/**
* 运行服务
*
* @param id 任务服务ID
* @return
*/
Boolean runServerJob(Long id);
/**
* 数据同步
*
* @return
*/
Boolean sync();
}
@@ -0,0 +1,40 @@
package org.springblade.job.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.springblade.core.mp.base.BaseService;
import org.springblade.job.pojo.entity.JobServer;
import org.springblade.job.pojo.vo.JobServerVO;
/**
* 任务服务表 服务类
*
* @author BladeX
*/
public interface IJobServerService extends BaseService<JobServer> {
/**
* 自定义分页
*
* @param page
* @param jobServer
* @return
*/
IPage<JobServerVO> selectJobServerPage(IPage<JobServerVO> page, JobServerVO jobServer);
/**
* 保存并同步
*
* @param jobServer
* @return
*/
Boolean submitAndSync(JobServer jobServer);
/**
* 同步数据
*
* @param jobServer
* @return
*/
Boolean sync(JobServer jobServer);
}
@@ -0,0 +1,320 @@
package org.springblade.job.service.impl;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.AllArgsConstructor;
import org.springblade.core.log.exception.ServiceException;
import org.springblade.core.mp.base.BaseServiceImpl;
import org.springblade.core.powerjob.constant.PowerJobConstant;
import org.springblade.core.tool.jackson.JsonUtil;
import org.springblade.core.tool.utils.ConvertUtil;
import org.springblade.core.tool.utils.DateUtil;
import org.springblade.core.tool.utils.Func;
import org.springblade.core.tool.utils.StringPool;
import org.springblade.job.pojo.dto.JobDTO;
import org.springblade.job.pojo.entity.JobInfo;
import org.springblade.job.pojo.entity.JobServer;
import org.springblade.job.mapper.JobInfoMapper;
import org.springblade.job.service.IJobInfoService;
import org.springblade.job.service.IJobServerService;
import org.springblade.job.pojo.vo.JobInfoVO;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import tech.powerjob.client.PowerJobClient;
import tech.powerjob.common.enums.DispatchStrategy;
import tech.powerjob.common.enums.ExecuteType;
import tech.powerjob.common.enums.ProcessorType;
import tech.powerjob.common.enums.TimeExpressionType;
import tech.powerjob.common.model.AlarmConfig;
import tech.powerjob.common.model.LifeCycle;
import tech.powerjob.common.model.LogConfig;
import tech.powerjob.common.request.http.SaveJobInfoRequest;
import tech.powerjob.common.response.JobInfoDTO;
import tech.powerjob.common.response.ResultDTO;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;
/**
* 任务信息表 服务实现类
*
* @author BladeX
*/
@Service
@AllArgsConstructor
public class JobInfoServiceImpl extends BaseServiceImpl<JobInfoMapper, JobInfo> implements IJobInfoService {
private final IJobServerService jobServerService;
@Override
public IPage<JobInfoVO> selectJobInfoPage(IPage<JobInfoVO> page, JobInfoVO jobInfo) {
return page.setRecords(baseMapper.selectJobInfoPage(page, jobInfo));
}
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean submitAndSync(JobInfo jobInfo) {
//获取应用分组服务端信息
JobServer jobServer = jobServerService.getById(jobInfo.getJobServerId());
//构建Job客户端
PowerJobClient client = new PowerJobClient(jobServer.getJobServerUrl(), jobServer.getJobAppName(), jobServer.getJobAppPassword());
SaveJobInfoRequest request = convertToServer(jobInfo);
//获取上传结果
ResultDTO<Long> result = client.saveJob(request);
if (result.isSuccess()) {
jobInfo.setJobId(result.getData());
return this.saveOrUpdate(jobInfo);
} else {
throw new ServiceException(result.getMessage());
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean removeAndSync(List<Long> ids) {
ids.forEach(id -> {
JobDTO jobDTO = JobData(id);
if (Func.isNotEmpty(jobDTO)) {
JobInfo jobInfo = jobDTO.getJobInfo();
PowerJobClient powerJobClient = jobDTO.getPowerJobClient();
//删除服务数据
ResultDTO<Void> result = powerJobClient.deleteJob(jobInfo.getJobId());
if (result.isSuccess()) {
this.removeById(id);
} else {
throw new ServiceException(result.getMessage());
}
}
});
return true;
}
@Override
public Boolean changeServerJob(Long id, Integer enable) {
JobDTO jobDTO = JobData(id);
if (Func.isNotEmpty(jobDTO)) {
JobInfo jobInfo = jobDTO.getJobInfo();
PowerJobClient powerJobClient = jobDTO.getPowerJobClient();
//更换服务端状态
ResultDTO<Void> result = (enable == PowerJobConstant.JOB_ENABLED) ?
powerJobClient.enableJob(jobInfo.getJobId()) :
powerJobClient.disableJob(jobInfo.getJobId());
//删除客户端数据
if (result.isSuccess()) {
return this.update(Wrappers.<JobInfo>update().lambda().set(JobInfo::getEnable, enable).eq(JobInfo::getId, id));
} else {
throw new ServiceException(result.getMessage());
}
}
return false;
}
@Override
public Boolean runServerJob(Long id) {
JobDTO jobDTO = JobData(id);
if (Func.isNotEmpty(jobDTO)) {
JobInfo jobInfo = jobDTO.getJobInfo();
PowerJobClient powerJobClient = jobDTO.getPowerJobClient();
ResultDTO<Long> result = powerJobClient.runJob(jobInfo.getJobId());
return result.isSuccess();
}
return false;
}
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean sync() {
//任务信息列表
List<JobInfo> jobInfos = this.list();
//任务服务列表
List<JobServer> jobServers = jobServerService.list();
//按应用分组
Map<Long, List<JobInfo>> jobGroups = jobInfos.stream().collect(Collectors.groupingBy(JobInfo::getJobServerId));
//处理服务端数据下载
jobServers.forEach(jobServer -> {
//构建Job客户端
PowerJobClient client = new PowerJobClient(jobServer.getJobServerUrl(), jobServer.getJobAppName(), jobServer.getJobAppPassword());
//从服务端获取数据
List<JobInfoDTO> serverInfoList = Optional.ofNullable(client.fetchAllJob())
.filter(ResultDTO::isSuccess)
.map(ResultDTO::getData)
.orElseGet(ArrayList::new);
//获取客户端数据
List<JobInfo> localInfoList = jobGroups.get(jobServer.getId());
//处理需要从服务端下载的数据
List<JobInfoDTO> jobInfoDTOList = serverInfoList.stream()
.filter(serverData -> serverData.getStatus() != PowerJobConstant.JOB_DELETED)
.filter(serverData -> Func.isEmpty(localInfoList) || localInfoList.stream().noneMatch(localData -> Func.equalsSafe(localData.getJobId(), serverData.getId())))
.collect(Collectors.toList());
List<JobInfo> dataToDownload = convertToLocalList(jobInfoDTOList, jobServer.getId());
//调用本地Service保存数据
this.saveBatch(dataToDownload);
});
//处理客户端数据上传
jobGroups.forEach((jobServerId, localInfoList) -> {
//获取应用分组服务端信息
JobServer jobServer = jobServers.stream().filter(js -> Func.equalsSafe(js.getId(), jobServerId))
.findFirst().orElseThrow(() -> new ServiceException(PowerJobConstant.JOB_SYNC_ALERT));
//构建Job客户端
PowerJobClient client = new PowerJobClient(jobServer.getJobServerUrl(), jobServer.getJobAppName(), jobServer.getJobAppPassword());
//处理需要上传到服务端的数据
localInfoList.forEach(localData -> {
//转换数据格式
SaveJobInfoRequest data = convertToServer(localData);
//调用OpenAPI接口上传数据
ResultDTO<Long> saveResult = client.saveJob(data);
if (saveResult.isSuccess()) {
//更新服务端JobId至客户端
this.update(Wrappers.<JobInfo>update().lambda().set(JobInfo::getJobId, saveResult.getData()).eq(JobInfo::getId, localData.getId()));
} else {
throw new RuntimeException(saveResult.getMessage());
}
});
});
return true;
}
/**
* 获取Job数据集合
*
* @param jobInfoId 服务信息ID
* @return PowerJobClient
*/
public JobDTO JobData(Long jobInfoId) {
//构建DTO类
JobDTO jobDTO = new JobDTO();
//获取任务信息
JobInfo jobInfo = this.getById(jobInfoId);
jobDTO.setJobInfo(jobInfo);
if (Func.isEmpty(jobInfo.getJobId())) {
throw new ServiceException(PowerJobConstant.JOB_SYNC_ALERT);
}
if (Func.isNotEmpty(jobInfo.getJobServerId())) {
//获取应用分组服务端信息
JobServer jobServer = jobServerService.getById(jobInfo.getJobServerId());
jobDTO.setJobServer(jobServer);
//构建Job客户端
PowerJobClient powerJobClient = new PowerJobClient(jobServer.getJobServerUrl(), jobServer.getJobAppName(), jobServer.getJobAppPassword());
jobDTO.setPowerJobClient(powerJobClient);
return jobDTO;
}
return null;
}
/**
* 服务端Job列表转换
*
* @param jobInfoList 本地任务信息列表
* @return List<SaveJobInfoRequest>
*/
public List<SaveJobInfoRequest> convertToServerList(List<JobInfo> jobInfoList) {
return jobInfoList.stream().map(this::convertToServer).collect(Collectors.toList());
}
/**
* 本地Job列表转换
*
* @param jobInfoDTOList 服务端任务信息列表
* @return List<JobInfo>
*/
public List<JobInfo> convertToLocalList(List<JobInfoDTO> jobInfoDTOList, Long jobServerId) {
return jobInfoDTOList.stream().map(jobInfoDTO -> convertToLocal(jobInfoDTO, jobServerId)).collect(Collectors.toList());
}
/**
* 服务端Job单个转换
*
* @param jobInfo 本地任务信息
* @return SaveJobInfoRequest
*/
public SaveJobInfoRequest convertToServer(JobInfo jobInfo) {
SaveJobInfoRequest saveJobInfoRequest = new SaveJobInfoRequest();
if (Func.toLong(jobInfo.getJobId()) > 0L) {
saveJobInfoRequest.setId(jobInfo.getJobId());
}
saveJobInfoRequest.setJobName(jobInfo.getJobName());
saveJobInfoRequest.setJobDescription(jobInfo.getJobDescription());
saveJobInfoRequest.setJobParams(jobInfo.getJobParams());
saveJobInfoRequest.setTimeExpressionType(TimeExpressionType.of(jobInfo.getTimeExpressionType()));
saveJobInfoRequest.setTimeExpression(jobInfo.getTimeExpression());
saveJobInfoRequest.setExecuteType(ExecuteType.of(jobInfo.getExecuteType()));
saveJobInfoRequest.setProcessorType(ProcessorType.of(jobInfo.getProcessorType()));
saveJobInfoRequest.setProcessorInfo(jobInfo.getProcessorInfo());
saveJobInfoRequest.setMaxInstanceNum(jobInfo.getMaxInstanceNum());
saveJobInfoRequest.setConcurrency(jobInfo.getConcurrency());
saveJobInfoRequest.setInstanceTimeLimit(jobInfo.getInstanceTimeLimit());
saveJobInfoRequest.setInstanceRetryNum(jobInfo.getInstanceRetryNum());
saveJobInfoRequest.setTaskRetryNum(jobInfo.getTaskRetryNum());
saveJobInfoRequest.setMinCpuCores(jobInfo.getMinCpuCores().doubleValue());
saveJobInfoRequest.setMinMemorySpace(jobInfo.getMinMemorySpace().doubleValue());
saveJobInfoRequest.setMinDiskSpace(jobInfo.getMinDiskSpace().doubleValue());
saveJobInfoRequest.setDesignatedWorkers(jobInfo.getDesignatedWorkers());
saveJobInfoRequest.setMaxWorkerCount(jobInfo.getMaxWorkerCount());
saveJobInfoRequest.setNotifyUserIds(Func.toLongList(jobInfo.getNotifyUserIds()));
saveJobInfoRequest.setEnable(jobInfo.getEnable() == 1);
saveJobInfoRequest.setDispatchStrategy(DispatchStrategy.of(jobInfo.getDispatchStrategy()));
saveJobInfoRequest.setAlarmConfig(new AlarmConfig(jobInfo.getAlertThreshold(), jobInfo.getStatisticWindowLen(), jobInfo.getSilenceWindowLen()));
saveJobInfoRequest.setLogConfig(new LogConfig().setLevel(jobInfo.getLogLevel()).setType(jobInfo.getLogType()));
if (Func.isNotEmpty(jobInfo.getLifecycle())) {
LifeCycle lifeCycle = new LifeCycle();
String[] lifeCycleArr = Func.toStrArray(jobInfo.getLifecycle());
lifeCycle.setStart(DateUtil.parse(lifeCycleArr[0], DateUtil.PATTERN_DATETIME).getTime());
lifeCycle.setEnd(DateUtil.parse(lifeCycleArr[1], DateUtil.PATTERN_DATETIME).getTime());
saveJobInfoRequest.setLifeCycle(lifeCycle);
}
saveJobInfoRequest.setExtra(jobInfo.getExtra());
return saveJobInfoRequest;
}
/**
* 本地Job单个转换
*
* @param jobInfoDTO 服务端任务信息
* @return SaveJobInfoRequest
*/
public JobInfo convertToLocal(JobInfoDTO jobInfoDTO, Long jobServerId) {
JobInfo jobInfo = new JobInfo();
jobInfo.setJobServerId(jobServerId);
jobInfo.setJobId(jobInfoDTO.getId());
jobInfo.setJobName(jobInfoDTO.getJobName());
jobInfo.setJobDescription(jobInfoDTO.getJobDescription());
jobInfo.setJobParams(jobInfoDTO.getJobParams());
jobInfo.setTimeExpressionType(jobInfoDTO.getTimeExpressionType());
jobInfo.setTimeExpression(jobInfoDTO.getTimeExpression());
jobInfo.setExecuteType(jobInfoDTO.getExecuteType());
jobInfo.setProcessorType(jobInfoDTO.getProcessorType());
jobInfo.setProcessorInfo(jobInfoDTO.getProcessorInfo());
jobInfo.setMaxInstanceNum(jobInfoDTO.getMaxInstanceNum());
jobInfo.setConcurrency(jobInfoDTO.getConcurrency());
jobInfo.setInstanceTimeLimit(jobInfoDTO.getInstanceTimeLimit());
jobInfo.setInstanceRetryNum(jobInfoDTO.getInstanceRetryNum());
jobInfo.setTaskRetryNum(jobInfoDTO.getTaskRetryNum());
jobInfo.setMinCpuCores(ConvertUtil.convert(jobInfoDTO.getMinCpuCores(), BigDecimal.class));
jobInfo.setMinMemorySpace(ConvertUtil.convert(jobInfoDTO.getMinMemorySpace(), BigDecimal.class));
jobInfo.setMinDiskSpace(ConvertUtil.convert(jobInfoDTO.getMinDiskSpace(), BigDecimal.class));
jobInfo.setDesignatedWorkers(jobInfoDTO.getDesignatedWorkers());
jobInfo.setMaxWorkerCount(jobInfoDTO.getMaxWorkerCount());
jobInfo.setNotifyUserIds(jobInfoDTO.getNotifyUserIds());
jobInfo.setEnable(jobInfoDTO.getStatus());
jobInfo.setDispatchStrategy(jobInfoDTO.getDispatchStrategy());
if (Func.isNotEmpty(jobInfoDTO.getLifecycle()) && !Func.equalsSafe(jobInfoDTO.getLifecycle(), StringPool.EMPTY_JSON)) {
LifeCycle lifeCycle = JsonUtil.parse(jobInfoDTO.getLifecycle(), LifeCycle.class);
String start = DateUtil.format(new Date(lifeCycle.getStart()), DateUtil.PATTERN_DATETIME);
String end = DateUtil.format(new Date(lifeCycle.getEnd()), DateUtil.PATTERN_DATETIME);
jobInfo.setLifecycle(start + StringPool.COMMA + end);
}
if (Func.isNotEmpty(jobInfoDTO.getAlarmConfig())) {
jobInfo.setAlertThreshold(jobInfoDTO.getAlarmConfig().getAlertThreshold());
jobInfo.setStatisticWindowLen(jobInfoDTO.getAlarmConfig().getStatisticWindowLen());
jobInfo.setSilenceWindowLen(jobInfoDTO.getAlarmConfig().getSilenceWindowLen());
}
if (Func.isNotEmpty(jobInfoDTO.getLogConfig())) {
jobInfo.setLogType(jobInfoDTO.getLogConfig().getType());
jobInfo.setLogLevel(jobInfoDTO.getLogConfig().getLevel());
}
jobInfo.setExtra(jobInfoDTO.getExtra());
return jobInfo;
}
}
@@ -0,0 +1,47 @@
package org.springblade.job.service.impl;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.fasterxml.jackson.core.type.TypeReference;
import org.springblade.core.http.util.HttpUtil;
import org.springblade.core.mp.base.BaseServiceImpl;
import org.springblade.core.tool.jackson.JsonUtil;
import org.springblade.core.tool.support.Kv;
import org.springblade.core.tool.utils.Func;
import org.springblade.job.pojo.entity.JobServer;
import org.springblade.job.mapper.JobServerMapper;
import org.springblade.job.service.IJobServerService;
import org.springblade.job.pojo.vo.JobServerVO;
import org.springframework.stereotype.Service;
import tech.powerjob.common.response.ResultDTO;
/**
* 任务服务表 服务实现类
*
* @author BladeX
*/
@Service
public class JobServerServiceImpl extends BaseServiceImpl<JobServerMapper, JobServer> implements IJobServerService {
@Override
public IPage<JobServerVO> selectJobServerPage(IPage<JobServerVO> page, JobServerVO jobServer) {
return page.setRecords(baseMapper.selectJobServerPage(page, jobServer));
}
@Override
public Boolean submitAndSync(JobServer jobServer) {
if (Func.isEmpty(jobServer.getId())) {
this.sync(jobServer);
}
return this.saveOrUpdate(jobServer);
}
@Override
public Boolean sync(JobServer jobServer) {
Kv appInfo = Kv.create().set("appName", jobServer.getJobAppName()).set("password", jobServer.getJobAppPassword());
String data = HttpUtil.postJson(jobServer.getJobServerUrl() + "/appInfo/save", JsonUtil.toJson(appInfo));
ResultDTO<Void> result = JsonUtil.parse(data, new TypeReference<ResultDTO<Void>>() {});
return result.isSuccess();
}
}
@@ -0,0 +1,56 @@
package org.springblade.modules.auth.config;
import org.springblade.core.jwt.props.JwtProperties;
import org.springblade.core.oauth2.config.OAuth2AutoConfiguration;
import org.springblade.core.oauth2.handler.AuthorizationHandler;
import org.springblade.core.oauth2.handler.PasswordHandler;
import org.springblade.core.oauth2.handler.TokenHandler;
import org.springblade.core.oauth2.service.OAuth2ClientService;
import org.springblade.core.oauth2.service.OAuth2UserService;
import org.springblade.core.redis.cache.BladeRedis;
import org.springblade.core.tenant.BladeTenantProperties;
import org.springblade.modules.auth.handler.BladeAuthorizationHandler;
import org.springblade.modules.auth.handler.BladePasswordHandler;
import org.springblade.modules.auth.handler.BladeTokenHandler;
import org.springblade.modules.auth.service.BladeClientDetailService;
import org.springblade.modules.auth.service.BladeUserDetailService;
import org.springblade.modules.system.service.IUserService;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
/**
* BladeAuthConfiguration
*
* @author Chill
*/
@Configuration(proxyBeanMethods = false)
@AutoConfigureBefore(OAuth2AutoConfiguration.class)
public class BladeAuthConfiguration {
@Bean
public AuthorizationHandler authorizationHandler(BladeRedis bladeRedis, BladeTenantProperties tenantProperties) {
return new BladeAuthorizationHandler(bladeRedis, tenantProperties);
}
@Bean
public PasswordHandler passwordHandler() {
return new BladePasswordHandler();
}
@Bean
public TokenHandler tokenHandler(JwtProperties jwtProperties) {
return new BladeTokenHandler(jwtProperties);
}
@Bean
public OAuth2ClientService oAuth2ClientService(JdbcTemplate jdbcTemplate) {
return new BladeClientDetailService(jdbcTemplate);
}
@Bean
public OAuth2UserService oAuth2UserService(IUserService userService) {
return new BladeUserDetailService(userService);
}
}
@@ -0,0 +1,22 @@
package org.springblade.modules.auth.constant;
/**
* AuthorizationConstant
*
* @author Chill
*/
public interface BladeAuthConstant {
/**
* 是否开启注册参数key
*/
String REGISTER_USER_VALUE = "account.registerUser";
/**
* 账号锁定错误次数参数key
*/
String FAIL_COUNT_VALUE = "account.failCount";
/**
* 账号锁定默认错误次数
*/
Integer FAIL_COUNT = 5;
}
@@ -0,0 +1,49 @@
package org.springblade.modules.auth.granter;
import org.springblade.core.oauth2.constant.OAuth2TokenConstant;
import org.springblade.core.oauth2.exception.UserInvalidException;
import org.springblade.core.oauth2.granter.PasswordTokenGranter;
import org.springblade.core.oauth2.handler.PasswordHandler;
import org.springblade.core.oauth2.provider.OAuth2Request;
import org.springblade.core.oauth2.service.OAuth2ClientService;
import org.springblade.core.oauth2.service.OAuth2User;
import org.springblade.core.oauth2.service.OAuth2UserService;
import org.springblade.core.redis.cache.BladeRedis;
import org.springblade.core.tool.utils.StringUtil;
import org.springframework.stereotype.Component;
/**
* CaptchaTokenGranter
*
* @author BladeX
*/
@Component
public class CaptchaTokenGranter extends PasswordTokenGranter {
private final BladeRedis bladeRedis;
public CaptchaTokenGranter(OAuth2ClientService clientService, OAuth2UserService userService, PasswordHandler passwordHandler, BladeRedis bladeRedis) {
super(clientService, userService, passwordHandler);
this.bladeRedis = bladeRedis;
}
@Override
public String type() {
return CAPTCHA;
}
@Override
public OAuth2User user(OAuth2Request request) {
// 获取验证码信息
String key = request.getCaptchaKey();
String code = request.getCaptchaCode();
// 获取验证码
String redisCode = bladeRedis.get(OAuth2TokenConstant.CAPTCHA_CACHE_KEY + key);
// 判断验证码
if (code == null || !StringUtil.equalsIgnoreCase(redisCode, code)) {
throw new UserInvalidException(OAuth2TokenConstant.CAPTCHA_NOT_CORRECT);
}
return super.user(request);
}
}
@@ -0,0 +1,115 @@
package org.springblade.modules.auth.granter;
import org.jetbrains.annotations.NotNull;
import org.springblade.common.cache.ParamCache;
import org.springblade.core.oauth2.exception.ExceptionCode;
import org.springblade.core.oauth2.exception.UserInvalidException;
import org.springblade.core.oauth2.granter.AbstractTokenGranter;
import org.springblade.core.oauth2.handler.PasswordHandler;
import org.springblade.core.oauth2.provider.OAuth2Request;
import org.springblade.core.oauth2.service.OAuth2Client;
import org.springblade.core.oauth2.service.OAuth2ClientService;
import org.springblade.core.oauth2.service.OAuth2User;
import org.springblade.core.oauth2.service.OAuth2UserService;
import org.springblade.core.oauth2.service.impl.OAuth2UserDetail;
import org.springblade.core.tool.utils.Func;
import org.springblade.modules.auth.provider.UserType;
import org.springblade.modules.system.pojo.entity.User;
import org.springblade.modules.system.service.IUserService;
import org.springframework.stereotype.Component;
import java.util.Collections;
import java.util.function.Predicate;
import static org.springblade.modules.auth.constant.BladeAuthConstant.REGISTER_USER_VALUE;
/**
* RegisterTokenGranter
*
* @author BladeX
*/
@Component
public class RegisterTokenGranter extends AbstractTokenGranter {
private final IUserService service;
public RegisterTokenGranter(OAuth2ClientService clientService, OAuth2UserService userService, PasswordHandler passwordHandler, IUserService service) {
super(clientService, userService, passwordHandler);
this.service = service;
}
@Override
public String type() {
return REGISTER;
}
@Override
public OAuth2User user(OAuth2Request request) {
// 校验注册功能是否开启
Boolean registerOpen = Func.toBoolean(ParamCache.getValue(REGISTER_USER_VALUE), false);
if (!registerOpen) {
throw new UserInvalidException("注册功能暂未开启,请联系管理员");
}
// 用户注册信息
User user = new User();
user.setUserType(UserType.WEB.getCategory());
user.setTenantId(request.getTenantId());
user.setAccount(request.getUsername());
user.setPassword(request.getPassword());
user.setName(request.getName());
user.setRealName(request.getName());
user.setPhone(request.getPhone());
user.setEmail(request.getEmail());
// 校验用户格式
validateUser(user);
// 执行用户注册
if (service.registerUser(user)) {
// 构建oauth2所需用户信息
return convertOAuth2UserDetail(user, client(request));
}
throw new UserInvalidException(ExceptionCode.INVALID_USER.getMessage());
}
private void validateUser(User user) {
Predicate<String> isNameValid = name -> name.matches("^([\\u4e00-\\u9fa5]{2,20}|[a-zA-Z]{2,10})$");
Predicate<String> isUsernameValid = username -> username.matches("^(?=.*[a-zA-Z])[a-zA-Z0-9_\\-@]{3,20}$");
Predicate<String> isPasswordValid = password -> password.matches("^(?=.*[0-9])(?=.*[a-zA-Z])[\\w@-]{6,45}$");
Predicate<String> isPhoneValid = phone -> phone.matches("^1[3-9]\\d{9}$");
Predicate<String> isEmailValid = email -> email.matches("^[A-Za-z0-9+_.-]+@(.+)$");
if (!isNameValid.test(user.getName())) {
throw new UserInvalidException("用户姓名长度必须在2-10之间,且仅能设置纯中文或纯英文");
}
if (!isUsernameValid.test(user.getAccount())) {
throw new UserInvalidException("用户账号长度必须在3-20之间,且需要包含英文,可额外携带数字、下划线、横杠、@");
}
if (!isPhoneValid.test(user.getPhone())) {
throw new UserInvalidException("手机号格式不正确");
}
if (!isEmailValid.test(user.getEmail())) {
throw new UserInvalidException("邮箱格式不正确");
}
if (!isPasswordValid.test(user.getPassword())) {
throw new UserInvalidException("密码长度格式不符合要求");
}
}
@NotNull
private OAuth2UserDetail convertOAuth2UserDetail(User user, OAuth2Client client) {
OAuth2UserDetail userDetail = new OAuth2UserDetail();
userDetail.setUserId(String.valueOf(user.getId()));
userDetail.setTenantId(user.getTenantId());
userDetail.setName(user.getName());
userDetail.setRealName(user.getName());
userDetail.setAccount(user.getAccount());
userDetail.setPassword(user.getPassword());
userDetail.setPhone(user.getPhone());
userDetail.setEmail(user.getEmail());
userDetail.setAuthorities(Collections.singletonList(REGISTER));
userDetail.setClient(client);
return userDetail;
}
}
@@ -0,0 +1,87 @@
package org.springblade.modules.auth.granter;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.request.AuthRequest;
import org.springblade.core.oauth2.exception.OAuth2ErrorCode;
import org.springblade.core.oauth2.granter.AbstractTokenGranter;
import org.springblade.core.oauth2.handler.PasswordHandler;
import org.springblade.core.oauth2.provider.OAuth2Request;
import org.springblade.core.oauth2.service.OAuth2ClientService;
import org.springblade.core.oauth2.service.OAuth2User;
import org.springblade.core.oauth2.service.OAuth2UserService;
import org.springblade.core.oauth2.utils.OAuth2ExceptionUtil;
import org.springblade.core.social.props.SocialProperties;
import org.springblade.core.social.utils.SocialUtil;
import org.springblade.core.tool.utils.BeanUtil;
import org.springblade.modules.auth.utils.TokenUtil;
import org.springblade.modules.system.pojo.entity.UserInfo;
import org.springblade.modules.system.pojo.entity.UserOauth;
import org.springblade.modules.system.service.IUserService;
import org.springframework.stereotype.Component;
import java.util.Objects;
/**
* SocialTokenGranter
*
* @author Chill
*/
@Component
public class SocialTokenGranter extends AbstractTokenGranter {
private static final Integer AUTH_SUCCESS_CODE = 2000;
private final IUserService userService;
private final SocialProperties socialProperties;
public SocialTokenGranter(OAuth2ClientService clientService, OAuth2UserService oAuth2UserService, PasswordHandler passwordHandler, IUserService userService, SocialProperties socialProperties) {
super(clientService, oAuth2UserService, passwordHandler);
this.userService = userService;
this.socialProperties = socialProperties;
}
@Override
public String type() {
return SOCIAL;
}
@Override
public OAuth2User user(OAuth2Request request) {
String tenantId = request.getTenantId();
// 开放平台来源
String sourceParameter = request.getSource();
// 匹配是否有别名定义
String source = socialProperties.getAlias().getOrDefault(sourceParameter, sourceParameter);
// 开放平台授权码
String code = request.getCode();
// 开放平台状态吗
String state = request.getState();
// 获取开放平台授权数据
AuthRequest authRequest = SocialUtil.getAuthRequest(source, socialProperties);
AuthCallback authCallback = new AuthCallback();
authCallback.setCode(code);
authCallback.setState(state);
AuthResponse<?> authResponse = authRequest.login(authCallback);
AuthUser authUser = null;
if (authResponse.getCode() == AUTH_SUCCESS_CODE) {
authUser = (AuthUser) authResponse.getData();
} else {
OAuth2ExceptionUtil.throwFromCode(OAuth2ErrorCode.INVALID_USER);
}
// 组装数据
UserOauth userOauth = Objects.requireNonNull(BeanUtil.copyProperties(authUser, UserOauth.class));
userOauth.setSource(authUser.getSource());
userOauth.setTenantId(tenantId);
userOauth.setUuid(authUser.getUuid());
UserInfo userInfo = userService.userInfo(userOauth);
return TokenUtil.convertUser(userInfo, request);
}
}
@@ -0,0 +1,176 @@
package org.springblade.modules.auth.handler;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springblade.common.cache.CacheNames;
import org.springblade.common.cache.ParamCache;
import org.springblade.common.cache.SysCache;
import org.springblade.common.constant.TenantConstant;
import org.springblade.core.oauth2.exception.ExceptionCode;
import org.springblade.core.oauth2.handler.AbstractAuthorizationHandler;
import org.springblade.core.oauth2.provider.OAuth2Request;
import org.springblade.core.oauth2.provider.OAuth2Validation;
import org.springblade.core.oauth2.service.OAuth2User;
import org.springblade.core.redis.cache.BladeRedis;
import org.springblade.core.tenant.BladeTenantProperties;
import org.springblade.core.tool.jackson.JsonUtil;
import org.springblade.core.tool.utils.DateUtil;
import org.springblade.core.tool.utils.DesUtil;
import org.springblade.core.tool.utils.Func;
import org.springblade.modules.system.pojo.entity.Tenant;
import java.time.Duration;
import java.util.Date;
import static org.springblade.modules.auth.constant.BladeAuthConstant.FAIL_COUNT;
import static org.springblade.modules.auth.constant.BladeAuthConstant.FAIL_COUNT_VALUE;
/**
* AbstractAuthorizationHandler
*
* @author BladeX
*/
@Slf4j
@RequiredArgsConstructor
public class BladeAuthorizationHandler extends AbstractAuthorizationHandler {
private final BladeRedis bladeRedis;
private final BladeTenantProperties tenantProperties;
/**
* 认证校验
*
* @param user 用户信息
* @param request 请求信息
* @return boolean
*/
@Override
public OAuth2Validation authValidation(OAuth2User user, OAuth2Request request) {
// 密码模式刷新token模式验证码模式需要校验租户状态
if (request.isPassword() || request.isRefreshToken() || request.isCaptchaCode()) {
// 租户校验
OAuth2Validation tenantValidation = validateTenant(user.getTenantId());
if (!tenantValidation.isSuccess()) {
return tenantValidation;
}
// 判断登录是否锁定
OAuth2Validation failCountValidation = validateFailCount(user.getTenantId(), user.getAccount());
if (!failCountValidation.isSuccess()) {
return failCountValidation;
}
}
return super.authValidation(user, request);
}
/**
* 认证成功回调
*
* @param user 用户信息
*/
@Override
public void authSuccessful(OAuth2User user, OAuth2Request request) {
// 清空错误锁定次数
delFailCount(user.getTenantId(), user.getAccount());
log.info("用户:{},认证成功", user.getAccount());
}
/**
* 认证失败回调
*
* @param user 用户信息
* @param validation 失败信息
*/
@Override
public void authFailure(OAuth2User user, OAuth2Request request, OAuth2Validation validation) {
// 增加错误锁定次数
addFailCount(user.getTenantId(), user.getAccount());
log.error("用户:{},认证失败,失败原因:{}", user.getAccount(), validation.getMessage());
}
/**
* 租户授权校验
*
* @param tenantId 租户id
* @return OAuth2Validation
*/
private OAuth2Validation validateTenant(String tenantId) {
// 租户校验
Tenant tenant = SysCache.getTenant(tenantId);
if (tenant == null) {
return buildValidationFailure(ExceptionCode.USER_TENANT_NOT_FOUND);
}
// 租户授权时间校验
Date expireTime = tenant.getExpireTime();
if (tenantProperties.getLicense()) {
String licenseKey = tenant.getLicenseKey();
String decrypt = DesUtil.decryptFormHex(licenseKey, TenantConstant.DES_KEY);
expireTime = JsonUtil.parse(decrypt, Tenant.class).getExpireTime();
}
if (expireTime != null && expireTime.before(DateUtil.now())) {
return buildValidationFailure(ExceptionCode.UNAUTHORIZED_USER_TENANT);
}
return new OAuth2Validation();
}
/**
* 判断登录是否锁定
*
* @param tenantId 租户id
* @param account 账号
* @return OAuth2Validation
*/
private OAuth2Validation validateFailCount(String tenantId, String account) {
int cnt = getFailCount(tenantId, account);
int failCount = Func.toInt(ParamCache.getValue(FAIL_COUNT_VALUE), FAIL_COUNT);
if (cnt >= failCount) {
return buildValidationFailure(ExceptionCode.USER_TOO_MANY_FAILS);
}
return new OAuth2Validation();
}
/**
* 获取账号错误次数
*
* @param tenantId 租户id
* @param username 账号
* @return int
*/
private int getFailCount(String tenantId, String username) {
return Func.toInt(bladeRedis.get(CacheNames.tenantKey(tenantId, CacheNames.USER_FAIL_KEY, username)), 0);
}
/**
* 设置账号错误次数
*
* @param tenantId 租户id
* @param username 账号
*/
private void addFailCount(String tenantId, String username) {
int count = getFailCount(tenantId, username);
bladeRedis.setEx(CacheNames.tenantKey(tenantId, CacheNames.USER_FAIL_KEY, username), count + 1, Duration.ofMinutes(30));
}
/**
* 设置账号错误次数
*
* @param tenantId 租户id
* @param username 账号
* @param count 次数
*/
private void setFailCount(String tenantId, String username, int count) {
bladeRedis.setEx(CacheNames.tenantKey(tenantId, CacheNames.USER_FAIL_KEY, username), count + 1, Duration.ofMinutes(30));
}
/**
* 清空账号错误次数
*
* @param tenantId 租户id
* @param username 账号
*/
private void delFailCount(String tenantId, String username) {
bladeRedis.del(CacheNames.tenantKey(tenantId, CacheNames.USER_FAIL_KEY, username));
}
}
@@ -0,0 +1,35 @@
package org.springblade.modules.auth.handler;
import org.springblade.core.oauth2.handler.OAuth2PasswordHandler;
/**
* BladePasswordHandler
*
* @author BladeX
*/
public class BladePasswordHandler extends OAuth2PasswordHandler {
/**
* 判断密码是否匹配
*
* @param rawPassword 请求时提交的原密码
* @param encodedPassword 数据库加密后的密码
* @return boolean
*/
@Override
public boolean matches(String rawPassword, String encodedPassword) {
return super.matches(rawPassword, encodedPassword);
}
/**
* 加密密码规则
*
* @param rawPassword 密码
* @return 加密后的密码
*/
@Override
public String encode(String rawPassword) {
return super.encode(rawPassword);
}
}
@@ -0,0 +1,34 @@
package org.springblade.modules.auth.handler;
import org.springblade.core.jwt.props.JwtProperties;
import org.springblade.core.launch.constant.TokenConstant;
import org.springblade.core.oauth2.handler.OAuth2TokenHandler;
import org.springblade.core.oauth2.provider.OAuth2Request;
import org.springblade.core.oauth2.provider.OAuth2Token;
import org.springblade.core.oauth2.service.OAuth2User;
import org.springblade.core.tool.support.Kv;
/**
* BladeTokenHandler
*
* @author BladeX
*/
public class BladeTokenHandler extends OAuth2TokenHandler {
public BladeTokenHandler(JwtProperties properties) {
super(properties);
}
@Override
public OAuth2Token enhance(OAuth2User user, OAuth2Token token, OAuth2Request request) {
// 父类令牌状态配置
OAuth2Token enhanceToken = super.enhance(user, token, request);
// 令牌统一处理增加或删减字段
Kv args = enhanceToken.getArgs();
args.set(TokenConstant.USER_NAME, user.getAccount());
// 返回令牌
return enhanceToken;
}
}
@@ -0,0 +1,43 @@
package org.springblade.modules.auth.provider;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
/**
* 用户类型枚举
*
* @author Chill
*/
@Getter
@AllArgsConstructor
public enum UserType {
/**
* web
*/
WEB("web", 1),
/**
* app
*/
APP("app", 2),
/**
* other
*/
OTHER("other", 3),
;
final String name;
final int category;
public static UserType of(String name) {
return Arrays.stream(UserType.values())
.filter(userEnum -> userEnum.getName().equalsIgnoreCase(name != null ? name : "web"))
.findFirst()
.orElse(UserType.WEB); // 在没有找到匹配项时返回默认值
}
}
@@ -0,0 +1,37 @@
package org.springblade.modules.auth.service;
import org.springblade.core.oauth2.provider.OAuth2Request;
import org.springblade.core.oauth2.service.OAuth2Client;
import org.springblade.core.oauth2.service.impl.OAuth2ClientDetailService;
import org.springframework.jdbc.core.JdbcTemplate;
/**
* BladeClientDetailService
*
* @author Chill
*/
public class BladeClientDetailService extends OAuth2ClientDetailService {
public BladeClientDetailService(JdbcTemplate jdbcTemplate) {
super(jdbcTemplate);
}
@Override
public OAuth2Client loadByClientId(String clientId) {
return super.loadByClientId(clientId);
}
@Override
public OAuth2Client loadByClientId(String clientId, OAuth2Request request) {
return super.loadByClientId(clientId, request);
}
@Override
public boolean validateClient(OAuth2Client client, String clientId, String clientSecret) {
return super.validateClient(client, clientId, clientSecret);
}
@Override
public boolean validateGranter(OAuth2Client client, String grantType) {
return super.validateGranter(client, grantType);
}
}
@@ -0,0 +1,62 @@
package org.springblade.modules.auth.service;
import lombok.RequiredArgsConstructor;
import org.springblade.core.oauth2.provider.OAuth2Request;
import org.springblade.core.oauth2.service.OAuth2User;
import org.springblade.core.oauth2.service.OAuth2UserService;
import org.springblade.core.tool.utils.Func;
import org.springblade.core.tool.utils.StringUtil;
import org.springblade.modules.auth.provider.UserType;
import org.springblade.modules.auth.utils.TokenUtil;
import org.springblade.modules.system.pojo.entity.UserInfo;
import org.springblade.modules.system.service.IUserService;
import java.util.Optional;
/**
* BladeUserDetailService
*
* @author Chill
*/
@RequiredArgsConstructor
public class BladeUserDetailService implements OAuth2UserService {
private final IUserService userService;
@Override
public OAuth2User loadByUserId(String userId, OAuth2Request request) {
// 获取用户参数
String userType = Optional.ofNullable(request.getUserType())
.filter(s -> !StringUtil.isBlank(s))
.orElse(UserType.WEB.getName());
// 获取用户信息
UserInfo userInfo = userService.userInfo(Func.toLong(userId), UserType.of(userType));
// 构建oauth2用户信息
return TokenUtil.convertUser(userInfo, request);
}
@Override
public OAuth2User loadByUsername(String username, OAuth2Request request) {
// 获取用户参数
String userType = Optional.ofNullable(request.getUserType())
.filter(s -> !StringUtil.isBlank(s))
.orElse(UserType.WEB.getName());
String tenantId = request.getTenantId();
// 获取用户信息
UserInfo userInfo = userService.userInfo(tenantId, username, UserType.of(userType));
// 构建oauth2用户信息
return TokenUtil.convertUser(userInfo, request);
}
@Override
public boolean validateUser(OAuth2User user) {
return Optional.ofNullable(user)
.filter(u -> u.getUserId() != null && !u.getUserId().isEmpty()) // 检查userId不为空
.filter(u -> u.getAuthorities() != null && !u.getAuthorities().isEmpty()) // 检查authorities不为空
.isPresent(); // 如果上述条件都满足则返回true否则返回false
}
}

Some files were not shown because too many files have changed in this diff Show More