109 Commits

Author SHA1 Message Date
随遇而安
8216db72b4 update README.md.
Signed-off-by: 随遇而安 <164770707@qq.com>
2025-12-11 17:53:10 +00:00
随遇而安
8501632079 update README.md.
Signed-off-by: 随遇而安 <164770707@qq.com>
2025-12-08 02:11:38 +00:00
随遇而安
1a3c6fd460 update README.md.
Signed-off-by: 随遇而安 <164770707@qq.com>
2025-12-03 14:02:54 +00:00
随遇而安
2cf44dc4ef update README.md.
Signed-off-by: 随遇而安 <164770707@qq.com>
2025-12-03 14:00:10 +00:00
随遇而安
7b361e6eae update README.md.
Signed-off-by: 随遇而安 <164770707@qq.com>
2025-12-03 13:57:36 +00:00
随遇而安
07392576f3 update README.md.
Signed-off-by: 随遇而安 <164770707@qq.com>
2025-12-03 13:52:38 +00:00
随遇而安
79d2a82e2d update README.md.
Signed-off-by: 随遇而安 <164770707@qq.com>
2025-12-03 13:51:11 +00:00
随遇而安
d357a68e35 update README.md.
Signed-off-by: 随遇而安 <164770707@qq.com>
2025-12-03 13:48:27 +00:00
随遇而安
ce0abdfbb1 update README.md.
Signed-off-by: 随遇而安 <164770707@qq.com>
2025-12-03 13:47:58 +00:00
kerwincui
bc93c2d2eb 增加gitee投票说明 2025-12-03 21:45:15 +08:00
随遇而安
5f33234e8b update README.md.
Signed-off-by: 随遇而安 <164770707@qq.com>
2025-12-03 08:47:07 +00:00
随遇而安
73f66f8d78 update README.md.
Signed-off-by: 随遇而安 <164770707@qq.com>
2025-12-03 08:28:49 +00:00
随遇而安
3c7a64327d update README.md.
Signed-off-by: 随遇而安 <164770707@qq.com>
2025-12-03 08:19:27 +00:00
随遇而安
fa25f771a2 update README.md.
Signed-off-by: 随遇而安 <164770707@qq.com>
2025-12-03 07:40:45 +00:00
随遇而安
3e4d271be8 update README.md.
Signed-off-by: 随遇而安 <164770707@qq.com>
2025-12-03 07:37:47 +00:00
随遇而安
63d2258911 update README.md.
Signed-off-by: 随遇而安 <164770707@qq.com>
2025-12-03 07:36:19 +00:00
随遇而安
f48e7a1579 update README.md.
Signed-off-by: 随遇而安 <164770707@qq.com>
2025-12-03 07:12:51 +00:00
随遇而安
44f3182ec3 update README.md.
Signed-off-by: 随遇而安 <164770707@qq.com>
2025-12-03 07:04:27 +00:00
随遇而安
0306e5c6cb update README.md.
Signed-off-by: 随遇而安 <164770707@qq.com>
2025-12-03 06:29:21 +00:00
随遇而安
4dc62fdecd update README.md.
Signed-off-by: 随遇而安 <164770707@qq.com>
2025-12-03 06:25:07 +00:00
随遇而安
61a9c0a846 update README.md.
Signed-off-by: 随遇而安 <164770707@qq.com>
2025-12-03 06:22:07 +00:00
随遇而安
b3cb59e151 update README.md.
Signed-off-by: 随遇而安 <164770707@qq.com>
2025-12-03 03:42:59 +00:00
随遇而安
9dad5ec04c update README.md.
Signed-off-by: 随遇而安 <164770707@qq.com>
2025-12-03 03:41:28 +00:00
随遇而安
6cf181ee8d update README.md.
Signed-off-by: 随遇而安 <164770707@qq.com>
2025-12-02 10:05:42 +00:00
随遇而安
d7b5ce1c16 update README.md.
Signed-off-by: 随遇而安 <164770707@qq.com>
2025-12-02 10:03:17 +00:00
随遇而安
c5a9d0d567 update README.md.
Signed-off-by: 随遇而安 <164770707@qq.com>
2025-11-22 15:13:28 +00:00
gx_ma
f5af688500 refactor(多数据源): 运行报错修复 2025-11-20 15:40:42 +08:00
gx_ma
908c4e58a7 Merge remote-tracking branch 'origin/master' 2025-11-20 15:39:37 +08:00
gx_ma
1e7bd72bfa refactor(多数据源): 运行报错修复 2025-11-20 15:33:13 +08:00
zhuangpeng.li
5058e2edc7 fix(XXE漏洞修复): XXE漏洞修复 2025-11-20 11:23:29 +08:00
随遇而安
d56c600643 update README.md.
Signed-off-by: 随遇而安 <164770707@qq.com>
2025-11-06 03:01:50 +00:00
随遇而安
c5ddae9565 update README.md.
Signed-off-by: 随遇而安 <164770707@qq.com>
2025-09-04 04:06:01 +00:00
随遇而安
edf2dc0b88 update README.md.
Signed-off-by: 随遇而安 <164770707@qq.com>
2025-07-29 07:49:43 +00:00
随遇而安
21f0a90a2d update README.md.
Signed-off-by: 随遇而安 <164770707@qq.com>
2025-07-29 07:46:25 +00:00
gx_ma
fe28413ab2 fix(删除用户): 用户删除bug修复 2025-06-04 14:50:23 +08:00
gx_ma
25bcaae210 fix(用户注册): 注册失败修复 2025-05-26 10:16:41 +08:00
gx_ma
a082c61f6b refactor(开源演示地址删除): 开源地址删除 2025-05-23 16:31:47 +08:00
baiying
2ad5d6ff28 fix(修改密码):修改密码传参方式修改 2025-05-20 16:52:26 +08:00
kerwincui
7f58d3e12b 后端配置简化 2025-05-15 12:07:16 +08:00
kerwincui
7079610ee4 认证信息输入框宽度调整 2025-05-15 12:06:50 +08:00
Zhu
5bb2794e8c fix(设备详情): 报错修复 2025-05-15 10:10:29 +08:00
gx_ma
4ec2f3eeaa feat(多数据源): 初始化sql更新 2025-05-07 15:45:40 +08:00
zhuangpeng.li
bc5b492d26 Merge branch 'dev-ruoyi-5.8.9' 2025-04-28 10:44:57 +08:00
zhuangpeng.li
a3625cd254 fix(去除sun包): 去除sun包,兼容其他版本jdk 2025-04-28 10:43:48 +08:00
gx_ma
3500faf044 feat(多数据源): 新增字段 2025-04-27 11:51:39 +08:00
zhuangpeng.li
5b6ec185c4 fix(ruoyi3.8.9): 后端代码同步 2025-04-27 11:21:09 +08:00
gx_ma
f545198645 feat(多数据源): Oracle脚本 2025-04-24 18:01:30 +08:00
gx_ma
32cc5e794e fix(视频设备): 报错修改 2025-04-24 17:14:00 +08:00
gx_ma
461ab2a37e Merge remote-tracking branch 'origin/dev-ruoyi-5.8.9' into dev-ruoyi-5.8.9 2025-04-24 16:49:27 +08:00
gx_ma
44bd079c06 refactor(代码生成): 代码生成支持多数据源 2025-04-24 16:48:48 +08:00
Zhu
70f466e471 fix(优化): 代码优化 2025-04-24 16:48:38 +08:00
Zhu
8d60474904 fix(代码更新): 更新代码 2025-04-24 16:35:20 +08:00
Zhu
2df210fe6b fix(生成代码): 数据源的优化 2025-04-24 16:05:18 +08:00
Zhu
00f5c882e2 fix(生成代码): 新增数据源的显示 2025-04-24 15:47:04 +08:00
gx_ma
25239c32cb refactor(多数据源): 报错与关键字修改 2025-04-24 11:10:17 +08:00
gx_ma
bfd2977ff4 fix(通知公告): string与blob类型互转报错 2025-04-24 09:34:26 +08:00
gx_ma
8c9031acef fix(多数据源): 配置增加 2025-04-23 17:05:43 +08:00
gx_ma
f5598f1663 fix(多数据源): 报错修改 2025-04-23 17:04:02 +08:00
zhuangpeng.li
d681d4b22b Merge remote-tracking branch 'origin/dev-ruoyi-5.8.9' into dev-ruoyi-5.8.9 2025-04-23 11:39:55 +08:00
zhuangpeng.li
0215feb4b0 fix(登录问题): 修复登录用户缓存为空的问题 2025-04-23 11:39:44 +08:00
gx_ma
905e36243c refactor(字段规范): sip_device字段命名规范 2025-04-22 15:18:20 +08:00
zhuangpeng.li
201bbfeefb Merge remote-tracking branch 'origin/dev-ruoyi-5.8.9' into dev-ruoyi-5.8.9
# Conflicts:
#	springboot/fastbee-framework/src/main/java/com/fastbee/framework/config/DataSourceConfig.java
2025-04-22 10:20:28 +08:00
zhuangpeng.li
39aba490c6 feat(多数据源): 支持多数据源动态切换 2025-04-22 10:20:02 +08:00
gx_ma
54f2487330 feat(多数据源脚本): 达梦、SQL server、postgres 2025-04-22 10:03:31 +08:00
zhuangpeng.li
ff51ea78af feat(ruoyi版本同步): ruoyi-3.8.9版本同步 2025-04-17 09:44:10 +08:00
zhuangpeng.li
e404cd26d4 build(依赖包整理): 依赖包版本统一整理 2025-04-16 14:31:24 +08:00
zhuangpeng.li
b86acd933b build(版本号修改): 版本号修改 2025-04-16 11:08:49 +08:00
zhuangpeng.li
5fc141719a fix(规则脚本增加日志): 规则脚本增加日志 2025-04-15 17:22:45 +08:00
zhuangpeng.li
8939c78e37 fix(规则脚本增加日志): 规则脚本增加日志 2025-04-15 16:07:23 +08:00
zhuangpeng.li
19fedbe841 fix(规则脚本增加日志): 规则脚本增加日志 2025-04-15 15:42:19 +08:00
kerwincui
1805c99f4f 日志报错信息处理 2025-04-15 15:37:08 +08:00
kerwincui
56577f4262 规则脚本增加日志 2025-04-15 15:34:04 +08:00
kerwincui
12dc1e869e 说明简化 2025-04-01 16:31:52 +08:00
kerwincui
7f5d432f5a 更新图片 2025-04-01 16:23:42 +08:00
kerwincui
d979c9025a 认证信息界面和接口调整 2025-04-01 12:00:21 +08:00
随遇而安
f71cbff3d7 update springboot/fastbee-admin/src/main/resources/application-prod.yml.
Signed-off-by: 随遇而安 <164770707@qq.com>
2025-03-31 09:38:58 +00:00
随遇而安
645fca0119 update springboot/fastbee-admin/src/main/resources/application-dev.yml.
Signed-off-by: 随遇而安 <164770707@qq.com>
2025-03-31 09:37:45 +00:00
kerwincui
4065392ccc 简化后端配置 2025-03-31 17:36:44 +08:00
kerwincui
bc8b5796d2 sql更新 2025-03-26 17:55:56 +08:00
kerwincui
18568aa663 增加定时更新设备状态任务 2025-03-26 17:50:29 +08:00
kerwincui
38e236a8ff 修复前端打包警告信息 2025-03-26 11:19:22 +08:00
kerwincui
8099d7bdcc 接口添加说明 2025-03-24 15:50:44 +08:00
kerwincui
a5a88ae379 脚本更新 2025-03-20 22:32:42 +08:00
kerwincui
462da2a6ce 删除多余文件,添加引用的js文件 2025-03-20 22:28:07 +08:00
kerwincui
f7e9e403b0 界面简化 2025-03-20 22:27:27 +08:00
kerwincui
2772c49df1 设备详情调整,其中tab-panel直接添加key属性解决key重复问题 2025-03-20 22:02:59 +08:00
kerwincui
1afceda43c sql数据简化 2025-03-19 16:31:08 +08:00
kerwincui
4920d84516 物模型和产品操作优化 2025-03-19 16:27:17 +08:00
kerwincui
14ad72b6fa Merge branch 'dev'
# Conflicts:
#	docker/data/mysql/initdb/fastbee2.1.sql
2024-12-17 22:05:18 +08:00
kerwincui
59f4185d2b sql脚本中注释bug修复 2024-12-17 22:04:50 +08:00
kerwincui
59c83b1601 Merge branch 'dev' 2024-12-13 10:44:49 +08:00
随遇而安
e8c192dbd4 update README.md.
Signed-off-by: 随遇而安 <164770707@qq.com>
2024-12-13 02:37:03 +00:00
gx_ma
1424423769 Merge remote-tracking branch 'origin/master' 2024-12-10 15:07:32 +08:00
gx_ma
0615f005b7 fix(初始化脚本): sql修改 2024-12-10 15:06:02 +08:00
随遇而安
c2a7aafcd4 !45 fix:解决设备运行状态无法显示问题https://gitee.com/beecue/fastbee/issues/IAW9KE
Merge pull request !45 from Ming/master
2024-11-29 03:54:26 +00:00
Ming
d8e07328c7 fix:解决设备运行状态无法显示问题https://gitee.com/beecue/fastbee/issues/ 2024-11-28 05:21:24 +08:00
Ming
5abe95f617 fix:解决设备运行状态无法显示问题https://gitee.com/beecue/fastbee/issues/IAW9KE 2024-11-28 05:12:59 +08:00
随遇而安
c12352ffea update springboot/fastbee-admin/src/main/resources/application-dev.yml.
Signed-off-by: 随遇而安 <164770707@qq.com>
2024-11-11 07:30:48 +00:00
随遇而安
5715a15d46 update springboot/fastbee-admin/src/main/resources/application.yml.
Signed-off-by: 随遇而安 <164770707@qq.com>
2024-11-11 07:29:51 +00:00
Zhu
92b504d78a Merge branch 'master' into dev 2024-11-06 12:02:04 +08:00
Zhu
c8a9fd0a99 fix(运行状态):实时更新问题修复 2024-11-06 12:00:55 +08:00
随遇而安
4af4f250eb update README.md.
Signed-off-by: 随遇而安 <164770707@qq.com>
2024-10-28 07:20:28 +00:00
随遇而安
6d50802b60 update vue/README.md.
Signed-off-by: 随遇而安 <164770707@qq.com>
2024-09-26 03:06:15 +00:00
gx_ma
87cb9f2ba1 1.开源app已经上线,代码删除对应安装包 2024-08-19 16:26:20 +08:00
gx_ma
2f481c1b13 1.sql脚本运行报错修改 2024-08-19 15:17:09 +08:00
gx_ma
cfebb8753f Merge branch 'dev' 2024-08-14 16:32:43 +08:00
gx_ma
a7f60c902a !44 修复验证码关闭的bug
Merge pull request !44 from 像风一样/N/A
2024-08-14 08:29:48 +00:00
像风一样
aab9710423 修复验证码关闭的bug
后台配置了取消验证,但实际没有取消

Signed-off-by: 像风一样 <963565242@qq.com>
2024-08-14 07:59:03 +00:00
zhuangpeng.li
a56ab82a94 1.播放器版本更新 2024-08-09 15:46:03 +08:00
322 changed files with 42671 additions and 5742 deletions

163
README.md
View File

