Compare commits

...

144 Commits

Author SHA1 Message Date
李佳航
5162b99e3e Merge pull request #155 from dromara/dev
Dev
2025-12-15 10:14:47 +08:00
lijiahangmax
263436405c 🔨 移除无用字段. 2025-12-14 10:04:02 +08:00
lijiahangmax
2296ca46a4 🔖 升级版本. 2025-12-12 11:01:15 +08:00
lijiahangmax
ffcb3baf44 🔨 优化缓存删除逻辑. 2025-12-11 10:00:12 +08:00
lijiahangmax
501755231a 🔨 优化缓存删除逻辑. 2025-12-10 09:49:35 +08:00
lijiahangmax
f4ddeb3e6c 🔨 对外服务 api 配置. 2025-12-09 10:06:24 +08:00
lijiahangmax
40afc03203 🔨 调整列宽. 2025-12-08 16:25:55 +08:00
lijiahangmax
7c479b1720 🔨 优化保存逻辑. 2025-12-07 22:10:55 +08:00
lijiahangmax
6c6f69ae24 🔨 优化异常处理逻辑. 2025-12-04 15:31:40 +08:00
lijiahangmax
d1cb056adf 🔨 优化异常处理逻辑. 2025-12-04 15:31:11 +08:00
lijiahangmax
14dc8e0407 🔨 优化异常处理逻辑. 2025-12-03 09:45:21 +08:00
lijiahangmax
528cfc90f2 🔨 优化异常处理逻辑. 2025-12-03 09:42:21 +08:00
lijiahangmax
327bb72659 🔨 优化异常处理逻辑. 2025-12-02 10:09:26 +08:00
lijiahangmax
c6b248ab6f 🔨 优化异常处理逻辑. 2025-12-01 09:59:48 +08:00
lijiahangmax
0ece84bdf1 🔨 优化异常处理逻辑. 2025-11-28 10:27:02 +08:00
lijiahangmax
61fa7a6e32 🔨 全局异常处理. 2025-11-27 09:44:20 +08:00
lijiahangmax
21e7d29077 🔨 修改 sftp 样式. 2025-11-26 10:25:59 +08:00
李佳航
fbf4299a61 Merge pull request #150 from dromara/dev
🔨 替换换行符.
2025-11-12 00:35:52 +08:00
lijiahangmax
ad42be8fe8 🔨 替换换行符. 2025-11-12 00:34:48 +08:00
李佳航
d9c8923b6d Merge pull request #149 from dromara/dev
Dev
2025-11-12 00:19:03 +08:00
lijiahangmax
aa913bce8d 🔖 升级版本. 2025-11-12 00:11:39 +08:00
lijiahangmax
3e3af89939 🔖 升级版本. 2025-11-11 11:30:22 +08:00
lijiahangmax
783baaf8c8 🐛 修复 sftp 下载文件失败. 2025-11-11 11:29:51 +08:00
lijiahangmax
a53565e06b 🔨 替换换行符. 2025-11-10 21:46:08 +08:00
lijiahangmax
061495eb13 🔨 修改文案. 2025-11-10 09:57:36 +08:00
lijiahangmax
6514363847 🔨 用户会话列表. 2025-11-08 09:40:44 +08:00
lijiahangmax
c3da882950 🔨 用户锁定列表. 2025-11-07 10:01:16 +08:00
lijiahangmax
6deebedc75 🔨 解锁用户. 2025-11-06 09:50:09 +08:00
lijiahangmax
2012f20a09 Merge remote-tracking branch 'origin/dev' into dev 2025-11-03 14:25:56 +08:00
lijiahangmax
2377c50187 🔨 锁定用户列表. 2025-11-03 14:25:45 +08:00
lijiahangmax
91b22297a2 🔨 修改重复提示. 2025-11-02 01:06:12 +08:00
lijiahangmax
5bbf46d141 🔨 添加规格信息. 2025-11-01 22:52:50 +08:00
lijiahangmax
3c7a0947ee 🔨 优化代码逻辑. 2025-10-31 09:39:03 +08:00
lijiahangmax
83c64dddfb 🔨 修改认证逻辑. 2025-10-30 16:42:43 +08:00
lijiahangmax
5d86c330fe 🔨 tag 管理. 2025-10-29 10:39:19 +08:00
lijiahangmax
6a13d3cb22 🔨 tag 管理. 2025-10-28 13:58:33 +08:00
lijiahangmax
3a8addb4d2 🔨 IP 请求头可配置. 2025-10-27 09:50:17 +08:00
lijiahangmax
90705781f2 🔨 优化操作日志逻辑. 2025-10-24 11:18:54 +08:00
李佳航
d8818c3ec2 Merge pull request #143 from dromara/dev
Dev
2025-10-21 17:04:29 +08:00
lijiahangmax
91fecad956 🔨 升级 sql 脚本. 2025-10-21 16:56:47 +08:00
lijiahangmax
9635aa34a7 🔨 修改标题样式. 2025-10-21 13:52:02 +08:00
lijiahangmax
a2f7ab7f9c 🔨 升级版本. 2025-10-20 00:23:48 +08:00
lijiahangmax
55d0dfd27d 🔨 优化告警引擎. 2025-10-19 15:34:53 +08:00
lijiahangmax
eb18142926 🔨 可调整列宽. 2025-10-18 22:21:30 +08:00
lijiahangmax
0649c4e3de 🔨 优化错误提示. 2025-10-17 14:14:34 +08:00
lijiahangmax
f648e18557 🔨 优化数据分组逻辑. 2025-10-17 14:12:14 +08:00
lijiahangmax
9d3b46e9b3 🔨 策略描述非必填. 2025-10-15 15:07:37 +08:00
lijiahangmax
14dfe457bf 🔨 优化告警引擎. 2025-10-15 01:35:40 +08:00
lijiahangmax
9651354317 🔨 添加策略类型. 2025-10-13 18:23:07 +08:00
lijiahangmax
8929aa2f74 🔨 优化监控逻辑. 2025-10-13 17:39:00 +08:00
lijiahangmax
ea98592012 🔨 监控页面连接终端. 2025-10-10 13:26:01 +08:00
lijiahangmax
b0be444fba 🔨 优化系统配置逻辑. 2025-10-09 14:31:43 +08:00
李佳航
b3daacbd8f Merge pull request #141 from dromara/dev
🚀 升级版本.
2025-10-08 10:00:02 +08:00
lijiahangmax
37fc271741 🚀 升级版本. 2025-10-08 09:59:30 +08:00
李佳航
bd76eb255d Merge pull request #140 from dromara/dev
Dev
2025-10-08 09:56:44 +08:00
lijiahangmax
0810de23ea 🔨 设置 ulimits. 2025-10-08 09:51:29 +08:00
lijiahangmax
9f2e4582cc Merge remote-tracking branch 'origin/dev' into dev
# Conflicts:
#	orion-visor-ui/src/api/monitor/monitor-host.ts
2025-10-08 09:44:24 +08:00
lijiahangmax
8e52631b46 🔨 修改前端逻辑. 2025-10-08 09:39:28 +08:00
lijiahangmax
7cd885a050 Merge remote-tracking branch 'origin/dev' into dev
# Conflicts:
#	sql/init-4-data.sql
2025-10-07 22:25:16 +08:00
lijiahangmax
3045512320 🚀 升级版本. 2025-10-07 22:24:50 +08:00
lijiahangmax
55c2199c3f 🔨 修改 sql 脚本. 2025-10-07 22:23:37 +08:00
lijiahangmax
963cd0b227 🔨 修改标签查询逻辑. 2025-10-07 16:06:00 +08:00
lijiahangmax
1d5c579e64 🔨 修改文本描述. 2025-10-07 15:46:53 +08:00
lijiahangmax
773d95207f 🔨 统一前端卡片风格. 2025-10-07 14:27:19 +08:00
lijiahangmax
2103698417 🔨 优化标签查询逻辑. 2025-10-07 14:26:40 +08:00
lijiahangmax
25082b9ea1 🔨 线程池配置化. 2025-10-07 00:28:49 +08:00
lijiahangmax
cb20d56a7b 🚀 修复告警引擎初始化异常的问题. 2025-10-07 00:28:34 +08:00
lijiahangmax
d58d46ca8c 🔨 修改 influx 查询语句. 2025-10-06 16:18:20 +08:00
lijiahangmax
29e6db75ca 🔨 登录失败提示. 2025-10-04 21:31:03 +08:00
lijiahangmax
86abf4f634 🔨 告警记录. 2025-09-29 13:35:47 +08:00
李佳航
876e763fcc Merge pull request #139 from dromara/dev
Dev
2025-09-27 19:35:47 +08:00
lijiahangmax
32d60a4d43 🚀 升级版本. 2025-09-27 19:32:22 +08:00
lijiahangmax
6ffc5d27b5 🔨 修改样式. 2025-09-25 21:57:52 +08:00
lijiahangmax
ac412b0dde 🔨 修改代码生成器模板. 2025-09-25 14:43:07 +08:00
lijiahangmax
4bdb61870a 🔨 修改配置. 2025-09-25 12:20:32 +08:00
lijiahangmax
24ad1f64df 🔨 修改配置. 2025-09-25 11:31:57 +08:00
李佳航
6774376418 Merge pull request #138 from dromara/dev
🔨 修改样式.
2025-09-25 01:40:40 +08:00
lijiahangmax
364120c143 🔨 修改样式. 2025-09-25 01:37:48 +08:00
李佳航
cb59390fed Merge pull request #137 from dromara/dev
Dev
2025-09-25 00:59:39 +08:00
lijiahangmax
69bc5b859f 🔨 nginx 修改. 2025-09-25 00:51:44 +08:00
lijiahangmax
c420747c6f 🔨 sql 升级脚本. 2025-09-24 23:38:44 +08:00
lijiahangmax
1881086e98 🔨 监控模块. 2025-09-24 23:09:58 +08:00
lijiahangmax
eb8d618c2a 🔨 修改配置. 2025-09-24 18:37:22 +08:00
lijiahangmax
8aa8cda677 🔨 前端升级. 2025-09-24 15:54:01 +08:00
lijiahangmax
e67ee60361 Merge remote-tracking branch 'origin/dev' into dev 2025-09-16 00:17:39 +08:00
lijiahangmax
29fcdd311e 🔨 添加 logo. 2025-09-16 00:17:25 +08:00
lijiahangmax
bb243cf195 🔨 修改配置. 2025-09-12 17:33:41 +08:00
李佳航
670e40f6f0 Merge pull request #135 from dromara/dev
✏️ 修改 issues 模板.
2025-09-11 23:22:05 +08:00
lijiahangmax
feb379f85f ✏️ 修改 issues 模板. 2025-09-11 23:10:39 +08:00
李佳航
a8de5ab713 Merge pull request #134 from dromara/dev
🚑 修改 docker 配置.
2025-09-11 21:30:29 +08:00
lijiahangmax
4b0c91f106 🚑 修改 docker 配置. 2025-09-11 21:24:16 +08:00
李佳航
8d46e1d44d Merge pull request #133 from dromara/dev
🔨 修改 tsc.
2025-09-11 20:44:36 +08:00
lijiahangmax
b3c045aa46 🔨 修改 tsc. 2025-09-11 20:44:03 +08:00
李佳航
ca4ec20e49 Merge pull request #132 from dromara/dev
Dev
2025-09-11 20:38:27 +08:00
lijiahangmax
8db2986dfc 🔖 升级版本. 2025-09-11 01:09:37 +08:00
lijiahangmax
3156ae1dff ✏️ 添加切图. 2025-09-10 21:53:53 +08:00
lijiahangmax
697d29c6f2 🔨 修改配置. 2025-09-10 18:11:54 +08:00
lijiahangmax
bf4b1f9702 🔖 修改版本. 2025-09-10 01:44:58 +08:00
lijiahangmax
d2703661c8 🐳 修改 docker 配置. 2025-09-10 01:43:11 +08:00
lijiahangmax
df78fc5977 🔨 修改配置文件. 2025-09-09 23:46:02 +08:00
lijiahangmax
5e03810295 🔨 sql 脚本. 2025-09-09 23:10:03 +08:00
lijiahangmax
6b44e193f5 🔨 修改文档. 2025-09-09 22:45:23 +08:00
lijiahangmax
919e8383bf 🔨 修改插件包名. 2025-09-09 22:27:51 +08:00
lijiahangmax
0b7faa038a 🔨 监控逻辑. 2025-09-09 21:25:44 +08:00
lijiahangmax
3c75aedcec 🔨 优化锁逻辑. 2025-08-23 15:05:03 +08:00
lijiahangmax
393286d309 🔨 修改项目加密模块. 2025-08-23 14:11:15 +08:00
lijiahangmax
8501e900c7 🔨 修改代码生成器模板. 2025-08-13 00:00:45 +08:00
lijiahangmax
c53042a4b5 🔨 修改代码生成器模板. 2025-08-12 23:28:30 +08:00
lijiahangmax
c661d34a79 🔨 修改主机配置字段. 2025-08-10 19:29:57 +08:00
lijiahangmax
a3476596dd 🔨 修改额外配置字段名称. 2025-08-10 19:26:33 +08:00
lijiahangmax
8a4176bc9e Merge remote-tracking branch 'origin/dev' into dev 2025-08-02 14:46:57 +08:00
lijiahangmax
a0a7240191 Merge remote-tracking branch 'origin/main' 2025-08-01 12:29:14 +08:00
lijiahangmax
2b52697cdc merge dev into main
merge

Created-by: lijiahangmax
Commit-by: lijiahangmax;2022521971;autoscope;qq_23961285;hailan1024;muzi_teacher;haobo96
Merged-by: lijiahangmax
Description: update: 更新文件 README.md
update: 更新文件 README.md
merge dev into dev
update: 更新文件 README.md
merge dev into dev
...

See merge request: dromara/orion-visor!24
2025-08-01 12:25:53 +08:00
lijiahangmax
63d82b5a19 Merge remote-tracking branch 'origin/dev' into dev 2025-08-01 11:24:49 +08:00
lijiahangmax
af23e56f03 merge dev into dev
✏️ 修改文档.

Created-by: 2022521971
Commit-by: 2022521971
Merged-by: lijiahangmax
Description: ✏️ 修改文档.

See merge request: dromara/orion-visor!23
2025-08-01 11:19:02 +08:00
2022521971
cecd71c42a ✏️ 修改文档.
Signed-off-by: 2022521971 <202252197@qq.com>
2025-08-01 11:15:33 +08:00
lijiahangmax
3ed9df8788 Merge remote-tracking branch 'origin/dev' into dev 2025-08-01 11:04:40 +08:00
lijiahangmax
7a89858790 merge dev into dev
🐳 添加 docker 参数.

Created-by: autoscope
Commit-by: autoscope
Merged-by: lijiahangmax
Description: 🐳 添加 docker 参数.

See merge request: dromara/orion-visor!22
2025-07-30 18:09:06 +08:00
autoscope
a548413358 🐳 添加 docker 参数.
Signed-off-by: autoscope <autoscope@noreply.gitcode.com>
2025-07-30 18:04:19 +08:00
lijiahangmax
c859225908 merge dev into dev
✏️ 修改文档.

Created-by: qq_23961285
Commit-by: qq_23961285
Merged-by: lijiahangmax
Description: ✏️ 修改文档.

See merge request: dromara/orion-visor!21
2025-07-30 17:50:38 +08:00
qq_23961285
b097836ec8 ✏️ 修改文档.
Signed-off-by: qq_23961285 <qq_23961285@noreply.gitcode.com>
2025-07-30 17:49:11 +08:00
lijiahangmax
9e0cfef4da merge dev into dev
🔨 更新文件 git-pull.sh

