@@ -26,7 +26,7 @@
|
||||
</a>
|
||||
</p>
|
||||
|
||||
当前版本: **1.0.7**
|
||||
当前版本: **1.0.8**
|
||||
|
||||
**github:** https://github.com/lijiahangmax/orion-ops-pro
|
||||
**gitee:** https://gitee.com/lijiahangmax/orion-ops-pro
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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} .
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
</a>
|
||||
</p>
|
||||
|
||||
当前版本: **1.0.7**
|
||||
当前版本: **1.0.8**
|
||||
|
||||
**github:** https://github.com/lijiahangmax/orion-ops-pro
|
||||
**gitee:** https://gitee.com/lijiahangmax/orion-ops-pro
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# orion-ops-pro <small>1.0.7</small>
|
||||
# orion-ops-pro <small>1.0.8</small>
|
||||
|
||||
> 一款开箱即用的运维平台。
|
||||
|
||||
|
||||
@@ -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`
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
## 功能排期
|
||||
|
||||
* 批量上传
|
||||
* 站内消息
|
||||
* 终端背景图片
|
||||
* 资产授权 UI 改版
|
||||
* RDP 远程桌面
|
||||
|
||||
@@ -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);
|
||||
```
|
||||
|
||||
11
docs/update/v2.0.0.md
Normal file
11
docs/update/v2.0.0.md
Normal file
@@ -0,0 +1,11 @@
|
||||
## v2.0.0
|
||||
|
||||
> sql 脚本 - DDL
|
||||
|
||||
```sql
|
||||
```
|
||||
|
||||
> sql 脚本 - DML
|
||||
|
||||
```sql
|
||||
```
|
||||
@@ -14,7 +14,7 @@
|
||||
<url>https://github.com/lijiahangmax/orion-ops-pro</url>
|
||||
|
||||
<properties>
|
||||
<revision>1.0.7</revision>
|
||||
<revision>1.0.8</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>
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -55,6 +55,10 @@ public interface FieldConst {
|
||||
|
||||
String COUNT = "count";
|
||||
|
||||
String DATE = "date";
|
||||
|
||||
String TIME = "time";
|
||||
|
||||
String LOCATION = "location";
|
||||
|
||||
String USER_AGENT = "userAgent";
|
||||
|
||||
@@ -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(),
|
||||
};
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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,
|
||||
"命令执行失败",
|
||||
"您在 <sb>${time}</sb> 执行的命令部分失败, 或者返回了非零的 exitCode。点击查看详情 <sb>#${id}</sb> >>>"),
|
||||
|
||||
;
|
||||
|
||||
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;
|
||||
|
||||
}
|
||||
@@ -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,
|
||||
"批量上传失败",
|
||||
"您在 <sb>${time}</sb> 提交的上传任务中, 有部分主机文件上传失败。点击查看详情 <sb>#${id}</sb> >>>"),
|
||||
|
||||
;
|
||||
|
||||
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;
|
||||
|
||||
}
|
||||
@@ -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")
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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: ")
|
||||
|
||||
@@ -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<TimeoutEndpoint> timeoutChecker;
|
||||
@@ -45,6 +56,8 @@ public class ExecTaskHandler implements IExecTaskHandler {
|
||||
@Getter
|
||||
private final List<IExecCommandHandler> 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<String, Object> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -31,6 +31,13 @@ public interface IExecCommandHandler extends Runnable, SafeCloseable {
|
||||
*/
|
||||
ExecHostStatusEnum getStatus();
|
||||
|
||||
/**
|
||||
* 获取退出码
|
||||
*
|
||||
* @return exit code
|
||||
*/
|
||||
Integer getExitCode();
|
||||
|
||||
/**
|
||||
* 获取主机 id
|
||||
*
|
||||
|
||||
@@ -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<String, Object> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<result column="status" property="status"/>
|
||||
<result column="command" property="command"/>
|
||||
<result column="parameter" property="parameter"/>
|
||||
<result column="exit_status" property="exitStatus"/>
|
||||
<result column="exit_code" property="exitCode"/>
|
||||
<result column="log_path" property="logPath"/>
|
||||
<result column="script_path" property="scriptPath"/>
|
||||
<result column="error_message" property="errorMessage"/>
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
<!-- 通用查询结果列 -->
|
||||
<sql id="Base_Column_List">
|
||||
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
|
||||
</sql>
|
||||
|
||||
</mapper>
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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<String, Object> params) {
|
||||
return Strings.format(this.getContent(), params);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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<String, Object> params;
|
||||
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
;
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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}}
|
||||
|
||||
###
|
||||
@@ -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<SystemMessageVO> getSystemMessageList(@RequestBody SystemMessageQueryRequest request) {
|
||||
return systemMessageService.getSystemMessageList(request);
|
||||
}
|
||||
|
||||
@IgnoreLog(IgnoreLogMode.ALL)
|
||||
@GetMapping("/count")
|
||||
@Operation(summary = "查询系统消息数量")
|
||||
@Parameter(name = "queryUnread", description = "queryUnread", required = true)
|
||||
public Map<String, Integer> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<SystemMessageVO> to(List<SystemMessageDO> list);
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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<SystemMessageDO> {
|
||||
|
||||
/**
|
||||
* 查询消息数量
|
||||
*
|
||||
* @param receiverId receiverId
|
||||
* @param status status
|
||||
* @return count
|
||||
*/
|
||||
List<SystemMessageCountDTO> selectSystemMessageCount(@Param("receiverId") Long receiverId,
|
||||
@Param("status") Integer status);
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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<SystemMessageVO> getSystemMessageList(SystemMessageQueryRequest request);
|
||||
|
||||
/**
|
||||
* 查询系统消息数量
|
||||
*
|
||||
* @param queryUnread queryUnread
|
||||
* @return rows
|
||||
*/
|
||||
Map<String, Integer> 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);
|
||||
|
||||
}
|
||||
@@ -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<SystemMessageVO> 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<String, Integer> getSystemMessageCount(Boolean queryUnread) {
|
||||
Long userId = SecurityUtils.getLoginUserId();
|
||||
Integer status = queryUnread ? MessageStatusEnum.UNREAD.getStatus() : null;
|
||||
// 查询数量
|
||||
List<SystemMessageCountDTO> 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<SystemMessageDO> 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<SystemMessageDO> 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<SystemMessageDO> 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<SystemMessageDO> 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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.orion.ops.module.infra.dao.SystemMessageDAO">
|
||||
|
||||
<!-- 通用查询映射结果 -->
|
||||
<resultMap id="BaseResultMap" type="com.orion.ops.module.infra.entity.domain.SystemMessageDO">
|
||||
<id column="id" property="id"/>
|
||||
<result column="classify" property="classify"/>
|
||||
<result column="type" property="type"/>
|
||||
<result column="status" property="status"/>
|
||||
<result column="rel_key" property="relKey"/>
|
||||
<result column="title" property="title"/>
|
||||
<result column="content" property="content"/>
|
||||
<result column="receiver_id" property="receiverId"/>
|
||||
<result column="receiver_username" property="receiverUsername"/>
|
||||
<result column="create_time" property="createTime"/>
|
||||
<result column="update_time" property="updateTime"/>
|
||||
<result column="deleted" property="deleted"/>
|
||||
</resultMap>
|
||||
|
||||
<resultMap id="CountResultMap" type="com.orion.ops.module.infra.entity.dto.SystemMessageCountDTO">
|
||||
<result column="classify" property="classify"/>
|
||||
<result column="count" property="count"/>
|
||||
</resultMap>
|
||||
|
||||
<!-- 通用查询结果列 -->
|
||||
<sql id="Base_Column_List">
|
||||
id, classify, type, status, rel_key, title, content, receiver_id, receiver_username, create_time, update_time, deleted
|
||||
</sql>
|
||||
|
||||
<!-- 查询消息数量 -->
|
||||
<select id="selectSystemMessageCount" resultMap="CountResultMap">
|
||||
SELECT classify, count(1) count
|
||||
FROM system_message
|
||||
WHERE deleted = 0
|
||||
AND receiver_id = #{receiverId}
|
||||
<if test="status != null">
|
||||
AND status = #{status}
|
||||
</if>
|
||||
GROUP BY classify
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<MessageListType>('/api/message/list');
|
||||
contentHtml: string;
|
||||
createTime: number;
|
||||
}
|
||||
|
||||
interface MessageStatus {
|
||||
ids: number[];
|
||||
/**
|
||||
* 查询系统消息列表
|
||||
*/
|
||||
export function getSystemMessageList(request: MessageQueryRequest) {
|
||||
return axios.post<Array<MessageRecordResponse>>('/infra/system-message/list', request);
|
||||
}
|
||||
|
||||
export function setMessageStatus(data: MessageStatus) {
|
||||
return axios.post<MessageListType>('/api/message/read', data);
|
||||
/**
|
||||
* 查询系统消息数量
|
||||
*/
|
||||
export function getSystemMessageCount(queryUnread: boolean) {
|
||||
return axios.get<Record<string, number>>('/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<boolean>('/infra/system-message/has-unread');
|
||||
}
|
||||
|
||||
export function queryChatList() {
|
||||
return axios.post<ChatRecord[]>('/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 } });
|
||||
}
|
||||
|
||||
@@ -80,11 +80,11 @@
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
</li>
|
||||
<!-- 消息列表 -->
|
||||
<li v-if="false">
|
||||
<a-tooltip content="消息通知">
|
||||
<!-- 系统消息 -->
|
||||
<li>
|
||||
<a-tooltip content="系统消息" :show-arrow="false">
|
||||
<div class="message-box-trigger">
|
||||
<a-badge :count="9" dot>
|
||||
<a-badge :count="messageCount" dot>
|
||||
<a-button class="nav-btn"
|
||||
type="outline"
|
||||
shape="circle"
|
||||
@@ -95,9 +95,12 @@
|
||||
</div>
|
||||
</a-tooltip>
|
||||
<a-popover trigger="click"
|
||||
:arrow-style="{ display: 'none' }"
|
||||
:content-style="{ padding: 0, minWidth: '400px' }"
|
||||
content-class="message-popover">
|
||||
content-class="message-popover"
|
||||
position="br"
|
||||
:show-arrow="false"
|
||||
:popup-style="{ marginLeft: '198px' }"
|
||||
:content-style="{ padding: 0, width: '428px' }"
|
||||
@hide="pullHasUnreadMessage">
|
||||
<div ref="messageRef" class="ref-btn" />
|
||||
<template #content>
|
||||
<message-box />
|
||||
@@ -202,7 +205,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, inject, ref } from 'vue';
|
||||
import { computed, inject, onMounted, onUnmounted, ref } from 'vue';
|
||||
import useLocale from '@/hooks/locale';
|
||||
import useUser from '@/hooks/user';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
@@ -214,6 +217,7 @@
|
||||
import { preferenceTipsKey } from './const';
|
||||
import { REDIRECT_ROUTE_NAME, routerToTag } from '@/router/constants';
|
||||
import { openNewRoute } from '@/router';
|
||||
import { checkHasUnreadMessage } from '@/api/system/message';
|
||||
import SystemMenuTree from '@/components/system/menu/tree/index.vue';
|
||||
import MessageBox from '@/components/system/message-box/index.vue';
|
||||
import UpdatePasswordModal from '@/components/user/user/update-password-modal/index.vue';
|
||||
@@ -258,6 +262,9 @@
|
||||
const messageRef = ref();
|
||||
// 语言
|
||||
const localeRef = ref();
|
||||
// 消息数量
|
||||
const messageCount = ref(0);
|
||||
const messageIntervalId = ref();
|
||||
|
||||
// 打开应用设置
|
||||
const openAppSetting = inject(openAppSettingKey) as () => void;
|
||||
@@ -302,6 +309,14 @@
|
||||
await logout();
|
||||
};
|
||||
|
||||
// 获取是否有未读的消息
|
||||
const pullHasUnreadMessage = () => {
|
||||
// 查询
|
||||
checkHasUnreadMessage().then(({ data }) => {
|
||||
messageCount.value = data ? 1 : 0;
|
||||
});
|
||||
};
|
||||
|
||||
// 关闭偏好提示
|
||||
const closePreferenceTip = (ack: boolean) => {
|
||||
tippedPreference.value = false;
|
||||
@@ -310,6 +325,18 @@
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
// 查询未读消息
|
||||
pullHasUnreadMessage();
|
||||
// 注册未读消息轮询
|
||||
messageIntervalId.value = setInterval(pullHasUnreadMessage, 30000);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
// 清理消息轮询
|
||||
clearInterval(messageIntervalId.value);
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
|
||||
const logViewRef = ref();
|
||||
const currentHostExecId = ref();
|
||||
const statusIntervalId = ref();
|
||||
const pullIntervalId = ref();
|
||||
const execLog = ref<ExecLogQueryResponse>();
|
||||
const appender = ref<ILogAppender>();
|
||||
|
||||
@@ -57,9 +57,9 @@
|
||||
if (record.status === execStatus.WAITING ||
|
||||
record.status === execStatus.RUNNING) {
|
||||
// 等待一秒后先查询一下状态
|
||||
setTimeout(fetchTaskStatus, 1000);
|
||||
setTimeout(pullExecStatus, 1000);
|
||||
// 注册状态轮询
|
||||
statusIntervalId.value = setInterval(fetchTaskStatus, 5000);
|
||||
pullIntervalId.value = setInterval(pullExecStatus, 5000);
|
||||
}
|
||||
// 打开日志
|
||||
nextTick(() => {
|
||||
@@ -68,7 +68,7 @@
|
||||
};
|
||||
|
||||
// 加载状态
|
||||
const fetchTaskStatus = async () => {
|
||||
const pullExecStatus = async () => {
|
||||
if (!execLog.value) {
|
||||
return;
|
||||
}
|
||||
@@ -95,7 +95,7 @@
|
||||
host.startTime = hostStatus.startTime;
|
||||
// 结束时间绑定了使用时间 如果未完成则使用当前时间
|
||||
host.finishTime = hostStatus.finishTime || Date.now();
|
||||
host.exitStatus = hostStatus.exitStatus;
|
||||
host.exitCode = hostStatus.exitCode;
|
||||
host.errorMessage = hostStatus.errorMessage;
|
||||
}
|
||||
}
|
||||
@@ -150,7 +150,7 @@
|
||||
// 清理轮询
|
||||
const clearAllInterval = () => {
|
||||
// 关闭状态轮询
|
||||
clearInterval(statusIntervalId.value);
|
||||
clearInterval(pullIntervalId.value);
|
||||
};
|
||||
|
||||
// 加载字典值
|
||||
|
||||
@@ -12,15 +12,15 @@
|
||||
<a-tag :color="getDictValue(execHostStatusKey, host.status, 'color')">
|
||||
{{ getDictValue(execHostStatusKey, host.status) }}
|
||||
</a-tag>
|
||||
<!-- exitStatus -->
|
||||
<a-tag v-if="host.exitStatus || host.exitStatus === 0"
|
||||
:color="host.exitStatus === 0 ? 'arcoblue' : 'orangered'"
|
||||
title="exit status">
|
||||
<!-- exitCode -->
|
||||
<a-tag v-if="host.exitCode || host.exitCode === 0"
|
||||
:color="host.exitCode === 0 ? 'arcoblue' : 'orangered'"
|
||||
title="exit code">
|
||||
<template #icon>
|
||||
<icon-check v-if="host.exitStatus === 0" />
|
||||
<icon-check v-if="host.exitCode === 0" />
|
||||
<icon-exclamation v-else />
|
||||
</template>
|
||||
<span class="tag-value">{{ host.exitStatus }}</span>
|
||||
<span class="tag-value">{{ host.exitCode }}</span>
|
||||
</a-tag>
|
||||
<!-- 持续时间 -->
|
||||
<a-tag v-if="host.startTime"
|
||||
|
||||
22
orion-ops-ui/src/components/system/message-box/const.ts
Normal file
22
orion-ops-ui/src/components/system/message-box/const.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
// 消息状态
|
||||
export const MessageStatus = {
|
||||
UNREAD: 0,
|
||||
READ: 1,
|
||||
};
|
||||
|
||||
export const MESSAGE_CONFIG_KEY = 'messageConfig';
|
||||
|
||||
// 查询数量
|
||||
export const messageLimit = 15;
|
||||
|
||||
// 默认消息分类 通知
|
||||
export const defaultClassify = 'NOTICE';
|
||||
|
||||
// 消息分类 字典项
|
||||
export const messageClassifyKey = 'messageClassify';
|
||||
|
||||
// 消息类型 字典项
|
||||
export const messageTypeKey = 'messageType';
|
||||
|
||||
// 加载的字典值
|
||||
export const dictKeys = [messageClassifyKey, messageTypeKey];
|
||||
@@ -1,108 +1,242 @@
|
||||
<template>
|
||||
<a-spin style="display: block" :loading="loading">
|
||||
<a-tabs v-model:activeKey="messageType" type="rounded" destroy-on-hide>
|
||||
<a-tab-pane v-for="item in tabList" :key="item.key">
|
||||
<template #title>
|
||||
<span> {{ item.title }}{{ formatUnreadLength(item.key) }} </span>
|
||||
<div class="full">
|
||||
<!-- 消息分类 -->
|
||||
<a-spin class="message-classify-container"
|
||||
:hide-icon="true"
|
||||
:loading="fetchLoading">
|
||||
<a-tabs v-model:activeKey="currentClassify"
|
||||
type="rounded"
|
||||
:hide-content="true"
|
||||
@change="loadClassifyMessage">
|
||||
<!-- 消息列表 -->
|
||||
<a-tab-pane v-for="item in toOptions(messageClassifyKey)"
|
||||
:key="item.value as string">
|
||||
<!-- 标题 -->
|
||||
<template #title>
|
||||
<span class="usn">{{ item.label }} ({{ classifyCount[item.value as any] || 0 }})</span>
|
||||
</template>
|
||||
<!-- 消息列表 -->
|
||||
</a-tab-pane>
|
||||
<!-- 右侧操作 -->
|
||||
<template #extra>
|
||||
<a-space>
|
||||
<!-- 状态 -->
|
||||
<a-switch v-model="queryUnread"
|
||||
type="round"
|
||||
checked-text="未读"
|
||||
unchecked-text="全部"
|
||||
@change="reloadAllMessage" />
|
||||
<!-- 清空 -->
|
||||
<a-button class="header-button"
|
||||
type="text"
|
||||
size="small"
|
||||
@click="clearAllMessage">
|
||||
清空
|
||||
</a-button>
|
||||
<!-- 全部已读 -->
|
||||
<a-button class="header-button"
|
||||
type="text"
|
||||
size="small"
|
||||
@click="setAllRead">
|
||||
全部已读
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
<a-result v-if="!renderList.length" status="404">
|
||||
<template #subtitle>暂无内容</template>
|
||||
</a-result>
|
||||
<list :render-list="renderList"
|
||||
:unread-count="unreadCount"
|
||||
@item-click="handleItemClick" />
|
||||
</a-tab-pane>
|
||||
<template #extra>
|
||||
<a-button type="text" @click="emptyList">
|
||||
清空
|
||||
</a-button>
|
||||
</template>
|
||||
</a-tabs>
|
||||
</a-spin>
|
||||
</a-tabs>
|
||||
</a-spin>
|
||||
<!-- 消息列表 -->
|
||||
<list :fetch-loading="fetchLoading"
|
||||
:message-loading="messageLoading"
|
||||
:has-more="hasMore"
|
||||
:message-list="messageList"
|
||||
@load="loadMessage"
|
||||
@click="clickMessage"
|
||||
@delete="deleteMessage" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'messageBox'
|
||||
};
|
||||
</script>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { MessageRecord, MessageListType } from '@/api/system/message';
|
||||
import { ref, reactive, toRefs, computed } from 'vue';
|
||||
import { queryMessageList, setMessageStatus } from '@/api/system/message';
|
||||
import type { MessageRecordResponse } from '@/api/system/message';
|
||||
import { ref, onMounted, onUnmounted } from 'vue';
|
||||
import {
|
||||
clearSystemMessage,
|
||||
deleteSystemMessage,
|
||||
getSystemMessageCount,
|
||||
getSystemMessageList,
|
||||
updateSystemMessageRead,
|
||||
updateSystemMessageReadAll
|
||||
} from '@/api/system/message';
|
||||
import useLoading from '@/hooks/loading';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { clearHtmlTag, replaceHtmlTag } from '@/utils';
|
||||
import { useDictStore } from '@/store';
|
||||
import { dictKeys, messageClassifyKey, messageTypeKey, defaultClassify, MESSAGE_CONFIG_KEY, messageLimit, MessageStatus } from './const';
|
||||
import List from './list.vue';
|
||||
|
||||
interface TabItem {
|
||||
key: string;
|
||||
title: string;
|
||||
avatar?: string;
|
||||
}
|
||||
const { loading: fetchLoading, setLoading: setFetchLoading } = useLoading();
|
||||
const { loading: messageLoading, setLoading: setMessageLoading } = useLoading();
|
||||
const { loadKeys, toOptions, getDictValue } = useDictStore();
|
||||
const router = useRouter();
|
||||
|
||||
const { loading, setLoading } = useLoading(true);
|
||||
const currentClassify = ref(defaultClassify);
|
||||
const queryUnread = ref(false);
|
||||
const classifyCount = ref<Record<string, number>>({});
|
||||
const messageList = ref<Array<MessageRecordResponse>>([]);
|
||||
const hasMore = ref(true);
|
||||
|
||||
const messageType = ref('message');
|
||||
const messageData = reactive<{
|
||||
renderList: MessageRecord[];
|
||||
messageList: MessageRecord[];
|
||||
}>({
|
||||
renderList: [],
|
||||
messageList: [],
|
||||
});
|
||||
toRefs(messageData);
|
||||
const tabList: TabItem[] = [
|
||||
{
|
||||
key: 'message',
|
||||
title: '消息',
|
||||
},
|
||||
{
|
||||
key: 'notice',
|
||||
title: '通知',
|
||||
},
|
||||
{
|
||||
key: 'todo',
|
||||
title: '待办',
|
||||
},
|
||||
];
|
||||
// 重新加载消息
|
||||
const reloadAllMessage = async () => {
|
||||
hasMore.value = true;
|
||||
messageList.value = [];
|
||||
// 查询数量
|
||||
queryMessageCount();
|
||||
// 加载列表
|
||||
await loadMessage();
|
||||
};
|
||||
|
||||
async function fetchSourceData() {
|
||||
setLoading(true);
|
||||
// 获取数量
|
||||
const queryMessageCount = async () => {
|
||||
setFetchLoading(true);
|
||||
try {
|
||||
const { data } = await queryMessageList();
|
||||
messageData.messageList = data;
|
||||
} catch (err) {
|
||||
// you can report use errorHandler or other
|
||||
const { data } = await getSystemMessageCount(queryUnread.value);
|
||||
classifyCount.value = data;
|
||||
} catch (ex) {
|
||||
} finally {
|
||||
setLoading(false);
|
||||
setFetchLoading(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
async function readMessage(data: MessageListType) {
|
||||
const ids = data.map((item) => item.id);
|
||||
await setMessageStatus({ ids });
|
||||
await fetchSourceData();
|
||||
}
|
||||
// 查询分类消息
|
||||
const loadClassifyMessage = async () => {
|
||||
hasMore.value = true;
|
||||
messageList.value = [];
|
||||
await loadMessage();
|
||||
};
|
||||
|
||||
const renderList = computed(() => {
|
||||
return messageData.messageList.filter(
|
||||
(item) => messageType.value === item.type
|
||||
);
|
||||
// 加载消息
|
||||
const loadMessage = async () => {
|
||||
hasMore.value = true;
|
||||
setFetchLoading(true);
|
||||
try {
|
||||
const maxId = messageList.value.length
|
||||
? messageList.value[messageList.value.length - 1].id
|
||||
: undefined;
|
||||
// 查询数据
|
||||
const { data } = await getSystemMessageList({
|
||||
limit: messageLimit,
|
||||
classify: currentClassify.value,
|
||||
queryUnread: queryUnread.value,
|
||||
maxId,
|
||||
});
|
||||
data.forEach(s => {
|
||||
messageList.value.push({
|
||||
...s,
|
||||
content: clearHtmlTag(s.content),
|
||||
contentHtml: replaceHtmlTag(s.content),
|
||||
});
|
||||
});
|
||||
hasMore.value = data.length === messageLimit;
|
||||
} catch (ex) {
|
||||
} finally {
|
||||
setFetchLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// 设置全部已读
|
||||
const setAllRead = async () => {
|
||||
setMessageLoading(true);
|
||||
try {
|
||||
// 设置为已读
|
||||
await updateSystemMessageReadAll(currentClassify.value);
|
||||
// 修改状态
|
||||
messageList.value.forEach(s => s.status = MessageStatus.READ);
|
||||
} catch (ex) {
|
||||
} finally {
|
||||
setMessageLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// 清理已读消息
|
||||
const clearAllMessage = async () => {
|
||||
setMessageLoading(true);
|
||||
try {
|
||||
// 清理消息
|
||||
await clearSystemMessage(currentClassify.value);
|
||||
} catch (ex) {
|
||||
} finally {
|
||||
setMessageLoading(false);
|
||||
}
|
||||
// 查询消息
|
||||
await reloadAllMessage();
|
||||
};
|
||||
|
||||
// 点击消息
|
||||
const clickMessage = (message: MessageRecordResponse) => {
|
||||
// 设置为已读
|
||||
if (message.status === MessageStatus.UNREAD) {
|
||||
updateSystemMessageRead(message.id);
|
||||
message.status = MessageStatus.READ;
|
||||
}
|
||||
const redirectComponent = getDictValue(messageTypeKey, message.type, 'redirectComponent');
|
||||
if (redirectComponent && redirectComponent !== '0') {
|
||||
// 跳转组件
|
||||
router.push({ name: redirectComponent, query: { key: message.relKey } });
|
||||
}
|
||||
};
|
||||
|
||||
// 删除消息
|
||||
const deleteMessage = async (message: MessageRecordResponse) => {
|
||||
setMessageLoading(true);
|
||||
try {
|
||||
// 删除消息
|
||||
await deleteSystemMessage(message.id);
|
||||
// 减少数量
|
||||
classifyCount.value[currentClassify.value] -= 1;
|
||||
// 移除
|
||||
const index = messageList.value.findIndex(s => s.id === message.id);
|
||||
messageList.value.splice(index, 1);
|
||||
} catch (ex) {
|
||||
} finally {
|
||||
setMessageLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// 加载字典值
|
||||
onMounted(() => {
|
||||
loadKeys(dictKeys);
|
||||
});
|
||||
const unreadCount = computed(() => {
|
||||
return renderList.value.filter((item) => !item.status).length;
|
||||
|
||||
// 获取消息
|
||||
onMounted(() => {
|
||||
// 获取配置缓存
|
||||
const item = localStorage.getItem(MESSAGE_CONFIG_KEY);
|
||||
if (item) {
|
||||
const config = JSON.parse(item) as Record<string, any>;
|
||||
if (config?.currentClassify) {
|
||||
currentClassify.value = config.currentClassify;
|
||||
}
|
||||
if (config?.queryUnread) {
|
||||
queryUnread.value = config.queryUnread;
|
||||
}
|
||||
}
|
||||
// 查询数据
|
||||
reloadAllMessage();
|
||||
});
|
||||
const getUnreadList = (type: string) => {
|
||||
const list = messageData.messageList.filter(
|
||||
(item) => item.type === type && !item.status
|
||||
);
|
||||
return list;
|
||||
};
|
||||
const formatUnreadLength = (type: string) => {
|
||||
const list = getUnreadList(type);
|
||||
return list.length ? `(${list.length})` : '';
|
||||
};
|
||||
const handleItemClick = (items: MessageListType) => {
|
||||
if (renderList.value.length) readMessage([...items]);
|
||||
};
|
||||
const emptyList = () => {
|
||||
messageData.messageList = [];
|
||||
};
|
||||
fetchSourceData();
|
||||
|
||||
// 设置缓存配置
|
||||
onUnmounted(() => {
|
||||
localStorage.setItem(MESSAGE_CONFIG_KEY, JSON.stringify({
|
||||
currentClassify: currentClassify.value,
|
||||
queryUnread: queryUnread.value,
|
||||
}));
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@@ -110,20 +244,23 @@
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
:deep(.arco-list-item-meta) {
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
:deep(.arco-tabs-nav) {
|
||||
padding: 14px 0 12px 16px;
|
||||
padding: 10px 12px;
|
||||
border-bottom: 1px solid var(--color-neutral-3);
|
||||
}
|
||||
|
||||
:deep(.arco-tabs-content) {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.arco-result-subtitle {
|
||||
color: rgb(var(--gray-6));
|
||||
.message-classify-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
|
||||
.header-button {
|
||||
padding: 0 6px;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -1,149 +1,218 @@
|
||||
<template>
|
||||
<a-list :bordered="false">
|
||||
<a-list-item v-for="item in renderList"
|
||||
:key="item.id"
|
||||
action-layout="vertical"
|
||||
:style="{
|
||||
opacity: item.status ? 0.5 : 1,
|
||||
}">
|
||||
<template #extra>
|
||||
<a-tag v-if="item.messageType === 0" color="gray">未开始</a-tag>
|
||||
<a-tag v-else-if="item.messageType === 1" color="green">已开通</a-tag>
|
||||
<a-tag v-else-if="item.messageType === 2" color="blue">进行中</a-tag>
|
||||
<a-tag v-else-if="item.messageType === 3" color="red">即将到期</a-tag>
|
||||
</template>
|
||||
<div class="item-wrap" @click="onItemClick(item)">
|
||||
<a-list-item-meta>
|
||||
<template v-if="item.avatar" #avatar>
|
||||
<a-avatar shape="circle">
|
||||
<img v-if="item.avatar" :src="item.avatar" />
|
||||
<icon-desktop v-else />
|
||||
</a-avatar>
|
||||
</template>
|
||||
<template #title>
|
||||
<a-space :size="4">
|
||||
<span>{{ item.title }}</span>
|
||||
<a-typography-text type="secondary">
|
||||
{{ item.subTitle }}
|
||||
</a-typography-text>
|
||||
</a-space>
|
||||
</template>
|
||||
<template #description>
|
||||
<div>
|
||||
<a-typography-paragraph :ellipsis="{
|
||||
rows: 1,
|
||||
}">
|
||||
{{ item.content }}
|
||||
</a-typography-paragraph>
|
||||
<a-typography-text v-if="item.type === 'message'"
|
||||
class="time-text">
|
||||
{{ item.time }}
|
||||
</a-typography-text>
|
||||
</div>
|
||||
</template>
|
||||
</a-list-item-meta>
|
||||
</div>
|
||||
</a-list-item>
|
||||
<template #footer>
|
||||
<a-space fill
|
||||
:size="0"
|
||||
:class="{ 'add-border-top': renderList.length < showMax }">
|
||||
<div class="footer-wrap">
|
||||
<a-link @click="allRead">全部已读</a-link>
|
||||
</div>
|
||||
<div class="footer-wrap">
|
||||
<a-link>查看更多</a-link>
|
||||
</div>
|
||||
</a-space>
|
||||
</template>
|
||||
<div v-if="renderList.length && renderList.length < 3"
|
||||
:style="{ height: (showMax - renderList.length) * 86 + 'px' }">
|
||||
<!-- 消息列表 -->
|
||||
<a-spin class="message-list-container" :loading="messageLoading">
|
||||
<!-- 加载中 -->
|
||||
<div v-if="!messageList.length && fetchLoading">
|
||||
<!-- 加载中 -->
|
||||
<a-skeleton class="skeleton-wrapper" :animation="true">
|
||||
<a-skeleton-line :rows="3"
|
||||
:line-height="96"
|
||||
:line-spacing="8" />
|
||||
</a-skeleton>
|
||||
</div>
|
||||
</a-list>
|
||||
<!-- 无数据 -->
|
||||
<div v-else-if="!messageList.length && !fetchLoading">
|
||||
<a-result status="404">
|
||||
<template #subtitle>暂无内容</template>
|
||||
</a-result>
|
||||
</div>
|
||||
<!-- 消息容器 -->
|
||||
<div v-else class="message-list-wrapper">
|
||||
<a-scrollbar style="overflow-y: auto; height: 100%;">
|
||||
<!-- 消息列表-->
|
||||
<div v-for="message in messageList"
|
||||
class="message-item"
|
||||
:class="[ message.status === MessageStatus.READ ? 'message-item-read' : 'message-item-unread' ]"
|
||||
@click="emits('click', message)">
|
||||
<!-- 标题 -->
|
||||
<div class="message-item-title">
|
||||
<!-- 标题 -->
|
||||
<div class="message-item-title-text text-ellipsis" :title="message.title">
|
||||
{{ message.title }}
|
||||
</div>
|
||||
<!-- tag -->
|
||||
<div class="message-item-title-status">
|
||||
<template v-if="getDictValue(messageTypeKey, message.type, 'tagVisible', false)">
|
||||
<a-tag size="small" :color="getDictValue(messageTypeKey, message.type, 'tagColor')">
|
||||
{{ getDictValue(messageTypeKey, message.type, 'tagLabel') }}
|
||||
</a-tag>
|
||||
</template>
|
||||
</div>
|
||||
<!-- 操作 -->
|
||||
<div class="message-item-title-actions">
|
||||
<!-- 删除 -->
|
||||
<a-button size="mini"
|
||||
type="text"
|
||||
status="danger"
|
||||
@click.stop="emits('delete', message)">
|
||||
删除
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 内容 -->
|
||||
<div v-html="message.contentHtml" class="message-item-content" />
|
||||
<!-- 时间 -->
|
||||
<div class="message-item-time">
|
||||
{{ dateFormat(new Date(message.createTime)) }}
|
||||
</div>
|
||||
</div>
|
||||
<!-- 加载中 -->
|
||||
<a-skeleton v-if="fetchLoading"
|
||||
class="skeleton-wrapper"
|
||||
:animation="true">
|
||||
<a-skeleton-line :rows="3"
|
||||
:line-height="96"
|
||||
:line-spacing="8" />
|
||||
</a-skeleton>
|
||||
<!-- 加载更多 -->
|
||||
<div v-if="hasMore" class="load-more-wrapper">
|
||||
<a-button size="small"
|
||||
:fetchLoading="fetchLoading"
|
||||
@click="() => emits('load')">
|
||||
加载更多
|
||||
</a-button>
|
||||
</div>
|
||||
</a-scrollbar>
|
||||
</div>
|
||||
</a-spin>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'messageBoxList'
|
||||
};
|
||||
</script>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { MessageListType, MessageRecord } from '@/api/system/message';
|
||||
import type { MessageRecordResponse } from '@/api/system/message';
|
||||
import { MessageStatus, messageTypeKey } from './const';
|
||||
import { useDictStore } from '@/store';
|
||||
import { dateFormat } from '@/utils';
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
renderList: MessageListType;
|
||||
unreadCount?: number;
|
||||
}>(), {
|
||||
unreadCount: 0,
|
||||
});
|
||||
const emits = defineEmits(['load', 'click', 'delete']);
|
||||
const props = defineProps<{
|
||||
fetchLoading: boolean;
|
||||
messageLoading: boolean;
|
||||
hasMore: boolean;
|
||||
messageList: Array<MessageRecordResponse>;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits(['itemClick']);
|
||||
const allRead = () => {
|
||||
emit('itemClick', [...props.renderList]);
|
||||
};
|
||||
const { getDictValue } = useDictStore();
|
||||
|
||||
const onItemClick = (item: MessageRecord) => {
|
||||
if (!item.status) {
|
||||
emit('itemClick', [item]);
|
||||
}
|
||||
};
|
||||
const showMax = 3;
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
:deep(.arco-list) {
|
||||
.arco-list-item {
|
||||
min-height: 86px;
|
||||
border-bottom: 1px solid rgb(var(--gray-3));
|
||||
@gap: 8px;
|
||||
@actions-width: 82px;
|
||||
|
||||
.skeleton-wrapper {
|
||||
padding: 8px 12px 0 12px;
|
||||
}
|
||||
|
||||
.message-list-container {
|
||||
width: 100%;
|
||||
height: 338px;
|
||||
display: block;
|
||||
|
||||
.message-list-wrapper {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.arco-list-item-extra {
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
.load-more-wrapper {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin: 12px 0;
|
||||
}
|
||||
}
|
||||
|
||||
.message-item {
|
||||
padding: 12px 20px;
|
||||
border-bottom: 1px solid var(--color-neutral-3);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
transition: all .2s;
|
||||
|
||||
&-title {
|
||||
height: 22px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
|
||||
&-text {
|
||||
width: calc(100% - @actions-width - @gap);
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
text-overflow: clip;
|
||||
color: var(--color-text-1);
|
||||
}
|
||||
|
||||
&-status {
|
||||
width: @actions-width;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
&-actions {
|
||||
width: @actions-width;
|
||||
display: none;
|
||||
justify-content: flex-end;
|
||||
align-items: flex-end;
|
||||
|
||||
button {
|
||||
padding: 0 6px !important;
|
||||
|
||||
&:hover {
|
||||
background: var(--color-fill-3) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.arco-list-item-meta-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.item-wrap {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.time-text {
|
||||
&-content {
|
||||
display: block;
|
||||
margin-top: 4px;
|
||||
font-size: 12px;
|
||||
color: rgb(var(--gray-6));
|
||||
color: var(--color-text-2);
|
||||
}
|
||||
|
||||
.arco-empty {
|
||||
&-time {
|
||||
height: 18px;
|
||||
margin-top: 4px;
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
color: var(--color-text-2);
|
||||
}
|
||||
}
|
||||
|
||||
.message-item:hover {
|
||||
background: var(--color-fill-1);
|
||||
|
||||
.message-item-title-status {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.arco-list-footer {
|
||||
padding: 0;
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
border-top: none;
|
||||
|
||||
.arco-space-item {
|
||||
width: 100%;
|
||||
border-right: 1px solid rgb(var(--gray-3));
|
||||
|
||||
&:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
}
|
||||
|
||||
.add-border-top {
|
||||
border-top: 1px solid rgb(var(--gray-3));
|
||||
}
|
||||
}
|
||||
|
||||
.footer-wrap {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.arco-typography {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.add-border {
|
||||
border-top: 1px solid rgb(var(--gray-3));
|
||||
.message-item-title-actions {
|
||||
display: flex;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.message-item-read {
|
||||
.message-item-title-text, .message-item-title-status, .message-item-content, .message-item-time {
|
||||
opacity: .65;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.arco-scrollbar) {
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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]);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@@ -76,7 +76,7 @@
|
||||
|
||||
const { loading, setLoading } = useLoading();
|
||||
|
||||
const pullStatusId = ref();
|
||||
const pullIntervalId = ref();
|
||||
const taskId = ref();
|
||||
const task = ref<UploadTaskQueryResponse>({} 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);
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<div class="wrapper">
|
||||
<exec-log-panel ref="log"
|
||||
type="BATCH"
|
||||
:visible-back="false" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'execCommandLogView'
|
||||
};
|
||||
</script>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref, nextTick } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { getExecCommandLog } from '@/api/exec/exec-command-log';
|
||||
import ExecLogPanel from '@/components/exec/log/panel/index.vue';
|
||||
|
||||
const route = useRoute();
|
||||
const log = ref();
|
||||
|
||||
// 初始化
|
||||
const init = async (id: number) => {
|
||||
// 获取执行日志
|
||||
const { data } = await getExecCommandLog(id);
|
||||
// 打开日志
|
||||
await nextTick(() => {
|
||||
setTimeout(() => {
|
||||
log.value.open(data);
|
||||
}, 50);
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
const idParam = route.query.id;
|
||||
if (idParam) {
|
||||
init(Number.parseInt(idParam as string));
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
padding: 16px;
|
||||
background: var(--color-fill-2);
|
||||
|
||||
.wrapper {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -236,7 +236,7 @@
|
||||
const { loading, setLoading } = useLoading();
|
||||
const { toOptions, getDictValue } = useDictStore();
|
||||
|
||||
const intervalId = ref();
|
||||
const pullIntervalId = ref();
|
||||
const tableRef = ref();
|
||||
const selectedKeys = ref<number[]>([]);
|
||||
const tableRenderData = ref<ExecLogQueryResponse[]>([]);
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
if (newWindow) {
|
||||
// 跳转新页面
|
||||
openNewRoute({
|
||||
name: 'execCommandLogView',
|
||||
name: 'execCommand',
|
||||
query: {
|
||||
id
|
||||
}
|
||||
|
||||
@@ -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: '执行状态',
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
||||
@@ -199,7 +199,7 @@
|
||||
const { loading, setLoading } = useLoading();
|
||||
const { toOptions, getDictValue } = useDictStore();
|
||||
|
||||
const intervalId = ref();
|
||||
const pullIntervalId = ref();
|
||||
const selectedKeys = ref<number[]>([]);
|
||||
const tableRenderData = ref<UploadTaskQueryResponse[]>([]);
|
||||
const formModel = reactive<UploadTaskQueryRequest>({
|
||||
@@ -264,7 +264,7 @@
|
||||
};
|
||||
|
||||
// 加载状态
|
||||
const fetchTaskStatus = async () => {
|
||||
const pullTaskStatus = async () => {
|
||||
const unCompleteIdList = tableRenderData.value
|
||||
.filter(s => s.status === UploadTaskStatus.WAITING || s.status === UploadTaskStatus.UPLOADING)
|
||||
.map(s => s.id);
|
||||
@@ -311,12 +311,12 @@
|
||||
// 加载数据
|
||||
fetchTableData();
|
||||
// 注册状态轮询
|
||||
intervalId.value = setInterval(fetchTaskStatus, 10000);
|
||||
pullIntervalId.value = setInterval(pullTaskStatus, 10000);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
// 卸载状态轮询
|
||||
clearInterval(intervalId.value);
|
||||
clearInterval(pullIntervalId.value);
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
@@ -14,7 +14,7 @@ export default class SftpTransferManager implements ISftpTransferManager {
|
||||
|
||||
private run: boolean;
|
||||
|
||||
private progressId?: number;
|
||||
private progressIntervalId?: number;
|
||||
|
||||
private currentItem?: SftpTransferItem;
|
||||
|
||||
@@ -136,7 +136,7 @@ export default class SftpTransferManager implements ISftpTransferManager {
|
||||
// 处理消息
|
||||
this.client.onmessage = this.resolveMessage.bind(this);
|
||||
// 计算传输进度
|
||||
this.progressId = setInterval(this.calcProgress.bind(this), 500);
|
||||
this.progressIntervalId = setInterval(this.calcProgress.bind(this), 500);
|
||||
// 打开后自动传输下一个任务
|
||||
this.transferNextItem();
|
||||
}
|
||||
@@ -275,7 +275,7 @@ export default class SftpTransferManager implements ISftpTransferManager {
|
||||
// 重置 run
|
||||
this.run = false;
|
||||
// 关闭传输进度
|
||||
clearInterval(this.progressId);
|
||||
clearInterval(this.progressIntervalId);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ export default class TerminalSessionManager implements ITerminalSessionManager {
|
||||
|
||||
private sessions: Record<string, ITerminalSession>;
|
||||
|
||||
private keepAliveTask?: any;
|
||||
private keepAliveTaskId?: any;
|
||||
|
||||
private readonly dispatchResizeFn: () => {};
|
||||
|
||||
@@ -136,7 +136,7 @@ export default class TerminalSessionManager implements ITerminalSessionManager {
|
||||
// 注册 resize 事件
|
||||
addEventListen(window, 'resize', this.dispatchResizeFn);
|
||||
// 注册 ping 事件
|
||||
this.keepAliveTask = setInterval(() => {
|
||||
this.keepAliveTaskId = setInterval(() => {
|
||||
this.channel.send(InputProtocol.PING, {});
|
||||
}, 15000);
|
||||
}
|
||||
@@ -158,10 +158,7 @@ export default class TerminalSessionManager implements ITerminalSessionManager {
|
||||
// 关闭 channel
|
||||
this.channel.close();
|
||||
// 清除 ping 事件
|
||||
if (this.keepAliveTask) {
|
||||
clearInterval(this.keepAliveTask);
|
||||
this.keepAliveTask = undefined;
|
||||
}
|
||||
clearInterval(this.keepAliveTaskId);
|
||||
// 移除 resize 事件
|
||||
removeEventListen(window, 'resize', this.dispatchResizeFn);
|
||||
} catch (e) {
|
||||
|
||||
@@ -217,7 +217,7 @@
|
||||
const { loading, setLoading } = useLoading();
|
||||
const { toOptions, getDictValue } = useDictStore();
|
||||
|
||||
const intervalId = ref();
|
||||
const pullIntervalId = ref();
|
||||
const tableRef = ref();
|
||||
const selectedKeys = ref<number[]>([]);
|
||||
const tableRenderData = ref<ExecLogQueryResponse[]>([]);
|
||||
@@ -308,7 +308,7 @@
|
||||
};
|
||||
|
||||
// 加载状态
|
||||
const fetchTaskStatus = async () => {
|
||||
const pullJobStatus = async () => {
|
||||
const unCompleteIdList = tableRenderData.value
|
||||
.filter(s => s.status === execStatus.WAITING || s.status === execStatus.RUNNING)
|
||||
.map(s => s.id);
|
||||
@@ -339,7 +339,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;
|
||||
});
|
||||
};
|
||||
@@ -374,12 +374,12 @@
|
||||
// 加载数据
|
||||
fetchTableData();
|
||||
// 注册状态轮询
|
||||
intervalId.value = setInterval(fetchTaskStatus, 10000);
|
||||
pullIntervalId.value = setInterval(pullJobStatus, 10000);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
// 卸载状态轮询
|
||||
clearInterval(intervalId.value);
|
||||
clearInterval(pullIntervalId.value);
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
2
pom.xml
2
pom.xml
@@ -22,7 +22,7 @@
|
||||
</modules>
|
||||
|
||||
<properties>
|
||||
<revision>1.0.7</revision>
|
||||
<revision>1.0.8</revision>
|
||||
<maven.compiler.source>8</maven.compiler.source>
|
||||
<maven.compiler.target>8</maven.compiler.target>
|
||||
<maven.surefire.plugin.version>3.0.0-M5</maven.surefire.plugin.version>
|
||||
|
||||
@@ -211,7 +211,7 @@ CREATE TABLE `exec_host_log`
|
||||
`status` char(12) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '执行状态',
|
||||
`command` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '执行命令',
|
||||
`parameter` json NULL COMMENT '执行参数',
|
||||
`exit_status` int(0) NULL DEFAULT NULL COMMENT '退出码',
|
||||
`exit_code` int(0) NULL DEFAULT NULL COMMENT '退出码',
|
||||
`log_path` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '日志路径',
|
||||
`script_path` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '脚本路径',
|
||||
`error_message` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '错误信息',
|
||||
@@ -644,6 +644,32 @@ CREATE TABLE `system_menu`
|
||||
COLLATE = utf8mb4_unicode_ci COMMENT = '菜单表'
|
||||
ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for system_message
|
||||
-- ----------------------------
|
||||
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;
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for system_role
|
||||
-- ----------------------------
|
||||
|
||||
@@ -90,7 +90,7 @@ INSERT INTO `system_menu` VALUES (157, 122, '清空操作日志', 'infra:operato
|
||||
INSERT INTO `system_menu` VALUES (158, 152, '文件操作日志', NULL, 2, 30, 1, 1, 1, 0, 'IconFile', NULL, 'sftpLog', '2024-03-05 15:30:13', '2024-05-07 11:11:24', '1', '1', 0);
|
||||
INSERT INTO `system_menu` VALUES (159, 158, '查询文件操作日志', 'asset:host-sftp-log:management:query', 3, 10, 1, 1, 1, 0, NULL, NULL, NULL, '2024-03-05 15:31:02', '2024-04-12 14:49:18', '1', '1', 0);
|
||||
INSERT INTO `system_menu` VALUES (160, 158, '删除文件操作日志', 'asset:host-sftp-log:management:delete', 3, 20, 1, 1, 1, 0, NULL, NULL, NULL, '2024-03-05 15:31:17', '2024-04-12 14:49:21', '1', '1', 0);
|
||||
INSERT INTO `system_menu` VALUES (161, 176, '执行模板', NULL, 2, 60, 1, 1, 1, 0, 'IconBookmark', NULL, 'execTemplate', '2024-03-07 18:32:41', '2024-04-28 17:14:30', '1', '1', 0);
|
||||
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 (162, 161, '查询执行模板', 'asset:exec-template:query', 3, 10, 1, 1, 1, 0, NULL, NULL, NULL, '2024-03-07 18:32:41', '2024-03-07 18:32:41', '1', '1', 0);
|
||||
INSERT INTO `system_menu` VALUES (163, 161, '创建执行模板', 'asset:exec-template:create', 3, 20, 1, 1, 1, 0, NULL, NULL, NULL, '2024-03-07 18:32:41', '2024-03-07 18:32:41', '1', '1', 0);
|
||||
INSERT INTO `system_menu` VALUES (164, 161, '修改执行模板', 'asset:exec-template:update', 3, 30, 1, 1, 1, 0, NULL, NULL, NULL, '2024-03-07 18:32:41', '2024-03-07 18:32:41', '1', '1', 0);
|
||||
@@ -102,7 +102,6 @@ INSERT INTO `system_menu` VALUES (171, 167, '清理执行日志', 'asset:exec-co
|
||||
INSERT INTO `system_menu` VALUES (172, 176, '命令执行', NULL, 2, 10, 1, 1, 1, 0, 'icon-thunderbolt', NULL, 'execCommand', '2024-03-13 15:08:23', '2024-05-08 21:58:24', '1', '1', 0);
|
||||
INSERT INTO `system_menu` VALUES (173, 172, '执行命令', 'asset:exec-command:exec', 3, 10, 1, 1, 1, 0, NULL, NULL, NULL, '2024-03-13 15:08:23', '2024-04-10 22:04:32', '1', '1', 0);
|
||||
INSERT INTO `system_menu` VALUES (174, 167, '中断批量执行', 'asset:exec-command-log:interrupt', 3, 40, 1, 1, 1, 0, NULL, NULL, NULL, '2024-03-13 15:25:36', '2024-04-12 12:48:36', '1', '1', 0);
|
||||
INSERT INTO `system_menu` VALUES (175, 176, '批量执行日志新页面', NULL, 2, 30, 0, 1, 0, 1, '', NULL, 'execCommandLogView', '2024-03-21 14:03:10', '2024-04-11 23:45:08', '1', '1', 0);
|
||||
INSERT INTO `system_menu` VALUES (176, 0, '批量执行', NULL, 1, 420, 1, 1, 1, 0, 'icon-relation', NULL, 'execModule', '2024-04-10 16:13:27', '2024-04-28 15:30:31', '1', '1', 0);
|
||||
INSERT INTO `system_menu` VALUES (177, 193, '任务列表', NULL, 2, 10, 1, 1, 1, 0, 'IconUnorderedList', NULL, 'execJob', '2024-04-10 16:13:27', '2024-04-28 15:36:10', '1', '1', 0);
|
||||
INSERT INTO `system_menu` VALUES (178, 177, '查询计划任务', 'asset:exec-job:query', 3, 10, 1, 1, 1, 0, NULL, NULL, NULL, '2024-04-10 16:13:27', '2024-04-10 16:13:27', '1', '1', 0);
|
||||
@@ -123,8 +122,8 @@ INSERT INTO `system_menu` VALUES (193, 0, '计划任务', NULL, 1, 430, 1, 1, 1,
|
||||
INSERT INTO `system_menu` VALUES (194, 152, '在线会话', NULL, 2, 20, 1, 1, 1, 0, 'IconUserGroup', NULL, 'connectSession', '2024-05-07 11:12:17', '2024-05-07 11:12:35', '1', '1', 0);
|
||||
INSERT INTO `system_menu` VALUES (195, 194, '查询在线会话', 'asset:host-connect-session:management:query', 3, 10, 1, 1, 1, 0, NULL, NULL, NULL, '2024-05-07 11:13:16', '2024-05-07 11:13:16', '1', '1', 0);
|
||||
INSERT INTO `system_menu` VALUES (196, 194, '强制断开连接', 'asset:host-connect-session:management:force-offline', 3, 20, 1, 1, 1, 0, NULL, NULL, NULL, '2024-05-07 11:13:37', '2024-05-07 11:13:37', '1', '1', 0);
|
||||
INSERT INTO `system_menu` VALUES (197, 176, '批量上传', NULL, 2, 40, 1, 1, 1, 0, 'IconUpload', NULL, 'batchUpload', '2024-05-08 22:12:23', '2024-05-08 22:12:23', '1', '1', 0);
|
||||
INSERT INTO `system_menu` VALUES (198, 176, '上传任务', NULL, 2, 50, 1, 1, 1, 0, 'IconCloud', NULL, 'uploadTask', '2024-05-08 22:16:05', '2024-05-10 23:09:58', '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);
|
||||
INSERT INTO `system_menu` VALUES (199, 197, '上传文件', 'asset:upload-task:upload', 3, 10, 1, 1, 1, 0, NULL, NULL, NULL, '2024-05-08 22:19:35', '2024-05-08 22:19:35', '1', '1', 0);
|
||||
INSERT INTO `system_menu` VALUES (200, 198, '查询上传日志', 'asset:upload-task:query', 3, 10, 1, 1, 1, 0, NULL, NULL, NULL, '2024-05-08 22:20:01', '2024-05-08 22:20:01', '1', '1', 0);
|
||||
INSERT INTO `system_menu` VALUES (201, 198, '删除上传日志', 'asset:upload-task:delete', 3, 20, 1, 1, 1, 0, NULL, NULL, NULL, '2024-05-08 22:20:26', '2024-05-08 22:20:26', '1', '1', 0);
|
||||
@@ -165,6 +164,8 @@ INSERT INTO `dict_key` VALUES (39, 'pathBookmarkType', 'STRING', '[]', '路径
|
||||
INSERT INTO `dict_key` VALUES (40, 'sftpTransferStatus', 'STRING', '[{\"name\": \"status\", \"type\": \"STRING\"}, {\"name\": \"color\", \"type\": \"COLOR\"}, {\"name\": \"icon\", \"type\": \"STRING\"}]', 'SFTP 传输状态', '2024-05-06 11:54:49', '2024-05-06 11:54:49', '1', '1', 0);
|
||||
INSERT INTO `dict_key` VALUES (41, 'uploadTaskStatus', 'STRING', '[{\"name\": \"color\", \"type\": \"COLOR\"}]', '上传任务状态', '2024-05-07 22:18:48', '2024-05-08 22:06:23', '1', '1', 0);
|
||||
INSERT INTO `dict_key` VALUES (42, 'uploadTaskFileStatus', 'STRING', '[{\"name\": \"status\", \"type\": \"STRING\"}]', '上传任务文件状态', '2024-05-08 10:30:29', '2024-05-10 17:34:13', '1', '1', 0);
|
||||
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);
|
||||
|
||||
-- 字典值
|
||||
INSERT INTO `dict_value` VALUES (3, 4, 'systemMenuType', '1', '父菜单', '{}', 10, '2023-10-26 15:58:59', '2023-10-26 15:58:59', '1', '1', 0);
|
||||
@@ -405,3 +406,7 @@ INSERT INTO `dict_value` VALUES (291, 2, 'operatorLogType', 'upload-task:cancel'
|
||||
INSERT INTO `dict_value` VALUES (292, 2, 'operatorLogType', 'upload-task:delete', '删除上传记录', '{}', 30, '2024-05-08 22:23:44', '2024-05-08 22:23:44', '1', '1', 0);
|
||||
INSERT INTO `dict_value` VALUES (293, 2, 'operatorLogType', 'upload-task:clear', '清理上传记录', '{}', 40, '2024-05-08 22:23:59', '2024-05-08 22:23:59', '1', '1', 0);
|
||||
INSERT INTO `dict_value` VALUES (294, 41, 'uploadTaskStatus', 'FAILED', '已失败', '{\"color\": \"red\"}', 40, '2024-05-10 11:29:17', '2024-05-10 11:29:17', '1', '1', 0);
|
||||
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);
|
||||
|
||||
Reference in New Issue
Block a user