代码初始化提交。

This commit is contained in:
wangzhijiang
2024-02-20 09:33:24 +08:00
parent c66445edd4
commit 8abe03b22a
2184 changed files with 718173 additions and 0 deletions
@@ -0,0 +1,4 @@
*.js linguist-language=Java
*.css linguist-language=Java
*.html linguist-language=Java
*.vue linguist-language=Java
+63
View File
@@ -0,0 +1,63 @@
/target/
/.idea/
jeecg-boot-module-demo
rebel.xml
#java
*.class
#package file
*.war
*.ear
*.zip
*.tar.gz
*.rar
#maven ignore
target/
build/
#eclipse ignore
.settings/
.project
.classpatch
#Intellij idea
.idea/
/idea/
out/
logs/
*.ipr
*.iml
*.iws
# temp file
*.log
*.cache
*.diff
*.patch
*.tmp
.DS_Store
node_modules
/node_modules/
/dist
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 jero-boot
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@@ -0,0 +1,33 @@
-- 老法规数据 导入的全部操作 文件位置NAS: \中国重汽法规\3.程序设计文档\老法规系统数据迁移\2023-12-06(最终版)\ 文件夹下
### 1、执行sql文件 【FCC-同步正式环境的数据库结构.sql】 或者直接本地同步结构
### 2、老法规数据导入的操作:
- 导入组织机构对应关系 对应文件 【1.标准清单- 部门对应关系(和IAM对比).xlsx】
### 3、国标数据
- 导入标准数据 【2.国内标准-最终导入数据.xls】
- 导入国标-准入性数据 【3.国标标准-体系(准入性).xlsx】
- 导入国标-推荐性数据 【4.国标标准-体系(推荐性).xlsx】
### 4、海外标准的数据
- 导入海外标准 【5.海外标准-标准清单-导入数据.xls】
### 5、企业标准
- 导入企标数据的文件 【6.企标-能正常导入的数据.xlsx】
- 企标体系 【7.企标-体系导入文件.xlsx】
- 企标权限-所有人【8.企标-标准权限-1 所有人.xlsx】
- 企标权限-个人【9.企标-标准权限-2 个人2.xlsx】
- 企标权限-组织【10.企标-标准权限-部门权限.xlsx】
### 6、海外标准有一些带文字的需要特殊处理
- 【11.海外标准-需要做文件特殊处理的.xls】
### 7、导入角色用户对应关系的接口
- 【laws-sinotruk/laws/localTool/importUtil/importUserPermission】
+132
View File
@@ -0,0 +1,132 @@
框架继承注意要点
===
1. 在本框架中搭建工作流模块,重新设计工作流模块
(包括用户对审批节点配置增减;待办、已办、已发、监控、草稿的设计;流程过程中加签;发起时可配置人员节点选择;
每个人显示自己关联的流程图+显示流程节点对应的办理人;节点未到时发起人可修改已配置的审批人员等等)
2. 所有与工作流相关的表都使用`activiti_`开头
3. 熟悉后端的双语操作(预留但不开发,仅中文即可)
4. 熟悉文档库属性配置相关代码(重汽需要将文档库标签扩充成 国内标准法规标签、海外标准法规标签、企业标准标签三部分),补充注释
5. 去除无效的application配置项
6. 分析代码,删掉不用的代码和包,为后续开发搭好基础。留下相关接口为:文档管理-文档库、文档工具-文档拆分、文档工具-文档对比、业务支持-对外报告管理(改名为资料中心)、
信息中心-文档动态、信息中心-我的消息、个人中心-我的收藏、日志中心-操作日志、系统管理(全部)、开发工具全部(仅给开发用)
7. 分析结果由项目二组提供excel说明项目框架各部分范围(高嵩负责)
中间件要求
===
1. 数据库 Mysql 5.7
2. 缓存 Redis 7.0
3. 搜索引擎 EasticSearch 7.9
4. 文件上传 Minio 任意版本
5. 支持K8S部署 docker
6. 工作流 Activiti 6.0.0
jero-boot 低代码开发平台
===============
当前最新版本: 2.5.1(发布日期:20210126
## 后端技术架构
- 基础框架:Spring Boot 2.3.5.RELEASE
- 持久层框架:Mybatis-plus 3.4.1
- 安全框架:Apache Shiro 1.7.0Jwt 3.11.0
- 数据库连接池:阿里巴巴Druid 1.1.22
- 缓存框架:redis
- 日志打印:logback
- 其他:fastjsonpoiSwagger-uiquartz, lombok(简化代码)等。
## 开发环境
- 语言:Java 8
- IDE(JAVA)IDEA安装lombok插件
- 依赖管理:Maven
- 数据库:MySQL5.7+
- 缓存:Redis
## 数据库设计规范
- 数据库表名全部使用小写,多个单词之间使用 _ 隔开
- 字段内容全部使用小写,多个单词之间使用 _ 隔开,并且每个字段需要有注释
- 每个表中都需要有id,用varchar(36
- 新建的表中必须要有创建人(create_by)、创建时间(create_time)、更新人(update_by)、更新时间(update_time)、所属部门(sys_code)五个字段
- create_by 和 update_by 存用户账号
- id的增长规则设定保持统一,统一按照框架生成的内容形成
## 系统规范
- 日志记录
方法中使用`@AutoLog` 记录日志
- 异常处理
所有异常使用`JeroBootException`抛出
- 权限控制
需要进行数据权限控制
- 项目模块创建
后续创建模块在`jero-boot-modules.com.jero.modules.laws`
`jero-boot-modules.com.jero.modules.project`下创建
- 系统导入
系统导入使用注解`@ExcelImport`进行字段校验
- 系统常量定义在`com.jero.common.constant.CommonConstant`
## 专项文档
#### 一、查询过滤器用法
```
QueryWrapper<?> queryWrapper = QueryGenerator.initQueryWrapper(?, req.getParameterMap());
```
代码示例:
```
@GetMapping(value = "/list")
public Result<IPage<JeroDemo>> list(JeroDemo jeroDemo, @RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize,
HttpServletRequest req) {
Result<IPage<JeroDemo>> result = new Result<IPage<JeroDemo>>();
//调用QueryGenerator的initQueryWrapper
QueryWrapper<JeroDemo> queryWrapper = QueryGenerator.initQueryWrapper(jeroDemo, req.getParameterMap());
Page<JeroDemo> page = new Page<JeroDemo>(pageNo, pageSize);
IPage<JeroDemo> pageList = JeroDemoService.page(page, queryWrapper);
result.setSuccess(true);
result.setResult(pageList);
return result;
}
```
- 查询规则 (本规则不适用于高级查询,高级查询有自己对应的查询类型可以选择 )
| 查询模式 | 用法 | 说明 |
|---------- |-------------------------------------------------------|------------------|
| 模糊查询 | 支持左右模糊和全模糊 需要在查询输入框内前或后带\*或是前后全部带\* | |
| 取非查询 | 在查询输入框前面输入! 则查询该字段不等于输入值的数据(数值类型不支持此种查询,可以将数值字段定义为字符串类型的) | |
| \> \>= < <= | 同取非查询 在输入框前面输入对应特殊字符即表示走对应规则查询 | |
| in查询 | 若传入的数据带,(逗号) 则表示该查询为in查询 | |
| 多选字段模糊查询 | 上述4 有一个特例,若某一查询字段前后都带逗号 则会将其视为走这种查询方式 ,该查询方式是将查询条件以逗号分割再遍历数组 将每个元素作like查询 用or拼接,例如 现在name传入值 ,a,b,c, 那么结果sql就是 name like '%a%' or name like '%b%' or name like '%c%' | |
#### 三、代码生成器
> 功能说明: 一键生成的代码(包括:controller、service、dao、mapper、entity、vue
- 模板位置: src/main/resources/jero/code-template
- 文件配置路径: jero-boot/jero-boot-single-startup/src/main/resources/jero/jero_config.properties
@@ -0,0 +1,449 @@
DROP TABLE IF EXISTS `laws_domestic_standard`;
CREATE TABLE `laws_domestic_standard`
(
`id` varchar(36) NOT NULL PRIMARY KEY COMMENT '主键ID',
`create_by` varchar(36) COMMENT '创建人',
`create_time` datetime(0) COMMENT '创建时间',
`update_by` varchar(36) COMMENT '更新人',
`update_time` datetime(0) COMMENT '更新时间',
`sys_code` varchar(64) COMMENT '所属部门',
`del_flag` int(1) DEFAULT 0 COMMENT '0表示未删除,1表示删除',
`standard_property` varchar(36) COMMENT '标准性质',
`standard_class` varchar(36) COMMENT '标准类别',
`year_number` varchar(50) COMMENT '年代号',
`standard_number` varchar(50) COMMENT '标准编号',
`standard_name` varchar(50) COMMENT '标准名称',
`standard_english_name` varchar(50) COMMENT '标准英文名称',
`standard_system` varchar(1) COMMENT '标准体系',
`release_date` date COMMENT '发布日期',
`implementation_date` date COMMENT '实施日期',
`on_the_production_date` varchar(200) COMMENT '在产车实施日期',
`new_model_implementation_date` varchar(200) COMMENT '新车型实施日期',
`annulment_date` date COMMENT '废止日期',
`standard_state` varchar(36) COMMENT '标准状态',
`centralized_management_departments` varchar(50) COMMENT '归口管理部门',
`bidding_committee` varchar(36) COMMENT '分标委',
`responsible_department` varchar(36) COMMENT '责任部门',
`enterprise_participation` varchar(36) COMMENT '企业参与情况',
`part_name` varchar(36) COMMENT '零部件名称',
`applicable_certification` varchar(200) COMMENT '适用认证',
`applicable_vehicle` varchar(200) COMMENT '适用车型',
`dynamic_type` varchar(200) COMMENT '动力类型',
`substitute_standard_number` varchar(50) COMMENT '代替标准号',
`replaced_by_standard_number` varchar(50) COMMENT '被代替标准号',
`reference_standard` varchar(50) COMMENT '引用标准',
`referenced_standard` varchar(50) COMMENT '被引用标准',
`associated_file` varchar(50) COMMENT '关联文件',
`degree_of_bid_acquisition` varchar(36) COMMENT '采标程度',
`adopt_the_international_standard_number` varchar(50) COMMENT '采用国际标准号',
`associated_foreign_standards` varchar(50) COMMENT '关联国外标准',
`publish_of_original` varchar(50) COMMENT '发布搞(原文)',
`modification_list` varchar(50) COMMENT '修改单',
`draft_for_review` varchar(50) COMMENT '报批稿',
`draft_for_approval` varchar(50) COMMENT '送审稿',
`draft_for_comment` varchar(50) COMMENT '征求意见稿',
`draft` varchar(50) COMMENT '草案'
) COMMENT = '国内标准';
alter table laws_domestic_standard
comment '国内标准';
DROP TABLE IF EXISTS `laws_overseas_standard`;
CREATE TABLE `laws_overseas_standard`
(
`id` varchar(36) NOT NULL PRIMARY KEY COMMENT '主键ID',
`create_by` varchar(36) COMMENT '创建人',
`create_time` datetime(0) COMMENT '创建时间',
`update_by` varchar(36) COMMENT '更新人',
`update_time` datetime(0) COMMENT '更新时间',
`sys_code` varchar(64) COMMENT '所属部门',
`del_flag` int(1) DEFAULT 0 COMMENT '0表示未删除,1表示删除',
`country_or_region` varchar(50) COMMENT '国家/地区',
`standard_class` varchar(36) COMMENT '标准类别',
`year_number` varchar(50) COMMENT '年代号',
`standard_number` varchar(50) COMMENT '标准编号',
`standard_name` varchar(50) COMMENT '标准名称',
`standard_english_name` varchar(50) COMMENT '标准英文名称',
`standard_system` varchar(1) COMMENT '标准体系',
`release_date` date COMMENT '发布日期',
`implementation_date` date COMMENT '实施日期',
`on_the_production_date` varchar(200) COMMENT '在产车实施日期',
`new_model_implementation_date` varchar(200) COMMENT '新车型实施日期',
`annulment_date` date COMMENT '废止日期',
`standard_state` varchar(36) COMMENT '标准状态',
`responsible_department` varchar(36) COMMENT '责任部门',
`part_name` varchar(36) COMMENT '零部件名称',
`applicable_vehicle` varchar(200) COMMENT '适用车型',
`substitute_standard_number` varchar(50) COMMENT '代替标准号',
`replaced_by_standard_number` varchar(50) COMMENT '被代替标准号',
`reference_standard` varchar(50) COMMENT '引用标准',
`referenced_standard` varchar(50) COMMENT '被引用标准',
`associated_file` varchar(50) COMMENT '关联文件',
`publish_of_original` varchar(50) COMMENT '发布搞(原文)',
`publish_of_translation` varchar(50) COMMENT '发布搞(译文)',
`modification_list` varchar(50) COMMENT '修改单',
`draft_for_review` varchar(50) COMMENT '报批稿',
`draft_for_approval` varchar(50) COMMENT '送审稿',
`draft_for_comment` varchar(50) COMMENT '征求意见稿',
`draft` varchar(50) COMMENT '草案'
) COMMENT = '海外标准';
alter table laws_overseas_standard
comment '海外标准';
DROP TABLE IF EXISTS `laws_news_feed`;
CREATE TABLE `laws_news_feed`
(
`id` varchar(36) NOT NULL PRIMARY KEY COMMENT '主键ID',
`create_by` varchar(36) COMMENT '创建人',
`create_time` datetime(0) COMMENT '创建时间',
`update_by` varchar(36) COMMENT '更新人',
`update_time` datetime(0) COMMENT '更新时间',
`sys_code` varchar(64) COMMENT '所属部门',
`del_flag` int(1) DEFAULT 0 COMMENT '0表示未删除,1表示删除',
`dynamic_heading` varchar(50) COMMENT '动态标题',
`dynamic_classification` varchar(50) COMMENT '动态分类',
`dynamic_details` varchar(500) COMMENT '动态详情',
`data_text` varchar(2000) COMMENT '资料文本',
`release_status` int(1) DEFAULT 0 COMMENT '发布状态(0-未发布、1-已发布)',
`release_time` datetime(0) COMMENT '发布时间',
`read_count` int(8) DEFAULT 0 COMMENT '阅读次数'
) COMMENT = '动态消息';
alter table laws_news_feed
comment '动态消息';
DROP TABLE IF EXISTS `laws_push_range`;
CREATE TABLE `laws_push_range`
(
`id` varchar(36) NOT NULL PRIMARY KEY COMMENT '主键ID',
`unique_relation_flag` varchar(50) COMMENT '唯一关联标识(id或者编号)',
`user_id` varchar(36) COMMENT '用户id',
`module_code` varchar(64) COMMENT '所属模块编码',
`read_status` int(1) DEFAULT 0 COMMENT '阅读状态(0-未读、1-已读)'
) COMMENT = '推送范围';
alter table laws_push_range
comment '推送范围';
DROP TABLE IF EXISTS `laws_tree_node`;
CREATE TABLE `laws_tree_node`
(
`id` varchar(36) NOT NULL PRIMARY KEY COMMENT '主键ID',
`create_by` varchar(36) COMMENT '创建人',
`create_time` datetime(0) COMMENT '创建时间',
`update_by` varchar(36) COMMENT '更新人',
`update_time` datetime(0) COMMENT '更新时间',
`sys_code` varchar(64) COMMENT '所属部门',
`del_flag` int(1) DEFAULT 0 COMMENT '0表示未删除,1表示删除',
`parent_id` varchar(36) COMMENT '父节点id',
`parent_code` varchar(64) COMMENT '父节点code',
`node_code` varchar(64) COMMENT '节点code',
`node_name` varchar(50) COMMENT '节点名',
`module_code` varchar(64) COMMENT '所属模块编码',
`node_type` int(1) DEFAULT 0 COMMENT '节点类型(1-根节点、2-子节点)'
) COMMENT = '树型节点';
alter table laws_tree_node
comment '树型节点';
DROP TABLE IF EXISTS `laws_node_relation`;
CREATE TABLE `laws_node_relation`
(
`id` varchar(36) NOT NULL PRIMARY KEY COMMENT '主键ID',
`node_code` varchar(64) COMMENT '节点code',
`unique_relation_flag` varchar(50) COMMENT '唯一关联标识(id或者编号)',
`module_code` varchar(64) COMMENT '所属模块编码'
) COMMENT = '树型节点关联';
alter table laws_node_relation
comment '树型节点关联';
DROP TABLE IF EXISTS `laws_data_center`;
CREATE TABLE `laws_data_center`
(
`id` varchar(36) NOT NULL PRIMARY KEY COMMENT '主键ID',
`create_by` varchar(36) COMMENT '创建人',
`create_time` datetime(0) COMMENT '创建时间',
`update_by` varchar(36) COMMENT '更新人',
`update_time` datetime(0) COMMENT '更新时间',
`sys_code` varchar(64) COMMENT '所属部门',
`del_flag` int(1) DEFAULT 0 COMMENT '0表示未删除,1表示删除',
`file_name` varchar(100) COMMENT '文件名称',
`file_description` varchar(50) COMMENT '文件说明',
`file_ids` varchar(2000) COMMENT '文件ids'
) COMMENT = '资料中心';
alter table laws_data_center
comment '资料中心';
DROP TABLE IF EXISTS `laws_update_record`;
CREATE TABLE `laws_update_record`
(
`id` varchar(36) NOT NULL PRIMARY KEY COMMENT '主键ID',
`create_by` varchar(36) COMMENT '创建人',
`create_time` datetime(0) COMMENT '创建时间',
`update_by` varchar(36) COMMENT '更新人',
`update_time` datetime(0) COMMENT '更新时间',
`sys_code` varchar(64) COMMENT '所属部门',
`del_flag` int(1) DEFAULT 0 COMMENT '0表示未删除,1表示删除',
`unique_relation_flag` varchar(50) COMMENT '唯一关联标识(id或者编号)',
`sort` int(8) DEFAULT 1 COMMENT '排序',
`content` varchar(2000) COMMENT '文件说明'
) COMMENT = '更新记录';
alter table laws_update_record
comment '更新记录';
DROP TABLE IF EXISTS `laws_replaced_standard`;
CREATE TABLE `laws_replaced_standard`
(
`id` varchar(36) NOT NULL PRIMARY KEY COMMENT '主键ID',
`replaced` varchar(50) COMMENT '代替标准',
`replaced_by` varchar(50) COMMENT '被代替标准',
`type` int(1) COMMENT '类型(1-代替、2-引用)'
) COMMENT = '代替标准';
alter table laws_replaced_standard
comment '代替标准';
DROP TABLE IF EXISTS `laws_problem_library`;
CREATE TABLE `laws_problem_library`
(
`id` varchar(36) NOT NULL PRIMARY KEY COMMENT '主键ID',
`create_by` varchar(36) COMMENT '创建人',
`create_time` datetime(0) COMMENT '创建时间',
`update_by` varchar(36) COMMENT '更新人',
`update_time` datetime(0) COMMENT '更新时间',
`org_code` varchar(64) COMMENT '所属部门',
`del_flag` int(1) DEFAULT 0 COMMENT '0表示未删除,1表示删除',
`type` char(1) COMMENT '分类(国内1、海外2、企业3、其他4',
`standard_id` varchar(50) COMMENT '标准id',
`standard_number` varchar(50) COMMENT '标准编号',
`standard_name` varchar(50) COMMENT '标准名称',
`title` varchar(50) COMMENT '标题',
`project` varchar(50) COMMENT '项目',
`problem_description` varchar(500) COMMENT '问题描述',
`file` varchar(2000) COMMENT '附件',
`is_answer` int(1) DEFAULT 0 COMMENT '是否有人回答(0否,1是)',
`user_id` varchar(50) COMMENT '发帖人id'
) COMMENT = '问答库-问题';
alter table laws_problem_library
comment '问答库-问题';
DROP TABLE IF EXISTS `laws_answer_library`;
CREATE TABLE `laws_answer_library`
(
`id` varchar(36) NOT NULL PRIMARY KEY COMMENT '主键ID',
`create_time` datetime(0) COMMENT '创建时间',
`org_code` varchar(64) COMMENT '所属部门',
`problem_id` varchar(50) COMMENT '问题id',
`answer_description` varchar(500) COMMENT '答案描述',
`is_top` int(1) DEFAULT 0 COMMENT '是否置顶(0否,1是)',
`user_id` varchar(50) COMMENT '回答人id'
) COMMENT = '问答库-回答';
alter table laws_answer_library
comment '问答库-回答';
DROP TABLE IF EXISTS `laws_contact_manage`;
CREATE TABLE `laws_contact_manage`
(
`id` varchar(36) NOT NULL PRIMARY KEY COMMENT '主键ID',
`create_by` varchar(36) COMMENT '创建人',
`create_time` datetime(0) COMMENT '创建时间',
`update_by` varchar(36) COMMENT '更新人',
`update_time` datetime(0) COMMENT '更新时间',
`org_code` varchar(64) COMMENT '所属部门',
`del_flag` int(1) DEFAULT 0 COMMENT '0表示未删除,1表示删除',
`depart_name` varchar(200) COMMENT '部门名称',
`depart_id` varchar(50) COMMENT '部门id',
`engineer_id` varchar(50) COMMENT '标准化工程师id',
`contact_id` varchar(50) COMMENT '联络人id',
`leader_id` varchar(50) COMMENT '部门联络人主管级领导id'
) COMMENT = '联络人管理';
alter table laws_contact_manage
comment '联络人管理';
DROP TABLE IF EXISTS `laws_asms_open_api`;
CREATE TABLE `laws_asms_open_api`
(
`id` varchar(36) NOT NULL PRIMARY KEY COMMENT '主键ID',
`api_name` varchar(200) COMMENT 'api名称',
`api_url` varchar(200) COMMENT 'api路径',
`dict_id` varchar(50) COMMENT '数据字典id',
`api_type` varchar(5) COMMENT '接口类型(1字典,2分标委,3法规)',
`query_param_json` text COMMENT '参数map'
) COMMENT = '汽车标准数字化平台ASMS开放API接口管理';
alter table laws_asms_open_api
comment '汽车标准数字化平台ASMS开放API接口管理';
DROP TABLE IF EXISTS `laws_asms_domestic`;
CREATE TABLE `laws_asms_domestic`
(
`id` varchar(100) NOT NULL PRIMARY KEY COMMENT '主键ID',
`code` varchar(100) COMMENT '标准法规编号/标准号',
`name` varchar(100) COMMENT '标准法规名称/标准名称',
`english_name` varchar(100) COMMENT '法规英文名称',
`type` varchar(100) COMMENT '标准法规类别/标准类别',
`applicable_certification_list_string` varchar(100) COMMENT '适用认证',
`applicable_models_list_string` varchar(100) COMMENT '适用车型拼接名称',
`degree_adoption` varchar(100) COMMENT '采标程度',
`drafting_people` varchar(500) COMMENT '起草人',
`drafting_unit` varchar(500) COMMENT '起草单位',
`focal_point_name` varchar(100) COMMENT '标准领域名称',
`implementation_date` varchar(100) COMMENT '实施日期',
`international_standard` varchar(100) COMMENT '采用国际标准号',
`new_car_implementation_date` varchar(100) COMMENT '新车实施日期',
`new_register_car_implementation_date` varchar(100) COMMENT '新注册车实施日期',
`power_type_list_string` varchar(100) COMMENT '动力类型',
`production_car_implementation_date` varchar(100) COMMENT '在产车实施日期',
`proposing_department` varchar(100) COMMENT '提出部门',
`publish_date` varchar(100) COMMENT '发布日期',
`replace_code` varchar(100) COMMENT '代替标准号',
`scope_application` varchar(100) COMMENT '适用范围',
`standard_nature` varchar(100) COMMENT '标准性质',
`standard_status` varchar(100) COMMENT '标准状态',
`subcommittee` varchar(100) COMMENT '分标委',
`create_time` varchar(100) COMMENT '创建时间',
`update_time` varchar(100) COMMENT '更新时间',
`is_sync` varchar(5) COMMENT '是否同步(0未同步,1已同步)'
) COMMENT = 'ASMS国内法规';
alter table laws_asms_domestic
comment 'ASMS国内法规';
DROP TABLE IF EXISTS `laws_data_center_folder`;
CREATE TABLE `laws_data_center_folder`
(
`id` varchar(36) NOT NULL PRIMARY KEY COMMENT '主键ID',
`create_by` varchar(36) COMMENT '创建人',
`create_time` datetime(0) COMMENT '创建时间',
`update_by` varchar(36) COMMENT '更新人',
`update_time` datetime(0) COMMENT '更新时间',
`org_code` varchar(64) COMMENT '所属部门',
`del_flag` int(1) DEFAULT 0 COMMENT '0表示未删除,1表示删除',
`folder_name` varchar(50) COMMENT '文件夹名称',
`auth_manage_ids` varchar(2000) COMMENT '管理权限(用户id逗号拼接)',
`auth_view_ids` varchar(2000) COMMENT '查看权限(用户id逗号拼接)',
`sort` int(10) COMMENT '文件夹顺序',
`parent_id` varchar(36) COMMENT '父id',
`owner_id` varchar(36) COMMENT '拥有者id'
) COMMENT = '资料中心-文件夹';
alter table laws_data_center_folder
comment '资料中心-文件夹';
DROP TABLE IF EXISTS `laws_data_center_file`;
CREATE TABLE `laws_data_center_file`
(
`id` varchar(36) NOT NULL PRIMARY KEY COMMENT '主键ID',
`create_by` varchar(36) COMMENT '创建人',
`create_time` datetime(0) COMMENT '创建时间',
`update_by` varchar(36) COMMENT '更新人',
`update_time` datetime(0) COMMENT '更新时间',
`org_code` varchar(64) COMMENT '所属部门',
`del_flag` int(1) DEFAULT 0 COMMENT '0表示未删除,1表示删除',
`file_name` varchar(50) COMMENT '文件名称',
`file_id` varchar(50) COMMENT '上传资料(文件id',
`push_range_ids` varchar(2000) COMMENT '推送范围(用户id逗号拼接)',
`file_description` varchar(2000) COMMENT '文件说明',
`folder_id` varchar(36) COMMENT '文件夹id',
`owner_id` varchar(36) COMMENT '拥有者id'
) COMMENT = '资料中心-文件数据';
alter table laws_data_center_file
comment '资料中心-文件数据';
DROP TABLE IF EXISTS `process_fb_entry_change`;
CREATE TABLE `process_fb_entry_change`
(
`id` varchar(36) NOT NULL PRIMARY KEY COMMENT '主键ID',
`create_by` varchar(36) COMMENT '创建人',
`create_time` datetime(0) COMMENT '创建时间',
`update_by` varchar(36) COMMENT '更新人',
`update_time` datetime(0) COMMENT '更新时间',
`org_code` varchar(64) COMMENT '所属部门',
`del_flag` int(1) DEFAULT 0 COMMENT '0表示未删除,1表示删除',
`source` varchar(5) COMMENT '来源(1国内、2海外)',
`operate_type` varchar(5) COMMENT '操作类型(1标准法规入库、2标准法规变更)',
`business_json` longtext COMMENT 'JSON字符串'
) COMMENT = '标准法规入库/变更流程';
alter table process_fb_entry_change
comment '标准法规入库/变更流程';
DROP TABLE IF EXISTS `process_fb_standard_consultation`;
CREATE TABLE `process_fb_standard_consultation`
(
`id` varchar(36) NOT NULL PRIMARY KEY COMMENT '主键ID',
`create_by` varchar(36) COMMENT '创建人',
`create_time` datetime(0) COMMENT '创建时间',
`update_by` varchar(36) COMMENT '更新人',
`update_time` datetime(0) COMMENT '更新时间',
`org_code` varchar(64) COMMENT '所属部门',
`del_flag` int(1) DEFAULT 0 COMMENT '0表示未删除,1表示删除',
`standard_number` varchar(50) COMMENT '标准编号',
`standard_name` varchar(50) COMMENT '标准名称',
`standard_text` varchar(50) COMMENT '标准文本',
`related_file` varchar(2000) COMMENT '相关文件',
`consultation_due_date` date COMMENT '征求意见结束日期'
) COMMENT = '标准征求意见流程表';
alter table process_fb_standard_consultation
comment '标准征求意见流程表';
DROP TABLE IF EXISTS `process_fb_consultation_user_link`;
CREATE TABLE `process_fb_consultation_user_link`
(
`id` varchar(36) NOT NULL PRIMARY KEY COMMENT '主键ID',
`standard_consultation_id` varchar(50) COMMENT '标准征求意见流程表id',
`depart_leader_id` varchar(50) COMMENT '部门主管级领导',
`module_owner_id` varchar(50) COMMENT '模块负责人',
`design_engineer_id` varchar(50) COMMENT '设计工程师'
) COMMENT = '标准征求意见流程-用户关联表';
alter table process_fb_consultation_user_link
comment '标准征求意见流程-用户关联表';
DROP TABLE IF EXISTS `process_fb_consultation_list`;
CREATE TABLE `process_fb_consultation_list`
(
`id` varchar(36) NOT NULL PRIMARY KEY COMMENT '主键ID',
`user_link_id` varchar(50) COMMENT '用户关联表id',
`serial_number` varchar(50) COMMENT '序号',
`chapter_number` varchar(50) COMMENT '章节编号',
`chapter_name` varchar(50) COMMENT '章节名称',
`modification_before` varchar(100) COMMENT '修改前',
`modification_after` varchar(100) COMMENT '修改后',
`modification_reason` varchar(500) COMMENT '修改理由',
`is_adopt` varchar(5) COMMENT '模块负责人意见(0不采纳,1采纳)',
`basis_description` varchar(500) COMMENT '依据描述',
`basis_file` varchar(2000) COMMENT '依据材料'
) COMMENT = '标准征求意见流程-征求意见列表表';
alter table process_fb_consultation_list
comment '标准征求意见流程-征求意见列表表';
DROP TABLE IF EXISTS `process_fb_standard_purchase`;
CREATE TABLE `process_fb_standard_purchase`
(
`id` varchar(36) NOT NULL PRIMARY KEY COMMENT '主键ID',
`create_by` varchar(36) COMMENT '创建人',
`create_time` datetime(0) COMMENT '创建时间',
`update_by` varchar(36) COMMENT '更新人',
`update_time` datetime(0) COMMENT '更新时间',
`org_code` varchar(64) COMMENT '所属部门',
`del_flag` int(1) DEFAULT 0 COMMENT '0表示未删除,1表示删除',
`business_id` varchar(50) COMMENT '业务id',
`sort` int(8) COMMENT '排序',
`standard_number` varchar(50) COMMENT '标准编号',
`standard_name` varchar(50) COMMENT '标准名称',
`purchase_reason` varchar(500) COMMENT '采购理由'
) COMMENT = '法规采购流程表';
alter table process_fb_standard_purchase
comment '法规采购流程表';
DROP TABLE IF EXISTS `laws_early_warning_message`;
CREATE TABLE `laws_early_warning_message`
(
`id` varchar(36) NOT NULL PRIMARY KEY COMMENT '主键ID',
`standard_number` varchar(50) COMMENT '标准编号',
`warning_day` varchar(50) COMMENT '警告日期',
`warning_type` varchar(5) COMMENT '警告类型(1新车型实施日期,2在产车实施日期)'
) COMMENT = '标准预警-已发消息';
alter table laws_early_warning_message
comment '标准预警-已发消息';
+13
View File
@@ -0,0 +1,13 @@
FROM mysql:8.0.19
MAINTAINER jeecgos@163.com
ENV TZ=Asia/Shanghai
RUN ln -sf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
COPY ./tables_nacos.sql /docker-entrypoint-initdb.d
COPY jeroboot-mysql-5.7.sql /docker-entrypoint-initdb.d
COPY ./tables_xxl_job.sql /docker-entrypoint-initdb.d
@@ -0,0 +1,25 @@
alter table act_evt_log comment = '事件日志表';
alter table act_ge_bytearray comment = '二进制资源表';
alter table act_ge_property comment = '系统相关属性表';
alter table act_hi_actinst comment = '历史的流程实例表';
alter table act_hi_attachment comment = '历史的流程附件表';
alter table act_hi_comment comment = '历史审批意见表';
alter table act_hi_detail comment = '历史详情表';
alter table act_hi_identitylink comment = '历史的流程运行过程中用户关系表';
alter table act_hi_procinst comment = '历史的流程实例表';
alter table act_hi_taskinst comment = '历史的任务实例表';
alter table act_hi_varinst comment = '历史的流程运行中的变量信息表';
alter table act_procdef_info comment = '流程定义更新信息表';
alter table act_re_deployment comment = '流程部署表';
alter table act_re_model comment = '流程模型表';
alter table act_re_procdef comment = '已部署的流程定义表';
alter table act_ru_deadletter_job comment = '运行时死信作业表';
alter table act_ru_event_subscr comment = '运行时事件表';
alter table act_ru_execution comment = '运行时流程执行实例表';
alter table act_ru_identitylink comment = '运行时用户关系信息,存储任务节点与参与者的相关信息表';
-- alter table act_ru_integration comment = '运行时积分表';
alter table act_ru_job comment = '运行时作业表';
alter table act_ru_suspended_job comment = '运行时挂起的定时作业表';
alter table act_ru_task comment = '运行时任务表';
alter table act_ru_timer_job comment = '运行时定时作业表';
alter table act_ru_variable comment = '运行时变量表';
File diff suppressed because it is too large Load Diff
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
@@ -0,0 +1,119 @@
#
# XXL-JOB v2.2.0
# Copyright (c) 2015-present, xuxueli.
CREATE database if NOT EXISTS `xxl_job` default character set utf8mb4 collate utf8mb4_unicode_ci;
use `xxl_job`;
SET NAMES utf8mb4;
CREATE TABLE `xxl_job_info` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`job_group` int(11) NOT NULL COMMENT '执行器主键ID',
`job_cron` varchar(128) NOT NULL COMMENT '任务执行CRON',
`job_desc` varchar(255) NOT NULL,
`add_time` datetime DEFAULT NULL,
`update_time` datetime DEFAULT NULL,
`author` varchar(64) DEFAULT NULL COMMENT '作者',
`alarm_email` varchar(255) DEFAULT NULL COMMENT '报警邮件',
`executor_route_strategy` varchar(50) DEFAULT NULL COMMENT '执行器路由策略',
`executor_handler` varchar(255) DEFAULT NULL COMMENT '执行器任务handler',
`executor_param` varchar(512) DEFAULT NULL COMMENT '执行器任务参数',
`executor_block_strategy` varchar(50) DEFAULT NULL COMMENT '阻塞处理策略',
`executor_timeout` int(11) NOT NULL DEFAULT '0' COMMENT '任务执行超时时间,单位秒',
`executor_fail_retry_count` int(11) NOT NULL DEFAULT '0' COMMENT '失败重试次数',
`glue_type` varchar(50) NOT NULL COMMENT 'GLUE类型',
`glue_source` mediumtext COMMENT 'GLUE源代码',
`glue_remark` varchar(128) DEFAULT NULL COMMENT 'GLUE备注',
`glue_updatetime` datetime DEFAULT NULL COMMENT 'GLUE更新时间',
`child_jobid` varchar(255) DEFAULT NULL COMMENT '子任务ID,多个逗号分隔',
`trigger_status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '调度状态:0-停止,1-运行',
`trigger_last_time` bigint(13) NOT NULL DEFAULT '0' COMMENT '上次调度时间',
`trigger_next_time` bigint(13) NOT NULL DEFAULT '0' COMMENT '下次调度时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `xxl_job_log` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`job_group` int(11) NOT NULL COMMENT '执行器主键ID',
`job_id` int(11) NOT NULL COMMENT '任务,主键ID',
`executor_address` varchar(255) DEFAULT NULL COMMENT '执行器地址,本次执行的地址',
`executor_handler` varchar(255) DEFAULT NULL COMMENT '执行器任务handler',
`executor_param` varchar(512) DEFAULT NULL COMMENT '执行器任务参数',
`executor_sharding_param` varchar(20) DEFAULT NULL COMMENT '执行器任务分片参数,格式如 1/2',
`executor_fail_retry_count` int(11) NOT NULL DEFAULT '0' COMMENT '失败重试次数',
`trigger_time` datetime DEFAULT NULL COMMENT '调度-时间',
`trigger_code` int(11) NOT NULL COMMENT '调度-结果',
`trigger_msg` text COMMENT '调度-日志',
`handle_time` datetime DEFAULT NULL COMMENT '执行-时间',
`handle_code` int(11) NOT NULL COMMENT '执行-状态',
`handle_msg` text COMMENT '执行-日志',
`alarm_status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '告警状态:0-默认、1-无需告警、2-告警成功、3-告警失败',
PRIMARY KEY (`id`),
KEY `I_trigger_time` (`trigger_time`),
KEY `I_handle_code` (`handle_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `xxl_job_log_report` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`trigger_day` datetime DEFAULT NULL COMMENT '调度-时间',
`running_count` int(11) NOT NULL DEFAULT '0' COMMENT '运行中-日志数量',
`suc_count` int(11) NOT NULL DEFAULT '0' COMMENT '执行成功-日志数量',
`fail_count` int(11) NOT NULL DEFAULT '0' COMMENT '执行失败-日志数量',
PRIMARY KEY (`id`),
UNIQUE KEY `i_trigger_day` (`trigger_day`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `xxl_job_logglue` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`job_id` int(11) NOT NULL COMMENT '任务,主键ID',
`glue_type` varchar(50) DEFAULT NULL COMMENT 'GLUE类型',
`glue_source` mediumtext COMMENT 'GLUE源代码',
`glue_remark` varchar(128) NOT NULL COMMENT 'GLUE备注',
`add_time` datetime DEFAULT NULL,
`update_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `xxl_job_registry` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`registry_group` varchar(50) NOT NULL,
`registry_key` varchar(255) NOT NULL,
`registry_value` varchar(255) NOT NULL,
`update_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `i_g_k_v` (`registry_group`,`registry_key`,`registry_value`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `xxl_job_group` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`app_name` varchar(64) NOT NULL COMMENT '执行器AppName',
`title` varchar(12) NOT NULL COMMENT '执行器名称',
`address_type` tinyint(4) NOT NULL DEFAULT '0' COMMENT '执行器地址类型:0=自动注册、1=手动录入',
`address_list` varchar(512) DEFAULT NULL COMMENT '执行器地址列表,多地址逗号分隔',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `xxl_job_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL COMMENT '账号',
`password` varchar(50) NOT NULL COMMENT '密码',
`role` tinyint(4) NOT NULL COMMENT '角色:0-普通用户、1-管理员',
`permission` varchar(255) DEFAULT NULL COMMENT '权限:执行器ID列表,多个逗号分割',
PRIMARY KEY (`id`),
UNIQUE KEY `i_username` (`username`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `xxl_job_lock` (
`lock_name` varchar(50) NOT NULL COMMENT '锁名称',
PRIMARY KEY (`lock_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
INSERT INTO `xxl_job_group`(`id`, `app_name`, `title`, `address_type`, `address_list`) VALUES (1, 'xxl-job-executor-sample', '示例执行器', 0, NULL);
INSERT INTO `xxl_job_info`(`id`, `job_group`, `job_cron`, `job_desc`, `add_time`, `update_time`, `author`, `alarm_email`, `executor_route_strategy`, `executor_handler`, `executor_param`, `executor_block_strategy`, `executor_timeout`, `executor_fail_retry_count`, `glue_type`, `glue_source`, `glue_remark`, `glue_updatetime`, `child_jobid`) VALUES (1, 1, '0 0 0 * * ? *', '测试任务1', '2018-11-03 22:21:31', '2018-11-03 22:21:31', 'XXL', '', 'FIRST', 'demoJobHandler', '', 'SERIAL_EXECUTION', 0, 0, 'BEAN', '', 'GLUE代码初始化', '2018-11-03 22:21:31', '');
INSERT INTO `xxl_job_user`(`id`, `username`, `password`, `role`, `permission`) VALUES (1, 'admin', 'e10adc3949ba59abbe56e057f20f883e', 1, NULL);
INSERT INTO `xxl_job_lock` ( `lock_name`) VALUES ( 'schedule_lock');
commit;
@@ -0,0 +1,70 @@
-- 1、创建内置的几种角色
INSERT INTO `sys_role`(`id`, `role_name`, `role_code`, `description`, `create_by`, `create_time`, `update_by`, `update_time`, `parent_code`, `role_order`, `app_id`) VALUES ('817f7f818bb3506e018bb3506ef70000', '标准查询', 'BZCX', NULL, 'admin', '2023-11-09 17:01:15', NULL, NULL, NULL, NULL, NULL);
INSERT INTO `sys_role`(`id`, `role_name`, `role_code`, `description`, `create_by`, `create_time`, `update_by`, `update_time`, `parent_code`, `role_order`, `app_id`) VALUES ('817f7f818bb3506e018bb35153cb0002', '标准管理员', 'BZGLY', NULL, 'admin', '2023-11-09 17:02:13', NULL, NULL, NULL, NULL, NULL);
INSERT INTO `sys_role`(`id`, `role_name`, `role_code`, `description`, `create_by`, `create_time`, `update_by`, `update_time`, `parent_code`, `role_order`, `app_id`) VALUES ('8989f8bc8c5bd483018c5bd483060000', '标准化联络员', 'BZHLLY', '标准化联络员', 'admin', '2023-12-12 10:21:43', NULL, NULL, NULL, NULL, NULL);
UPDATE sys_role SET role_name = '系统管理员' WHERE id = 'f6817f48af4fb3af11b9e8bf182f618b' and role_name = '开发管理员';
-- 2、将IAM默认生成的角色id=123 的数据 修改成 标准查询 角色的id
UPDATE sys_user_role SET role_id = '817f7f818bb3506e018bb3506ef70000' WHERE role_id = '123';
-- 3、标准类别代号 字段的修改,删除多余的,将J10 改成J
-- DELETE FROM sys_dict_item WHERE dict_id = '1696792594350952450' AND item_value > 11;
-- UPDATE sys_dict_item SET item_text = 'J' WHERE dict_id = '1696792594350952450' AND item_value = 11;
-- 4、 UPDATE 国家/地区 数据字典 增加 欧洲经济委员会 2023-12-05
INSERT INTO `sys_dict_item`(`id`, `dict_id`, `parent_id`, `item_text`, `en_name`, `item_value`, `description`, `sort_order`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `is_tag_dict`, `is_read_only`, `attribute_type`, `del_flag`, `stat_node`) VALUES ('1731917591764377602', '1706192138039345153', 'c138d98de3152e7811456b15c2577a3a', '欧洲经济委员会', 'ECE', 'ECE5', NULL, 99, 1, 'admin', '2023-12-05 14:05:06', 'admin', '2023-12-05 14:06:00', 1, 0, NULL, '0', NULL);
-- 5 一阶段先去掉【零部件名称】字段 2023-12-05
UPDATE laws_tag SET del_flag = 1 WHERE `db_field_txt` LIKE '%零部件名称%';
-- 6、 责任部门修改成 多选
UPDATE laws_tag SET field_show_type = 7 WHERE db_field_txt = '责任部门';
UPDATE laws_tag SET field_show_type = 7, db_length = 500 WHERE db_field_txt = '责任部门';
-- 修改 法规表对应的字段
ALTER TABLE laws_overseas_standard MODIFY COLUMN responsible_department VARCHAR(500) comment '责任部门';
ALTER TABLE laws_domestic_standard MODIFY COLUMN responsible_department VARCHAR(500) comment '责任部门';
-- bug79060 国外标准调整顺序 调整顺序
UPDATE laws_tag SET order_num = 24 WHERE db_field_txt = '代替标准号' AND id = '1696041640985288701';
UPDATE laws_tag SET order_num = 25 WHERE db_field_txt = '被代替标准号' AND id = '1696041812461019101';
UPDATE laws_tag SET order_num = 26 WHERE db_field_txt = '引用标准' AND id = '1696041991343890401';
-- bug79064 企标搜索中心 字段的处理
UPDATE laws_tag
SET is_show_search = 1
WHERE
table_name = 'laws_enterprise_standard'
AND (
db_field_txt = '企标编号'
OR db_field_txt = '企标名称'
OR db_field_txt = '企业标准代号'
OR db_field_txt = '企业名称代号'
OR db_field_txt = '标准类别代号'
OR db_field_txt = '年代号'
OR db_field_txt = '标准体系'
OR db_field_txt = '标准英文名称'
OR db_field_txt = '标准等级分类'
OR db_field_txt = '授权部门'
OR db_field_txt = '发布日期'
OR db_field_txt = '实施日期'
OR db_field_txt = '是否试行'
OR db_field_txt = '试行周期'
OR db_field_txt = '标准状态'
OR db_field_txt = '主起草人'
OR db_field_txt = '主起草单位'
OR db_field_txt = '主起草部门负责人'
OR db_field_txt = '配合单位'
OR db_field_txt = '标准推进人'
OR db_field_txt = '零部件名称'
OR db_field_txt = '标准适用范围'
OR db_field_txt = '代替标准号'
OR db_field_txt = '被代替标准号'
OR db_field_txt = '引用标准'
OR db_field_txt = '被引用标准'
OR db_field_txt = '采标程度'
OR db_field_txt = '采用国际标准号'
);
-- 2023-12-6 修改 海外标准 标准名称的长度为200 因为老法规数据导入的时候50报数据库长度不够
ALTER TABLE laws_overseas_standard MODIFY COLUMN standard_name VARCHAR(200) comment '标准名称';
ALTER TABLE laws_browsing_history MODIFY COLUMN standard_name VARCHAR(200) comment '标准名称';
@@ -0,0 +1,5 @@
ALTER TABLE laws_replaced_standard
MODIFY COLUMN replaced VARCHAR(200);
ALTER TABLE laws_replaced_standard
MODIFY COLUMN replaced_by VARCHAR(200);
+95
View File
@@ -0,0 +1,95 @@
-- auto-generated definition
create table process_model_node
(
id varchar(36) not null comment '主键id'
primary key,
model_id varchar(64) null comment '模型id',
node_name varchar(50) null comment '节点名称',
node_type varchar(50) null comment '节点类型',
xml_sub_node_id varchar(50) null comment '子流程id',
xml_node_id varchar(50) null comment 'xml节点Id',
xml_source_ref varchar(50) null comment 'xml来源节点id',
xml_target_ref varchar(50) null comment 'xml目标节点id',
variable_type varchar(50) null comment '变量类型',
variable_name varchar(50) null comment '变量名称',
is_approval_node tinyint null comment '是否审批节点',
is_can_deleted tinyint null comment '是否可删除',
sort tinyint null comment '排序',
create_by varchar(36) null comment '创建人',
create_time datetime null comment '创建时间',
update_by varchar(36) null comment '更新人',
update_time datetime null comment '更新时间',
sys_code varchar(64) null comment '所属部门'
)
comment '流程模型节点表';
create table process_node
(
id varchar(36) not null comment '主键id'
primary key,
process_definition_id varchar(64) null comment '流程定义id',
node_name varchar(50) null comment '节点名称',
variable_type varchar(50) null comment '变量类型',
variable_name varchar(50) null comment '变量名称',
sort tinyint null comment '排序',
create_by varchar(36) null comment '创建人',
create_time datetime null comment '创建时间',
update_by varchar(36) null comment '更新人',
update_time datetime null comment '更新时间',
sys_code varchar(64) null comment '所属部门'
)
comment '流程定义节点表';
create table process_node_link
(
id varchar(36) not null primary key comment '主键id',
process_definition_id varchar(64) comment '流程定义id',
process_node_id varchar(36) comment '流程节点id',
process_instance_id varchar(64) comment '流程实例id',
user_id varchar(36) comment '用户id'
) comment = '流程实例节点关联用户表';
create table process_approval_record
(
id varchar(32) not null comment '主键'
primary key,
task_id varchar(64) comment '任务id',
task_name varchar(255) comment '任务名称',
process_instance_id varchar(64) comment '流程实例id',
msg_type varchar(120) comment '资源类型',
user_id varchar(32) comment '处理人id',
mesg longtext comment '信息',
finish_flag tinyint(4) comment '完成注记(0未完成,1完成)',
project_id varchar(32) comment '项目id',
commit_flag tinyint(4) comment '审批意见(0同意,1不同意)',
commit_text varchar(255) comment '审批内容',
commit_file varchar(1000) comment '审批附件',
has_delegate_task tinyint(4) default 0 comment '是否派发过加签任务',
is_delegate_task tinyint(4) default 0 comment '是否加签任务',
prc_name varchar(255) comment '流程名称',
create_time datetime comment '创建时间',
update_time datetime comment '更新时间',
end_time datetime comment '完成时间'
) comment '流程审批记录表';
create table process_all
(
id varchar(32) not null
primary key,
prc_name varchar(255) comment '流程名称',
process_instance_id varchar(64) comment '流程实例id',
prc_mes varchar(255) comment '流程描述',
prc_num varchar(50) comment '流程编号',
create_user_id varchar(50) comment '创建人id',
prc_type tinyint(4) comment '流程类型',
prc_status tinyint(4) comment '流程状态(1未完成,2已完成,3已撤回)',
create_user_name varchar(255) comment '发起人名称',
mes longtext comment 'json信息',
create_time datetime default current_timestamp comment '创建时间',
end_time datetime comment '完成时间',
over_time varchar(255) comment '耗时',
due_time datetime comment '截止时间',
project_id varchar(255) comment '项目id',
project_name varchar(255) comment '项目名称',
prc_name_en varchar(255) comment '流程名称(英文)'
) comment '流程总表';
@@ -0,0 +1,3 @@
第二行替换为:<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.activiti.org/test">
第三行修改为:isExecutable="true"
camunda: 替换为 activiti:
@@ -0,0 +1,150 @@
#
#
# id使sql的idstandard-assessment-1 使idstandard-assessment
# key更新流程图
update process_model_node
set is_approval_node = 0
where process_key = 'standard-assessment';
# 使
# '%standard-assessment%''%standard-assessment-1%'sql后再换回来
SELECT *
FROM process_node
WHERE (process_definition_id LIKE '%standard-assessment%'
OR process_definition_id LIKE '%standard-assessment:9%')
and is_view = 1
ORDER BY process_definition_id, sort;
#
update process_node
set is_view = 0
where process_definition_id like 'standard-assessment:%'
and substr(xml_node_id, -2) in ('_2', '_3', '_4', '_5', '_6', '_7', '_8');
#
update process_node
set is_skip = 1
where process_definition_id like 'standard-assessment:%'
and xml_node_id in ('sjgcsbcgzlbh', 'mkzrrff_1', 'sjgcspg_1', 'mkzrrsp_1', 'bszgjldsp_1');
#
update process_node
set can_choose = 0
where process_definition_id like 'standard-assessment:%';
#
update process_node
set transfer_flag = 0
where process_definition_id like 'standard-assessment:%'
and xml_node_id in ('sjgcsbcgzlbh','fggcsfq');
#
update process_node
set sort = case
when xml_node_id = 'fggcsfq' then '1'
when xml_node_id = 'bszgjldxf' then '2'
when xml_node_id = 'mkzrrff_1' then '3'
when xml_node_id = 'sjgcspg_1' then '4'
when xml_node_id = 'mkzrrsp_1' then '5'
when xml_node_id = 'bszgjldsp_1' then '6'
when xml_node_id = 'fggcssp' then '10'
when xml_node_id = 'sjgcsbcgzlbh' then '15'
end
where process_definition_id like 'standard-assessment:%';
#
# /
# /
#
#
#
#
update process_node
set is_skip = 1
where process_definition_id like 'project-assessment:%'
and xml_node_id in ('mkfzrxg');
#
update process_node
set can_choose = 0
where process_definition_id like 'project-assessment:%';
#
#
#
#
#
#
#
# /-
# /-
# /-
# /-
# -
# -
# -
# -
# //
# //
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
@@ -0,0 +1,51 @@
#### 镜像上传
# 仓库私服: 81.70.17.111:5000
# 第一步:上传镜像到docker仓库
#docker tag jero-boot-mysql 81.70.17.111:5000/jero-boot-mysql:1.1
#docker tag jero-boot-system 81.70.17.111:5000/jero-boot-system:1.0
#docker tag nginxhtml:jeroboot 81.70.17.111:5000/nginxhtml:1.2
#docker push 81.70.17.111:5000/jero-boot-mysql:1.1
#docker push 81.70.17.111:5000/jero-boot-system:1.0
#docker push 81.70.17.111:5000/nginxhtml:1.2
# 第二步:将此yml文件上传服务器,执行启动命令 docker-compose -f ./docker-compose-server.yml up
version: '2'
services:
jero-boot-mysql:
image: 81.70.17.111:5000/jero-boot-mysql:1.0
environment:
MYSQL_ROOT_PASSWORD: root
restart: always
container_name: jero-boot-mysql
command:
--character-set-server=utf8mb4
--collation-server=utf8mb4_general_ci
--explicit_defaults_for_timestamp=true
--lower_case_table_names=1
--max_allowed_packet=128M
ports:
- 3306:3306
jero-boot-redis:
image: redis:5.0
ports:
- 6379:6379
restart: always
container_name: jero-boot-redis
jero-boot-system:
image: 81.70.17.111:5000/jero-boot-system:1.0
restart: always
container_name: jero-boot-system
volumes:
- /data/config:/jero-boot/config
ports:
- 8080:8080
jero-boot-nginx:
image: 81.70.17.111:5000/nginxhtml
restart: always
container_name: jero-boot-nginx
ports:
- 80:80
@@ -0,0 +1,35 @@
version: '2'
services:
jero-boot-mysql:
build:
context: ./db
environment:
MYSQL_ROOT_PASSWORD: root
restart: always
container_name: jero-boot-mysql
image: jero-boot-mysql
command:
--character-set-server=utf8mb4
--collation-server=utf8mb4_general_ci
--explicit_defaults_for_timestamp=true
--lower_case_table_names=1
--max_allowed_packet=128M
ports:
- 3306:3306
jero-boot-redis:
image: redis:5.0
ports:
- 6379:6379
restart: always
container_name: jero-boot-redis
jero-boot-system:
build:
context: ./jero-boot-module-system
restart: always
container_name: jero-boot-system
image: jero-boot-system
ports:
- 8080:8080
@@ -0,0 +1,60 @@
[{
"id": "jero-system",
"order": 0,
"predicates": [{
"name": "Path",
"args": {
"_genkey_0": "/sys/**",
"_genkey_1": "/eoa/**",
"_genkey_2": "/joa/**",
"_genkey_3": "/jmreport/**",
"_genkey_4": "/bigscreen/**",
"_genkey_5": "/desform/**",
"_genkey_6": "/online/**",
"_genkey_8": "/act/**",
"_genkey_9": "/plug-in/**",
"_genkey_10": "/generic/**",
"_genkey_11": "/v1/**"
}
}],
"filters": [],
"uri": "lb://jero-system"
}, {
"id": "jero-demo",
"order": 1,
"predicates": [{
"name": "Path",
"args": {
"_genkey_0": "/mock/**",
"_genkey_1": "/test/**",
"_genkey_2": "/bigscreen/template1/**",
"_genkey_3": "/bigscreen/template2/**"
}
}],
"filters": [],
"uri": "lb://jero-demo"
}, {
"id": "jero-system-websocket",
"order": 2,
"predicates": [{
"name": "Path",
"args": {
"_genkey_0": "/websocket/**",
"_genkey_1": "/eoaSocket/**",
"_genkey_2": "/newsWebsocket/**"
}
}],
"filters": [],
"uri": "lb:ws://jero-system"
}, {
"id": "jero-demo-websocket",
"order": 3,
"predicates": [{
"name": "Path",
"args": {
"_genkey_0": "/vxeSocket/**"
}
}],
"filters": [],
"uri": "lb:ws://jero-demo"
}]
@@ -0,0 +1,321 @@
# 标准配置的模板,可自行删减
server:
tomcat:
max-swallow-size: -1
error:
include-exception: true
include-stacktrace: ALWAYS
include-message: ALWAYS
compression:
enabled: true
min-response-size: 1024
mime-types: application/javascript,application/json,application/xml,text/html,text/xml,text/plain,text/css,image/*
management:
health:
mail:
enabled: false
endpoints:
web:
exposure:
include: "*" #暴露所有节点
health:
sensitive: true #关闭过滤敏感信息
endpoint:
health:
show-details: ALWAYS #显示详细信息
spring:
servlet:
multipart:
max-file-size: 10MB
max-request-size: 10MB
mail:
host: smtp.163.com
username: test@163.com
password: ??
properties:
mail:
smtp:
auth: true
starttls:
enable: true
required: true
## quartz定时任务,采用数据库方式
quartz:
job-store-type: jdbc
initialize-schema: embedded
#定时任务启动开关,true-开 false-关
auto-startup: true
#启动时更新己存在的Job
overwrite-existing-jobs: true
properties:
org:
quartz:
scheduler:
instanceName: MyScheduler
instanceId: AUTO
jobStore:
class: org.quartz.impl.jdbcjobstore.JobStoreTX
driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
tablePrefix: QRTZ_
isClustered: true
misfireThreshold: 60000
clusterCheckinInterval: 10000
threadPool:
class: org.quartz.simpl.SimpleThreadPool
threadCount: 10
threadPriority: 5
threadsInheritContextClassLoaderOfInitializingThread: true
#json 时间戳统一转换
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
jpa:
open-in-view: false
activiti:
check-process-definitions: false
#启用作业执行器
async-executor-activate: false
#启用异步执行器
job-executor-activate: false
aop:
proxy-target-class: true
#配置freemarker
freemarker:
# 设置模板后缀名
suffix: .ftl
# 设置文档类型
content-type: text/html
# 设置页面编码格式
charset: UTF-8
# 设置页面缓存
cache: false
prefer-file-system-access: false
# 设置ftl文件路径
template-loader-path:
- classpath:/templates
# 设置静态文件路径,js,css等
mvc:
static-path-pattern: /**
resource:
static-locations: classpath:/static/,classpath:/public/
autoconfigure:
exclude: com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure
datasource:
druid:
stat-view-servlet:
enabled: true
loginUsername: druid
loginPassword: druid
allow:
web-stat-filter:
enabled: true
dynamic:
druid: # 全局druid参数,绝大部分值和默认保持一致。(现已支持的参数如下,不清楚含义不要乱设置)
# 连接池的配置信息
# 初始化大小,最小,最大
initial-size: 5
min-idle: 5
maxActive: 20
# 配置获取连接等待超时的时间
maxWait: 60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
timeBetweenEvictionRunsMillis: 60000
# 配置一个连接在池中最小生存的时间,单位是毫秒
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
# 打开PSCache,并且指定每个连接上PSCache的大小
poolPreparedStatements: true
maxPoolPreparedStatementPerConnectionSize: 20
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
filters: stat,wall,slf4j
# 通过connectProperties属性来打开mergeSql功能;慢SQL记录
connectionProperties: druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000;druid.stat.logSlowSql\=true
datasource:
master:
url: jdbc:mysql://127.0.0.1:3306/jero-boot?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
# url: jdbc:mysql://127.0.0.1:3306/jero-boot?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
# username: root
# password: 123456
# driver-class-name: com.mysql.cj.jdbc.Driver
# 多数据源配置
#multi-datasource1:
#url: jdbc:mysql://localhost:3306/jero-boot2?useUnicode=true&characterEncoding=utf8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
#username: root
#password: root
#driver-class-name: com.mysql.cj.jdbc.Driver
#redis 配置
redis:
database: 3
host: 121.36.69.172
lettuce:
pool:
max-active: 8 #最大连接数据库连接数,设 0 为没有限制
max-idle: 8 #最大等待连接中的数量,设 0 为没有限制
max-wait: -1ms #最大建立连接等待时间。如果超过此时间将接到异常。设为-1表示无限制。
min-idle: 0 #最小等待连接中的数量,设 0 为没有限制
shutdown-timeout: 100ms
password: 'hzwlsoft.com'
port: 4780
#rabbitmq 配置
rabbitmq:
host: 121.36.69.172
username: admin
password: hzwlsoft.com
port: 5672
publisher-confirm-type: correlated
publisher-returns: true
virtual-host: /
listener:
simple:
acknowledge-mode: manual
#消费者的最小数量
concurrency: 1
#消费者的最大数量
max-concurrency: 1
#是否支持重试
retry:
enabled: true
#mybatis plus 设置
mybatis-plus:
mapper-locations: classpath*:com/jero/**/xml/*Mapper.xml
global-config:
# 关闭MP3.0自带的banner
banner: false
db-config:
#主键类型 0:"数据库ID自增",1:"该类型为未设置主键类型", 2:"用户输入ID",3:"全局唯一ID (数字类型唯一ID)", 4:"全局唯一ID UUID",5:"字符串全局唯一ID (idWorker 的字符串表示)";
id-type: ASSIGN_ID
# 默认数据库表下划线命名
table-underline: true
configuration:
# 这个配置会将执行的sql打印出来,在开发或测试的时候可以用
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# 返回类型为Map,显示null对应的字段
call-setters-on-nulls: true
#jero专用配置
jero :
# 签名密钥串(前后端要一致,正式发布请自行修改)
signatureSecret: dd05f1c54d63749eda95f9fa6d49v442a
# 本地:local\Miniominio\阿里云:alioss
uploadType: local
path :
#文件上传根目录 设置
upload: D://opt//upFiles
#webapp文件路径
webapp: D://opt//webapp
shiro:
excludeUrls: /test/jeroDemo/demo3,/test/jeroDemo/redisDemo/**,/category/**,/visual/**,/map/**,/jmreport/bigscreen2/**
#阿里云oss存储配置
oss:
endpoint: oss-cn-beijing.aliyuncs.com
accessKey: ??
secretKey: ??
bucketName: jeroos
staticDomain: jerodev
# ElasticSearch 6设置
elasticsearch:
cluster-name: jero-ES
cluster-nodes: 127.0.0.1:9200
check-enabled: false
# 表单设计器配置
desform:
# 主题颜色(仅支持 16进制颜色代码)
theme-color: "#1890ff"
# 文件、图片上传方式,可选项:qiniu(七牛云)、system(跟随系统配置)
upload-type: system
# 在线预览文件服务器地址配置
file-view-domain: 127.0.0.1:8012
# minio文件上传
minio:
minio_url: http://minio.jero.com
minio_name: ??
minio_pass: ??
bucketName: otatest
#大屏报表参数设置
jmreport:
mode: dev
#数据字典是否可以全局看到
saas: false
#是否需要校验token
is_verify_token: false
#必须校验方法
verify_methods: remove,delete,save,add,update
#Wps在线文档
wps:
domain: https://wwo.wps.cn/office/
appid: ??
appsecret: ??
#xxl-job配置
xxljob:
enabled: false
adminAddresses: http://127.0.0.1:9080/xxl-job-admin
appname: ${spring.application.name}
accessToken: ''
address: 127.0.0.1:30007
ip: 127.0.0.1
port: 30007
logPath: logs/jero/job/jobhandler/
logRetentionDays: 30
route:
config:
data-id: jero-gateway-router
group: DEFAULT_GROUP
#自定义路由配置 yml nacos database
data-type: yml
#分布式锁配置
redisson:
address: 121.36.69.172:4780
password: 'hzwlsoft.com'
type: STANDALONE
enabled: true
# 文件限制后缀黑名单
fileSuffixLimits : 0x00,%00,\\00,.jsp,.exe,.php,.asp,.aspx,.jspx,.xml,.html,.js,.sh,.bin
# 跨站白名单
whiteUrls:
#cas单点登录
cas:
prefixUrl: http://cas.example.org:8443/cas
#Mybatis输出sql日志
logging:
level:
com.jero.modules.system.mapper : info
#swagger
knife4j:
#开启增强配置
enable: true
#开启生产环境屏蔽
production: false
basic:
enable: false
username: jero
password: jero.com
#第三方登录
justauth:
enabled: true
type:
GITHUB:
client-id: true
client-secret: true
redirect-uri: http://sso.test.com:8080/jero-boot/sys/thirdLogin/github/callback
WECHAT_ENTERPRISE:
client-id: true
client-secret: true
redirect-uri: http://sso.test.com:8080/jero-boot/sys/thirdLogin/wechat_enterprise/callback
agent-id: 1000002
DINGTALK:
client-id: true
client-secret: true
redirect-uri: http://sso.test.com:8080/jero-boot/sys/thirdLogin/dingtalk/callback
WECHAT_OPEN:
client-id: ??
client-secret: ??
redirect-uri: http://sso.test.com:8080/jero-boot/sys/thirdLogin/wechat_open/callback
cache:
type: default
prefix: 'demo::'
timeout: 1h
@@ -0,0 +1,58 @@
<?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">
<parent>
<artifactId>laws-starter</artifactId>
<groupId>com.jero.boot</groupId>
<version>2.5.1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>laws-starter-cloud</artifactId>
<dependencies>
<dependency>
<groupId>com.jero.boot</groupId>
<artifactId>jero-system-cloud-api</artifactId>
</dependency>
<!-- Nacos注册中心 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<exclusions>
<exclusion>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Nacos配置中心 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
<exclusions>
<exclusion>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Nacos 2.0客户端 -->
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
<version>2.0.4</version>
</dependency>
<!-- feign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- 服务降级 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,170 @@
package com.jero.config;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import com.alibaba.fastjson.support.springfox.SwaggerJsonSerializer;
import feign.Feign;
import feign.Logger;
import feign.RequestInterceptor;
import feign.codec.Decoder;
import feign.codec.Encoder;
import feign.form.spring.SpringFormEncoder;
import lombok.extern.slf4j.Slf4j;
import com.jero.common.config.mqtoken.UserTokenContext;
import com.jero.common.constant.CommonConstant;
import com.jero.common.util.DateUtils;
import com.jero.common.util.PathMatcherUtil;
import com.jero.config.sign.interceptor.SignAuthConfiguration;
import com.jero.config.sign.util.HttpUtils;
import com.jero.config.sign.util.SignUtil;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.cloud.openfeign.FeignAutoConfiguration;
import org.springframework.cloud.openfeign.support.SpringDecoder;
import org.springframework.cloud.openfeign.support.SpringEncoder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Scope;
import org.springframework.http.MediaType;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.SortedMap;
@ConditionalOnClass(Feign.class)
@AutoConfigureBefore(FeignAutoConfiguration.class)
@Slf4j
@Configuration
public class FeignConfig {
@Bean
public RequestInterceptor requestInterceptor() {
return requestTemplate -> {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (null != attributes) {
HttpServletRequest request = attributes.getRequest();
log.debug("Feign request: {}", request.getRequestURI());
// 将token信息放入header中
String token = getToken(request);
log.debug("Feign request token: {}", token);
requestTemplate.header(CommonConstant.X_ACCESS_TOKEN, token);
//根据URL地址过滤请求 【字典表参数签名验证】
if (PathMatcherUtil.matches(Arrays.asList(SignAuthConfiguration.urlList),requestTemplate.path())) {
try {
log.info("============================ [begin] fegin starter url ============================");
log.info(requestTemplate.path());
log.info(requestTemplate.method());
String queryLine = requestTemplate.queryLine();
queryLine = getQueryLine(queryLine);
log.info(queryLine);
if(requestTemplate.body()!=null){
log.info(new String(requestTemplate.body()));
}
SortedMap<String, String> allParams = HttpUtils.getAllParams(requestTemplate.path(),queryLine,requestTemplate.body(),requestTemplate.method());
String sign = SignUtil.getParamsSign(allParams);
log.info(" Feign request params sign: {}",sign);
log.info("============================ [end] fegin starter url ============================");
requestTemplate.header(CommonConstant.X_SIGN, sign);
requestTemplate.header(CommonConstant.X_TIMESTAMP, DateUtils.getCurrentTimestamp().toString());
} catch (IOException e) {
e.printStackTrace();
}
}
}else{
String token = UserTokenContext.getToken();
log.debug("Feign request token: {}", token);
requestTemplate.header(CommonConstant.X_ACCESS_TOKEN, token);
}
};
}
private String getQueryLine(String queryLine) {
if(queryLine!=null && queryLine.startsWith("?")){
queryLine = queryLine.substring(1);
}
return queryLine;
}
private String getToken(HttpServletRequest request) {
String token = request.getHeader(CommonConstant.X_ACCESS_TOKEN);
if(token==null || "".equals(token)){
token = request.getParameter("token");
}
return token;
}
/**
* Feign 客户端的日志记录,默认级别为NONE
* Logger.Level 的具体级别如下:
* NONE:不记录任何信息
* BASIC:仅记录请求方法、URL以及响应状态码和执行时间
* HEADERS:除了记录 BASIC级别的信息外,还会记录请求和响应的头信息
* FULL:记录所有请求与响应的明细,包括头信息、请求体、元数据
*/
@Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
/**
* Feign支持文件上传
* @param messageConverters
* @return
*/
@Bean
@Primary
@Scope("prototype")
public Encoder multipartFormEncoder(ObjectFactory<HttpMessageConverters> messageConverters) {
return new SpringFormEncoder(new SpringEncoder(messageConverters));
}
// update-begin--Author:sunjianlei Date:20210604 for 给 Feign 添加 FastJson 的解析支持 ----------
@Bean
public Encoder feignEncoder() {
return new SpringEncoder(feignHttpMessageConverter());
}
@Bean
public Decoder feignDecoder() {
return new SpringDecoder(feignHttpMessageConverter());
}
/**
* 设置解码器为fastjson
*
* @return
*/
private ObjectFactory<HttpMessageConverters> feignHttpMessageConverter() {
final HttpMessageConverters httpMessageConverters = new HttpMessageConverters(this.getFastJsonConverter());
return () -> httpMessageConverters;
}
private FastJsonHttpMessageConverter getFastJsonConverter() {
FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
List<MediaType> supportedMediaTypes = new ArrayList<>();
MediaType mediaTypeJson = MediaType.valueOf(MediaType.APPLICATION_JSON_VALUE);
supportedMediaTypes.add(mediaTypeJson);
converter.setSupportedMediaTypes(supportedMediaTypes);
FastJsonConfig config = new FastJsonConfig();
config.getSerializeConfig().put(JSON.class, new SwaggerJsonSerializer());
config.setSerializerFeatures(SerializerFeature.DisableCircularReferenceDetect);
converter.setFastJsonConfig(config);
return converter;
}
// update-end--Author:sunjianlei Date:20210604 for 给 Feign 添加 FastJson 的解析支持 ----------
}
@@ -0,0 +1,24 @@
package com.jero.starter.cloud.config;
import feign.Client;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import org.springframework.cloud.openfeign.ribbon.CachingSpringLoadBalancerFactory;
import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class PersonBeanConfiguration {
/**
* 创建FeignClient
*/
@Bean
@ConditionalOnMissingBean
public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
SpringClientFactory clientFactory) {
return new LoadBalancerFeignClient(new Client.Default(null, null),
cachingFactory, clientFactory);
}
}
@@ -0,0 +1,6 @@
package com.jero.starter.cloud.feign;
public interface IJeroFeignService {
<T> T newInstance(Class<T> apiType, String name);
}
@@ -0,0 +1,62 @@
package com.jero.starter.cloud.feign.impl;
import feign.Client;
import feign.Contract;
import feign.Feign;
import feign.codec.Decoder;
import feign.codec.Encoder;
import lombok.extern.slf4j.Slf4j;
import com.jero.common.constant.CommonConstant;
import com.jero.starter.cloud.feign.IJeroFeignService;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.cloud.openfeign.FeignAutoConfiguration;
import org.springframework.cloud.openfeign.FeignClientsConfiguration;
import org.springframework.context.annotation.Import;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
@Service
@Slf4j
@ConditionalOnClass(Feign.class)
@AutoConfigureBefore(FeignAutoConfiguration.class)
@Import(FeignClientsConfiguration.class)
public class JeroFeignService implements IJeroFeignService {
//Feign 原生构造器
Feign.Builder builder;
//创建构造器
public JeroFeignService(Decoder decoder, Encoder encoder, Client client, Contract contract) {
this.builder = Feign.builder()
.client(client)
.encoder(encoder)
.decoder(decoder)
.contract(contract);
builder.requestInterceptor(requestTemplate -> {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (null != attributes) {
HttpServletRequest request = attributes.getRequest();
log.info("Feign request: {}", request.getRequestURI());
// 将token信息放入header中
String token = request.getHeader(CommonConstant.X_ACCESS_TOKEN);
if(token==null){
token = request.getParameter("token");
}
log.info("Feign request token: {}", token);
requestTemplate.header(CommonConstant.X_ACCESS_TOKEN, token);
}
});
}
@Override
public <T> T newInstance(Class<T> clientClass, String serviceName) {
return builder.target(clientClass, String.format("http://%s/", serviceName));
}
}
@@ -0,0 +1,21 @@
<?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">
<parent>
<artifactId>laws-starter</artifactId>
<groupId>com.jero.boot</groupId>
<version>2.5.1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>laws-starter-job</artifactId>
<description>laws-starter-定时任务</description>
<dependencies>
<dependency>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-core</artifactId>
<version>${xxl-job-core.version}</version>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,46 @@
package com.jero.boot.starter.job.config;
import com.xxl.job.core.executor.impl.XxlJobSpringExecutor;
import lombok.extern.slf4j.Slf4j;
import com.jero.boot.starter.job.prop.XxlJobProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.annotation.Resource;
/**
* 定时任务配置
*/
@Slf4j
@Configuration
@EnableConfigurationProperties(value = XxlJobProperties.class)
@ConditionalOnProperty(value = "jero.xxljob.enabled", havingValue = "true", matchIfMissing = true)
public class XxlJobConfiguration {
@Resource
private XxlJobProperties xxlJobProperties;
//@Bean(initMethod = "start", destroyMethod = "destroy")
@Bean
@ConditionalOnClass()
public XxlJobSpringExecutor xxlJobExecutor() {
log.info(">>>>>>>>>>> xxl-job config init.");
XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
xxlJobSpringExecutor.setAdminAddresses(xxlJobProperties.getAdminAddresses());
xxlJobSpringExecutor.setAppname(xxlJobProperties.getAppname());
// xxlJobSpringExecutor.setIp(xxlJobProperties.getIp());
// xxlJobSpringExecutor.setPort(xxlJobProperties.getPort());
xxlJobSpringExecutor.setAccessToken(xxlJobProperties.getAccessToken());
xxlJobSpringExecutor.setLogPath(xxlJobProperties.getLogPath());
xxlJobSpringExecutor.setLogRetentionDays(xxlJobProperties.getLogRetentionDays());
return xxlJobSpringExecutor;
}
}
@@ -0,0 +1,35 @@
package com.jero.boot.starter.job.prop;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
@Data
@ConfigurationProperties(prefix = "jero.xxljob")
public class XxlJobProperties {
private String adminAddresses;
private String appname;
private String ip;
private int port;
private String accessToken;
private String logPath;
private int logRetentionDays;
/**
* 是否开启xxljob
*/
private Boolean enable = true;
}
@@ -0,0 +1,2 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.jero.boot.starter.job.config.XxlJobConfiguration
@@ -0,0 +1,37 @@
<?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">
<parent>
<artifactId>laws-starter</artifactId>
<groupId>com.jero.boot</groupId>
<version>2.5.1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>laws-starter-lock</artifactId>
<description>laws-starter-分布式锁</description>
<dependencies>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,57 @@
package com.jero.boot.starter.lock.annotation;
import com.jero.boot.starter.lock.enums.LockModel;
import java.lang.annotation.*;
/**
* Redisson分布式锁注解
*
* @author zyf
* @date 2020-11-11
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface JLock {
/**
* 锁的模式:如果不设置,自动模式,当参数只有一个.使用 REENTRANT 参数多个 MULTIPLE
*/
LockModel lockModel() default LockModel.AUTO;
/**
* 如果keys有多个,如果不设置,则使用 联锁
* @return
*/
String[] lockKey() default {};
/**
* key的静态常量:当key的spel的值是LIST,数组时使用+号连接将会被spel认为这个变量是个字符串
* @return
*/
String keyConstant() default "";
/**
* 锁超时时间,默认30000毫秒
*
* @return int
*/
long expireSeconds() default 30000L;
/**
* 等待加锁超时时间,默认10000毫秒 -1 则表示一直等待
*
* @return int
*/
long waitTime() default 10000L;
/**
* 未取到锁时提示信息
*
* @return
*/
String failMsg() default "获取锁失败,请稍后重试";
}
@@ -0,0 +1,36 @@
package com.jero.boot.starter.lock.annotation;
/**
* @author zyf
*/
import java.lang.annotation.*;
/**
* 防止重复提交的注解
*
* @author 2019年6月18日
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
public @interface JRepeat {
/**
* 超时时间
*
* @return
*/
int lockTime();
/**
* redis 锁key的
*
* @return redis 锁key
*/
String lockKey() default "";
}
@@ -0,0 +1,72 @@
package com.jero.boot.starter.lock.annotation;
/**
* @author zyf
* @date 2019/10/26 18:26
*/
/**
* 分布式锁枚举类
* @author zyf
*/
public enum LockConstant {
/**
* 通用锁常量
*/
COMMON("commonLock:", 1, 500, "请勿重复点击");
/**
* 分布式锁前缀
*/
private String keyPrefix;
/**
* 等到最大时间,强制获取锁
*/
private int waitTime;
/**
* 锁失效时间
*/
private int leaseTime;
/**
* 加锁提示
*/
private String message;
LockConstant(String keyPrefix, int waitTime, int leaseTime, String message) {
this.keyPrefix = keyPrefix;
this.waitTime = waitTime;
this.leaseTime = leaseTime;
this.message = message;
}
public String getKeyPrefix() {
return keyPrefix;
}
public void setKeyPrefix(String keyPrefix) {
this.keyPrefix = keyPrefix;
}
public int getWaitTime() {
return waitTime;
}
public void setWaitTime(int waitTime) {
this.waitTime = waitTime;
}
public int getLeaseTime() {
return leaseTime;
}
public void setLeaseTime(int leaseTime) {
this.leaseTime = leaseTime;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
@@ -0,0 +1,67 @@
package com.jero.boot.starter.lock.aspect;
import lombok.extern.slf4j.Slf4j;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import java.util.ArrayList;
import java.util.List;
/**
* @author zyf
*/
@Slf4j
public class BaseAspect {
/**
* 通过spring SpEL 获取参数
*
* @param key 定义的key值 以#开头 例如:#user
* @param parameterNames 形参
* @param values 形参值
* @param keyConstant key的常亮
* @return
*/
public List<String> getValueBySpEL(String key, String[] parameterNames, Object[] values, String keyConstant) {
List<String> keys = new ArrayList<>();
if (!key.contains("#")) {
String s = "redis:lock:" + key + keyConstant;
log.info("lockKey:" + s);
keys.add(s);
return keys;
}
//spel解析器
ExpressionParser parser = new SpelExpressionParser();
//spel上下文
EvaluationContext context = new StandardEvaluationContext();
for (int i = 0; i < parameterNames.length; i++) {
context.setVariable(parameterNames[i], values[i]);
}
Expression expression = parser.parseExpression(key);
Object value = expression.getValue(context);
if (value != null) {
if (value instanceof List) {
List<Object> value1 = (List) value;
for (Object o : value1) {
addKeys(keys, o, keyConstant);
}
} else if (value.getClass().isArray()) {
Object[] obj = (Object[]) value;
for (Object o : obj) {
addKeys(keys, o, keyConstant);
}
} else {
addKeys(keys, value, keyConstant);
}
}
log.info("表达式key={},value={}", key, keys);
return keys;
}
private void addKeys(List<String> keys, Object o, String keyConstant) {
keys.add("redis:lock:" + o.toString() + keyConstant);
}
}
@@ -0,0 +1,166 @@
package com.jero.boot.starter.lock.aspect;
import com.jero.boot.starter.lock.annotation.JLock;
import com.jero.boot.starter.lock.enums.LockModel;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.RedissonMultiLock;
import org.redisson.RedissonRedLock;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* 分布式锁解析器
*
* @author zyf
* @date 2020-11-11
*/
@Slf4j
@Aspect
@Component
public class DistributedLockHandler extends BaseAspect{
@Autowired(required = false)
private RedissonClient redissonClient;
/**
* 切面环绕通知
*
* @param joinPoint
* @param jLock
* @return Object
*/
@SneakyThrows
@Around("@annotation(jLock)")
public Object around(ProceedingJoinPoint joinPoint, JLock jLock) {
Object obj = null;
RLock rLock = getLock(joinPoint, jLock);
boolean res = false;
//获取超时时间
long expireSeconds = jLock.expireSeconds();
//等待多久,n秒内获取不到锁,则直接返回
long waitTime = jLock.waitTime();
//执行aop
if (rLock != null) {
try {
if (waitTime == -1) {
res = true;
//一直等待加锁
rLock.lock(expireSeconds, TimeUnit.MILLISECONDS);
} else {
res = rLock.tryLock(waitTime, expireSeconds, TimeUnit.MILLISECONDS);
}
if (res) {
obj = joinPoint.proceed();
} else {
log.error("获取锁异常");
}
} finally {
if (res) {
rLock.unlock();
}
}
}
log.info("结束RedisLock环绕通知...");
return obj;
}
@SneakyThrows
private RLock getLock(ProceedingJoinPoint joinPoint, JLock jLock) {
String[] keys = jLock.lockKey();
if (keys.length == 0) {
throw new IllegalArgumentException("keys不能为空");
}
String[] parameterNames = new LocalVariableTableParameterNameDiscoverer().getParameterNames(((MethodSignature) joinPoint.getSignature()).getMethod());
Object[] args = joinPoint.getArgs();
LockModel lockModel = jLock.lockModel();
if (!lockModel.equals(LockModel.MULTIPLE) && !lockModel.equals(LockModel.REDLOCK) && keys.length > 1) {
throw new IllegalArgumentException("参数有多个,锁模式为->" + lockModel.name() + ".无法锁定");
}
RLock rLock = null;
String keyConstant = jLock.keyConstant();
lockModel = getLockModel(keys, lockModel);
switch (lockModel) {
case FAIR:
rLock = redissonClient.getFairLock(getValueBySpEL(keys[0], parameterNames, args, keyConstant).get(0));
break;
case REDLOCK:
List<RLock> rLocks = new ArrayList<>();
getRedLock(keys, parameterNames, args, keyConstant, rLocks);
RLock[] locks = new RLock[rLocks.size()];
int index = 0;
for (RLock r : rLocks) {
locks[index++] = r;
}
rLock = new RedissonRedLock(locks);
break;
case MULTIPLE:
rLocks = new ArrayList<>();
getRedLock(keys, parameterNames, args, keyConstant, rLocks);
locks = new RLock[rLocks.size()];
index = 0;
for (RLock r : rLocks) {
locks[index++] = r;
}
rLock = new RedissonMultiLock(locks);
break;
case REENTRANT:
List<String> valueBySpEL = getValueBySpEL(keys[0], parameterNames, args, keyConstant);
//如果spel表达式是数组或者LIST 则使用红锁
if (valueBySpEL.size() == 1) {
rLock = redissonClient.getLock(valueBySpEL.get(0));
} else {
locks = new RLock[valueBySpEL.size()];
index = 0;
for (String s : valueBySpEL) {
locks[index++] = redissonClient.getLock(s);
}
rLock = new RedissonRedLock(locks);
}
break;
case READ:
rLock = redissonClient.getReadWriteLock(getValueBySpEL(keys[0], parameterNames, args, keyConstant).get(0)).readLock();
break;
case WRITE:
rLock = redissonClient.getReadWriteLock(getValueBySpEL(keys[0], parameterNames, args, keyConstant).get(0)).writeLock();
break;
default: break;
}
return rLock;
}
private LockModel getLockModel(String[] keys, LockModel lockModel) {
if (lockModel.equals(LockModel.AUTO)) {
if (keys.length > 1) {
lockModel = LockModel.REDLOCK;
} else {
lockModel = LockModel.REENTRANT;
}
}
return lockModel;
}
private void getRedLock(String[] keys, String[] parameterNames, Object[] args, String keyConstant, List<RLock> rLocks) {
for (String key : keys) {
List<String> valueBySpEL = getValueBySpEL(key, parameterNames, args, keyConstant);
for (String s : valueBySpEL) {
rLocks.add(redissonClient.getLock(s));
}
}
}
}
@@ -0,0 +1,80 @@
package com.jero.boot.starter.lock.aspect;
/**
* @author zyf
*/
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import com.jero.boot.starter.lock.annotation.JRepeat;
import com.jero.boot.starter.lock.client.RedissonLockClient;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
/**
* 防止重复提交分布式锁拦截器
*
* @author 2019年6月18日
*/
@Aspect
@Component
public class RepeatSubmitAspect extends BaseAspect{
@Resource
private RedissonLockClient redissonLockClient;
/***
* 定义controller切入点拦截规则,拦截JRepeat注解的业务方法
*/
@Pointcut("@annotation(jRepeat)")
public void pointCut(JRepeat jRepeat) {
// do point
}
/**
* AOP分布式锁拦截
*
* @param joinPoint
* @return
* @throws Exception
*/
@Around("pointCut(jRepeat)")
public Object repeatSubmit(ProceedingJoinPoint joinPoint,JRepeat jRepeat) throws Throwable {
String[] parameterNames = new LocalVariableTableParameterNameDiscoverer().getParameterNames(((MethodSignature) joinPoint.getSignature()).getMethod());
if (Objects.nonNull(jRepeat)) {
// 获取参数
Object[] args = joinPoint.getArgs();
// 进行一些参数的处理,比如获取订单号,操作人id等
String key =getValueBySpEL(jRepeat.lockKey(), parameterNames, args,"RepeatSubmit").get(0);
// 公平加锁,lockTime后锁自动释放
boolean isLocked = false;
try {
isLocked = redissonLockClient.fairLock(key, TimeUnit.SECONDS, jRepeat.lockTime());
// 如果成功获取到锁就继续执行
if (isLocked) {
// 执行进程
return joinPoint.proceed();
} else {
// 未获取到锁
throw new IllegalArgumentException("请勿重复提交");
}
} finally {
// 如果锁还存在,在方法执行完成后,释放锁
if (isLocked) {
redissonLockClient.unlock(key);
}
}
}
return joinPoint.proceed();
}
}
@@ -0,0 +1,143 @@
package com.jero.boot.starter.lock.client;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
/**
* 分布式锁实现基于Redisson
*
* @author zyf
* @date 2020-11-11
*/
@Slf4j
@Component
public class RedissonLockClient {
@Autowired
private RedissonClient redissonClient;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
/**
* 获取锁
*/
public RLock getLock(String lockKey) {
return redissonClient.getLock(lockKey);
}
/**
* 加锁操作
*
* @return boolean
*/
public boolean tryLock(String lockName, long expireSeconds) {
return tryLock(lockName, 0, expireSeconds);
}
/**
* 加锁操作
*
* @return boolean
*/
public boolean tryLock(String lockName, long waitTime, long expireSeconds) {
RLock rLock = getLock(lockName);
boolean getLock = false;
try {
getLock = rLock.tryLock(waitTime, expireSeconds, TimeUnit.SECONDS);
if (getLock) {
log.info("获取锁成功,lockName={}", lockName);
} else {
log.info("获取锁失败,lockName={}", lockName);
}
} catch (InterruptedException e) {
log.error("获取式锁异常,lockName=" + lockName, e);
Thread.currentThread().interrupt();
}finally {
rLock.unlock();
}
return getLock;
}
public boolean fairLock(String lockKey, TimeUnit unit, int leaseTime) {
RLock fairLock = redissonClient.getFairLock(lockKey);
try {
boolean existKey = existKey(lockKey);
// 已经存在了,就直接返回
if (existKey) {
return false;
}
return fairLock.tryLock(3, leaseTime, unit);
} catch (InterruptedException e) {
e.printStackTrace();
Thread.currentThread().interrupt();
}finally {
fairLock.unlock();
}
return false;
}
public boolean existKey(String key) {
return redisTemplate.hasKey(key);
}
/**
* 锁lockKey
*
* @param lockKey
* @return
*/
public RLock lock(String lockKey) {
RLock lock = getLock(lockKey);
try {
lock.lock();
}catch (Exception e){
e.printStackTrace();
} finally {
lock.unlock();
}
return lock;
}
/**
* 锁lockKey
*
* @param lockKey
* @param leaseTime
* @return
*/
public RLock lock(String lockKey, long leaseTime) {
RLock lock = getLock(lockKey);
try {
lock.lock(leaseTime, TimeUnit.SECONDS);
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
return lock;
}
/**
* 解锁
*
* @param lockName 锁名称
*/
public void unlock(String lockName) {
try {
redissonClient.getLock(lockName).unlock();
} catch (Exception e) {
log.error("解锁异常,lockName=" + lockName, e);
}
}
}
@@ -0,0 +1,36 @@
package com.jero.boot.starter.lock.config;
import lombok.extern.slf4j.Slf4j;
import com.jero.boot.starter.lock.core.RedissonManager;
import com.jero.boot.starter.lock.prop.RedissonProperties;
import org.redisson.api.RedissonClient;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Redisson自动化配置
*
* @author zyf
* @date 2020-11-11
*/
@Slf4j
@Configuration
@ConditionalOnClass(RedissonProperties.class)
@EnableConfigurationProperties(RedissonProperties.class)
public class RedissonConfiguration {
@Bean
@ConditionalOnMissingBean(RedissonClient.class)
public RedissonClient redissonClient(RedissonProperties redissonProperties) {
RedissonManager redissonManager = new RedissonManager(redissonProperties);
log.info("RedissonManager初始化完成,当前连接方式:" + redissonProperties.getType() + ",连接地址:" + redissonProperties.getAddress());
return redissonManager.getRedisson();
}
}
@@ -0,0 +1,98 @@
package com.jero.boot.starter.lock.core;
import com.google.common.base.Preconditions;
import lombok.extern.slf4j.Slf4j;
import com.jero.boot.starter.lock.core.strategy.RedissonConfigStrategy;
import com.jero.boot.starter.lock.prop.RedissonProperties;
import com.jero.boot.starter.lock.core.strategy.impl.ClusterRedissonConfigStrategyImpl;
import com.jero.boot.starter.lock.core.strategy.impl.MasterslaveRedissonConfigStrategyImpl;
import com.jero.boot.starter.lock.core.strategy.impl.SentinelRedissonConfigStrategyImpl;
import com.jero.boot.starter.lock.core.strategy.impl.StandaloneRedissonConfigStrategyImpl;
import com.jero.boot.starter.lock.enums.RedisConnectionType;
import org.redisson.Redisson;
import org.redisson.config.Config;
/**
* Redisson配置管理器,用于初始化的redisson实例
*
* @author zyf
* @date 2020-11-12
*/
@Slf4j
public class RedissonManager {
private Config config = new Config();
private Redisson redisson = null;
public RedissonManager() {
}
public RedissonManager(RedissonProperties redissonProperties) {
//装配开关
Boolean enabled = redissonProperties.getEnabled();
if (enabled != null && enabled) {
try {
config = RedissonConfigFactory.getInstance().createConfig(redissonProperties);
redisson = (Redisson) Redisson.create(config);
} catch (Exception e) {
log.error("Redisson初始化错误", e);
}
}
}
public Redisson getRedisson() {
return redisson;
}
/**
* Redisson连接方式配置工厂
* 双重检查锁
*/
static class RedissonConfigFactory {
private RedissonConfigFactory() {
}
private static RedissonConfigFactory factory = null;
public static RedissonConfigFactory getInstance() {
synchronized (Object.class) {
if (factory == null) {
factory = new RedissonConfigFactory();
}
}
return factory;
}
/**
* 根据连接类型創建连接方式的配置
*
* @param redissonProperties
* @return Config
*/
Config createConfig(RedissonProperties redissonProperties) {
Preconditions.checkNotNull(redissonProperties);
Preconditions.checkNotNull(redissonProperties.getAddress(), "redis地址未配置");
RedisConnectionType connectionType = redissonProperties.getType();
// 声明连接方式
RedissonConfigStrategy redissonConfigStrategy;
if (connectionType.equals(RedisConnectionType.SENTINEL)) {
redissonConfigStrategy = new SentinelRedissonConfigStrategyImpl();
} else if (connectionType.equals(RedisConnectionType.CLUSTER)) {
redissonConfigStrategy = new ClusterRedissonConfigStrategyImpl();
} else if (connectionType.equals(RedisConnectionType.MASTERSLAVE)) {
redissonConfigStrategy = new MasterslaveRedissonConfigStrategyImpl();
} else {
redissonConfigStrategy = new StandaloneRedissonConfigStrategyImpl();
}
Preconditions.checkNotNull(redissonConfigStrategy, "连接方式创建异常");
return redissonConfigStrategy.createRedissonConfig(redissonProperties);
}
}
}
@@ -0,0 +1,21 @@
package com.jero.boot.starter.lock.core.strategy;
import com.jero.boot.starter.lock.prop.RedissonProperties;
import org.redisson.config.Config;
/**
* Redisson配置构建接口
*
* @author zyf
* @date 2020-11-11
*/
public interface RedissonConfigStrategy {
/**
* 根据不同的Redis配置策略创建对应的Config
*
* @param redissonProperties
* @return Config
*/
Config createRedissonConfig(RedissonProperties redissonProperties);
}
@@ -0,0 +1,43 @@
package com.jero.boot.starter.lock.core.strategy.impl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import com.jero.boot.starter.lock.core.strategy.RedissonConfigStrategy;
import com.jero.boot.starter.lock.prop.RedissonProperties;
import com.jero.boot.starter.lock.enums.GlobalConstant;
import org.redisson.config.Config;
/**
* 集群方式Redisson配置
* cluster方式至少6个节点(3主3从)
* 配置方式:127.0.0.1:6379,127.0.0.1:6380,127.0.0.1:6381,127.0.0.1:6382,127.0.0.1:6383,127.0.0.1:6384
*
* @author zyf
* @date 2020-11-11
*/
@Slf4j
public class ClusterRedissonConfigStrategyImpl implements RedissonConfigStrategy {
@Override
public Config createRedissonConfig(RedissonProperties redissonProperties) {
Config config = new Config();
try {
String address = redissonProperties.getAddress();
String password = redissonProperties.getPassword();
String[] addrTokens = address.split(",");
// 设置集群(cluster)节点的服务IP和端口
for (int i = 0; i < addrTokens.length; i++) {
config.useClusterServers().addNodeAddress(GlobalConstant.REDIS_CONNECTION_PREFIX + addrTokens[i]);
if (StringUtils.isNotBlank(password)) {
config.useClusterServers().setPassword(password);
}
}
log.info("初始化集群方式Config,连接地址:" + address);
} catch (Exception e) {
log.error("集群Redisson初始化错误", e);
e.printStackTrace();
}
return config;
}
}
@@ -0,0 +1,54 @@
package com.jero.boot.starter.lock.core.strategy.impl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import com.jero.boot.starter.lock.core.strategy.RedissonConfigStrategy;
import com.jero.boot.starter.lock.prop.RedissonProperties;
import com.jero.boot.starter.lock.enums.GlobalConstant;
import org.redisson.config.Config;
import java.util.ArrayList;
import java.util.List;
/**
* 主从方式Redisson配置
* <p>配置方式: 127.0.0.1:6379(主),127.0.0.1:6380(子),127.0.0.1:6381(子)</p>
*
* @author zyf
* @date 2020-11-11
*/
@Slf4j
public class MasterslaveRedissonConfigStrategyImpl implements RedissonConfigStrategy {
@Override
public Config createRedissonConfig(RedissonProperties redissonProperties) {
Config config = new Config();
try {
String address = redissonProperties.getAddress();
String password = redissonProperties.getPassword();
int database = redissonProperties.getDatabase();
String[] addrTokens = address.split(",");
String masterNodeAddr = addrTokens[0];
// 设置主节点ip
config.useMasterSlaveServers().setMasterAddress(masterNodeAddr);
if (StringUtils.isNotBlank(password)) {
config.useMasterSlaveServers().setPassword(password);
}
config.useMasterSlaveServers().setDatabase(database);
// 设置从节点,移除第一个节点,默认第一个为主节点
List<String> slaveList = new ArrayList<>();
for (String addrToken : addrTokens) {
slaveList.add(GlobalConstant.REDIS_CONNECTION_PREFIX + addrToken);
}
slaveList.remove(0);
config.useMasterSlaveServers().addSlaveAddress(slaveList.toArray(new String[0]));
log.info("初始化主从方式Config,redisAddress:" + address);
} catch (Exception e) {
log.error("主从Redisson初始化错误", e);
e.printStackTrace();
}
return config;
}
}
@@ -0,0 +1,47 @@
package com.jero.boot.starter.lock.core.strategy.impl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import com.jero.boot.starter.lock.core.strategy.RedissonConfigStrategy;
import com.jero.boot.starter.lock.prop.RedissonProperties;
import com.jero.boot.starter.lock.enums.GlobalConstant;
import org.redisson.config.Config;
/**
* 哨兵方式Redis连接配置
* 比如sentinel.conf里配置为sentinel monitor my-sentinel-name 127.0.0.1 6379 2,那么这里就配置my-sentinel-name
* 配置方式:my-sentinel-name,127.0.0.1:26379,127.0.0.1:26389,127.0.0.1:26399
* @author zyf
* @date 2020-11-11
*/
@Slf4j
public class SentinelRedissonConfigStrategyImpl implements RedissonConfigStrategy {
@Override
public Config createRedissonConfig(RedissonProperties redissonProperties) {
Config config = new Config();
try {
String address = redissonProperties.getAddress();
String password = redissonProperties.getPassword();
int database = redissonProperties.getDatabase();
String[] addrTokens = address.split(",");
String sentinelAliasName = addrTokens[0];
// 设置redis配置文件sentinel.conf配置的sentinel别名
config.useSentinelServers().setMasterName(sentinelAliasName);
config.useSentinelServers().setDatabase(database);
if (StringUtils.isNotBlank(password)) {
config.useSentinelServers().setPassword(password);
}
// 设置哨兵节点的服务IP和端口
for (int i = 1; i < addrTokens.length; i++) {
config.useSentinelServers().addSentinelAddress(GlobalConstant.REDIS_CONNECTION_PREFIX+ addrTokens[i]);
}
log.info("初始化哨兵方式Config,redisAddress:" + address);
} catch (Exception e) {
log.error("哨兵Redisson初始化错误", e);
e.printStackTrace();
}
return config;
}
}
@@ -0,0 +1,40 @@
package com.jero.boot.starter.lock.core.strategy.impl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import com.jero.boot.starter.lock.core.strategy.RedissonConfigStrategy;
import com.jero.boot.starter.lock.prop.RedissonProperties;
import com.jero.boot.starter.lock.enums.GlobalConstant;
import org.redisson.config.Config;
/**
* 单机方式Redisson配置
*
* @author zyf
* @date 2020-11-11
*/
@Slf4j
public class StandaloneRedissonConfigStrategyImpl implements RedissonConfigStrategy {
@Override
public Config createRedissonConfig(RedissonProperties redissonProperties) {
Config config = new Config();
try {
String address = redissonProperties.getAddress();
String password = redissonProperties.getPassword();
int database = redissonProperties.getDatabase();
String redisAddr = GlobalConstant.REDIS_CONNECTION_PREFIX + address;
config.useSingleServer().setAddress(redisAddr);
config.useSingleServer().setDatabase(database);
if (StringUtils.isNotBlank(password)) {
config.useSingleServer().setPassword(password);
}
log.info("初始化Redisson单机配置,连接地址:" + address);
} catch (Exception e) {
log.error("单机Redisson初始化错误", e);
e.printStackTrace();
}
return config;
}
}
@@ -0,0 +1,21 @@
package com.jero.boot.starter.lock.enums;
/**
* 全局常量枚举
*
* @author zyf
* @date 2020-11-11
*/
public class GlobalConstant {
private GlobalConstant(){
}
/**
* Redis地址连接前缀
*/
public static final String REDIS_CONNECTION_PREFIX = "redis://";
}
@@ -0,0 +1,22 @@
package com.jero.boot.starter.lock.enums;
/**
* 锁的模式
* @author jero
*/
public enum LockModel {
//可重入锁
REENTRANT,
//公平锁
FAIR,
//联锁(可以把一组锁当作一个锁来加锁和释放)
MULTIPLE,
//红锁
REDLOCK,
//读锁
READ,
//写锁
WRITE,
//自动模式,当参数只有一个.使用 REENTRANT 参数多个 REDLOCK
AUTO
}
@@ -0,0 +1,39 @@
package com.jero.boot.starter.lock.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* Redis连接方式
* @author zyf
* @date 2020-11-11
*/
@Getter
@AllArgsConstructor
public enum RedisConnectionType {
/**
* 单机部署方式(默认)
*/
STANDALONE("standalone", "单机部署方式"),
/**
* 哨兵部署方式
*/
SENTINEL("sentinel", "哨兵部署方式"),
/**
* 集群部署方式
*/
CLUSTER("cluster", "集群方式"),
/**
* 主从部署方式
*/
MASTERSLAVE("masterslave", "主从部署方式");
/**
* 编码
*/
private final String code;
/**
* 名称
*/
private final String name;
}
@@ -0,0 +1,39 @@
package com.jero.boot.starter.lock.prop;
import lombok.Data;
import com.jero.boot.starter.lock.enums.RedisConnectionType;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Redisson配置映射类
*
* @author zyf
* @date 2020-11-11
*/
@Data
@ConfigurationProperties(prefix = "jero.redisson")
public class RedissonProperties {
/**
* redis主机地址,ipport,多个用逗号(,)分隔
*/
private String address;
/**
* 连接类型
*/
private RedisConnectionType type;
/**
* 密码
*/
private String password;
/**
* 数据库(默认0)
*/
private int database;
/**
* 是否装配redisson配置
*/
private Boolean enabled = true;
}
@@ -0,0 +1,4 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.jero.boot.starter.lock.config.RedissonConfiguration
@@ -0,0 +1,59 @@
package org.jero.boot.starter.lock.test;
import com.jero.boot.starter.lock.annotation.JLock;
import com.jero.boot.starter.lock.annotation.JRepeat;
import com.jero.boot.starter.lock.annotation.LockConstant;
import com.jero.boot.starter.lock.client.RedissonLockClient;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service
public class LockService {
@Resource
private RedissonLockClient redissonLockClient;
int n = 10;
/**
* 模拟秒杀(注解方式)
*/
@JLock(lockKey = "#productId", expireSeconds = 5000)
public void seckill(String productId) {
if (n <= 0) {
System.out.println("活动已结束,请下次再来");
return;
}
System.out.println(Thread.currentThread().getName() + ":秒杀到了商品");
System.out.println(--n);
}
/**
* 模拟秒杀(编程方式)
*/
public void seckill2(String productId) {
redissonLockClient.tryLock(productId, 5000);
if (n <= 0) {
System.out.println("活动已结束,请下次再来");
return;
}
System.out.println(Thread.currentThread().getName() + ":秒杀到了商品");
System.out.println(--n);
redissonLockClient.unlock(productId);
}
/**
* 测试重复提交
*/
@JRepeat(lockKey = "#name", lockTime = 5)
public void reSubmit(String name) {
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("提交成功" + name);
}
}
@@ -0,0 +1,69 @@
package org.jero.boot.starter.lock.test;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;
import static junit.framework.TestCase.assertNull;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = LockTestApplication.class)
public class LockTest {
@Autowired
LockService lockService;
/**
* 测试分布式锁(模拟秒杀)
*/
@Test
public void test1() throws Exception {
ExecutorService executorService = Executors.newFixedThreadPool(6);
IntStream.range(0, 30).forEach(i -> executorService.submit(() -> {
try {
lockService.seckill("20120508784");
} catch (Exception e) {
e.printStackTrace();
}
}));
assertNull(executorService.awaitTermination(30, TimeUnit.SECONDS));
}
/**
* 测试分布式锁(模拟秒杀)
*/
@Test
public void test2() throws Exception {
ExecutorService executorService = Executors.newFixedThreadPool(6);
IntStream.range(0, 30).forEach(i -> executorService.submit(() -> {
try {
lockService.seckill2("20120508784");
} catch (Exception e) {
e.printStackTrace();
}
}));
assertNull(executorService.awaitTermination(30, TimeUnit.SECONDS));
}
/**
* 测试分布式锁(模拟重复提交)
*/
@Test
public void test3() throws Exception {
ExecutorService executorService = Executors.newFixedThreadPool(6);
IntStream.range(0, 20).forEach(i -> executorService.submit(() -> {
try {
lockService.reSubmit("test");
} catch (Exception e) {
e.printStackTrace();
}
}));
assertNull(executorService.awaitTermination(30, TimeUnit.SECONDS));
}
}
@@ -0,0 +1,15 @@
package org.jero.boot.starter.lock.test;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@SpringBootApplication(scanBasePackages = "com.jero")
@EnableAspectJAutoProxy
public class LockTestApplication {
public static void main(String[] args) {
SpringApplication.run(LockTestApplication.class, args);
}
}
@@ -0,0 +1,9 @@
package org.jero.boot.starter.lock.test;
import lombok.Data;
@Data
public class TestUser {
private String userId;
private String userName;
}
@@ -0,0 +1,19 @@
spring:
redis:
database: 0
host: 127.0.0.1
lettuce:
pool:
max-active: 8 #最大连接数据库连接数,设 0 为没有限制
max-idle: 8 #最大等待连接中的数量,设 0 为没有限制
max-wait: -1ms #最大建立连接等待时间。如果超过此时间将接到异常。设为-1表示无限制。
min-idle: 0 #最小等待连接中的数量,设 0 为没有限制
shutdown-timeout: 100ms
password: hzwlsoft123
port: 6379
jero :
redisson:
address: 127.0.0.1:6379
password: hzwlsoft123
type: STANDALONE
enabled: true
@@ -0,0 +1,21 @@
<?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">
<parent>
<artifactId>laws-starter</artifactId>
<groupId>com.jero.boot</groupId>
<version>2.5.1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>laws-starter-rabbitmq</artifactId>
<description>laws-starter-消息队列</description>
<dependencies>
<!-- 消息总线 rabbitmq -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,364 @@
package com.jero.boot.starter.rabbitmq.client;
import cn.hutool.core.util.ObjectUtil;
import lombok.extern.slf4j.Slf4j;
import com.jero.boot.starter.rabbitmq.event.EventObj;
import com.jero.boot.starter.rabbitmq.event.JeroRemoteApplicationEvent;
import com.jero.boot.starter.rabbitmq.exchange.DelayExchangeBuilder;
import com.jero.common.annotation.RabbitComponent;
import com.jero.common.base.BaseMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.bus.BusProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
/**
* 消息队列客户端
*/
@Slf4j
@Configuration
public class RabbitMqClient {
private final RabbitAdmin rabbitAdmin;
private final RabbitTemplate rabbitTemplate;
@Resource
private SimpleMessageListenerContainer messageListenerContainer;
@Resource
BusProperties busProperties;
@Resource
private ApplicationEventPublisher publisher;
@Resource
private ApplicationContext applicationContext;
@Bean
public void initQueue() {
Map<String, Object> beansWithRqbbitComponentMap = this.applicationContext.getBeansWithAnnotation(RabbitComponent.class);
Class<? extends Object> clazz = null;
for (Map.Entry<String, Object> entry : beansWithRqbbitComponentMap.entrySet()) {
log.info("初始化队列............");
//获取到实例对象的class信息
clazz = entry.getValue().getClass();
Method[] methods = clazz.getMethods();
RabbitListener rabbitListener = clazz.getAnnotation(RabbitListener.class);
if (ObjectUtil.isNotEmpty(rabbitListener)) {
createQueue(rabbitListener);
}
for (Method method : methods) {
RabbitListener methodRabbitListener = method.getAnnotation(RabbitListener.class);
if (ObjectUtil.isNotEmpty(methodRabbitListener)) {
createQueue(methodRabbitListener);
}
}
}
}
/**
* 初始化队列
*
* @param rabbitListener
*/
private void createQueue(RabbitListener rabbitListener) {
String[] queues = rabbitListener.queues();
DirectExchange directExchange = createExchange(DelayExchangeBuilder.DELAY_EXCHANGE);
//创建交换机
rabbitAdmin.declareExchange(directExchange);
if (ObjectUtil.isNotEmpty(queues)) {
for (String queueName : queues) {
Properties result = rabbitAdmin.getQueueProperties(queueName);
if (ObjectUtil.isEmpty(result)) {
Queue queue = new Queue(queueName);
addQueue(queue);
Binding binding = BindingBuilder.bind(queue).to(directExchange).with(queueName);
rabbitAdmin.declareBinding(binding);
log.info("创建队列:" + queueName);
}else{
log.info("已有队列:" + queueName);
}
}
}
}
private Map<String,Object> sentObj = new HashMap<>();
@Autowired
public RabbitMqClient(RabbitAdmin rabbitAdmin, RabbitTemplate rabbitTemplate) {
this.rabbitAdmin = rabbitAdmin;
this.rabbitTemplate = rabbitTemplate;
}
/**
* 发送远程事件
*
* @param handlerName
* @param baseMap
*/
public void publishEvent(String handlerName, BaseMap baseMap) {
EventObj eventObj = new EventObj();
eventObj.setHandlerName(handlerName);
eventObj.setBaseMap(baseMap);
publisher.publishEvent(new JeroRemoteApplicationEvent(eventObj, busProperties.getId()));
}
/**
* 转换Message对象
*
* @param messageType 返回消息类型 MessageProperties类中常量
* @param msg
* @return
*/
public Message getMessage(String messageType, Object msg) {
MessageProperties messageProperties = new MessageProperties();
messageProperties.setContentType(messageType);
return new Message(msg.toString().getBytes(), messageProperties);
}
/**
* 有绑定Key的Exchange发送
*
* @param routingKey
* @param msg
*/
public void sendMessageToExchange(TopicExchange topicExchange, String routingKey, Object msg) {
Message message = getMessage(MessageProperties.CONTENT_TYPE_JSON, msg);
rabbitTemplate.send(topicExchange.getName(), routingKey, message);
}
/**
* 没有绑定KEY的Exchange发送
*
* @param exchange
* @param msg
*/
public void sendMessageToExchange(TopicExchange topicExchange, AbstractExchange exchange, String msg) {
addExchange(exchange);
log.info("RabbitMQ send " + exchange.getName() + "->" + msg);
rabbitTemplate.convertAndSend(topicExchange.getName(), msg);
}
/**
* 发送消息
*
* @param queueName 队列名称
* @param params 消息内容map
*/
public void sendMessage(String queueName, Object params) {
log.info("发送消息到mq");
try {
rabbitTemplate.convertAndSend(DelayExchangeBuilder.DELAY_EXCHANGE, queueName, params, message -> message);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 发送消息
*
* @param queueName 队列名称
*/
public void sendMessage(String queueName) {
this.send(queueName, this.sentObj, 0);
this.sentObj.clear();
}
public RabbitMqClient put(String key, Object value) {
this.sentObj.put(key, value);
return this;
}
/**
* 延迟发送消息
*
* @param queueName 队列名称
* @param params 消息内容params
* @param expiration 延迟时间 单位毫秒
*/
public void sendMessage(String queueName, Object params, Integer expiration) {
this.send(queueName, params, expiration);
}
private void send(String queueName, Object params, Integer expiration) {
Queue queue = new Queue(queueName);
addQueue(queue);
CustomExchange customExchange = DelayExchangeBuilder.buildExchange();
rabbitAdmin.declareExchange(customExchange);
Binding binding = BindingBuilder.bind(queue).to(customExchange).with(queueName).noargs();
rabbitAdmin.declareBinding(binding);
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
log.debug("发送时间:" + sf.format(new Date()));
messageListenerContainer.setQueueNames(queueName);
/* messageListenerContainer.setMessageListener(new MqListener<Message>() {
@Override
public void onMessage(Message message, Channel channel) {
MqListener messageListener = SpringContextHolder.getHandler(queueName + "Listener", MqListener.class);
if (ObjectUtil.isNotEmpty(messageListener)) {
messageListener.onMessage(message, channel);
}
}
});*/
rabbitTemplate.convertAndSend(DelayExchangeBuilder.DEFAULT_DELAY_EXCHANGE, queueName, params, message -> {
if (expiration != null && expiration > 0) {
message.getMessageProperties().setHeader("x-delay", expiration);
}
return message;
});
}
/**
* 给queue发送消息
*
* @param queueName
*/
public String receiveFromQueue(String queueName) {
return receiveFromQueue(DirectExchange.DEFAULT, queueName);
}
/**
* 给direct交换机指定queue发送消息
*
* @param directExchange
* @param queueName
*/
public String receiveFromQueue(DirectExchange directExchange, String queueName) {
Queue queue = new Queue(queueName);
addQueue(queue);
Binding binding = BindingBuilder.bind(queue).to(directExchange).withQueueName();
rabbitAdmin.declareBinding(binding);
String messages = (String) rabbitTemplate.receiveAndConvert(queueName);
log.info("Receive:" + messages);
return messages;
}
/**
* 创建Exchange
*
* @param exchange
*/
public void addExchange(AbstractExchange exchange) {
rabbitAdmin.declareExchange(exchange);
}
/**
* 删除一个Exchange
*
* @param exchangeName
*/
public boolean deleteExchange(String exchangeName) {
return rabbitAdmin.deleteExchange(exchangeName);
}
/**
* 声明其名称自动命名的队列。它是用exclusive=true、autoDelete=true和 durable = false
*
* @return Queue
*/
public Queue addQueue() {
return rabbitAdmin.declareQueue();
}
/**
* 创建一个指定的Queue
*
* @param queue
* @return queueName
*/
public String addQueue(Queue queue) {
return rabbitAdmin.declareQueue(queue);
}
/**
* 删除一个队列
*
* @param queueName the name of the queue.
* @param unused true if the queue should be deleted only if not in use.
* @param empty true if the queue should be deleted only if empty.
*/
public void deleteQueue(String queueName, boolean unused, boolean empty) {
rabbitAdmin.deleteQueue(queueName, unused, empty);
}
/**
* 删除一个队列
*
* @param queueName
* @return true if the queue existed and was deleted.
*/
public boolean deleteQueue(String queueName) {
return rabbitAdmin.deleteQueue(queueName);
}
/**
* 绑定一个队列到一个匹配型交换器使用一个routingKey
*
* @param queue
* @param exchange
* @param routingKey
*/
public void addBinding(Queue queue, TopicExchange exchange, String routingKey) {
Binding binding = BindingBuilder.bind(queue).to(exchange).with(routingKey);
rabbitAdmin.declareBinding(binding);
}
/**
* 绑定一个Exchange到一个匹配型Exchange 使用一个routingKey
*
* @param exchange
* @param topicExchange
* @param routingKey
*/
public void addBinding(Exchange exchange, TopicExchange topicExchange, String routingKey) {
Binding binding = BindingBuilder.bind(exchange).to(topicExchange).with(routingKey);
rabbitAdmin.declareBinding(binding);
}
/**
* 去掉一个binding
*
* @param binding
*/
public void removeBinding(Binding binding) {
rabbitAdmin.removeBinding(binding);
}
/**
* 创建交换器
*
* @param exchangeName
* @return
*/
public DirectExchange createExchange(String exchangeName) {
return new DirectExchange(exchangeName, true, false);
}
}
@@ -0,0 +1,66 @@
package com.jero.boot.starter.rabbitmq.config;
import com.jero.boot.starter.rabbitmq.event.JeroRemoteApplicationEvent;
import com.jero.common.config.mqtoken.TransmitUserTokenFilter;
import org.springframework.amqp.core.AcknowledgeMode;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.cloud.bus.jackson.RemoteApplicationEventScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.UUID;
/**
* 消息队列配置类
*
* @author zyf
*/
@Configuration
@RemoteApplicationEventScan(basePackageClasses = JeroRemoteApplicationEvent.class)
public class RabbitMqConfig {
@Bean
public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory) {
RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory);
//设置忽略声明异常
rabbitAdmin.setIgnoreDeclarationExceptions(true);
return rabbitAdmin;
}
/**
* 注入获取token过滤器
* @return
*/
@Bean
public TransmitUserTokenFilter transmitUserInfoFromHttpHeader(){
return new TransmitUserTokenFilter();
}
@Bean
public SimpleMessageListenerContainer messageListenerContainer(ConnectionFactory connectionFactory) {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
//手动确认
container.setAcknowledgeMode(AcknowledgeMode.MANUAL);
//当前的消费者数量
container.setConcurrentConsumers(1);
//最大的消费者数量
container.setMaxConcurrentConsumers(1);
//是否重回队列
container.setDefaultRequeueRejected(true);
//消费端的标签策略
/*container.setConsumerTagStrategy(new ConsumerTagStrategy() {
@Override
public String createConsumerTag(String queue) {
return queue + "_" + UUID.randomUUID().toString();
}
});*/
container.setConsumerTagStrategy(queue-> queue + "_" + UUID.randomUUID());
return container;
}
}
@@ -0,0 +1,35 @@
package com.jero.boot.starter.rabbitmq.core;
import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import com.jero.boot.starter.rabbitmq.listenter.MqListener;
import com.jero.common.config.mqtoken.UserTokenContext;
import java.io.IOException;
@Slf4j
public class BaseRabbiMqHandler<T> {
private String token= UserTokenContext.getToken();
public void onMessage(T t, Long deliveryTag, Channel channel, MqListener<T> mqListener) {
try {
UserTokenContext.setToken(token);
mqListener.handler(t, channel);
channel.basicAck(deliveryTag, false);
} catch (Exception e) {
log.info("接收消息失败,重新放回队列");
try {
/**
* deliveryTag:该消息的index
* multiple:是否批量.true:将一次性拒绝所有小于deliveryTag的消息。
* requeue:被拒绝的是否重新入队列
*/
channel.basicNack(deliveryTag, false, true);
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
}
@@ -0,0 +1,36 @@
package com.jero.boot.starter.rabbitmq.core;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.support.converter.MessageConversionException;
import org.springframework.amqp.support.converter.MessageConverter;
import java.io.ByteArrayInputStream;
import java.io.ObjectInputStream;
public class MapMessageConverter implements MessageConverter {
@Override
public Message toMessage(Object object, MessageProperties messageProperties) throws MessageConversionException {
return new Message(object.toString().getBytes(), messageProperties);
}
@Override
public Object fromMessage(Message message) throws MessageConversionException {
String contentType = message.getMessageProperties().getContentType();
if (null != contentType && contentType.contains("text")) {
return new String(message.getBody());
} else {
ObjectInputStream objInt = null;
try {
ByteArrayInputStream byteInt = new ByteArrayInputStream(message.getBody());
objInt = new ObjectInputStream(byteInt);
//byte[]转map
return objInt.readObject();
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
}
}
@@ -0,0 +1,28 @@
package com.jero.boot.starter.rabbitmq.event;
import cn.hutool.core.util.ObjectUtil;
import com.jero.common.util.SpringContextHolder;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
/**
* 监听远程事件,并分发消息到业务模块消息处理器
*/
@Component
public class BaseApplicationEvent implements ApplicationListener<JeroRemoteApplicationEvent> {
@Override
public void onApplicationEvent(JeroRemoteApplicationEvent jeroRemoteApplicationEvent) {
EventObj eventObj = jeroRemoteApplicationEvent.getEventObj();
if (ObjectUtil.isNotEmpty(eventObj)) {
//获取业务模块消息处理器
JeroBusEventHandler busEventHandler = SpringContextHolder.getHandler(eventObj.getHandlerName(), JeroBusEventHandler.class);
if (ObjectUtil.isNotEmpty(busEventHandler)) {
//通知业务模块
busEventHandler.onMessage(eventObj);
}
}
}
}
@@ -0,0 +1,21 @@
package com.jero.boot.starter.rabbitmq.event;
import lombok.Data;
import com.jero.common.base.BaseMap;
import java.io.Serializable;
/**
* 远程事件数据对象
*/
@Data
public class EventObj implements Serializable {
/**
* 数据对象
*/
private BaseMap baseMap;
/**
* 自定义业务模块消息处理器beanName
*/
private String handlerName;
}
@@ -0,0 +1,8 @@
package com.jero.boot.starter.rabbitmq.event;
/**
* 业务模块消息处理器接口
*/
public interface JeroBusEventHandler {
void onMessage(EventObj map);
}
@@ -0,0 +1,29 @@
package com.jero.boot.starter.rabbitmq.event;
import lombok.Data;
import org.springframework.cloud.bus.event.RemoteApplicationEvent;
/**
* 自定义网关刷新远程事件
*
* @author : zyf
* @date :2020-11-10
*/
@Data
public class JeroRemoteApplicationEvent extends RemoteApplicationEvent {
private JeroRemoteApplicationEvent() {
}
private EventObj eventObj;
public JeroRemoteApplicationEvent(EventObj source, String originService, String destinationService) {
super(source, originService, destinationService);
this.eventObj = source;
}
public JeroRemoteApplicationEvent(EventObj source, String originService) {
super(source, originService, null);
this.eventObj = source;
}
}
@@ -0,0 +1,36 @@
package com.jero.boot.starter.rabbitmq.exchange;
import org.springframework.amqp.core.CustomExchange;
import java.util.HashMap;
import java.util.Map;
/**
* 延迟交换器构造器
* @author: zyf
* @date: 2019/3/8 13:31
* @description:
*/
public class DelayExchangeBuilder {
/**
* 默认延迟消息交换器
*/
public static final String DEFAULT_DELAY_EXCHANGE = "jero.delayed.exchange";
/**
* 普通交换器
*/
public static final String DELAY_EXCHANGE = "jero.direct.exchange";
private DelayExchangeBuilder() {
}
/**
* 构建延迟消息交换器
* @return
*/
public static CustomExchange buildExchange() {
Map<String, Object> args = new HashMap<>();
args.put("x-delayed-type", "direct");
return new CustomExchange(DEFAULT_DELAY_EXCHANGE, "x-delayed-message", true, false, args);
}
}
@@ -0,0 +1,10 @@
package com.jero.boot.starter.rabbitmq.listenter;
import com.rabbitmq.client.Channel;
public interface MqListener<T> {
default void handler(T map, Channel channel) {
}
}
@@ -0,0 +1,41 @@
<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">
<parent>
<groupId>com.jero.boot</groupId>
<artifactId>byd-structuredplugins</artifactId>
<version>2.5.1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>laws-starter</artifactId>
<packaging>pom</packaging>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<modules>
<module>laws-starter-cloud</module>
<module>laws-starter-job</module>
<module>laws-starter-lock</module>
<module>laws-starter-rabbitmq</module>
</modules>
<dependencies>
<!--jero-tools-->
<dependency>
<groupId>com.jero.boot</groupId>
<artifactId>laws-base-tools</artifactId>
</dependency>
<!--加载配置信息-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,92 @@
version: '2'
services:
jero-boot-mysql:
build:
context: ../db
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_ROOT_HOST: '%'
TZ: Asia/Shanghai
restart: always
container_name: jero-boot-mysql
command:
--character-set-server=utf8mb4
--collation-server=utf8mb4_general_ci
--explicit_defaults_for_timestamp=true
--lower_case_table_names=1
--max_allowed_packet=128M
--default-authentication-plugin=caching_sha2_password
ports:
- 3306:3306
jero-boot-redis:
image: redis:5.0
ports:
- 6379:6379
restart: always
container_name: jero-boot-redis
hostname: jero-boot-redis
jero-boot-nacos:
restart: always
image: nacos/nacos-server:1.4.0
container_name: jero-boot-nacos
hostname: jero-boot-nacos
ports:
- 8848:8848
depends_on:
- jero-boot-mysql
# volumes:
# - ./init/docker-startup.sh:/home/nacos/bin/docker-startup.sh
# - ./init/application.properties:/home/nacos/conf/application.properties
environment:
MODE: standalone
PREFER_HOST_MODE: hostname
SPRING_DATASOURCE_PLATFORM: mysql
MYSQL_SERVICE_HOST: jero-boot-mysql
MYSQL_SERVICE_PORT: 3306
MYSQL_SERVICE_USER: root
MYSQL_SERVICE_PASSWORD: root
MYSQL_SERVICE_DB_NAME: nacos
jero-boot-gateway:
build:
context: ./jero-cloud-gateway
ports:
- 9999:9999
depends_on:
- jero-boot-nacos
- jero-boot-redis
container_name: jero-boot-gateway
hostname: jero-boot-gateway
jero-boot-system:
depends_on:
- jero-boot-mysql
- jero-boot-redis
- jero-boot-nacos
build:
context: ./jero-cloud-system-start
container_name: jero-boot-system
hostname: jero-boot-system
restart: on-failure
environment:
- TZ=Asia/Shanghai
jero-boot-xxljob:
build:
context: ./jero-cloud-xxljob
ports:
- 9080:9080
container_name: jero-boot-xxljob
hostname: jero-boot-xxljob
# jero-boot-rabbitmq:
# # image: rabbitmq:3-management
# image: rabbitmq:3
# ports:
# - 5672:5672
# # - 15672:15672
# restart: always
# container_name: jero-boot-rabbitmq
# hostname: jero-boot-rabbitmq
@@ -0,0 +1,15 @@
FROM anapsix/alpine-java:8_server-jre_unlimited
MAINTAINER test@163.com
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
RUN mkdir -p /jero-cloud-gateway
WORKDIR /jero-cloud-gateway
EXPOSE 9999
ADD ./target/jero-cloud-gateway-2.5.1.jar ./
CMD java -Dfile.encoding=utf-8 -Djava.security.egd=file:/dev/./urandom -jar jero-cloud-gateway-2.5.1.jar
@@ -0,0 +1,105 @@
<?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">
<parent>
<artifactId>jero-cloud-module</artifactId>
<groupId>com.jero.boot</groupId>
<version>2.5.1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jero-cloud-gateway</artifactId>
<dependencies>
<!-- jero 微服务基础依赖-->
<dependency>
<groupId>com.jero.boot</groupId>
<artifactId>laws-starter-cloud</artifactId>
<exclusions>
<exclusion>
<groupId>com.jero.boot</groupId>
<artifactId>jero-system-cloud-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- spring-cloud网关-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--sentinel断路器依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-web-servlet</artifactId>
</dependency>
<!--Spring Webflux-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<!-- 熔断、降级 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--健康监控-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- 限流Redis实现 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
<!--springboot2.X默认使用lettuce连接池,需要引入commons-pool2-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<!--server-api-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
</dependency>
<!-- Swagger API文档 -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-ui</artifactId>
<version>${knife4j-spring-ui.version}</version>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>${knife4j-spring-boot-starter.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
</project>
@@ -0,0 +1,34 @@
package com.jero;
import com.jero.loader.DynamicRouteLoader;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import javax.annotation.Resource;
@EnableFeignClients
@EnableDiscoveryClient
@SpringBootApplication
public class JeroGatewayApplication implements CommandLineRunner {
@Resource
private DynamicRouteLoader dynamicRouteLoader;
public static void main(String[] args) {
SpringApplication.run(JeroGatewayApplication.class, args);
}
/**
* 容器初始化后加载路由
* @param strings
*/
@Override
public void run(String... strings) {
dynamicRouteLoader.refresh();
}
}
@@ -0,0 +1,110 @@
package com.jero.config;
import lombok.extern.slf4j.Slf4j;
import com.jero.handler.HystrixFallbackHandler;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.server.RequestPredicates;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerResponse;
import javax.annotation.Resource;
import static org.springframework.web.reactive.function.server.RequestPredicates.GET;
import static org.springframework.web.reactive.function.server.RouterFunctions.route;
import static org.springframework.web.reactive.function.server.ServerResponse.ok;
/**
* @author scott
* @date 2020/05/26
* 路由配置信息
*/
@Slf4j
@Configuration
public class GatewayRoutersConfiguration {
public static final long DEFAULT_TIMEOUT = 30000;
public static String SERVER_ADDR;
public static String NAMESPACE;
public static String DATA_ID;
public static String ROUTE_GROUP;
/**
* 路由配置文件数据获取方式yml,nacos,database
*/
public static String DATA_TYPE;
@Value("${spring.cloud.nacos.discovery.server-addr}")
public void setServerAddr(String serverAddr) {
synchronized (GatewayRoutersConfiguration.class){
if(SERVER_ADDR == null){
SERVER_ADDR = serverAddr;
}
}
}
@Value("${spring.cloud.nacos.discovery.namespace}")
public void setNamespace(String namespace) {
synchronized (GatewayRoutersConfiguration.class){
if(NAMESPACE == null){
NAMESPACE = namespace;
}
}
}
@Value("${jero.route.config.data-id:#{null}}")
public void setRouteDataId(String dataId) {
synchronized (GatewayRoutersConfiguration.class){
if(DATA_ID == null){
DATA_ID = dataId + ".json";
}
}
}
@Value("${jero.route.config.group:DEFAULT_GROUP:#{null}}")
public void setRouteGroup(String routeGroup) {
synchronized (GatewayRoutersConfiguration.class){
if(ROUTE_GROUP == null){
ROUTE_GROUP = routeGroup;
}
}
}
@Value("${jero.route.config.data-type}")
public void setDataType(String dataType) {
synchronized (GatewayRoutersConfiguration.class){
if(DATA_TYPE == null){
DATA_TYPE = dataType;
}
}
}
/**
* 路由断言
* @return
*/
@Bean
public RouterFunction<ServerResponse> routerFunction() {
return RouterFunctions.route(
RequestPredicates.path("/globalFallback").and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), hystrixFallbackHandler);
}
@Bean
public RouterFunction<ServerResponse> indexRouter(@Value("classpath:/META-INF/resources/doc.html") final org.springframework.core.io.Resource indexHtml) {
return route(GET("/"), request -> ok().contentType(MediaType.TEXT_HTML).syncBody(indexHtml));
}
@Resource
private HystrixFallbackHandler hystrixFallbackHandler;
}
@@ -0,0 +1,43 @@
package com.jero.config;
import com.jero.filter.GlobalAccessTokenFilter;
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import reactor.core.publisher.Mono;
/**
* @author scott
* @date 2020/5/26
* 路由限流配置
*/
@Configuration
public class RateLimiterConfiguration {
/**
* IP限流 (通过exchange对象可以获取到请求信息这边用了HostName)
*/
@Bean
@Primary
public KeyResolver ipKeyResolver() {
return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getAddress().getHostAddress());
}
/**
* 用户限流 (通过exchange对象可以获取到请求信息获取当前请求的用户 TOKEN)
*/
@Bean
public KeyResolver userKeyResolver() {
//使用这种方式限流请求Header中必须携带X-Access-Token参数
return exchange -> Mono.just(exchange.getRequest().getHeaders().getFirst(GlobalAccessTokenFilter.X_ACCESS_TOKEN));
}
/**
* 接口限流 (获取请求地址的uri作为限流key)
*/
@Bean
public KeyResolver apiKeyResolver() {
return exchange -> Mono.just(exchange.getRequest().getPath().value());
}
}
@@ -0,0 +1,22 @@
package com.jero.config;
/**
* nocos配置方式枚举
*/
public class RouterDataType {
private RouterDataType(){
}
/**
* 数据库加载路由配置
*/
public static final String DATABASE = "database";
/**
* 本地yml加载路由配置
*/
public static final String YML = "yml";
/**
* nacos加载路由配置
*/
public static final String NACOS = "nacos";
}
@@ -0,0 +1,32 @@
package com.jero.fallback;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
/**
* 响应超时熔断处理器
*
* @author zyf
*/
@RestController
public class FallbackController {
/**
* 全局熔断处理
* @return
*/
@RequestMapping("/fallback")
public Mono<String> fallback() {
return Mono.just("访问超时,请稍后再试!");
}
/**
* demo熔断处理
* @return
*/
@RequestMapping("/demo/fallback")
public Mono<String> fallback2() {
return Mono.just("访问超时,请稍后再试!");
}
}
@@ -0,0 +1,54 @@
package com.jero.filter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.io.File;
import java.util.Arrays;
import java.util.stream.Collectors;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.addOriginalRequestUrl;
/**
*
*/
@Slf4j
@Component
public class GlobalAccessTokenFilter implements GlobalFilter, Ordered {
public static final String X_ACCESS_TOKEN = "X-Access-Token";
public static final String X_GATEWAY_BASE_PATH = "X_GATEWAY_BASE_PATH";
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String scheme = exchange.getRequest().getURI().getScheme();
String host = exchange.getRequest().getURI().getHost();
int port = exchange.getRequest().getURI().getPort();
String basePath = scheme + "://" + host + ":" + port;
// 1. 重写StripPrefix(获取真实的URL)
addOriginalRequestUrl(exchange, exchange.getRequest().getURI());
String rawPath = exchange.getRequest().getURI().getRawPath();
String newPath = File.separator + Arrays.stream(StringUtils.tokenizeToStringArray(rawPath, File.separator)).skip(1L).collect(Collectors.joining("/"));
ServerHttpRequest newRequest = exchange.getRequest().mutate().path(newPath).build();
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, newRequest.getURI());
//将现在的request添加当前身份
ServerHttpRequest mutableReq = exchange.getRequest().mutate().header("Authorization-UserName", "").header(X_GATEWAY_BASE_PATH,basePath).build();
ServerWebExchange mutableExchange = exchange.mutate().request(mutableReq).build();
return chain.filter(mutableExchange);
}
@Override
public int getOrder() {
return 0;
}
}
@@ -0,0 +1,26 @@
package com.jero.filter;
import com.alibaba.csp.sentinel.adapter.servlet.CommonFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.servlet.Filter;
/**
* @author Administrator
*/
@Configuration
public class SentinelFilterContextConfig {
@Bean
public FilterRegistrationBean<Filter> sentinelFilterRegistration() {
FilterRegistrationBean<Filter> registration = new FilterRegistrationBean<>();
registration.setFilter(new CommonFilter());
registration.addUrlPatterns("/*");
// 入口资源关闭聚合
registration.addInitParameter(CommonFilter.WEB_CONTEXT_UNIFY, "false");
registration.setName("sentinelFilter");
registration.setOrder(1);
return registration;
}
}
@@ -0,0 +1,34 @@
package com.jero.handler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.HandlerFunction;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;
import java.util.Optional;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR;
/**
* @author scott
* @date 2020/05/26
* Hystrix 降级处理
*/
@Slf4j
@Component
public class HystrixFallbackHandler implements HandlerFunction<ServerResponse> {
@Override
public Mono<ServerResponse> handle(ServerRequest serverRequest) {
Optional<Object> originalUris = serverRequest.attribute(GATEWAY_ORIGINAL_REQUEST_URL_ATTR);
originalUris.ifPresent(originalUri -> log.error("网关执行请求:{}失败,hystrix服务降级处理", originalUri));
return ServerResponse.status(HttpStatus.INTERNAL_SERVER_ERROR.value())
.header("Content-Type","text/plain; charset=utf-8").body(BodyInserters.fromObject("访问超时,请稍后再试"));
}
}
@@ -0,0 +1,27 @@
package com.jero.handler;
import com.jero.common.base.BaseMap;
import com.jero.common.modules.redis.listener.JeroRedisListerer;
import lombok.extern.slf4j.Slf4j;
import com.jero.loader.DynamicRouteLoader;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* 路由刷新监听
*/
@Slf4j
@Component
public class LoderRouderHandler implements JeroRedisListerer {
@Resource
private DynamicRouteLoader dynamicRouteLoader;
@Override
public void onMessage(BaseMap message) {
dynamicRouteLoader.refresh();
}
}
@@ -0,0 +1,67 @@
package com.jero.handler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import springfox.documentation.swagger.web.SwaggerResource;
import springfox.documentation.swagger.web.SwaggerResourcesProvider;
import java.util.*;
/**
* 聚合各个服务的swagger接口
*/
@Component
@Primary
public class MySwaggerResourceProvider implements SwaggerResourcesProvider {
/**
* swagger2默认的url后缀
*/
private static final String SWAGGER2URL = "/v2/api-docs";
/**
* 网关路由
*/
private final RouteLocator routeLocator;
/**
* 网关应用名称
*/
@Value("${spring.application.name}")
private String self;
@Autowired
public MySwaggerResourceProvider(RouteLocator routeLocator) {
this.routeLocator = routeLocator;
}
@Override
public List<SwaggerResource> get() {
List<SwaggerResource> resources = new ArrayList<>();
List<String> routeHosts = new ArrayList<>();
// 获取所有可用的hostserviceId
routeLocator.getRoutes().filter(route -> route.getUri().getHost() != null)
.filter(route -> !self.equals(route.getUri().getHost()))
.subscribe(route -> routeHosts.add(route.getUri().getHost()));
// 记录已经添加过的server存在同一个应用注册了多个服务在nacos上
Set<String> dealed = new HashSet<>();
routeHosts.forEach(instance -> {
// 拼接url
String url = "/" + instance.toLowerCase() + SWAGGER2URL;
if (!dealed.contains(url)) {
dealed.add(url);
SwaggerResource swaggerResource = new SwaggerResource();
swaggerResource.setUrl(url);
swaggerResource.setName(instance);
//Swagger排除监控
if(instance.indexOf("jero-cloud-monitor")==-1){
resources.add(swaggerResource);
}
}
});
return resources;
}
}
@@ -0,0 +1,39 @@
package com.jero.handler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import springfox.documentation.swagger.web.*;
import java.util.List;
/**
* swagger聚合接口三个接口都是 doc.html需要访问的接口
*/
@RestController
@RequestMapping("/swagger-resources")
public class SwaggerResourceController {
private MySwaggerResourceProvider swaggerResourceProvider;
@Autowired
public SwaggerResourceController(MySwaggerResourceProvider swaggerResourceProvider) {
this.swaggerResourceProvider = swaggerResourceProvider;
}
@RequestMapping(value = "/configuration/security")
public ResponseEntity<SecurityConfiguration> securityConfiguration() {
return new ResponseEntity<>(SecurityConfigurationBuilder.builder().build(), HttpStatus.OK);
}
@RequestMapping(value = "/configuration/ui")
public ResponseEntity<UiConfiguration> uiConfiguration() {
return new ResponseEntity<>(UiConfigurationBuilder.builder().build(), HttpStatus.OK);
}
@RequestMapping
public ResponseEntity<List<SwaggerResource>> swaggerResources() {
return new ResponseEntity<>(swaggerResourceProvider.get(), HttpStatus.OK);
}
}
@@ -0,0 +1,290 @@
package com.jero.loader;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import com.google.common.collect.Lists;
import com.jero.common.constant.CacheConstant;
import com.jero.common.util.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import com.jero.config.GatewayRoutersConfiguration;
import com.jero.config.RouterDataType;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.filter.FilterDefinition;
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
import org.springframework.cloud.gateway.route.InMemoryRouteDefinitionRepository;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;
import javax.annotation.PostConstruct;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.Executor;
/**
* 动态路由加载器
*
* @author : zyf
* @date :2020-11-10
*/
@Slf4j
@Component
@DependsOn({"gatewayRoutersConfiguration"})
public class DynamicRouteLoader implements ApplicationEventPublisherAware {
private ApplicationEventPublisher publisher;
private InMemoryRouteDefinitionRepository repository;
private DynamicRouteService dynamicRouteService;
private ConfigService configService;
private RedisUtil redisUtil;
private static String updateRouteText = "update route : {}";
public DynamicRouteLoader(InMemoryRouteDefinitionRepository repository, DynamicRouteService dynamicRouteService, RedisUtil redisUtil) {
if(this.repository == null){
this.repository = repository;
}
this.dynamicRouteService = dynamicRouteService;
this.redisUtil = redisUtil;
}
@PostConstruct
public void init() {
String dataType = GatewayRoutersConfiguration.DATA_TYPE;
log.info("初始化路由,dataType"+ dataType);
if (RouterDataType.NACOS.endsWith(dataType)) {
loadRoutesByNacos();
}
//从数据库加载路由
if (RouterDataType.DATABASE.endsWith(dataType)) {
loadRoutesByRedis();
}
}
/**
* 刷新路由
*
* @return
*/
public Mono<Void> refresh() {
String dataType = GatewayRoutersConfiguration.DATA_TYPE;
if (!RouterDataType.YML.endsWith(dataType)) {
this.init();
}
return Mono.empty();
}
/**
* 从nacos中读取路由配置
*
* @return
*/
private void loadRoutesByNacos() {
List<RouteDefinition> routes = Lists.newArrayList();
configService = createConfigService();
try {
String configInfo = "";
if (configService == null) {
log.warn("initConfigService fail");
}else {
configInfo = configService.getConfig(GatewayRoutersConfiguration.DATA_ID, GatewayRoutersConfiguration.ROUTE_GROUP, GatewayRoutersConfiguration.DEFAULT_TIMEOUT);
}
if (StringUtils.isNotBlank(configInfo)) {
log.info("获取网关当前配置:\r\n{}", configInfo);
routes = JSON.parseArray(configInfo, RouteDefinition.class);
}
} catch (NacosException e) {
log.error("初始化网关路由时发生错误", e);
e.printStackTrace();
}
for (RouteDefinition definition : routes) {
log.info(updateRouteText, definition.toString());
dynamicRouteService.add(definition);
}
this.publisher.publishEvent(new RefreshRoutesEvent(this));
dynamicRouteByNacosListener(GatewayRoutersConfiguration.DATA_ID, GatewayRoutersConfiguration.ROUTE_GROUP);
}
/**
* 从redis中读取路由配置
*
* @return
*/
private void loadRoutesByRedis() {
List<MyRouteDefinition> routes = Lists.newArrayList();
configService = createConfigService();
if (configService == null) {
log.warn("initConfigService fail");
}
Object configInfo = redisUtil.get(CacheConstant.GATEWAY_ROUTES);
if (ObjectUtil.isNotEmpty(configInfo)) {
log.info("获取网关当前配置:\r\n{}", configInfo);
JSONArray array = JSON.parseArray(configInfo.toString());
try {
routes = getRoutesByJson(array);
} catch (URISyntaxException e) {
e.printStackTrace();
}
}
for (MyRouteDefinition definition : routes) {
log.info(updateRouteText, definition.toString());
Integer status=definition.getStatus();
if(status.equals(0)){
dynamicRouteService.delete(definition.getId());
}else{
dynamicRouteService.add(definition);
}
}
this.publisher.publishEvent(new RefreshRoutesEvent(this));
}
/**
* redis中的信息需要处理下 转成RouteDefinition对象
* - id: login
* uri: lb://cloud-jero-system
* predicates:
* - Path=/jero-boot/sys/**,
*
* @param array
* @return
*/
public static List<MyRouteDefinition> getRoutesByJson(JSONArray array) throws URISyntaxException {
List<MyRouteDefinition> ls = new ArrayList<>();
for (int i = 0; i < array.size(); i++) {
JSONObject obj = array.getJSONObject(i);
MyRouteDefinition route = new MyRouteDefinition();
route.setId(obj.getString("routerId"));
route.setStatus(obj.getInteger("status"));
Object uri = obj.get("uri");
if (uri == null) {
route.setUri(new URI("lb://" + obj.getString("name")));
} else {
route.setUri(new URI(obj.getString("uri")));
}
Object predicates = obj.get("predicates");
if (predicates != null) {
JSONArray list = JSON.parseArray(predicates.toString());
List<PredicateDefinition> predicateDefinitionList = getPredicateDefinitions(list);
route.setPredicates(predicateDefinitionList);
}
Object filters = obj.get("filters");
if (filters != null) {
JSONArray list = JSON.parseArray(filters.toString());
List<FilterDefinition> filterDefinitionList = new ArrayList<>();
if (ObjectUtil.isNotEmpty(list)) {
addFilterDefinition(list, filterDefinitionList);
route.setFilters(filterDefinitionList);
}
}
ls.add(route);
}
return ls;
}
private static void addFilterDefinition(JSONArray list, List<FilterDefinition> filterDefinitionList) {
for (Object map : list) {
JSONObject json = (JSONObject) map;
JSONArray jsonArray = json.getJSONArray("args");
String name = json.getString("name");
FilterDefinition filterDefinition = new FilterDefinition();
for (Object o : jsonArray) {
JSONObject params = (JSONObject) o;
filterDefinition.addArg(params.getString("key"), params.get("value").toString());
}
filterDefinition.setName(name);
filterDefinitionList.add(filterDefinition);
}
}
private static List<PredicateDefinition> getPredicateDefinitions(JSONArray list) {
List<PredicateDefinition> predicateDefinitionList = new ArrayList<>();
for (Object map : list) {
JSONObject json = (JSONObject) map;
PredicateDefinition predicateDefinition = new PredicateDefinition();
predicateDefinition.setName(json.getString("name"));
JSONArray jsonArray = json.getJSONArray("args");
for (int j = 0; j < jsonArray.size(); j++) {
predicateDefinition.addArg("_genkey" + j, jsonArray.get(j).toString());
}
predicateDefinitionList.add(predicateDefinition);
}
return predicateDefinitionList;
}
/**
* 监听Nacos下发的动态路由配置
*
* @param dataId
* @param group
*/
public void dynamicRouteByNacosListener(String dataId, String group) {
try {
configService.addListener(dataId, group, new Listener() {
@Override
public void receiveConfigInfo(String configInfo) {
log.info("进行网关更新:\n\r{}", configInfo);
List<RouteDefinition> definitionList = JSON.parseArray(configInfo, RouteDefinition.class);
for (RouteDefinition definition : definitionList) {
log.info(updateRouteText, definition.toString());
dynamicRouteService.update(definition);
}
}
@Override
public Executor getExecutor() {
log.info("getExecutor\n\r");
return null;
}
});
} catch (Exception e) {
log.error("从nacos接收动态路由配置出错!!!", e);
}
}
/**
* 创建ConfigService
*
* @return
*/
private ConfigService createConfigService() {
try {
Properties properties = new Properties();
properties.setProperty("serverAddr", GatewayRoutersConfiguration.SERVER_ADDR);
properties.setProperty("namespace", GatewayRoutersConfiguration.NAMESPACE);
configService = NacosFactory.createConfigService(properties);
return configService;
} catch (Exception e) {
log.error("创建ConfigService异常", e);
return null;
}
}
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.publisher = applicationEventPublisher;
}
}
@@ -0,0 +1,96 @@
package com.jero.loader;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.route.InMemoryRouteDefinitionRepository;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.cloud.gateway.support.NotFoundException;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;
/**
* 动态更新路由网关service
* 1实现一个Spring提供的事件推送接口ApplicationEventPublisherAware
* 2提供动态路由的基础方法可通过获取bean操作该类的方法该类提供新增路由更新路由删除路由然后实现发布的功能
*
* @author zyf
*/
@Slf4j
@Service
public class DynamicRouteService implements ApplicationEventPublisherAware {
@Autowired
private RouteDefinitionWriter routeDefinitionWriter;
@Autowired
private InMemoryRouteDefinitionRepository repository;
/**
* 发布事件
*/
private ApplicationEventPublisher publisher;
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.publisher = applicationEventPublisher;
}
/**
* 删除路由
*
* @param id
* @return
*/
public synchronized void delete(String id) {
try {
repository.delete(Mono.just(id)).subscribe();
this.publisher.publishEvent(new RefreshRoutesEvent(this));
}catch (Exception e){
e.printStackTrace();
}
}
/**
* 更新路由
*
* @param definition
* @return
*/
public synchronized String update(RouteDefinition definition) {
try {
log.info("gateway update route {}", definition);
delete(definition.getId());
} catch (Exception e) {
return "update fail,not find route routeId: " + definition.getId();
}
try {
repository.save(Mono.just(definition)).subscribe();
this.publisher.publishEvent(new RefreshRoutesEvent(this));
return "success";
} catch (Exception e) {
return "update route fail";
}
}
/**
* 增加路由
*
* @param definition
* @return
*/
public synchronized String add(RouteDefinition definition) {
log.info("gateway add route {}", definition);
try {
repository.save(Mono.just(definition)).subscribe();
} catch (Exception e) {
log.error(e.toString());
}
return "success";
}
}
@@ -0,0 +1,16 @@
package com.jero.loader;
import lombok.Data;
@Data
public class GatewayRouteVo {
private String id;
private String name;
private String uri;
private String predicates;
private String filters;
private Integer stripPrefix;
private Integer retryable;
private Integer persist;
private Integer status;
}
@@ -0,0 +1,38 @@
package com.jero.loader;
import org.springframework.cloud.gateway.route.RouteDefinition;
import java.util.Objects;
/**
* 自定义RouteDefinition
* @author zyf
*/
public class MyRouteDefinition extends RouteDefinition {
/**
* 路由状态
*/
private Integer status;
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (!super.equals(o)) return false;
MyRouteDefinition that = (MyRouteDefinition) o;
return Objects.equals(status, that.status);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), status);
}
}
@@ -0,0 +1,61 @@
server:
port: 8080
spring:
application:
name: jero-gateway
cloud:
gateway:
discovery:
locator:
enabled: true
globalcors:
cors-configurations:
'[/**]':
allowCredentials: true
allowedOrigins: "*"
allowedMethods: "*"
allowedHeaders: "*"
#如果启用nacos或者数据库配置请删除一下配置
routes:
- id: jero-demo
uri: lb://jero-demo
predicates:
- Path=/mock/**,/test/**,/bigscreen/template1/**,/bigscreen/template2/**
- id: jero-system
uri: lb://jero-system
predicates:
- Path=/sys/**,/eoa/**,/v1/**,/joa/**,/online/**,/bigscreen/**,/jmreport/**,/desform/**,/act/**,/plug-in/**,/generic/**
- id: jero-system-websocket
uri: lb:ws://jero-system
predicates:
- Path=/websocket/**,/eoaSocket/**,/newsWebsocket/**
- id: jero-demo-websocket
uri: lb:ws://jero-demo
predicates:
- Path=/vxeSocket/**
# 全局熔断降级配置
default-filters:
- name: Hystrix
args:
name: default
#转发地址
fallbackUri: 'forward:/fallback'
- name: Retry
args:
#重试次数,默认值是 3 次
retries: 3
#HTTP 的状态返回码
statuses: BAD_GATEWAY,BAD_REQUEST
#指定哪些方法的请求需要进行重试逻辑,默认值是 GET 方法
methods: GET,POST
# hystrix 信号量隔离,10秒后自动超时
hystrix:
enabled: true
shareSecurityContext: true
command:
default:
execution:
isolation:
strategy: SEMAPHORE
thread:
timeoutInMilliseconds: 10000
@@ -0,0 +1,47 @@
# 以下@符号包裹的配置,均在父POM中进行定义.
# 在微服务环境下,该配置文件会先于application.yml加载
# 对应到nacos配置的data-id是 application.name(应用名称) + profiles.active(开发环境是dev)
spring:
profiles:
# 当前激活环境
active: @profile.name@
cloud:
#配置Bus id(远程推送事件)
bus:
id: ${spring.application.name}:${server.port}
nacos:
config:
# Nacos 认证用户
username: nacos
# Nacos 认证密码
password: nacos
# 命名空间 常用场景之一是不同环境的配置的区分隔离,例如开发测试环境和生产环境的资源(如配置、服务)隔离等
namespace: @config.namespace@
# 配置中心地址
server-addr: @config.server-addr@
# 配置对应的分组
group: @config.group@
# 配置nacos中的配置文件名称
prefix: jero-gateway
# 配置文件后缀
file-extension: yaml
# 支持多个共享 Data Id 的配置,优先级小于extension-configs,自定义 Data Id 配置 属性是个集合,内部由 Config POJO 组成。Config 有 3 个属性,分别是 dataId, group 以及 refresh
#shared-configs[0]:
#data-id: @prefix.name@-common.yaml # 配置文件名-Data Id
#group: @config.group@ # 默认为DEFAULT_GROUP
#refresh: false # 是否动态刷新,默认为false
discovery:
namespace: @config.namespace@
server-addr: @config.server-addr@
watch:
enabled: false
jero:
# 签名密钥串(前后端要一致)
signatureSecret: dd05f1c54d63749eda95f9fa6d49v442a
# 文件限制后缀黑名单
fileSuffixLimits: 0x00,%00,\\00,.jsp,.exe,.php,.asp,.aspx,.jspx,.xml,.html,.js,.sh,.bin
# 跨站白名单
whiteUrls:
route:
config:
data-type: yml
@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
<!--定义日志文件的存储地址 -->
<property name="LOG_HOME" value="../logs" />
<!--<property name="COLOR_PATTERN" value="%black(%contextName-) %red(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta( %replace(%caller{1}){'\t|Caller.{1}0|\r\n', ''})- %gray(%msg%xEx%n)" />-->
<!-- 控制台输出 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}:%L - %msg%n</pattern>-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %highlight(%-5level) %cyan(%logger{50}:%L) - %msg%n</pattern>
</encoder>
</appender>
<!-- 按照每天生成日志文件 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!--日志文件输出的文件名 -->
<FileNamePattern>${LOG_HOME}/jeroboot-%d{yyyy-MM-dd}.%i.log</FileNamePattern>
<!--日志文件保留天数 -->
<MaxHistory>30</MaxHistory>
<maxFileSize>10MB</maxFileSize>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符 -->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}:%L - %msg%n</pattern>
</encoder>
</appender>
<!--myibatis log configure -->
<logger name="com.apache.ibatis" level="TRACE" />
<logger name="java.sql.Connection" level="DEBUG" />
<logger name="java.sql.Statement" level="DEBUG" />
<logger name="java.sql.PreparedStatement" level="DEBUG" />
<!-- 日志输出级别 -->
<root level="INFO">
<appender-ref ref="STDOUT" />
<appender-ref ref="FILE" />
</root>
</configuration>
@@ -0,0 +1,65 @@
<?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">
<parent>
<artifactId>jero-cloud-module</artifactId>
<groupId>com.jero.boot</groupId>
<version>2.5.1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jero-cloud-monitor</artifactId>
<dependencies>
<!--Spring Boot Admin Server监控服务端-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-server</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--安全模块-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--undertow容器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
</project>
@@ -0,0 +1,16 @@
package com.jero.monitor;
import de.codecentric.boot.admin.server.config.EnableAdminServer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* 监控服务
*/
@SpringBootApplication
@EnableAdminServer
public class JeroMonitorApplication {
public static void main(String[] args) {
SpringApplication.run(JeroMonitorApplication.class);
}
}
@@ -0,0 +1,52 @@
package com.jero.monitor.config;
import de.codecentric.boot.admin.server.config.AdminServerProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
/**
* @author scott
*/
@Configuration
public class SecuritySecureConfig extends WebSecurityConfigurerAdapter {
private final String adminContextPath;
public SecuritySecureConfig(AdminServerProperties adminServerProperties) {
this.adminContextPath = adminServerProperties.getContextPath();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
// 登录成功处理类
SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
successHandler.setTargetUrlParameter("redirectTo");
successHandler.setDefaultTargetUrl(adminContextPath + "/");
http.authorizeRequests()
//静态文件允许访问
.antMatchers(adminContextPath + "/assets/**").permitAll()
//登录页面允许访问
.antMatchers(adminContextPath + "/login", "/css/**", "/js/**", "/image/*").permitAll()
//其他所有请求需要登录
.anyRequest().authenticated()
.and()
//登录页面配置用于替换security默认页面
.formLogin().loginPage(adminContextPath + "/login").successHandler(successHandler).and()
//登出页面配置用于替换security默认页面
.logout().logoutUrl(adminContextPath + "/logout").and()
.httpBasic().and()
.csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.ignoringAntMatchers(
"/instances",
"/actuator/**"
);
}
}
@@ -0,0 +1,41 @@
server:
port: 9111
spring:
boot:
admin:
ui:
title: JeroCloud监控中心
client:
instance:
metadata:
tags:
environment: local
security:
user:
name: "admin"
password: "admin"
application:
name: jero-monitor
cloud:
nacos:
discovery:
server-addr: @config.server-addr@
metadata:
user.name: ${spring.security.user.name}
user.password: ${spring.security.user.password}
# 服务端点检查
management:
trace:
http:
enabled: true
endpoints:
web:
exposure:
include: "*"
endpoint:
health:
show-details: always
@@ -0,0 +1,15 @@
FROM anapsix/alpine-java:8_server-jre_unlimited
MAINTAINER jeroos@163.com
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
RUN mkdir -p /jero-cloud-nacos
WORKDIR /jero-cloud-nacos
EXPOSE 8848
ADD ./target/jero-cloud-nacos-2.5.1.jar ./
CMD sleep 5;java -Dfile.encoding=utf-8 -Djava.security.egd=file:/dev/./urandom -jar jero-cloud-nacos-2.5.1.jar
@@ -0,0 +1,75 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>jero-cloud-module</artifactId>
<groupId>com.jero.boot</groupId>
<version>2.5.1</version>
</parent>
<artifactId>jero-cloud-nacos</artifactId>
<name>jero-cloud-nacos</name>
<description>nacos启动模块</description>
<repositories>
<repository>
<id>aliyun</id>
<name>aliyun Repository</name>
<url>https://maven.aliyun.com/repository/public</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>jeecg</id>
<name>jeecg Repository</name>
<url>https://maven.jeecg.org/nexus/content/repositories/jeecg</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-naming</artifactId>
<version>2.0.4</version>
</dependency>
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-istio</artifactId>
<version>2.0.4</version>
</dependency>
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-config</artifactId>
<version>2.0.4</version>
</dependency>
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-console</artifactId>
<version>2.0.4</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
@@ -0,0 +1,37 @@
package com.alibaba.nacos;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.scheduling.annotation.EnableScheduling;
/**
* Nacos 启动类
* 引用的nacos console 源码运行简化开发
* 生产建议从官网下载最新版配置运行
* @author zyf
*/
@SpringBootApplication(scanBasePackages = "com.alibaba.nacos")
@ServletComponentScan
@EnableScheduling
public class JeroNacosApplication {
/**
* 是否单机模式启动
*/
private static String standalone = "true";
/**
* 是否开启鉴权
*/
private static String enabled = "false";
public static void main(String[] args) {
System.setProperty("nacos.standalone", standalone);
System.setProperty("nacos.core.auth.enabled", enabled);
System.setProperty("server.tomcat.basedir","logs");
//自定义启动端口号
System.setProperty("server.port","8848");
SpringApplication.run(JeroNacosApplication.class, args);
}
}
@@ -0,0 +1,54 @@
server:
servlet:
contextPath: /nacos
tomcat:
accesslog:
enabled: true
pattern: '%h %l %u %t "%r" %s %b %D %{User-Agent}i %{Request-Source}i'
basedir: ''
spring:
datasource:
platform: mysql
db:
num: 1
password:
'0': 123456
url:
'0': jdbc:mysql://127.0.0.1:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true
user:
'0': root
management:
metrics:
export:
elastic:
enabled: false
influx:
enabled: false
nacos:
core:
auth:
caching:
enabled: true
default:
token:
expire:
seconds: 18000
secret:
key: SecretKey012345678901234567890123456789012345678901234567890123456789
enabled: false
system:
type: nacos
istio:
mcp:
server:
enabled: false
naming:
empty-service:
auto-clean: true
clean:
initial-delay-ms: 50000
period-time-ms: 30000
security:
ignore:
urls: /,/error,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-ui/public/**,/v1/auth/**,/v1/console/health/**,/actuator/**,/v1/console/server/**
standalone: true
@@ -0,0 +1,111 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>jero-cloud-module</artifactId>
<groupId>com.jero.boot</groupId>
<version>2.5.1</version>
</parent>
<artifactId>jero-cloud-sentinel</artifactId>
<name>jero-cloud-sentinel</name>
<description>sentinel启动模块</description>
<repositories>
<repository>
<id>aliyun</id>
<name>aliyun Repository</name>
<url>https://maven.aliyun.com/repository/public</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>jeecg</id>
<name>jeecg Repository</name>
<url>https://maven.jeecg.org/nexus/content/repositories/jeecg</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.jeecgframework.cloud</groupId>
<artifactId>sentinel-dashboard</artifactId>
<version>1.8.2</version>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-core</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-web-servlet</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-transport-simple-http</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-parameter-flow-control</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-api-gateway-adapter-common</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--undertow容器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.3</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
<version>4.4.5</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpasyncclient</artifactId>
<version>4.1.3</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore-nio</artifactId>
<version>4.4.6</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
@@ -0,0 +1,49 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.csp.sentinel.dashboard;
import com.alibaba.csp.sentinel.init.InitExecutor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.Environment;
/**
* Sentinel dashboard application.
*
* @author Carpenter Lee
*/
@SpringBootApplication
@Slf4j
public class JeroSentinelDashboardApplication {
public static void main(String[] args) {
System.setProperty("csp.sentinel.app.type", "1");
triggerSentinelInit();
ConfigurableApplicationContext application = SpringApplication.run(JeroSentinelDashboardApplication.class, args);
Environment env = application.getEnvironment();
String port = env.getProperty("server.port");
log.info("\n----------------------------------------------------------\n\t" +
"Application SentinelDashboard is running! Access URLs:\n\t" +
"Local: \t\thttp://localhost:" + port + "/\n\t" +
"----------------------------------------------------------");
}
private static void triggerSentinelInit() {
new Thread(InitExecutor::doInit).start();
}
}
@@ -0,0 +1,28 @@
#spring settings
spring.http.encoding.force=true
spring.http.encoding.charset=UTF-8
spring.http.encoding.enabled=true
#cookie name setting
server.servlet.session.cookie.name=sentinel_dashboard_cookie
#spring.cloud.nacos.config.server-addr=127.0.0.1:8848
#spring.cloud.nacos.config.namespace=
#spring.cloud.nacos.config.group-id=DEFAULT_GROUP
server.port=8087
#logging settings
logging.level.org.springframework.web=INFO
logging.file=${user.home}/logs/csp/sentinel-dashboard.log
logging.pattern.file= %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n
#logging.pattern.console= %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n
#auth settings
auth.filter.exclude-urls=/,/auth/login,/auth/logout,/registry/machine,/version
auth.filter.exclude-url-suffixes=htm,html,js,css,map,ico,ttf,woff,png
# If auth.enabled=false, Sentinel console disable login
auth.username=sentinel
auth.password=sentinel
# Inject the dashboard version. It's required to enable
# filtering in pom.xml for this resource file.
sentinel.dashboard.version=1.8.2

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