Created-by: hailan1024
Commit-by: hailan1024
Merged-by: lijiahangmax
Description: 🔨 更新文件 git-pull.sh

See merge request: dromara/orion-visor!20
2025-07-30 17:04:09 +08:00
hailan1024
a76a7d4150 🔨 更新文件 git-pull.sh
Signed-off-by: hailan1024 <hailan1024@noreply.gitcode.com>
2025-07-30 17:00:52 +08:00
lijiahangmax
2920504023 merge dev into dev
update: 更新文件 README.md

Created-by: muzi_teacher
Commit-by: muzi_teacher
Merged-by: lijiahangmax
Description: update: 更新文件 README.md

See merge request: dromara/orion-visor!19
2025-07-30 16:37:16 +08:00
muzi_teacher
c32b590bb4 update: 更新文件 README.md
Signed-off-by: muzi_teacher <muzi_teacher@noreply.gitcode.com>
2025-07-30 16:36:34 +08:00
lijiahangmax
612d7f1166 merge dev into dev
update: 更新文件 README.md

Created-by: haobo96
Commit-by: haobo96
Merged-by: lijiahangmax
Description: update: 更新文件 README.md

See merge request: dromara/orion-visor!18
2025-07-30 16:35:45 +08:00
haobo96
b7c4fbcab8 update: 更新文件 README.md 2025-07-30 16:34:17 +08:00
haobo96
a3f84e799c update: 更新文件 README.md 2025-07-30 16:29:47 +08:00
李佳航
5ab3f168d8 Merge pull request #129 from dromara/dev
✏️ 修改文档.
2025-07-19 18:52:13 +08:00
lijiahangmax
402e183d2f ✏️ 修改文档. 2025-07-14 19:06:36 +08:00
lijiahangmax
d3e5e08f6c ✏️ 修改文档. 2025-07-14 19:02:16 +08:00
李佳航
e214fbde5c Merge pull request #128 from dromara/dev
Dev
2025-07-13 18:32:17 +08:00
lijiahangmax
2f7b4bd5ea ✏️ 修改文档. 2025-07-13 18:24:02 +08:00
lijiahangmax
8bba423ff3 ✏️ 修改文档. 2025-07-13 18:06:07 +08:00
李佳航
0d0eadc3bf Merge pull request #127 from dromara/dev
🐳 修改 docker 配置.
2025-07-10 15:17:18 +08:00
lijiahangmax
ab430d8934 🐳 修改 docker 配置. 2025-07-10 15:16:29 +08:00
李佳航
edcc2cf0c8 Merge pull request #126 from dromara/dev
🐳 修改 docker 配置.
2025-07-10 14:51:48 +08:00
lijiahangmax
2913ddb2e0 🐳 修改 docker 配置. 2025-07-10 14:50:36 +08:00
李佳航
7d35f839df Merge pull request #125 from dromara/dev
🐳 修改 docker 配置.
2025-07-10 14:30:21 +08:00
lijiahangmax
ef9c34f7d9 🐳 修改 docker 配置. 2025-07-10 14:28:45 +08:00
李佳航
f1a4e049ca Merge pull request #124 from dromara/dev
Dev
2025-07-10 03:50:05 +08:00
lijiahangmax
8ee1e6acf1 🐳 修改 docker 配置. 2025-07-10 03:43:56 +08:00
lijiahangmax
d0eddf1e15 升级版本. 2025-07-10 03:34:23 +08:00
lijiahangmax
b42645b0ce 🐛 修复会话打开黑屏. 2025-07-10 03:32:45 +08:00
788 changed files with 35264 additions and 2705 deletions

View File

@@ -1,11 +1,20 @@
VOLUME_BASE=/data/orion-visor-space/docker-volumes
DEMO_MODE=false
SERVICE_PORT=1081
SPRING_PROFILES_ACTIVE=prod
DEMO_MODE=false
API_CORS=true
API_HOST=0.0.0.0
# API_URL=http://127.0.0.1:9700/orion-visor/api
API_IP_HEADERS=X-Forwarded-For,X-Real-IP
API_EXPOSE_TOKEN=pmqeHOyZaumHm0Wt
SECRET_KEY=uQeacXV8b3isvKLK
NGINX_SERVICE_HOST=service
NGINX_SERVICE_PORT=9200
MYSQL_HOST=mysql
MYSQL_PORT=3306
MYSQL_DATABASE=orion_visor
@@ -20,4 +29,16 @@ REDIS_DATA_VERSION=1
GUACD_HOST=guacd
GUACD_PORT=4822
GUACD_SSH_PORT=22
GUACD_SSH_USERNAME=guacd
GUACD_SSH_PASSWORD=guacd
GUACD_DRIVE_PATH=/drive
INFLUXDB_ENABLED=true
INFLUXDB_HOST=influxdb
INFLUXDB_PORT=8086
INFLUXDB_ORG=orion-visor
INFLUXDB_BUCKET=metrics
INFLUXDB_TOKEN=Data@123456
INFLUXDB_ADMIN_USERNAME=admin
INFLUXDB_ADMIN_PASSWORD=Data@123456

View File

@@ -0,0 +1,10 @@
### *当前使用版本 (必填)
### 问题描述
### 该问题是如何引起的
### 重现步骤
### 报错信息

View File

@@ -0,0 +1,8 @@
### 修改描述
### 关联的 Issue
### 测试用例
### 修复效果的截屏

52
.github/ISSUE_TEMPLATE/bug_report.yaml vendored Normal file
View File

