diff --git a/README.md b/README.md index 61df4063..327ef10c 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@

-当前版本: **1.0.7** +当前版本: **1.0.8** **github:** https://github.com/lijiahangmax/orion-ops-pro **gitee:** https://gitee.com/lijiahangmax/orion-ops-pro diff --git a/docker-compose.yml b/docker-compose.yml index 6f8f21f0..3b204e2b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ version: '3.3' services: orion-ops-pro: - image: registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-ops-pro:1.0.7 + image: registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-ops-pro:1.0.8 ports: - 1081:80 environment: @@ -19,7 +19,7 @@ services: - orion-ops-pro-db - orion-ops-pro-redis orion-ops-pro-db: - image: registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-ops-pro-mysql:1.0.7 + image: registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-ops-pro-mysql:1.0.8 privileged: true ports: - 3307:3306 diff --git a/docker/mysql/build.sh b/docker/mysql/build.sh index 597372e8..4b3100c9 100644 --- a/docker/mysql/build.sh +++ b/docker/mysql/build.sh @@ -1,5 +1,5 @@ #/bin/bash -version=1.0.7 +version=1.0.8 cp -r ../../sql ./sql docker build -t orion-ops-pro-mysql:${version} . rm -rf ./sql diff --git a/docker/orion-ops-pro/build.sh b/docker/orion-ops-pro/build.sh index 29f79d71..012efdb4 100644 --- a/docker/orion-ops-pro/build.sh +++ b/docker/orion-ops-pro/build.sh @@ -1,5 +1,5 @@ #/bin/bash -version=1.0.7 +version=1.0.8 mv ../../orion-ops-launch/target/orion-ops-launch.jar ./orion-ops-launch.jar mv ../../orion-ops-ui/dist ./dist docker build -t orion-ops-pro:${version} . diff --git a/docs/README.md b/docs/README.md index 12e7f642..78db4771 100644 --- a/docs/README.md +++ b/docs/README.md @@ -26,7 +26,7 @@