@@ -1,110 +1,95 @@
![](https://oscimg.oschina.net/oscnet/up-09e0fd4e7966a3049aa39e7ab2a99dc5586.png)
[![gitee投票](./images/banner1.png)](https://gitee.com/activity/2025opensource?ident=IKKZS9)
FastBee 是一款面向中小企业与个人开发者的**轻量**、**易用**、**全栈**开源物联网平台。
---
### 一、项目介绍
1. FastBee开源物联网平台,简单易用,更适合中小企业和个人学习使用。适用于智能家居、智慧办公、智慧社区、农业监测、水利监测、工业控制等
2. 系统后端采用Spring boot前端采用VueMqtt服务端使用netty搭建移动端支持微信小程序、安卓、苹果和H5采用Uniapp数据库采用Mysql、TDengine和Redis设备端支持ESP32、ESP8266、树莓派、合宙等
FastBee 集设备管理、数据监控、多端应用于一体,支持[一键容器化部署](https://fastbee.cn/doc/install/docker.html),适用于智能家居、智慧办公、智慧社区、农业监测、水利监测、工业控制等多种场景。平台基于 Spring Boot 开发,<b>内置高性能 MQTT 服务端,无需额外部署 EMQX 或其他 MQTT 服务端</b>,前端采用 Vue 与 ElementUI移动端支持微信小程序、Android、iOS 及 H5设备端兼容 ESP32、ESP8266、树莓派、合宙等常见硬件
---
### 二、系统功能
| 系统功能 | 功能说明 | 开源版本 | 商业版本 |
|:------------------------:|:-----------------------------------------------:|------|--------------|
| 产品管理 | 产品详情、产品物模型、产品分类、设备授权、产品固件 | 支持 | 支持 |
| 设备管理 | 设备详情、设备分组、设备日志、设备分享、设备实时控制、实时状态、数据监测 | 支持 | 支持 |
| 物模型管理 | 属性(设备状态和监测数据),功能(执行特定任务),事件(设备主动上报给云端) | 支持 | 支持 |
| MQTT接入 | emqx开源版、netty版本MqttBroker | 支持 | 支持 |
| 硬件 SDK | ESP-IDF、Arduino、RaspberryPi、合宙等平台设备接入 | 支持 | 支持 |
| 视频监控接入 | 基于GB/T28181协议支持主流厂商监控设备接入,直播设备录制回放、 云端录像和云台控制 | 支持设备接入和直播 | 支持 |
| 多协议管理 | 硬件设备多种协议支持管理 | 支持JSON | 支持多种 |
| TCP接入 | 基于Netty搭建的TCP服务器 | 不支持 | 支持 |
| Modbus接入 | 透传Modbus/边缘网关接入Modbus设备 | 不支持 | 支持 |
| 采集点管理 | 网关设备管理子设备接入 | 不支持 | 支持 |
| 设备模拟器调试 | Modbus设备在线调试 | 不支持 | 支持 |
| 数据大屏 | 数据大屏可视化将图表或页面元素封装为基础组件0代码即可完成业务需求。 | 不支持 | 支持 |
| 规则引擎-规则脚本 | 可视化规则引擎编写支持js,java等脚本修改消息结构处理设备上行/下行/上线/下线/数据解析/数据转换 | 不支持 | 支持 |
| 场景联动 | 基于规则引擎生成场景联动 | 不支持 | 支持 |
| 告警功能 | 告警: 设备告警/平台告警判定 告警配置: 基于规则引擎开发的平台告警判定 告警记录:设备告警记录入库 | 不支持 | 支持 |
| 消息通知功能 | 阿里云短信/腾讯云短信阿里云语言/腾讯云语音/QQ邮箱/163邮箱/微信小程序/企业微信群机器人/企业微信应用信息/钉钉消息通知/钉钉群机器人 | 不支持 | 支持 |
| 多租户 | 系统内租户的管理,独占一套系统配置,数据相互隔离。如:租户权限、过期时间、用户数量、企业信息等 | 不支持 | 支持 |
| 移动端app | 移动端(安卓 / 苹果 / 微信小程序) | 不支持 | 支持 |
| 智能音响对接 | 云云对接:智能音响(小度、天猫精灵、小爱同学) | 不支持 | 额外付费模块 |
| web组态 | 自定义数据大屏/2D/3D | 不支持 | 额外付费模块 |
| 第三方OpenAPT接入 | 萤石云海康sdk接入、AI SDK接入 | 不支持 | 额外付费模块 |
| 数据存储 | 设备数据处理 | redis存储最后一条数据不支持实时更新 | redis存储最新数据,实时更新/mysql存储系统数据/TDengine时序数据库存储设备数据 |
| 设备接入数/上行数据并发支持 | 设备接入数,以及设备数据并发量 | 支持小规模设备接入,同步处理数据 | 支持规模量大设备。消息队列削峰,线程池异步处理高并发数据 |
| 技术支持 | | 不支持 | 提供一定的技术支持/技术方案 |
| 系统功能 | 功能说明 |
|----------------|--------------------------------------------------------------------------|
| 产品管理 | 创建产品、定义物模型、设置设备认证方式、管理产品分类及固件版本 |
| 设备管理 | 设备注册、分组、实时状态监测、远程控制、操作日志追溯及设备分享功能 |
| 物模型管理 | 提供标准化的属性、功能和事件定义,实现设备与平台的数据语义统一 |
| 规则脚本 | 对设备上报和平台下发的原始数据进行处理,转发后匹配设备交互格式 |
| MQTT 接入 | 内置基于 Netty 开发的高性能 MQTT Broker无需额外部署第三方服务 |
| 视频监控接入 | 支持 GB/T 28181 国家标准协议,实现视频直播设备管理 |
| 权限管理 | 基于成熟的若依权限框架,实现用户、角色、部门、岗位及操作日志的精细化管控 |
| 多数据源 | 支持 MySQL、PostgreSQL、MSSQL 等关系型数据库 |
| 硬件 SDK | 提供 ESP-IDF、Arduino、树莓派、合宙等常见硬件平台的接入示例与 SDK |
| 多端应用支持 | 提供 Web 控制台Vue + ElementUI、微信小程序、Android/iOS App 及 H5 界面 |
| 一键部署 | 推荐使用 Docker 容器化部署,支持[快速搭建](https://fastbee.cn/doc/install/docker.html)完整环境 |
[![](https://gitee.com/beecue/fastbee-sdk/raw/master/Arduino/FastBeeEsp32/device.png)](https://gitee.com/beecue/FastBee-Arduino)
![](https://oscimg.oschina.net/oscnet/up-a9a7fdaf40208becd26c2485783bc0f86e6.png)
---
|[空气检测仪](https://fastbee.cn/doc/pages/hardware/) | [物联网开发板](https://fastbee.cn/doc/pages/hardware/) | [Air724开发板](https://fastbee.cn/doc/pages/hardware/) | [智能开关](https://fastbee.cn/doc/pages/hardware/) | [查看更多>>](https://fastbee.cn/doc/pages/hardware/) |
### 三、快速开始
1. **环境准备**:确保服务器已安装 `Docker``Docker Compose`
2. **执行命令**
```bash
sudo wget -c https://hub.fastbee.cn/resource/install.sh && bash ./install.sh
```
根据提示选择“开源版本”,脚本将自动完成所有服务的拉取与启动。
3. **访问系统**:部署完成后,通过浏览器访问 `http://你的服务器IP:端口`,默认账号为 `admin`,密码为 `admin123`。
4. **详细步骤**:请查阅 [官方安装文档](https://fastbee.cn/doc/install/docker.html)。
---
### 四、技术栈
##### 服务端
- 相关技术Spring boot、MyBatis、Spring Security、Jwt、Mysql、Redis、TDengine、Netty等
- 开发工具IDEA
- 源码目录spring-boot文件夹下
##### Web端
- 相关技术ES6、Vue、Vuex、Vue-router、Vue-cli、Axios、Element-ui、Echart等
- 开发工具Visual Studio Code
- 源码目录vue文件夹下
##### 移动端(微信小程序 / Android / Ios / H5
- 相关技术uniapp、[uView](https://www.uviewui.com/)、[uChart](https://www.ucharts.cn/)
- 开发工具HBuilder
- 源码地址:[https://gitee.com/beecue/fastbee-app](https://gitee.com/beecue/fastbee-app)
##### 硬件端
- 相关技术: ESP-IDF、Arduino、FreeRTOS、Python、Lua等
- 开发工具Visual Studio Code 和 Arduino等
- 源码地址:[https://gitee.com/beecue/fastbee-sdk](https://gitee.com/beecue/fastbee-sdk)
##### 部署与运维
- 相关技术docker、docker-compose、shell
- [快速部署](https://fastbee.cn/doc/install/docker.html)安装好docker和docker-compose后执行命令 ` sudo wget -c https://hub.fastbee.cn/resource/install.sh && bash ./install.sh ` 然后选择开源版本,等待完成部署
|空气检测仪| 物联网开发板 | Air724开发板 | 智能开关| [查看更多>>](https://fastbee.cn/doc/device/) |
| :----: | :----------: |:----------: |:----------: |:----------: |
| ![](https://oscimg.oschina.net/oscnet/up-ad98a81677e5e68d660866770e3266ca4cf.png) | ![](https://oscimg.oschina.net/oscnet/up-68caf860d0659444e73f42717a03d2fdf72.png) | ![](https://oscimg.oschina.net/oscnet/up-cf690f6058042dccb567ff382ea9432ebab.png) |![](https://oscimg.oschina.net/oscnet/up-c4a7971510127324d6566dd6ea95d571483.jpg) | ![](https://oscimg.oschina.net/oscnet/up-4ce09be3599e3ff7ed91fe182572abd258b.jpg) |
---
### 三、技术栈
* 服务端
- 相关技术Spring boot、MyBatis、Spring Security、Jwt、Mysql、Redis、TDengine、EMQX、Netty等
- 开发工具IDEA
* Web端
- 相关技术ES6、Vue、Vuex、Vue-router、Vue-cli、Axios、Element-ui、Echart等
- 开发工具Visual Studio Code
* 移动端(微信小程序 / Android / Ios / H5
- 相关技术uniapp、[uView](https://www.uviewui.com/)、[uChart](https://www.ucharts.cn/)
- 开发工具HBuilder
* 硬件端
- 相关技术: ESP-IDF、Arduino、FreeRTOS、Python、Lua等
- 开发工具Visual Studio Code 和 Arduino等
### 五、其他
1. **商用授权**:开源版本采用 AGPL3 协议,可用于个人学习和使用,商业用途推荐购买[商业版本授权](https://fastbee.cn/doc/pages/sponsor/)。
2. **在线演示**[商业版本演示地址](https://iot.fastbee.cn/)(开源版本暂仅支持 MQTT 协议)。
3. **系统文档**[https://fastbee.cn/doc/](https://fastbee.cn/doc/)
4. **QQ 交流群**720136372、946029159、1073236354
### 四、项目目录
&nbsp;&nbsp;&nbsp;&nbsp; app -------------------- 移动端(微信小程序 / Android / Ios / H5 商业版开源<br/>
&nbsp;&nbsp;&nbsp;&nbsp; docker ---------------- docker部署文件<br />
&nbsp;&nbsp;&nbsp;&nbsp; spring-boot ---------- 后端<br/>
&nbsp;&nbsp;&nbsp;&nbsp; vue -------------------- 前端<br />
---
### 六、部分图片
### 五、商用授权
项目采用AGPL3协议可用于个人学习和使用商业用途需要赞助项目获得授权并提供商业版本源码、可视化平台和移动端源码。赞助过的用户请下载商业版本源码。
- [授权详情>>](https://fastbee.cn/doc/pages/sponsor/) &nbsp; [商业版本源码>>](https://fastbee.cn/doc/pages/sponsor/)
- [移动端源码>>](https://gitee.com/beecue/fastbee-app) &nbsp; [可视化平台源码>>](https://fastbee.cn/doc/pages/sponsor/)
- [硬件SDK源码>>](https://gitee.com/beecue/fastbee-sdk)
- 二开项目同样遵守AGPL3.0协议进行开源,可以向原作者申请授权
- 如果商业项目想转闭源,可以向原作者申请或者购买闭源授权
### 六、贡献代码
- [贡献者指南>>](./doc/贡献者指南.md)
- [Git提交规范>>](./doc/Git提交规范.md)
- [功能规划>>](./RoadMap.md)
### 七、其他
1. QQ交流群&#x1F680;946029159 &#x1F680;1073236354(已满)
2. 权限管理基于ruoyi-vue系统开发Mqtt消息服务器使用EMQX5.0开源版
* [开源版本在线演示](http://101.33.237.12/)
* [商业版本在线演示](https://iot.fastbee.cn/)
* [项目使用文档](https://fastbee.cn/doc/)
* [若依权限管理系统文档](http://doc.ruoyi.vip/ruoyi-vue/)
* [EMQX5.0消息服务器文档](https://www.emqx.io/docs/zh/v5.0/)
* [uCharts高性能跨平台图表库](https://www.ucharts.cn)
3. 项目贡献者(如有遗漏请联系作者)
- [小驿物联](https://gitee.com/iot-xiaoyi)、[CrazyDull](https://gitee.com/crazyDull)、[YBZX](https://github.com/YBZX)、 [CQAdu](https://gitee.com/iot.adu)、[孙阿龙](https://gitee.com/sunalong)、[xxmfl](https://gitee.com/xxmfl)、[董晓龙-3715687@qq.com](https://fastbee.cn/)
- [SXH](https://gitee.com/sixiaohu)、 [Redamancy_zxp](https://gitee.com/redamancy-zxp)、 [LEE](https://gitee.com/yueming188)、 [LemonTree](https://gitee.com/fishhunterplus)、 [Tang](https://gitee.com/mexiaotang)、 [Tang](https://gitee.com/mexiaotang)、[KUN](https://gitee.com/L_KUN_KUN)
4. 主要参与用户:
- [Guanshubiao](https://gitee.com/guanshubiao):熟悉物联网开发,完善和优化系统的网关架构和部分功能等
- [帐篷](https://gitee.com/zhuangpengli):熟悉物联网开发,完善视频监控模块和部分协议等
- [JaminDeng](https://gitee.com/jamin-deng):熟悉物联网开发,完善平台前端设计可视化等
### 八、部分图片
![](https://oscimg.oschina.net/oscnet/up-972dea7b54eca705dcc8bf2fe0680b12c09.png)
![](https://oscimg.oschina.net/oscnet/up-6d89f1558797a9becf07c20f92c1407a13a.png)
![部分功能截图](https://oscimg.oschina.net/oscnet/up-972dea7b54eca705dcc8bf2fe0680b12c09.png)
![部分功能截图](https://oscimg.oschina.net/oscnet/up-6d89f1558797a9becf07c20f92c1407a13a.png)
<table>
<tr>
@@ -129,4 +114,6 @@
</tr>
</table>
---
FastBee 项目代码完全开源,覆盖设备 SDK、后端服务、前端应用和移动端兼顾成熟度、性能与开发效率适合中小企业快速构建物联网业务也便于开发者进行二次开发与学习研究。

View File

@@ -1,47 +0,0 @@
<br />
* 项目可用于个人学习;商业使用请赞助该项目,获得授权;
* 赞助后还提供了小程序、APP源码。[详情](https://wumei.live/doc/pages/sponsor/)
#### 移动端介绍
|安卓/Android|苹果/IOS|微信小程序| 网页/H5|Vue2.0
| :---: | :---: | :---: | :---: |:---: |
| √ | √| √ | √ | √ |
1. 项目使用uniapp开发适配小程序、安卓、苹果和H5其他平台未测试。
2. UI框架使用uView2.0
3. 组件使用easycom模式只要组件安装在项目的components目录下或uni_modules目录下并符合components/组件名称/组件名称.vue目录结构。就可以不用引用、注册直接在页面中使用。
4. 开发工具为Hbuilder3.3版本,版本不易过低。
###### 项目结构
```
├─apis // 接口管理
│ ├─modules // api模块化目录
│ │ └─device.js // 设备接口地址
│ ├─http.api.js // 接口定义文件
│ └─http.interceptor // 拦截器
├─common // 公共文件
│ ├─config // 环境配置 (设置baseurl等)
│ ├─extend // 扩展原型方法
│ ├─filters // 全局过滤器
│ └─tools // 全局公共方法
├─components // 项目组件库,组件放置这里,其他页面可直接使用
│ ├─cl-test // easycom测试组件
│ └─cl-icon // iconfont图标组件
├─pages // 页面目录
│ ├─public // 公共页面
│ └─tarbar // 底部导航栏页面
├─static // 图片目录
├─store // vuex
│ ├─$u.mixin // store全局混入方法
│ └─index // vuex 组件全局状态管理
├─uni_modules // 插件市场插件目录
│ └─uview-ui // uview-ui
```
<br /><br />

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

BIN
images/banner.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

BIN
images/banner1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

View File

@@ -5,7 +5,7 @@
<parent>
<artifactId>fastbee</artifactId>
<groupId>com.fastbee</groupId>
<version>3.8.5</version>
<version>${fastbee.version}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>
@@ -34,7 +34,7 @@
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
<version>1.6.2</version>
<version>${io.swagger.version}</version>
</dependency>
<!-- Mysql驱动包 -->
@@ -84,7 +84,7 @@
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.1.1.RELEASE</version>
<version>2.5.15</version>
<configuration>
<fork>true</fork> <!-- 如果没有该配置devtools不会生效 -->
</configuration>

View File

@@ -2,14 +2,13 @@ package com.fastbee;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
/**
* 启动程序
*
* @author ruoyi
*/
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
@SpringBootApplication
public class FastBeeApplication
{
public static void main(String[] args)

View File

@@ -7,6 +7,7 @@ import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.TreeSet;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
@@ -26,7 +27,7 @@ import com.fastbee.system.domain.SysCache;
/**
* 缓存监控
*
*
* @author ruoyi
*/
@Api(tags = "缓存监控")
@@ -87,7 +88,7 @@ public class CacheController
public AjaxResult getCacheKeys(@PathVariable String cacheName)
{
Set<String> cacheKeys = redisTemplate.keys(cacheName + "*");
return AjaxResult.success(cacheKeys);
return AjaxResult.success(new TreeSet<>(cacheKeys));
}
@ApiOperation("缓存内容")

View File

@@ -28,7 +28,7 @@ import com.fastbee.system.service.ISysUserOnlineService;
/**
* 在线用户监控
*
*
* @author ruoyi
*/
@Api(tags = "在线用户监控")
@@ -54,24 +54,15 @@ public class SysUserOnlineController extends BaseController
LoginUser user = redisCache.getCacheObject(key);
if (StringUtils.isNotEmpty(ipaddr) && StringUtils.isNotEmpty(userName))
{
if (StringUtils.equals(ipaddr, user.getIpaddr()) && StringUtils.equals(userName, user.getUsername()))
{
userOnlineList.add(userOnlineService.selectOnlineByInfo(ipaddr, userName, user));
}
userOnlineList.add(userOnlineService.selectOnlineByInfo(ipaddr, userName, user));
}
else if (StringUtils.isNotEmpty(ipaddr))
{
if (StringUtils.equals(ipaddr, user.getIpaddr()))
{
userOnlineList.add(userOnlineService.selectOnlineByIpaddr(ipaddr, user));
}
userOnlineList.add(userOnlineService.selectOnlineByIpaddr(ipaddr, user));
}
else if (StringUtils.isNotEmpty(userName) && StringUtils.isNotNull(user.getUser()))
{
if (StringUtils.equals(userName, user.getUsername()))
{
userOnlineList.add(userOnlineService.selectOnlineByUserName(userName, user));
}
userOnlineList.add(userOnlineService.selectOnlineByUserName(userName, user));
}
else
{

View File

@@ -28,7 +28,7 @@ import com.fastbee.system.service.ISysConfigService;
/**
* 参数配置 信息操作处理
*
*
* @author ruoyi
*/
@Api(tags = "参数设置")
@@ -93,7 +93,7 @@ public class SysConfigController extends BaseController
@PostMapping
public AjaxResult add(@Validated @RequestBody SysConfig config)
{
if (UserConstants.NOT_UNIQUE.equals(configService.checkConfigKeyUnique(config)))
if (!configService.checkConfigKeyUnique(config))
{
return error("新增参数'" + config.getConfigName() + "'失败,参数键名已存在");
}
@@ -110,7 +110,7 @@ public class SysConfigController extends BaseController
@PutMapping
public AjaxResult edit(@Validated @RequestBody SysConfig config)
{
if (UserConstants.NOT_UNIQUE.equals(configService.checkConfigKeyUnique(config)))
if (!configService.checkConfigKeyUnique(config))
{
return error("修改参数'" + config.getConfigName() + "'失败,参数键名已存在");
}

View File

@@ -27,7 +27,7 @@ import com.fastbee.system.service.ISysDeptService;
/**
* 部门信息
*
*
* @author ruoyi
*/
@Api(tags = "部门管理")
@@ -84,7 +84,7 @@ public class SysDeptController extends BaseController
@PostMapping
public AjaxResult add(@Validated @RequestBody SysDept dept)
{
if (UserConstants.NOT_UNIQUE.equals(deptService.checkDeptNameUnique(dept)))
if (!deptService.checkDeptNameUnique(dept))
{
return error("新增部门'" + dept.getDeptName() + "'失败,部门名称已存在");
}
@@ -103,7 +103,7 @@ public class SysDeptController extends BaseController
{
Long deptId = dept.getDeptId();
deptService.checkDeptDataScope(deptId);
if (UserConstants.NOT_UNIQUE.equals(deptService.checkDeptNameUnique(dept)))
if (!deptService.checkDeptNameUnique(dept))
{
return error("修改部门'" + dept.getDeptName() + "'失败,部门名称已存在");
}

View File

@@ -28,7 +28,7 @@ import com.fastbee.system.service.ISysDictTypeService;
/**
* 数据字典信息
*
*
* @author ruoyi
*/
@Api(tags = "字典管理")
@@ -80,7 +80,7 @@ public class SysDictTypeController extends BaseController
@PostMapping
public AjaxResult add(@Validated @RequestBody SysDictType dict)
{
if (UserConstants.NOT_UNIQUE.equals(dictTypeService.checkDictTypeUnique(dict)))
if (!dictTypeService.checkDictTypeUnique(dict))
{
return error("新增字典'" + dict.getDictName() + "'失败,字典类型已存在");
}
@@ -97,7 +97,7 @@ public class SysDictTypeController extends BaseController
@PutMapping
public AjaxResult edit(@Validated @RequestBody SysDictType dict)
{
if (UserConstants.NOT_UNIQUE.equals(dictTypeService.checkDictTypeUnique(dict)))
if (!dictTypeService.checkDictTypeUnique(dict))
{
return error("修改字典'" + dict.getDictName() + "'失败,字典类型已存在");
}

View File

@@ -1,32 +1,30 @@
package com.fastbee.web.controller.system;
import java.util.List;
import java.util.Set;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import com.fastbee.common.constant.Constants;
import com.fastbee.common.core.domain.AjaxResult;
import com.fastbee.common.core.domain.entity.SysMenu;
import com.fastbee.common.core.domain.entity.SysUser;
import com.fastbee.common.core.domain.model.LoginBody;
import com.fastbee.common.core.domain.model.LoginUser;
import com.fastbee.common.utils.SecurityUtils;
import com.fastbee.framework.web.service.SysLoginService;
import com.fastbee.framework.web.service.SysPermissionService;
import com.fastbee.framework.web.service.TokenService;
import com.fastbee.system.service.ISysMenuService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Set;
/**
* 登录验证
*
*
* @author ruoyi
*/
@Api(tags = "登录验证")
@RestController
public class SysLoginController
{
@@ -38,16 +36,16 @@ public class SysLoginController
@Autowired
private SysPermissionService permissionService;
@Value("${server.broker.enabled}")
private Boolean enabled;
@Autowired
private TokenService tokenService;
/**
* 登录方法
*
*
* @param loginBody 登录信息
* @return 结果
*/
@ApiOperation("用户登录")
@PostMapping("/login")
public AjaxResult login(@RequestBody LoginBody loginBody)
{
@@ -61,32 +59,35 @@ public class SysLoginController
/**
* 获取用户信息
*
*
* @return 用户信息
*/
@ApiOperation("获取用户信息")
@GetMapping("getInfo")
public AjaxResult getInfo()
{
SysUser user = SecurityUtils.getLoginUser().getUser();
LoginUser loginUser = SecurityUtils.getLoginUser();
SysUser user = loginUser.getUser();
// 角色集合
Set<String> roles = permissionService.getRolePermission(user);
// 权限集合
Set<String> permissions = permissionService.getMenuPermission(user);
if (!loginUser.getPermissions().equals(permissions))
{
loginUser.setPermissions(permissions);
tokenService.refreshToken(loginUser);
}
AjaxResult ajax = AjaxResult.success();
ajax.put("user", user);
ajax.put("roles", roles);
ajax.put("permissions", permissions);
ajax.put("mqtt",enabled);
return ajax;
}
/**
* 获取路由信息
*
*
* @return 路由信息
*/
@ApiOperation("获取路由信息")
@GetMapping("getRouters")
public AjaxResult getRouters()
{

View File

@@ -26,7 +26,7 @@ import com.fastbee.system.service.ISysMenuService;
/**
* 菜单信息
*
*
* @author ruoyi
*/
@Api(tags = "菜单管理")
@@ -94,7 +94,7 @@ public class SysMenuController extends BaseController
@PostMapping
public AjaxResult add(@Validated @RequestBody SysMenu menu)
{
if (UserConstants.NOT_UNIQUE.equals(menuService.checkMenuNameUnique(menu)))
if (!menuService.checkMenuNameUnique(menu))
{
return error("新增菜单'" + menu.getMenuName() + "'失败,菜单名称已存在");
}
@@ -115,7 +115,7 @@ public class SysMenuController extends BaseController
@PutMapping
public AjaxResult edit(@Validated @RequestBody SysMenu menu)
{
if (UserConstants.NOT_UNIQUE.equals(menuService.checkMenuNameUnique(menu)))
if (!menuService.checkMenuNameUnique(menu))
{
return error("修改菜单'" + menu.getMenuName() + "'失败,菜单名称已存在");
}
@@ -150,4 +150,4 @@ public class SysMenuController extends BaseController
}
return toAjax(menuService.deleteMenuById(menuId));
}
}
}

View File

@@ -28,7 +28,7 @@ import com.fastbee.system.service.ISysPostService;
/**
* 岗位信息操作处理
*
*
* @author ruoyi
*/
@Api(tags = "岗位管理")
@@ -83,11 +83,11 @@ public class SysPostController extends BaseController
@PostMapping
public AjaxResult add(@Validated @RequestBody SysPost post)
{
if (UserConstants.NOT_UNIQUE.equals(postService.checkPostNameUnique(post)))
if (!postService.checkPostNameUnique(post))
{
return error("新增岗位'" + post.getPostName() + "'失败,岗位名称已存在");
}
else if (UserConstants.NOT_UNIQUE.equals(postService.checkPostCodeUnique(post)))
else if (!postService.checkPostCodeUnique(post))
{
return error("新增岗位'" + post.getPostName() + "'失败,岗位编码已存在");
}
@@ -104,11 +104,11 @@ public class SysPostController extends BaseController
@PutMapping
public AjaxResult edit(@Validated @RequestBody SysPost post)
{
if (UserConstants.NOT_UNIQUE.equals(postService.checkPostNameUnique(post)))
if (!postService.checkPostNameUnique(post))
{
return error("修改岗位'" + post.getPostName() + "'失败,岗位名称已存在");
}
else if (UserConstants.NOT_UNIQUE.equals(postService.checkPostCodeUnique(post)))
else if (!postService.checkPostCodeUnique(post))
{
return error("修改岗位'" + post.getPostName() + "'失败,岗位编码已存在");
}

View File

@@ -30,6 +30,7 @@ import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
import java.util.Map;
/**
* 个人信息 业务处理
@@ -78,29 +79,22 @@ public class SysProfileController extends BaseController
public AjaxResult updateProfile(@RequestBody SysUser user)
{
LoginUser loginUser = getLoginUser();
SysUser sysUser = loginUser.getUser();
user.setUserName(sysUser.getUserName());
if (StringUtils.isNotEmpty(user.getPhonenumber())
&& UserConstants.NOT_UNIQUE.equals(userService.checkPhoneUnique(user)))
SysUser currentUser = loginUser.getUser();
currentUser.setNickName(user.getNickName());
currentUser.setEmail(user.getEmail());
currentUser.setPhonenumber(user.getPhonenumber());
currentUser.setSex(user.getSex());
if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(currentUser))
{
return error("修改用户'" + user.getUserName() + "'失败,手机号码已存在");
}
if (StringUtils.isNotEmpty(user.getEmail())
&& UserConstants.NOT_UNIQUE.equals(userService.checkEmailUnique(user)))
if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(currentUser))
{
return error("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在");
return error("修改用户'" + loginUser.getUsername() + "'失败,邮箱账号已存在");
}
user.setUserId(sysUser.getUserId());
user.setPassword(null);
user.setAvatar(null);
user.setDeptId(null);
if (userService.updateUserProfile(user) > 0)
if (userService.updateUserProfile(currentUser) > 0)
{
// 更新缓存用户信息
sysUser.setNickName(user.getNickName());
sysUser.setPhonenumber(user.getPhonenumber());
sysUser.setEmail(user.getEmail());
sysUser.setSex(user.getSex());
tokenService.setLoginUser(loginUser);
return success();
}
@@ -113,8 +107,10 @@ public class SysProfileController extends BaseController
@ApiOperation("重置密码")
@Log(title = "个人信息", businessType = BusinessType.UPDATE)
@PutMapping("/updatePwd")
public AjaxResult updatePwd(String oldPassword, String newPassword)
public AjaxResult updatePwd(@RequestBody Map<String, String> params)
{
String oldPassword = params.get("oldPassword");
String newPassword = params.get("newPassword");
LoginUser loginUser = getLoginUser();
String userName = loginUser.getUsername();
String password = loginUser.getPassword();
@@ -126,10 +122,11 @@ public class SysProfileController extends BaseController
{
return error("新密码不能与旧密码相同");
}
if (userService.resetUserPwd(userName, SecurityUtils.encryptPassword(newPassword)) > 0)
newPassword = SecurityUtils.encryptPassword(newPassword);
if (userService.resetUserPwd(userName, newPassword) > 0)
{
// 更新缓存用户密码
loginUser.getUser().setPassword(SecurityUtils.encryptPassword(newPassword));
loginUser.getUser().setPassword(newPassword);
tokenService.setLoginUser(loginUser);
return success();
}

View File

@@ -37,7 +37,7 @@ import com.fastbee.system.service.ISysUserService;
/**
* 角色信息
*
*
* @author ruoyi
*/
@Api(tags = "角色管理")
@@ -102,11 +102,11 @@ public class SysRoleController extends BaseController
@PostMapping
public AjaxResult add(@Validated @RequestBody SysRole role)
{
if (UserConstants.NOT_UNIQUE.equals(roleService.checkRoleNameUnique(role)))
if (!roleService.checkRoleNameUnique(role))
{
return error("新增角色'" + role.getRoleName() + "'失败,角色名称已存在");
}
else if (UserConstants.NOT_UNIQUE.equals(roleService.checkRoleKeyUnique(role)))
else if (!roleService.checkRoleKeyUnique(role))
{
return error("新增角色'" + role.getRoleName() + "'失败,角色权限已存在");
}
@@ -126,24 +126,24 @@ public class SysRoleController extends BaseController
{
roleService.checkRoleAllowed(role);
roleService.checkRoleDataScope(role.getRoleId());
if (UserConstants.NOT_UNIQUE.equals(roleService.checkRoleNameUnique(role)))
if (!roleService.checkRoleNameUnique(role))
{
return error("修改角色'" + role.getRoleName() + "'失败,角色名称已存在");
}
else if (UserConstants.NOT_UNIQUE.equals(roleService.checkRoleKeyUnique(role)))
else if (!roleService.checkRoleKeyUnique(role))
{
return error("修改角色'" + role.getRoleName() + "'失败,角色权限已存在");
}
role.setUpdateBy(getUsername());
if (roleService.updateRole(role) > 0)
{
// 更新缓存用户权限
LoginUser loginUser = getLoginUser();
if (StringUtils.isNotNull(loginUser.getUser()) && !loginUser.getUser().isAdmin())
{
loginUser.setPermissions(permissionService.getMenuPermission(loginUser.getUser()));
loginUser.setUser(userService.selectUserByUserName(loginUser.getUser().getUserName()));
loginUser.setPermissions(permissionService.getMenuPermission(loginUser.getUser()));
tokenService.setLoginUser(loginUser);
}
return success();

View File

@@ -1,26 +1,6 @@
package com.fastbee.web.controller.system;
import java.util.List;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletResponse;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import com.fastbee.common.annotation.Log;
import com.fastbee.common.constant.UserConstants;
import com.fastbee.common.core.controller.BaseController;
import com.fastbee.common.core.domain.AjaxResult;
import com.fastbee.common.core.domain.entity.SysDept;
@@ -35,13 +15,22 @@ import com.fastbee.system.service.ISysDeptService;
import com.fastbee.system.service.ISysPostService;
import com.fastbee.system.service.ISysRoleService;
import com.fastbee.system.service.ISysUserService;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.stream.Collectors;
/**
* 用户信息
*
*
* @author ruoyi
*/
@Api(tags = "用户管理")
@RestController
@RequestMapping("/system/user")
public class SysUserController extends BaseController
@@ -61,7 +50,6 @@ public class SysUserController extends BaseController
/**
* 获取用户列表
*/
@ApiOperation("获取用户分页列表")
@PreAuthorize("@ss.hasPermi('system:user:list')")
@GetMapping("/list")
public TableDataInfo list(SysUser user)
@@ -71,7 +59,6 @@ public class SysUserController extends BaseController
return getDataTable(list);
}
@ApiOperation("导出用户列表")
@Log(title = "用户管理", businessType = BusinessType.EXPORT)
@PreAuthorize("@ss.hasPermi('system:user:export')")
@PostMapping("/export")
@@ -82,7 +69,6 @@ public class SysUserController extends BaseController
util.exportExcel(response, list, "用户数据");
}
@ApiOperation("批量导入用户")
@Log(title = "用户管理", businessType = BusinessType.IMPORT)
@PreAuthorize("@ss.hasPermi('system:user:import')")
@PostMapping("/importData")
@@ -95,8 +81,6 @@ public class SysUserController extends BaseController
return success(message);
}
@ApiOperation("下载用户导入模板")
@PostMapping("/importTemplate")
public void importTemplate(HttpServletResponse response)
{
@@ -107,46 +91,44 @@ public class SysUserController extends BaseController
/**
* 根据用户编号获取详细信息
*/
@ApiOperation("根据用户编号获取详细信息")
@PreAuthorize("@ss.hasPermi('system:user:query')")
@GetMapping(value = { "/", "/{userId}" })
public AjaxResult getInfo(@PathVariable(value = "userId", required = false) Long userId)
{
userService.checkUserDataScope(userId);
AjaxResult ajax = AjaxResult.success();
List<SysRole> roles = roleService.selectRoleAll();
ajax.put("roles", SysUser.isAdmin(userId) ? roles : roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList()));
ajax.put("posts", postService.selectPostAll());
if (StringUtils.isNotNull(userId))
{
userService.checkUserDataScope(userId);
SysUser sysUser = userService.selectUserById(userId);
ajax.put(AjaxResult.DATA_TAG, sysUser);
ajax.put("postIds", postService.selectPostListByUserId(userId));
ajax.put("roleIds", sysUser.getRoles().stream().map(SysRole::getRoleId).collect(Collectors.toList()));
}
List<SysRole> roles = roleService.selectRoleAll();
ajax.put("roles", SysUser.isAdmin(userId) ? roles : roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList()));
ajax.put("posts", postService.selectPostAll());
return ajax;
}
/**
* 新增用户
*/
@ApiOperation("新增用户")
@PreAuthorize("@ss.hasPermi('system:user:add')")
@Log(title = "用户管理", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@Validated @RequestBody SysUser user)
{
if (UserConstants.NOT_UNIQUE.equals(userService.checkUserNameUnique(user)))
deptService.checkDeptDataScope(user.getDeptId());
roleService.checkRoleDataScope(user.getRoleIds());
if (!userService.checkUserNameUnique(user))
{
return error("新增用户'" + user.getUserName() + "'失败,登录账号已存在");
}
else if (StringUtils.isNotEmpty(user.getPhonenumber())
&& UserConstants.NOT_UNIQUE.equals(userService.checkPhoneUnique(user)))
else if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user))
{
return error("新增用户'" + user.getUserName() + "'失败,手机号码已存在");
}
else if (StringUtils.isNotEmpty(user.getEmail())
&& UserConstants.NOT_UNIQUE.equals(userService.checkEmailUnique(user)))
else if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user))
{
return error("新增用户'" + user.getUserName() + "'失败,邮箱账号已存在");
}
@@ -158,7 +140,6 @@ public class SysUserController extends BaseController
/**
* 修改用户
*/
@ApiOperation("修改用户")
@PreAuthorize("@ss.hasPermi('system:user:edit')")
@Log(title = "用户管理", businessType = BusinessType.UPDATE)
@PutMapping
@@ -166,17 +147,17 @@ public class SysUserController extends BaseController
{
userService.checkUserAllowed(user);
userService.checkUserDataScope(user.getUserId());
if (UserConstants.NOT_UNIQUE.equals(userService.checkUserNameUnique(user)))
deptService.checkDeptDataScope(user.getDeptId());
roleService.checkRoleDataScope(user.getRoleIds());
if (!userService.checkUserNameUnique(user))
{
return error("修改用户'" + user.getUserName() + "'失败,登录账号已存在");
}
else if (StringUtils.isNotEmpty(user.getPhonenumber())
&& UserConstants.NOT_UNIQUE.equals(userService.checkPhoneUnique(user)))
else if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user))
{
return error("修改用户'" + user.getUserName() + "'失败,手机号码已存在");
}
else if (StringUtils.isNotEmpty(user.getEmail())
&& UserConstants.NOT_UNIQUE.equals(userService.checkEmailUnique(user)))
else if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user))
{
return error("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在");
}
@@ -187,7 +168,6 @@ public class SysUserController extends BaseController
/**
* 删除用户
*/
@ApiOperation("删除用户")
@PreAuthorize("@ss.hasPermi('system:user:remove')")
@Log(title = "用户管理", businessType = BusinessType.DELETE)
@DeleteMapping("/{userIds}")
@@ -203,7 +183,6 @@ public class SysUserController extends BaseController
/**
* 重置密码
*/
@ApiOperation("重置用户密码")
@PreAuthorize("@ss.hasPermi('system:user:resetPwd')")
@Log(title = "用户管理", businessType = BusinessType.UPDATE)
@PutMapping("/resetPwd")
@@ -219,7 +198,6 @@ public class SysUserController extends BaseController
/**
* 状态修改
*/
@ApiOperation("修改用户状态")
@PreAuthorize("@ss.hasPermi('system:user:edit')")
@Log(title = "用户管理", businessType = BusinessType.UPDATE)
@PutMapping("/changeStatus")
@@ -234,7 +212,6 @@ public class SysUserController extends BaseController
/**
* 根据用户编号获取授权角色
*/
@ApiOperation("根据用户编号获取授权角色")
@PreAuthorize("@ss.hasPermi('system:user:query')")
@GetMapping("/authRole/{userId}")
public AjaxResult authRole(@PathVariable("userId") Long userId)
@@ -250,13 +227,13 @@ public class SysUserController extends BaseController
/**
* 用户授权角色
*/
@ApiOperation("为用户授权角色")
@PreAuthorize("@ss.hasPermi('system:user:edit')")
@Log(title = "用户管理", businessType = BusinessType.GRANT)
@PutMapping("/authRole")
public AjaxResult insertAuthRole(Long userId, Long[] roleIds)
{
userService.checkUserDataScope(userId);
roleService.checkRoleDataScope(roleIds);
userService.insertUserAuth(userId, roleIds);
return success();
}
@@ -264,7 +241,6 @@ public class SysUserController extends BaseController
/**
* 获取部门树列表
*/
@ApiOperation("获取部门树列表")
@PreAuthorize("@ss.hasPermi('system:user:list')")
@GetMapping("/deptTree")
public AjaxResult deptTree(SysDept dept)

View File

@@ -4,6 +4,7 @@ import com.fastbee.common.annotation.Anonymous;
import com.fastbee.iot.mapper.DeviceMapper;
import com.fastbee.iot.model.DeviceRelateAlertLogVO;
import com.fastbee.iot.service.IDeviceService;
import io.swagger.annotations.Api;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@@ -22,6 +23,7 @@ import java.util.stream.Collectors;
* @date 2023-09-13 11:42
*/
@Anonymous
@Api("测试方法")
@RestController
@RequestMapping("/test2")
public class TestController2 {

View File

@@ -1,57 +1,50 @@
# 数据源配置
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: com.mysql.cj.jdbc.Driver
druid:
# 主库数据源
master:
url: jdbc:mysql://localhost/fastbee?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: root
password: 123456
# 从库数据源
slave:
enabled: false # 从数据源开关/默认关闭
url:
username:
password:
initialSize: 5 # 初始连接数
minIdle: 10 # 最小连接池数量
maxActive: 20 # 最大连接池数量
maxWait: 60000 # 配置获取连接等待超时的时间
timeBetweenEvictionRunsMillis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
minEvictableIdleTimeMillis: 300000 # 配置一个连接在池中最小生存的时间,单位是毫秒
maxEvictableIdleTimeMillis: 900000 # 配置一个连接在池中最大生存的时间,单位是毫秒
validationQuery: SELECT 1 FROM DUAL # 配置检测连接是否有效
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
webStatFilter:
enabled: true
statViewServlet:
enabled: true
# 设置白名单,不填则允许所有访问
allow:
url-pattern: /druid/*
# 控制台管理用户名和密码
login-username: fastbee
login-password: fastbee
filter:
stat:
enabled: true
# 慢SQL记录
log-slow-sql: true
slow-sql-millis: 1000
merge-sql: true
wall:
config:
multi-statement-allow: true
dynamic:
druid:
initial-size: 5
min-idle: 10
max-wait: 60000
max-active: 20
timeBetweenEvictionRunsMillis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
minEvictableIdleTimeMillis: 300000 # 配置一个连接在池中最小生存的时间,单位是毫秒
maxEvictableIdleTimeMillis: 900000 # 配置一个连接在池中最大生存的时间,单位是毫秒
validation-query: 'SELECT 1'
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
datasource:
master:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost/fastbee?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: root
password: fastbee
druid:
filters: stat,wall
stat:
# 慢SQL记录
log-slow-sql: true
slow-sql-millis: 1000
merge-sql: true
wall:
none-base-statement-allow: true
# slave:
# type: com.alibaba.druid.pool.DruidDataSource
# driver-class-name: com.mysql.cj.jdbc.Driver
# url: jdbc:mysql://localhost:3306/fastbee1?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
# username: root
# password: fastbee
# redis 配置
redis:
host: localhost # 地址
port: 6379 # 端口默认为6379
database: 1 # 数据库索引
password: 123456 # 密码
database: 15 # 数据库索引
password: fastbee # 密码
timeout: 10s # 连接超时时间
lettuce:
pool:
@@ -59,20 +52,10 @@ spring:
max-idle: 8 # 连接池中的最大空闲连接
max-active: 8 # 连接池的最大数据库连接数
max-wait: -1ms # 连接池最大阻塞等待时间(使用负值表示没有限制)
# mqtt 配置
mqtt:
username: fastbee # 账号
password: fastbee # 密码
host-url: tcp://localhost:1883 # mqtt连接tcp地址
client-id: ${random.int} # 客户端Id不能相同采用随机数 ${random.value}
default-topic: test # 默认主题
timeout: 30 # 超时时间
keepalive: 30 # 保持连接
clearSession: true # 清除会话(设置为false,断开连接,重连后使用原来的会话 保留订阅的主题,能接收离线期间的消息)
# sip 配置
sip:
enabled: true # 是否启用视频监控SIPtrue为启用
enabled: false # 是否启用视频监控SIPtrue为启用
## 本地调试时绑定网卡局域网IP设备在同一局域网设备接入IP填写绑定IP
## 部署服务端时默认绑定容器IP设备接入IP填写服务器公网IP
#ip: 177.7.0.13

View File

@@ -1,52 +1,42 @@
# 数据源配置
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: com.mysql.cj.jdbc.Driver
druid:
# 主库数据源
master:
url: jdbc:mysql://177.7.0.11/fastbee?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: root
password: fastbee
# 从库数据源
slave:
enabled: false # 从数据源开关/默认关闭
url:
username:
password:
dynamic:
druid:
initial-size: 5
min-idle: 10
max-wait: 60000
max-active: 20
timeBetweenEvictionRunsMillis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
minEvictableIdleTimeMillis: 300000 # 配置一个连接在池中最小生存的时间,单位是毫秒
maxEvictableIdleTimeMillis: 900000 # 配置一个连接在池中最大生存的时间,单位是毫秒
validation-query: 'SELECT 1'
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
datasource:
master:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://177.7.0.11/fastbee?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: root
password: fastbee
druid:
filters: stat,wall
stat:
# 慢SQL记录
log-slow-sql: true
slow-sql-millis: 1000
merge-sql: true
wall:
none-base-statement-allow: true
# slave:
# type: com.alibaba.druid.pool.DruidDataSource
# driver-class-name: com.mysql.cj.jdbc.Driver
# url: jdbc:mysql://localhost:3306/fastbee1?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
# username: root
# password: fastbee
initialSize: 5 # 初始连接数
minIdle: 10 # 最小连接池数量
maxActive: 20 # 最大连接池数量
maxWait: 60000 # 配置获取连接等待超时的时间
timeBetweenEvictionRunsMillis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
minEvictableIdleTimeMillis: 300000 # 配置一个连接在池中最小生存的时间,单位是毫秒
maxEvictableIdleTimeMillis: 900000 # 配置一个连接在池中最大生存的时间,单位是毫秒
validationQuery: SELECT 1 FROM DUAL # 配置检测连接是否有效
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
webStatFilter:
enabled: true
statViewServlet:
enabled: true
# 设置白名单,不填则允许所有访问
allow:
url-pattern: /druid/*
# 控制台管理用户名和密码
login-username: fastbee
login-password: fastbee
filter:
stat:
enabled: true
# 慢SQL记录
log-slow-sql: true
slow-sql-millis: 1000
merge-sql: true
wall:
config:
multi-statement-allow: true
# redis 配置
redis:
host: 177.7.0.10 # 地址
@@ -60,16 +50,6 @@ spring:
max-idle: 8 # 连接池中的最大空闲连接
max-active: 8 # 连接池的最大数据库连接数
max-wait: -1ms # 连接池最大阻塞等待时间(使用负值表示没有限制)
# mqtt 配置
mqtt:
username: fastbee # 账号(仅用于后端自认证)
password: fastbee # 密码(仅用于后端自认证)
host-url: tcp://177.7.0.12:1883 # 连接消息服务器地址
client-id: ${random.int} # 客户端Id不能相同采用随机数 ${random.value}
default-topic: test # 默认主题
timeout: 30 # 超时时间
keepalive: 30 # 保持连接
clearSession: true # 清除会话(设置为false,断开连接,重连后使用原来的会话 保留订阅的主题,能接收离线期间的消息)
# sip 配置
sip:

View File

@@ -0,0 +1,131 @@
# 数据源配置
spring:
datasource:
dynamic:
druid:
initial-size: 5
min-idle: 10
max-wait: 60000
max-active: 20
timeBetweenEvictionRunsMillis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
minEvictableIdleTimeMillis: 300000 # 配置一个连接在池中最小生存的时间,单位是毫秒
maxEvictableIdleTimeMillis: 900000 # 配置一个连接在池中最大生存的时间,单位是毫秒
validation-query: 'SELECT 1'
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
datasource:
master:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: dm.jdbc.driver.DmDriver
url: jdbc:dm://192.168.5.12:5236/OPENSOURCE&useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
username: OPENSOURCE
password: fastbee@123
druid:
filters: stat
stat:
# 慢SQL记录
log-slow-sql: true
slow-sql-millis: 1000
merge-sql: true
wall:
none-base-statement-allow: true
# sqlServer: # 配置 SQLServer 数据源
# type: com.alibaba.druid.pool.DruidDataSource
# driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver
# url: jdbc:sqlserver://localhost:1433;databaseName=fastbee
# username: sa
# password: fastbee@123
# postgres: # 配置 postgres 数据源
# type: com.alibaba.druid.pool.DruidDataSource
# driver-class-name: org.postgresql.Driver
# url: jdbc:postgresql://101.33.237.12:5432/fastbee?useUnicode=true&characterEncoding=utf8&useSSL=true&autoReconnect=true&reWriteBatchedInserts=true
# username: root
# password: fastbee@123
# dameng: # 配置达梦数据源
# type: com.alibaba.druid.pool.DruidDataSource
# driver-class-name: dm.jdbc.driver.DmDriver
# url: jdbc:dm://192.168.5.28:5236/fastbee&useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
# username: root
# password: fastbee@123
# druid:
# filters: stat
# slave:
# type: com.alibaba.druid.pool.DruidDataSource
# driver-class-name: com.mysql.cj.jdbc.Driver
# url: jdbc:mysql://localhost:3306/fastbee1?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
# username: root
# password: fastbee
# slave:
# type: com.alibaba.druid.pool.DruidDataSource
# driver-class-name: com.mysql.cj.jdbc.Driver
# url: jdbc:mysql://localhost:3306/fastbee1?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
# username: root
# password: fastbee
# redis 配置
redis:
host: 81.71.97.58 # 地址
port: 6379 # 端口默认为6379
database: 11 # 数据库索引
password: fastbee # 密码
timeout: 10s # 连接超时时间
lettuce:
pool:
min-idle: 0 # 连接池中的最小空闲连接
max-idle: 8 # 连接池中的最大空闲连接
max-active: 8 # 连接池的最大数据库连接数
max-wait: -1ms # 连接池最大阻塞等待时间(使用负值表示没有限制)
# sip 配置
sip:
enabled: false # 是否启用视频监控SIPtrue为启用
## 本地调试时绑定网卡局域网IP设备在同一局域网设备接入IP填写绑定IP
## 部署服务端时默认绑定容器IP设备接入IP填写服务器公网IP
#ip: 177.7.0.13
ip: 192.168.5.27
port: 5061 # SIP端口(保持默认)
domain: 3402000000 # 由省级、市级、区级、基层编号组成
id: 34020000002000000001 # 同上,另外增加编号,(可保持默认)
password: 12345678 # 监控设备接入的密码
# 日志配置
logging:
level:
com.fastbee: debug
org.springframework: warn
# Swagger配置
swagger:
enabled: true # 是否开启swagger
pathMapping: /dev-api # 请求前缀
liteflow:
rule-source-ext-data-map:
# 应用名称,规则链和脚本组件名称需要一致,不要修改
applicationName: fastbee
#是否开启SQL日志
sqlLogEnabled: true
# 规则多时,启用快速加载模式
fast-load: false
#是否开启SQL数据轮询自动刷新机制 默认不开启
pollingEnabled: false
pollingIntervalSeconds: 60
pollingStartSeconds: 60
#以下是chain表的配置
chainTableName: iot_scene
chainApplicationNameField: application_name
chainNameField: chain_name
elDataField: el_data
chainEnableField: enable
#以下是script表的配置
scriptTableName: iot_script
scriptApplicationNameField: application_name
scriptIdField: script_id
scriptNameField: script_name
scriptDataField: script_data
scriptTypeField: script_type
scriptLanguageField: script_language
scriptEnableField: enable

View File

@@ -1,11 +1,11 @@
# 项目相关配置
fastbee:
name: fastbee # 名称
version: 3.8.5 # 版本
copyrightYear: 2023 # 版权年份
version: 2.1.0 # 版本
copyrightYear: 2024 # 版权年份
demoEnabled: true # 实例演示开关
# 文件路径以uploadPath结尾 示例( Windows配置 D:/uploadPathLinux配置 /uploadPath
profile: /uploadPath
profile: D:/uploadPath
addressEnabled: true # 获取ip地址开关
captchaType: math # 验证码类型 math 数组计算 char 字符验证
@@ -22,19 +22,17 @@ server:
min-spare: 100 # Tomcat启动初始化的线程数默认值10
# 基于netty的服务器
broker:
must-pass: false # 客户端连接是否需要密码
enabled: true # mqttBroker类型选择, true: 基于netty的mqttBroker和webSocket false: emq的mqttBroker
broker-node: node1
port: 1883
websocket-port: 8083
websocket-path: /mqtt
keep-alive: 70 # 默认的全部客户端心跳上传时间
keep-alive: 70 # 默认的全部客户端心跳上传时间
# Spring配置
spring:
# 环境配置dev=开发环境prod=生产环境
profiles:
active: prod # 环境配置dev=开发环境prod=生产环境
active: dev # 环境配置dev=开发环境prod=生产环境
# 资源信息
messages:
# 国际化资源文件路径
@@ -55,12 +53,20 @@ spring:
max-size: 200 # 最大连接数
queue-capacity: 3000 # 最大容量
keep-alive: 60
#集群配置
cluster:
enable: true
type: redis
datasource:
druid:
webStatFilter:
enabled: true
stat-view-servlet:
enabled: true
allow:
url-pattern: /druid/*
loginUsername: fastbee
loginPassword: fastbee
dynamic:
primary: master
strict: false
lazy: true
# 用户配置
user:

View File

@@ -9,13 +9,14 @@ user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定{1}分
user.password.delete=对不起,您的账号已被删除
user.blocked=用户已封禁,请联系管理员
role.blocked=角色已封禁,请联系管理员
login.blocked=很遗憾访问IP已被列入系统黑名单
user.logout.success=退出成功
length.not.valid=长度必须在{min}到{max}个字符之间
user.username.not.valid=* 2到20个汉字、字母、数字或下划线组成且必须以非数字开头
user.password.not.valid=* 5-50个字符
user.email.not.valid=邮箱格式错误
user.mobile.phone.number.not.valid=手机号格式错误
user.login.success=登录成功

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 日志存放路径 -->
<property name="log.path" value="/logs" />
<property name="log.path" scope="context" value="D:/logs" />
<!-- 日志输出格式 -->
<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />
@@ -95,6 +95,24 @@
</encoder>
</appender>
<!-- 规则引擎日志输出 -->
<appender name="script" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/rule/script.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 按天回滚 daily -->
<fileNamePattern>${log.path}/rule/script.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志最大的历史 3天 -->
<maxHistory>3</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%method,%line] - %msg%n</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
</appender>
<!-- 系统模块日志级别控制 -->
<logger name="com.fastbee" level="debug" />
<!-- Spring日志级别控制 -->
@@ -115,4 +133,10 @@
<logger name="sys-user" level="info">
<appender-ref ref="sys-user"/>
</logger>
</configuration>
<!--规则引擎日志-->
<logger name="script" level="info">
<appender-ref ref="script"/>
</logger>
</configuration>

View File

@@ -14,7 +14,9 @@ PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
<!-- 指定 MyBatis 所用日志的具体实现 -->
<setting name="logImpl" value="SLF4J" />
<!-- 使用驼峰命名法转换字段 -->
<!-- <setting name="mapUnderscoreToCamelCase" value="true"/> -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<setting name="autoMappingBehavior" value="FULL"/>
<setting name="autoMappingUnknownColumnBehavior" value="NONE"/>
</settings>
</configuration>

View File

@@ -5,7 +5,7 @@
<parent>
<artifactId>fastbee</artifactId>
<groupId>com.fastbee</groupId>
<version>3.8.5</version>
<version>${fastbee.version}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -40,6 +40,7 @@
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
@@ -74,7 +75,6 @@
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.5.2</version>
</dependency>
<!-- 阿里JSON解析器 -->
@@ -150,20 +150,11 @@
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
<version>1.6.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.eclipse.paho</groupId>
<artifactId>org.eclipse.paho.client.mqttv3</artifactId>
<version>1.2.5</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.eclipse.paho</groupId>
<artifactId>org.eclipse.paho.client.mqttv3</artifactId>
<version>1.2.5</version>
<scope>compile</scope>
</dependency>
<dependency>

View File

@@ -1,17 +1,18 @@
package com.fastbee.common.annotation;
import com.fastbee.common.utils.poi.ExcelHandlerAdapter;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.math.BigDecimal;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;
import com.fastbee.common.utils.poi.ExcelHandlerAdapter;
/**
* 自定义导出Excel数据注解
*
*
* @author ruoyi
*/
@Retention(RetentionPolicy.RUNTIME)
@@ -59,12 +60,12 @@ public @interface Excel
public int roundingMode() default BigDecimal.ROUND_HALF_EVEN;
/**
* 导出时在excel中每个列的高度 单位为字符
* 导出时在excel中每个列的高度
*/
public double height() default 14;
/**
* 导出时在excel中每个列的宽 单位为字符
* 导出时在excel中每个列的宽
*/
public double width() default 16;
@@ -83,11 +84,21 @@ public @interface Excel
*/
public String prompt() default "";
/**
* 是否允许内容换行
*/
public boolean wrapText() default false;
/**
* 设置只能选择不能输入的列内容.
*/
public String[] combo() default {};
/**
* 是否从字典读数据到combo,默认不读取,如读取需要设置dictType注解.
*/
public boolean comboReadDict() default false;
/**
* 是否需要纵向合并单元格,应对需求:含有list集合单元格)
*/
@@ -114,7 +125,7 @@ public @interface Excel
public ColumnType cellType() default ColumnType.STRING;
/**
* 导出列头背景色
* 导出列头背景
*/
public IndexedColors headerBackgroundColor() default IndexedColors.GREY_50_PERCENT;
@@ -124,7 +135,7 @@ public @interface Excel
public IndexedColors headerColor() default IndexedColors.WHITE;
/**
* 导出单元格背景色
* 导出单元格背景
*/
public IndexedColors backgroundColor() default IndexedColors.WHITE;
@@ -171,7 +182,7 @@ public @interface Excel
public enum ColumnType
{
NUMERIC(0), STRING(1), IMAGE(2);
NUMERIC(0), STRING(1), IMAGE(2), TEXT(3);
private final int value;
ColumnType(int value)
@@ -184,4 +195,4 @@ public @interface Excel
return this.value;
}
}
}
}

View File

@@ -1,16 +1,13 @@
package com.fastbee.common.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.fastbee.common.enums.BusinessType;
import com.fastbee.common.enums.OperatorType;
import java.lang.annotation.*;
/**
* 自定义操作日志记录注解
*
*
* @author ruoyi
*
*/
@@ -20,7 +17,7 @@ import com.fastbee.common.enums.OperatorType;
public @interface Log
{
/**
* 模块
* 模块
*/
public String title() default "";
@@ -43,4 +40,9 @@ public @interface Log
* 是否保存响应的参数
*/
public boolean isSaveResponseData() default true;
/**
* 排除指定的请求参数
*/
public String[] excludeParamNames() default {};
}

View File

@@ -0,0 +1,24 @@
package com.fastbee.common.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fastbee.common.config.serializer.SensitiveJsonSerializer;
import com.fastbee.common.enums.DesensitizedType;
/**
* 数据脱敏注解
*
* @author ruoyi
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@JacksonAnnotationsInside
@JsonSerialize(using = SensitiveJsonSerializer.class)
public @interface Sensitive
{
DesensitizedType desensitizedType();
}

View File

@@ -0,0 +1,67 @@
package com.fastbee.common.config.serializer;
import java.io.IOException;
import java.util.Objects;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
import com.fastbee.common.annotation.Sensitive;
import com.fastbee.common.core.domain.model.LoginUser;
import com.fastbee.common.enums.DesensitizedType;
import com.fastbee.common.utils.SecurityUtils;
/**
* 数据脱敏序列化过滤
*
* @author ruoyi
*/
public class SensitiveJsonSerializer extends JsonSerializer<String> implements ContextualSerializer
{
private DesensitizedType desensitizedType;
@Override
public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException
{
if (desensitization())
{
gen.writeString(desensitizedType.desensitizer().apply(value));
}
else
{
gen.writeString(value);
}
}
@Override
public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property)
throws JsonMappingException
{
Sensitive annotation = property.getAnnotation(Sensitive.class);
if (Objects.nonNull(annotation) && Objects.equals(String.class, property.getType().getRawClass()))
{
this.desensitizedType = annotation.desensitizedType();
return this;
}
return prov.findValueSerializer(property.getType(), property);
}
/**
* 是否需要脱敏处理
*/
private boolean desensitization()
{
try
{
LoginUser securityUser = SecurityUtils.getLoginUser();
// 管理员不脱敏
return !securityUser.getUser().isAdmin();
}
catch (Exception e)
{
return true;
}
}
}

View File

@@ -2,9 +2,11 @@ package com.fastbee.common.constant;
import io.jsonwebtoken.Claims;
import java.util.Locale;
/**
* 通用常量信息
*
*
* @author ruoyi
*/
public class Constants
@@ -19,6 +21,11 @@ public class Constants
*/
public static final String GBK = "GBK";
/**
* 系统语言
*/
public static final Locale DEFAULT_LOCALE = Locale.SIMPLIFIED_CHINESE;
/**
* www主域
*/
@@ -63,7 +70,27 @@ public class Constants
* 登录失败
*/
public static final String LOGIN_FAIL = "Error";
/**
* 所有权限标识
*/
public static final String ALL_PERMISSION = "*:*:*";
/**
* 管理员角色权限标识
*/
public static final String SUPER_ADMIN = "admin";
/**
* 角色权限分隔符
*/
public static final String ROLE_DELIMETER = ",";
/**
* 权限标识分隔符
*/
public static final String PERMISSION_DELIMETER = ",";
/**
* 验证码有效期(分钟)
*/
@@ -129,14 +156,19 @@ public class Constants
*/
public static final String LOOKUP_LDAPS = "ldaps:";
/**
* 自动识别json对象白名单配置仅允许解析的包名范围越小越安全
*/
public static final String[] JSON_WHITELIST_STR = { "org.springframework", "com.ruoyi" };
/**
* 定时任务白名单配置(仅允许访问的包名,如其他需要可以自行添加)
*/
public static final String[] JOB_WHITELIST_STR = { "com.fastbee" };
public static final String[] JOB_WHITELIST_STR = { "com.ruoyi.quartz.task" };
/**
* 定时任务违规的字符
*/
public static final String[] JOB_ERROR_STR = { "java.net.URL", "javax.naming.InitialContext", "org.yaml.snakeyaml",
"org.springframework", "org.apache", "com.fastbee.common.utils.file", "com.fastbee.common.config" };
"org.springframework", "org.apache", "com.ruoyi.common.utils.file", "com.ruoyi.common.config", "com.ruoyi.generator" };
}

View File

@@ -2,7 +2,7 @@ package com.fastbee.common.constant;
/**
* 用户常量信息
*
*
* @author ruoyi
*/
public class UserConstants
@@ -21,6 +21,9 @@ public class UserConstants
/** 用户封禁状态 */
public static final String USER_DISABLE = "1";
/** 角色正常状态 */
public static final String ROLE_NORMAL = "0";
/** 角色封禁状态 */
public static final String ROLE_DISABLE = "1";
@@ -53,16 +56,16 @@ public class UserConstants
/** Layout组件标识 */
public final static String LAYOUT = "Layout";
/** ParentView组件标识 */
public final static String PARENT_VIEW = "ParentView";
/** InnerLink组件标识 */
public final static String INNER_LINK = "InnerLink";
/** 校验返回结果码 */
public final static String UNIQUE = "0";
public final static String NOT_UNIQUE = "1";
/** 校验是否唯一的返回标识 */
public final static boolean UNIQUE = true;
public final static boolean NOT_UNIQUE = false;
/**
* 用户名长度限制

View File

@@ -1,12 +1,13 @@
package com.fastbee.common.core.domain;
import java.util.HashMap;
import java.util.Objects;
import com.fastbee.common.constant.HttpStatus;
import com.fastbee.common.utils.StringUtils;
/**
* 操作消息提醒
*
*
* @author ruoyi
*/
public class AjaxResult extends HashMap<String, Object>
@@ -31,7 +32,7 @@ public class AjaxResult extends HashMap<String, Object>
/**
* 初始化一个新创建的 AjaxResult 对象
*
*
* @param code 状态码
* @param msg 返回内容
*/
@@ -43,7 +44,7 @@ public class AjaxResult extends HashMap<String, Object>
/**
* 初始化一个新创建的 AjaxResult 对象
*
*
* @param code 状态码
* @param msg 返回内容
* @param data 数据对象
@@ -79,7 +80,7 @@ public class AjaxResult extends HashMap<String, Object>
/**
* 返回成功消息
*
*
* @return 成功消息
*/
public static AjaxResult success()
@@ -89,7 +90,7 @@ public class AjaxResult extends HashMap<String, Object>
/**
* 返回成功数据
*
*
* @return 成功消息
*/
public static AjaxResult success(Object data)
@@ -109,7 +110,7 @@ public class AjaxResult extends HashMap<String, Object>
/**
* 返回成功消息
*
*
* @param msg 返回内容
* @return 成功消息
*/
@@ -120,7 +121,7 @@ public class AjaxResult extends HashMap<String, Object>
/**
* 返回成功消息
*
*
* @param msg 返回内容
* @param data 数据对象
* @return 成功消息
@@ -155,7 +156,7 @@ public class AjaxResult extends HashMap<String, Object>
/**
* 返回错误消息
*
*
* @return 错误消息
*/
public static AjaxResult error()
@@ -165,7 +166,7 @@ public class AjaxResult extends HashMap<String, Object>
/**
* 返回错误消息
*
*
* @param msg 返回内容
* @return 错误消息
*/
@@ -176,7 +177,7 @@ public class AjaxResult extends HashMap<String, Object>
/**
* 返回错误消息
*
*
* @param msg 返回内容
* @param data 数据对象
* @return 错误消息
@@ -188,7 +189,7 @@ public class AjaxResult extends HashMap<String, Object>
/**
* 返回错误消息
*
*
* @param code 状态码
* @param msg 返回内容
* @return 错误消息
@@ -198,6 +199,36 @@ public class AjaxResult extends HashMap<String, Object>
return new AjaxResult(code, msg, null);
}
/**
* 是否为成功消息
*
* @return 结果
*/
public boolean isSuccess()
{
return Objects.equals(HttpStatus.SUCCESS, this.get(CODE_TAG));
}
/**
* 是否为警告消息
*
* @return 结果
*/
public boolean isWarn()
{
return Objects.equals(HttpStatus.WARN, this.get(CODE_TAG));
}
/**
* 是否为错误消息
*
* @return 结果
*/
public boolean isError()
{
return Objects.equals(HttpStatus.ERROR, this.get(CODE_TAG));
}
/**
* 方便链式调用
*

View File

@@ -1,126 +1,175 @@
package com.fastbee.common.core.domain;
import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.annotations.ApiModelProperty;
import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* Entity基类
*
*
* @author ruoyi
*/
public class BaseEntity implements Serializable
{
public class BaseEntity implements Serializable {
private static final long serialVersionUID = 1L;
/** 搜索值 */
/**
* 搜索值
*/
@TableField(exist = false)
@ApiModelProperty("搜索值")
@JsonIgnore
private String searchValue;
/** 创建者 */
/**
* 创建者
*/
@ApiModelProperty("创建者")
private String createBy;
/** 创建时间 */
/**
* 创建时间
*/
@ApiModelProperty("创建时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
/** 更新者 */
/**
* 更新者
*/
@ApiModelProperty("更新者")
private String updateBy;
/** 更新时间 */
/**
* 更新时间
*/
@ApiModelProperty("更新时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date updateTime;
/** 备注 */
/**
* 备注
*/
@ApiModelProperty("备注")
private String remark;
/** 请求参数 */
/**
* 当前记录起始索引 默认值
*/
public static final int DEFAULT_PAGE_NUM = 1;
/**
* 每页显示记录数 默认值 默认查全部
*/
public static final int DEFAULT_PAGE_SIZE = Integer.MAX_VALUE;
/**
* 构建分页对象
*/
public <T> Page<T> buildPage() {
Integer pageNum = ObjectUtil.defaultIfNull(this.pageNum, DEFAULT_PAGE_NUM);
Integer pageSize = ObjectUtil.defaultIfNull(this.pageSize, DEFAULT_PAGE_SIZE);
if (pageNum <= 0) {
pageNum = DEFAULT_PAGE_NUM;
}
Page<T> page = new Page<>(pageNum, pageSize);
return page;
}
public Integer getPageNum() {
return ObjectUtil.defaultIfNull(this.pageNum, DEFAULT_PAGE_NUM);
}
public void setPageNum(Integer pageNum) {
this.pageNum = pageNum;
}
public Integer getPageSize() {
return ObjectUtil.defaultIfNull(this.pageSize, DEFAULT_PAGE_SIZE);
}
public void setPageSize(Integer pageSize) {
this.pageSize = pageSize;
}
@TableField(exist = false)
private Integer pageNum;
@TableField(exist = false)
private Integer pageSize;
/**
* 请求参数
*/
@TableField(exist = false)
@ApiModelProperty("请求参数")
@JsonInclude(JsonInclude.Include.NON_EMPTY)
private Map<String, Object> params;
public String getSearchValue()
{
public String getSearchValue() {
return searchValue;
}
public void setSearchValue(String searchValue)
{
public void setSearchValue(String searchValue) {
this.searchValue = searchValue;
}
public String getCreateBy()
{
public String getCreateBy() {
return createBy;
}
public void setCreateBy(String createBy)
{
public void setCreateBy(String createBy) {
this.createBy = createBy;
}
public Date getCreateTime()
{
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime)
{
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public String getUpdateBy()
{
public String getUpdateBy() {
return updateBy;
}
public void setUpdateBy(String updateBy)
{
public void setUpdateBy(String updateBy) {
this.updateBy = updateBy;
}
public Date getUpdateTime()
{
public Date getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Date updateTime)
{
public void setUpdateTime(Date updateTime) {
this.updateTime = updateTime;
}
public String getRemark()
{
public String getRemark() {
return remark;
}
public void setRemark(String remark)
{
public void setRemark(String remark) {
this.remark = remark;
}
public Map<String, Object> getParams()
{
if (params == null)
{
public Map<String, Object> getParams() {
if (params == null) {
params = new HashMap<>();
}
return params;
}
public void setParams(Map<String, Object> params)
{
public void setParams(Map<String, Object> params) {
this.params = params;
}
}

View File

@@ -0,0 +1,76 @@
package com.fastbee.common.core.domain;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
/**
* @author admin
* @version 1.0
* @description: 分页参数基础类
* @date 2024-11-15 18:00
*/
@Data
public class PageEntity implements Serializable {
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
@TableField(exist = false)
private Integer pageNum;
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
@TableField(exist = false)
private Integer pageSize;
/**
* 当前记录起始索引 默认值
*/
public static final int DEFAULT_PAGE_NUM = 1;
/**
* 每页显示记录数 默认值 默认查全部
*/
public static final int DEFAULT_PAGE_SIZE = Integer.MAX_VALUE;
/**
* 构建分页对象
*/
public <T> Page<T> buildPage() {
Integer pageNum = ObjectUtil.defaultIfNull(this.pageNum, DEFAULT_PAGE_NUM);
Integer pageSize = ObjectUtil.defaultIfNull(this.pageSize, DEFAULT_PAGE_SIZE);
if (pageNum <= 0) {
pageNum = DEFAULT_PAGE_NUM;
}
Page<T> page = new Page<>(pageNum, pageSize);
return page;
}
/**
* 请求参数
*/
@TableField(exist = false)
@ApiModelProperty("请求参数")
@JsonInclude(JsonInclude.Include.NON_EMPTY)
private Map<String, Object> params;
public Map<String, Object> getParams() {
if (params == null) {
params = new HashMap<>();
}
return params;
}
public Integer getPageNum() {
return ObjectUtil.defaultIfNull(this.pageNum, DEFAULT_PAGE_NUM);
}
public Integer getPageSize() {
return ObjectUtil.defaultIfNull(this.pageSize, DEFAULT_PAGE_SIZE);
}
}

View File

@@ -14,7 +14,7 @@ import com.fastbee.common.core.domain.BaseEntity;
/**
* 菜单权限表 sys_menu
*
*
* @author ruoyi
*/
@ApiModel(value = "SysMenu", description = "菜单权限表 sys_menu")
@@ -54,6 +54,9 @@ public class SysMenu extends BaseEntity
@ApiModelProperty("路由参数")
private String query;
/** 路由名称默认和路由地址相同的驼峰格式注意因为vue3版本的router会删除名称相同路由为避免名字的冲突特殊情况可以自定义 */
private String routeName;
/** 是否为外链0是 1否 */
@ApiModelProperty("是否为外链0是 1否")
private String isFrame;
@@ -69,7 +72,7 @@ public class SysMenu extends BaseEntity
/** 显示状态0显示 1隐藏 */
@ApiModelProperty("显示状态0显示 1隐藏")
private String visible;
/** 菜单状态0正常 1停用 */
@ApiModelProperty("菜单状态0正常 1停用")
private String status;
@@ -171,6 +174,16 @@ public class SysMenu extends BaseEntity
this.query = query;
}
public String getRouteName()
{
return routeName;
}
public void setRouteName(String routeName)
{
this.routeName = routeName;
}
public String getIsFrame()
{
return isFrame;
@@ -252,7 +265,7 @@ public class SysMenu extends BaseEntity
{
this.children = children;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
@@ -262,6 +275,8 @@ public class SysMenu extends BaseEntity
.append("orderNum", getOrderNum())
.append("path", getPath())
.append("component", getComponent())
.append("query", getQuery())
.append("routeName", getRouteName())
.append("isFrame", getIsFrame())
.append("IsCache", getIsCache())
.append("menuType", getMenuType())

View File

@@ -17,7 +17,7 @@ import com.fastbee.common.xss.Xss;
/**
* 用户对象 sys_user
*
*
* @author ruoyi
*/
@ApiModel(value = "SysUser", description = "用户对象 sys_user")
@@ -27,7 +27,7 @@ public class SysUser extends BaseEntity
/** 用户ID */
@ApiModelProperty("用户ID")
@Excel(name = "用户序号", cellType = ColumnType.NUMERIC, prompt = "用户编号")
@Excel(name = "用户序号", type = Type.EXPORT, cellType = ColumnType.NUMERIC, prompt = "用户编号")
private Long userId;
/** 部门ID */
@@ -52,7 +52,7 @@ public class SysUser extends BaseEntity
/** 手机号码 */
@ApiModelProperty("手机号码")
@Excel(name = "手机号码")
@Excel(name = "手机号码", cellType = ColumnType.TEXT)
private String phonenumber;
/** 用户性别 */

View File

@@ -1,19 +1,19 @@
package com.fastbee.common.core.domain.model;
import com.alibaba.fastjson2.annotation.JSONField;
import com.fastbee.common.core.domain.entity.SysUser;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.Set;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import com.alibaba.fastjson2.annotation.JSONField;
import com.fastbee.common.core.domain.entity.SysUser;
/**
* 登录用户身份权限
*
*
* @author ruoyi
*/
public class LoginUser implements UserDetails
{
public class LoginUser implements UserDetails {
private static final long serialVersionUID = 1L;
/**
@@ -31,6 +31,9 @@ public class LoginUser implements UserDetails
*/
private String token;
private String requestToken;
/**
* 登录时间
*/
@@ -71,64 +74,100 @@ public class LoginUser implements UserDetails
*/
private SysUser user;
public Long getUserId()
{
private String language;
private Long deptUserId;
private Boolean neverExpire = Boolean.FALSE;
public LoginUser() {
}
public LoginUser(SysUser user, Set<String> permissions) {
this.user = user;
this.permissions = permissions;
}
public LoginUser(Long userId, Long deptId, SysUser user, Set<String> permissions) {
this.userId = userId;
this.deptId = deptId;
this.user = user;
this.permissions = permissions;
}
public LoginUser(Long userId, Long deptId, String language, SysUser user, Set<String> permissions) {
this.userId = userId;
this.deptId = deptId;
this.user = user;
this.language = language;
this.permissions = permissions;
}
public Boolean getNeverExpire() {
return neverExpire;
}
public void setNeverExpire(Boolean neverExpire) {
this.neverExpire = neverExpire;
}
public String getLanguage() {
return language;
}
public Long getDeptUserId() {
return deptUserId;
}
public void setDeptUserId(Long deptUserId) {
this.deptUserId = deptUserId;
}
public void setLanguage(String language) {
this.language = language;
}
public Long getUserId() {
return userId;
}
public void setUserId(Long userId)
{
public void setUserId(Long userId) {
this.userId = userId;
}
public Long getDeptId()
{
public Long getDeptId() {
return deptId;
}
public void setDeptId(Long deptId)
{
public void setDeptId(Long deptId) {
this.deptId = deptId;
}
public String getToken()
{
public String getToken() {
return token;
}
public void setToken(String token)
{
public void setToken(String token) {
this.token = token;
}
public LoginUser()
{
public String getRequestToken() {
return requestToken;
}
public LoginUser(SysUser user, Set<String> permissions)
{
this.user = user;
this.permissions = permissions;
public void setRequestToken(String requestToken) {
this.requestToken = requestToken;
}
public LoginUser(Long userId, Long deptId, SysUser user, Set<String> permissions)
{
this.userId = userId;
this.deptId = deptId;
this.user = user;
this.permissions = permissions;
}
@JSONField(serialize = false)
@Override
public String getPassword()
{
public String getPassword() {
return user.getPassword();
}
@Override
public String getUsername()
{
public String getUsername() {
return user.getUserName();
}
@@ -137,130 +176,109 @@ public class LoginUser implements UserDetails
*/
@JSONField(serialize = false)
@Override
public boolean isAccountNonExpired()
{
public boolean isAccountNonExpired() {
return true;
}
/**
* 指定用户是否解锁,锁定的用户无法进行身份验证
*
*
* @return
*/
@JSONField(serialize = false)
@Override
public boolean isAccountNonLocked()
{
public boolean isAccountNonLocked() {
return true;
}
/**
* 指示是否已过期的用户的凭据(密码),过期的凭据防止认证
*
*
* @return
*/
@JSONField(serialize = false)
@Override
public boolean isCredentialsNonExpired()
{
public boolean isCredentialsNonExpired() {
return true;
}
/**
* 是否可用 ,禁用的用户不能身份验证
*
*
* @return
*/
@JSONField(serialize = false)
@Override
public boolean isEnabled()
{
public boolean isEnabled() {
return true;
}
public Long getLoginTime()
{
public Long getLoginTime() {
return loginTime;
}
public void setLoginTime(Long loginTime)
{
public void setLoginTime(Long loginTime) {
this.loginTime = loginTime;
}
public String getIpaddr()
{
public String getIpaddr() {
return ipaddr;
}
public void setIpaddr(String ipaddr)
{
public void setIpaddr(String ipaddr) {
this.ipaddr = ipaddr;
}
public String getLoginLocation()
{
public String getLoginLocation() {
return loginLocation;
}
public void setLoginLocation(String loginLocation)
{
public void setLoginLocation(String loginLocation) {
this.loginLocation = loginLocation;
}
public String getBrowser()
{
public String getBrowser() {
return browser;
}
public void setBrowser(String browser)
{
public void setBrowser(String browser) {
this.browser = browser;
}
public String getOs()
{
public String getOs() {
return os;
}
public void setOs(String os)
{
public void setOs(String os) {
this.os = os;
}
public Long getExpireTime()
{
public Long getExpireTime() {
return expireTime;
}
public void setExpireTime(Long expireTime)
{
public void setExpireTime(Long expireTime) {
this.expireTime = expireTime;
}
public Set<String> getPermissions()
{
public Set<String> getPermissions() {
return permissions;
}
public void setPermissions(Set<String> permissions)
{
public void setPermissions(Set<String> permissions) {
this.permissions = permissions;
}
public SysUser getUser()
{
public SysUser getUser() {
return user;
}
public void setUser(SysUser user)
{
public void setUser(SysUser user) {
this.user = user;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities()
{
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
}

View File

@@ -1,6 +1,15 @@
package com.fastbee.common.core.page;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fastbee.common.exception.ServiceException;
import com.fastbee.common.utils.StringUtils;
import com.fastbee.common.utils.sql.SqlUtil;
import java.util.ArrayList;
import java.util.List;
/**
* 分页数据
@@ -24,6 +33,16 @@ public class PageDomain
/** 分页参数合理化 */
private Boolean reasonable = true;
/**
* 当前记录起始索引 默认值
*/
public static final int DEFAULT_PAGE_NUM = 1;
/**
* 每页显示记录数 默认值 默认查全部
*/
public static final int DEFAULT_PAGE_SIZE = Integer.MAX_VALUE;
public String getOrderBy()
{
if (StringUtils.isEmpty(orderByColumn))
@@ -33,6 +52,64 @@ public class PageDomain
return StringUtils.toUnderScoreCase(orderByColumn) + " " + isAsc;
}
/**
* 构建分页对象
*/
public <T> Page<T> build() {
Integer pageNum = ObjectUtil.defaultIfNull(getPageNum(), DEFAULT_PAGE_NUM);
Integer pageSize = ObjectUtil.defaultIfNull(getPageSize(), DEFAULT_PAGE_SIZE);
if (pageNum <= 0) {
pageNum = DEFAULT_PAGE_NUM;
}
Page<T> page = new Page<>(pageNum, pageSize);
List<OrderItem> orderItems = buildOrderItem();
if (CollUtil.isNotEmpty(orderItems)) {
page.addOrder(orderItems);
}
return page;
}
/**
* 构建排序
*
* 支持的用法如下:
* {isAsc:"asc",orderByColumn:"id"} order by id asc
* {isAsc:"asc",orderByColumn:"id,createTime"} order by id asc,create_time asc
* {isAsc:"desc",orderByColumn:"id,createTime"} order by id desc,create_time desc
* {isAsc:"asc,desc",orderByColumn:"id,createTime"} order by id asc,create_time desc
*/
private List<OrderItem> buildOrderItem() {
if (StringUtils.isBlank(orderByColumn) || StringUtils.isBlank(isAsc)) {
return null;
}
String orderBy = SqlUtil.escapeOrderBySql(orderByColumn);
orderBy = StringUtils.toUnderScoreCase(orderBy);
// 兼容前端排序类型
isAsc = StringUtils.replaceEach(isAsc, new String[]{"ascending", "descending"}, new String[]{"asc", "desc"});
String[] orderByArr = orderBy.split(StringUtils.SLASH);
String[] isAscArr = isAsc.split(StringUtils.SLASH);
if (isAscArr.length != 1 && isAscArr.length != orderByArr.length) {
throw new ServiceException("排序参数有误");
}
List<OrderItem> list = new ArrayList<>();
// 每个字段各自排序
for (int i = 0; i < orderByArr.length; i++) {
String orderByStr = orderByArr[i];
String isAscStr = isAscArr.length == 1 ? isAscArr[0] : isAscArr[i];
if ("asc".equals(isAscStr)) {
list.add(OrderItem.asc(orderByStr));
} else if ("desc".equals(isAscStr)) {
list.add(OrderItem.desc(orderByStr));
} else {
throw new ServiceException("排序参数有误");
}
}
return list;
}
public Integer getPageNum()
{
return pageNum;

View File

@@ -1,5 +1,8 @@
package com.fastbee.common.core.page;
import cn.hutool.http.HttpStatus;
import com.baomidou.mybatisplus.core.metadata.IPage;
import java.io.Serializable;
import java.util.List;
@@ -43,6 +46,40 @@ public class TableDataInfo implements Serializable
this.total = total;
}
/**
* 根据分页对象构建表格分页数据对象
*/
public static <T> TableDataInfo build(IPage<T> page) {
TableDataInfo rspData = new TableDataInfo();
rspData.setCode(HttpStatus.HTTP_OK);
rspData.setMsg("查询成功");
rspData.setRows(page.getRecords());
rspData.setTotal(page.getTotal());
return rspData;
}
/**
* 根据数据列表构建表格分页数据对象
*/
public static <T> TableDataInfo build(List<T> list) {
TableDataInfo rspData = new TableDataInfo();
rspData.setCode(HttpStatus.HTTP_OK);
rspData.setMsg("查询成功");
rspData.setRows(list);
rspData.setTotal(list.size());
return rspData;
}
/**
* 构建表格分页数据对象
*/
public static <T> TableDataInfo build() {
TableDataInfo rspData = new TableDataInfo();
rspData.setCode(HttpStatus.HTTP_OK);
rspData.setMsg("查询成功");
return rspData;
}
public long getTotal()
{
return total;

View File

@@ -1,13 +1,14 @@
package com.fastbee.common.core.text;
import com.fastbee.common.utils.StringUtils;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.text.NumberFormat;
import java.util.Set;
import com.fastbee.common.utils.StringUtils;
import org.apache.commons.lang3.ArrayUtils;
/**
* 类型转换器
@@ -364,6 +365,10 @@ public class Convert
*/
public static String[] toStrArray(String str)
{
if (StringUtils.isEmpty(str))
{
return new String[] {};
}
return toStrArray(",", str);
}
@@ -536,7 +541,7 @@ public class Convert
/**
* 转换为boolean<br>
* String支持的值为true、false、yes、ok、no1,0 如果给定的值为空,或者转换失败,返回默认值<br>
* String支持的值为true、false、yes、ok、no、1、0、是、否, 如果给定的值为空,或者转换失败,返回默认值<br>
* 转换失败不会报错
*
* @param value 被转换的值
@@ -565,10 +570,12 @@ public class Convert
case "yes":
case "ok":
case "1":
case "":
return true;
case "false":
case "no":
case "0":
case "":
return false;
default:
return defaultValue;
@@ -791,14 +798,23 @@ public class Convert
{
return (String) obj;
}
else if (obj instanceof byte[])
else if (obj instanceof byte[] || obj instanceof Byte[])
{
return str((byte[]) obj, charset);
}
else if (obj instanceof Byte[])
{
byte[] bytes = ArrayUtils.toPrimitive((Byte[]) obj);
return str(bytes, charset);
if (obj instanceof byte[])
{
return str((byte[]) obj, charset);
}
else
{
Byte[] bytes = (Byte[]) obj;
int length = bytes.length;
byte[] dest = new byte[length];
for (int i = 0; i < length; i++)
{
dest[i] = bytes[i];
}
return str(dest, charset);
}
}
else if (obj instanceof ByteBuffer)
{
@@ -954,9 +970,7 @@ public class Convert
c[i] = (char) (c[i] - 65248);
}
}
String returnString = new String(c);
return returnString;
return new String(c);
}
/**
@@ -977,7 +991,12 @@ public class Convert
String s = "";
for (int i = 0; i < fraction.length; i++)
{
s += (digit[(int) (Math.floor(n * 10 * Math.pow(10, i)) % 10)] + fraction[i]).replaceAll("(零.)+", "");
// 优化double计算精度丢失问题
BigDecimal nNum = new BigDecimal(n);
BigDecimal decimal = new BigDecimal(10);
BigDecimal scale = nNum.multiply(decimal).setScale(2, RoundingMode.HALF_EVEN);
double d = scale.doubleValue();
s += (digit[(int) (Math.floor(d * Math.pow(10, i)) % 10)] + fraction[i]).replaceAll("(零.)+", "");
}
if (s.length() < 1)
{

View File

@@ -0,0 +1,60 @@
package com.fastbee.common.enums;
import com.fastbee.common.utils.DesensitizedUtil;
import java.util.function.Function;
/**
* 脱敏类型
*
* @author ruoyi
*/
public enum DesensitizedType
{
/**
* 姓名第2位星号替换
*/
USERNAME(s -> s.replaceAll("(\\S)\\S(\\S*)", "$1*$2")),
/**
* 密码,全部字符都用*代替
*/
PASSWORD(DesensitizedUtil::password),
/**
* 身份证中间10位星号替换
*/
ID_CARD(s -> s.replaceAll("(\\d{4})\\d{10}(\\d{3}[Xx]|\\d{4})", "$1** **** ****$2")),
/**
* 手机号中间4位星号替换
*/
PHONE(s -> s.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2")),
/**
* 电子邮箱,仅显示第一个字母和@后面的地址显示,其他星号替换
*/
EMAIL(s -> s.replaceAll("(^.)[^@]*(@.*$)", "$1****$2")),
/**
* 银行卡号保留最后4位其他星号替换
*/
BANK_CARD(s -> s.replaceAll("\\d{15}(\\d{3})", "**** **** **** **** $1")),
/**
* 车牌号码,包含普通车辆、新能源车辆
*/
CAR_LICENSE(DesensitizedUtil::carLicense);
private final Function<String, String> desensitizer;
DesensitizedType(Function<String, String> desensitizer)
{
this.desensitizer = desensitizer;
}
public Function<String, String> desensitizer()
{
return desensitizer;
}
}

View File

@@ -0,0 +1,61 @@
package com.fastbee.common.exception.file;
import java.io.PrintStream;
import java.io.PrintWriter;
/**
* 文件上传异常类
*
* @author ruoyi
*/
public class FileUploadException extends Exception
{
private static final long serialVersionUID = 1L;
private final Throwable cause;
public FileUploadException()
{
this(null, null);
}
public FileUploadException(final String msg)
{
this(msg, null);
}
public FileUploadException(String msg, Throwable cause)
{
super(msg);
this.cause = cause;
}
@Override
public void printStackTrace(PrintStream stream)
{
super.printStackTrace(stream);
if (cause != null)
{
stream.println("Caused by:");
cause.printStackTrace(stream);
}
}
@Override
public void printStackTrace(PrintWriter writer)
{
super.printStackTrace(writer);
if (cause != null)
{
writer.println("Caused by:");
cause.printStackTrace(writer);
}
}
@Override
public Throwable getCause()
{
return cause;
}
}

View File

@@ -0,0 +1,16 @@
package com.fastbee.common.exception.user;
/**
* 黑名单IP异常类
*
* @author ruoyi
*/
public class BlackListException extends UserException
{
private static final long serialVersionUID = 1L;
public BlackListException()
{
super("login.blocked", null);
}
}

View File

@@ -0,0 +1,16 @@
package com.fastbee.common.exception.user;
/**
* 用户不存在异常类
*
* @author ruoyi
*/
public class UserNotExistsException extends UserException
{
private static final long serialVersionUID = 1L;
public UserNotExistsException()
{
super("user.not.exists", null);
}
}

View File

@@ -16,7 +16,7 @@ import com.fastbee.common.enums.HttpMethod;
/**
* 防止XSS攻击的过滤器
*
*
* @author ruoyi
*/
public class XssFilter implements Filter
@@ -32,10 +32,10 @@ public class XssFilter implements Filter
String tempExcludes = filterConfig.getInitParameter("excludes");
if (StringUtils.isNotEmpty(tempExcludes))
{
String[] url = tempExcludes.split(",");
for (int i = 0; url != null && i < url.length; i++)
String[] urls = tempExcludes.split(",");
for (String url : urls)
{
excludes.add(url[i]);
excludes.add(url);
}
}
}
@@ -72,4 +72,4 @@ public class XssFilter implements Filter
{
}
}
}

View File

@@ -0,0 +1,64 @@
package com.fastbee.common.mybatis.enums;
import com.fastbee.common.utils.StringUtils;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 数据库类型
*
* @author Lion Li
*/
@Getter
@AllArgsConstructor
public enum DataBaseType {
/**
* MySQL
*/
MY_SQL("MySQL"),
/**
* Oracle
*/
ORACLE("Oracle"),
/**
* PostgreSQL
*/
POSTGRE_SQL("PostgreSQL"),
/**
* SQL Server
*/
SQL_SERVER("Microsoft SQL Server"),
/**
* 达梦
*/
DM("DM DBMS");
/**
* 数据库类型
*/
private final String type;
/**
* 根据数据库产品名称查找对应的数据库类型
*
* @param databaseProductName 数据库产品名称
* @return 对应的数据库类型枚举值,如果未找到则返回 null
*/
public static DataBaseType find(String databaseProductName) {
if (StringUtils.isBlank(databaseProductName)) {
return null;
}
for (DataBaseType type : values()) {
if (type.getType().equals(databaseProductName)) {
return type;
}
}
return null;
}
}

View File

@@ -1,4 +1,4 @@
package com.fastbee.framework.mybatis.mapper;
package com.fastbee.common.mybatis.mapper;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
@@ -9,7 +9,7 @@ import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.extension.toolkit.Db;
import com.fastbee.common.core.domain.PageParam;
import com.fastbee.common.core.domain.PageResult;
import com.fastbee.framework.mybatis.utils.MyBatisUtils;
import com.fastbee.common.mybatis.utils.MyBatisUtils;
import org.apache.ibatis.annotations.Param;
import java.util.Collection;

View File

@@ -1,4 +1,4 @@
package com.fastbee.framework.mybatis.utils;
package com.fastbee.common.mybatis.utils;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.metadata.OrderItem;

View File

@@ -5,7 +5,7 @@ import java.math.RoundingMode;
/**
* 精确的浮点数运算
*
*
* @author ruoyi
*/
public class Arith
@@ -108,7 +108,6 @@ public class Arith
"The scale must be a positive integer or zero");
}
BigDecimal b = new BigDecimal(Double.toString(v));
BigDecimal one = BigDecimal.ONE;
return b.divide(one, scale, RoundingMode.HALF_UP).doubleValue();
return b.divide(BigDecimal.ONE, scale, RoundingMode.HALF_UP).doubleValue();
}
}

View File

@@ -1,18 +1,14 @@
package com.fastbee.common.utils;
import org.apache.commons.lang3.time.DateFormatUtils;
import java.lang.management.ManagementFactory;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.*;
import java.util.Date;
import java.util.Random;
import org.apache.commons.lang3.time.DateFormatUtils;
/**
* 时间工具类
*
@@ -160,16 +156,20 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils
}
/**
* 计算两个时间差
* 计算时间差
*
* @param endDate 最后时间
* @param startTime 开始时间
* @return 时间差(天/小时/分钟)
*/
public static String getDatePoor(Date endDate, Date nowDate)
public static String timeDistance(Date endDate, Date startTime)
{
long nd = 1000 * 24 * 60 * 60;
long nh = 1000 * 60 * 60;
long nm = 1000 * 60;
// long ns = 1000;
// 获得两个时间的毫秒时间差异
long diff = endDate.getTime() - nowDate.getTime();
long diff = endDate.getTime() - startTime.getTime();
// 计算差多少天
long day = diff / nd;
// 计算差多少小时

View File

@@ -0,0 +1,49 @@
package com.fastbee.common.utils;
/**
* 脱敏工具类
*
* @author ruoyi
*/
public class DesensitizedUtil
{
/**
* 密码的全部字符都用*代替,比如:******
*
* @param password 密码
* @return 脱敏后的密码
*/
public static String password(String password)
{
if (StringUtils.isBlank(password))
{
return StringUtils.EMPTY;
}
return StringUtils.repeat('*', password.length());
}
/**
* 车牌中间用*代替,如果是错误的车牌,不处理
*
* @param carLicense 完整的车牌号
* @return 脱敏后的车牌
*/
public static String carLicense(String carLicense)
{
if (StringUtils.isBlank(carLicense))
{
return StringUtils.EMPTY;
}
// 普通车牌
if (carLicense.length() == 7)
{
carLicense = StringUtils.hide(carLicense, 3, 6);
}
else if (carLicense.length() == 8)
{
// 新能源车牌
carLicense = StringUtils.hide(carLicense, 3, 7);
}
return carLicense;
}
}

View File

@@ -1,13 +1,14 @@
package com.fastbee.common.utils;
import java.util.Collection;
import java.util.List;
import com.alibaba.fastjson2.JSONArray;
import com.fastbee.common.constant.CacheConstants;
import com.fastbee.common.core.domain.entity.SysDictData;
import com.fastbee.common.core.redis.RedisCache;
import com.fastbee.common.utils.spring.SpringUtils;
import java.util.Collection;
import java.util.List;
/**
* 字典工具类
*
@@ -56,6 +57,10 @@ public class DictUtils
*/
public static String getDictLabel(String dictType, String dictValue)
{
if (StringUtils.isEmpty(dictValue))
{
return StringUtils.EMPTY;
}
return getDictLabel(dictType, dictValue, SEPARATOR);
}
@@ -68,6 +73,10 @@ public class DictUtils
*/
public static String getDictValue(String dictType, String dictLabel)
{
if (StringUtils.isEmpty(dictLabel))
{
return StringUtils.EMPTY;
}
return getDictValue(dictType, dictLabel, SEPARATOR);
}
@@ -83,31 +92,31 @@ public class DictUtils
{
StringBuilder propertyString = new StringBuilder();
List<SysDictData> datas = getDictCache(dictType);
if (StringUtils.isNotNull(datas))
if (StringUtils.isNull(datas))
{
if (StringUtils.containsAny(separator, dictValue))
return StringUtils.EMPTY;
}
if (StringUtils.containsAny(separator, dictValue))
{
for (SysDictData dict : datas)
{
for (SysDictData dict : datas)
for (String value : dictValue.split(separator))
{
for (String value : dictValue.split(separator))
if (value.equals(dict.getDictValue()))
{
if (value.equals(dict.getDictValue()))
{
propertyString.append(dict.getDictLabel()).append(separator);
break;
}
propertyString.append(dict.getDictLabel()).append(separator);
break;
}
}
}
else
}
else
{
for (SysDictData dict : datas)
{
for (SysDictData dict : datas)
if (dictValue.equals(dict.getDictValue()))
{
if (dictValue.equals(dict.getDictValue()))
{
return dict.getDictLabel();
}
return dict.getDictLabel();
}
}
}
@@ -126,8 +135,11 @@ public class DictUtils
{
StringBuilder propertyString = new StringBuilder();
List<SysDictData> datas = getDictCache(dictType);
if (StringUtils.containsAny(separator, dictLabel) && StringUtils.isNotEmpty(datas))
if (StringUtils.isNull(datas))
{
return StringUtils.EMPTY;
}
if (StringUtils.containsAny(separator, dictLabel))
{
for (SysDictData dict : datas)
{
@@ -154,6 +166,48 @@ public class DictUtils
return StringUtils.stripEnd(propertyString.toString(), separator);
}
/**
* 根据字典类型获取字典所有值
*
* @param dictType 字典类型
* @return 字典值
*/
public static String getDictValues(String dictType)
{
StringBuilder propertyString = new StringBuilder();
List<SysDictData> datas = getDictCache(dictType);
if (StringUtils.isNull(datas))
{
return StringUtils.EMPTY;
}
for (SysDictData dict : datas)
{
propertyString.append(dict.getDictValue()).append(SEPARATOR);
}
return StringUtils.stripEnd(propertyString.toString(), SEPARATOR);
}
/**
* 根据字典类型获取字典所有标签
*
* @param dictType 字典类型
* @return 字典值
*/
public static String getDictLabels(String dictType)
{
StringBuilder propertyString = new StringBuilder();
List<SysDictData> datas = getDictCache(dictType);
if (StringUtils.isNull(datas))
{
return StringUtils.EMPTY;
}
for (SysDictData dict : datas)
{
propertyString.append(dict.getDictLabel()).append(SEPARATOR);
}
return StringUtils.stripEnd(propertyString.toString(), SEPARATOR);
}
/**
* 删除指定字典缓存
*

View File

@@ -1,19 +1,27 @@
package com.fastbee.common.utils;
import com.fastbee.common.constant.Constants;
import com.fastbee.common.constant.HttpStatus;
import com.fastbee.common.core.domain.entity.SysRole;
import com.fastbee.common.core.domain.model.LoginUser;
import com.fastbee.common.exception.ServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import com.fastbee.common.constant.HttpStatus;
import com.fastbee.common.core.domain.model.LoginUser;
import com.fastbee.common.exception.ServiceException;
import org.springframework.util.PatternMatchUtils;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
/**
* 安全服务工具类
*
*
* @author ruoyi
*/
public class SecurityUtils
{
/**
* 用户ID
**/
@@ -43,7 +51,7 @@ public class SecurityUtils
throw new ServiceException("获取部门ID异常", HttpStatus.UNAUTHORIZED);
}
}
/**
* 获取用户账户
**/
@@ -109,7 +117,7 @@ public class SecurityUtils
/**
* 是否为管理员
*
*
* @param userId 用户ID
* @return 结果
*/
@@ -117,4 +125,55 @@ public class SecurityUtils
{
return userId != null && 1L == userId;
}
/**
* 验证用户是否具备某权限
*
* @param permission 权限字符串
* @return 用户是否具备某权限
*/
public static boolean hasPermi(String permission)
{
return hasPermi(getLoginUser().getPermissions(), permission);
}
/**
* 判断是否包含权限
*
* @param authorities 权限列表
* @param permission 权限字符串
* @return 用户是否具备某权限
*/
public static boolean hasPermi(Collection<String> authorities, String permission)
{
return authorities.stream().filter(StringUtils::hasText)
.anyMatch(x -> Constants.ALL_PERMISSION.equals(x) || PatternMatchUtils.simpleMatch(x, permission));
}
/**
* 验证用户是否拥有某个角色
*
* @param role 角色标识
* @return 用户是否具备某角色
*/
public static boolean hasRole(String role)
{
List<SysRole> roleList = getLoginUser().getUser().getRoles();
Collection<String> roles = roleList.stream().map(SysRole::getRoleKey).collect(Collectors.toSet());
return hasRole(roles, role);
}
/**
* 判断是否包含角色
*
* @param roles 角色列表
* @param role 角色
* @return 用户是否具备某角色权限
*/
public static boolean hasRole(Collection<String> roles, String role)
{
return roles.stream().filter(StringUtils::hasText)
.anyMatch(x -> Constants.SUPER_ADMIN.equals(x) || PatternMatchUtils.simpleMatch(x, role));
}
}

View File

@@ -1,43 +1,37 @@
package com.fastbee.common.utils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import org.springframework.util.AntPathMatcher;
import com.fastbee.common.constant.Constants;
import com.fastbee.common.core.text.StrFormatter;
import org.springframework.util.AntPathMatcher;
import java.util.*;
/**
* 字符串工具类
*
* @author ruoyi
*/
public class StringUtils extends org.apache.commons.lang3.StringUtils {
/**
* 空字符串
*/
public class StringUtils extends org.apache.commons.lang3.StringUtils
{
/** 空字符串 */
private static final String NULLSTR = "";
/**
* 下划线
*/
/** 下划线 */
private static final char SEPARATOR = '_';
/** 星号 */
private static final char ASTERISK = '*';
public static final String SLASH = "/";
/**
* 获取参数不为空值
*
* @param value defaultValue 要判断的value
* @return value 返回值
*/
public static <T> T nvl(T value, T defaultValue) {
public static <T> T nvl(T value, T defaultValue)
{
return value != null ? value : defaultValue;
}
@@ -47,7 +41,8 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
* @param coll 要判断的Collection
* @return true为空 false非空
*/
public static boolean isEmpty(Collection<?> coll) {
public static boolean isEmpty(Collection<?> coll)
{
return isNull(coll) || coll.isEmpty();
}
@@ -57,7 +52,8 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
* @param coll 要判断的Collection
* @return true非空 false
*/
public static boolean isNotEmpty(Collection<?> coll) {
public static boolean isNotEmpty(Collection<?> coll)
{
return !isEmpty(coll);
}
@@ -65,9 +61,10 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
* * 判断一个对象数组是否为空
*
* @param objects 要判断的对象数组
* * @return true为空 false非空
** @return true为空 false非空
*/
public static boolean isEmpty(Object[] objects) {
public static boolean isEmpty(Object[] objects)
{
return isNull(objects) || (objects.length == 0);
}
@@ -77,7 +74,8 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
* @param objects 要判断的对象数组
* @return true非空 false
*/
public static boolean isNotEmpty(Object[] objects) {
public static boolean isNotEmpty(Object[] objects)
{
return !isEmpty(objects);
}
@@ -87,7 +85,8 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
* @param map 要判断的Map
* @return true为空 false非空
*/
public static boolean isEmpty(Map<?, ?> map) {
public static boolean isEmpty(Map<?, ?> map)
{
return isNull(map) || map.isEmpty();
}
@@ -97,7 +96,8 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
* @param map 要判断的Map
* @return true非空 false
*/
public static boolean isNotEmpty(Map<?, ?> map) {
public static boolean isNotEmpty(Map<?, ?> map)
{
return !isEmpty(map);
}
@@ -107,7 +107,8 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
* @param str String
* @return true为空 false非空
*/
public static boolean isEmpty(String str) {
public static boolean isEmpty(String str)
{
return isNull(str) || NULLSTR.equals(str.trim());
}
@@ -117,7 +118,8 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
* @param str String
* @return true非空串 false空串
*/
public static boolean isNotEmpty(String str) {
public static boolean isNotEmpty(String str)
{
return !isEmpty(str);
}
@@ -127,7 +129,8 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
* @param object Object
* @return true为空 false非空
*/
public static boolean isNull(Object object) {
public static boolean isNull(Object object)
{
return object == null;
}
@@ -137,7 +140,8 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
* @param object Object
* @return true非空 false
*/
public static boolean isNotNull(Object object) {
public static boolean isNotNull(Object object)
{
return !isNull(object);
}
@@ -147,37 +151,87 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
* @param object 对象
* @return true是数组 false不是数组
*/
public static boolean isArray(Object object) {
public static boolean isArray(Object object)
{
return isNotNull(object) && object.getClass().isArray();
}
/**
* 去空格
*/
public static String trim(String str) {
public static String trim(String str)
{
return (str == null ? "" : str.trim());
}
/**
* 替换指定字符串的指定区间内字符为"*"
*
* @param str 字符串
* @param startInclude 开始位置(包含)
* @param endExclude 结束位置(不包含)
* @return 替换后的字符串
*/
public static String hide(CharSequence str, int startInclude, int endExclude)
{
if (isEmpty(str))
{
return NULLSTR;
}
final int strLength = str.length();
if (startInclude > strLength)
{
return NULLSTR;
}
if (endExclude > strLength)
{
endExclude = strLength;
}
if (startInclude > endExclude)
{
// 如果起始位置大于结束位置,不替换
return NULLSTR;
}
final char[] chars = new char[strLength];
for (int i = 0; i < strLength; i++)
{
if (i >= startInclude && i < endExclude)
{
chars[i] = ASTERISK;
}
else
{
chars[i] = str.charAt(i);
}
}
return new String(chars);
}
/**
* 截取字符串
*
* @param str 字符串
* @param str 字符串
* @param start 开始
* @return 结果
*/
public static String substring(final String str, int start) {
if (str == null) {
public static String substring(final String str, int start)
{
if (str == null)
{
return NULLSTR;
}
if (start < 0) {
if (start < 0)
{
start = str.length() + start;
}
if (start < 0) {
if (start < 0)
{
start = 0;
}
if (start > str.length()) {
if (start > str.length())
{
return NULLSTR;
}
@@ -187,41 +241,99 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
/**
* 截取字符串
*
* @param str 字符串
* @param str 字符串
* @param start 开始
* @param end 结束
* @param end 结束
* @return 结果
*/
public static String substring(final String str, int start, int end) {
if (str == null) {
public static String substring(final String str, int start, int end)
{
if (str == null)
{
return NULLSTR;
}
if (end < 0) {
if (end < 0)
{
end = str.length() + end;
}
if (start < 0) {
if (start < 0)
{
start = str.length() + start;
}
if (end > str.length()) {
if (end > str.length())
{
end = str.length();
}
if (start > end) {
if (start > end)
{
return NULLSTR;
}
if (start < 0) {
if (start < 0)
{
start = 0;
}
if (end < 0) {
if (end < 0)
{
end = 0;
}
return str.substring(start, end);
}
/**
* 在字符串中查找第一个出现的 `open` 和最后一个出现的 `close` 之间的子字符串
*
* @param str 要截取的字符串
* @param open 起始字符串
* @param close 结束字符串
* @return 截取结果
*/
public static String substringBetweenLast(final String str, final String open, final String close)
{
if (isEmpty(str) || isEmpty(open) || isEmpty(close))
{
return NULLSTR;
}
final int start = str.indexOf(open);
if (start != INDEX_NOT_FOUND)
{
final int end = str.lastIndexOf(close);
if (end != INDEX_NOT_FOUND)
{
return str.substring(start + open.length(), end);
}
}
return NULLSTR;
}
/**
* 判断是否为空,并且不是空白字符
*
* @param str 要判断的value
* @return 结果
*/
public static boolean hasText(String str)
{
return (str != null && !str.isEmpty() && containsText(str));
}
private static boolean containsText(CharSequence str)
{
int strLen = str.length();
for (int i = 0; i < strLen; i++)
{
if (!Character.isWhitespace(str.charAt(i)))
{
return true;
}
}
return false;
}
/**
* 格式化文本, {} 表示占位符<br>
* 此方法只是简单将占位符 {} 按照顺序替换为参数<br>
@@ -232,11 +344,13 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
* 转义\ format("this is \\\\{} for {}", "a", "b") -> this is \a for b<br>
*
* @param template 文本模板,被替换的部分用 {} 表示
* @param params 参数值
* @param params 参数值
* @return 格式化后的文本
*/
public static String format(String template, Object... params) {
if (isEmpty(params) || isEmpty(template)) {
public static String format(String template, Object... params)
{
if (isEmpty(params) || isEmpty(template))
{
return template;
}
return StrFormatter.format(template, params);
@@ -248,7 +362,8 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
* @param link 链接
* @return 结果
*/
public static boolean ishttp(String link) {
public static boolean ishttp(String link)
{
return StringUtils.startsWithAny(link, Constants.HTTP, Constants.HTTPS);
}
@@ -259,35 +374,54 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
* @param sep 分隔符
* @return set集合
*/
public static final Set<String> str2Set(String str, String sep) {
public static final Set<String> str2Set(String str, String sep)
{
return new HashSet<String>(str2List(str, sep, true, false));
}
/**
* 字符串转list
*
* @param str 字符串
* @param sep 分隔符
* @param filterBlank 过滤纯空白
* @param trim 去掉首尾空白
* @param str 字符串
* @param sep 分隔符
* @return list集合
*/
public static final List<String> str2List(String str, String sep, boolean filterBlank, boolean trim) {
public static final List<String> str2List(String str, String sep)
{
return str2List(str, sep, true, false);
}
/**
* 字符串转list
*
* @param str 字符串
* @param sep 分隔符
* @param filterBlank 过滤纯空白
* @param trim 去掉首尾空白
* @return list集合
*/
public static final List<String> str2List(String str, String sep, boolean filterBlank, boolean trim)
{
List<String> list = new ArrayList<String>();
if (StringUtils.isEmpty(str)) {
if (StringUtils.isEmpty(str))
{
return list;
}
// 过滤空白字符串
if (filterBlank && StringUtils.isBlank(str)) {
if (filterBlank && StringUtils.isBlank(str))
{
return list;
}
String[] split = str.split(sep);
for (String string : split) {
if (filterBlank && StringUtils.isBlank(string)) {
for (String string : split)
{
if (filterBlank && StringUtils.isBlank(string))
{
continue;
}
if (trim) {
if (trim)
{
string = string.trim();
}
list.add(string);
@@ -297,18 +431,24 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
}
/**
* 判断给定的set列表中是否包含数组array 判断给定的数组array中是否包含给定的元素value
* 判断给定的collection列表中是否包含数组array 判断给定的数组array中是否包含给定的元素value
*
* @param set 给定的集合
* @param collection 给定的集合
* @param array 给定的数组
* @return boolean 结果
*/
public static boolean containsAny(Collection<String> collection, String... array) {
if (isEmpty(collection) || isEmpty(array)) {
public static boolean containsAny(Collection<String> collection, String... array)
{
if (isEmpty(collection) || isEmpty(array))
{
return false;
} else {
for (String str : array) {
if (collection.contains(str)) {
}
else
{
for (String str : array)
{
if (collection.contains(str))
{
return true;
}
}
@@ -319,16 +459,20 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
/**
* 查找指定字符串是否包含指定字符串列表中的任意一个字符串同时串忽略大小写
*
* @param cs 指定字符串
* @param cs 指定字符串
* @param searchCharSequences 需要检查的字符串数组
* @return 是否包含任意一个字符串
*/
public static boolean containsAnyIgnoreCase(CharSequence cs, CharSequence... searchCharSequences) {
if (isEmpty(cs) || isEmpty(searchCharSequences)) {
public static boolean containsAnyIgnoreCase(CharSequence cs, CharSequence... searchCharSequences)
{
if (isEmpty(cs) || isEmpty(searchCharSequences))
{
return false;
}
for (CharSequence testStr : searchCharSequences) {
if (containsIgnoreCase(cs, testStr)) {
for (CharSequence testStr : searchCharSequences)
{
if (containsIgnoreCase(cs, testStr))
{
return true;
}
}
@@ -338,8 +482,10 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
/**
* 驼峰转下划线命名
*/
public static String toUnderScoreCase(String str) {
if (str == null) {
public static String toUnderScoreCase(String str)
{
if (str == null)
{
return null;
}
StringBuilder sb = new StringBuilder();
@@ -349,23 +495,31 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
boolean curreCharIsUpperCase = true;
// 下一字符是否大写
boolean nexteCharIsUpperCase = true;
for (int i = 0; i < str.length(); i++) {
for (int i = 0; i < str.length(); i++)
{
char c = str.charAt(i);
if (i > 0) {
if (i > 0)
{
preCharIsUpperCase = Character.isUpperCase(str.charAt(i - 1));
} else {
}
else
{
preCharIsUpperCase = false;
}
curreCharIsUpperCase = Character.isUpperCase(c);
if (i < (str.length() - 1)) {
if (i < (str.length() - 1))
{
nexteCharIsUpperCase = Character.isUpperCase(str.charAt(i + 1));
}
if (preCharIsUpperCase && curreCharIsUpperCase && !nexteCharIsUpperCase) {
if (preCharIsUpperCase && curreCharIsUpperCase && !nexteCharIsUpperCase)
{
sb.append(SEPARATOR);
} else if ((i != 0 && !preCharIsUpperCase) && curreCharIsUpperCase) {
}
else if ((i != 0 && !preCharIsUpperCase) && curreCharIsUpperCase)
{
sb.append(SEPARATOR);
}
sb.append(Character.toLowerCase(c));
@@ -377,14 +531,18 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
/**
* 是否包含字符串
*
* @param str 验证字符串
* @param str 验证字符串
* @param strs 字符串组
* @return 包含返回true
*/
public static boolean inStringIgnoreCase(String str, String... strs) {
if (str != null && strs != null) {
for (String s : strs) {
if (str.equalsIgnoreCase(trim(s))) {
public static boolean inStringIgnoreCase(String str, String... strs)
{
if (str != null && strs != null)
{
for (String s : strs)
{
if (str.equalsIgnoreCase(trim(s)))
{
return true;
}
}
@@ -398,21 +556,27 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
* @param name 转换前的下划线大写方式命名的字符串
* @return 转换后的驼峰式命名的字符串
*/
public static String convertToCamelCase(String name) {
public static String convertToCamelCase(String name)
{
StringBuilder result = new StringBuilder();
// 快速检查
if (name == null || name.isEmpty()) {
if (name == null || name.isEmpty())
{
// 没必要转换
return "";
} else if (!name.contains("_")) {
}
else if (!name.contains("_"))
{
// 不含下划线,仅将首字母大写
return name.substring(0, 1).toUpperCase() + name.substring(1);
}
// 用下划线将原始字符串分割
String[] camels = name.split("_");
for (String camel : camels) {
for (String camel : camels)
{
// 跳过原始字符串中开头、结尾的下换线或双重下划线
if (camel.isEmpty()) {
if (camel.isEmpty())
{
continue;
}
// 首字母大写
@@ -423,24 +587,37 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
}
/**
* 驼峰式命名法 例如user_name->userName
* 驼峰式命名法
* 例如user_name->userName
*/
public static String toCamelCase(String s) {
if (s == null) {
public static String toCamelCase(String s)
{
if (s == null)
{
return null;
}
if (s.indexOf(SEPARATOR) == -1)
{
return s;
}
s = s.toLowerCase();
StringBuilder sb = new StringBuilder(s.length());
boolean upperCase = false;
for (int i = 0; i < s.length(); i++) {
for (int i = 0; i < s.length(); i++)
{
char c = s.charAt(i);
if (c == SEPARATOR) {
if (c == SEPARATOR)
{
upperCase = true;
} else if (upperCase) {
}
else if (upperCase)
{
sb.append(Character.toUpperCase(c));
upperCase = false;
} else {
}
else
{
sb.append(c);
}
}
@@ -450,16 +627,20 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
/**
* 查找指定字符串是否匹配指定字符串列表中的任意一个字符串
*
* @param str 指定字符串
* @param str 指定字符串
* @param strs 需要检查的字符串数组
* @return 是否匹配
*/
public static boolean matches(String str, List<String> strs) {
if (isEmpty(str) || isEmpty(strs)) {
public static boolean matches(String str, List<String> strs)
{
if (isEmpty(str) || isEmpty(strs))
{
return false;
}
for (String pattern : strs) {
if (isMatch(pattern, str)) {
for (String pattern : strs)
{
if (isMatch(pattern, str))
{
return true;
}
}
@@ -473,202 +654,67 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
* ** 表示任意层路径;
*
* @param pattern 匹配规则
* @param url 需要匹配的url
* @param url 需要匹配的url
* @return
*/
public static boolean isMatch(String pattern, String url) {
public static boolean isMatch(String pattern, String url)
{
AntPathMatcher matcher = new AntPathMatcher();
return matcher.match(pattern, url);
}
@SuppressWarnings("unchecked")
public static <T> T cast(Object obj) {
public static <T> T cast(Object obj)
{
return (T) obj;
}
/**
* 数字左边补齐0使之达到指定长度。注意如果数字转换为字符串后长度大于size则只保留 最后size个字符。
*
* @param num 数字对象
* @param num 数字对象
* @param size 字符串指定长度
* @return 返回数字的字符串格式,该字符串为指定长度。
*/
public static final String padl(final Number num, final int size) {
public static final String padl(final Number num, final int size)
{
return padl(num.toString(), size, '0');
}
/**
* 字符串左补齐。如果原始字符串s长度大于size则只保留最后size个字符。
*
* @param s 原始字符串
* @param s 原始字符串
* @param size 字符串指定长度
* @param c 用于补齐的字符
* @param c 用于补齐的字符
* @return 返回指定长度的字符串,由原字符串左补齐或截取得到。
*/
public static final String padl(final String s, final int size, final char c) {
public static final String padl(final String s, final int size, final char c)
{
final StringBuilder sb = new StringBuilder(size);
if (s != null) {
if (s != null)
{
final int len = s.length();
if (s.length() <= size) {
for (int i = size - len; i > 0; i--) {
if (s.length() <= size)
{
for (int i = size - len; i > 0; i--)
{
sb.append(c);
}
sb.append(s);
} else {
}
else
{
return s.substring(len - size, len);
}
} else {
for (int i = size; i > 0; i--) {
}
else
{
for (int i = size; i > 0; i--)
{
sb.append(c);
}
}
return sb.toString();
}
/*将字符串转小写,首字母大写,其他小写*/
public static String upperCase(String str) {
char[] ch = str.toLowerCase().toCharArray();
if (ch[0] >= 'a' && ch[0] <= 'z') {
ch[0] = (char) (ch[0] - 32);
}
return new String(ch);
}
public static String toString(Object value) {
if (value == null) {
return "null";
}
if (value instanceof ByteBuf) {
return ByteBufUtil.hexDump((ByteBuf) value);
}
if (!value.getClass().isArray()) {
return value.toString();
}
StringBuilder root = new StringBuilder(32);
toString(value, root);
return root.toString();
}
public static StringBuilder toString(Object value, StringBuilder builder) {
if (value == null) {
return builder;
}
builder.append('[');
int start = builder.length();
if (value instanceof long[]) {
long[] array = (long[]) value;
for (long t : array) {
builder.append(t).append(',');
}
} else if (value instanceof int[]) {
int[] array = (int[]) value;
for (int t : array) {
builder.append(t).append(',');
}
} else if (value instanceof short[]) {
short[] array = (short[]) value;
for (short t : array) {
builder.append(t).append(',');
}
} else if (value instanceof byte[]) {
byte[] array = (byte[]) value;
for (byte t : array) {
builder.append(t).append(',');
}
} else if (value instanceof char[]) {
char[] array = (char[]) value;
for (char t : array) {
builder.append(t).append(',');
}
} else if (value instanceof double[]) {
double[] array = (double[]) value;
for (double t : array) {
builder.append(t).append(',');
}
} else if (value instanceof float[]) {
float[] array = (float[]) value;
for (float t : array) {
builder.append(t).append(',');
}
} else if (value instanceof boolean[]) {
boolean[] array = (boolean[]) value;
for (boolean t : array) {
builder.append(t).append(',');
}
} else if (value instanceof String[]) {
String[] array = (String[]) value;
for (String t : array) {
builder.append(t).append(',');
}
} else if (isArray1(value)) {
Object[] array = (Object[]) value;
for (Object t : array) {
toString(t, builder).append(',');
}
} else if (value instanceof Object[]) {
Object[] array = (Object[]) value;
for (Object t : array) {
builder.append(t).append(',');
}
}
int end = builder.length();
if (end <= start) {
builder.append(']');
} else {
builder.setCharAt(end - 1, ']');
}
return builder;
}
private static boolean isArray1(Object value) {
Class<?> componentType = value.getClass().getComponentType();
if (componentType == null) {
return false;
}
return componentType.isArray();
}
public static String leftPad(String str, int size, char ch) {
int length = str.length();
int pads = size - length;
if (pads > 0) {
char[] result = new char[size];
str.getChars(0, length, result, pads);
while (pads > 0) {
result[--pads] = ch;
}
return new String(result);
}
return str;
}
/**
* 获取字符串中的数字
* @param str
* @return
*/
public static Integer matcherNum(String str){
Pattern pattern = Pattern.compile("\\d+");
Matcher matcher = pattern.matcher(str);
while (matcher.find()){
return Integer.parseInt(matcher.group());
}
return 0;
}
}

View File

@@ -24,8 +24,7 @@ import java.util.*;
@Component
public class TopicsUtils {
@Value("${server.broker.enabled}")
private Boolean enabled;
private Boolean enabled = true;
/**
* 拼接topic

View File

@@ -21,6 +21,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fastbee.common.constant.Constants;
import com.fastbee.common.utils.StringUtils;
import org.springframework.http.MediaType;
/**
* 通用http发送方法
@@ -74,7 +75,7 @@ public class HttpUtils
URLConnection connection = realUrl.openConnection();
connection.setRequestProperty("accept", "*/*");
connection.setRequestProperty("connection", "Keep-Alive");
connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
connection.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)");
connection.connect();
in = new BufferedReader(new InputStreamReader(connection.getInputStream(), contentType));
String line;
@@ -121,10 +122,23 @@ public class HttpUtils
* 向指定 URL 发送POST方法的请求
*
* @param url 发送请求的 URL
* @param param 请求参数,请求参数应该是 JSON String格式 的形式。
* @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
* @return 所代表远程资源的响应结果
*/
public static String sendPost(String url, String param)
{
return sendPost(url, param, MediaType.APPLICATION_FORM_URLENCODED_VALUE);
}
/**
* 向指定 URL 发送POST方法的请求
*
* @param url 发送请求的 URL
* @param param 请求参数
* @param contentType 内容类型
* @return 所代表远程资源的响应结果
*/
public static String sendPost(String url, String param, String contentType)
{
PrintWriter out = null;
BufferedReader in = null;
@@ -136,9 +150,9 @@ public class HttpUtils
URLConnection conn = realUrl.openConnection();
conn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("connection", "Keep-Alive");
conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
conn.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)");
conn.setRequestProperty("Accept-Charset", "utf-8");
conn.setRequestProperty("contentType", "utf-8");
conn.setRequestProperty("Content-Type", contentType);
conn.setDoOutput(true);
conn.setDoInput(true);
out = new PrintWriter(conn.getOutputStream());
@@ -190,6 +204,11 @@ public class HttpUtils
}
public static String sendSSLPost(String url, String param)
{
return sendSSLPost(url, param, MediaType.APPLICATION_FORM_URLENCODED_VALUE);
}
public static String sendSSLPost(String url, String param, String contentType)
{
StringBuilder result = new StringBuilder();
String urlNameString = url + "?" + param;
@@ -202,9 +221,9 @@ public class HttpUtils
HttpsURLConnection conn = (HttpsURLConnection) console.openConnection();
conn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("connection", "Keep-Alive");
conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
conn.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)");
conn.setRequestProperty("Accept-Charset", "utf-8");
conn.setRequestProperty("contentType", "utf-8");
conn.setRequestProperty("Content-Type", contentType);
conn.setDoOutput(true);
conn.setDoInput(true);

View File

@@ -1,20 +1,39 @@
package com.fastbee.common.utils.ip;
import com.fastbee.common.utils.ServletUtils;
import com.fastbee.common.utils.StringUtils;
import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.UnknownHostException;
import javax.servlet.http.HttpServletRequest;
import com.fastbee.common.utils.StringUtils;
/**
* 获取IP方法
*
*
* @author ruoyi
*/
public class IpUtils
{
public final static String REGX_0_255 = "(25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]\\d|\\d)";
// 匹配 ip
public final static String REGX_IP = "((" + REGX_0_255 + "\\.){3}" + REGX_0_255 + ")";
public final static String REGX_IP_WILDCARD = "(((\\*\\.){3}\\*)|(" + REGX_0_255 + "(\\.\\*){3})|(" + REGX_0_255 + "\\." + REGX_0_255 + ")(\\.\\*){2}" + "|((" + REGX_0_255 + "\\.){3}\\*))";
// 匹配网段
public final static String REGX_IP_SEG = "(" + REGX_IP + "\\-" + REGX_IP + ")";
/**
* 获取客户端IP
*
*
* @return IP地址
*/
public static String getIpAddr()
{
return getIpAddr(ServletUtils.getRequest());
}
/**
* 获取客户端IP
*
* @param request 请求对象
* @return IP地址
*/
@@ -52,7 +71,7 @@ public class IpUtils
/**
* 检查是否为内部IP地址
*
*
* @param ip IP地址
* @return 结果
*/
@@ -64,7 +83,7 @@ public class IpUtils
/**
* 检查是否为内部IP地址
*
*
* @param addr byte地址
* @return 结果
*/
@@ -107,7 +126,7 @@ public class IpUtils
/**
* 将IPv4地址转换成字节
*
*
* @param text IPv4地址
* @return byte 字节
*/
@@ -195,7 +214,7 @@ public class IpUtils
/**
* 获取IP地址
*
*
* @return 本地IP地址
*/
public static String getHostIp()
@@ -212,7 +231,7 @@ public class IpUtils
/**
* 获取主机名
*
*
* @return 本地主机名
*/
public static String getHostName()
@@ -248,7 +267,7 @@ public class IpUtils
}
}
}
return ip;
return StringUtils.substring(ip, 0, 255);
}
/**
@@ -261,4 +280,104 @@ public class IpUtils
{
return StringUtils.isBlank(checkString) || "unknown".equalsIgnoreCase(checkString);
}
}
/**
* 是否为IP
*/
public static boolean isIP(String ip)
{
return StringUtils.isNotBlank(ip) && ip.matches(REGX_IP);
}
/**
* 是否为IP或 *为间隔的通配符地址
*/
public static boolean isIpWildCard(String ip)
{
return StringUtils.isNotBlank(ip) && ip.matches(REGX_IP_WILDCARD);
}
/**
* 检测参数是否在ip通配符里
*/
public static boolean ipIsInWildCardNoCheck(String ipWildCard, String ip)
{
String[] s1 = ipWildCard.split("\\.");
String[] s2 = ip.split("\\.");
boolean isMatchedSeg = true;
for (int i = 0; i < s1.length && !s1[i].equals("*"); i++)
{
if (!s1[i].equals(s2[i]))
{
isMatchedSeg = false;
break;
}
}
return isMatchedSeg;
}
/**
* 是否为特定格式如:“10.10.10.1-10.10.10.99”的ip段字符串
*/
public static boolean isIPSegment(String ipSeg)
{
return StringUtils.isNotBlank(ipSeg) && ipSeg.matches(REGX_IP_SEG);
}
/**
* 判断ip是否在指定网段中
*/
public static boolean ipIsInNetNoCheck(String iparea, String ip)
{
int idx = iparea.indexOf('-');
String[] sips = iparea.substring(0, idx).split("\\.");
String[] sipe = iparea.substring(idx + 1).split("\\.");
String[] sipt = ip.split("\\.");
long ips = 0L, ipe = 0L, ipt = 0L;
for (int i = 0; i < 4; ++i)
{
ips = ips << 8 | Integer.parseInt(sips[i]);
ipe = ipe << 8 | Integer.parseInt(sipe[i]);
ipt = ipt << 8 | Integer.parseInt(sipt[i]);
}
if (ips > ipe)
{
long t = ips;
ips = ipe;
ipe = t;
}
return ips <= ipt && ipt <= ipe;
}
/**
* 校验ip是否符合过滤串规则
*
* @param filter 过滤IP列表,支持后缀'*'通配,支持网段如:`10.10.10.1-10.10.10.99`
* @param ip 校验IP地址
* @return boolean 结果
*/
public static boolean isMatchedIp(String filter, String ip)
{
if (StringUtils.isEmpty(filter) || StringUtils.isEmpty(ip))
{
return false;
}
String[] ips = filter.split(";");
for (String iStr : ips)
{
if (isIP(iStr) && iStr.equals(ip))
{
return true;
}
else if (isIpWildCard(iStr) && ipIsInWildCardNoCheck(iStr, ip))
{
return true;
}
else if (isIPSegment(iStr) && ipIsInNetNoCheck(iStr, ip))
{
return true;
}
}
return false;
}
}

View File

@@ -1,19 +1,24 @@
package com.fastbee.common.utils.poi;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Workbook;
/**
* Excel数据格式处理适配器
*
*
* @author ruoyi
*/
public interface ExcelHandlerAdapter
{
/**
* 格式化
*
*
* @param value 单元格数据值
* @param args excel注解args参数组
* @param cell 单元格对象
* @param wb 工作簿对象
*
* @return 处理后的值
*/
Object format(Object value, String[] args);
Object format(Object value, String[] args, Cell cell, Workbook wb);
}

View File

@@ -1,5 +1,6 @@
package com.fastbee.common.utils.spring;
import org.springframework.aop.framework.Advised;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
@@ -18,11 +19,11 @@ import java.util.Map;
/**
* spring工具类 方便在非spring管理环境中获取bean
*
*
* @author ruoyi
*/
@Component
public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationContextAware
public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationContextAware
{
/** Spring应用上下文环境 */
private static ConfigurableListableBeanFactory beanFactory;
@@ -30,13 +31,13 @@ public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationC
private static ApplicationContext applicationContext;
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException
{
SpringUtils.beanFactory = beanFactory;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
{
SpringUtils.applicationContext = applicationContext;
}
@@ -119,14 +120,19 @@ public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationC
/**
* 获取aop代理对象
*
*
* @param invoker
* @return
*/
@SuppressWarnings("unchecked")
public static <T> T getAopProxy(T invoker)
{
return (T) AopContext.currentProxy();
Object proxy = AopContext.currentProxy();
if (((Advised) proxy).getTargetSource().getTargetClass() == invoker.getClass())
{
return (T) proxy;
}
return invoker;
}
/**

View File

@@ -5,7 +5,7 @@ import com.fastbee.common.utils.StringUtils;
/**
* sql操作工具类
*
*
* @author ruoyi
*/
public class SqlUtil
@@ -13,13 +13,18 @@ public class SqlUtil
/**
* 定义常用的 sql关键字
*/
public static String SQL_REGEX = "and |extractvalue|updatexml|exec |insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |or |+|user()";
public static String SQL_REGEX = "\u000B|and |extractvalue|updatexml|sleep|exec |insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |or |union |like |+|/*|user()";
/**
* 仅支持字母、数字、下划线、空格、逗号、小数点(支持多个字段排序)
*/
public static String SQL_PATTERN = "[a-zA-Z0-9_\\ \\,\\.]+";
/**
* 限制orderBy最大长度
*/
private static final int ORDER_BY_MAX_LENGTH = 500;
/**
* 检查字符,防止注入绕过
*/
@@ -29,6 +34,10 @@ public class SqlUtil
{
throw new UtilException("参数不符合规范,不能进行查询");
}
if (StringUtils.length(value) > ORDER_BY_MAX_LENGTH)
{
throw new UtilException("参数已超过最大限制,不能进行查询");
}
return value;
}

View File

@@ -22,21 +22,21 @@ public class Seq
private static AtomicInteger uploadSeq = new AtomicInteger(1);
// 机器标识
private static String machineCode = "A";
private static final String machineCode = "A";
/**
* 获取通用序列号
*
*
* @return 序列值
*/
public static String getId()
{
return getId(commSeqType);
}
/**
* 默认16位序列号 yyMMddHHmmss + 一位机器标识 + 3长度循环递增字符串
*
*
* @return 序列值
*/
public static String getId(String type)
@@ -51,7 +51,7 @@ public class Seq
/**
* 通用接口序列号 yyMMddHHmmss + 一位机器标识 + length长度循环递增字符串
*
*
* @param atomicInt 序列数
* @param length 数值长度
* @return 序列值
@@ -66,7 +66,7 @@ public class Seq
/**
* 序列循环递增字符串[1, 10 的 (length)幂次方), 用0左补齐length位数
*
*
* @return 序列值
*/
private synchronized static String getSeq(AtomicInteger atomicInt, int length)

View File

@@ -8,7 +8,7 @@ import java.util.regex.Pattern;
/**
* 自定义xss校验注解实现
*
*
* @author ruoyi
*/
public class XssValidator implements ConstraintValidator<Xss, String>
@@ -27,8 +27,13 @@ public class XssValidator implements ConstraintValidator<Xss, String>
public static boolean containsHtml(String value)
{
StringBuilder sHtml = new StringBuilder();
Pattern pattern = Pattern.compile(HTML_PATTERN);
Matcher matcher = pattern.matcher(value);
return matcher.matches();
while (matcher.find())
{
sHtml.append(matcher.group());
}
return pattern.matcher(sHtml).matches();
}
}
}

View File

@@ -5,7 +5,7 @@
<parent>
<artifactId>fastbee</artifactId>
<groupId>com.fastbee</groupId>
<version>3.8.5</version>
<version>${fastbee.version}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -16,6 +16,10 @@
</description>
<dependencies>
<dependency>
<groupId>com.fastbee</groupId>
<artifactId>fastbee-common</artifactId>
</dependency>
<!-- SpringBoot Web容器 -->
<dependency>
@@ -59,6 +63,13 @@
<artifactId>oshi-core</artifactId>
</dependency>
<!-- 动态数据源 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>${dynamic-datasource.version}</version>
</dependency>
</dependencies>
</project>
</project>

View File

@@ -7,6 +7,7 @@ import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
import com.fastbee.common.annotation.DataScope;
import com.fastbee.common.constant.UserConstants;
import com.fastbee.common.core.domain.BaseEntity;
import com.fastbee.common.core.domain.entity.SysRole;
import com.fastbee.common.core.domain.entity.SysUser;
@@ -73,8 +74,7 @@ public class DataScopeAspect
if (StringUtils.isNotNull(currentUser) && !currentUser.isAdmin())
{
String permission = StringUtils.defaultIfEmpty(controllerDataScope.permission(), PermissionContextHolder.getContext());
dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptAlias(),
controllerDataScope.userAlias(), permission);
dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptAlias(), controllerDataScope.userAlias(), permission);
}
}
}
@@ -92,29 +92,42 @@ public class DataScopeAspect
{
StringBuilder sqlString = new StringBuilder();
List<String> conditions = new ArrayList<String>();
List<String> scopeCustomIds = new ArrayList<String>();
user.getRoles().forEach(role -> {
if (DATA_SCOPE_CUSTOM.equals(role.getDataScope()) && StringUtils.equals(role.getStatus(), UserConstants.ROLE_NORMAL) && StringUtils.containsAny(role.getPermissions(), Convert.toStrArray(permission)))
{
scopeCustomIds.add(Convert.toStr(role.getRoleId()));
}
});
for (SysRole role : user.getRoles())
{
String dataScope = role.getDataScope();
if (!DATA_SCOPE_CUSTOM.equals(dataScope) && conditions.contains(dataScope))
if (conditions.contains(dataScope) || StringUtils.equals(role.getStatus(), UserConstants.ROLE_DISABLE))
{
continue;
}
if (StringUtils.isNotEmpty(permission) && StringUtils.isNotEmpty(role.getPermissions())
&& !StringUtils.containsAny(role.getPermissions(), Convert.toStrArray(permission)))
if (!StringUtils.containsAny(role.getPermissions(), Convert.toStrArray(permission)))
{
continue;
}
if (DATA_SCOPE_ALL.equals(dataScope))
{
sqlString = new StringBuilder();
conditions.add(dataScope);
break;
}
else if (DATA_SCOPE_CUSTOM.equals(dataScope))
{
sqlString.append(StringUtils.format(
" OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ", deptAlias,
role.getRoleId()));
if (scopeCustomIds.size() > 1)
{
// 多个自定数据权限使用in查询避免多次拼接。
sqlString.append(StringUtils.format(" OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id in ({}) ) ", deptAlias, String.join(",", scopeCustomIds)));
}
else
{
sqlString.append(StringUtils.format(" OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ", deptAlias, role.getRoleId()));
}
}
else if (DATA_SCOPE_DEPT.equals(dataScope))
{
@@ -122,9 +135,7 @@ public class DataScopeAspect
}
else if (DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope))
{
sqlString.append(StringUtils.format(
" OR {}.dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} or find_in_set( {} , ancestors ) )",
deptAlias, user.getDeptId(), user.getDeptId()));
sqlString.append(StringUtils.format(" OR {}.dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} or find_in_set( {} , ancestors ) )", deptAlias, user.getDeptId(), user.getDeptId()));
}
else if (DATA_SCOPE_SELF.equals(dataScope))
{
@@ -141,6 +152,12 @@ public class DataScopeAspect
conditions.add(dataScope);
}
// 角色都不包含传递过来的权限字符这个时候sqlString也会为空所以要限制一下,不查询任何数据
if (StringUtils.isEmpty(conditions))
{
sqlString.append(StringUtils.format(" OR {}.dept_id = 0 ", deptAlias));
}
if (StringUtils.isNotBlank(sqlString.toString()))
{
Object params = joinPoint.getArgs()[0];

View File

@@ -1,24 +1,14 @@
package com.fastbee.framework.aspectj;
import java.util.Collection;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.web.multipart.MultipartFile;
import com.alibaba.fastjson2.JSON;
import com.fastbee.common.annotation.Log;
import com.fastbee.common.core.domain.entity.SysUser;
import com.fastbee.common.core.domain.model.LoginUser;
import com.fastbee.common.core.text.Convert;
import com.fastbee.common.enums.BusinessStatus;
import com.fastbee.common.enums.HttpMethod;
import com.fastbee.common.filter.PropertyPreExcludeFilter;
import com.fastbee.common.utils.ExceptionUtil;
import com.fastbee.common.utils.SecurityUtils;
import com.fastbee.common.utils.ServletUtils;
import com.fastbee.common.utils.StringUtils;
@@ -26,10 +16,27 @@ import com.fastbee.common.utils.ip.IpUtils;
import com.fastbee.framework.manager.AsyncManager;
import com.fastbee.framework.manager.factory.AsyncFactory;
import com.fastbee.system.domain.SysOperLog;
import org.apache.commons.lang3.ArrayUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.NamedThreadLocal;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Collection;
import java.util.Map;
/**
* 操作日志记录处理
*
*
* @author ruoyi
*/
@Aspect
@@ -41,6 +48,18 @@ public class LogAspect
/** 排除敏感属性字段 */
public static final String[] EXCLUDE_PROPERTIES = { "password", "oldPassword", "newPassword", "confirmPassword" };
/** 计算操作消耗时间 */
private static final ThreadLocal<Long> TIME_THREADLOCAL = new NamedThreadLocal<Long>("Cost Time");
/**
* 处理请求前执行
*/
@Before(value = "@annotation(controllerLog)")
public void doBefore(JoinPoint joinPoint, Log controllerLog)
{
TIME_THREADLOCAL.set(System.currentTimeMillis());
}
/**
* 处理完请求后执行
*
@@ -54,7 +73,7 @@ public class LogAspect
/**
* 拦截异常操作
*
*
* @param joinPoint 切点
* @param e 异常
*/
@@ -75,18 +94,23 @@ public class LogAspect
SysOperLog operLog = new SysOperLog();
operLog.setStatus(BusinessStatus.SUCCESS.ordinal());
// 请求的地址
String ip = IpUtils.getIpAddr(ServletUtils.getRequest());
String ip = IpUtils.getIpAddr();
operLog.setOperIp(ip);
operLog.setOperUrl(StringUtils.substring(ServletUtils.getRequest().getRequestURI(), 0, 255));
if (loginUser != null)
{
operLog.setOperName(loginUser.getUsername());
SysUser currentUser = loginUser.getUser();
if (StringUtils.isNotNull(currentUser) && StringUtils.isNotNull(currentUser.getDept()))
{
operLog.setDeptName(currentUser.getDept().getDeptName());
}
}
if (e != null)
{
operLog.setStatus(BusinessStatus.FAIL.ordinal());
operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000));
operLog.setErrorMsg(StringUtils.substring(Convert.toStr(e.getMessage(), ExceptionUtil.getExceptionMessage(e)), 0, 2000));
}
// 设置方法名称
String className = joinPoint.getTarget().getClass().getName();
@@ -96,6 +120,8 @@ public class LogAspect
operLog.setRequestMethod(ServletUtils.getRequest().getMethod());
// 处理设置注解上的参数
getControllerMethodDescription(joinPoint, controllerLog, operLog, jsonResult);
// 设置消耗时间
operLog.setCostTime(System.currentTimeMillis() - TIME_THREADLOCAL.get());
// 保存数据库
AsyncManager.me().execute(AsyncFactory.recordOper(operLog));
}
@@ -105,11 +131,15 @@ public class LogAspect
log.error("异常信息:{}", exp.getMessage());
exp.printStackTrace();
}
finally
{
TIME_THREADLOCAL.remove();
}
}
/**
* 获取注解中对方法的描述信息 用于Controller层注解
*
*
* @param log 日志
* @param operLog 操作日志
* @throws Exception
@@ -126,7 +156,7 @@ public class LogAspect
if (log.isSaveRequestData())
{
// 获取参数的信息,传入到数据库中。
setRequestValue(joinPoint, operLog);
setRequestValue(joinPoint, operLog, log.excludeParamNames());
}
// 是否需要保存response参数和值
if (log.isSaveResponseData() && StringUtils.isNotNull(jsonResult))
@@ -137,29 +167,29 @@ public class LogAspect
/**
* 获取请求的参数放到log中
*
*
* @param operLog 操作日志
* @throws Exception 异常
*/
private void setRequestValue(JoinPoint joinPoint, SysOperLog operLog) throws Exception
private void setRequestValue(JoinPoint joinPoint, SysOperLog operLog, String[] excludeParamNames) throws Exception
{
Map<?, ?> paramsMap = ServletUtils.getParamMap(ServletUtils.getRequest());
String requestMethod = operLog.getRequestMethod();
if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod))
if (StringUtils.isEmpty(paramsMap) && StringUtils.equalsAny(requestMethod, HttpMethod.PUT.name(), HttpMethod.POST.name(), HttpMethod.DELETE.name()))
{
String params = argsArrayToString(joinPoint.getArgs());
String params = argsArrayToString(joinPoint.getArgs(), excludeParamNames);
operLog.setOperParam(StringUtils.substring(params, 0, 2000));
}
else
{
Map<?, ?> paramsMap = ServletUtils.getParamMap(ServletUtils.getRequest());
operLog.setOperParam(StringUtils.substring(JSON.toJSONString(paramsMap, excludePropertyPreFilter()), 0, 2000));
operLog.setOperParam(StringUtils.substring(JSON.toJSONString(paramsMap, excludePropertyPreFilter(excludeParamNames)), 0, 2000));
}
}
/**
* 参数拼装
*/
private String argsArrayToString(Object[] paramsArray)
private String argsArrayToString(Object[] paramsArray, String[] excludeParamNames)
{
String params = "";
if (paramsArray != null && paramsArray.length > 0)
@@ -170,7 +200,7 @@ public class LogAspect
{
try
{
String jsonObj = JSON.toJSONString(o, excludePropertyPreFilter());
String jsonObj = JSON.toJSONString(o, excludePropertyPreFilter(excludeParamNames));
params += jsonObj.toString() + " ";
}
catch (Exception e)
@@ -185,14 +215,14 @@ public class LogAspect
/**
* 忽略敏感属性
*/
public PropertyPreExcludeFilter excludePropertyPreFilter()
public PropertyPreExcludeFilter excludePropertyPreFilter(String[] excludeParamNames)
{
return new PropertyPreExcludeFilter().addExcludes(EXCLUDE_PROPERTIES);
return new PropertyPreExcludeFilter().addExcludes(ArrayUtils.addAll(EXCLUDE_PROPERTIES, excludeParamNames));
}
/**
* 判断是否需要过滤的对象。
*
*
* @param o 对象信息。
* @return 如果是需要过滤的对象则返回true否则返回false。
*/

View File

@@ -79,7 +79,7 @@ public class RateLimiterAspect
StringBuffer stringBuffer = new StringBuffer(rateLimiter.key());
if (rateLimiter.limitType() == LimitType.IP)
{
stringBuffer.append(IpUtils.getIpAddr(ServletUtils.getRequest())).append("-");
stringBuffer.append(IpUtils.getIpAddr()).append("-");
}
MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod();

View File

@@ -1,7 +1,6 @@
package com.fastbee.framework.config;
import java.util.TimeZone;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -15,8 +14,6 @@ import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
// 表示通过aop框架暴露该代理对象,AopContext能够访问
@EnableAspectJAutoProxy(exposeProxy = true)
// 指定要扫描的Mapper类的包的路径
@MapperScan("com.fastbee.**.mapper")
public class ApplicationConfig
{
/**

View File

@@ -0,0 +1,61 @@
package com.fastbee.framework.config;
import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
import com.baomidou.dynamic.datasource.creator.DefaultDataSourceCreator;
import com.baomidou.dynamic.datasource.provider.AbstractDataSourceProvider;
import com.baomidou.dynamic.datasource.provider.DynamicDataSourceProvider;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Configuration
public class DataSourceConfig {
private final DynamicDataSourceProperties properties;
private final DefaultDataSourceCreator dataSourceCreator;
public DataSourceConfig(DynamicDataSourceProperties properties,
DefaultDataSourceCreator dataSourceCreator) {
this.properties = properties;
this.dataSourceCreator = dataSourceCreator;
}
@Bean
public DynamicDataSourceProvider dynamicDataSourceProvider() {
return new AbstractDataSourceProvider(dataSourceCreator) {
@Override
public Map<String, DataSource> loadDataSources() {
Map<String, DataSource> dataSourceMap = new HashMap<>();
return dataSourceMap;
}
};
}
@Primary
@Bean
public DataSource dataSource(List<DynamicDataSourceProvider> providers) {
DynamicRoutingDataSource dataSource = new DynamicRoutingDataSource(providers);
dataSource.setPrimary(properties.getPrimary());
dataSource.setStrict(properties.getStrict());
dataSource.setStrategy(properties.getStrategy());
dataSource.setP6spy(properties.getP6spy());
dataSource.setSeata(properties.getSeata());
return dataSource;
}
@Bean
public DataSourceTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}

View File

@@ -1,89 +1,23 @@
package com.fastbee.framework.config;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.sql.DataSource;
import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties;
import com.alibaba.druid.util.Utils;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties;
import com.alibaba.druid.util.Utils;
import com.fastbee.common.enums.DataSourceType;
import com.fastbee.common.utils.spring.SpringUtils;
import com.fastbee.framework.config.properties.DruidProperties;
import com.fastbee.framework.datasource.DynamicDataSource;
/**
* druid 配置多数据源
*
* @author ruoyi
*/
import javax.servlet.*;
import java.io.IOException;
@Configuration
public class DruidConfig
{
@Bean
@ConfigurationProperties("spring.datasource.druid.master")
public DataSource masterDataSource(DruidProperties druidProperties)
{
DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
return druidProperties.dataSource(dataSource);
}
@Bean
@ConfigurationProperties("spring.datasource.druid.slave")
@ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true")
public DataSource slaveDataSource(DruidProperties druidProperties)
{
DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
return druidProperties.dataSource(dataSource);
}
@Bean(name = "dynamicDataSource")
@Primary
public DynamicDataSource dataSource(DataSource masterDataSource)
{
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource);
setDataSource(targetDataSources, DataSourceType.SLAVE.name(), "slaveDataSource");
return new DynamicDataSource(masterDataSource, targetDataSources);
}
/**
* 设置数据源
*
* @param targetDataSources 备选数据源集合
* @param sourceName 数据源名称
* @param beanName bean名称
*/
public void setDataSource(Map<Object, Object> targetDataSources, String sourceName, String beanName)
{
try
{
DataSource dataSource = SpringUtils.getBean(beanName);
targetDataSources.put(sourceName, dataSource);
}
catch (Exception e)
{
}
}
public class DruidConfig {
/**
* 去除监控页面底部的广告
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
@Bean
@ConditionalOnProperty(name = "spring.datasource.druid.statViewServlet.enabled", havingValue = "true")
@ConditionalOnProperty(name = "spring.datasource.druid.stat-view-servlet.enabled", havingValue = "true")
public FilterRegistrationBean removeDruidFilterRegistrationBean(DruidStatProperties properties)
{
// 获取web监控页面的参数

View File

@@ -9,7 +9,7 @@ import com.alibaba.fastjson2.JSONWriter;
/**
* Redis使用FastJson序列化
*
*
* @author ruoyi
*/
public class FastJson2JsonRedisSerializer<T> implements RedisSerializer<T>

View File

@@ -1,16 +1,17 @@
package com.fastbee.framework.config;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.DispatcherType;
import com.fastbee.common.filter.RepeatableFilter;
import com.fastbee.common.filter.XssFilter;
import com.fastbee.common.utils.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.fastbee.common.filter.RepeatableFilter;
import com.fastbee.common.filter.XssFilter;
import com.fastbee.common.utils.StringUtils;
import javax.servlet.DispatcherType;
import java.util.HashMap;
import java.util.Map;
/**
* Filter配置

View File

@@ -0,0 +1,43 @@
package com.fastbee.framework.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
import org.springframework.web.servlet.i18n.SessionLocaleResolver;
import com.fastbee.common.constant.Constants;
/**
* 资源文件配置加载
*
* @author ruoyi
*/
@Configuration
public class I18nConfig implements WebMvcConfigurer
{
@Bean
public LocaleResolver localeResolver()
{
SessionLocaleResolver slr = new SessionLocaleResolver();
// 默认语言
slr.setDefaultLocale(Constants.DEFAULT_LOCALE);
return slr;
}
@Bean
public LocaleChangeInterceptor localeChangeInterceptor()
{
LocaleChangeInterceptor lci = new LocaleChangeInterceptor();
// 参数名
lci.setParamName("lang");
return lci;
}
@Override
public void addInterceptors(InterceptorRegistry registry)
{
registry.addInterceptor(localeChangeInterceptor());
}
}

View File

@@ -1,177 +0,0 @@
package com.fastbee.framework.config;
import com.baomidou.mybatisplus.autoconfigure.SpringBootVFS;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import com.fastbee.common.utils.StringUtils;
import org.apache.ibatis.io.VFS;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.util.ClassUtils;
import javax.sql.DataSource;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
/**
* Mybatis支持*匹配扫描包
*
* @author ruoyi
*/
@Configuration
public class MyBatisConfig
{
@Autowired
private Environment env;
static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";
public static String setTypeAliasesPackage(String typeAliasesPackage)
{
ResourcePatternResolver resolver = (ResourcePatternResolver) new PathMatchingResourcePatternResolver();
MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resolver);
List<String> allResult = new ArrayList<String>();
try
{
for (String aliasesPackage : typeAliasesPackage.split(","))
{
List<String> result = new ArrayList<String>();
aliasesPackage = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
+ ClassUtils.convertClassNameToResourcePath(aliasesPackage.trim()) + "/" + DEFAULT_RESOURCE_PATTERN;
Resource[] resources = resolver.getResources(aliasesPackage);
if (resources != null && resources.length > 0)
{
MetadataReader metadataReader = null;
for (Resource resource : resources)
{
if (resource.isReadable())
{
metadataReader = metadataReaderFactory.getMetadataReader(resource);
try
{
result.add(Class.forName(metadataReader.getClassMetadata().getClassName()).getPackage().getName());
}
catch (ClassNotFoundException e)
{
e.printStackTrace();
}
}
}
}
if (result.size() > 0)
{
HashSet<String> hashResult = new HashSet<String>(result);
allResult.addAll(hashResult);
}
}
if (allResult.size() > 0)
{
typeAliasesPackage = String.join(",", (String[]) allResult.toArray(new String[0]));
}
else
{
throw new RuntimeException("mybatis typeAliasesPackage 路径扫描错误,参数typeAliasesPackage:" + typeAliasesPackage + "未找到任何包");
}
}
catch (IOException e)
{
e.printStackTrace();
}
return typeAliasesPackage;
}
public Resource[] resolveMapperLocations(String[] mapperLocations)
{
ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();
List<Resource> resources = new ArrayList<Resource>();
if (mapperLocations != null)
{
for (String mapperLocation : mapperLocations)
{
try
{
Resource[] mappers = resourceResolver.getResources(mapperLocation);
resources.addAll(Arrays.asList(mappers));
}
catch (IOException e)
{
// ignore
}
}
}
return resources.toArray(new Resource[resources.size()]);
}
/**
* mybatis 配置
*/
// @Bean(name = "mysqlSessionFactory")
// @Primary
// public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception
// {
// String typeAliasesPackage = env.getProperty("mybatis.typeAliasesPackage");
// String mapperLocations = env.getProperty("mybatis.mapperLocations");
// String configLocation = env.getProperty("mybatis.configLocation");
// typeAliasesPackage = setTypeAliasesPackage(typeAliasesPackage);
// VFS.addImplClass(SpringBootVFS.class);
//
// final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
// sessionFactory.setDataSource(dataSource);
// sessionFactory.setTypeAliasesPackage(typeAliasesPackage);
// sessionFactory.setMapperLocations(resolveMapperLocations(StringUtils.split(mapperLocations, ",")));
// sessionFactory.setConfigLocation(new DefaultResourceLoader().getResource(configLocation));
// return sessionFactory.getObject();
// }
/**
* mybatis-plus 配置:把 SqlSessionFactoryBean 换成 MybatisSqlSessionFactoryBean就行
* @param dataSource 数据源
* @return
*/
@Bean(name = "mysqlSessionFactory")
@Primary
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception
{
String typeAliasesPackage = env.getProperty("mybatis-plus.typeAliasesPackage");
String mapperLocations = env.getProperty("mybatis-plus.mapperLocations");
String configLocation = env.getProperty("mybatis-plus.configLocation");
typeAliasesPackage = setTypeAliasesPackage(typeAliasesPackage);
VFS.addImplClass(SpringBootVFS.class);
final MybatisSqlSessionFactoryBean sessionFactory = new MybatisSqlSessionFactoryBean();
sessionFactory.setDataSource(dataSource);
sessionFactory.setTypeAliasesPackage(typeAliasesPackage);
sessionFactory.setMapperLocations(resolveMapperLocations(StringUtils.split(mapperLocations, ",")));
sessionFactory.setConfigLocation(new DefaultResourceLoader().getResource(configLocation));
return sessionFactory.getObject();
}
@Bean(name = "mysqlTransactionManager")
@Primary
public DataSourceTransactionManager mysqlTransactionManager(@Qualifier("dynamicDataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
@Bean(name = "mysqlSqlSessionTemplate")
@Primary
public SqlSessionTemplate mysqlSqlSessionTemplate(@Qualifier("mysqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
}

View File

@@ -11,7 +11,7 @@ import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* redis配置
*
*
* @author ruoyi
*/
@Configuration

View File

@@ -17,7 +17,7 @@ import com.fastbee.framework.interceptor.RepeatSubmitInterceptor;
/**
* 通用配置
*
*
* @author ruoyi
*/
@Configuration
@@ -36,7 +36,7 @@ public class ResourcesConfig implements WebMvcConfigurer
/** swagger配置 */
registry.addResourceHandler("/swagger-ui/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/")
.setCacheControl(CacheControl.maxAge(5, TimeUnit.HOURS).cachePublic());;
.setCacheControl(CacheControl.maxAge(5, TimeUnit.HOURS).cachePublic());
}
/**
@@ -70,4 +70,4 @@ public class ResourcesConfig implements WebMvcConfigurer
// 返回新的CorsFilter
return new CorsFilter(source);
}
}
}

View File

@@ -1,32 +1,35 @@
package com.fastbee.framework.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.web.filter.CorsFilter;
import com.fastbee.framework.config.properties.PermitAllUrlProperties;
import com.fastbee.framework.security.filter.JwtAuthenticationTokenFilter;
import com.fastbee.framework.security.handle.AuthenticationEntryPointImpl;
import com.fastbee.framework.security.handle.LogoutSuccessHandlerImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.web.filter.CorsFilter;
/**
* spring security配置
*
* @author ruoyi
*/
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter
@EnableMethodSecurity(prePostEnabled = true, securedEnabled = true)
@Configuration
public class SecurityConfig
{
/**
* 自定义用户认证逻辑
@@ -65,16 +68,15 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter
private PermitAllUrlProperties permitAllUrl;
/**
* 解决 无法直接注入 AuthenticationManager
*
* @return
* @throws Exception
* 身份验证实现
*/
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception
public AuthenticationManager authenticationManager()
{
return super.authenticationManagerBean();
DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
daoAuthenticationProvider.setUserDetailsService(userDetailsService);
daoAuthenticationProvider.setPasswordEncoder(bCryptPasswordEncoder());
return new ProviderManager(daoAuthenticationProvider);
}
/**
@@ -92,44 +94,45 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter
* rememberMe | 允许通过remember-me登录的用户访问
* authenticated | 用户登录后可访问
*/
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception
@Bean
@Primary
protected SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception
{
// 注解标记允许匿名访问的url
ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = httpSecurity.authorizeRequests();
permitAllUrl.getUrls().forEach(url -> registry.antMatchers(url).permitAll());
httpSecurity
// CSRF禁用因为不使用session
.csrf().disable()
// 禁用HTTP响应标头
.headers().cacheControl().disable().and()
// 认证失败处理类
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
// 基于token所以不需要session
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
// 过滤请求
.authorizeRequests()
return httpSecurity
// CSRF禁用因为不使用session
.csrf(csrf -> csrf.disable())
// 禁用HTTP响应标头
.headers((headersCustomizer) -> {
headersCustomizer.cacheControl(cache -> cache.disable()).frameOptions(options -> options.sameOrigin());
})
// 认证失败处理类
.exceptionHandling(exception -> exception.authenticationEntryPoint(unauthorizedHandler))
// 基于token所以不需要session
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
// 注解标记允许匿名访问的url
.authorizeHttpRequests((requests) -> {
permitAllUrl.getUrls().forEach(url -> requests.antMatchers(url).permitAll());
// 对于登录login 注册register 验证码captchaImage 允许匿名访问
.antMatchers("/login", "/register", "/captchaImage","/iot/tool/register","/iot/tool/ntp","/iot/tool/download",
"/iot/tool/mqtt/auth","/iot/tool/mqtt/authv5","/iot/tool/mqtt/webhook","/iot/tool/mqtt/webhookv5","/auth/**/**",
"/wechat/mobileLogin", "/wechat/miniLogin", "/wechat/wxBind/callback").permitAll()
.antMatchers("/zlmhook/**").permitAll()
.antMatchers("/goview/sys/login","/goview/project/getData").permitAll()
// 静态资源,可匿名访问
.antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll()
.antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll()
// 除上面外的所有请求全部需要鉴权认证
.anyRequest().authenticated()
.and()
.headers().frameOptions().disable();
// 添加Logout filter
httpSecurity.logout().logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler);
// 添加JWT filter
httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
// 添加CORS filter
httpSecurity.addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class);
httpSecurity.addFilterBefore(corsFilter, LogoutFilter.class);
requests.antMatchers("/login", "/register", "/captchaImage","/iot/tool/register","/iot/tool/ntp","/iot/tool/download",
"/iot/tool/mqtt/auth","/iot/tool/mqtt/authv5","/iot/tool/mqtt/webhook","/iot/tool/mqtt/webhookv5","/auth/**/**",
"/wechat/mobileLogin", "/wechat/miniLogin", "/wechat/wxBind/callback").permitAll()
.antMatchers("/zlmhook/**").permitAll()
.antMatchers("/goview/sys/login","/goview/project/getData").permitAll()
// 静态资源,可匿名访问
.antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll()
.antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll()
// 除上面外的所有请求全部需要鉴权认证
.anyRequest().authenticated();
})
// 添加Logout filter
.logout(logout -> logout.logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler))
// 添加JWT filter
.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class)
// 添加CORS filter
.addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class)
.addFilterBefore(corsFilter, LogoutFilter.class)
.build();
}
/**
@@ -140,13 +143,4 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter
{
return new BCryptPasswordEncoder();
}
/**
* 身份认证接口
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception
{
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
}
}

View File

@@ -1,12 +1,13 @@
package com.fastbee.framework.config;
import javax.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Component;
import com.fastbee.common.utils.ServletUtils;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
/**
* 服务相关配置
*
*
* @author ruoyi
*/
@Component
@@ -14,7 +15,7 @@ public class ServerConfig
{
/**
* 获取完整的请求路径,包括:域名,端口,上下文访问路径
*
*
* @return 服务地址
*/
public String getUrl()

View File

@@ -0,0 +1,92 @@
package com.fastbee.framework.config;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.stream.Collectors;
/**
* 解决Mybatis Plus Order By SQL注入问题
* @author admin
*/
@Slf4j
public class SqlFilterArgumentResolver implements HandlerMethodArgumentResolver {
private final static String[] KEYWORDS = { "master", "truncate", "insert", "select", "delete", "update", "declare",
"alter", "drop", "sleep" };
/**
* 判断Controller是否包含page 参数
* @param parameter 参数
* @return 是否过滤
*/
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.getParameterType().equals(Page.class);
}
/**
* @param parameter 入参集合
* @param mavContainer model 和 view
* @param webRequest web相关
* @param binderFactory 入参解析
* @return 检查后新的page对象
* <p>
* page 只支持查询 GET .如需解析POST获取请求报文体处理
*/
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) {
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
String[] ascs = request.getParameterValues("ascs");
String[] descs = request.getParameterValues("descs");
String current = request.getParameter("current");
String size = request.getParameter("size");
Page<?> page = new Page<>();
if (StrUtil.isNotBlank(current)) {
page.setCurrent(Long.parseLong(current));
}
if (StrUtil.isNotBlank(size)) {
page.setSize(Long.parseLong(size));
}
List<OrderItem> orderItemList = new ArrayList<>();
Optional.ofNullable(ascs).ifPresent(s -> orderItemList.addAll(
Arrays.stream(s).filter(sqlInjectPredicate()).map(OrderItem::asc).collect(Collectors.toList())));
Optional.ofNullable(descs).ifPresent(s -> orderItemList.addAll(
Arrays.stream(s).filter(sqlInjectPredicate()).map(OrderItem::desc).collect(Collectors.toList())));
page.addOrder(orderItemList);
return page;
}
/**
* 判断用户输入里面有没有关键字
* @return Predicate
*/
private Predicate<String> sqlInjectPredicate() {
return sql -> {
for (String keyword : KEYWORDS) {
if (StrUtil.containsIgnoreCase(sql, keyword)) {
return false;
}
}
return true;
};
}
}

View File

@@ -1,50 +1,31 @@
package com.fastbee.framework.config.properties;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import com.alibaba.druid.pool.DruidDataSource;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
* druid 配置属性
*
*
* @author ruoyi
*/
@Data
@Configuration
@ConfigurationProperties(prefix = "spring.datasource.dynamic.druid")
public class DruidProperties
{
@Value("${spring.datasource.druid.initialSize}")
private int initialSize;
@Value("${spring.datasource.druid.minIdle}")
private int minIdle;
@Value("${spring.datasource.druid.maxActive}")
private int maxActive;
@Value("${spring.datasource.druid.maxWait}")
private int maxWait;
@Value("${spring.datasource.druid.timeBetweenEvictionRunsMillis}")
private int timeBetweenEvictionRunsMillis;
@Value("${spring.datasource.druid.minEvictableIdleTimeMillis}")
private int minEvictableIdleTimeMillis;
@Value("${spring.datasource.druid.maxEvictableIdleTimeMillis}")
private int maxEvictableIdleTimeMillis;
@Value("${spring.datasource.druid.validationQuery}")
private Integer initialSize;
private Integer minIdle;
private Integer maxActive;
private Integer maxWait;
private Long timeBetweenEvictionRunsMillis;
private Long minEvictableIdleTimeMillis;
private Long maxEvictableIdleTimeMillis;
private String validationQuery;
@Value("${spring.datasource.druid.testWhileIdle}")
private boolean testWhileIdle;
@Value("${spring.datasource.druid.testOnBorrow}")
private boolean testOnBorrow;
@Value("${spring.datasource.druid.testOnReturn}")
private boolean testOnReturn;
private Boolean testWhileIdle;
private Boolean testOnBorrow;
private Boolean testOnReturn;
public DruidDataSource dataSource(DruidDataSource datasource)
{
/** 配置初始化大小、最小、最大 */

View File

@@ -3,6 +3,7 @@ package com.fastbee.framework.config.properties;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.regex.Pattern;
import org.apache.commons.lang3.RegExUtils;
@@ -19,7 +20,7 @@ import com.fastbee.common.annotation.Anonymous;
/**
* 设置Anonymous注解允许匿名访问的url
*
*
* @author ruoyi
*/
@Configuration
@@ -44,12 +45,12 @@ public class PermitAllUrlProperties implements InitializingBean, ApplicationCont
// 获取方法上边的注解 替代path variable 为 *
Anonymous method = AnnotationUtils.findAnnotation(handlerMethod.getMethod(), Anonymous.class);
Optional.ofNullable(method).ifPresent(anonymous -> info.getPatternsCondition().getPatterns()
Optional.ofNullable(method).ifPresent(anonymous -> Objects.requireNonNull(info.getPatternsCondition().getPatterns())
.forEach(url -> urls.add(RegExUtils.replaceAll(url, PATTERN, ASTERISK))));
// 获取类上边的注解, 替代path variable 为 *
Anonymous controller = AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), Anonymous.class);
Optional.ofNullable(controller).ifPresent(anonymous -> info.getPatternsCondition().getPatterns()
Optional.ofNullable(controller).ifPresent(anonymous -> Objects.requireNonNull(info.getPatternsCondition().getPatterns())
.forEach(url -> urls.add(RegExUtils.replaceAll(url, PATTERN, ASTERISK))));
});
}

View File

@@ -5,7 +5,7 @@ import org.slf4j.LoggerFactory;
/**
* 数据源切换处理
*
*
* @author ruoyi
*/
public class DynamicDataSourceContextHolder
@@ -14,7 +14,7 @@ public class DynamicDataSourceContextHolder
/**
* 使用ThreadLocal维护变量ThreadLocal为每个使用该变量的线程提供独立的变量副本
* 所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
* 所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
*/
private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();

View File

@@ -47,8 +47,9 @@ public abstract class RepeatSubmitInterceptor implements HandlerInterceptor
/**
* 验证是否重复提交由子类实现具体的防重复提交的规则
*
* @param request
* @return
* @param request 请求信息
* @param annotation 防重复注解参数
* @return 结果
* @throws Exception
*/
public abstract boolean isRepeatSubmit(HttpServletRequest request, RepeatSubmit annotation);

View File

@@ -18,7 +18,7 @@ import eu.bitwalker.useragentutils.UserAgent;
/**
* 异步工厂(产生任务用)
*
*
* @author ruoyi
*/
public class AsyncFactory
@@ -27,7 +27,7 @@ public class AsyncFactory
/**
* 记录登录信息
*
*
* @param username 用户名
* @param status 状态
* @param message 消息
@@ -38,7 +38,7 @@ public class AsyncFactory
final Object... args)
{
final UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent"));
final String ip = IpUtils.getIpAddr(ServletUtils.getRequest());
final String ip = IpUtils.getIpAddr();
return new TimerTask()
{
@Override
@@ -82,7 +82,7 @@ public class AsyncFactory
/**
* 操作日志记录
*
*
* @param operLog 操作日志信息
* @return 任务task
*/

View File

@@ -0,0 +1,33 @@
package com.fastbee.framework.mybatis.config;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.pagination.DialectFactory;
import com.baomidou.mybatisplus.extension.plugins.pagination.dialects.IDialect;
import com.baomidou.mybatisplus.extension.toolkit.JdbcUtils;
import org.apache.ibatis.executor.Executor;
import java.sql.Connection;
import java.sql.SQLException;
public class AutoPaginationInnerInterceptor extends PaginationInnerInterceptor {
@Override
protected IDialect findIDialect(Executor executor) {
Connection conn = null;
try {
conn = executor.getTransaction().getConnection();
String jdbcUrl = conn.getMetaData().getURL();
} catch (SQLException e) {
throw new RuntimeException(e);
}
if (this.getDialect() != null) {
return this.getDialect();
} else if (this.getDbType() != null) {
this.setDialect(DialectFactory.getDialect(this.getDbType()));
return this.getDialect();
} else {
return DialectFactory.getDialect(JdbcUtils.getDbType(executor));
}
}
}

View File

@@ -0,0 +1,67 @@
package com.fastbee.framework.mybatis.config;
import cn.hutool.core.net.NetUtil;
import com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator;
import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.fastbee.framework.config.SqlFilterArgumentResolver;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.List;
/**
* @author admin
*/
@Configuration(proxyBeanMethods = false)
@MapperScan("com.fastbee.**.mapper")
public class MybatisPlusConfig implements WebMvcConfigurer {
/**
* SQL 过滤器避免SQL 注入
* @param argumentResolvers
*/
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(new SqlFilterArgumentResolver());
}
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(paginationInnerInterceptor());
return interceptor;
}
/**
* 分页插件,自动识别数据库类型
*/
public PaginationInnerInterceptor paginationInnerInterceptor() {
PaginationInnerInterceptor paginationInnerInterceptor = new AutoPaginationInnerInterceptor();
// 分页合理化
paginationInnerInterceptor.setOverflow(true);
return paginationInnerInterceptor;
}
/**
* 使用网卡信息绑定雪花生成器
* 防止集群雪花ID重复
*/
@Bean
public IdentifierGenerator idGenerator() {
return new DefaultIdentifierGenerator(NetUtil.getLocalhost());
}
/**
* 审计字段自动填充
* @return {@link MybatisPlusMetaObjectHandler}
*/
@Bean
public MybatisPlusMetaObjectHandler mybatisPlusMetaObjectHandler() {
return new MybatisPlusMetaObjectHandler();
}
}

View File

@@ -0,0 +1,75 @@
package com.fastbee.framework.mybatis.config;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.fastbee.common.core.domain.model.LoginUser;
import com.fastbee.common.utils.SecurityUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.util.ClassUtils;
import java.nio.charset.Charset;
import java.util.Date;
/**
* MybatisPlus 自动填充配置
*
* @author admin
*/
@Slf4j
public class MybatisPlusMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
log.debug("mybatisPlus插入填充--------------");
Date date = new Date();
fillValIfNullByName("createTime", date, metaObject, false);
fillValIfNullByName("updateTime", date, metaObject, false);
fillValIfNullByName("createBy", getUserName(), metaObject, false);
fillValIfNullByName("updateBy", getUserName(), metaObject, false);
fillValIfNullByName("deleteFlag", "1", metaObject, false);
}
@Override
public void updateFill(MetaObject metaObject) {
log.debug("mybatisPlus更新填充--------------");
fillValIfNullByName("updateTime", new Date(), metaObject, true);
fillValIfNullByName("updateBy", getUserName(), metaObject, true);
}
/**
* 填充值先判断是否有手动设置优先手动设置的值例如job必须手动设置
* @param fieldName 属性名
* @param fieldVal 属性值
* @param metaObject MetaObject
* @param isCover 是否覆盖原有值,避免更新操作手动入参
*/
private static void fillValIfNullByName(String fieldName, Object fieldVal, MetaObject metaObject, boolean isCover) {
// 1. 没有 get 方法
if (!metaObject.hasSetter(fieldName)) {
return;
}
// 2. 如果用户有手动设置的值
Object userSetValue = metaObject.getValue(fieldName);
String setValueStr = StrUtil.str(userSetValue, Charset.defaultCharset());
if (StrUtil.isNotBlank(setValueStr) && !isCover) {
return;
}
// 3. field 类型相同时设置
Class<?> getterType = metaObject.getGetterType(fieldName);
if (ClassUtils.isAssignableValue(getterType, fieldVal)) {
metaObject.setValue(fieldName, fieldVal);
}
}
/**
* 获取 spring security 当前的用户名
* @return 当前用户名
*/
private String getUserName() {
LoginUser loginUser = SecurityUtils.getLoginUser();
return loginUser.getUsername();
}
}

View File

@@ -0,0 +1,164 @@
package com.fastbee.framework.mybatis.helper;
import cn.hutool.core.convert.Convert;
import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
import com.fastbee.common.exception.ServiceException;
import com.fastbee.common.mybatis.enums.DataBaseType;
import com.fastbee.common.utils.spring.SpringUtils;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
/**
* 数据库助手
*
* @author Lion Li
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class DataBaseHelper {
private static final DynamicRoutingDataSource DS = SpringUtils.getBean(DynamicRoutingDataSource.class);
public static final String DEFAULT_DATASOURCE_NAME = "master";
/**
* 获取当前数据库类型
*/
public static DataBaseType getDataBaseType(String dataName) {
DataSource dataSource = DS.getDataSources().get(dataName);
try (Connection conn = dataSource.getConnection()) {
DatabaseMetaData metaData = conn.getMetaData();
String databaseProductName = metaData.getDatabaseProductName();
return DataBaseType.find(databaseProductName);
} catch (SQLException e) {
throw new ServiceException(e.getMessage());
}
}
public static boolean isMySql() {
return DataBaseType.MY_SQL == getDataBaseType(DEFAULT_DATASOURCE_NAME);
}
public static boolean isOracle() {
return DataBaseType.ORACLE == getDataBaseType(DEFAULT_DATASOURCE_NAME);
}
public static boolean isPostgerSql() {
return DataBaseType.POSTGRE_SQL == getDataBaseType(DEFAULT_DATASOURCE_NAME);
}
public static boolean isSqlServer() {
return DataBaseType.SQL_SERVER == getDataBaseType(DEFAULT_DATASOURCE_NAME);
}
public static boolean isDm() {
return DataBaseType.DM == getDataBaseType(DEFAULT_DATASOURCE_NAME);
}
public static boolean isMySql(String dataName) {
return DataBaseType.MY_SQL == getDataBaseType(dataName);
}
public static boolean isOracle(String dataName) {
return DataBaseType.ORACLE == getDataBaseType(dataName);
}
public static boolean isPostgerSql(String dataName) {
return DataBaseType.POSTGRE_SQL == getDataBaseType(dataName);
}
public static boolean isSqlServer(String dataName) {
return DataBaseType.SQL_SERVER == getDataBaseType(dataName);
}
public static boolean isDm(String dataName) {
return DataBaseType.DM == getDataBaseType(dataName);
}
public static String findInSet(Object var1, String var2) {
DataBaseType dataBasyType = getDataBaseType(DEFAULT_DATASOURCE_NAME);
String var = Convert.toStr(var1);
if (dataBasyType == DataBaseType.SQL_SERVER) {
// charindex(',100,' , ',0,100,101,') <> 0
return String.format("charindex(',%s,' , ','+%s+',') <> 0", var, var2);
} else if (dataBasyType == DataBaseType.POSTGRE_SQL) {
// (select strpos(',0,100,101,' , ',100,')) <> 0
return String.format("(select strpos(','||%s||',' , ',%s,')) <> 0", var2, var);
} else if (dataBasyType == DataBaseType.ORACLE || dataBasyType == DataBaseType.DM) {
// instr(',0,100,101,' , ',100,') <> 0
return String.format("instr(','||%s||',' , ',%s,') <> 0", var2, var);
}
// find_in_set(100 , '0,100,101')
return String.format("find_in_set(%s , %s) <> 0", var, var2);
}
public static String findInSetColumn(String var1, String var2) {
DataBaseType dataBasyType = getDataBaseType(DEFAULT_DATASOURCE_NAME);
String var = Convert.toStr(var1);
if (dataBasyType == DataBaseType.SQL_SERVER) {
// charindex(','+de.dept_id+',' , ',0,100,101,') <> 0
return String.format("charindex(',' + %s + ',' , ',' + %s + ',') <> 0", var, var2);
} else if (dataBasyType == DataBaseType.POSTGRE_SQL) {
// (select strpos(',0,100,101,' , ',' || de.dept_id || ',')) <> 0
return String.format("(select strpos(','||%s||',' , ','|| %s ||',')) <> 0", var2, var);
} else if (dataBasyType == DataBaseType.ORACLE|| dataBasyType == DataBaseType.DM) {
// instr(',0,100,101,' , ','||de.dept_id||',') <> 0
return String.format("instr(','||%s||',' , ','||%s||',') <> 0", var2, var);
}
// find_in_set(de.dept_id , '0,100,101')
return String.format("find_in_set(%s , '%s') <> 0", var, var2);
}
/**
* 获取当前加载的数据库名
*/
public static List<String> getDataSourceNameList() {
return new ArrayList<>(DS.getDataSources().keySet());
}
public static String getDeptCondition(Long deptId) {
if (deptId == null || deptId == 0) {
// 无效条件,确保查询不会返回结果
return "1=1";
}
if (isPostgerSql()) {
return "SELECT u.user_id FROM sys_user u WHERE u.dept_id IN (SELECT dept_id FROM sys_dept WHERE " + deptId + "::text = ANY(string_to_array(ancestors, ',')) OR dept_id = " + deptId + ")";
} else if (isSqlServer()) {
return "SELECT u.user_id FROM sys_user u WHERE u.dept_id IN (SELECT dept_id FROM sys_dept WHERE CHARINDEX(',' + CAST(" + deptId + " AS VARCHAR) + ',', ',' + ancestors + ',') > 0 OR dept_id = " + deptId + ")";
} else if (isOracle()) {
return "SELECT u.user_id FROM sys_user u WHERE u.dept_id IN (SELECT dept_id FROM sys_dept WHERE INSTR(',' || ancestors || ',', ',' || " + deptId + " || ',') > 0 OR dept_id = " + deptId + ")";
} else if (isDm()) {
return "SELECT u.user_id FROM sys_user u WHERE u.dept_id IN (SELECT dept_id FROM sys_dept WHERE INSTR(',' || ancestors || ',', ',' || " + deptId + " || ',') > 0 OR dept_id = " + deptId + ")";
} else if (isMySql()) {
return "SELECT u.user_id FROM sys_user u WHERE u.dept_id IN (SELECT dept_id FROM sys_dept WHERE FIND_IN_SET(" + deptId + ", ancestors) > 0 OR dept_id = " + deptId + ")";
} else {
throw new UnsupportedOperationException("Unsupported database type");
}
}
public static String checkTime(Integer timeout) {
if (timeout == null || timeout == 0) {
// 无效条件,确保查询不会返回结果
return "";
}
if (isPostgerSql()) {
return "CURRENT_TIMESTAMP > last_connect_time + interval '1 seconds' * " + timeout;
} else if (isSqlServer()) {
return "CURRENT_TIMESTAMP > DATEADD(SECOND, " + timeout + " last_connect_time)";
} else if (isOracle()) {
return "CURRENT_TIMESTAMP > last_connect_time + (" + timeout + " / 86400)";
} else if (isDm()) {
return "CURRENT_TIMESTAMP > DATEADD(SECOND, " + timeout + ", last_connect_time)";
} else if (isMySql()) {
return "CURRENT_TIMESTAMP > DATE_ADD(last_connect_time, INTERVAL " + timeout + " SECOND)";
} else {
throw new UnsupportedOperationException("Unsupported database type");
}
}
}

View File

@@ -4,6 +4,8 @@ import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.fastbee.common.utils.MessageUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.Authentication;
@@ -20,7 +22,7 @@ import com.fastbee.framework.web.service.TokenService;
/**
* 自定义退出处理类 返回成功
*
*
* @author ruoyi
*/
@Configuration
@@ -31,7 +33,7 @@ public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler
/**
* 退出处理
*
*
* @return
*/
@Override
@@ -45,8 +47,8 @@ public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler
// 删除用户缓存记录
tokenService.delLoginUser(loginUser.getToken());
// 记录用户退出日志
AsyncManager.me().execute(AsyncFactory.recordLogininfor(userName, Constants.LOGOUT, "退出成功"));
AsyncManager.me().execute(AsyncFactory.recordLogininfor(userName, Constants.LOGOUT, MessageUtils.message("user.logout.success")));
}
ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.success("退出成功")));
ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.success(MessageUtils.message("user.logout.success"))));
}
}

View File

@@ -1,16 +1,8 @@
package com.fastbee.framework.web.domain;
import java.net.UnknownHostException;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
import com.fastbee.common.utils.Arith;
import com.fastbee.common.utils.ip.IpUtils;
import com.fastbee.framework.web.domain.server.Cpu;
import com.fastbee.framework.web.domain.server.Jvm;
import com.fastbee.framework.web.domain.server.Mem;
import com.fastbee.framework.web.domain.server.Sys;
import com.fastbee.framework.web.domain.server.SysFile;
import com.fastbee.framework.web.domain.server.*;
import oshi.SystemInfo;
import oshi.hardware.CentralProcessor;
import oshi.hardware.CentralProcessor.TickType;
@@ -21,15 +13,20 @@ import oshi.software.os.OSFileStore;
import oshi.software.os.OperatingSystem;
import oshi.util.Util;
import java.net.UnknownHostException;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
/**
* 服务器相关信息
*
*
* @author ruoyi
*/
public class Server
{
private static final int OSHI_WAIT_SECOND = 1000;
/**
* CPU相关信息
*/
@@ -209,7 +206,7 @@ public class Server
/**
* 字节转换
*
*
* @param size 字节大小
* @return 转换后值
*/

View File

@@ -1,10 +1,11 @@
package com.fastbee.framework.web.domain.server;
import com.fastbee.common.utils.Arith;
/**
* CPU相关信息
*
*
* @author ruoyi
*/
public class Cpu

View File

@@ -1,12 +1,14 @@
package com.fastbee.framework.web.domain.server;
import java.lang.management.ManagementFactory;
import com.fastbee.common.utils.Arith;
import com.fastbee.common.utils.DateUtils;
import java.lang.management.ManagementFactory;
/**
* JVM相关信息
*
*
* @author ruoyi
*/
public class Jvm
@@ -117,7 +119,7 @@ public class Jvm
*/
public String getRunTime()
{
return DateUtils.getDatePoor(DateUtils.getNowDate(), DateUtils.getServerStartDate());
return DateUtils.timeDistance(DateUtils.getNowDate(), DateUtils.getServerStartDate());
}
/**

View File

@@ -1,10 +1,11 @@
package com.fastbee.framework.web.domain.server;
import com.fastbee.common.utils.Arith;
/**
* 內存相关信息
*
*
* @author ruoyi
*/
public class Mem

View File

@@ -2,7 +2,7 @@ package com.fastbee.framework.web.domain.server;
/**
* 系统相关信息
*
*
* @author ruoyi
*/
public class Sys

View File

@@ -2,7 +2,7 @@ package com.fastbee.framework.web.domain.server;
/**
* 系统文件相关信息
*
*
* @author ruoyi
*/
public class SysFile

View File

@@ -7,17 +7,21 @@ import org.springframework.security.access.AccessDeniedException;
import org.springframework.validation.BindException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingPathVariableException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import com.fastbee.common.constant.HttpStatus;
import com.fastbee.common.core.domain.AjaxResult;
import com.fastbee.common.core.text.Convert;
import com.fastbee.common.exception.DemoModeException;
import com.fastbee.common.exception.ServiceException;
import com.fastbee.common.utils.StringUtils;
import com.fastbee.common.utils.html.EscapeUtil;
/**
* 全局异常处理器
*
*
* @author ruoyi
*/
@RestControllerAdvice
@@ -59,6 +63,33 @@ public class GlobalExceptionHandler
return StringUtils.isNotNull(code) ? AjaxResult.error(code, e.getMessage()) : AjaxResult.error(e.getMessage());
}
/**
* 请求路径中缺少必需的路径变量
*/
@ExceptionHandler(MissingPathVariableException.class)
public AjaxResult handleMissingPathVariableException(MissingPathVariableException e, HttpServletRequest request)
{
String requestURI = request.getRequestURI();
log.error("请求路径中缺少必需的路径变量'{}',发生系统异常.", requestURI, e);
return AjaxResult.error(String.format("请求路径中缺少必需的路径变量[%s]", e.getVariableName()));
}
/**
* 请求参数类型不匹配
*/
@ExceptionHandler(MethodArgumentTypeMismatchException.class)
public AjaxResult handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e, HttpServletRequest request)
{
String requestURI = request.getRequestURI();
String value = Convert.toStr(e.getValue());
if (StringUtils.isNotEmpty(value))
{
value = EscapeUtil.clean(value);
}
log.error("请求参数类型不匹配'{}',发生系统异常.", requestURI, e);
return AjaxResult.error(String.format("请求参数类型不匹配,参数[%s]要求类型为:'%s',但输入值为:'%s'", e.getName(), e.getRequiredType().getName(), value));
}
/**
* 拦截未知的运行时异常
*/

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