@@ -0,0 +1,52 @@
name: 错误报告
description: File a bug report.
title: "[错误报告]: "
labels: [ "" ]
body:
- type: checkboxes
id: confirm
attributes:
label: 确认
description: 在提交 issue 之前, 请确认你已经阅读并确认以下内容
options:
- label: 我使用的是最新版本 [最新版](https://github.com/dromara/orion-visor/releases)
required: true
- label: 我使用官方文档进行部署 [安装文档](https://visor.orionsec.cn/quickstart/docker.html)
required: true
- label: 我已检查了 [常见问题](https://visor.orionsec.cn/support/faq.html) 并没有找到解决方法
required: true
- label: 我已搜索 [issue](https://github.com/dromara/orion-visor/issues) 并没有找到相关问题
required: true
- label: 我已 star 了此项目, 否则可能会被自动关闭
required: true
- type: input
id: version
attributes:
label: 当前程序版本
description: 遇到问题时程序所在的版本号
validations:
required: true
- type: dropdown
id: install
attributes:
label: 安装方式
options:
- Docker
- 普通安装
- 其他
validations:
required: true
- type: textarea
id: what-happened
attributes:
label: 问题描述
description: 请详细描述你碰到的问题
placeholder: "问题描述"
validations:
required: true
- type: textarea
id: logs
attributes:
label: 详细日志
description: 问题出现时的程序日志
render: bash

5
.github/ISSUE_TEMPLATE/config.yaml vendored Normal file
View File

@@ -0,0 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: 官网
url: https://visor.orionsec.cn/
about: document.

View File

@@ -0,0 +1,35 @@
name: 功能改进
description: 提出新功能建议 (请提交到需求收集帖)
title: "[功能建议]: "
labels: [ "" ]
body:
- type: markdown
attributes:
value: |
所有功能建议请统一提交到需求收集帖: 🔗 [#83 需求收集](https://github.com/dromara/orion-visor/issues/83)
在提交前请确认:
- ✅ 使用的是[最新版本](https://github.com/dromara/orion-visor/releases)
- ✅ 已搜索[已有 issue](https://github.com/dromara/orion-visor/issues) 和 需求收集帖避免重复
- ✅ 定制化需求请联系作者
---
### 如何提交高质量建议?
1. **功能描述**: 你希望增加什么?
2. **使用场景**: 你在什么情况下需要它?
4. **参考实现**: 开源项目中的类似功能(**禁止引用商业闭源软件**
- type: textarea
id: feature
attributes:
label: 功能改进
description: 请详细描述需要改进或者添加的功能。
placeholder: "功能改进"
validations:
required: true
- type: textarea
id: references
attributes:
label: 参考资料
description: 可以列举一些参考资料, 但是不要引用同类但商业化软件的任何内容。
placeholder: "参考资料"

View File

@@ -41,6 +41,10 @@ jobs:
pnpm install
pnpm build
- name: 📦️ Download instance-agent
working-directory: ./docker/service
run: wget https://github.com/lijiahangmax/orion-visor-agent/releases/latest/download/instance-agent-release.tar.gz -O instance-agent-release.tar.gz
- name: 📁 Prepare build context
run: |
cp -r ./sql ./docker/mysql/sql
@@ -62,7 +66,7 @@ jobs:
strategy:
matrix:
service: [ adminer, guacd, mysql, redis, service, ui ]
service: [ adminer, guacd, mysql, redis, influxdb, service, ui ]
env:
GITHUB_REGISTRY: ghcr.io
@@ -117,11 +121,15 @@ jobs:
type=semver,pattern={{major}}
- name: 🛠️ Build and push Docker image for orion-visor-${{ matrix.service }}
uses: docker/build-push-action@v5
uses: docker/build-push-action@v6
with:
context: ./docker
file: ./docker/${{ matrix.service }}/Dockerfile
push: true
cache-from: type=gha
cache-to: type=gha,mode=max
platforms: linux/amd64,linux/arm64
labels: ${{ steps.meta.outputs.labels }}
tags: |
${{ env.DOCKERHUB_NAMESPACE }}/orion-visor-${{ matrix.service }}:${{ steps.meta.outputs.version }}
${{ env.DOCKERHUB_NAMESPACE }}/orion-visor-${{ matrix.service }}:latest
@@ -129,5 +137,3 @@ jobs:
${{ env.GITHUB_REGISTRY }}/${{ github.repository_owner }}/orion-visor-${{ matrix.service }}:latest
${{ env.ALIYUN_REGISTRY }}/${{ env.ALIYUN_NAMESPACE }}/orion-visor-${{ matrix.service }}:${{ steps.meta.outputs.version }}
${{ env.ALIYUN_REGISTRY }}/${{ env.ALIYUN_NAMESPACE }}/orion-visor-${{ matrix.service }}:latest
labels: ${{ steps.meta.outputs.labels }}
platforms: linux/amd64,linux/arm64

View File

@@ -1,9 +1,10 @@
name: E2E
on:
pull_request:
branches:
- main
workflow_dispatch:
# pull_request:
# branches:
# - main
concurrency:
group: ${{github.workflow}} - ${{github.ref}}
@@ -18,4 +19,4 @@ jobs:
run: |
sudo curl -L https://github.com/docker/compose/releases/download/v2.23.0/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose
sudo chmod u+x /usr/local/bin/docker-compose
docker compose -f docker-compose-testing.yml up --build testing --exit-code-from testing --remove-orphans
docker compose -f docker-compose-testing.yaml up --build testing --exit-code-from testing --remove-orphans

3
.gitignore vendored
View File

@@ -16,7 +16,8 @@ target/
.sts4-cache
### IntelliJ IDEA ###
.idea
**/.idea/*
!**/.idea/icon.png
*.iws
*.iml
*.ipr

BIN
.idea/icon.png generated Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

2
NOTICE
View File

@@ -5,5 +5,5 @@
1. 禁止修改或删除 LICENSE 文件。
2. 禁止修改或删除源码头部的版权声明。
3. 本项目可免费商业使用,商业使用请保留项目源码、出处、描述文件和作者声明等。
4. 分发源码时候,请注明软件出处 https://visor.dromara.org/
4. 分发源码时候,请注明软件出处 https://visor.orionsec.cn/
5. 不可二次开发或参与同类竞品的开发。

View File

@@ -52,6 +52,7 @@
* **文件管理**:支持远程主机 SFTP 大文件的批量上传、下载和在线编辑等操作。
* **批量操作**:支持批量执行主机命令、多主机文件分发等功能。
* **计划任务**:支持配置 cron 表达式,定时执行主机命令。
* **系统监控**:支持对主机 CPU、内存、磁盘、网络等系统指标的监控和告警。
* **安全可靠**:动态配置权限,记录用户操作日志,提供简单的审计功能。
## 演示环境
@@ -62,9 +63,6 @@
这对我很重要! [github](https://github.com/dromara/orion-visor) [gitee](https://gitee.com/dromara/orion-visor) [gitcode](https://gitcode.com/dromara/orion-visor)
* 🌈 如果本项目对你有帮助请帮忙推广一下 让更多的人知道此项目!
* 🎭 演示环境部分功能不可用, 完整功能请本地部署!
* 📛 演示环境请不要随便删除数据!
* 📧 如果演示环境不可用请联系我!
* 📨 **作者随缘寻java高级/资深内推 望京/5号/10号线 有坑位的联系我哦** 微信: `ljh1553488`
## 快速开始
@@ -79,18 +77,19 @@ docker compose up -d
## 项目文档
* [文档地址](https://visor.dromara.org/)
* [安装文档](https://visor.dromara.org/quickstart/docker.html)
* [更新日志](https://visor.dromara.org/update/change-log.html)
* [操作手册](https://visor.dromara.org/operator/asset.html)
* [常见问题](https://visor.dromara.org/support/faq.html)
* [文档地址](https://visor.orionsec.cn/)
* [安装文档](https://visor.orionsec.cn/quickstart/docker.html)
* [更新日志](https://visor.orionsec.cn/update/change-log.html)
* [操作手册](https://visor.orionsec.cn/operator/asset.html)
* [常见问题](https://visor.orionsec.cn/support/faq.html)
## 技术栈
* SpringBoot 2.7.17
* SpringBoot 2.7+
* Mysql 8.0+
* Redis 6.0+
* Vue3 3.5+
* InfluxDB 2.7+
* Vue 3.5+
* Arco Design 2.56+
## 主要功能预览
@@ -111,6 +110,13 @@ docker compose up -d
![主机列表](docs/assets/screenshot/host-list.png?time=20250627 "主机列表")
#### 主机监控
![主机监控](docs/assets/screenshot/monitor-list.png?time=20250627 "主机监控")
![监控概览](docs/assets/screenshot/monitor-override.png?time=20250627 "监控概览")
![监控详情](docs/assets/screenshot/monitor-chart.png?time=20250627 "监控表格")
![告警通知](docs/assets/screenshot/monitor-alarm.png?time=20250627 "告警通知")
#### 批量执行
![批量执行](docs/assets/screenshot/exec-command.png?time=20250627 "批量执行")
@@ -130,7 +136,8 @@ docker compose up -d
## 关于我
本人专注于使用 Java 和 Vue 进行全栈开发, 并在系统自动化运维方面拥有丰富开发的经验。如果您在这些领域有需求或遇到痛点, 请随时联系我, 并备注“合作”。
本人专注于使用 Java 和 Vue 进行全栈开发, 并在系统自动化运维方面拥有丰富开发的经验, 并提供企业级的解决方案。如果您在这些领域有需求或遇到痛点, 请随时联系我,
并备注“合作”。
## 联系我
@@ -156,6 +163,10 @@ QQ群: 755242157
本项目遵循 [Apache-2.0](https://github.com/dromara/orion-visor/blob/main/LICENSE) 开源许可证。
## 贡献者
[![Contributors](https://contri.buzz/api/wall?repo=dromara/orion-visor)](https://github.com/dromara/orion-visor, "Contributors")
## Gitee 最有价值的开源项目 GVP
![GVP](docs/assets/gvp.jpg?time=20250627 "GVP")

View File

@@ -5,7 +5,7 @@ services:
image: registry.cn-hangzhou.aliyuncs.com/orionsec/orion-visor-service:latest
privileged: true
ports:
- 9200:9200
- "9200:9200"
environment:
SPRING_PROFILES_ACTIVE: prod
MYSQL_HOST: mysql
@@ -19,6 +19,10 @@ services:
DEMO_MODE: false
volumes:
- /data/orion-visor-space/docker-volumes/service/root-orion:/root/orion
ulimits:
nofile:
soft: 65536
hard: 65536
healthcheck:
test: [ "CMD", "curl", "http://127.0.0.1:9200/orion-visor/api/server/bootstrap/health" ]
interval: 15s
@@ -37,7 +41,7 @@ services:
image: registry.cn-hangzhou.aliyuncs.com/orionsec/orion-visor-mysql:latest
privileged: true
ports:
- 3307:3306
- "3307:3306"
environment:
MYSQL_DATABASE: orion_visor
MYSQL_USER: orion
@@ -59,7 +63,7 @@ services:
image: registry.cn-hangzhou.aliyuncs.com/orionsec/orion-visor-redis:latest
privileged: true
ports:
- 6380:6379
- "6380:6379"
environment:
REDIS_PASSWORD: Data@123456
volumes:
@@ -87,4 +91,4 @@ services:
networks:
orion-visor-net:
driver: bridge
driver: bridge

View File

@@ -1,6 +1,6 @@
version: '3.3'
# latest = 2.4.2
# latest = 2.5.6
# 支持以下源
# lijiahangmax/*
@@ -12,6 +12,9 @@ services:
image: registry.cn-hangzhou.aliyuncs.com/orionsec/orion-visor-ui:latest
ports:
- ${SERVICE_PORT:-1081}:80
environment:
NGINX_SERVICE_HOST: ${NGINX_SERVICE_HOST:-service}
NGINX_SERVICE_PORT: ${NGINX_SERVICE_PORT:-9200}
restart: unless-stopped
depends_on:
service:
@@ -23,7 +26,7 @@ services:
image: registry.cn-hangzhou.aliyuncs.com/orionsec/orion-visor-service:latest
privileged: true
ports:
- 9200:9200
- "9200:9200"
environment:
SPRING_PROFILES_ACTIVE: ${SPRING_PROFILES_ACTIVE:-prod}
MYSQL_HOST: ${MYSQL_HOST:-mysql}
@@ -35,13 +38,27 @@ services:
REDIS_PASSWORD: ${REDIS_PASSWORD:-Data@123456}
REDIS_DATABASE: ${REDIS_DATABASE:-0}
REDIS_DATA_VERSION: ${REDIS_DATA_VERSION:-1}
INFLUXDB_ENABLED: ${INFLUXDB_ENABLED:-true}
INFLUXDB_HOST: ${INFLUXDB_HOST:-influxdb}
INFLUXDB_PORT: ${INFLUXDB_PORT:-8086}
INFLUXDB_ORG: ${INFLUXDB_ORG:-orion-visor}
INFLUXDB_BUCKET: ${INFLUXDB_BUCKET:-metrics}
INFLUXDB_TOKEN: ${INFLUXDB_TOKEN:-Data@123456}
GUACD_HOST: ${GUACD_HOST:-guacd}
GUACD_PORT: ${GUACD_PORT:-4822}
GUACD_DRIVE_PATH: ${GUACD_DRIVE_PATH:-/drive}
SECRET_KEY: ${SECRET_KEY:-uQeacXV8b3isvKLK}
API_EXPOSE_TOKEN: ${API_EXPOSE_TOKEN:-pmqeHOyZaumHm0Wt}
API_IP_HEADERS: ${API_IP_HEADERS:-X-Forwarded-For,X-Real-IP}
API_HOST: ${API_HOST:-0.0.0.0}
API_CORS: ${API_CORS:-true}
DEMO_MODE: ${DEMO_MODE:-false}
volumes:
- ${VOLUME_BASE:-/data/orion-visor-space/docker-volumes}/service/root-orion:/root/orion
ulimits:
nofile:
soft: 65536
hard: 65536
restart: unless-stopped
healthcheck:
test: [ "CMD", "curl", "http://127.0.0.1:9200/orion-visor/api/server/bootstrap/health" ]
@@ -54,6 +71,8 @@ services:
condition: service_healthy
redis:
condition: service_healthy
influxdb:
condition: service_healthy
networks:
- orion-visor-net
@@ -61,7 +80,7 @@ services:
image: registry.cn-hangzhou.aliyuncs.com/orionsec/orion-visor-mysql:latest
privileged: true
ports:
- 3307:3306
- "3307:3306"
environment:
MYSQL_DATABASE: ${MYSQL_DATABASE:-orion_visor}
MYSQL_USER: ${MYSQL_USER:-orion}
@@ -84,7 +103,7 @@ services:
image: registry.cn-hangzhou.aliyuncs.com/orionsec/orion-visor-redis:latest
privileged: true
ports:
- 6380:6379
- "6380:6379"
environment:
REDIS_PASSWORD: ${REDIS_PASSWORD:-Data@123456}
volumes:
@@ -100,10 +119,35 @@ services:
networks:
- orion-visor-net
influxdb:
image: registry.cn-hangzhou.aliyuncs.com/orionsec/orion-visor-influxdb:latest
privileged: true
ports:
- "8086:8086"
environment:
DOCKER_INFLUXDB_INIT_MODE: setup
DOCKER_INFLUXDB_INIT_USERNAME: ${INFLUXDB_ADMIN_USERNAME:-admin}
DOCKER_INFLUXDB_INIT_PASSWORD: ${INFLUXDB_ADMIN_PASSWORD:-Data@123456}
DOCKER_INFLUXDB_INIT_ADMIN_TOKEN: ${INFLUXDB_TOKEN:-Data@123456}
DOCKER_INFLUXDB_INIT_ORG: ${INFLUXDB_ORG:-orion-visor}
DOCKER_INFLUXDB_INIT_BUCKET: ${INFLUXDB_BUCKET:-metrics}
volumes:
- ${VOLUME_BASE:-/data/orion-visor-space/docker-volumes}/influxdb/data:/var/lib/influxdb2
- ${VOLUME_BASE:-/data/orion-visor-space/docker-volumes}/influxdb/config:/etc/influxdb2
restart: unless-stopped
healthcheck:
test: [ "CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/8086" ]
interval: 15s
timeout: 5s
retries: 10
start_period: 10s
networks:
- orion-visor-net
guacd:
image: registry.cn-hangzhou.aliyuncs.com/orionsec/orion-visor-guacd:latest
ports:
- 4822:4822
- "4822:4822"
environment:
GUACD_LOG_LEVEL: info
GUACD_LOG_FILE: /var/log/guacd.log
@@ -125,7 +169,7 @@ services:
adminer:
image: registry.cn-hangzhou.aliyuncs.com/orionsec/orion-visor-adminer:latest
ports:
- 8081:8080
- "8081:8080"
environment:
ADMINER_DEFAULT_SERVER: ${MYSQL_HOST:-mysql}
depends_on:
@@ -136,4 +180,4 @@ services:
networks:
orion-visor-net:
driver: bridge
driver: bridge

View File

@@ -41,7 +41,7 @@ if [ "$DEMO_MODE" = true ]; then
echo "Starting services for demo mode..."
# 启动指定的服务
docker compose up -d --remove-orphans mysql redis ui service guacd adminer
docker compose up -d --remove-orphans mysql redis ui service guacd influxdb adminer
echo "Started services for demo mode..."
else
# 启动所有服务

View File

@@ -1 +1 @@
FROM --platform=$BUILDPLATFORM adminer:latest
FROM --platform=$TARGETPLATFORM adminer:latest

View File

@@ -1,4 +1,5 @@
FROM maven:3.9.10-eclipse-temurin-8-alpine AS builder
#FROM --platform=$BUILDPLATFORM maven:3.9-amazoncorretto-8 AS builder
FROM maven:3.9-amazoncorretto-8 AS builder
# 设置阿里云镜像加速
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories

View File

@@ -1,3 +1,4 @@
#FROM --platform=$BUILDPLATFORM node:18-alpine AS builder
FROM node:18-alpine AS builder
# 设置阿里云镜像加速

View File

@@ -7,7 +7,7 @@ set -e
source ./project-build.sh "$@"
# 版本号
version=2.4.2
version=2.5.6
# 是否推送镜像
push_image=false
# 是否构建 latest
@@ -46,6 +46,7 @@ declare -A images=(
["./service/Dockerfile"]="orion-visor-service"
["./mysql/Dockerfile"]="orion-visor-mysql"
["./redis/Dockerfile"]="orion-visor-redis"
["./influxdb/Dockerfile"]="orion-visor-influxdb"
["./adminer/Dockerfile"]="orion-visor-adminer"
["./guacd/Dockerfile"]="orion-visor-guacd"
)
@@ -68,6 +69,30 @@ function prepare_app_jar() {
fi
}
# 准备 instance-agent
function prepare_instance_agent() {
local target_file="./service/instance-agent-release.tar.gz"
if [ ! -f "$target_file" ]; then
echo "警告: $target_file 不存在, 正在尝试从 Github Release 下载..."
# 尝试从 GitHub Release 下载
if curl -L --fail \
--connect-timeout 30 --max-time 30 \
https://github.com/lijiahangmax/orion-visor-agent/releases/latest/download/instance-agent-release.tar.gz \
-o "$target_file"; then
echo "已成功下载到 $target_file"
fi
# 如果下载失败, 提示用户手动下载
echo "错误: 无法从 Release 获取 instance-agent-release.tar.gz"
echo "请手动从以下地址下载, 并放置到 $target_file"
echo " 1) https://github.com/lijiahangmax/orion-visor-agent/raw/main/instance-agent-release.tar.gz"
echo " 2) https://gitee.com/lijiahangmax/orion-visor-agent/raw/main/instance-agent-release.tar.gz"
exit 1
else
echo "$target_file 已存在, 无需下载."
fi
}
# 准备前端 dist 目录
function prepare_dist_directory() {
local source_dir="../orion-visor-ui/dist"
@@ -115,7 +140,7 @@ function modify_dockerfiles() {
if [ -f "$file" ]; then
echo "备份并修改: $file"
cp "$file" "$file$backup_suffix"
sed -i 's/--platform=\$BUILDPLATFORM//g' "$file"
sed -i 's/--platform=\TARGETPLATFORM//g' "$file"
else
echo "文件不存在 -> $file"
fi
@@ -185,6 +210,7 @@ fi
# 检查资源
echo "正在检查并准备必要的构建资源..."
prepare_app_jar
prepare_instance_agent
prepare_dist_directory
prepare_sql_directory
echo "所有前置资源已准备完毕"

View File

@@ -1,4 +1,4 @@
FROM --platform=$BUILDPLATFORM guacamole/guacd:1.6.0
FROM --platform=$TARGETPLATFORM guacamole/guacd:1.6.0
USER root

View File

@@ -0,0 +1,8 @@
FROM --platform=$TARGETPLATFORM influxdb:2
# 系统时区
ARG TZ=Asia/Shanghai
# 设置时区
RUN ln -sf /usr/share/zoneinfo/${TZ} /etc/localtime && \
echo "${TZ}" > /etc/timezone

View File

@@ -1,4 +1,4 @@
FROM --platform=$BUILDPLATFORM mysql:8.0.28
FROM --platform=$TARGETPLATFORM mysql:8.0.39
# 系统时区
ARG TZ=Asia/Shanghai
@@ -12,3 +12,7 @@ COPY ./mysql/my.cnf /etc/mysql/conf.d/my.cnf
# 复制初始化脚本
COPY ./mysql/sql/init-*.sql /docker-entrypoint-initdb.d/
# 心跳检测
HEALTHCHECK --interval=10s --timeout=3s --start-period=3s --retries=3 \
CMD mysqladmin ping -h localhost -u root --password=${MYSQL_ROOT_PASSWORD} || exit 1

View File

@@ -4,7 +4,7 @@ set -e
# DockerContext: orion-visor
# 版本号
version=2.4.2
version=2.5.6
# 是否构建 service
export build_service=false
# 是否构建 ui

View File

@@ -1,4 +1,4 @@
FROM --platform=$BUILDPLATFORM redis:6.0.16-alpine
FROM --platform=$TARGETPLATFORM redis:6.0.16-alpine
WORKDIR /data

View File

@@ -1,4 +1,4 @@
FROM --platform=$BUILDPLATFORM openjdk:8-jdk-alpine
FROM --platform=$TARGETPLATFORM openjdk:8u171-jdk-alpine3.7
USER root
@@ -17,8 +17,19 @@ RUN \
ln -sf /usr/share/zoneinfo/${TZ} /etc/localtime && \
echo "${TZ}" > /etc/timezone
# 复制启动脚本
COPY ./service/entrypoint.sh /app/entrypoint.sh
RUN chmod +x /app/entrypoint.sh
# 复制 jar 包
COPY ./service/orion-visor-launch.jar /app/app.jar
# 复制探针包
ADD ./service/instance-agent-release.tar.gz /app/instance-agent-release
# 启动检测
HEALTHCHECK --interval=15s --timeout=5s --retries=5 --start-period=10s \
CMD wget -T5 -qO- http://127.0.0.1:9200/orion-visor/api/server/bootstrap/health | grep ok || exit 1
# 启动
ENTRYPOINT ["/app/entrypoint.sh"]
CMD ["java", "-jar", "/app/app.jar"]

View File

@@ -0,0 +1,23 @@
#!/bin/sh
AGENT_RELEASE_DIR="/root/orion/orion-visor/instance-agent-release"
DEFAULT_AGENT_DIR="/app/instance-agent-release"
# 确保父目录存在
mkdir -p "$(dirname "$AGENT_RELEASE_DIR")"
# 加载探针
if [ -d "$AGENT_RELEASE_DIR" ] && [ -n "$(ls -A "$AGENT_RELEASE_DIR" 2>/dev/null)" ]; then
echo "Using mounted agent release: $AGENT_RELEASE_DIR"
else
echo "Using default agent release: $DEFAULT_AGENT_DIR"
# 复制探针
cp -rf "$DEFAULT_AGENT_DIR" "$AGENT_RELEASE_DIR"
fi
# 打印探针版本信息
if [ -f "$AGENT_RELEASE_DIR/.version" ]; then
echo "Agent version: $(cat "$AGENT_RELEASE_DIR/.version")"
fi
exec "$@"

View File

@@ -1,4 +1,4 @@
FROM --platform=$BUILDPLATFORM nginx:alpine
FROM --platform=$TARGETPLATFORM nginx:alpine
# 系统时区
ARG TZ=Asia/Shanghai
@@ -11,13 +11,20 @@ RUN \
ln -sf /usr/share/zoneinfo/${TZ} /etc/localtime && \
echo "${TZ}" > /etc/timezone && \
rm -rf /var/cache/apk/* && \
rm -rf /etc/nginx/nginx.conf && \
rm -rf /etc/nginx/conf.d/*
# 复制
# 复制前端静态文件
COPY ./ui/dist /usr/share/nginx/html
# 复制配置
COPY ./ui/nginx.conf /etc/nginx/conf.d
COPY ./ui/nginx.conf /etc/nginx
COPY ./ui/service.conf /etc/nginx/conf.d
# 复制启动脚本
COPY ./ui/entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
# 启动
ENTRYPOINT ["/entrypoint.sh"]
CMD ["nginx", "-g", "daemon off;"]

11
docker/ui/entrypoint.sh Normal file
View File

@@ -0,0 +1,11 @@
#!/bin/sh
# 设置环境变量
NGINX_SERVICE_HOST="${NGINX_SERVICE_HOST:-service}"
NGINX_SERVICE_PORT="${NGINX_SERVICE_PORT:-9200}"
# 替换环境变量
sed -i "s|\${NGINX_SERVICE_HOST}|${NGINX_SERVICE_HOST}|g" /etc/nginx/conf.d/service.conf
sed -i "s|\${NGINX_SERVICE_PORT}|${NGINX_SERVICE_PORT}|g" /etc/nginx/conf.d/service.conf
exec "$@"

View File

@@ -1,56 +1,30 @@
server {
listen 80;
server_name localhost;
client_max_body_size 1024m;
user nginx;
worker_processes auto;
# 是否启动 gzip 压缩
gzip on;
# 需要压缩的常见静态资源
gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png;
# 如果文件大于 1k 就启动压缩
gzip_min_length 1k;
# 缓冲区
gzip_buffers 4 16k;
# 压缩的等级
gzip_comp_level 2;
# access_log /var/log/nginx/host.access.log main;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# web history 模式 404
try_files $uri $uri/ /index.html;
}
location /orion-visor/api {
proxy_pass http://service:9200/orion-visor/api;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /orion-visor/keep-alive {
proxy_pass http://service:9200/orion-visor/keep-alive;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
error_log /var/log/nginx/error.log notice;
pid /run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
server_tokens off;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
include /etc/nginx/conf.d/*.conf;
}

56
docker/ui/service.conf Normal file
View File

@@ -0,0 +1,56 @@
server {
listen 80;
server_name localhost;
client_max_body_size 1024m;
# 是否启动 gzip 压缩
gzip on;
# 需要压缩的常见静态资源
gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png;
# 如果文件大于 1k 就启动压缩
gzip_min_length 1k;
# 缓冲区
gzip_buffers 4 16k;
# 压缩的等级
gzip_comp_level 2;
# access_log /var/log/nginx/host.access.log main;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# web history 模式 404
try_files $uri $uri/ /index.html;
}
location /orion-visor/api {
proxy_pass http://${NGINX_SERVICE_HOST}:${NGINX_SERVICE_PORT}/orion-visor/api;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /orion-visor/keep-alive {
proxy_pass http://${NGINX_SERVICE_HOST}:${NGINX_SERVICE_PORT}/orion-visor/keep-alive;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}

View File

@@ -1,4 +1,4 @@
## 文档已迁移至网页端
## 文档已迁移至在线文档
* https://visor.dromara.org
* https://visor.dromara.org.cn

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 232 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 201 KiB

View File

@@ -1,5 +1,9 @@
#!/bin/bash
# 清空
git clean -df
# 切换到 HEAD
git reset --hard HEAD
# 拉取远程
git pull
# 查看日志
git log -n 1

View File

@@ -20,7 +20,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.visor.common.interfaces;
package org.dromara.visor.common.cipher;
import cn.orionsec.kit.lang.utils.codec.Base62s;
import cn.orionsec.kit.lang.utils.crypto.symmetric.SymmetricCrypto;

View File

@@ -20,7 +20,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.visor.common.interfaces;
package org.dromara.visor.common.cipher;
/**
* rsa 解密器

View File

@@ -0,0 +1,55 @@
/*
* Copyright (c) 2023 - present Dromara, All rights reserved.
*
* https://visor.dromara.org
* https://visor.dromara.org.cn
* https://visor.orionsec.cn
*
* Members:
* Jiahang Li - ljh1553488six@139.com - author
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.visor.common.configuration;
import lombok.extern.slf4j.Slf4j;
import org.dromara.visor.common.utils.IpUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import javax.annotation.PostConstruct;
/**
* 公共配置类
*
* @author Jiahang Li
* @version 1.0.0
* @since 2023/6/20 10:34
*/
@Slf4j
@Configuration
public class CommonConfiguration {
@Value("${orion.api.ip-headers}")
private String[] ipHeaders;
/**
* 设置 IP 请求头
*/
@PostConstruct
public void setIpHeader() {
IpUtils.setIpHeader(ipHeaders);
log.info("IpUtils.setIpHeader {}", String.join(",", ipHeaders));
}
}

View File

@@ -36,7 +36,7 @@ public interface AppConst extends OrionConst {
/**
* 同 ${orion.version} 迭代时候需要手动更改
*/
String VERSION = "2.4.2";
String VERSION = "2.5.6";
/**
* 同 ${spring.application.name}

View File

@@ -33,6 +33,8 @@ package org.dromara.visor.common.constant;
*/
public interface AutoConfigureOrderConst {
int FRAMEWORK_EXECUTOR = Integer.MIN_VALUE + 1000;
int FRAMEWORK_WEB = Integer.MIN_VALUE + 1100;
int FRAMEWORK_SECURITY = Integer.MIN_VALUE + 1200;
@@ -53,21 +55,23 @@ public interface AutoConfigureOrderConst {
int FRAMEWORK_REDIS_CACHE = Integer.MIN_VALUE + 2000;
int FRAMEWORK_CONFIG = Integer.MIN_VALUE + 2100;
int FRAMEWORK_INFLUXDB = Integer.MIN_VALUE + 2100;
int FRAMEWORK_ENCRYPT = Integer.MIN_VALUE + 2200;
int FRAMEWORK_CONFIG = Integer.MIN_VALUE + 2300;
int FRAMEWORK_STORAGE = Integer.MIN_VALUE + 2300;
int FRAMEWORK_CYPHER = Integer.MIN_VALUE + 2400;
int FRAMEWORK_JOB = Integer.MIN_VALUE + 2400;
int FRAMEWORK_STORAGE = Integer.MIN_VALUE + 2500;
int FRAMEWORK_JOB_QUARTZ = Integer.MIN_VALUE + 2500;
int FRAMEWORK_JOB = Integer.MIN_VALUE + 2600;
int FRAMEWORK_JOB_ASYNC = Integer.MIN_VALUE + 2600;
int FRAMEWORK_JOB_QUARTZ = Integer.MIN_VALUE + 2610;
int FRAMEWORK_MONITOR = Integer.MIN_VALUE + 2700;
int FRAMEWORK_BIZ_PUSH = Integer.MIN_VALUE + 2700;
int FRAMEWORK_BIZ_OPERATOR_LOG = Integer.MIN_VALUE + 2800;
int FRAMEWORK_BIZ_OPERATOR_LOG = Integer.MIN_VALUE + 7000;
int FRAMEWORK_MONITOR = Integer.MIN_VALUE + 9000;
int FRAMEWORK_BANNER = Integer.MIN_VALUE + 10000;

View File

@@ -31,24 +31,29 @@ package org.dromara.visor.common.constant;
*/
public interface BeanOrderConst {
/**
* 公共返回值包装处理器
*/
int RESPONSE_ADVICE_WRAPPER = Integer.MIN_VALUE + 1000;
/**
* 演示模式切面
*/
int DEMO_DISABLE_API_ASPECT = Integer.MIN_VALUE + 10;
int DEMO_DISABLE_API_ASPECT = Integer.MIN_VALUE + 100;
/**
* 全局日志打印
*/
int LOG_PRINT_ASPECT = Integer.MIN_VALUE + 20;
int LOG_PRINT_ASPECT = Integer.MIN_VALUE + 200;
/**
* 暴露接口切面
*/
int EXPOSE_API_ASPECT = Integer.MIN_VALUE + 300;
/**
* 操作日志切面
*/
int OPERATOR_LOG_ASPECT = Integer.MIN_VALUE + 30;
int OPERATOR_LOG_ASPECT = Integer.MIN_VALUE + 400;
/**
* 公共返回值包装处理器
*/
int RESPONSE_ADVICE_WRAPPER = Integer.MIN_VALUE + 1000;
}

View File

@@ -34,116 +34,116 @@ public interface ConfigKeys {
/**
* SFTP 文件预览大小
*/
String SFTP_PREVIEW_SIZE = "sftp_previewSize";
String SFTP_PREVIEW_SIZE = "sftp.preview-size";
/**
* SFTP 重复文件备份
*/
String SFTP_UPLOAD_PRESENT_BACKUP = "sftp_uploadPresentBackup";
String SFTP_UPLOAD_PRESENT_BACKUP = "sftp.upload-present-backup";
/**
* SFTP 备份文件名称
*/
String SFTP_UPLOAD_BACKUP_FILE_NAME = "sftp_uploadBackupFileName";
String SFTP_UPLOAD_BACKUP_FILE_NAME = "sftp.upload-backup-file-name";
/**
* 加密公钥
*/
String ENCRYPT_PUBLIC_KEY = "encrypt_publicKey";
String ENCRYPT_PUBLIC_KEY = "encrypt.public-key";
/**
* 加密私钥
*/
String ENCRYPT_PRIVATE_KEY = "encrypt_privateKey";
String ENCRYPT_PRIVATE_KEY = "encrypt.private-key";
/**
* 日志前端显示行数
*/
String LOG_WEB_SCROLL_LINES = "log_webScrollLines";
String LOG_WEB_SCROLL_LINES = "log.web-scroll-lines";
/**
* 日志加载偏移行
*/
String LOG_TRACKER_LOAD_LINES = "log_trackerLoadLines";
String LOG_TRACKER_LOAD_LINES = "log.tracker-load-lines";
/**
* 日志加载间隔毫秒
*/
String LOG_TRACKER_LOAD_INTERVAL = "log_trackerLoadInterval";
String LOG_TRACKER_LOAD_INTERVAL = "log.tracker-load-interval";
/**
* 是否生成详细的执行日志
*/
String LOG_EXEC_DETAIL_LOG = "log_execDetailLog";
String LOG_EXEC_DETAIL_ENABLED = "log.exec-detail.enabled";
/**
* 凭证有效期分
* 凭证有效期
*/
String LOGIN_LOGIN_SESSION_TIME = "login_loginSessionTime";
String LOGIN_LOGIN_SESSION_TIME = "login.login-session-time";
/**
* 允许多端登录
*/
String LOGIN_ALLOW_MULTI_DEVICE = "login_allowMultiDevice";
String LOGIN_ALLOW_MULTI_DEVICE = "login.allow-multi-device";
/**
* 允许凭证续签
*/
String LOGIN_ALLOW_REFRESH = "login_allowRefresh";
String LOGIN_ALLOW_REFRESH = "login.allow-refresh";
/**
* 凭证续签最大次数
*/
String LOGIN_MAX_REFRESH_COUNT = "login_maxRefreshCount";
String LOGIN_MAX_REFRESH_COUNT = "login.max-refresh-count";
/**
* 凭证续签间隔分
*/
String LOGIN_REFRESH_INTERVAL = "login_refreshInterval";
String LOGIN_REFRESH_INTERVAL = "login.refresh-interval";
/**
* 登录失败锁定
*/
String LOGIN_LOGIN_FAILED_LOCK = "login_loginFailedLock";
String LOGIN_LOGIN_FAILED_LOCK = "login.login-failed-lock";
/**
* 登录失败锁定阈值
* 登录失败锁定阈值
*/
String LOGIN_LOGIN_FAILED_LOCK_THRESHOLD = "login_loginFailedLockThreshold";
String LOGIN_LOGIN_FAILED_LOCK_THRESHOLD = "login.login-failed-lock-threshold";
/**
* 登录失败锁定时间分
* 登录失败锁定时间
*/
String LOGIN_LOGIN_FAILED_LOCK_TIME = "login_loginFailedLockTime";
String LOGIN_LOGIN_FAILED_LOCK_TIME = "login.login-failed-lock-time";
/**
* 登录失败发信
*/
String LOGIN_LOGIN_FAILED_SEND = "login_loginFailedSend";
String LOGIN_LOGIN_FAILED_SEND = "login.login-failed-send";
/**
* 登录失败发信阈值
*/
String LOGIN_LOGIN_FAILED_SEND_THRESHOLD = "login_loginFailedSendThreshold";
String LOGIN_LOGIN_FAILED_SEND_THRESHOLD = "login.login-failed-send-threshold";
/**
* 是否开启自动清理命令记录
*/
String AUTO_CLEAR_EXEC_LOG_ENABLED = "autoClear_execLogEnabled";
String AUTO_CLEAR_EXEC_LOG_ENABLED = "auto-clear.exec-log.enabled";
/**
* 自动清理命令记录保留天数
*/
String AUTO_CLEAR_EXEC_LOG_KEEP_DAYS = "autoClear_execLogKeepDays";
String AUTO_CLEAR_EXEC_LOG_KEEP_DAYS = "auto-clear.exec-log.keep-days";
/**
* 是否开启自动清理终端连接记录
*/
String AUTO_CLEAR_TERMINAL_LOG_ENABLED = "autoClear_terminalLogEnabled";
String AUTO_CLEAR_TERMINAL_LOG_ENABLED = "auto-clear.terminal-log.enabled";
/**
* 自动清理终端连接记录保留天数
*/
String AUTO_CLEAR_TERMINAL_LOG_KEEP_DAYS = "autoClear_terminalLogKeepDays";
String AUTO_CLEAR_TERMINAL_LOG_KEEP_DAYS = "auto-clear.terminal-log.keep-days";
}

View File

@@ -55,4 +55,6 @@ public interface Const extends cn.orionsec.kit.lang.constant.Const, FieldConst,
int BATCH_COUNT = 500;
String IP_0000 = "0.0.0.0";
}

View File

@@ -33,4 +33,8 @@ public interface CustomHeaderConst {
String APP_VERSION = "X-App-Version";
String AGENT_KEY_HEADER = "X-Agent-Key";
String AGENT_VERSION_HEADER = "X-Agent-Version";
}

View File

@@ -48,6 +48,8 @@ public enum ErrorCode implements CodeInfo {
UNAUTHORIZED(401, "当前认证信息已失效, 请重新登录"),
EXPOSE_UNAUTHORIZED(401, "当前认证信息错误, 请检查后重试"),
FORBIDDEN(403, "无操作权限"),
NOT_FOUND(404, "未找到该资源"),

View File

@@ -100,9 +100,15 @@ public interface ErrorMessage {
String GROUP_ABSENT = "分组不存在";
String METRICS_ABSENT = "指标不存在";
String RULE_ABSENT = "规则不存在";
String ALARM_POLICY_ABSENT = "告警策略不存在";
String HOST_TYPE_ERROR = "主机类型错误";
String HOST_NOT_ENABLED = "主机未启用";
String HOST_NOT_ENABLED = "{} 主机未启用";
String CONFIG_NOT_ENABLED = "配置未启用";
@@ -132,6 +138,8 @@ public interface ErrorMessage {
String CURRENT_USER_UNSUPPORTED_OPT = "当前" + USER_UNSUPPORTED_OPT;
String PUSH_USER_NOT_EMPTY = "推送用户不能为空";
String PATH_NOT_NORMALIZE = "路径不合法";
String OPERATE_ERROR = "操作失败";
@@ -140,12 +148,16 @@ public interface ErrorMessage {
String DECRYPT_ERROR = "数据解密失败";
String GET_REQUEST_URL_ERROR = "获取请求路径失败";
String UNKNOWN_TYPE = "未知类型";
String ERROR_TYPE = "错误的类型";
String FILE_ABSENT = "文件不存在";
String FILE_EXTENSION_TYPE = "文件类型不正确";
String FILE_ABSENT_CLEAR = "文件不存在 (可能已被清理)";
String LOG_ABSENT = "日志不存在";
@@ -158,6 +170,8 @@ public interface ErrorMessage {
String FILE_UPLOAD_ERROR = "文件上传失败";
String CALC_SIGN_FAILED = "计算签名失败";
String SCRIPT_UPLOAD_ERROR = "脚本上传失败";
String EXEC_ERROR = "执行失败";
@@ -182,6 +196,8 @@ public interface ErrorMessage {
String COMPRESS_FILE_ABSENT = "压缩文件不存在";
String DECOMPRESS_FILE_ABSENT = "压缩文件不存在";
String UNABLE_DOWNLOAD_FOLDER = "无法下载文件夹";
String VALID_ERROR = "验证失败";
@@ -194,6 +210,10 @@ public interface ErrorMessage {
String PLEASE_SELECT_SUFFIX_FILE = "请选择 {} 类型的文件";
String SPEC_FORMAT_INCORRECT = "规格格式不正确";
String INFLUXDB_UNSUPPORTED = "InfluxDB 服务未开启";
/**
* 是否为业务异常
*
@@ -209,6 +229,27 @@ public interface ErrorMessage {
|| ex instanceof ApplicationException;
}
/**
* 获取错误信息
*
* @param ex ex
* @return message
*/
static String getErrorMessage(Exception ex) {
return getErrorMessage(ex, ErrorMessage.EXEC_ERROR, 0);
}
/**
* 获取错误信息
*
* @param ex ex
* @param len len
* @return message
*/
static String getErrorMessage(Exception ex, int len) {
return getErrorMessage(ex, ErrorMessage.EXEC_ERROR, len);
}
/**
* 获取错误信息
*
@@ -217,6 +258,18 @@ public interface ErrorMessage {
* @return message
*/
static String getErrorMessage(Exception ex, String defaultMsg) {
return getErrorMessage(ex, defaultMsg, 0);
}
/**
* 获取错误信息
*
* @param ex ex
* @param defaultMsg defaultMsg
* @param len len
* @return message
*/
static String getErrorMessage(Exception ex, String defaultMsg, int len) {
if (ex == null) {
return null;
}
@@ -226,7 +279,11 @@ public interface ErrorMessage {
}
// 业务异常
if (isBizException(ex)) {
return message;
if (len > 0) {
return Strings.retain(message, len);
} else {
return message;
}
}
return defaultMsg;
}

View File

@@ -35,6 +35,8 @@ public interface ExtraFieldConst extends FieldConst {
String TRACE_ID = "traceId";
String TASK_ID = "taskId";
String IDENTITY = "identity";
String GROUP_NAME = "groupName";
@@ -69,4 +71,6 @@ public interface ExtraFieldConst extends FieldConst {
String DARK = "dark";
String AGENT_KEY = "agentKey";
}

View File

@@ -45,12 +45,18 @@ public interface FieldConst {
String LABEL = "label";
String FIELD = "field";
String TYPE = "type";
String COLOR = "color";
String LOADING = "loading";
String STATUS = "status";
String SWITCH = "switch";
String INFO = "info";
String EXTRA = "extra";
@@ -71,6 +77,8 @@ public interface FieldConst {
String SEQ = "seq";
String START = "start";
String PATH = "path";
String ADDRESS = "address";
@@ -119,4 +127,12 @@ public interface FieldConst {
String CONFIG = "config";
String VERSION = "version";
String SYNCED = "synced";
String SIGN = "sign";
String SIGN_SHORT = "signShort";
}

View File

@@ -37,4 +37,20 @@ public interface FileConst {
String SCRIPT = "script";
String INSTANCE_AGENT_PATH = "instance-agent";
String INSTANCE_AGENT_NAME = "instance_agent";
String INSTANCE_AGENT_FILE_FORMAT = "instance_agent_{}_{}{}";
String INSTANCE_AGENT_RELEASE = "instance-agent-release";
String INSTANCE_AGENT_RELEASE_TEMP = "instance-agent-release-temp";
String INSTANCE_AGENT_RELEASE_TAR_GZ = "instance-agent-release.tar.gz";
String VERSION = ".version";
String CONFIG_YAML = "config.yaml";
}

View File

@@ -0,0 +1,62 @@
/*
* Copyright (c) 2023 - present Dromara, All rights reserved.
*
* https://visor.dromara.org
* https://visor.dromara.org.cn
* https://visor.orionsec.cn
*
* Members:
* Jiahang Li - ljh1553488six@139.com - author
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.visor.common.entity;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
/**
* 推送用户
*
* @author Jiahang Li
* @version 1.0.0
* @since 2025/9/18 21:46
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(name = "PushUser", description = "推送用户")
public class PushUser implements Serializable {
@NotNull
@Schema(description = "用户id")
private Long id;
@NotNull
@Schema(description = "用户名")
private String username;
@Schema(description = "花名")
private String nickname;
@Schema(description = "手机号")
private String mobile;
}

View File

@@ -33,6 +33,20 @@ import java.io.Serializable;
*/
public interface RequestIdentity extends Serializable {
/**
* 获取请求时间戳
*
* @return timestamp
*/
Long getTimestamp();
/**
* 设置请求时间戳
*
* @param timestamp timestamp
*/
void setTimestamp(Long timestamp);
/**
* 获取请求地址
*

View File

@@ -23,7 +23,10 @@
package org.dromara.visor.common.entity;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 请求留痕模型
@@ -33,9 +36,15 @@ import lombok.Data;
* @since 2023/12/29 11:57
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(name = "RequestIdentityModel", description = "请求留痕模型")
public class RequestIdentityModel implements RequestIdentity {
@Schema(description = "时间戳")
private Long timestamp;
@Schema(description = "请求地址")
private String address;

View File

@@ -0,0 +1,60 @@
/*
* Copyright (c) 2023 - present Dromara, All rights reserved.
*
* https://visor.dromara.org
* https://visor.dromara.org.cn
* https://visor.orionsec.cn
*
* Members:
* Jiahang Li - ljh1553488six@139.com - author
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.visor.common.entity.chart;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
import java.util.Map;
/**
* 时序图系列
*
* @author Jiahang Li
* @version 1.0.0
* @since 2025/9/3 21:08
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(name = "TimeChartSeries", description = "时序图系列")
public class TimeChartSeries {
@Schema(description = "name")
private String name;
@Schema(description = "颜色")
private String color;
@Schema(description = "tags")
private Map<String, Object> tags;
@Schema(description = "数据 [0]timestampMills [1]value")
private List<List<Object>> data;
}

View File

@@ -20,7 +20,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.visor.common.interfaces;
package org.dromara.visor.common.file;
import java.io.InputStream;
import java.io.OutputStream;

View File

@@ -0,0 +1,80 @@
/*
* Copyright (c) 2023 - present Dromara, All rights reserved.
*
* https://visor.dromara.org
* https://visor.dromara.org.cn
* https://visor.orionsec.cn
*
* Members:
* Jiahang Li - ljh1553488six@139.com - author
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.visor.common.lock;
import cn.orionsec.kit.lang.able.Executable;
import java.util.function.Supplier;
/**
* 空实现的锁
*
* @author Jiahang Li
* @version 1.0.0
* @since 2025/8/23 13:59
*/
public class EmptyLocker implements Locker {
@Override
public boolean tryLockExecute(String key, Executable executable) {
executable.exec();
return true;
}
@Override
public boolean tryLockExecute(String key, long timeout, Executable executable) {
executable.exec();
return true;
}
@Override
public <T> T tryLockExecute(String key, Supplier<T> callable) {
return callable.get();
}
@Override
public <T> T tryLockExecute(String key, long timeout, Supplier<T> callable) {
return callable.get();
}
@Override
public void lockExecute(String key, Executable executable) {
executable.exec();
}
@Override
public void lockExecute(String key, long timeout, Executable executable) {
executable.exec();
}
@Override
public <T> T lockExecute(String key, Supplier<T> callable) {
return callable.get();
}
@Override
public <T> T lockExecute(String key, long timeout, Supplier<T> callable) {
return callable.get();
}
}

View File

@@ -0,0 +1,116 @@
/*
* Copyright (c) 2023 - present Dromara, All rights reserved.
*
* https://visor.dromara.org
* https://visor.dromara.org.cn
* https://visor.orionsec.cn
*
* Members:
* Jiahang Li - ljh1553488six@139.com - author
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.visor.common.lock;
import cn.orionsec.kit.lang.able.Executable;
import java.util.function.Supplier;
/**
* 分布式锁
*
* @author Jiahang Li
* @version 1.0.0
* @since 2024/5/16 12:24
*/
public interface Locker {
/**
* 尝试获取锁并执行
*
* @param key key
* @param executable exec
* @return 是否获取到锁
*/
boolean tryLockExecute(String key, Executable executable);
/**
* 尝试获取锁并执行
*
* @param key key
* @param timeout timeout
* @param executable exec
* @return 是否获取到锁
*/
boolean tryLockExecute(String key, long timeout, Executable executable);
/**
* 尝试获取锁并执行 未获取到锁则抛出异常
*
* @param key key
* @param callable callable
* @param <T> T
* @return 执行结果
*/
<T> T tryLockExecute(String key, Supplier<T> callable);
/**
* 尝试获取锁并执行 未获取到锁则抛出异常
*
* @param key key
* @param timeout timeout
* @param callable callable
* @param <T> T
* @return 执行结果
*/
<T> T tryLockExecute(String key, long timeout, Supplier<T> callable);
/**
* 阻塞获取锁并执行
*
* @param key key
* @param executable exec
*/
void lockExecute(String key, Executable executable);
/**
* 阻塞获取锁并执行
*
* @param key key
* @param timeout timeout
* @param executable exec
*/
void lockExecute(String key, long timeout, Executable executable);
/**
* 阻塞获取锁并执行
*
* @param key key
* @param callable callable
* @param <T> T
* @return 执行结果
*/
<T> T lockExecute(String key, Supplier<T> callable);
/**
* 阻塞获取锁并执行
*
* @param key key
* @param timeout timeout
* @param callable callable
* @param <T> T
* @return 执行结果
*/
<T> T lockExecute(String key, long timeout, Supplier<T> callable);
}

View File

@@ -0,0 +1,59 @@
/*
* Copyright (c) 2023 - present Dromara, All rights reserved.
*
* https://visor.dromara.org
* https://visor.dromara.org.cn
* https://visor.orionsec.cn
*
* Members:
* Jiahang Li - ljh1553488six@139.com - author
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.visor.common.mapstruct;
import java.util.Date;
/**
* date 转换器
*
* @author Jiahang Li
* @version 1.0.0
* @since 2025/3/7 17:43
*/
public class DateConversion {
private DateConversion() {
}
/**
* Long > Date
*
* @param timestamp timestamp
* @return Date
*/
public static Date longToDate(Long timestamp) {
return timestamp != null ? new Date(timestamp) : null;
}
/**
* Date > Long
*
* @param date date
* @return Long
*/
public static Long dateToLong(Date date) {
return date != null ? date.getTime() : null;
}
}

View File

@@ -63,6 +63,9 @@ public class BaseConnectConfig implements IBaseConnectConfig {
@Schema(description = "主机端口")
private Integer hostPort;
@Schema(description = "agentKey")
private String agentKey;
@Schema(description = "用户名")
private String username;

View File

@@ -61,6 +61,10 @@ public interface IBaseConnectConfig {
void setHostPort(Integer hostPort);
String getAgentKey();
void setAgentKey(String agentKey);
String getUsername();
void setUsername(String username);

View File

@@ -23,7 +23,7 @@
package org.dromara.visor.common.utils;
import cn.orionsec.kit.lang.utils.Exceptions;
import org.dromara.visor.common.interfaces.AesEncryptor;
import org.dromara.visor.common.cipher.AesEncryptor;
/**
* aes 数据加密工具类

View File

@@ -37,13 +37,13 @@ import java.util.Set;
import java.util.function.Function;
/**
* 验证器
* 断言
*
* @author Jiahang Li
* @version 1.0.0
* @since 2023/7/18 11:23
*/
public class Valid extends cn.orionsec.kit.lang.utils.Valid {
public class Assert extends cn.orionsec.kit.lang.utils.Assert {
private static final Validator validator = SpringHolder.getBean(Validator.class);
@@ -155,8 +155,8 @@ public class Valid extends cn.orionsec.kit.lang.utils.Valid {
* @param path path
*/
public static String checkNormalize(String path) {
Valid.notBlank(path);
Valid.isTrue(Files1.isNormalize(path), ErrorMessage.PATH_NOT_NORMALIZE);
Assert.notBlank(path);
Assert.isTrue(Files1.isNormalize(path), ErrorMessage.PATH_NOT_NORMALIZE);
return Files1.getPath(path);
}
@@ -168,8 +168,8 @@ public class Valid extends cn.orionsec.kit.lang.utils.Valid {
* @return file
*/
public static String checkSuffix(String file, String suffix) {
Valid.notBlank(file);
Valid.isTrue(file.toLowerCase().endsWith(Const.DOT + suffix), ErrorMessage.PLEASE_SELECT_SUFFIX_FILE, suffix);
Assert.notBlank(file);
Assert.isTrue(file.toLowerCase().endsWith(Const.DOT + suffix), ErrorMessage.PLEASE_SELECT_SUFFIX_FILE, suffix);
return file;
}

View File

@@ -24,7 +24,7 @@ package org.dromara.visor.common.utils;
import cn.orionsec.kit.ext.location.Region;
import cn.orionsec.kit.ext.location.region.LocationRegions;
import cn.orionsec.kit.web.servlet.web.Servlets;
import cn.orionsec.kit.lang.utils.net.IPs;
import org.dromara.visor.common.constant.Const;
import javax.servlet.http.HttpServletRequest;
@@ -40,6 +40,8 @@ import java.util.Map;
*/
public class IpUtils {
private static String[] IP_HEADER = new String[]{"X-Forwarded-For", "X-Real-IP"};
private static final Map<String, String> CACHE = new HashMap<>();
private IpUtils() {
@@ -52,13 +54,17 @@ public class IpUtils {
* @return addr
*/
public static String getRemoteAddr(HttpServletRequest request) {
// 获取实际地址 X_REAL_IP 在多代理情况下会有问题
// String realIp = request.getHeader(StandardHttpHeader.X_REAL_IP);
// if (!Strings.isBlank(realIp)) {
// return realIp;
// }
// 获取请求地址
return Servlets.getRemoteAddr(request);
if (request == null) {
return null;
} else {
for (String remoteAddrHeader : IP_HEADER) {
String addr = checkIpHeader(request.getHeader(remoteAddrHeader));
if (addr != null) {
return addr;
}
}
return checkIpHeader(request.getRemoteAddr());
}
}
/**
@@ -112,4 +118,23 @@ public class IpUtils {
return Const.CN_UNKNOWN;
}
/**
* 检查 ip 请求头
*
* @param headerValue headerValue
* @return header
*/
private static String checkIpHeader(String headerValue) {
if (headerValue == null) {
return null;
} else {
headerValue = headerValue.split(",")[0];
return IPs.checkIp(headerValue);
}
}
public static void setIpHeader(String[] ipHeader) {
IP_HEADER = ipHeader;
}
}

View File

@@ -22,9 +22,10 @@
*/
package org.dromara.visor.common.utils;
import cn.orionsec.kit.lang.able.Executable;
import cn.orionsec.kit.lang.utils.Exceptions;
import lombok.extern.slf4j.Slf4j;
import org.dromara.visor.common.interfaces.Locker;
import org.dromara.visor.common.lock.Locker;
import java.util.function.Supplier;
@@ -44,26 +45,97 @@ public class LockerUtils {
}
/**
* 尝试获取锁
* 尝试获取锁并执行
*
* @param key key
* @param run run
* @param key key
* @param executable exec
* @return 是否获取到锁
*/
public static boolean tryLock(String key, Runnable run) {
return delegate.tryLock(key, run);
public static boolean tryLockExecute(String key, Executable executable) {
return delegate.tryLockExecute(key, executable);
}
/**
* 尝试获取锁
* 尝试获取锁并执行
*
* @param key key
* @param call call
* @param <T> T
* @param key key
* @param timeout timeout
* @param executable exec
* @return 是否获取到锁
*/
public static boolean tryLockExecute(String key, long timeout, Executable executable) {
return delegate.tryLockExecute(key, timeout, executable);
}
/**
* 尝试获取锁并执行 未获取到锁则抛出异常
*
* @param key key
* @param callable callable
* @param <T> T
* @return 执行结果
*/
public static <T> T tryLock(String key, Supplier<T> call) {
return delegate.tryLock(key, call);
public static <T> T tryLockExecute(String key, Supplier<T> callable) {
return delegate.tryLockExecute(key, callable);
}
/**
* 尝试获取锁并执行 未获取到锁则抛出异常
*
* @param key key
* @param timeout timeout
* @param callable callable
* @param <T> T
* @return 执行结果
*/
public static <T> T tryLockExecute(String key, long timeout, Supplier<T> callable) {
return delegate.tryLockExecute(key, timeout, callable);
}
/**
* 阻塞获取锁并执行
*
* @param key key
* @param executable exec
*/
public static void lockExecute(String key, Executable executable) {
delegate.lockExecute(key, executable);
}
/**
* 阻塞获取锁并执行
*
* @param key key
* @param timeout timeout
* @param executable exec
*/
public static void lockExecute(String key, long timeout, Executable executable) {
delegate.lockExecute(key, timeout, executable);
}
/**
* 阻塞获取锁并执行
*
* @param key key
* @param callable callable
* @param <T> T
* @return 执行结果
*/
public static <T> T lockExecute(String key, Supplier<T> callable) {
return delegate.lockExecute(key, callable);
}
/**
* 阻塞获取锁并执行
*
* @param key key
* @param timeout timeout
* @param callable callable
* @param <T> T
* @return 执行结果
*/
public static <T> T lockExecute(String key, long timeout, Supplier<T> callable) {
return delegate.lockExecute(key, timeout, callable);
}
public static void setDelegate(Locker delegate) {

View File

@@ -64,10 +64,24 @@ public class Requests {
.map(ServletRequestAttributes::getRequest)
.ifPresent(request -> {
String address = IpUtils.getRemoteAddr(request);
identity.setTimestamp(System.currentTimeMillis());
identity.setAddress(address);
identity.setLocation(IpUtils.getLocation(address));
identity.setUserAgent(Servlets.getUserAgent(request));
});
}
/**
* 复制留痕信息
*
* @param source source
* @param target target
*/
public static void copyIdentity(RequestIdentityModel source, RequestIdentityModel target) {
target.setTimestamp(source.getTimestamp());
target.setAddress(source.getAddress());
target.setLocation(source.getLocation());
target.setUserAgent(source.getUserAgent());
}
}

View File

@@ -23,7 +23,7 @@
package org.dromara.visor.common.utils;
import cn.orionsec.kit.lang.utils.Exceptions;
import org.dromara.visor.common.interfaces.RsaDecryptor;
import org.dromara.visor.common.cipher.RsaDecryptor;
/**
* rsa 参数解密工具类

View File

@@ -23,7 +23,7 @@
package org.dromara.visor.common.validator.group;
/**
* 分页验证分组
* id 验证分组
*
* @author Jiahang Li
* @version 1.0.0

View File

@@ -0,0 +1,33 @@
/*
* Copyright (c) 2023 - present Dromara, All rights reserved.
*
* https://visor.dromara.org
* https://visor.dromara.org.cn
* https://visor.orionsec.cn
*
* Members:
* Jiahang Li - ljh1553488six@139.com - author
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.visor.common.validator.group;
/**
* key 验证分组
*
* @author Jiahang Li
* @version 1.0.0
* @since 2023/9/1 19:13
*/
public interface Key {
}

View File

@@ -14,11 +14,11 @@
<url>https://github.com/dromara/orion-visor</url>
<properties>
<revision>2.4.2</revision>
<revision>2.5.6</revision>
<spring.boot.version>2.7.17</spring.boot.version>
<spring.boot.admin.version>2.7.15</spring.boot.admin.version>
<flatten.maven.plugin.version>1.5.0</flatten.maven.plugin.version>
<orion.kit.version>2.0.2</orion.kit.version>
<orion.kit.version>2.0.5</orion.kit.version>
<aspectj.version>1.9.7</aspectj.version>
<lombok.version>1.18.26</lombok.version>
<springdoc.version>1.6.15</springdoc.version>
@@ -31,6 +31,7 @@
<druid.version>1.2.16</druid.version>
<redisson.version>3.18.0</redisson.version>
<transmittable.thread.local.version>2.14.2</transmittable.thread.local.version>
<influxdb.client.version>6.6.0</influxdb.client.version>
<mockito.inline.version>4.11.0</mockito.inline.version>
<jedis.mock.version>1.0.7</jedis.mock.version>
<podam.version>7.2.11.RELEASE</podam.version>
@@ -54,8 +55,12 @@
<version>${orion.kit.version}</version>
<exclusions>
<exclusion>
<artifactId>orion-log</artifactId>
<groupId>cn.orionsec.kit</groupId>
<artifactId>orion-log</artifactId>
</exclusion>
<exclusion>
<groupId>cn.orionsec.kit</groupId>
<artifactId>orion-generator</artifactId>
</exclusion>
</exclusions>
</dependency>
@@ -101,6 +106,11 @@
<artifactId>orion-visor-spring-boot-starter-job</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>org.dromara.visor</groupId>
<artifactId>orion-visor-spring-boot-starter-executor</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>org.dromara.visor</groupId>
<artifactId>orion-visor-spring-boot-starter-websocket</artifactId>
@@ -118,7 +128,7 @@
</dependency>
<dependency>
<groupId>org.dromara.visor</groupId>
<artifactId>orion-visor-spring-boot-starter-encrypt</artifactId>
<artifactId>orion-visor-spring-boot-starter-cipher</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
@@ -146,6 +156,16 @@
<artifactId>orion-visor-spring-boot-starter-test</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>org.dromara.visor</groupId>
<artifactId>orion-visor-spring-boot-starter-influxdb</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>org.dromara.visor</groupId>
<artifactId>orion-visor-spring-boot-starter-biz-push</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>org.dromara.visor</groupId>
<artifactId>orion-visor-spring-boot-starter-biz-operator-log</artifactId>
@@ -272,6 +292,13 @@
<version>${transmittable.thread.local.version}</version>
</dependency>
<!-- influxdb -->
<dependency>
<groupId>com.influxdb</groupId>
<artifactId>influxdb-client-java</artifactId>
<version>${influxdb.client.version}</version>
</dependency>
<!-- test -->
<dependency>
<groupId>org.springframework.boot</groupId>

View File

@@ -73,7 +73,6 @@ public class OperatorLogAspect {
.maxPoolSize(1)
.useLinkedBlockingQueue()
.allowCoreThreadTimeout()
.useLinkedBlockingQueue()
.build();
private final OperatorLogFrameworkService operatorLogFrameworkService;

View File

@@ -23,8 +23,11 @@
package org.dromara.visor.framework.biz.operator.log.core.factory;
import cn.orionsec.kit.lang.utils.Arrays1;
import cn.orionsec.kit.spring.SpringHolder;
import org.dromara.visor.framework.biz.operator.log.core.annotation.Module;
import org.dromara.visor.framework.biz.operator.log.core.model.OperatorType;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import javax.annotation.PostConstruct;
@@ -35,7 +38,14 @@ import javax.annotation.PostConstruct;
* @version 1.0.0
* @since 2023/10/13 17:45
*/
public abstract class InitializingOperatorTypes implements OperatorTypeDefinition {
public abstract class InitializingOperatorTypes implements OperatorTypeDefinition, BeanNameAware {
private String beanName;
@Override
public void setBeanName(String name) {
this.beanName = name;
}
@PostConstruct
public void init() {
@@ -55,6 +65,8 @@ public abstract class InitializingOperatorTypes implements OperatorTypeDefinitio
type.setModule(module);
OperatorTypeHolder.set(type);
}
// 自动销毁
((BeanDefinitionRegistry) SpringHolder.getBeanFactory()).removeBeanDefinition(beanName);
}
}

View File

@@ -123,8 +123,8 @@ public class OperatorLogModel implements RequestIdentity {
private Date endTime;
/**
* 创建时间
* 时间
*/
private Date createTime;
private Long timestamp;
}

View File

@@ -31,8 +31,8 @@ import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializeFilter;
import org.dromara.visor.common.entity.RequestIdentity;
import org.dromara.visor.common.enums.BooleanBit;
import org.dromara.visor.common.trace.TraceIdHolder;
import org.dromara.visor.common.security.LoginUser;
import org.dromara.visor.common.trace.TraceIdHolder;
import org.dromara.visor.common.utils.Requests;
import org.dromara.visor.framework.biz.operator.log.configuration.config.OperatorLogConfig;
import org.dromara.visor.framework.biz.operator.log.core.enums.ReturnType;
@@ -91,6 +91,7 @@ public class OperatorLogFiller implements Gettable<OperatorLogModel> {
*/
public OperatorLogFiller fillUsedTime(long start) {
long end = System.currentTimeMillis();
model.setTimestamp(start);
model.setDuration((int) (end - start));
model.setStartTime(new Date(start));
model.setEndTime(new Date(end));

View File

@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.dromara.visor</groupId>
<artifactId>orion-visor-framework</artifactId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>orion-visor-spring-boot-starter-biz-push</artifactId>
<name>${project.artifactId}</name>
<packaging>jar</packaging>
<description>项目业务推送包</description>
<url>https://github.com/dromara/orion-visor</url>
<dependencies>
<dependency>
<groupId>org.dromara.visor</groupId>
<artifactId>orion-visor-common</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,121 @@
/*
* Copyright (c) 2023 - present Dromara, All rights reserved.
*
* https://visor.dromara.org
* https://visor.dromara.org.cn
* https://visor.orionsec.cn
*
* Members:
* Jiahang Li - ljh1553488six@139.com - author
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.visor.framework.biz.push.configuration;
import cn.orionsec.kit.lang.function.Functions;
import org.dromara.visor.common.constant.AutoConfigureOrderConst;
import org.dromara.visor.framework.biz.push.core.enums.PushChannelEnum;
import org.dromara.visor.framework.biz.push.core.framework.service.PushTemplateFrameworkService;
import org.dromara.visor.framework.biz.push.core.framework.service.PushTemplateFrameworkServiceDelegate;
import org.dromara.visor.framework.biz.push.core.framework.service.WebsiteMessageFrameworkService;
import org.dromara.visor.framework.biz.push.core.listener.PushMessageEventListener;
import org.dromara.visor.framework.biz.push.core.message.PushMessage;
import org.dromara.visor.framework.biz.push.core.service.*;
import org.dromara.visor.framework.biz.push.core.utils.MessageChannelUtils;
import org.dromara.visor.framework.biz.push.core.utils.PushUtils;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* 推送自动配置类
*
* @author Shihao Lv
* @version 1.0.0
* @since 2025/9/17 23:04
*/
@AutoConfiguration
@AutoConfigureOrder(AutoConfigureOrderConst.FRAMEWORK_BIZ_PUSH)
public class OrionPushAutoConfiguration {
/**
* @return 钉钉推送服务
*/
@Bean
public DingPushService dingPushService() {
return new DingPushService();
}
/**
* @return 飞书推送服务
*/
@Bean
public FeiShuPushService feiShuPushService() {
return new FeiShuPushService();
}
/**
* @return 企业微信推送服务
*/
@Bean
public WeComPushService weComPushService() {
return new WeComPushService();
}
/**
* @param websiteMessageFrameworkService websiteMessageFrameworkService
* @return 站内信推送服务
*/
@Bean
public WebsitePushService websitePushService(WebsiteMessageFrameworkService websiteMessageFrameworkService) {
return new WebsitePushService(websiteMessageFrameworkService);
}
/**
* @param impl impl
* @return 推送模板服务
*/
@Bean
@Primary
@ConditionalOnBean(PushTemplateFrameworkService.class)
public PushTemplateFrameworkService pushTemplateFrameworkService(PushTemplateFrameworkService impl) {
PushTemplateFrameworkServiceDelegate delegate = new PushTemplateFrameworkServiceDelegate(impl);
// 设置到工具类
PushUtils.setPushTemplateFrameworkService(delegate);
return delegate;
}
/**
* @param pushServices 推送服务
* @return 消息推送事件监听器
*/
@Bean
public PushMessageEventListener pushMessageEventListener(List<IPushService<? extends PushMessage>> pushServices) {
// 服务列表
Map<PushChannelEnum, IPushService<? extends PushMessage>> serviceMap = pushServices.stream()
.collect(Collectors.toMap(
MessageChannelUtils::getPushChannel,
Function.identity(),
Functions.right()));
// 创建监听器
return new PushMessageEventListener(serviceMap);
}
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright (c) 2023 - present Dromara, All rights reserved.
*
* https://visor.dromara.org
* https://visor.dromara.org.cn
* https://visor.orionsec.cn
*
* Members:
* Jiahang Li - ljh1553488six@139.com - author
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.visor.framework.biz.push.core.annotation;
import org.dromara.visor.framework.biz.push.core.enums.PushChannelEnum;
import java.lang.annotation.*;
/**
* 消息渠道
*
* @author Jiahang Li
* @version 1.0.0
* @since 2025/9/18 21:58
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MessageChannel {
/**
* 消息渠道
*
* @return channel
*/
PushChannelEnum value();
}

View File

@@ -0,0 +1,104 @@
/*
* Copyright (c) 2023 - present Dromara, All rights reserved.
*
* https://visor.dromara.org
* https://visor.dromara.org.cn
* https://visor.orionsec.cn
*
* Members:
* Jiahang Li - ljh1553488six@139.com - author
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.visor.framework.biz.push.core.entity;
import cn.orionsec.kit.lang.able.IJsonObject;
import com.alibaba.fastjson.annotation.JSONField;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.List;
/**
* 钉钉请求体
*
* @author Jiahang Li
* @version 1.0.0
* @since 2025/9/18 18:41
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DingRequestBody implements Serializable, IJsonObject {
/**
* 消息类型
*/
@JSONField(name = "msgtype")
private String msgType;
/**
* markdown 内容
*/
private DingRequestBody.MarkdownPayload markdown;
/**
* at 配置
*/
private DingRequestBody.AtPayload at;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class MarkdownPayload implements Serializable {
/**
* 标题
*/
private String title;
/**
* 内容
*/
private String text;
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class AtPayload implements Serializable {
/**
* 被 at 的手机号
*/
private List<String> atMobiles;
/**
* 被 at 的 userId
*/
private List<String> atUserIds;
/**
* 是否 at 所有人
*/
private Boolean isAtAll;
}
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright (c) 2023 - present Dromara, All rights reserved.
*
* https://visor.dromara.org
* https://visor.dromara.org.cn
* https://visor.orionsec.cn
*
* Members:
* Jiahang Li - ljh1553488six@139.com - author
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.visor.framework.biz.push.core.entity;
import com.alibaba.fastjson.annotation.JSONField;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* 钉钉响应体
*
* @author Jiahang Li
* @version 1.0.0
* @since 2025/9/18 18:41
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DingResponseBody implements Serializable {
/**
* 错误码值
*/
@JSONField(name = "errcode")
private Integer errCode;
/**
* 错误码描述
*/
@JSONField(name = "errmsg")
private String errMsg;
}

View File

@@ -0,0 +1,81 @@
/*
* Copyright (c) 2023 - present Dromara, All rights reserved.
*
* https://visor.dromara.org
* https://visor.dromara.org.cn
* https://visor.orionsec.cn
*
* Members:
* Jiahang Li - ljh1553488six@139.com - author
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.visor.framework.biz.push.core.entity;
import cn.orionsec.kit.lang.able.IJsonObject;
import com.alibaba.fastjson.annotation.JSONField;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* 飞书请求体
*
* @author Jiahang Li
* @version 1.0.0
* @since 2025/9/18 18:41
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class FeiShuRequestBody implements Serializable, IJsonObject {
/**
* 时间戳
*/
private Long timestamp;
/**
* 签名
*/
private String sign;
/**
* 消息类型
*/
@JSONField(name = "msg_type")
private String msgType;
/**
* text 内容
*/
private TextPayload content;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class TextPayload implements Serializable {
/**
* 内容
*/
private String text;
}
}

View File

@@ -20,36 +20,36 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.visor.common.interfaces;
package org.dromara.visor.framework.biz.push.core.entity;
import java.util.function.Supplier;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* 分布式锁
* 飞书响应体
*
* @author Jiahang Li
* @version 1.0.0
* @since 2024/5/16 12:24
* @since 2025/9/18 18:41
*/
public interface Locker {
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class FeiShuResponseBody implements Serializable {
/**
* 尝试获取锁
*
* @param key key
* @param run run
* @return 是否获取到锁
* code
*/
boolean tryLock(String key, Runnable run);
private Integer code;
/**
* 尝试获取锁
*
* @param key key
* @param call call
* @param <T> T
* @return 执行结果
* msg
*/
<T> T tryLock(String key, Supplier<T> call);
private String msg;
}

View File

@@ -0,0 +1,78 @@
/*
* Copyright (c) 2023 - present Dromara, All rights reserved.
*
* https://visor.dromara.org
* https://visor.dromara.org.cn
* https://visor.orionsec.cn
*
* Members:
* Jiahang Li - ljh1553488six@139.com - author
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.visor.framework.biz.push.core.entity;
import cn.orionsec.kit.lang.able.IJsonObject;
import com.alibaba.fastjson.annotation.JSONField;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.List;
/**
* 企业微信请求体
*
* @author Jiahang Li
* @version 1.0.0
* @since 2025/9/18 18:41
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class WeComRequestBody implements Serializable, IJsonObject {
/**
* 消息类型
*/
@JSONField(name = "msgtype")
private String msgType;
/**
* markdown 内容
*/
private MarkdownPayload markdown;
/**
* 被 at 的 userId
*/
@JSONField(name = "mentioned_list")
private List<String> mentionedList;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class MarkdownPayload implements Serializable {
/**
* 内容
*/
private String content;
}
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright (c) 2023 - present Dromara, All rights reserved.
*
* https://visor.dromara.org
* https://visor.dromara.org.cn
* https://visor.orionsec.cn
*
* Members:
* Jiahang Li - ljh1553488six@139.com - author
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.visor.framework.biz.push.core.entity;
import com.alibaba.fastjson.annotation.JSONField;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* 企业微信响应体
*
* @author Jiahang Li
* @version 1.0.0
* @since 2025/9/18 18:41
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class WeComResponseBody implements Serializable {
/**
* 错误码值
*/
@JSONField(name = "errcode")
private Integer errCode;
/**
* 错误码描述
*/
@JSONField(name = "errmsg")
private String errMsg;
}

View File

@@ -0,0 +1,89 @@
/*
* Copyright (c) 2023 - present Dromara, All rights reserved.
*
* https://visor.dromara.org
* https://visor.dromara.org.cn
* https://visor.orionsec.cn
*
* Members:
* Jiahang Li - ljh1553488six@139.com - author
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.visor.framework.biz.push.core.enums;
import com.alibaba.fastjson.JSON;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.dromara.visor.framework.biz.push.core.message.*;
/**
* 通知模板类型枚举
*
* @author Shihao Lv
* @version 1.0.0
* @since 2025/9/15 23:20
*/
@Getter
@AllArgsConstructor
public enum PushChannelEnum {
/**
* 站内信
*/
WEBSITE(WebsiteMessage.class),
/**
* 钉钉
*/
DING(DingPushMessage.class),
/**
* 飞书
*/
FEI_SHU(FeiShuPushMessage.class),
/**
* 企业微信
*/
WE_COM(WeComPushMessage.class),
;
public final Class<?> messageClass;
@SuppressWarnings("unchecked")
public <T extends PushMessage> T createMessage(String config) {
try {
return (T) JSON.parseObject(config, messageClass);
} catch (Exception e) {
return null;
}
}
/**
* 根据渠道名称获取枚举
*
* @param channel 渠道名称
* @return 枚举
*/
public static PushChannelEnum of(String channel) {
for (PushChannelEnum value : values()) {
if (value.name().equalsIgnoreCase(channel)) {
return value;
}
}
return WEBSITE;
}
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright (c) 2023 - present Dromara, All rights reserved.
*
* https://visor.dromara.org
* https://visor.dromara.org.cn
* https://visor.orionsec.cn
*
* Members:
* Jiahang Li - ljh1553488six@139.com - author
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.visor.framework.biz.push.core.event;
import org.dromara.visor.framework.biz.push.core.message.PushMessage;
import org.springframework.context.ApplicationEvent;
/**
* 消息推送事件
*
* @author Shihao Lv
* @version 1.0.0
* @since 2025/1/15
*/
public class PushMessageEvent extends ApplicationEvent {
public PushMessageEvent(PushMessage message) {
super(message);
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright (c) 2023 - present Dromara, All rights reserved.
*
* https://visor.dromara.org
* https://visor.dromara.org.cn
* https://visor.orionsec.cn
*
* Members:
* Jiahang Li - ljh1553488six@139.com - author
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.visor.framework.biz.push.core.framework.service;
import org.dromara.visor.framework.biz.push.core.message.PushMessage;
/**
* 推送消息模板框架服务
*
* @author Jiahang Li
* @version 1.0.0
* @since 2025/9/21 16:26
*/
public interface PushTemplateFrameworkService {
/**
* 根据 templateId 获取消息
*
* @param templateId templateId
* @return message
*/
PushMessage getPushMessageByTemplateId(Long templateId);
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright (c) 2023 - present Dromara, All rights reserved.
*
* https://visor.dromara.org
* https://visor.dromara.org.cn
* https://visor.orionsec.cn
*
* Members:
* Jiahang Li - ljh1553488six@139.com - author
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.visor.framework.biz.push.core.framework.service;
import org.dromara.visor.framework.biz.push.core.message.PushMessage;
/**
* 推送消息模板框架服务委托类
*
* @author Jiahang Li
* @version 1.0.0
* @since 2025/9/21 16:26
*/
public class PushTemplateFrameworkServiceDelegate implements PushTemplateFrameworkService {
private final PushTemplateFrameworkService delegate;
public PushTemplateFrameworkServiceDelegate(PushTemplateFrameworkService delegate) {
this.delegate = delegate;
}
@Override
public PushMessage getPushMessageByTemplateId(Long templateId) {
return delegate.getPushMessageByTemplateId(templateId);
}
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright (c) 2023 - present Dromara, All rights reserved.
*
* https://visor.dromara.org
* https://visor.dromara.org.cn
* https://visor.orionsec.cn
*
* Members:
* Jiahang Li - ljh1553488six@139.com - author
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.visor.framework.biz.push.core.framework.service;
import org.dromara.visor.framework.biz.push.core.message.WebsiteMessage;
/**
* 发送站内信框架服务
*
* @author Jiahang Li
* @version 1.0.0
* @since 2025/9/18 22:09
*/
public interface WebsiteMessageFrameworkService {
/**
* 发送站内信
*
* @param message message
*/
void push(WebsiteMessage message);
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright (c) 2023 - present Dromara, All rights reserved.
*
* https://visor.dromara.org
* https://visor.dromara.org.cn
* https://visor.orionsec.cn
*
* Members:
* Jiahang Li - ljh1553488six@139.com - author
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.visor.framework.biz.push.core.listener;
import lombok.extern.slf4j.Slf4j;
import org.dromara.visor.framework.biz.push.core.enums.PushChannelEnum;
import org.dromara.visor.framework.biz.push.core.event.PushMessageEvent;
import org.dromara.visor.framework.biz.push.core.message.PushMessage;
import org.dromara.visor.framework.biz.push.core.service.IPushService;
import org.springframework.context.ApplicationListener;
import org.springframework.scheduling.annotation.Async;
import java.util.Map;
/**
* 消息推送事件监听器
*
* @author Shihao Lv
* @version 1.0.0
* @since 2025/1/15
*/
@Slf4j
public class PushMessageEventListener implements ApplicationListener<PushMessageEvent> {
private final Map<PushChannelEnum, IPushService<? extends PushMessage>> pushServiceMap;
public PushMessageEventListener(Map<PushChannelEnum, IPushService<? extends PushMessage>> pushServiceMap) {
this.pushServiceMap = pushServiceMap;
}
@Async("pushExecutor")
@Override
public void onApplicationEvent(PushMessageEvent event) {
try {
// 获取消息
PushMessage message = (PushMessage) event.getSource();
// 发送消息
this.getPushService(message).push(message);
log.info("PushMessageEventListener push success");
} catch (Exception e) {
log.error("PushMessageEventListener push error", e);
}
}
@SuppressWarnings("unchecked")
private <T extends PushMessage> IPushService<T> getPushService(PushMessage message) {
return (IPushService<T>) pushServiceMap.get(message.getChannel());
}
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright (c) 2023 - present Dromara, All rights reserved.
*
* https://visor.dromara.org
* https://visor.dromara.org.cn
* https://visor.orionsec.cn
*
* Members:
* Jiahang Li - ljh1553488six@139.com - author
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.visor.framework.biz.push.core.message;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
import org.dromara.visor.common.entity.PushUser;
import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
/**
* 推送消息基类
*
* @author Shihao Lv
* @version 1.0.0
* @since 2025/9/15 23:28
*/
@Data
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@Schema(name = "BasePushMessage", description = "推送消息基类")
public abstract class BasePushMessage implements PushMessage, Serializable {
@NotBlank
@Schema(description = "消息模板")
private String template;
@Schema(description = "消息参数")
private Map<String, Object> params;
@Valid
@Schema(description = "推送用户")
private List<PushUser> pushUsers;
}

View File

@@ -0,0 +1,66 @@
/*
* Copyright (c) 2023 - present Dromara, All rights reserved.
*
* https://visor.dromara.org
* https://visor.dromara.org.cn
* https://visor.orionsec.cn
*
* Members:
* Jiahang Li - ljh1553488six@139.com - author
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.visor.framework.biz.push.core.message;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
import org.dromara.visor.framework.biz.push.core.enums.PushChannelEnum;
import javax.validation.constraints.NotBlank;
/**
* 钉钉推送消息
*
* @author Shihao Lv
* @version 1.0.0
* @since 2025/9/15 23:29
*/
@Data
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
@Schema(name = "DingPushMessage", description = "钉钉推送消息")
public class DingPushMessage extends BasePushMessage {
@NotBlank
@Schema(description = "webhook")
private String webhook;
@Schema(description = "密钥")
private String secret;
@NotBlank
@Schema(description = "推送标题")
private String title;
@Override
public PushChannelEnum getChannel() {
return PushChannelEnum.DING;
}
}

View File

@@ -0,0 +1,62 @@
/*
* Copyright (c) 2023 - present Dromara, All rights reserved.
*
* https://visor.dromara.org
* https://visor.dromara.org.cn
* https://visor.orionsec.cn
*
* Members:
* Jiahang Li - ljh1553488six@139.com - author
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.visor.framework.biz.push.core.message;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
import org.dromara.visor.framework.biz.push.core.enums.PushChannelEnum;
import javax.validation.constraints.NotBlank;
/**
* 飞书推送消息
*
* @author Shihao Lv
* @version 1.0.0
* @since 2025/9/16 00:26
*/
@Data
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
@Schema(name = "FeiShuPushMessage", description = "飞书推送消息")
public class FeiShuPushMessage extends BasePushMessage {
@NotBlank
@Schema(description = "webhook")
private String webhook;
@Schema(description = "密钥")
private String secret;
@Override
public PushChannelEnum getChannel() {
return PushChannelEnum.FEI_SHU;
}
}

View File

@@ -0,0 +1,90 @@
/*
* Copyright (c) 2023 - present Dromara, All rights reserved.
*
* https://visor.dromara.org
* https://visor.dromara.org.cn
* https://visor.orionsec.cn
*
* Members:
* Jiahang Li - ljh1553488six@139.com - author
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.visor.framework.biz.push.core.message;
import org.dromara.visor.common.entity.PushUser;
import org.dromara.visor.framework.biz.push.core.enums.PushChannelEnum;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
/**
* 推送消息接口
*
* @author Jiahang Li
* @version 1.0.0
* @since 2025/9/18 18:42
*/
public interface PushMessage extends Serializable {
/**
* 获取模板
*
* @return 模板
*/
String getTemplate();
/**
* 设置模板
*
* @param template template
*/
void setTemplate(String template);
/**
* 获取参数
*
* @return 参数
*/
Map<String, Object> getParams();
/**
* 设置参数
*
* @param params params
*/
void setParams(Map<String, Object> params);
/**
* 获取推送用户
*
* @return 推送用户
*/
List<PushUser> getPushUsers();
/**
* 设置推送用户列表
*
* @param pushUsers pushUsers
*/
void setPushUsers(List<PushUser> pushUsers);
/**
* 获取推送渠道
*
* @return 推送渠道
*/
PushChannelEnum getChannel();
}

View File

@@ -0,0 +1,59 @@
/*
* Copyright (c) 2023 - present Dromara, All rights reserved.
*
* https://visor.dromara.org
* https://visor.dromara.org.cn
* https://visor.orionsec.cn
*
* Members:
* Jiahang Li - ljh1553488six@139.com - author
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.visor.framework.biz.push.core.message;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
import org.dromara.visor.framework.biz.push.core.enums.PushChannelEnum;
import javax.validation.constraints.NotBlank;
/**
* 企业微信推送消息
*
* @author Shihao Lv
* @version 1.0.0
* @since 2025/9/15 23:30
*/
@Data
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
@Schema(name = "WeComPushMessage", description = "企业微信推送消息")
public class WeComPushMessage extends BasePushMessage {
@NotBlank
@Schema(description = "webhook")
private String webhook;
@Override
public PushChannelEnum getChannel() {
return PushChannelEnum.WE_COM;
}
}

View File

@@ -0,0 +1,74 @@
/*
* Copyright (c) 2023 - present Dromara, All rights reserved.
*
* https://visor.dromara.org
* https://visor.dromara.org.cn
* https://visor.orionsec.cn
*
* Members:
* Jiahang Li - ljh1553488six@139.com - author
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.visor.framework.biz.push.core.message;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
import org.dromara.visor.framework.biz.push.core.enums.PushChannelEnum;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
/**
* 站内信推送
*
* @author Jiahang Li
* @version 1.0.0
* @since 2025/9/18 19:56
*/
@Data
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
@Schema(name = "WebsiteMessage", description = "站内信消息")
public class WebsiteMessage extends BasePushMessage {
@NotBlank
@Size(max = 10)
@Schema(description = "消息分类")
private String messageClassify;
@NotBlank
@Size(max = 32)
@Schema(description = "消息类型")
private String messageType;
@Schema(description = "消息关联")
private String relKey;
@NotBlank
@Size(max = 128)
@Schema(description = "标题")
private String title;
@Override
public PushChannelEnum getChannel() {
return PushChannelEnum.WEBSITE;
}
}

View File

@@ -0,0 +1,126 @@
/*
* Copyright (c) 2023 - present Dromara, All rights reserved.
*
* https://visor.dromara.org
* https://visor.dromara.org.cn
* https://visor.orionsec.cn
*
* Members:
* Jiahang Li - ljh1553488six@139.com - author
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.visor.framework.biz.push.core.service;
import cn.orionsec.kit.lang.utils.Strings;
import cn.orionsec.kit.lang.utils.collect.Lists;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.dromara.visor.common.constant.Const;
import org.dromara.visor.common.entity.PushUser;
import org.dromara.visor.common.utils.Assert;
import org.dromara.visor.framework.biz.push.core.message.PushMessage;
import java.util.stream.Collectors;
/**
* 消息推送抽象服务
*
* @author Jiahang Li
* @version 1.0.0
* @since 2025/9/18 18:51
*/
@Slf4j
public abstract class BasePushService<Message extends PushMessage> implements IPushService<Message> {
@Override
public void push(Message message) {
try {
// 验证消息
this.validateMessage(message);
} catch (Exception e) {
log.error("BasePushService validateMessage message: {}", JSON.toJSONString(message), e);
return;
}
// 请求路径
String url = this.buildRequestUrl(message);
// 构建请求体
String body = this.buildRequestBody(message);
// 发送请求
this.sendRequest(message, url, body);
}
/**
* 验证消息
*
* @param message message
*/
protected void validateMessage(Message message) {
// 验证消息
Assert.valid(message);
}
/**
* 构建请求 url
*
* @param message message
* @return url
*/
protected abstract String buildRequestUrl(Message message);
/**
* 构建请求体
*
* @param message message
* @return body
*/
protected abstract String buildRequestBody(Message message);
/**
* 发送请求
*
* @param message message
* @param url url
* @param body body
*/
protected abstract void sendRequest(Message message, String url, String body);
/**
* 追加 at 用户
*
* @param message message
* @param content content
* @return content
*/
protected String appendAtUsers(Message message, String content) {
if (Lists.isEmpty(message.getPushUsers())) {
return content;
}
return content + Const.LF + this.getAtUsers(message);
}
/**
* 获取 @ 的用户
*
* @param message message
* @return users
*/
protected String getAtUsers(Message message) {
return Lists.stream(message.getPushUsers())
.map(PushUser::getNickname)
.filter(Strings::isNotBlank)
.map(s -> Const.AT + s)
.collect(Collectors.joining(Const.SPACE));
}
}

View File

@@ -0,0 +1,114 @@
/*
* Copyright (c) 2023 - present Dromara, All rights reserved.
*
* https://visor.dromara.org
* https://visor.dromara.org.cn
* https://visor.orionsec.cn
*
* Members:
* Jiahang Li - ljh1553488six@139.com - author
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.visor.framework.biz.push.core.service;
import cn.orionsec.kit.http.ok.OkRequests;
import cn.orionsec.kit.http.ok.OkResponse;
import cn.orionsec.kit.lang.constant.StandardContentType;
import cn.orionsec.kit.lang.utils.Assert;
import cn.orionsec.kit.lang.utils.Exceptions;
import cn.orionsec.kit.lang.utils.Strings;
import cn.orionsec.kit.lang.utils.codec.Base64s;
import cn.orionsec.kit.lang.utils.crypto.Signatures;
import cn.orionsec.kit.lang.utils.math.Hex;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.dromara.visor.common.constant.ErrorMessage;
import org.dromara.visor.framework.biz.push.core.annotation.MessageChannel;
import org.dromara.visor.framework.biz.push.core.entity.DingRequestBody;
import org.dromara.visor.framework.biz.push.core.entity.DingResponseBody;
import org.dromara.visor.framework.biz.push.core.enums.PushChannelEnum;
import org.dromara.visor.framework.biz.push.core.message.DingPushMessage;
import org.dromara.visor.framework.biz.push.core.utils.MessageUtils;
/**
* 钉钉推送服务类
* <p>
* <a href="https://open.dingtalk.com/document/dingstart/obtain-the-webhook-address-of-a-custom-robot">doc</a>
*
* @author Shihao Lv
* @version 1.0.0
* @since 2025/9/17 23:13
*/
@Slf4j
@MessageChannel(value = PushChannelEnum.DING)
public class DingPushService extends BasePushService<DingPushMessage> {
private static final String MARKDOWN = "markdown";
private static final Integer SUCCESS_CODE = 0;
@Override
protected String buildRequestUrl(DingPushMessage message) {
String secret = message.getSecret();
if (Strings.isBlank(secret)) {
return message.getWebhook();
}
// 加签
try {
long timestamp = System.currentTimeMillis();
String plainText = timestamp + "\n" + secret;
String hexSign = Signatures.hmacSha256(plainText, secret);
if (hexSign == null) {
log.error("DingPushService-sign error plain: {}", plainText);
throw Exceptions.argument(ErrorMessage.GET_REQUEST_URL_ERROR);
}
byte[] signData = Hex.hexToBytes(hexSign);
String sign = Base64s.encodeToString(signData);
// 实际请求地址
return message.getWebhook() + "&timestamp=" + timestamp + "&sign=" + sign;
} catch (Exception e) {
log.error("DingPushService-buildRequestUrl error", e);
throw e;
}
}
@Override
protected String buildRequestBody(DingPushMessage message) {
// 格式化内容
String formattedContent = MessageUtils.format(message.getTemplate(), message.getParams());
String formattedTitle = MessageUtils.format(message.getTitle(), message.getParams());
// at 用户
formattedContent = this.appendAtUsers(message, formattedContent);
// 构建请求体
return DingRequestBody.builder()
.msgType(MARKDOWN)
.markdown(DingRequestBody.MarkdownPayload.builder()
.title(formattedTitle)
.text(formattedContent)
.build())
.build()
.toJsonString();
}
@Override
protected void sendRequest(DingPushMessage message, String url, String body) {
// 发送请求
OkResponse response = OkRequests.post(url, StandardContentType.APPLICATION_JSON_UTF8, body);
DingResponseBody responseBody = JSON.parseObject(response.getBodyString(), DingResponseBody.class);
// 验证发送结果
Assert.eq(responseBody.getErrCode(), SUCCESS_CODE, responseBody.getErrMsg());
}
}

View File

@@ -0,0 +1,118 @@
/*
* Copyright (c) 2023 - present Dromara, All rights reserved.
*
* https://visor.dromara.org
* https://visor.dromara.org.cn
* https://visor.orionsec.cn
*
* Members:
* Jiahang Li - ljh1553488six@139.com - author
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.visor.framework.biz.push.core.service;
import cn.orionsec.kit.http.ok.OkRequests;
import cn.orionsec.kit.http.ok.OkResponse;
import cn.orionsec.kit.lang.constant.Const;
import cn.orionsec.kit.lang.constant.StandardContentType;
import cn.orionsec.kit.lang.utils.Strings;
import cn.orionsec.kit.lang.utils.codec.Base64s;
import cn.orionsec.kit.lang.utils.crypto.Signatures;
import cn.orionsec.kit.lang.utils.crypto.enums.SecretKeySpecMode;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.dromara.visor.common.utils.Assert;
import org.dromara.visor.framework.biz.push.core.annotation.MessageChannel;
import org.dromara.visor.framework.biz.push.core.entity.FeiShuRequestBody;
import org.dromara.visor.framework.biz.push.core.entity.FeiShuResponseBody;
import org.dromara.visor.framework.biz.push.core.enums.PushChannelEnum;
import org.dromara.visor.framework.biz.push.core.message.FeiShuPushMessage;
import org.dromara.visor.framework.biz.push.core.utils.MessageUtils;
/**
* 飞书推送服务类
* <p>
* <a href="https://open.feishu.cn/document/client-docs/bot-v3/add-custom-bot?lang=zh-CN">doc</a>
*
* @author Shihao Lv
* @version 1.0.0
* @since 2025/9/17 23:13
*/
@Slf4j
@MessageChannel(value = PushChannelEnum.FEI_SHU)
public class FeiShuPushService extends BasePushService<FeiShuPushMessage> {
private static final String TEXT = "text";
private static final Integer SUCCESS_CODE = 0;
@Override
protected String buildRequestUrl(FeiShuPushMessage message) {
return message.getWebhook();
}
/**
* 生成签名
*
* @param message message
* @param timestamp timestamp
* @return sign
*/
protected String buildSign(FeiShuPushMessage message, long timestamp) {
String secret = message.getSecret();
if (Strings.isBlank(secret)) {
return null;
}
// 加签
try {
String stringToSign = timestamp + "\n" + secret;
byte[] signData = Signatures.hmacHashSignBytes(new byte[]{}, Strings.bytes(stringToSign), SecretKeySpecMode.HMAC_SHA256);
return Base64s.encodeToString(signData);
} catch (Exception e) {
log.error("FeiShuPushService-buildSign error", e);
return null;
}
}
@Override
protected String buildRequestBody(FeiShuPushMessage message) {
long timestamp = System.currentTimeMillis() / Const.MS_S_1;
// 加签
String sign = this.buildSign(message, timestamp);
// 格式化内容
String formattedContent = MessageUtils.format(message.getTemplate(), message.getParams());
// at 的用户
formattedContent = this.appendAtUsers(message, formattedContent);
// 构建请求体
return FeiShuRequestBody.builder()
.timestamp(timestamp)
.sign(sign)
.msgType(TEXT)
.content(FeiShuRequestBody.TextPayload.builder()
.text(formattedContent)
.build())
.build()
.toJsonString();
}
@Override
protected void sendRequest(FeiShuPushMessage message, String url, String body) {
// 发送请求
OkResponse response = OkRequests.post(url, StandardContentType.APPLICATION_JSON_UTF8, body);
FeiShuResponseBody responseBody = JSON.parseObject(response.getBodyString(), FeiShuResponseBody.class);
// 验证发送结果
Assert.eq(responseBody.getCode(), SUCCESS_CODE, responseBody.getMsg());
}
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright (c) 2023 - present Dromara, All rights reserved.
*
* https://visor.dromara.org
* https://visor.dromara.org.cn
* https://visor.orionsec.cn
*
* Members:
* Jiahang Li - ljh1553488six@139.com - author
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.visor.framework.biz.push.core.service;
import org.dromara.visor.framework.biz.push.core.message.PushMessage;
/**
* 消息推送服务
*
* @author Jiahang Li
* @version 1.0.0
* @since 2025/9/17 17:17
*/
public interface IPushService<Message extends PushMessage> {
/**
* 推送消息
*
* @param message message
*/
void push(Message message);
}

View File

@@ -0,0 +1,85 @@
/*
* Copyright (c) 2023 - present Dromara, All rights reserved.
*
* https://visor.dromara.org
* https://visor.dromara.org.cn
* https://visor.orionsec.cn
*
* Members:
* Jiahang Li - ljh1553488six@139.com - author
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.visor.framework.biz.push.core.service;
import cn.orionsec.kit.http.ok.OkRequests;
import cn.orionsec.kit.http.ok.OkResponse;
import cn.orionsec.kit.lang.constant.StandardContentType;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.dromara.visor.common.utils.Assert;
import org.dromara.visor.framework.biz.push.core.annotation.MessageChannel;
import org.dromara.visor.framework.biz.push.core.entity.WeComRequestBody;
import org.dromara.visor.framework.biz.push.core.entity.WeComResponseBody;
import org.dromara.visor.framework.biz.push.core.enums.PushChannelEnum;
import org.dromara.visor.framework.biz.push.core.message.WeComPushMessage;
import org.dromara.visor.framework.biz.push.core.utils.MessageUtils;
/**
* 企业微信推送服务
* <p>
* <a href="https://developer.work.weixin.qq.com/document/path/99110">docs</a>
*
* @author Shihao Lv
* @version 1.0.0
* @since 2025/9/17 23:13
*/
@Slf4j
@MessageChannel(value = PushChannelEnum.WE_COM)
public class WeComPushService extends BasePushService<WeComPushMessage> {
private static final String MAKRDOWN = "markdown";
private static final Integer SUCCESS_CODE = 0;
@Override
protected String buildRequestUrl(WeComPushMessage message) {
return message.getWebhook();
}
@Override
protected String buildRequestBody(WeComPushMessage message) {
// 格式化内容
String formattedContent = MessageUtils.format(message.getTemplate(), message.getParams());
// at 用户
formattedContent = this.appendAtUsers(message, formattedContent);
// 构建请求体
WeComRequestBody.WeComRequestBodyBuilder builder = WeComRequestBody.builder()
.msgType(MAKRDOWN)
.markdown(WeComRequestBody.MarkdownPayload.builder()
.content(formattedContent)
.build());
return builder.build().toJsonString();
}
@Override
protected void sendRequest(WeComPushMessage message, String url, String body) {
// 发送请求
OkResponse response = OkRequests.post(url, StandardContentType.APPLICATION_JSON_UTF8, body);
WeComResponseBody responseBody = JSON.parseObject(response.getBodyString(), WeComResponseBody.class);
// 验证发送结果
Assert.eq(responseBody.getErrCode(), SUCCESS_CODE, responseBody.getErrMsg());
}
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright (c) 2023 - present Dromara, All rights reserved.
*
* https://visor.dromara.org
* https://visor.dromara.org.cn
* https://visor.orionsec.cn
*
* Members:
* Jiahang Li - ljh1553488six@139.com - author
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.visor.framework.biz.push.core.service;
import lombok.extern.slf4j.Slf4j;
import org.dromara.visor.framework.biz.push.core.annotation.MessageChannel;
import org.dromara.visor.framework.biz.push.core.enums.PushChannelEnum;
import org.dromara.visor.framework.biz.push.core.framework.service.WebsiteMessageFrameworkService;
import org.dromara.visor.framework.biz.push.core.message.WebsiteMessage;
import org.dromara.visor.framework.biz.push.core.utils.MessageUtils;
/**
* 站内信推送服务
*
* @author Jiahang Li
* @version 1.0.0
* @since 2025/9/18 22:13
*/
@Slf4j
@MessageChannel(value = PushChannelEnum.WEBSITE)
public class WebsitePushService extends BasePushService<WebsiteMessage> {
private final WebsiteMessageFrameworkService websiteMessageFrameworkService;
public WebsitePushService(WebsiteMessageFrameworkService websiteMessageFrameworkService) {
this.websiteMessageFrameworkService = websiteMessageFrameworkService;
}
@Override
protected String buildRequestUrl(WebsiteMessage message) {
return null;
}
@Override
protected String buildRequestBody(WebsiteMessage message) {
// 格式化内容
message.setTemplate(MessageUtils.format(message.getTemplate(), message.getParams()));
// 格式化标题
message.setTitle(MessageUtils.format(message.getTitle(), message.getParams()));
return null;
}
@Override
protected void sendRequest(WebsiteMessage message, String url, String body) {
websiteMessageFrameworkService.push(message);
}
}

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