-当前版本: **1.0.7** +当前版本: **1.0.8** **github:** https://github.com/lijiahangmax/orion-ops-pro **gitee:** https://gitee.com/lijiahangmax/orion-ops-pro diff --git a/docs/_coverpage.md b/docs/_coverpage.md index 34ea5f45..f1676b7e 100644 --- a/docs/_coverpage.md +++ b/docs/_coverpage.md @@ -1,4 +1,4 @@ -# orion-ops-pro 1.0.7 +# orion-ops-pro 1.0.8 > 一款开箱即用的运维平台。 diff --git a/docs/about/change-log.md b/docs/about/change-log.md index f08e64e2..e400c15b 100644 --- a/docs/about/change-log.md +++ b/docs/about/change-log.md @@ -7,6 +7,16 @@ * 执行完成菜单 sql 后请刷新缓存 `系统设置` > `系统菜单` > `刷新缓存` * 执行完成字典 sql 后请刷新缓存 `系统设置` > `数据字典项` > `刷新缓存` +### v1.0.8 + +`2024-05-15` `release` + +* 🌈 新增 站内信模块 +* 🔨 优化 执行命令日志跳转逻辑 +* 🔨 修改 `exitStatus` 改为 `exitCode` + +[如何升级](/update/v1.0.8.md) + ### v1.0.7 `2024-05-13` `release` diff --git a/docs/about/roadmap.md b/docs/about/roadmap.md index 066aede7..f6f04cce 100644 --- a/docs/about/roadmap.md +++ b/docs/about/roadmap.md @@ -1,7 +1,5 @@ ## 功能排期 -* 批量上传 -* 站内消息 * 终端背景图片 * 资产授权 UI 改版 * RDP 远程桌面 diff --git a/docs/update/v1.0.8.md b/docs/update/v1.0.8.md index 50b325e0..64d79312 100644 --- a/docs/update/v1.0.8.md +++ b/docs/update/v1.0.8.md @@ -3,9 +3,53 @@ > sql 脚本 - DDL ```sql +-- 修改字段名称 +ALTER TABLE `exec_host_log` +CHANGE COLUMN `exit_status` `exit_code` int(0) NULL DEFAULT NULL COMMENT '退出码' AFTER `parameter`; + +-- 系统消息 +DROP TABLE IF EXISTS `system_message`; +CREATE TABLE `system_message` +( + `id` bigint(0) NOT NULL AUTO_INCREMENT COMMENT 'id', + `classify` char(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '消息分类', + `type` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '消息类型', + `status` tinyint(0) NULL DEFAULT NULL COMMENT '消息状态', + `rel_key` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '消息关联', + `title` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '标题', + `content` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '消息内容', + `receiver_id` bigint(0) NULL DEFAULT NULL COMMENT '接收人id', + `receiver_username` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '接收人用户名', + `create_time` datetime(0) NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` datetime(0) NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP(0) COMMENT '修改时间', + `deleted` tinyint(1) NULL DEFAULT 0 COMMENT '是否删除 0未删除 1已删除', + PRIMARY KEY (`id`) USING BTREE, + INDEX `idx_receiver_classify` (`receiver_id`, `classify`) USING BTREE +) ENGINE = InnoDB + AUTO_INCREMENT = 1 + CHARACTER SET = utf8mb4 + COLLATE = utf8mb4_general_ci COMMENT = '系统消息' + ROW_FORMAT = Dynamic; ``` > sql 脚本 - DML ```sql +-- 菜单 +DELETE FROM system_menu WHERE id IN (161, 175, 197, 198) OR id > 202; +INSERT INTO `system_menu` VALUES (161, 176, '执行模板', NULL, 2, 50, 1, 1, 1, 0, 'IconBookmark', NULL, 'execTemplate', '2024-03-07 18:32:41', '2024-05-14 15:58:51', '1', '1', 0); +INSERT INTO `system_menu` VALUES (197, 176, '批量上传', NULL, 2, 30, 1, 1, 1, 0, 'IconUpload', NULL, 'batchUpload', '2024-05-08 22:12:23', '2024-05-14 15:58:44', '1', '1', 0); +INSERT INTO `system_menu` VALUES (198, 176, '上传任务', NULL, 2, 40, 1, 1, 1, 0, 'IconCloud', NULL, 'uploadTask', '2024-05-08 22:16:05', '2024-05-14 15:58:46', '1', '1', 0); + +-- 字典项 +DELETE FROM dict_key WHERE id >= 43; +INSERT INTO `dict_key` VALUES (43, 'messageType', 'STRING', '[{\"name\": \"tagLabel\", \"type\": \"STRING\"}, {\"name\": \"tagVisible\", \"type\": \"STRING\"}, {\"name\": \"tagColor\", \"type\": \"STRING\"}, {\"name\": \"redirectComponent\", \"type\": \"STRING\"}]', '消息类型', '2024-05-13 12:07:56', '2024-05-14 14:48:28', '1', '1', 0); +INSERT INTO `dict_key` VALUES (44, 'messageClassify', 'STRING', '[]', '消息分类', '2024-05-13 15:06:27', '2024-05-13 15:06:27', '1', '1', 0); + +-- 字典值 +DELETE FROM dict_value WHERE id >= 295; +INSERT INTO `dict_value` VALUES (295, 43, 'messageType', 'EXEC_FAILED', '执行失败', '{\"tagColor\": \"red\", \"tagLabel\": \"部分失败\", \"tagVisible\": \"true\", \"redirectComponent\": \"execCommand\"}', 10, '2024-05-13 12:07:56', '2024-05-14 15:19:19', '1', '1', 0); +INSERT INTO `dict_value` VALUES (296, 43, 'messageType', 'UPLOAD_FAILED', '上传失败', '{\"tagColor\": \"red\", \"tagLabel\": \"部分失败\", \"tagVisible\": \"true\", \"redirectComponent\": \"batchUpload\"}', 20, '2024-05-13 12:07:56', '2024-05-14 15:11:21', '1', '1', 0); +INSERT INTO `dict_value` VALUES (297, 44, 'messageClassify', 'NOTICE', '通知', '{}', 10, '2024-05-13 15:06:27', '2024-05-13 15:06:27', '1', '1', 0); +INSERT INTO `dict_value` VALUES (298, 44, 'messageClassify', 'TODO', '待办', '{}', 20, '2024-05-13 15:06:27', '2024-05-13 15:06:27', '1', '1', 0); ``` diff --git a/docs/update/v2.0.0.md b/docs/update/v2.0.0.md new file mode 100644 index 00000000..712bfab8 --- /dev/null +++ b/docs/update/v2.0.0.md @@ -0,0 +1,11 @@ +## v2.0.0 + +> sql 脚本 - DDL + +```sql +``` + +> sql 脚本 - DML + +```sql +``` diff --git a/orion-ops-dependencies/pom.xml b/orion-ops-dependencies/pom.xml index f27115f1..c75a58c9 100644 --- a/orion-ops-dependencies/pom.xml +++ b/orion-ops-dependencies/pom.xml @@ -14,7 +14,7 @@ https://github.com/lijiahangmax/orion-ops-pro - 1.0.7 + 1.0.8 2.7.17 2.7.15 1.5.0 diff --git a/orion-ops-framework/orion-ops-framework-common/src/main/java/com/orion/ops/framework/common/constant/AppConst.java b/orion-ops-framework/orion-ops-framework-common/src/main/java/com/orion/ops/framework/common/constant/AppConst.java index a93b5cb9..6a9c1740 100644 --- a/orion-ops-framework/orion-ops-framework-common/src/main/java/com/orion/ops/framework/common/constant/AppConst.java +++ b/orion-ops-framework/orion-ops-framework-common/src/main/java/com/orion/ops/framework/common/constant/AppConst.java @@ -14,7 +14,7 @@ public interface AppConst extends OrionConst { /** * 同 ${orion.version} 迭代时候需要手动更改 */ - String VERSION = "1.0.7"; + String VERSION = "1.0.8"; String ORION_OPS_PRO = "orion-ops-pro"; diff --git a/orion-ops-framework/orion-ops-framework-common/src/main/java/com/orion/ops/framework/common/constant/FieldConst.java b/orion-ops-framework/orion-ops-framework-common/src/main/java/com/orion/ops/framework/common/constant/FieldConst.java index 5c48f978..505b777f 100644 --- a/orion-ops-framework/orion-ops-framework-common/src/main/java/com/orion/ops/framework/common/constant/FieldConst.java +++ b/orion-ops-framework/orion-ops-framework-common/src/main/java/com/orion/ops/framework/common/constant/FieldConst.java @@ -55,6 +55,10 @@ public interface FieldConst { String COUNT = "count"; + String DATE = "date"; + + String TIME = "time"; + String LOCATION = "location"; String USER_AGENT = "userAgent"; diff --git a/orion-ops-framework/orion-ops-spring-boot-starter-mybatis/src/main/java/com/orion/ops/framework/mybatis/core/generator/CodeGenerators.java b/orion-ops-framework/orion-ops-spring-boot-starter-mybatis/src/main/java/com/orion/ops/framework/mybatis/core/generator/CodeGenerators.java index 2811e166..e8ff942a 100644 --- a/orion-ops-framework/orion-ops-spring-boot-starter-mybatis/src/main/java/com/orion/ops/framework/mybatis/core/generator/CodeGenerators.java +++ b/orion-ops-framework/orion-ops-spring-boot-starter-mybatis/src/main/java/com/orion/ops/framework/mybatis/core/generator/CodeGenerators.java @@ -26,7 +26,7 @@ public class CodeGenerators { // 作者 String author = Const.ORION_AUTHOR; // 模块 - String module = "asset"; + String module = "infra"; // 生成的表 Table[] tables = { // Template.create("dict_key", "字典配置项", "dict") @@ -49,24 +49,22 @@ public class CodeGenerators { // .disableUnitTest() // .vue("exec", "exec-template-host") // .build(), - Template.create("upload_task", "上传任务", "upload") + Template.create("system_message", "系统消息", "message") .disableUnitTest() - .vue("exec", "upload-task") - .enableRowSelection() - .dict("uploadTaskStatus", "status") - .comment("上传任务状态") - .fields("WAITING", "UPLOADING", "FINISHED", "FAILED", "CANCELED") - .labels("等待中", "上传中", "已完成", "已失败", "已取消") + .enableProviderApi() + .vue("system", "message") + .dict("messageClassify", "classify", "messageClassify") + .comment("消息分类") + .fields("NOTICE", "TODO") + .labels("通知", "待办") .valueUseFields() - .build(), - Template.create("upload_task_file", "上传任务文件", "upload") - .disableUnitTest() - .vue("exec", "upload-task-file") - .enableRowSelection() - .dict("uploadTaskFileStatus", "status") - .comment("上传任务文件状态") - .fields("WAITING", "UPLOADING", "FINISHED", "FAILED", "CANCELED") - .labels("等待中", "上传中", "已完成", "已失败", "已取消") + .dict("messageType", "type", "messageType") + .comment("消息类型") + .fields("EXEC_FAILED", "UPLOAD_FAILED") + .labels("执行失败", "上传失败") + .extra("tagLabel", "执行失败", "上传失败") + .extra("tagVisible", true, true) + .extra("tagColor", "red", "red") .valueUseFields() .build(), }; diff --git a/orion-ops-framework/orion-ops-spring-boot-starter-mybatis/src/main/java/com/orion/ops/framework/mybatis/core/generator/template/DictTemplate.java b/orion-ops-framework/orion-ops-spring-boot-starter-mybatis/src/main/java/com/orion/ops/framework/mybatis/core/generator/template/DictTemplate.java index 4760400a..214a46aa 100644 --- a/orion-ops-framework/orion-ops-spring-boot-starter-mybatis/src/main/java/com/orion/ops/framework/mybatis/core/generator/template/DictTemplate.java +++ b/orion-ops-framework/orion-ops-spring-boot-starter-mybatis/src/main/java/com/orion/ops/framework/mybatis/core/generator/template/DictTemplate.java @@ -14,6 +14,17 @@ import java.util.LinkedHashMap; */ public class DictTemplate extends Template { + // // $comment + // export const $field = { + // // labels[0] + // fields[0]: 'values[0]', + // // labels[1] + // fields[0]: 'values[1]', + // }; + // + // // $comment 字典项 + // export const $keyField = '$keyName'; + private final DictMeta dictMeta; public DictTemplate(Table table, String keyName, String variable) { diff --git a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/define/message/ExecMessageDefine.java b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/define/message/ExecMessageDefine.java new file mode 100644 index 00000000..b850f79d --- /dev/null +++ b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/define/message/ExecMessageDefine.java @@ -0,0 +1,53 @@ +package com.orion.ops.module.asset.define.message; + +import com.orion.ops.module.infra.define.SystemMessageDefine; +import com.orion.ops.module.infra.enums.MessageClassifyEnum; +import lombok.Getter; + +/** + * 命令执行 系统消息定义 + * + * @author Jiahang Li + * @version 1.0.0 + * @since 2024/5/14 17:23 + */ +@Getter +public enum ExecMessageDefine implements SystemMessageDefine { + + /** + * 命令执行部分失败 + */ + EXEC_FAILED(MessageClassifyEnum.NOTICE, + "命令执行失败", + "您在 ${time} 执行的命令部分失败, 或者返回了非零的 exitCode。点击查看详情 #${id} >>>"), + + ; + + ExecMessageDefine(MessageClassifyEnum classify, String title, String content) { + this.classify = classify; + this.type = this.name(); + this.title = title; + this.content = content; + } + + /** + * 消息分类 + */ + private final MessageClassifyEnum classify; + + /** + * 消息类型 + */ + private final String type; + + /** + * 标题 + */ + private final String title; + + /** + * 内容 + */ + private final String content; + +} diff --git a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/define/message/UploadMessageDefine.java b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/define/message/UploadMessageDefine.java new file mode 100644 index 00000000..bd02296c --- /dev/null +++ b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/define/message/UploadMessageDefine.java @@ -0,0 +1,53 @@ +package com.orion.ops.module.asset.define.message; + +import com.orion.ops.module.infra.define.SystemMessageDefine; +import com.orion.ops.module.infra.enums.MessageClassifyEnum; +import lombok.Getter; + +/** + * 上传任务 系统消息定义 + * + * @author Jiahang Li + * @version 1.0.0 + * @since 2024/5/14 17:23 + */ +@Getter +public enum UploadMessageDefine implements SystemMessageDefine { + + /** + * 上传任务部分失败 + */ + UPLOAD_FAILED(MessageClassifyEnum.NOTICE, + "批量上传失败", + "您在 ${time} 提交的上传任务中, 有部分主机文件上传失败。点击查看详情 #${id} >>>"), + + ; + + UploadMessageDefine(MessageClassifyEnum classify, String title, String content) { + this.classify = classify; + this.type = this.name(); + this.title = title; + this.content = content; + } + + /** + * 消息分类 + */ + private final MessageClassifyEnum classify; + + /** + * 消息类型 + */ + private final String type; + + /** + * 标题 + */ + private final String title; + + /** + * 内容 + */ + private final String content; + +} diff --git a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/entity/domain/ExecHostLogDO.java b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/entity/domain/ExecHostLogDO.java index e6e950c5..3c9e1fee 100644 --- a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/entity/domain/ExecHostLogDO.java +++ b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/entity/domain/ExecHostLogDO.java @@ -61,8 +61,8 @@ public class ExecHostLogDO extends BaseDO { private String parameter; @Schema(description = "退出码") - @TableField("exit_status") - private Integer exitStatus; + @TableField("exit_code") + private Integer exitCode; @Schema(description = "日志路径") @TableField("log_path") diff --git a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/entity/vo/ExecHostLogVO.java b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/entity/vo/ExecHostLogVO.java index 5260a96f..6824aaf0 100644 --- a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/entity/vo/ExecHostLogVO.java +++ b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/entity/vo/ExecHostLogVO.java @@ -50,7 +50,7 @@ public class ExecHostLogVO implements Serializable { private String parameter; @Schema(description = "退出码") - private Integer exitStatus; + private Integer exitCode; @Schema(description = "错误信息") private String errorMessage; diff --git a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/exec/command/dto/ExecCommandDTO.java b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/exec/command/dto/ExecCommandDTO.java index 909c9dd1..6a239c13 100644 --- a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/exec/command/dto/ExecCommandDTO.java +++ b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/exec/command/dto/ExecCommandDTO.java @@ -22,7 +22,7 @@ import java.util.List; @Schema(name = "ExecCommandDTO", description = "批量执行启动对象") public class ExecCommandDTO { - @Schema(description = "hostId") + @Schema(description = "logId") private Long logId; @Schema(description = "用户id") diff --git a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/exec/command/handler/BaseExecCommandHandler.java b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/exec/command/handler/BaseExecCommandHandler.java index dde24a38..b8a2aecd 100644 --- a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/exec/command/handler/BaseExecCommandHandler.java +++ b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/exec/command/handler/BaseExecCommandHandler.java @@ -67,6 +67,9 @@ public abstract class BaseExecCommandHandler implements IExecCommandHandler { private CommandExecutor executor; + @Getter + private Integer exitCode; + private volatile boolean closed; private volatile boolean interrupted; @@ -227,7 +230,8 @@ public abstract class BaseExecCommandHandler implements IExecCommandHandler { } else if (ExecHostStatusEnum.COMPLETED.equals(status)) { // 完成 updateRecord.setFinishTime(new Date()); - updateRecord.setExitStatus(executor.getExitCode()); + updateRecord.setExitCode(executor.getExitCode()); + this.exitCode = executor.getExitCode(); } else if (ExecHostStatusEnum.FAILED.equals(status)) { // 失败 updateRecord.setFinishTime(new Date()); diff --git a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/exec/command/handler/ExecCommandAnsiHandler.java b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/exec/command/handler/ExecCommandAnsiHandler.java index 744bd2e5..b313457f 100644 --- a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/exec/command/handler/ExecCommandAnsiHandler.java +++ b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/exec/command/handler/ExecCommandAnsiHandler.java @@ -149,17 +149,17 @@ public class ExecCommandAnsiHandler extends BaseExecCommandHandler { .append(Dates.current()) .newLine(); } else { - long ms = this.updateRecord.getFinishTime().getTime() - this.updateRecord.getStartTime().getTime(); - Integer exitStatus = this.updateRecord.getExitStatus(); + long ms = updateRecord.getFinishTime().getTime() - updateRecord.getStartTime().getTime(); + Integer exitCode = updateRecord.getExitCode(); // 执行完成 appender.append(AnsiForeground.BRIGHT_GREEN, "< 命令执行完成 ") .append(Dates.current()) .newLine() .append(AnsiForeground.BRIGHT_BLUE, "exit: "); - if (ExitCode.isSuccess(exitStatus)) { - appender.append(AnsiForeground.BRIGHT_GREEN, exitStatus); + if (ExitCode.isSuccess(exitCode)) { + appender.append(AnsiForeground.BRIGHT_GREEN, exitCode); } else { - appender.append(AnsiForeground.BRIGHT_RED, exitStatus); + appender.append(AnsiForeground.BRIGHT_RED, exitCode); } appender.newLine() .append(AnsiForeground.BRIGHT_BLUE, "used: ") diff --git a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/exec/command/handler/ExecTaskHandler.java b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/exec/command/handler/ExecTaskHandler.java index 93d8ed93..53f58d58 100644 --- a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/exec/command/handler/ExecTaskHandler.java +++ b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/exec/command/handler/ExecTaskHandler.java @@ -7,20 +7,29 @@ import com.orion.lang.utils.Booleans; import com.orion.lang.utils.Threads; import com.orion.lang.utils.collect.Lists; import com.orion.lang.utils.io.Streams; +import com.orion.lang.utils.time.Dates; +import com.orion.net.host.ssh.ExitCode; +import com.orion.ops.framework.common.constant.ExtraFieldConst; import com.orion.ops.module.asset.dao.ExecLogDAO; import com.orion.ops.module.asset.define.AssetThreadPools; import com.orion.ops.module.asset.define.config.AppExecLogConfig; +import com.orion.ops.module.asset.define.message.ExecMessageDefine; import com.orion.ops.module.asset.entity.domain.ExecLogDO; +import com.orion.ops.module.asset.enums.ExecHostStatusEnum; import com.orion.ops.module.asset.enums.ExecStatusEnum; import com.orion.ops.module.asset.handler.host.exec.command.dto.ExecCommandDTO; import com.orion.ops.module.asset.handler.host.exec.command.dto.ExecCommandHostDTO; import com.orion.ops.module.asset.handler.host.exec.command.manager.ExecTaskManager; +import com.orion.ops.module.infra.api.SystemMessageApi; +import com.orion.ops.module.infra.entity.dto.message.SystemMessageDTO; import com.orion.spring.SpringHolder; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import java.util.Date; +import java.util.HashMap; import java.util.List; +import java.util.Map; /** * 命令执行任务 @@ -38,6 +47,8 @@ public class ExecTaskHandler implements IExecTaskHandler { private static final AppExecLogConfig appExecLogConfig = SpringHolder.getBean(AppExecLogConfig.class); + private static final SystemMessageApi systemMessageApi = SpringHolder.getBean(SystemMessageApi.class); + private final ExecCommandDTO execCommand; private TimeoutChecker timeoutChecker; @@ -45,6 +56,8 @@ public class ExecTaskHandler implements IExecTaskHandler { @Getter private final List handlers; + private Date startTime; + public ExecTaskHandler(ExecCommandDTO execCommand) { this.execCommand = execCommand; this.handlers = Lists.newList(); @@ -56,9 +69,9 @@ public class ExecTaskHandler implements IExecTaskHandler { // 添加任务 execTaskManager.addTask(id, this); log.info("ExecTaskHandler.run start id: {}", id); - // 更新状态 - this.updateStatus(ExecStatusEnum.RUNNING); try { + // 更新状态 + this.updateStatus(ExecStatusEnum.RUNNING); // 执行命令 this.runHostCommand(); // 更新状态-执行完成 @@ -69,10 +82,12 @@ public class ExecTaskHandler implements IExecTaskHandler { this.updateStatus(ExecStatusEnum.FAILED); log.error("ExecTaskHandler.run error id: {}", id, e); } finally { - // 释放资源 - Streams.close(this); + // 检查是否发送消息 + this.checkSendMessage(); // 移除任务 execTaskManager.removeTask(id); + // 释放资源 + this.close(); } } @@ -82,6 +97,13 @@ public class ExecTaskHandler implements IExecTaskHandler { handlers.forEach(IExecCommandHandler::interrupt); } + @Override + public void close() { + log.info("ExecTaskHandler-close id: {}", execCommand.getLogId()); + Streams.close(timeoutChecker); + this.handlers.forEach(Streams::close); + } + /** * 执行主机命令 * @@ -139,6 +161,7 @@ public class ExecTaskHandler implements IExecTaskHandler { update.setStatus(statusName); if (ExecStatusEnum.RUNNING.equals(status)) { // 执行中 + this.startTime = new Date(); update.setStartTime(new Date()); } else if (ExecStatusEnum.COMPLETED.equals(status)) { // 执行完成 @@ -151,11 +174,30 @@ public class ExecTaskHandler implements IExecTaskHandler { log.info("ExecTaskHandler-updateStatus finish id: {}, effect: {}", id, effect); } - @Override - public void close() { - log.info("ExecTaskHandler-close id: {}", execCommand.getLogId()); - Streams.close(timeoutChecker); - this.handlers.forEach(Streams::close); + /** + * 检查是否发送消息 + */ + private void checkSendMessage() { + // 检查是否执行失败/exitCode + boolean hasError = handlers.stream().anyMatch(s -> + ExecHostStatusEnum.FAILED.equals(s.getStatus()) + || ExecHostStatusEnum.TIMEOUT.equals(s.getStatus()) + || !ExitCode.isSuccess(s.getExitCode())); + if (!hasError) { + return; + } + // 参数 + Map params = new HashMap<>(); + params.put(ExtraFieldConst.ID, execCommand.getLogId()); + params.put(ExtraFieldConst.TIME, Dates.format(this.startTime, Dates.MD_HM)); + SystemMessageDTO message = SystemMessageDTO.builder() + .receiverId(execCommand.getUserId()) + .receiverUsername(execCommand.getUsername()) + .relKey(String.valueOf(execCommand.getLogId())) + .params(params) + .build(); + // 发送 + systemMessageApi.create(ExecMessageDefine.EXEC_FAILED, message); } } diff --git a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/exec/command/handler/IExecCommandHandler.java b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/exec/command/handler/IExecCommandHandler.java index 79f2c579..6e6ae93c 100644 --- a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/exec/command/handler/IExecCommandHandler.java +++ b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/exec/command/handler/IExecCommandHandler.java @@ -31,6 +31,13 @@ public interface IExecCommandHandler extends Runnable, SafeCloseable { */ ExecHostStatusEnum getStatus(); + /** + * 获取退出码 + * + * @return exit code + */ + Integer getExitCode(); + /** * 获取主机 id * diff --git a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/upload/task/FileUploadTask.java b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/upload/task/FileUploadTask.java index 504e5d66..a893e422 100644 --- a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/upload/task/FileUploadTask.java +++ b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/upload/task/FileUploadTask.java @@ -3,10 +3,13 @@ package com.orion.ops.module.asset.handler.host.upload.task; import com.orion.lang.utils.Threads; import com.orion.lang.utils.io.Files1; import com.orion.lang.utils.io.Streams; +import com.orion.lang.utils.time.Dates; import com.orion.ops.framework.common.constant.Const; +import com.orion.ops.framework.common.constant.ExtraFieldConst; import com.orion.ops.module.asset.dao.UploadTaskDAO; import com.orion.ops.module.asset.dao.UploadTaskFileDAO; import com.orion.ops.module.asset.define.AssetThreadPools; +import com.orion.ops.module.asset.define.message.UploadMessageDefine; import com.orion.ops.module.asset.entity.domain.UploadTaskDO; import com.orion.ops.module.asset.entity.domain.UploadTaskFileDO; import com.orion.ops.module.asset.enums.UploadTaskFileStatusEnum; @@ -16,14 +19,13 @@ import com.orion.ops.module.asset.handler.host.upload.manager.FileUploadTaskMana import com.orion.ops.module.asset.handler.host.upload.uploader.FileUploader; import com.orion.ops.module.asset.handler.host.upload.uploader.IFileUploader; import com.orion.ops.module.asset.service.UploadTaskService; +import com.orion.ops.module.infra.api.SystemMessageApi; +import com.orion.ops.module.infra.entity.dto.message.SystemMessageDTO; import com.orion.spring.SpringHolder; import lombok.Getter; import lombok.extern.slf4j.Slf4j; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.stream.Collectors; /** @@ -42,6 +44,8 @@ public class FileUploadTask implements IFileUploadTask { private static final UploadTaskService uploadTaskService = SpringHolder.getBean(UploadTaskService.class); + private static final SystemMessageApi systemMessageApi = SpringHolder.getBean(SystemMessageApi.class); + private static final FileUploadTaskManager fileUploadTaskManager = SpringHolder.getBean(FileUploadTaskManager.class); private final Long id; @@ -91,6 +95,8 @@ public class FileUploadTask implements IFileUploadTask { } else { this.updateStatus(UploadTaskStatusEnum.FINISHED); } + // 检查是否发送消息 + this.checkSendMessage(); // 移除任务 fileUploadTaskManager.removeTask(id); // 释放资源 @@ -187,4 +193,33 @@ public class FileUploadTask implements IFileUploadTask { uploadTaskDAO.updateById(update); } + /** + * 检查是否发送消息 + */ + private void checkSendMessage() { + if (canceled) { + return; + } + // 检查是否上传失败 + boolean hasError = uploaderList.stream() + .map(IFileUploader::getFiles) + .flatMap(Collection::stream) + .anyMatch(s -> UploadTaskFileStatusEnum.FAILED.name().equals(s.getStatus())); + if (!hasError) { + return; + } + // 参数 + Map params = new HashMap<>(); + params.put(ExtraFieldConst.ID, record.getId()); + params.put(ExtraFieldConst.TIME, Dates.format(record.getCreateTime(), Dates.MD_HM)); + SystemMessageDTO message = SystemMessageDTO.builder() + .receiverId(record.getUserId()) + .receiverUsername(record.getUsername()) + .relKey(String.valueOf(record.getId())) + .params(params) + .build(); + // 发送 + systemMessageApi.create(UploadMessageDefine.UPLOAD_FAILED, message); + } + } diff --git a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/service/impl/ExecLogServiceImpl.java b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/service/impl/ExecLogServiceImpl.java index 110d33af..a11cb412 100644 --- a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/service/impl/ExecLogServiceImpl.java +++ b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/service/impl/ExecLogServiceImpl.java @@ -156,7 +156,7 @@ public class ExecLogServiceImpl implements ExecLogService { ExecHostLogDO::getStatus, ExecHostLogDO::getStartTime, ExecHostLogDO::getFinishTime, - ExecHostLogDO::getExitStatus, + ExecHostLogDO::getExitCode, ExecHostLogDO::getErrorMessage) .in(ExecHostLogDO::getLogId, idList) .then() diff --git a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/resources/mapper/ExecHostLogMapper.xml b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/resources/mapper/ExecHostLogMapper.xml index 98177313..1f4b1991 100644 --- a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/resources/mapper/ExecHostLogMapper.xml +++ b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/resources/mapper/ExecHostLogMapper.xml @@ -12,7 +12,7 @@ - + @@ -27,7 +27,7 @@ - id, log_id, host_id, host_name, host_address, status, command, parameter, exit_status, log_path, script_path, error_message, start_time, finish_time, create_time, update_time, creator, updater, deleted + id, log_id, host_id, host_name, host_address, status, command, parameter, exit_code, log_path, script_path, error_message, start_time, finish_time, create_time, update_time, creator, updater, deleted diff --git a/orion-ops-module-infra/orion-ops-module-infra-provider/src/main/java/com/orion/ops/module/infra/api/SystemMessageApi.java b/orion-ops-module-infra/orion-ops-module-infra-provider/src/main/java/com/orion/ops/module/infra/api/SystemMessageApi.java new file mode 100644 index 00000000..0ecd4add --- /dev/null +++ b/orion-ops-module-infra/orion-ops-module-infra-provider/src/main/java/com/orion/ops/module/infra/api/SystemMessageApi.java @@ -0,0 +1,35 @@ +package com.orion.ops.module.infra.api; + +import com.orion.ops.module.infra.define.SystemMessageDefine; +import com.orion.ops.module.infra.entity.dto.message.SystemMessageCreateDTO; +import com.orion.ops.module.infra.entity.dto.message.SystemMessageDTO; +import com.orion.ops.module.infra.enums.MessageClassifyEnum; + +/** + * 系统消息 对外服务类 + * + * @author Jiahang Li + * @version 1.0.8 + * @since 2024-5-11 16:29 + */ +public interface SystemMessageApi { + + /** + * 创建系统消息 + * + * @param define define + * @param dto dto + * @return id + */ + Long create(SystemMessageDefine define, SystemMessageDTO dto); + + /** + * 创建系统消息 + * + * @param classify classify + * @param dto dto + * @return id + */ + Long create(MessageClassifyEnum classify, SystemMessageCreateDTO dto); + +} diff --git a/orion-ops-module-infra/orion-ops-module-infra-provider/src/main/java/com/orion/ops/module/infra/define/SystemMessageDefine.java b/orion-ops-module-infra/orion-ops-module-infra-provider/src/main/java/com/orion/ops/module/infra/define/SystemMessageDefine.java new file mode 100644 index 00000000..b2e836a9 --- /dev/null +++ b/orion-ops-module-infra/orion-ops-module-infra-provider/src/main/java/com/orion/ops/module/infra/define/SystemMessageDefine.java @@ -0,0 +1,47 @@ +package com.orion.ops.module.infra.define; + +import com.orion.lang.utils.Strings; +import com.orion.ops.module.infra.enums.MessageClassifyEnum; + +import java.util.Map; + +/** + * 系统消息定义 + * + * @author Jiahang Li + * @version 1.0.0 + * @since 2024/5/14 17:06 + */ +public interface SystemMessageDefine { + + /** + * @return 消息分类 + */ + MessageClassifyEnum getClassify(); + + /** + * @return 消息类型 + */ + String getType(); + + /** + * @return 标题 + */ + String getTitle(); + + /** + * @return 内容 + */ + String getContent(); + + /** + * 格式化内容 + * + * @param params params + * @return content + */ + default String formatContent(Map params) { + return Strings.format(this.getContent(), params); + } + +} diff --git a/orion-ops-module-infra/orion-ops-module-infra-provider/src/main/java/com/orion/ops/module/infra/entity/dto/message/SystemMessageCreateDTO.java b/orion-ops-module-infra/orion-ops-module-infra-provider/src/main/java/com/orion/ops/module/infra/entity/dto/message/SystemMessageCreateDTO.java new file mode 100644 index 00000000..c3ea71e9 --- /dev/null +++ b/orion-ops-module-infra/orion-ops-module-infra-provider/src/main/java/com/orion/ops/module/infra/entity/dto/message/SystemMessageCreateDTO.java @@ -0,0 +1,61 @@ +package com.orion.ops.module.infra.entity.dto.message; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import java.io.Serializable; + +/** + * 系统消息 创建请求业务对象 + * + * @author Jiahang Li + * @version 1.0.8 + * @since 2024-5-11 16:29 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Schema(name = "SystemMessageCreateDTO", description = "系统消息 创建请求业务对象") +public class SystemMessageCreateDTO implements Serializable { + + private static final long serialVersionUID = 1L; + + @NotBlank + @Size(max = 10) + @Schema(description = "消息分类") + private String classify; + + @NotBlank + @Size(max = 32) + @Schema(description = "消息类型") + private String type; + + @NotBlank + @Size(max = 64) + @Schema(description = "消息关联") + private String relKey; + + @NotBlank + @Size(max = 128) + @Schema(description = "标题") + private String title; + + @NotBlank + @Schema(description = "消息内容") + private String content; + + @NotNull + @Schema(description = "接收人id") + private Long receiverId; + + @Schema(description = "接收人用户名") + private String receiverUsername; + +} diff --git a/orion-ops-module-infra/orion-ops-module-infra-provider/src/main/java/com/orion/ops/module/infra/entity/dto/message/SystemMessageDTO.java b/orion-ops-module-infra/orion-ops-module-infra-provider/src/main/java/com/orion/ops/module/infra/entity/dto/message/SystemMessageDTO.java new file mode 100644 index 00000000..c11e05a1 --- /dev/null +++ b/orion-ops-module-infra/orion-ops-module-infra-provider/src/main/java/com/orion/ops/module/infra/entity/dto/message/SystemMessageDTO.java @@ -0,0 +1,46 @@ +package com.orion.ops.module.infra.entity.dto.message; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import java.io.Serializable; +import java.util.Map; + +/** + * 系统消息 请求业务对象 + * + * @author Jiahang Li + * @version 1.0.8 + * @since 2024-5-11 16:29 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Schema(name = "SystemMessageDTO", description = "系统消息 请求业务对象") +public class SystemMessageDTO implements Serializable { + + private static final long serialVersionUID = 1L; + + @NotBlank + @Size(max = 64) + @Schema(description = "消息关联") + private String relKey; + + @NotNull + @Schema(description = "接收人id") + private Long receiverId; + + @Schema(description = "接收人用户名") + private String receiverUsername; + + @Schema(description = "参数") + private Map params; + +} diff --git a/orion-ops-module-infra/orion-ops-module-infra-provider/src/main/java/com/orion/ops/module/infra/enums/MessageClassifyEnum.java b/orion-ops-module-infra/orion-ops-module-infra-provider/src/main/java/com/orion/ops/module/infra/enums/MessageClassifyEnum.java new file mode 100644 index 00000000..5bb3fe2a --- /dev/null +++ b/orion-ops-module-infra/orion-ops-module-infra-provider/src/main/java/com/orion/ops/module/infra/enums/MessageClassifyEnum.java @@ -0,0 +1,24 @@ +package com.orion.ops.module.infra.enums; + +/** + * 消息分类 + * + * @author Jiahang Li + * @version 1.0.8 + * @since 2024/5/11 16:38 + */ +public enum MessageClassifyEnum { + + /** + * 通知 + */ + NOTICE, + + /** + * 待办 + */ + TODO, + + ; + +} diff --git a/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/api/impl/SystemMessageApiImpl.java b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/api/impl/SystemMessageApiImpl.java new file mode 100644 index 00000000..b839b23b --- /dev/null +++ b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/api/impl/SystemMessageApiImpl.java @@ -0,0 +1,58 @@ +package com.orion.ops.module.infra.api.impl; + +import com.orion.ops.framework.common.utils.Valid; +import com.orion.ops.module.infra.api.SystemMessageApi; +import com.orion.ops.module.infra.convert.SystemMessageProviderConvert; +import com.orion.ops.module.infra.define.SystemMessageDefine; +import com.orion.ops.module.infra.entity.dto.message.SystemMessageCreateDTO; +import com.orion.ops.module.infra.entity.dto.message.SystemMessageDTO; +import com.orion.ops.module.infra.entity.request.message.SystemMessageCreateRequest; +import com.orion.ops.module.infra.enums.MessageClassifyEnum; +import com.orion.ops.module.infra.service.SystemMessageService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; + +/** + * 系统消息 对外服务实现类 + * + * @author Jiahang Li + * @version 1.0.8 + * @since 2024-5-11 16:29 + */ +@Slf4j +@Service +public class SystemMessageApiImpl implements SystemMessageApi { + + @Resource + private SystemMessageService systemMessageService; + + @Override + public Long create(SystemMessageDefine define, SystemMessageDTO dto) { + Valid.valid(dto); + // 转换 + SystemMessageCreateRequest request = SystemMessageCreateRequest.builder() + .classify(define.getClassify().name()) + .type(define.getType()) + .title(define.getTitle()) + .content(define.formatContent(dto.getParams())) + .relKey(dto.getRelKey()) + .receiverId(dto.getReceiverId()) + .receiverUsername(dto.getReceiverUsername()) + .build(); + // 创建 + return systemMessageService.createSystemMessage(request); + } + + @Override + public Long create(MessageClassifyEnum classify, SystemMessageCreateDTO dto) { + dto.setClassify(classify.name()); + Valid.valid(dto); + // 转换 + SystemMessageCreateRequest request = SystemMessageProviderConvert.MAPPER.toRequest(dto); + // 创建 + return systemMessageService.createSystemMessage(request); + } + +} diff --git a/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/controller/SystemMessageController.http b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/controller/SystemMessageController.http new file mode 100644 index 00000000..0d0df1fa --- /dev/null +++ b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/controller/SystemMessageController.http @@ -0,0 +1,39 @@ +### 查询系统消息列表 +POST {{baseUrl}}/infra/system-message/list +Content-Type: application/json +Authorization: {{token}} + +{ + "limit": 10, + "maxId": null, + "classify": "NOTICE", + "queryCount": true, + "queryUnread": true +} + + +### 查询是否有未读消息 +GET {{baseUrl}}/infra/system-message/has-unread +Authorization: {{token}} + + +### 查询是否有未读消息 +PUT {{baseUrl}}/infra/system-message/read?id=1 +Authorization: {{token}} + + +### 更新全部系统消息为已读 +PUT {{baseUrl}}/infra/system-message/read-all?classify=NOTICE +Authorization: {{token}} + + +### 删除系统消息 +DELETE {{baseUrl}}/infra/system-message/delete?id=1 +Authorization: {{token}} + + +### 清理已读的系统消息 +DELETE {{baseUrl}}/infra/system-message/clear?classify=NOTICE +Authorization: {{token}} + +### diff --git a/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/controller/SystemMessageController.java b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/controller/SystemMessageController.java new file mode 100644 index 00000000..fca4e08f --- /dev/null +++ b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/controller/SystemMessageController.java @@ -0,0 +1,90 @@ +package com.orion.ops.module.infra.controller; + +import com.orion.ops.framework.log.core.annotation.IgnoreLog; +import com.orion.ops.framework.log.core.enums.IgnoreLogMode; +import com.orion.ops.framework.web.core.annotation.RestWrapper; +import com.orion.ops.module.infra.entity.request.message.SystemMessageQueryRequest; +import com.orion.ops.module.infra.entity.vo.SystemMessageVO; +import com.orion.ops.module.infra.service.SystemMessageService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import java.util.List; +import java.util.Map; + +/** + * 系统消息 api + * + * @author Jiahang Li + * @version 1.0.8 + * @since 2024-5-11 16:29 + */ +@Tag(name = "infra - 系统消息服务") +@Slf4j +@Validated +@RestWrapper +@RestController +@RequestMapping("/infra/system-message") +@SuppressWarnings({"ELValidationInJSP", "SpringElInspection"}) +public class SystemMessageController { + + @Resource + private SystemMessageService systemMessageService; + + @IgnoreLog(IgnoreLogMode.ALL) + @PostMapping("/list") + @Operation(summary = "查询系统消息列表") + public List getSystemMessageList(@RequestBody SystemMessageQueryRequest request) { + return systemMessageService.getSystemMessageList(request); + } + + @IgnoreLog(IgnoreLogMode.ALL) + @GetMapping("/count") + @Operation(summary = "查询系统消息数量") + @Parameter(name = "queryUnread", description = "queryUnread", required = true) + public Map getSystemMessageCount(@RequestParam("queryUnread") Boolean queryUnread) { + return systemMessageService.getSystemMessageCount(queryUnread); + } + + @IgnoreLog(IgnoreLogMode.ALL) + @GetMapping("/has-unread") + @Operation(summary = "查询是否有未读消息") + public Boolean checkHasUnreadMessage() { + return systemMessageService.checkHasUnreadMessage(); + } + + @PutMapping("/read") + @Operation(summary = "更新系统消息为已读") + @Parameter(name = "id", description = "id", required = true) + public Integer readSystemMessage(@RequestParam("id") Long id) { + return systemMessageService.readSystemMessage(id); + } + + @PutMapping("/read-all") + @Operation(summary = "更新全部系统消息为已读") + @Parameter(name = "classify", description = "classify", required = true) + public Integer readAllSystemMessage(@RequestParam("classify") String classify) { + return systemMessageService.readAllSystemMessage(classify); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除系统消息") + @Parameter(name = "id", description = "id", required = true) + public Integer deleteSystemMessage(@RequestParam("id") Long id) { + return systemMessageService.deleteSystemMessageById(id); + } + + @DeleteMapping("/clear") + @Operation(summary = "清理已读的系统消息") + @Parameter(name = "classify", description = "classify", required = true) + public Integer clearSystemMessage(@RequestParam("classify") String classify) { + return systemMessageService.clearSystemMessage(classify); + } + +} + diff --git a/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/convert/SystemMessageConvert.java b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/convert/SystemMessageConvert.java new file mode 100644 index 00000000..64b79551 --- /dev/null +++ b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/convert/SystemMessageConvert.java @@ -0,0 +1,32 @@ +package com.orion.ops.module.infra.convert; + +import com.orion.ops.module.infra.entity.domain.SystemMessageDO; +import com.orion.ops.module.infra.entity.request.message.SystemMessageCreateRequest; +import com.orion.ops.module.infra.entity.request.message.SystemMessageQueryRequest; +import com.orion.ops.module.infra.entity.vo.SystemMessageVO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +import java.util.List; + +/** + * 系统消息 内部对象转换器 + * + * @author Jiahang Li + * @version 1.0.8 + * @since 2024-5-11 16:29 + */ +@Mapper +public interface SystemMessageConvert { + + SystemMessageConvert MAPPER = Mappers.getMapper(SystemMessageConvert.class); + + SystemMessageDO to(SystemMessageCreateRequest request); + + SystemMessageDO to(SystemMessageQueryRequest request); + + SystemMessageVO to(SystemMessageDO domain); + + List to(List list); + +} diff --git a/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/convert/SystemMessageProviderConvert.java b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/convert/SystemMessageProviderConvert.java new file mode 100644 index 00000000..7dfc71f2 --- /dev/null +++ b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/convert/SystemMessageProviderConvert.java @@ -0,0 +1,22 @@ +package com.orion.ops.module.infra.convert; + +import com.orion.ops.module.infra.entity.dto.message.SystemMessageCreateDTO; +import com.orion.ops.module.infra.entity.request.message.SystemMessageCreateRequest; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * 系统消息 对外服务对象转换器 + * + * @author Jiahang Li + * @version 1.0.8 + * @since 2024-5-11 16:29 + */ +@Mapper +public interface SystemMessageProviderConvert { + + SystemMessageProviderConvert MAPPER = Mappers.getMapper(SystemMessageProviderConvert.class); + + SystemMessageCreateRequest toRequest(SystemMessageCreateDTO request); + +} diff --git a/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/dao/SystemMessageDAO.java b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/dao/SystemMessageDAO.java new file mode 100644 index 00000000..2c9b1dca --- /dev/null +++ b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/dao/SystemMessageDAO.java @@ -0,0 +1,31 @@ +package com.orion.ops.module.infra.dao; + +import com.orion.ops.framework.mybatis.core.mapper.IMapper; +import com.orion.ops.module.infra.entity.domain.SystemMessageDO; +import com.orion.ops.module.infra.entity.dto.SystemMessageCountDTO; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 系统消息 Mapper 接口 + * + * @author Jiahang Li + * @version 1.0.8 + * @since 2024-5-11 16:29 + */ +@Mapper +public interface SystemMessageDAO extends IMapper { + + /** + * 查询消息数量 + * + * @param receiverId receiverId + * @param status status + * @return count + */ + List selectSystemMessageCount(@Param("receiverId") Long receiverId, + @Param("status") Integer status); + +} diff --git a/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/entity/domain/SystemMessageDO.java b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/entity/domain/SystemMessageDO.java new file mode 100644 index 00000000..b016fffc --- /dev/null +++ b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/entity/domain/SystemMessageDO.java @@ -0,0 +1,73 @@ +package com.orion.ops.module.infra.entity.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.orion.ops.framework.mybatis.core.domain.BaseDO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; + +/** + * 系统消息 实体对象 + * + * @author Jiahang Li + * @version 1.0.8 + * @since 2024-5-11 16:29 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = true) +@TableName(value = "system_message", autoResultMap = true) +@Schema(name = "SystemMessageDO", description = "系统消息 实体对象") +public class SystemMessageDO extends BaseDO { + + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + @TableId(value = "id", type = IdType.AUTO) + private Long id; + + @Schema(description = "消息分类") + @TableField("classify") + private String classify; + + @Schema(description = "消息类型") + @TableField("type") + private String type; + + @Schema(description = "消息状态") + @TableField("status") + private Integer status; + + @Schema(description = "消息关联") + @TableField("rel_key") + private String relKey; + + @Schema(description = "标题") + @TableField("title") + private String title; + + @Schema(description = "消息内容") + @TableField("content") + private String content; + + @Schema(description = "接收人id") + @TableField("receiver_id") + private Long receiverId; + + @Schema(description = "接收人用户名") + @TableField("receiver_username") + private String receiverUsername; + + @Schema(description = "创建人") + @TableField(exist = false) + private String creator; + + @Schema(description = "修改人") + @TableField(exist = false) + private String updater; + +} diff --git a/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/entity/dto/SystemMessageCountDTO.java b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/entity/dto/SystemMessageCountDTO.java new file mode 100644 index 00000000..05980814 --- /dev/null +++ b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/entity/dto/SystemMessageCountDTO.java @@ -0,0 +1,33 @@ +package com.orion.ops.module.infra.entity.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 系统消息数量对象 + * + * @author Jiahang Li + * @version 1.0.8 + * @since 2024/5/11 18:15 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Schema(name = "SystemMessageCountDTO", description = "系统消息数量对象") +public class SystemMessageCountDTO implements Serializable { + + private static final long serialVersionUID = 1L; + + @Schema(description = "消息分类") + private String classify; + + @Schema(description = "数量") + private Integer count; + +} \ No newline at end of file diff --git a/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/entity/request/message/SystemMessageCreateRequest.java b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/entity/request/message/SystemMessageCreateRequest.java new file mode 100644 index 00000000..1dfcf24c --- /dev/null +++ b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/entity/request/message/SystemMessageCreateRequest.java @@ -0,0 +1,62 @@ +package com.orion.ops.module.infra.entity.request.message; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import java.io.Serializable; + +/** + * 系统消息 创建请求对象 + * + * @author Jiahang Li + * @version 1.0.8 + * @since 2024-5-11 16:29 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Schema(name = "SystemMessageCreateRequest", description = "系统消息 创建请求对象") +public class SystemMessageCreateRequest implements Serializable { + + private static final long serialVersionUID = 1L; + + @NotBlank + @Size(max = 10) + @Schema(description = "消息分类") + private String classify; + + @NotBlank + @Size(max = 32) + @Schema(description = "消息类型") + private String type; + + @NotBlank + @Size(max = 64) + @Schema(description = "消息关联") + private String relKey; + + @NotBlank + @Size(max = 128) + @Schema(description = "标题") + private String title; + + @NotBlank + @Schema(description = "消息内容") + private String content; + + @NotNull + @Schema(description = "接收人id") + private Long receiverId; + + @Size(max = 32) + @Schema(description = "接收人用户名") + private String receiverUsername; + +} diff --git a/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/entity/request/message/SystemMessageQueryRequest.java b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/entity/request/message/SystemMessageQueryRequest.java new file mode 100644 index 00000000..ad6e2cc1 --- /dev/null +++ b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/entity/request/message/SystemMessageQueryRequest.java @@ -0,0 +1,35 @@ +package com.orion.ops.module.infra.entity.request.message; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 系统消息 查询请求对象 + * + * @author Jiahang Li + * @version 1.0.8 + * @since 2024-5-11 16:29 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Schema(name = "SystemMessageQueryRequest", description = "系统消息 查询请求对象") +public class SystemMessageQueryRequest { + + @Schema(description = "大小") + private Integer limit; + + @Schema(description = "maxId") + private Long maxId; + + @Schema(description = "消息分类") + private String classify; + + @Schema(description = "是否查询未读消息") + private Boolean queryUnread; + +} diff --git a/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/entity/vo/SystemMessageVO.java b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/entity/vo/SystemMessageVO.java new file mode 100644 index 00000000..fdbc1b00 --- /dev/null +++ b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/entity/vo/SystemMessageVO.java @@ -0,0 +1,52 @@ +package com.orion.ops.module.infra.entity.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.Date; + +/** + * 系统消息 视图响应对象 + * + * @author Jiahang Li + * @version 1.0.8 + * @since 2024-5-11 16:29 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Schema(name = "SystemMessageVO", description = "系统消息 视图响应对象") +public class SystemMessageVO implements Serializable { + + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + private Long id; + + @Schema(description = "消息分类") + private String classify; + + @Schema(description = "消息类型") + private String type; + + @Schema(description = "消息状态") + private Integer status; + + @Schema(description = "消息关联") + private String relKey; + + @Schema(description = "标题") + private String title; + + @Schema(description = "消息内容") + private String content; + + @Schema(description = "创建时间") + private Date createTime; + +} diff --git a/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/enums/MessageStatusEnum.java b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/enums/MessageStatusEnum.java new file mode 100644 index 00000000..bdb6f10f --- /dev/null +++ b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/enums/MessageStatusEnum.java @@ -0,0 +1,31 @@ +package com.orion.ops.module.infra.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 消息状态 + * + * @author Jiahang Li + * @version 1.0.8 + * @since 2024/5/11 16:35 + */ +@Getter +@AllArgsConstructor +public enum MessageStatusEnum { + + /** + * 未读 + */ + UNREAD(0), + + /** + * 已读 + */ + READ(1), + + ; + + private final Integer status; + +} diff --git a/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/service/SystemMessageService.java b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/service/SystemMessageService.java new file mode 100644 index 00000000..6a0389d7 --- /dev/null +++ b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/service/SystemMessageService.java @@ -0,0 +1,82 @@ +package com.orion.ops.module.infra.service; + +import com.orion.ops.module.infra.entity.request.message.SystemMessageCreateRequest; +import com.orion.ops.module.infra.entity.request.message.SystemMessageQueryRequest; +import com.orion.ops.module.infra.entity.vo.SystemMessageVO; + +import java.util.List; +import java.util.Map; + +/** + * 系统消息 服务类 + * + * @author Jiahang Li + * @version 1.0.8 + * @since 2024-5-11 16:29 + */ +public interface SystemMessageService { + + /** + * 创建系统消息 + * + * @param request request + * @return id + */ + Long createSystemMessage(SystemMessageCreateRequest request); + + /** + * 查询系统消息列表 + * + * @param request request + * @return rows + */ + List getSystemMessageList(SystemMessageQueryRequest request); + + /** + * 查询系统消息数量 + * + * @param queryUnread queryUnread + * @return rows + */ + Map getSystemMessageCount(Boolean queryUnread); + + /** + * 查询是否有未读消息 + * + * @return has + */ + Boolean checkHasUnreadMessage(); + + /** + * 更新系统消息为已读 + * + * @param id id + * @return effect + */ + Integer readSystemMessage(Long id); + + /** + * 更新全部系统消息为已读 + * + * @param classify classify + * @return effect + */ + Integer readAllSystemMessage(String classify); + + /** + * 删除系统消息 + * + * @param id id + * @return effect + */ + Integer deleteSystemMessageById(Long id); + + /** + * 清理已读的系统消息 + * + * @param classify classify + * @return effect + */ + Integer clearSystemMessage(String classify); + +} diff --git a/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/service/impl/SystemMessageServiceImpl.java b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/service/impl/SystemMessageServiceImpl.java new file mode 100644 index 00000000..0fb367b4 --- /dev/null +++ b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/service/impl/SystemMessageServiceImpl.java @@ -0,0 +1,167 @@ +package com.orion.ops.module.infra.service.impl; + +import com.alibaba.fastjson.JSON; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.orion.lang.function.Functions; +import com.orion.lang.utils.Booleans; +import com.orion.ops.framework.common.constant.Const; +import com.orion.ops.framework.security.core.utils.SecurityUtils; +import com.orion.ops.module.infra.convert.SystemMessageConvert; +import com.orion.ops.module.infra.dao.SystemMessageDAO; +import com.orion.ops.module.infra.dao.SystemUserDAO; +import com.orion.ops.module.infra.entity.domain.SystemMessageDO; +import com.orion.ops.module.infra.entity.domain.SystemUserDO; +import com.orion.ops.module.infra.entity.dto.SystemMessageCountDTO; +import com.orion.ops.module.infra.entity.request.message.SystemMessageCreateRequest; +import com.orion.ops.module.infra.entity.request.message.SystemMessageQueryRequest; +import com.orion.ops.module.infra.entity.vo.SystemMessageVO; +import com.orion.ops.module.infra.enums.MessageStatusEnum; +import com.orion.ops.module.infra.service.SystemMessageService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + +/** + * 系统消息 服务实现类 + * + * @author Jiahang Li + * @version 1.0.8 + * @since 2024-5-11 16:29 + */ +@Slf4j +@Service +public class SystemMessageServiceImpl implements SystemMessageService { + + @Resource + private SystemMessageDAO systemMessageDAO; + + @Resource + private SystemUserDAO systemUserDAO; + + @Override + public Long createSystemMessage(SystemMessageCreateRequest request) { + log.info("SystemMessageService.createSystemMessage request: {}", JSON.toJSONString(request)); + // 设置接收人用户名 + if (request.getReceiverUsername() == null) { + Optional.ofNullable(request.getReceiverId()) + .map(systemUserDAO::selectById) + .map(SystemUserDO::getUsername) + .ifPresent(request::setReceiverUsername); + } + // 转换 + SystemMessageDO record = SystemMessageConvert.MAPPER.to(request); + record.setStatus(MessageStatusEnum.UNREAD.getStatus()); + // 插入 + int effect = systemMessageDAO.insert(record); + Long id = record.getId(); + log.info("SystemMessageService-createSystemMessage id: {}, effect: {}", id, effect); + return id; + } + + @Override + public List getSystemMessageList(SystemMessageQueryRequest request) { + Long userId = SecurityUtils.getLoginUserId(); + Integer status = Booleans.isTrue(request.getQueryUnread()) ? MessageStatusEnum.UNREAD.getStatus() : null; + // 查询列表 + return systemMessageDAO.of() + .createValidateWrapper() + .eq(SystemMessageDO::getReceiverId, userId) + .eq(SystemMessageDO::getClassify, request.getClassify()) + .lt(SystemMessageDO::getId, request.getMaxId()) + .eq(SystemMessageDO::getStatus, status) + .last(Const.LIMIT + Const.SPACE + request.getLimit()) + .orderByDesc(SystemMessageDO::getId) + .then() + .list(SystemMessageConvert.MAPPER::to); + } + + @Override + public Map getSystemMessageCount(Boolean queryUnread) { + Long userId = SecurityUtils.getLoginUserId(); + Integer status = queryUnread ? MessageStatusEnum.UNREAD.getStatus() : null; + // 查询数量 + List countList = systemMessageDAO.selectSystemMessageCount(userId, status); + // 返回 + return countList.stream() + .collect(Collectors.toMap(SystemMessageCountDTO::getClassify, + SystemMessageCountDTO::getCount, + Functions.right())); + } + + @Override + public Boolean checkHasUnreadMessage() { + // 查询 + return systemMessageDAO.of() + .createWrapper() + .select(SystemMessageDO::getId) + .eq(SystemMessageDO::getReceiverId, SecurityUtils.getLoginUserId()) + .eq(SystemMessageDO::getStatus, MessageStatusEnum.UNREAD.getStatus()) + .then() + .only() + .optional() + .isPresent(); + } + + @Override + public Integer readSystemMessage(Long id) { + Long userId = SecurityUtils.getLoginUserId(); + log.info("SystemMessageService-readSystemMessage id: {}, userId: {}", id, userId); + // 修改状态 + SystemMessageDO update = new SystemMessageDO(); + update.setStatus(MessageStatusEnum.READ.getStatus()); + LambdaQueryWrapper wrapper = systemMessageDAO.wrapper() + .eq(SystemMessageDO::getId, id) + .eq(SystemMessageDO::getReceiverId, userId); + int effect = systemMessageDAO.update(update, wrapper); + log.info("SystemMessageService-readSystemMessage id: {}, effect: {}", id, effect); + return effect; + } + + @Override + public Integer readAllSystemMessage(String classify) { + Long userId = SecurityUtils.getLoginUserId(); + log.info("SystemMessageService-readAllSystemMessage classify: {}, userId: {}", classify, userId); + // 修改状态 + SystemMessageDO update = new SystemMessageDO(); + update.setStatus(MessageStatusEnum.READ.getStatus()); + LambdaQueryWrapper wrapper = systemMessageDAO.wrapper() + .eq(SystemMessageDO::getReceiverId, userId) + .eq(SystemMessageDO::getClassify, classify) + .eq(SystemMessageDO::getStatus, MessageStatusEnum.UNREAD.getStatus()); + int effect = systemMessageDAO.update(update, wrapper); + log.info("SystemMessageService-readAllSystemMessage classify: {}, effect: {}", classify, effect); + return effect; + } + + @Override + public Integer deleteSystemMessageById(Long id) { + log.info("SystemMessageService-deleteSystemMessageById id: {}", id); + // 删除 + LambdaQueryWrapper wrapper = systemMessageDAO.wrapper() + .eq(SystemMessageDO::getId, id) + .eq(SystemMessageDO::getReceiverId, SecurityUtils.getLoginUserId()); + int effect = systemMessageDAO.delete(wrapper); + log.info("SystemMessageService-deleteSystemMessageById id: {}, effect: {}", id, effect); + return effect; + } + + @Override + public Integer clearSystemMessage(String classify) { + Long userId = SecurityUtils.getLoginUserId(); + log.info("SystemMessageService-clearSystemMessage classify: {}, userId: {}", classify, userId); + // 删除 + LambdaQueryWrapper wrapper = systemMessageDAO.wrapper() + .eq(SystemMessageDO::getReceiverId, userId) + .eq(SystemMessageDO::getClassify, classify) + .eq(SystemMessageDO::getStatus, MessageStatusEnum.READ.getStatus()); + int effect = systemMessageDAO.delete(wrapper); + log.info("SystemMessageService-clearSystemMessage classify: {}, effect: {}", classify, effect); + return effect; + } + +} diff --git a/orion-ops-module-infra/orion-ops-module-infra-service/src/main/resources/mapper/SystemMessageMapper.xml b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/resources/mapper/SystemMessageMapper.xml new file mode 100644 index 00000000..2c3e9098 --- /dev/null +++ b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/resources/mapper/SystemMessageMapper.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + id, classify, type, status, rel_key, title, content, receiver_id, receiver_username, create_time, update_time, deleted + + + + + + diff --git a/orion-ops-ui/.env.development b/orion-ops-ui/.env.development index 01433b12..8baf6503 100644 --- a/orion-ops-ui/.env.development +++ b/orion-ops-ui/.env.development @@ -1,4 +1,4 @@ VITE_API_BASE_URL= 'http://127.0.0.1:9200/orion/api' VITE_WS_BASE_URL= 'ws://127.0.0.1:9200/orion/keep-alive' -VITE_APP_VERSION= '1.0.7' +VITE_APP_VERSION= '1.0.8' VITE_SFTP_PREVIEW_MB= 2 diff --git a/orion-ops-ui/.env.production b/orion-ops-ui/.env.production index d324128c..58105ca7 100644 --- a/orion-ops-ui/.env.production +++ b/orion-ops-ui/.env.production @@ -1,4 +1,4 @@ VITE_API_BASE_URL= '/orion/api' VITE_WS_BASE_URL= '/orion/keep-alive' -VITE_APP_VERSION= '1.0.7' +VITE_APP_VERSION= '1.0.8' VITE_SFTP_PREVIEW_MB= 2 diff --git a/orion-ops-ui/package.json b/orion-ops-ui/package.json index b908221b..7f16e257 100644 --- a/orion-ops-ui/package.json +++ b/orion-ops-ui/package.json @@ -1,7 +1,7 @@ { "name": "orion-ops-pro-ui", "description": "Orion Ops Pro for Vue", - "version": "1.0.7", + "version": "1.0.8", "private": true, "author": "Jiahang Li", "license": "Apache 2.0", diff --git a/orion-ops-ui/src/api/exec/exec-log.ts b/orion-ops-ui/src/api/exec/exec-log.ts index 1a3f4245..bd15063b 100644 --- a/orion-ops-ui/src/api/exec/exec-log.ts +++ b/orion-ops-ui/src/api/exec/exec-log.ts @@ -54,7 +54,7 @@ export interface ExecHostLogQueryResponse extends TableData { status: string; command: string; parameter: string; - exitStatus: number; + exitCode: number; errorMessage: string; startTime: number; finishTime: number; diff --git a/orion-ops-ui/src/api/system/message.ts b/orion-ops-ui/src/api/system/message.ts index e2d60eee..c58e9ff0 100644 --- a/orion-ops-ui/src/api/system/message.ts +++ b/orion-ops-ui/src/api/system/message.ts @@ -1,38 +1,75 @@ import axios from 'axios'; -export interface MessageRecord { +/** + * 系统消息查询请求 + */ +export interface MessageQueryRequest { + limit?: number; + maxId?: number; + classify?: string; + queryUnread?: boolean; +} + +/** + * 系统消息查询响应 + */ +export interface MessageRecordResponse { id: number; + classify: string; type: string; + status: number; + relKey: string; title: string; - subTitle: string; - avatar?: string; content: string; - time: string; - status: 0 | 1; - messageType?: number; -} -export type MessageListType = MessageRecord[]; - -export function queryMessageList() { - return axios.post('/api/message/list'); + contentHtml: string; + createTime: number; } -interface MessageStatus { - ids: number[]; +/** + * 查询系统消息列表 + */ +export function getSystemMessageList(request: MessageQueryRequest) { + return axios.post>('/infra/system-message/list', request); } -export function setMessageStatus(data: MessageStatus) { - return axios.post('/api/message/read', data); +/** + * 查询系统消息数量 + */ +export function getSystemMessageCount(queryUnread: boolean) { + return axios.get>('/infra/system-message/count', { params: { queryUnread } }); } -export interface ChatRecord { - id: number; - username: string; - content: string; - time: string; - isCollect: boolean; +/** + * 查询是否有未读消息 + */ +export function checkHasUnreadMessage() { + return axios.get('/infra/system-message/has-unread'); } -export function queryChatList() { - return axios.post('/api/chat/list'); +/** + * 更新系统消息为已读 + */ +export function updateSystemMessageRead(id: number) { + return axios.put('/infra/system-message/read', undefined, { params: { id } }); +} + +/** + * 更新全部系统消息为已读 + */ +export function updateSystemMessageReadAll(classify: string) { + return axios.put('/infra/system-message/read-all', undefined, { params: { classify } }); +} + +/** + * 删除系统消息 + */ +export function deleteSystemMessage(id: number) { + return axios.delete('/infra/system-message/delete', { params: { id } }); +} + +/** + * 清理已读的系统消息 + */ +export function clearSystemMessage(classify: string) { + return axios.delete('/infra/system-message/clear', { params: { classify } }); } diff --git a/orion-ops-ui/src/components/app/navbar/index.vue b/orion-ops-ui/src/components/app/navbar/index.vue index 42fecadd..b40813dd 100644 --- a/orion-ops-ui/src/components/app/navbar/index.vue +++ b/orion-ops-ui/src/components/app/navbar/index.vue @@ -80,11 +80,11 @@ - -
  • - + +
  • +
    - + + content-class="message-popover" + position="br" + :show-arrow="false" + :popup-style="{ marginLeft: '198px' }" + :content-style="{ padding: 0, width: '428px' }" + @hide="pullHasUnreadMessage">
    diff --git a/orion-ops-ui/src/components/system/message-box/list.vue b/orion-ops-ui/src/components/system/message-box/list.vue index a661c331..1920a21d 100644 --- a/orion-ops-ui/src/components/system/message-box/list.vue +++ b/orion-ops-ui/src/components/system/message-box/list.vue @@ -1,149 +1,218 @@ + + diff --git a/orion-ops-ui/src/router/routes/modules/exec.ts b/orion-ops-ui/src/router/routes/modules/exec.ts index d62abb9a..0c55f355 100644 --- a/orion-ops-ui/src/router/routes/modules/exec.ts +++ b/orion-ops-ui/src/router/routes/modules/exec.ts @@ -1,51 +1,37 @@ import type { AppRouteRecordRaw } from '../types'; -import { DEFAULT_LAYOUT, FULL_LAYOUT } from '../base'; +import { DEFAULT_LAYOUT } from '../base'; -const EXEC: AppRouteRecordRaw[] = [ - { - name: 'execModule', - path: '/exec-module', - component: DEFAULT_LAYOUT, - children: [ - { - name: 'execCommand', - path: '/exec-command', - component: () => import('@/views/exec/exec-command/index.vue'), - }, - { - name: 'execCommandLog', - path: '/exec-log', - component: () => import('@/views/exec/exec-command-log/index.vue'), - }, - { - name: 'batchUpload', - path: '/batch-upload', - component: () => import('@/views/exec/batch-upload/index.vue'), - }, - { - name: 'uploadTask', - path: '/upload-task', - component: () => import('@/views/exec/upload-task/index.vue'), - }, - { - name: 'execTemplate', - path: '/exec-template', - component: () => import('@/views/exec/exec-template/index.vue'), - }, - ], - }, - { - name: 'execFullModule', - path: '/exec-full-module', - component: FULL_LAYOUT, - children: [ - { - name: 'execCommandLogView', - path: '/exec-log-view', - component: () => import('@/views/exec/exec-command-log-view/index.vue'), - }, - ], - } -]; +const EXEC: AppRouteRecordRaw = { + name: 'execModule', + path: '/exec-module', + component: DEFAULT_LAYOUT, + children: [ + { + name: 'execCommand', + path: '/exec-command', + component: () => import('@/views/exec/exec-command/index.vue'), + }, + { + name: 'execCommandLog', + path: '/exec-log', + component: () => import('@/views/exec/exec-command-log/index.vue'), + }, + { + name: 'batchUpload', + path: '/batch-upload', + component: () => import('@/views/exec/batch-upload/index.vue'), + }, + { + name: 'uploadTask', + path: '/upload-task', + component: () => import('@/views/exec/upload-task/index.vue'), + }, + { + name: 'execTemplate', + path: '/exec-template', + component: () => import('@/views/exec/exec-template/index.vue'), + }, + ], +}; export default EXEC; diff --git a/orion-ops-ui/src/utils/setup-mock.ts b/orion-ops-ui/src/utils/setup-mock.ts index 816ec285..feaafea9 100644 --- a/orion-ops-ui/src/utils/setup-mock.ts +++ b/orion-ops-ui/src/utils/setup-mock.ts @@ -7,12 +7,12 @@ export default ({ mock, setup }: { mock?: boolean; setup: () => void }) => { export const successResponseWrap = (data: unknown) => { return { data, - msg: '请求成功', + msg: 'success', code: 200, }; }; -export const failResponseWrap = (data: unknown, msg: string, code = 5000) => { +export const failResponseWrap = (data: unknown, msg: string, code = 500) => { return { data, msg, diff --git a/orion-ops-ui/src/views/dashboard/workplace/mock.ts b/orion-ops-ui/src/views/dashboard/workplace/mock.ts index ef436f41..e037190f 100644 --- a/orion-ops-ui/src/views/dashboard/workplace/mock.ts +++ b/orion-ops-ui/src/views/dashboard/workplace/mock.ts @@ -7,99 +7,18 @@ import setupMock, { successResponseWrap } from '@/utils/setup-mock'; const textList = [ { key: 1, - clickNumber: '346.3w+', - title: '经济日报:财政政策要精准提升…', + clickNumber: '1w+', + title: 'text...', increases: 35, }, { key: 2, - clickNumber: '324.2w+', - title: '双12遇冷,消费者厌倦了电商平…', + clickNumber: '2w+', + title: 'text...', increases: 22, }, - { - key: 3, - clickNumber: '318.9w+', - title: '致敬坚守战“疫”一线的社区工作…', - increases: 9, - }, - { - key: 4, - clickNumber: '257.9w+', - title: '普高还是职高?家长们陷入选择…', - increases: 17, - }, - { - key: 5, - clickNumber: '124.2w+', - title: '人民快评:没想到“浓眉大眼”的…', - increases: 37, - }, -]; -const imageList = [ - { - key: 1, - clickNumber: '15.3w+', - title: '杨涛接替陆慷出任外交部美大司…', - increases: 15, - }, - { - key: 2, - clickNumber: '12.2w+', - title: '图集:龙卷风袭击美国多州房屋…', - increases: 26, - }, - { - key: 3, - clickNumber: '18.9w+', - title: '52岁大姐贴钱照顾自闭症儿童八…', - increases: 9, - }, - { - key: 4, - clickNumber: '7.9w+', - title: '杭州一家三口公园宿营取暖中毒', - increases: 0, - }, - { - key: 5, - clickNumber: '5.2w+', - title: '派出所副所长威胁市民?警方调…', - increases: 4, - }, -]; -const videoList = [ - { - key: 1, - clickNumber: '367.6w+', - title: '这是今日10点的南京', - increases: 5, - }, - { - key: 2, - clickNumber: '352.2w+', - title: '立陶宛不断挑衅致经济受损民众…', - increases: 17, - }, - { - key: 3, - clickNumber: '348.9w+', - title: '韩国艺人刘在石确诊新冠', - increases: 30, - }, - { - key: 4, - clickNumber: '346.3w+', - title: '关于北京冬奥会,文在寅表态', - increases: 12, - }, - { - key: 5, - clickNumber: '271.2w+', - title: '95后现役军人荣立一等功', - increases: 2, - }, ]; + setupMock({ setup() { Mock.mock(new RegExp('/api/content-data'), () => { @@ -117,13 +36,11 @@ setupMock({ }); Mock.mock(new RegExp('/api/popular/list'), (params: GetParams) => { const { type = 'text' } = qs.parseUrl(params.url).query; - if (type === 'image') { - return successResponseWrap([...videoList]); + if (type === 'text') { + return successResponseWrap([...textList]); + } else { + return successResponseWrap([]); } - if (type === 'video') { - return successResponseWrap([...imageList]); - } - return successResponseWrap([...textList]); }); }, }); diff --git a/orion-ops-ui/src/views/exec/batch-upload/components/upload-panel.vue b/orion-ops-ui/src/views/exec/batch-upload/components/upload-panel.vue index 733a1ec0..e4c81fa9 100644 --- a/orion-ops-ui/src/views/exec/batch-upload/components/upload-panel.vue +++ b/orion-ops-ui/src/views/exec/batch-upload/components/upload-panel.vue @@ -76,7 +76,7 @@ const { loading, setLoading } = useLoading(); - const pullStatusId = ref(); + const pullIntervalId = ref(); const taskId = ref(); const task = ref({} as UploadTaskQueryResponse); const selectedHost = ref(); @@ -266,12 +266,12 @@ // 设置轮询状态 onMounted(() => { - pullStatusId.value = setInterval(pullTaskStatus, 5000); + pullIntervalId.value = setInterval(pullTaskStatus, 5000); }); // 卸载状态查询 onUnmounted(() => { - clearInterval(pullStatusId.value); + clearInterval(pullIntervalId.value); }); diff --git a/orion-ops-ui/src/views/exec/batch-upload/index.vue b/orion-ops-ui/src/views/exec/batch-upload/index.vue index ec372558..7c67d048 100644 --- a/orion-ops-ui/src/views/exec/batch-upload/index.vue +++ b/orion-ops-ui/src/views/exec/batch-upload/index.vue @@ -28,10 +28,14 @@ await dictStore.loadKeys(dictKeys); }); + // 跳转日志 onMounted(async () => { const idParam = route.query.id; + const keyParam = route.query.key; if (idParam) { await panel.value?.openLog(Number.parseInt(idParam as string)); + } else if (keyParam) { + await panel.value?.openLog(Number.parseInt(keyParam as string)); } }); diff --git a/orion-ops-ui/src/views/exec/exec-command-log-view/index.vue b/orion-ops-ui/src/views/exec/exec-command-log-view/index.vue deleted file mode 100644 index 6e49ef8b..00000000 --- a/orion-ops-ui/src/views/exec/exec-command-log-view/index.vue +++ /dev/null @@ -1,61 +0,0 @@ - - - - - - - diff --git a/orion-ops-ui/src/views/exec/exec-command-log/components/exec-command-log-table.vue b/orion-ops-ui/src/views/exec/exec-command-log/components/exec-command-log-table.vue index 85cafa0f..0ac7e2cf 100644 --- a/orion-ops-ui/src/views/exec/exec-command-log/components/exec-command-log-table.vue +++ b/orion-ops-ui/src/views/exec/exec-command-log/components/exec-command-log-table.vue @@ -236,7 +236,7 @@ const { loading, setLoading } = useLoading(); const { toOptions, getDictValue } = useDictStore(); - const intervalId = ref(); + const pullIntervalId = ref(); const tableRef = ref(); const selectedKeys = ref([]); const tableRenderData = ref([]); @@ -343,7 +343,7 @@ }; // 加载状态 - const fetchTaskStatus = async () => { + const pullExecStatus = async () => { const unCompleteIdList = tableRenderData.value .filter(s => s.status === execStatus.WAITING || s.status === execStatus.RUNNING) .map(s => s.id); @@ -374,7 +374,7 @@ host.status = s.status; host.startTime = s.startTime; host.finishTime = s.finishTime; - host.exitStatus = s.exitStatus; + host.exitCode = s.exitCode; host.errorMessage = s.errorMessage; }); }; @@ -409,12 +409,12 @@ // 加载数据 fetchTableData(); // 注册状态轮询 - intervalId.value = setInterval(fetchTaskStatus, 10000); + pullIntervalId.value = setInterval(pullExecStatus, 10000); }); onUnmounted(() => { // 卸载状态轮询 - clearInterval(intervalId.value); + clearInterval(pullIntervalId.value); }); diff --git a/orion-ops-ui/src/views/exec/exec-command-log/index.vue b/orion-ops-ui/src/views/exec/exec-command-log/index.vue index 1c305a02..6869de2e 100644 --- a/orion-ops-ui/src/views/exec/exec-command-log/index.vue +++ b/orion-ops-ui/src/views/exec/exec-command-log/index.vue @@ -69,7 +69,7 @@ if (newWindow) { // 跳转新页面 openNewRoute({ - name: 'execCommandLogView', + name: 'execCommand', query: { id } diff --git a/orion-ops-ui/src/views/exec/exec-command-log/types/host-table.columns.ts b/orion-ops-ui/src/views/exec/exec-command-log/types/host-table.columns.ts index cb18fd99..04997dc3 100644 --- a/orion-ops-ui/src/views/exec/exec-command-log/types/host-table.columns.ts +++ b/orion-ops-ui/src/views/exec/exec-command-log/types/host-table.columns.ts @@ -18,12 +18,12 @@ const columns = [ tooltip: true, }, { title: '退出码', - dataIndex: 'exitStatus', - slotName: 'exitStatus', + dataIndex: 'exitCode', + slotName: 'exitCode', align: 'left', width: 118, render: ({ record }) => { - return isNumber(record.exitStatus) ? record.exitStatus : '-'; + return isNumber(record.exitCode) ? record.exitCode : '-'; }, }, { title: '执行状态', diff --git a/orion-ops-ui/src/views/exec/exec-command/index.vue b/orion-ops-ui/src/views/exec/exec-command/index.vue index 2af0d7d3..d1329e65 100644 --- a/orion-ops-ui/src/views/exec/exec-command/index.vue +++ b/orion-ops-ui/src/views/exec/exec-command/index.vue @@ -26,10 +26,13 @@ import useVisible from '@/hooks/visible'; import { useDictStore } from '@/store'; import { dictKeys } from '@/components/exec/log/const'; + import { useRoute } from 'vue-router'; + import { getExecCommandLog } from '@/api/exec/exec-command-log'; import ExecCommandPanel from './components/exec-command-panel.vue'; import ExecLogPanel from '@/components/exec/log/panel/index.vue'; const { visible: logVisible, setVisible: setLogVisible } = useVisible(); + const route = useRoute(); const log = ref(); @@ -41,12 +44,30 @@ }); }; + // 打开日志 + const openLogWithId = async (id: number) => { + setLogVisible(true); + // 查询日志 + const { data } = await getExecCommandLog(id); + openLog(data); + }; + // 加载字典值 onMounted(async () => { const dictStore = useDictStore(); await dictStore.loadKeys(dictKeys); }); + // 跳转日志 + onMounted(async () => { + const idParam = route.query.id; + const keyParam = route.query.key; + if (idParam) { + await openLogWithId(Number.parseInt(idParam as string)); + } else if (keyParam) { + await openLogWithId(Number.parseInt(keyParam as string)); + } + });