mirror of
https://gitee.com/dromara/mayfly-go
synced 2025-11-02 23:40:24 +08:00
refactor: 消息模块重构,infra包路径简写等
This commit is contained in:
@@ -7,4 +7,8 @@ VITE_OPEN = false
|
|||||||
# public path 配置线上环境路径(打包)
|
# public path 配置线上环境路径(打包)
|
||||||
VITE_PUBLIC_PATH = ''
|
VITE_PUBLIC_PATH = ''
|
||||||
|
|
||||||
VITE_EDITOR=idea
|
VITE_EDITOR=idea
|
||||||
|
|
||||||
|
# 路由模式
|
||||||
|
# Optional: hash | history
|
||||||
|
VITE_ROUTER_MODE = hash
|
||||||
@@ -4,8 +4,4 @@ ENV = 'development'
|
|||||||
VITE_OPEN = true
|
VITE_OPEN = true
|
||||||
|
|
||||||
# 本地环境接口地址
|
# 本地环境接口地址
|
||||||
VITE_API_URL = '/api'
|
VITE_API_URL = '/api'
|
||||||
|
|
||||||
# 路由模式
|
|
||||||
# Optional: hash | history
|
|
||||||
VITE_ROUTER_MODE = hash
|
|
||||||
@@ -3,7 +3,3 @@ ENV = 'production'
|
|||||||
|
|
||||||
# 线上环境接口地址
|
# 线上环境接口地址
|
||||||
VITE_API_URL = '/api'
|
VITE_API_URL = '/api'
|
||||||
|
|
||||||
# 路由模式
|
|
||||||
# Optional: hash | history
|
|
||||||
VITE_ROUTER_MODE = hash
|
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
"crypto-js": "^4.2.0",
|
"crypto-js": "^4.2.0",
|
||||||
"dayjs": "^1.11.13",
|
"dayjs": "^1.11.13",
|
||||||
"echarts": "^5.6.0",
|
"echarts": "^5.6.0",
|
||||||
"element-plus": "^2.10.3",
|
"element-plus": "^2.10.4",
|
||||||
"js-base64": "^3.7.7",
|
"js-base64": "^3.7.7",
|
||||||
"jsencrypt": "^3.3.2",
|
"jsencrypt": "^3.3.2",
|
||||||
"mitt": "^3.0.1",
|
"mitt": "^3.0.1",
|
||||||
@@ -39,8 +39,8 @@
|
|||||||
"sql-formatter": "^15.6.5",
|
"sql-formatter": "^15.6.5",
|
||||||
"trzsz": "^1.1.5",
|
"trzsz": "^1.1.5",
|
||||||
"uuid": "^9.0.1",
|
"uuid": "^9.0.1",
|
||||||
"vue": "^3.5.17",
|
"vue": "^v3.6.0-alpha.2",
|
||||||
"vue-i18n": "^11.1.9",
|
"vue-i18n": "^11.1.11",
|
||||||
"vue-router": "^4.5.1",
|
"vue-router": "^4.5.1",
|
||||||
"vuedraggable": "^4.1.0"
|
"vuedraggable": "^4.1.0"
|
||||||
},
|
},
|
||||||
@@ -56,7 +56,6 @@
|
|||||||
"@vue/compiler-sfc": "^3.5.17",
|
"@vue/compiler-sfc": "^3.5.17",
|
||||||
"autoprefixer": "^10.4.21",
|
"autoprefixer": "^10.4.21",
|
||||||
"code-inspector-plugin": "^0.20.12",
|
"code-inspector-plugin": "^0.20.12",
|
||||||
"dotenv": "^16.5.0",
|
|
||||||
"eslint": "^9.29.0",
|
"eslint": "^9.29.0",
|
||||||
"eslint-plugin-vue": "^10.2.0",
|
"eslint-plugin-vue": "^10.2.0",
|
||||||
"postcss": "^8.5.6",
|
"postcss": "^8.5.6",
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
class SocketBuilder {
|
|
||||||
websocket: WebSocket;
|
|
||||||
|
|
||||||
constructor(url: string) {
|
|
||||||
if (typeof WebSocket === 'undefined') {
|
|
||||||
throw new Error('不支持websocket');
|
|
||||||
}
|
|
||||||
if (!url) {
|
|
||||||
throw new Error('websocket url不能为空');
|
|
||||||
}
|
|
||||||
this.websocket = new WebSocket(url);
|
|
||||||
}
|
|
||||||
|
|
||||||
static builder(url: string) {
|
|
||||||
return new SocketBuilder(url);
|
|
||||||
}
|
|
||||||
|
|
||||||
open(onopen: any) {
|
|
||||||
this.websocket.onopen = onopen;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
error(onerror: any) {
|
|
||||||
this.websocket.onerror = onerror;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
message(onmessage: any) {
|
|
||||||
this.websocket.onmessage = onmessage;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
close(onclose: any) {
|
|
||||||
this.websocket.onclose = onclose;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
build() {
|
|
||||||
return this.websocket;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default SocketBuilder;
|
|
||||||
@@ -42,3 +42,27 @@ export const TagResourceTypePath = {
|
|||||||
Db: `${TagResourceTypeEnum.DbInstance.value}/${TagResourceTypeEnum.AuthCert.value}/${TagResourceTypeEnum.Db.value}`,
|
Db: `${TagResourceTypeEnum.DbInstance.value}/${TagResourceTypeEnum.AuthCert.value}/${TagResourceTypeEnum.Db.value}`,
|
||||||
Es: `${TagResourceTypeEnum.EsInstance.value}/${TagResourceTypeEnum.AuthCert.value}`,
|
Es: `${TagResourceTypeEnum.EsInstance.value}/${TagResourceTypeEnum.AuthCert.value}`,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 消息子类型
|
||||||
|
export const MsgSubtypeEnum = {
|
||||||
|
UserLogin: EnumValue.of('user.login', 'login.login').setExtra({
|
||||||
|
notifyType: 'primary',
|
||||||
|
}),
|
||||||
|
|
||||||
|
MachineFileUploadSuccess: EnumValue.of('machine.file.upload.success', 'machine.fileUploadSuccess').setExtra({
|
||||||
|
notifyType: 'success',
|
||||||
|
}),
|
||||||
|
MachineFileUploadFail: EnumValue.of('machine.file.upload.fail', 'machine.fileUploadFail').setExtra({
|
||||||
|
notifyType: 'error',
|
||||||
|
}),
|
||||||
|
|
||||||
|
DbDumpFail: EnumValue.of('db.dump.fail', 'db.dbDumpFail').setExtra({
|
||||||
|
notifyType: 'error',
|
||||||
|
}),
|
||||||
|
SqlScriptRunSuccess: EnumValue.of('db.sqlscript.run.success', 'db.sqlScriptRunSuccess').setExtra({
|
||||||
|
notifyType: 'success',
|
||||||
|
}),
|
||||||
|
SqlScriptRunFail: EnumValue.of('db.sqlscript.run.fail', 'db.sqlScriptRunFail').setExtra({
|
||||||
|
notifyType: 'error',
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import CryptoJS from 'crypto-js';
|
import CryptoJS from 'crypto-js';
|
||||||
import { getToken } from '@/common/utils/storage';
|
import { getToken } from '@/common/utils/storage';
|
||||||
|
import openApi from './openApi';
|
||||||
|
import JSEncrypt from 'jsencrypt';
|
||||||
|
import { notBlank } from './assert';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AES 加密数据
|
* AES 加密数据
|
||||||
@@ -36,3 +39,36 @@ export function AesDecrypt(word: string, key?: string): string {
|
|||||||
|
|
||||||
return decrypted.toString(CryptoJS.enc.Base64);
|
return decrypted.toString(CryptoJS.enc.Base64);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var encryptor: any = null;
|
||||||
|
|
||||||
|
export async function getRsaPublicKey() {
|
||||||
|
let publicKey = sessionStorage.getItem('RsaPublicKey');
|
||||||
|
if (publicKey) {
|
||||||
|
return publicKey;
|
||||||
|
}
|
||||||
|
publicKey = (await openApi.getPublicKey()) as string;
|
||||||
|
sessionStorage.setItem('RsaPublicKey', publicKey);
|
||||||
|
return publicKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 公钥加密指定值
|
||||||
|
*
|
||||||
|
* @param value value
|
||||||
|
* @returns 加密后的值
|
||||||
|
*/
|
||||||
|
export async function RsaEncrypt(value: any) {
|
||||||
|
// 不存在则返回空值
|
||||||
|
if (!value) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
if (encryptor != null && sessionStorage.getItem('RsaPublicKey') != null) {
|
||||||
|
return encryptor.encrypt(value);
|
||||||
|
}
|
||||||
|
encryptor = new JSEncrypt();
|
||||||
|
const publicKey = (await getRsaPublicKey()) as string;
|
||||||
|
notBlank(publicKey, '获取公钥失败');
|
||||||
|
encryptor.setPublicKey(publicKey); //设置公钥
|
||||||
|
return encryptor.encrypt(value);
|
||||||
|
}
|
||||||
|
|||||||
@@ -204,6 +204,24 @@ function getApiUrl(url: string) {
|
|||||||
return baseUrl + url + '?' + joinClientParams();
|
return baseUrl + url + '?' + joinClientParams();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建 websocket
|
||||||
|
*/
|
||||||
|
export const createWebSocket = (url: string): Promise<WebSocket> => {
|
||||||
|
return new Promise<WebSocket>((resolve, reject) => {
|
||||||
|
const clientParam = (url.includes('?') ? '&' : '?') + joinClientParams();
|
||||||
|
const socket = new WebSocket(`${config.baseWsUrl}${url}${clientParam}`);
|
||||||
|
|
||||||
|
socket.onopen = () => {
|
||||||
|
resolve(socket);
|
||||||
|
};
|
||||||
|
|
||||||
|
socket.onerror = (e) => {
|
||||||
|
reject(e);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
// 组装客户端参数,包括 token 和 clientId
|
// 组装客户端参数,包括 token 和 clientId
|
||||||
export function joinClientParams(): string {
|
export function joinClientParams(): string {
|
||||||
return `token=${getToken()}&clientId=${getClientId()}`;
|
return `token=${getToken()}&clientId=${getClientId()}`;
|
||||||
|
|||||||
@@ -1,36 +0,0 @@
|
|||||||
import openApi from './openApi';
|
|
||||||
import JSEncrypt from 'jsencrypt';
|
|
||||||
import { notBlank } from './assert';
|
|
||||||
|
|
||||||
var encryptor: any = null;
|
|
||||||
|
|
||||||
export async function getRsaPublicKey() {
|
|
||||||
let publicKey = sessionStorage.getItem('RsaPublicKey');
|
|
||||||
if (publicKey) {
|
|
||||||
return publicKey;
|
|
||||||
}
|
|
||||||
publicKey = (await openApi.getPublicKey()) as string;
|
|
||||||
sessionStorage.setItem('RsaPublicKey', publicKey);
|
|
||||||
return publicKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 公钥加密指定值
|
|
||||||
*
|
|
||||||
* @param value value
|
|
||||||
* @returns 加密后的值
|
|
||||||
*/
|
|
||||||
export async function RsaEncrypt(value: any) {
|
|
||||||
// 不存在则返回空值
|
|
||||||
if (!value) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
if (encryptor != null && sessionStorage.getItem('RsaPublicKey') != null) {
|
|
||||||
return encryptor.encrypt(value);
|
|
||||||
}
|
|
||||||
encryptor = new JSEncrypt();
|
|
||||||
const publicKey = (await getRsaPublicKey()) as string;
|
|
||||||
notBlank(publicKey, '获取公钥失败');
|
|
||||||
encryptor.setPublicKey(publicKey); //设置公钥
|
|
||||||
return encryptor.encrypt(value);
|
|
||||||
}
|
|
||||||
@@ -4,15 +4,15 @@ import { h, reactive } from 'vue';
|
|||||||
import { ElNotification } from 'element-plus';
|
import { ElNotification } from 'element-plus';
|
||||||
import ProgressNotify from '@/components/progress-notify/progress-notify.vue';
|
import ProgressNotify from '@/components/progress-notify/progress-notify.vue';
|
||||||
|
|
||||||
export function initSysMsgs() {
|
export async function initSysMsgs() {
|
||||||
registerDbSqlExecProgress();
|
await registerDbSqlExecProgress();
|
||||||
}
|
}
|
||||||
|
|
||||||
const sqlExecNotifyMap: Map<string, any> = new Map();
|
const sqlExecNotifyMap: Map<string, any> = new Map();
|
||||||
|
|
||||||
function registerDbSqlExecProgress() {
|
async function registerDbSqlExecProgress() {
|
||||||
syssocket.registerMsgHandler('execSqlFileProgress', function (message: any) {
|
await syssocket.registerMsgHandler('sqlScriptRunProgress', function (message: any) {
|
||||||
const content = JSON.parse(message.msg);
|
const content = message.params;
|
||||||
const id = content.id;
|
const id = content.id;
|
||||||
let progress = sqlExecNotifyMap.get(id);
|
let progress = sqlExecNotifyMap.get(id);
|
||||||
if (content.terminated) {
|
if (content.terminated) {
|
||||||
@@ -38,7 +38,7 @@ function registerDbSqlExecProgress() {
|
|||||||
duration: 0,
|
duration: 0,
|
||||||
title: message.title,
|
title: message.title,
|
||||||
message: h(ProgressNotify, progress.props),
|
message: h(ProgressNotify, progress.props),
|
||||||
type: syssocket.getMsgType(message.type),
|
type: 'info',
|
||||||
showClose: false,
|
showClose: false,
|
||||||
});
|
});
|
||||||
sqlExecNotifyMap.set(id, progress);
|
sqlExecNotifyMap.set(id, progress);
|
||||||
|
|||||||
@@ -1,34 +1,25 @@
|
|||||||
import Config from './config';
|
|
||||||
import SocketBuilder from './SocketBuilder';
|
|
||||||
import { getToken } from '@/common/utils/storage';
|
import { getToken } from '@/common/utils/storage';
|
||||||
|
|
||||||
import { joinClientParams } from './request';
|
import { createWebSocket } from './request';
|
||||||
import { ElNotification } from 'element-plus';
|
import { ElNotification } from 'element-plus';
|
||||||
|
import { MsgSubtypeEnum } from './commonEnum';
|
||||||
|
import EnumValue from './Enum';
|
||||||
|
|
||||||
class SysSocket {
|
class SysSocket {
|
||||||
/**
|
/**
|
||||||
* socket连接
|
* socket连接
|
||||||
*/
|
*/
|
||||||
socket: any;
|
socket: WebSocket | null = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* key -> 消息类别,value -> 消息对应的处理器函数
|
* key -> 消息类别,value -> 消息对应的处理器函数
|
||||||
*/
|
*/
|
||||||
categoryHandlers: Map<string, any> = new Map();
|
categoryHandlers: Map<string, any> = new Map();
|
||||||
|
|
||||||
/**
|
|
||||||
* 消息类型
|
|
||||||
*/
|
|
||||||
messageTypes: any = {
|
|
||||||
0: 'error',
|
|
||||||
1: 'success',
|
|
||||||
2: 'info',
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 初始化全局系统消息websocket
|
* 初始化全局系统消息websocket
|
||||||
*/
|
*/
|
||||||
init() {
|
async init() {
|
||||||
// 存在则不需要重新建立连接
|
// 存在则不需要重新建立连接
|
||||||
if (this.socket) {
|
if (this.socket) {
|
||||||
return;
|
return;
|
||||||
@@ -38,9 +29,9 @@ class SysSocket {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
console.log('init system ws');
|
console.log('init system ws');
|
||||||
const sysMsgUrl = `${Config.baseWsUrl}/sysmsg?${joinClientParams()}`;
|
try {
|
||||||
this.socket = SocketBuilder.builder(sysMsgUrl)
|
this.socket = await createWebSocket('/sysmsg');
|
||||||
.message((event: { data: string }) => {
|
this.socket.onmessage = async (event: { data: string }) => {
|
||||||
let message;
|
let message;
|
||||||
try {
|
try {
|
||||||
message = JSON.parse(event.data);
|
message = JSON.parse(event.data);
|
||||||
@@ -56,23 +47,32 @@ class SysSocket {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 默认通知处理
|
const msgSubtype = EnumValue.getEnumByValue(MsgSubtypeEnum, message.subtype);
|
||||||
const type = this.getMsgType(message.type);
|
if (!msgSubtype) {
|
||||||
let msg = message.msg;
|
console.log(`not found msg subtype: ${message.subtype}`);
|
||||||
let duration = 0;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 动态导入 i18n 或延迟获取 i18n 实例
|
||||||
|
let title = '';
|
||||||
|
try {
|
||||||
|
// 方式1: 动态导入
|
||||||
|
const { i18n } = await import('@/i18n');
|
||||||
|
title = i18n.global.t(msgSubtype?.label);
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('i18n not ready, using default title');
|
||||||
|
}
|
||||||
|
|
||||||
ElNotification({
|
ElNotification({
|
||||||
duration: duration,
|
duration: 0,
|
||||||
title: message.title,
|
title,
|
||||||
message: msg,
|
message: message.msg,
|
||||||
type: type,
|
type: msgSubtype?.extra.notifyType || 'info',
|
||||||
});
|
});
|
||||||
})
|
};
|
||||||
.open((event: any) => console.log(event))
|
} catch (e) {
|
||||||
.close(() => {
|
console.error('open system ws error', e);
|
||||||
console.log('close sys socket');
|
}
|
||||||
this.socket = null;
|
|
||||||
})
|
|
||||||
.build();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
destory() {
|
destory() {
|
||||||
@@ -87,8 +87,7 @@ class SysSocket {
|
|||||||
* @param category 消息类别
|
* @param category 消息类别
|
||||||
* @param handlerFunc 消息处理函数
|
* @param handlerFunc 消息处理函数
|
||||||
*/
|
*/
|
||||||
registerMsgHandler(category: any, handlerFunc: any) {
|
async registerMsgHandler(category: any, handlerFunc: any) {
|
||||||
this.init();
|
|
||||||
if (this.categoryHandlers.has(category)) {
|
if (this.categoryHandlers.has(category)) {
|
||||||
console.log(`${category}该类别消息处理器已存在...`);
|
console.log(`${category}该类别消息处理器已存在...`);
|
||||||
return;
|
return;
|
||||||
@@ -98,10 +97,6 @@ class SysSocket {
|
|||||||
}
|
}
|
||||||
this.categoryHandlers.set(category, handlerFunc);
|
this.categoryHandlers.set(category, handlerFunc);
|
||||||
}
|
}
|
||||||
|
|
||||||
getMsgType(msgType: any) {
|
|
||||||
return this.messageTypes[msgType];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 全局系统消息websocket;
|
// 全局系统消息websocket;
|
||||||
|
|||||||
@@ -1,241 +0,0 @@
|
|||||||
/**
|
|
||||||
* 2020.11.29 lyt 整理
|
|
||||||
* 工具类集合,适用于平时开发
|
|
||||||
*/
|
|
||||||
|
|
||||||
// 小数或整数(不可以负数)
|
|
||||||
export function verifyNumberIntegerAndFloat(val: string) {
|
|
||||||
// 匹配空格
|
|
||||||
let v = val.replace(/(^\s*)|(\s*$)/g, '');
|
|
||||||
// 只能是数字和小数点,不能是其他输入
|
|
||||||
v = v.replace(/[^\d.]/g, '');
|
|
||||||
// 以0开始只能输入一个
|
|
||||||
v = v.replace(/^0{2}$/g, '0');
|
|
||||||
// 保证第一位只能是数字,不能是点
|
|
||||||
v = v.replace(/^\./g, '');
|
|
||||||
// 小数只能出现1位
|
|
||||||
v = v.replace('.', '$#$').replace(/\./g, '').replace('$#$', '.');
|
|
||||||
// 小数点后面保留2位
|
|
||||||
v = v.replace(/^(\-)*(\d+)\.(\d\d).*$/, '$1$2.$3');
|
|
||||||
// 返回结果
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 正整数验证
|
|
||||||
export function verifiyNumberInteger(val: string) {
|
|
||||||
// 匹配空格
|
|
||||||
let v = val.replace(/(^\s*)|(\s*$)/g, '');
|
|
||||||
// 去掉 '.' , 防止贴贴的时候出现问题 如 0.1.12.12
|
|
||||||
v = v.replace(/[\.]*/g, '');
|
|
||||||
// 去掉以 0 开始后面的数, 防止贴贴的时候出现问题 如 00121323
|
|
||||||
v = v.replace(/(^0[\d]*)$/g, '0');
|
|
||||||
// 首位是0,只能出现一次
|
|
||||||
v = v.replace(/^0\d$/g, '0');
|
|
||||||
// 只匹配数字
|
|
||||||
v = v.replace(/[^\d]/g, '');
|
|
||||||
// 返回结果
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 去掉中文及空格
|
|
||||||
export function verifyCnAndSpace(val: string) {
|
|
||||||
// 匹配中文与空格
|
|
||||||
let v = val.replace(/[\u4e00-\u9fa5\s]+/g, '');
|
|
||||||
// 匹配空格
|
|
||||||
v = v.replace(/(^\s*)|(\s*$)/g, '');
|
|
||||||
// 返回结果
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 去掉英文及空格
|
|
||||||
export function verifyEnAndSpace(val: string) {
|
|
||||||
// 匹配英文与空格
|
|
||||||
let v = val.replace(/[a-zA-Z]+/g, '');
|
|
||||||
// 匹配空格
|
|
||||||
v = v.replace(/(^\s*)|(\s*$)/g, '');
|
|
||||||
// 返回结果
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 禁止输入空格
|
|
||||||
export function verifyAndSpace(val: string) {
|
|
||||||
// 匹配空格
|
|
||||||
let v = val.replace(/(^\s*)|(\s*$)/g, '');
|
|
||||||
// 返回结果
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 金额用 `,` 区分开
|
|
||||||
export function verifyNumberComma(val: string) {
|
|
||||||
// 调用小数或整数(不可以负数)方法
|
|
||||||
let v: any = verifyNumberIntegerAndFloat(val);
|
|
||||||
// 字符串转成数组
|
|
||||||
v = v.toString().split('.');
|
|
||||||
// \B 匹配非单词边界,两边都是单词字符或者两边都是非单词字符
|
|
||||||
v[0] = v[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',');
|
|
||||||
// 数组转字符串
|
|
||||||
v = v.join('.');
|
|
||||||
// 返回结果
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 匹配文字变色(搜索时)
|
|
||||||
export function verifyTextColor(val: string, text = '', color = 'red') {
|
|
||||||
// 返回内容,添加颜色
|
|
||||||
let v = text.replace(new RegExp(val, 'gi'), `<span style='color: ${color}'>${val}</span>`);
|
|
||||||
// 返回结果
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 数字转中文大写
|
|
||||||
export function verifyNumberCnUppercase(val: any, unit = '仟佰拾亿仟佰拾万仟佰拾元角分', v = '') {
|
|
||||||
// 当前内容字符串添加 2个0,为什么??
|
|
||||||
val += '00';
|
|
||||||
// 返回某个指定的字符串值在字符串中首次出现的位置,没有出现,则该方法返回 -1
|
|
||||||
let lookup = val.indexOf('.');
|
|
||||||
// substring:不包含结束下标内容,substr:包含结束下标内容
|
|
||||||
if (lookup >= 0) val = val.substring(0, lookup) + val.substr(lookup + 1, 2);
|
|
||||||
// 根据内容 val 的长度,截取返回对应大写
|
|
||||||
unit = unit.substr(unit.length - val.length);
|
|
||||||
// 循环截取拼接大写
|
|
||||||
for (let i = 0; i < val.length; i++) {
|
|
||||||
v += '零壹贰叁肆伍陆柒捌玖'.substr(val.substr(i, 1), 1) + unit.substr(i, 1);
|
|
||||||
}
|
|
||||||
// 正则处理
|
|
||||||
v = v
|
|
||||||
.replace(/零角零分$/, '整')
|
|
||||||
.replace(/零[仟佰拾]/g, '零')
|
|
||||||
.replace(/零{2,}/g, '零')
|
|
||||||
.replace(/零([亿|万])/g, '$1')
|
|
||||||
.replace(/零+元/, '元')
|
|
||||||
.replace(/亿零{0,3}万/, '亿')
|
|
||||||
.replace(/^元/, '零元');
|
|
||||||
// 返回结果
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 手机号码
|
|
||||||
export function verifyPhone(val: string) {
|
|
||||||
// false: 手机号码不正确
|
|
||||||
if (!/^((12[0-9])|(13[0-9])|(14[5|7])|(15([0-3]|[5-9]))|(18[0,5-9]))\d{8}$/.test(val)) return false;
|
|
||||||
// true: 手机号码正确
|
|
||||||
else return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 国内电话号码
|
|
||||||
export function verifyTelPhone(val: string) {
|
|
||||||
// false: 国内电话号码不正确
|
|
||||||
if (!/\d{3}-\d{8}|\d{4}-\d{7}/.test(val)) return false;
|
|
||||||
// true: 国内电话号码正确
|
|
||||||
else return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 登录账号 (字母开头,允许5-16字节,允许字母数字下划线)
|
|
||||||
export function verifyAccount(val: string) {
|
|
||||||
// false: 登录账号不正确
|
|
||||||
if (!/^[a-zA-Z][a-zA-Z0-9_]{4,15}$/.test(val)) return false;
|
|
||||||
// true: 登录账号正确
|
|
||||||
else return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 密码 (以字母开头,长度在6~16之间,只能包含字母、数字和下划线)
|
|
||||||
export function verifyPassword(val: string) {
|
|
||||||
// false: 密码不正确
|
|
||||||
if (!/^[a-zA-Z]\w{5,15}$/.test(val)) return false;
|
|
||||||
// true: 密码正确
|
|
||||||
else return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 强密码 (字母+数字+特殊字符,长度在6-16之间)
|
|
||||||
export function verifyPasswordPowerful(val: string) {
|
|
||||||
// false: 强密码不正确
|
|
||||||
if (!/^(?![a-zA-z]+$)(?!\d+$)(?![!@#$%^&\.*]+$)(?![a-zA-z\d]+$)(?![a-zA-z!@#$%^&\.*]+$)(?![\d!@#$%^&\.*]+$)[a-zA-Z\d!@#$%^&\.*]{6,16}$/.test(val))
|
|
||||||
return false;
|
|
||||||
// true: 强密码正确
|
|
||||||
else return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 密码强度
|
|
||||||
export function verifyPasswordStrength(val: string) {
|
|
||||||
let v = '';
|
|
||||||
// 弱:纯数字,纯字母,纯特殊字符
|
|
||||||
if (/^(?:\d+|[a-zA-Z]+|[!@#$%^&\.*]+){6,16}$/.test(val)) v = '弱';
|
|
||||||
// 中:字母+数字,字母+特殊字符,数字+特殊字符
|
|
||||||
if (/^(?![a-zA-z]+$)(?!\d+$)(?![!@#$%^&\.*]+$)[a-zA-Z\d!@#$%^&\.*]{6,16}$/.test(val)) v = '中';
|
|
||||||
// 强:字母+数字+特殊字符
|
|
||||||
if (/^(?![a-zA-z]+$)(?!\d+$)(?![!@#$%^&\.*]+$)(?![a-zA-z\d]+$)(?![a-zA-z!@#$%^&\.*]+$)(?![\d!@#$%^&\.*]+$)[a-zA-Z\d!@#$%^&\.*]{6,16}$/.test(val)) v = '强';
|
|
||||||
// 返回结果
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
// IP地址
|
|
||||||
export function verifyIPAddress(val: string) {
|
|
||||||
// false: IP地址不正确
|
|
||||||
if (!/^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/.test(val))
|
|
||||||
return false;
|
|
||||||
// true: IP地址正确
|
|
||||||
else return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 邮箱
|
|
||||||
export function verifyEmail(val: string) {
|
|
||||||
// false: 邮箱不正确
|
|
||||||
if (
|
|
||||||
!/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test(
|
|
||||||
val
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return false;
|
|
||||||
// true: 邮箱正确
|
|
||||||
else return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 身份证
|
|
||||||
export function verifyIdCard(val: string) {
|
|
||||||
// false: 身份证不正确
|
|
||||||
if (!/^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/.test(val)) return false;
|
|
||||||
// true: 身份证正确
|
|
||||||
else return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 姓名
|
|
||||||
export function verifyFullName(val: string) {
|
|
||||||
// false: 姓名不正确
|
|
||||||
if (!/^[\u4e00-\u9fa5]{1,6}(·[\u4e00-\u9fa5]{1,6}){0,2}$/.test(val)) return false;
|
|
||||||
// true: 姓名正确
|
|
||||||
else return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 邮政编码
|
|
||||||
export function verifyPostalCode(val: string) {
|
|
||||||
// false: 邮政编码不正确
|
|
||||||
if (!/^[1-9][0-9]{5}$/.test(val)) return false;
|
|
||||||
// true: 邮政编码正确
|
|
||||||
else return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// url
|
|
||||||
export function verifyUrl(val: string) {
|
|
||||||
// false: url不正确
|
|
||||||
if (
|
|
||||||
!/^(?:(?:(?:https?|ftp):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})).?)(?::\d{2,5})?(?:[/?#]\S*)?$/i.test(
|
|
||||||
val
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return false;
|
|
||||||
// true: url正确
|
|
||||||
else return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 车牌号
|
|
||||||
export function verifyCarNum(val: string) {
|
|
||||||
// false: 车牌号不正确
|
|
||||||
if (
|
|
||||||
!/^(([京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领][A-Z](([0-9]{5}[DF])|([DF]([A-HJ-NP-Z0-9])[0-9]{4})))|([京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领][A-Z][A-HJ-NP-Z0-9]{4}[A-HJ-NP-Z0-9挂学警港澳使领]))$/.test(
|
|
||||||
val
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return false;
|
|
||||||
// true:车牌号正确
|
|
||||||
else return true;
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
const mode = import.meta.env.VITE_ROUTER_MODE;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @description 获取不同路由模式所对应的 url
|
|
||||||
* @returns {String}
|
|
||||||
*/
|
|
||||||
export function getNowUrl() {
|
|
||||||
const url = {
|
|
||||||
hash: location.hash.substring(1),
|
|
||||||
history: location.pathname + location.search,
|
|
||||||
};
|
|
||||||
return url[mode];
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
// vite 打包相关
|
|
||||||
import dotenv from 'dotenv';
|
|
||||||
export interface ViteEnv {
|
|
||||||
VITE_PORT: number;
|
|
||||||
VITE_OPEN: boolean;
|
|
||||||
VITE_PUBLIC_PATH: string;
|
|
||||||
VITE_EDITOR: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function loadEnv(): ViteEnv {
|
|
||||||
const env = process.env.NODE_ENV;
|
|
||||||
const ret: any = {};
|
|
||||||
const envList = [`.env.${env}.local`, `.env.${env}`, '.env.local', '.env', ,];
|
|
||||||
envList.forEach((e) => {
|
|
||||||
dotenv.config({ path: e });
|
|
||||||
});
|
|
||||||
for (const envName of Object.keys(process.env)) {
|
|
||||||
console.log(envName);
|
|
||||||
let realName = (process.env as any)[envName].replace(/\\n/g, '\n');
|
|
||||||
realName = realName === 'true' ? true : realName === 'false' ? false : realName;
|
|
||||||
if (envName === 'VITE_PORT') realName = Number(realName);
|
|
||||||
if (envName === 'VITE_OPEN') realName = Boolean(realName);
|
|
||||||
ret[envName] = realName;
|
|
||||||
process.env[envName] = realName;
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
<div class="flex">
|
<div class="flex">
|
||||||
<!-- 简易单个搜索项 -->
|
<!-- 简易单个搜索项 -->
|
||||||
<div v-if="nowSearchItem" class="flex">
|
<div v-if="nowSearchItem" class="flex">
|
||||||
<el-dropdown v-if="searchItems?.length > 1">
|
<el-dropdown v-if="props.searchItems?.length > 1">
|
||||||
<SvgIcon :size="16" name="CaretBottom" class="!mr-1 !mt-1.5 simple-search-form-btn" />
|
<SvgIcon :size="16" name="CaretBottom" class="!mr-1 !mt-1.5 simple-search-form-btn" />
|
||||||
<template #dropdown>
|
<template #dropdown>
|
||||||
<el-dropdown-menu>
|
<el-dropdown-menu>
|
||||||
@@ -54,7 +54,7 @@
|
|||||||
<!-- <el-button v-if="showToolButton('refresh')" icon="Refresh" circle @click="execQuery()" /> -->
|
<!-- <el-button v-if="showToolButton('refresh')" icon="Refresh" circle @click="execQuery()" /> -->
|
||||||
|
|
||||||
<el-button
|
<el-button
|
||||||
v-if="showToolButton('search') && searchItems?.length > 1"
|
v-if="showToolButton('search') && props.searchItems?.length > 1"
|
||||||
:icon="isShowSearch ? 'ArrowDown' : 'ArrowUp'"
|
:icon="isShowSearch ? 'ArrowDown' : 'ArrowUp'"
|
||||||
circle
|
circle
|
||||||
@click="isShowSearch = !isShowSearch"
|
@click="isShowSearch = !isShowSearch"
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import { useDebounceFn, useEventListener, useIntervalFn } from '@vueuse/core';
|
|||||||
import themes from './themes';
|
import themes from './themes';
|
||||||
import { TrzszFilter } from 'trzsz';
|
import { TrzszFilter } from 'trzsz';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { createWebSocket } from '@/common/request';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
@@ -124,7 +125,7 @@ const initTerm = async () => {
|
|||||||
// 注册窗口大小监听器
|
// 注册窗口大小监听器
|
||||||
useEventListener('resize', useDebounceFn(fitTerminal, 400));
|
useEventListener('resize', useDebounceFn(fitTerminal, 400));
|
||||||
|
|
||||||
initSocket();
|
await initSocket();
|
||||||
// 注册其他插件
|
// 注册其他插件
|
||||||
loadAddon();
|
loadAddon();
|
||||||
|
|
||||||
@@ -140,33 +141,31 @@ const initTerm = async () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const initSocket = () => {
|
const initSocket = async () => {
|
||||||
if (!props.socketUrl) {
|
if (!props.socketUrl) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
socket = new WebSocket(`${props.socketUrl}&rows=${term?.rows}&cols=${term?.cols}`);
|
try {
|
||||||
// 监听socket连接
|
socket = await createWebSocket(`${props.socketUrl}?rows=${term?.rows}&cols=${term?.cols}`);
|
||||||
socket.onopen = () => {
|
} catch (e) {
|
||||||
// 注册心跳
|
|
||||||
useIntervalFn(sendPing, 15000);
|
|
||||||
|
|
||||||
state.status = TerminalStatus.Connected;
|
|
||||||
|
|
||||||
focus();
|
|
||||||
fitTerminal();
|
|
||||||
|
|
||||||
// 如果有初始要执行的命令,则发送执行命令
|
|
||||||
if (props.cmd) {
|
|
||||||
sendData(props.cmd + ' \r');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 监听socket错误信息
|
|
||||||
socket.onerror = (e: Event) => {
|
|
||||||
term.writeln(`\r\n\x1b[31m${t('components.terminal.connErrMsg')}`);
|
term.writeln(`\r\n\x1b[31m${t('components.terminal.connErrMsg')}`);
|
||||||
state.status = TerminalStatus.Error;
|
state.status = TerminalStatus.Error;
|
||||||
console.log('连接错误', e);
|
console.log('连接错误', e);
|
||||||
};
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 注册心跳
|
||||||
|
useIntervalFn(sendPing, 15000);
|
||||||
|
|
||||||
|
state.status = TerminalStatus.Connected;
|
||||||
|
|
||||||
|
focus();
|
||||||
|
fitTerminal();
|
||||||
|
|
||||||
|
// 如果有初始要执行的命令,则发送执行命令
|
||||||
|
if (props.cmd) {
|
||||||
|
sendData(props.cmd + ' \r');
|
||||||
|
}
|
||||||
|
|
||||||
socket.onclose = (e: CloseEvent) => {
|
socket.onclose = (e: CloseEvent) => {
|
||||||
console.log('terminal socket close...', e.reason);
|
console.log('terminal socket close...', e.reason);
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ export default {
|
|||||||
dbFilterPlaceholder: 'DB name: Input filterable',
|
dbFilterPlaceholder: 'DB name: Input filterable',
|
||||||
sqlRecord: 'SQL records',
|
sqlRecord: 'SQL records',
|
||||||
dump: 'Export',
|
dump: 'Export',
|
||||||
|
dbDumpFail: 'DB export failed',
|
||||||
dumpContent: 'Export Content',
|
dumpContent: 'Export Content',
|
||||||
structure: 'Structure',
|
structure: 'Structure',
|
||||||
data: 'Data',
|
data: 'Data',
|
||||||
@@ -55,6 +56,8 @@ export default {
|
|||||||
execSuccess: 'Successful execution',
|
execSuccess: 'Successful execution',
|
||||||
execFail: 'Execution failure',
|
execFail: 'Execution failure',
|
||||||
sqlScriptRun: 'Run SQL Script',
|
sqlScriptRun: 'Run SQL Script',
|
||||||
|
sqlScriptRunSuccess: 'SQL script executed successfully',
|
||||||
|
sqlScriptRunFail: 'SQL script execution failed',
|
||||||
saveSql: 'Save SQL',
|
saveSql: 'Save SQL',
|
||||||
execInfo: 'Execution info',
|
execInfo: 'Execution info',
|
||||||
result: 'Result',
|
result: 'Result',
|
||||||
|
|||||||
@@ -138,5 +138,7 @@ export default {
|
|||||||
fileTooLargeTips: 'The file is too large, please download and use it',
|
fileTooLargeTips: 'The file is too large, please download and use it',
|
||||||
uploadSuccess: 'Upload successfully',
|
uploadSuccess: 'Upload successfully',
|
||||||
fileExceedsSysConf: 'The uploaded file exceeds the system configuration [{uploadMaxFileSize}]',
|
fileExceedsSysConf: 'The uploaded file exceeds the system configuration [{uploadMaxFileSize}]',
|
||||||
|
fileUploadSuccess: 'Machine file upload successful',
|
||||||
|
fileUploadFail: 'Machine file upload failed',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ export default {
|
|||||||
dbFilterPlaceholder: '库名: 输入可过滤',
|
dbFilterPlaceholder: '库名: 输入可过滤',
|
||||||
sqlRecord: 'SQL记录',
|
sqlRecord: 'SQL记录',
|
||||||
dump: '导出',
|
dump: '导出',
|
||||||
|
dbDumpFail: '数据库导出失败',
|
||||||
dumpContent: '导出内容',
|
dumpContent: '导出内容',
|
||||||
structure: '结构',
|
structure: '结构',
|
||||||
data: '数据',
|
data: '数据',
|
||||||
@@ -55,6 +56,8 @@ export default {
|
|||||||
execSuccess: '执行成功',
|
execSuccess: '执行成功',
|
||||||
execFail: '执行失败',
|
execFail: '执行失败',
|
||||||
sqlScriptRun: 'SQL脚本执行',
|
sqlScriptRun: 'SQL脚本执行',
|
||||||
|
sqlScriptRunSuccess: 'SQL脚本执行成功',
|
||||||
|
sqlScriptRunFail: 'SQL脚本执行失败',
|
||||||
saveSql: '保存SQL',
|
saveSql: '保存SQL',
|
||||||
execInfo: '执行信息',
|
execInfo: '执行信息',
|
||||||
result: '结果',
|
result: '结果',
|
||||||
|
|||||||
@@ -139,5 +139,7 @@ export default {
|
|||||||
fileTooLargeTips: '文件太大, 请下载使用',
|
fileTooLargeTips: '文件太大, 请下载使用',
|
||||||
uploadSuccess: '上传成功',
|
uploadSuccess: '上传成功',
|
||||||
fileExceedsSysConf: '上传的文件超过系统配置的【{uploadMaxFileSize}】',
|
fileExceedsSysConf: '上传的文件超过系统配置的【{uploadMaxFileSize}】',
|
||||||
|
fileUploadSuccess: '机器文件上传成功',
|
||||||
|
fileUploadFail: '机器文件上传失败',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -41,25 +41,27 @@
|
|||||||
<div class="layout-navbars-breadcrumb-user-icon" @click="onSearchClick">
|
<div class="layout-navbars-breadcrumb-user-icon" @click="onSearchClick">
|
||||||
<SvgIcon name="search" :title="$t('layout.user.menuSearch')" />
|
<SvgIcon name="search" :title="$t('layout.user.menuSearch')" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="layout-navbars-breadcrumb-user-icon" @click="onLayoutSetingClick">
|
<div class="layout-navbars-breadcrumb-user-icon" @click="onLayoutSetingClick">
|
||||||
<SvgIcon name="setting" :title="$t('layout.user.layoutConf')" />
|
<SvgIcon name="setting" :title="$t('layout.user.layoutConf')" />
|
||||||
</div>
|
</div>
|
||||||
<div class="layout-navbars-breadcrumb-user-icon">
|
|
||||||
<el-popover placement="bottom" trigger="click" :visible="state.isShowUserNewsPopover" :width="300" popper-class="el-popover-pupop-user-news">
|
<el-popover @show="onShowMsgs" placement="bottom" trigger="click" :width="450">
|
||||||
<template #reference>
|
<template #reference>
|
||||||
<el-badge :is-dot="false" @click="state.isShowUserNewsPopover = !state.isShowUserNewsPopover">
|
<div class="layout-navbars-breadcrumb-user-icon">
|
||||||
|
<el-badge :show-zero="false" :value="state.unreadMsgCount">
|
||||||
<SvgIcon name="bell" :title="$t('layout.user.news')" />
|
<SvgIcon name="bell" :title="$t('layout.user.news')" />
|
||||||
</el-badge>
|
</el-badge>
|
||||||
</template>
|
</div>
|
||||||
<transition name="el-zoom-in-top">
|
</template>
|
||||||
<UserNews v-show="state.isShowUserNewsPopover" />
|
<UserNews ref="userNewsRef" @update:count="state.unreadMsgCount = $event" />
|
||||||
</transition>
|
</el-popover>
|
||||||
</el-popover>
|
|
||||||
</div>
|
|
||||||
<div class="layout-navbars-breadcrumb-user-icon mr-2" @click="onScreenfullClick">
|
<div class="layout-navbars-breadcrumb-user-icon mr-2" @click="onScreenfullClick">
|
||||||
<SvgIcon v-if="!state.isScreenfull" name="full-screen" :title="$t('layout.user.fullScreenOff')" />
|
<SvgIcon v-if="!state.isScreenfull" name="full-screen" :title="$t('layout.user.fullScreenOff')" />
|
||||||
<SvgIcon v-else name="crop" />
|
<SvgIcon v-else name="crop" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-dropdown trigger="click" :show-timeout="70" :hide-timeout="50" @command="onHandleCommandClick">
|
<el-dropdown trigger="click" :show-timeout="70" :hide-timeout="50" @command="onHandleCommandClick">
|
||||||
<span class="layout-navbars-breadcrumb-user-link cursor-pointer">
|
<span class="layout-navbars-breadcrumb-user-link cursor-pointer">
|
||||||
<img :src="userInfo.photo" class="layout-navbars-breadcrumb-user-link-photo mr-1" />
|
<img :src="userInfo.photo" class="layout-navbars-breadcrumb-user-link-photo mr-1" />
|
||||||
@@ -79,7 +81,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="layoutBreadcrumbUser">
|
<script setup lang="ts" name="layoutBreadcrumbUser">
|
||||||
import { ref, computed, reactive, onMounted, watch } from 'vue';
|
import { ref, computed, reactive, onMounted, watch, useTemplateRef } from 'vue';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
import { ElMessageBox, ElMessage } from 'element-plus';
|
import { ElMessageBox, ElMessage } from 'element-plus';
|
||||||
import screenfull from 'screenfull';
|
import screenfull from 'screenfull';
|
||||||
@@ -100,10 +102,12 @@ import EnumValue from '@/common/Enum';
|
|||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const searchRef = ref();
|
const searchRef = ref();
|
||||||
|
const userNewsRef = useTemplateRef('userNewsRef');
|
||||||
|
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
isScreenfull: false,
|
isScreenfull: false,
|
||||||
isShowUserNewsPopover: false,
|
|
||||||
disabledSize: '',
|
disabledSize: '',
|
||||||
|
unreadMsgCount: 0,
|
||||||
});
|
});
|
||||||
const { userInfo } = storeToRefs(useUserInfo());
|
const { userInfo } = storeToRefs(useUserInfo());
|
||||||
const themeConfigStore = useThemeConfig();
|
const themeConfigStore = useThemeConfig();
|
||||||
@@ -126,8 +130,15 @@ onMounted(() => {
|
|||||||
initComponentSize();
|
initComponentSize();
|
||||||
isDark.value = themeConfig.isDark;
|
isDark.value = themeConfig.isDark;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取未读消息数量
|
||||||
|
state.unreadMsgCount = 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const onShowMsgs = () => {
|
||||||
|
userNewsRef.value?.loadMsgs(true);
|
||||||
|
};
|
||||||
|
|
||||||
// 全屏点击时
|
// 全屏点击时
|
||||||
const onScreenfullClick = () => {
|
const onScreenfullClick = () => {
|
||||||
if (!screenfull.isEnabled) {
|
if (!screenfull.isEnabled) {
|
||||||
|
|||||||
@@ -1,117 +1,132 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-navbars-breadcrumb-user-news">
|
<div class="flex flex-col w-full rounded-md shadow-sm">
|
||||||
<div class="head-box">
|
<!-- Header -->
|
||||||
<div class="head-box-title">{{ $t('layout.user.newTitle') }}</div>
|
<div class="flex items-center justify-between border-b border-gray-200 px-4 py-2 text-sm text-gray-700">
|
||||||
<div class="head-box-btn" v-if="newsList.length > 0" @click="onAllReadClick">{{ $t('layout.user.newBtn') }}</div>
|
<div class="font-medium">{{ $t('layout.user.newTitle') }}</div>
|
||||||
|
<div v-if="unreadCount > 0" class="color-primary cursor-pointer opacity-80 transition-opacity hover:opacity-100" @click="onRead()">
|
||||||
|
{{ $t('layout.user.newBtn') }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="content-box">
|
|
||||||
<template v-if="newsList.length > 0">
|
<!-- Content -->
|
||||||
<div class="content-box-item" v-for="(v, k) in newsList" :key="k">
|
<el-scrollbar height="350px" v-loading="loadingMsgs" class="px-4 py-2 text-sm">
|
||||||
<div>{{ v.label }}</div>
|
<template v-if="msgs.length > 0">
|
||||||
<div class="content-box-msg">
|
<div
|
||||||
{{ v.value }}
|
v-for="(v, k) in msgs"
|
||||||
|
:key="k"
|
||||||
|
class="pt-1 mt-0.5"
|
||||||
|
:style="{ backgroundColor: v.status == 1 ? 'var(--el-color-info-light-9)' : 'transparent' }"
|
||||||
|
@click="onRead(v)"
|
||||||
|
>
|
||||||
|
<div class="flex justify-between items-start">
|
||||||
|
<el-text size="small" tag="b" :type="EnumValue.getEnumByValue(MsgSubtypeEnum, v.subtype)?.extra?.notifyType">
|
||||||
|
{{ $t(EnumValue.getEnumByValue(MsgSubtypeEnum, v.subtype)?.label || '') }}
|
||||||
|
</el-text>
|
||||||
</div>
|
</div>
|
||||||
<div class="content-box-time">{{ v.time }}</div>
|
<div class="text-gray-500 mt-1 mb-1">{{ v.msg }}</div>
|
||||||
|
<div class="text-gray-500">{{ formatDate(v.createTime) }}</div>
|
||||||
|
<div class="mt-2 border-t border-gray-200"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<el-button class="w-full mt-1" size="small" @click="loadMsgs()" v-if="!loadMoreDisable"> {{ $t('redis.loadMore') }} </el-button>
|
||||||
</template>
|
</template>
|
||||||
<el-empty :description="$t('layout.user.newDesc')" v-else></el-empty>
|
|
||||||
</div>
|
<el-empty v-if="msgs.length == 0 && !loadingMsgs" :image-size="100" :description="$t('layout.user.newDesc')" />
|
||||||
<div class="foot-box" @click="toMsgCenter" v-if="newsList.length > 0">{{ $t('layout.user.newGo') }}</div>
|
</el-scrollbar>
|
||||||
|
|
||||||
|
<!-- Footer -->
|
||||||
|
<!-- <div
|
||||||
|
v-if="msgs.length > 0"
|
||||||
|
class="color-primary flex h-9 items-center justify-center border-t border-gray-200 text-sm cursor-pointer opacity-80 transition-opacity hover:opacity-100"
|
||||||
|
@click="toMsgCenter"
|
||||||
|
>
|
||||||
|
{{ $t('layout.user.newGo') }}
|
||||||
|
</div> -->
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts" setup>
|
||||||
import { reactive, toRefs } from 'vue';
|
import { MsgSubtypeEnum } from '@/common/commonEnum';
|
||||||
export default {
|
import EnumValue from '@/common/Enum';
|
||||||
name: 'layoutBreadcrumbUserNews',
|
import { formatDate } from '@/common/utils/format';
|
||||||
setup() {
|
import { personApi } from '@/views/personal/api';
|
||||||
const state = reactive({
|
import { useIntervalFn } from '@vueuse/core';
|
||||||
newsList: [
|
import { onMounted, ref, watchEffect } from 'vue';
|
||||||
{
|
|
||||||
label: '关于学习交流的通知',
|
const emit = defineEmits(['update:count']);
|
||||||
value: 'QQ群号码 119699946',
|
|
||||||
time: '2021-09-08',
|
const msgQuery = ref<any>({
|
||||||
},
|
pageNum: 1,
|
||||||
],
|
pageSize: 10,
|
||||||
});
|
});
|
||||||
// 全部已读点击
|
|
||||||
const onAllReadClick = () => {
|
const loadMoreDisable = ref(true);
|
||||||
state.newsList = [];
|
const loadingMsgs = ref(true);
|
||||||
};
|
const msgs = ref<Array<any>>([]);
|
||||||
// 前往通知中心点击
|
const unreadCount = ref(0);
|
||||||
const toMsgCenter = () => {};
|
|
||||||
return {
|
onMounted(() => {
|
||||||
onAllReadClick,
|
useIntervalFn(
|
||||||
toMsgCenter,
|
() => {
|
||||||
...toRefs(state),
|
// 定时更新未读消息数
|
||||||
};
|
personApi.getUnreadMsgCount.request().then((res) => {
|
||||||
},
|
unreadCount.value = res;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
10 * 1000,
|
||||||
|
{ immediate: true, immediateCallback: true }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
emit('update:count', unreadCount.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
const loadMsgs = async (research: boolean = false) => {
|
||||||
|
if (research) {
|
||||||
|
msgQuery.value.pageNum = 1;
|
||||||
|
msgs.value = [];
|
||||||
|
}
|
||||||
|
const msgList = await getMsgs();
|
||||||
|
msgs.value.push(...msgList.list);
|
||||||
|
msgQuery.value.pageNum += 1;
|
||||||
|
|
||||||
|
loadMoreDisable.value = msgList.total <= msgs.value.length;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getMsgs = async () => {
|
||||||
|
try {
|
||||||
|
loadingMsgs.value = true;
|
||||||
|
return await personApi.getMsgs.request(msgQuery.value);
|
||||||
|
} catch (e) {
|
||||||
|
//
|
||||||
|
} finally {
|
||||||
|
loadingMsgs.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onRead = async (msg: any = null) => {
|
||||||
|
if (msg && (msg.status == 1 || !msg.status)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await personApi.readMsg.request({ id: msg?.id || 0 });
|
||||||
|
loadMsgs(true);
|
||||||
|
|
||||||
|
if (!msg) {
|
||||||
|
// 如果是全部已读,重置未读消息数
|
||||||
|
unreadCount.value = 0;
|
||||||
|
} else {
|
||||||
|
// 如果是单条已读,减少未读消息数
|
||||||
|
unreadCount.value = Math.max(unreadCount.value - 1, 0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
loadMsgs,
|
||||||
|
});
|
||||||
|
|
||||||
|
const toMsgCenter = () => {};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss"></style>
|
||||||
.layout-navbars-breadcrumb-user-news {
|
|
||||||
.head-box {
|
|
||||||
display: flex;
|
|
||||||
border-bottom: 1px solid #ebeef5;
|
|
||||||
box-sizing: border-box;
|
|
||||||
color: #333333;
|
|
||||||
justify-content: space-between;
|
|
||||||
height: 35px;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.head-box-btn {
|
|
||||||
color: var(--el-color-primary);
|
|
||||||
font-size: 13px;
|
|
||||||
cursor: pointer;
|
|
||||||
opacity: 0.8;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-box {
|
|
||||||
font-size: 13px;
|
|
||||||
|
|
||||||
.content-box-item {
|
|
||||||
padding-top: 12px;
|
|
||||||
|
|
||||||
&:last-of-type {
|
|
||||||
padding-bottom: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-box-msg {
|
|
||||||
color: #999999;
|
|
||||||
margin-top: 5px;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-box-time {
|
|
||||||
color: #999999;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.foot-box {
|
|
||||||
height: 35px;
|
|
||||||
color: var(--el-color-primary);
|
|
||||||
font-size: 13px;
|
|
||||||
cursor: pointer;
|
|
||||||
opacity: 0.8;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
border-top: 1px solid #ebeef5;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
::v-deep(.el-empty__description p) {
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { getNowUrl } from '@/common/utils/url';
|
|
||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -12,14 +11,5 @@ export const useTagsViews = defineStore('tagsViews', {
|
|||||||
setTagsViews(data: Array<TagsView>) {
|
setTagsViews(data: Array<TagsView>) {
|
||||||
this.tagsViews = data;
|
this.tagsViews = data;
|
||||||
},
|
},
|
||||||
// 设置当前页面的tags view title
|
|
||||||
setNowTitle(title: string) {
|
|
||||||
this.tagsViews.forEach((item) => {
|
|
||||||
// console.log(getNowUrl(), item.path);
|
|
||||||
if (item.path == getNowUrl()) {
|
|
||||||
item.title = title;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<div class="home-container personal">
|
<div class="home-container personal">
|
||||||
<el-row :gutter="15">
|
<el-row :gutter="15">
|
||||||
<!-- 个人信息 -->
|
<!-- 个人信息 -->
|
||||||
<el-col :xs="24" :sm="16">
|
<el-col :xs="24" :sm="24">
|
||||||
<el-card shadow="hover" :header="$t('home.personalInfo')">
|
<el-card shadow="hover" :header="$t('home.personalInfo')">
|
||||||
<div class="personal-user">
|
<div class="personal-user">
|
||||||
<div class="personal-user-left">
|
<div class="personal-user-left">
|
||||||
@@ -52,23 +52,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
</el-col>
|
</el-col>
|
||||||
|
|
||||||
<!-- 消息通知 -->
|
|
||||||
<el-col :xs="24" :sm="8" class="pl15 personal-info">
|
|
||||||
<el-card shadow="hover">
|
|
||||||
<template #header>
|
|
||||||
<span>{{ $t('home.msgNotify') }}</span>
|
|
||||||
<span @click="showMsgs" class="personal-info-more">{{ $t('common.more') }}</span>
|
|
||||||
</template>
|
|
||||||
<div class="personal-info-box">
|
|
||||||
<ul class="personal-info-ul">
|
|
||||||
<li v-for="(v, k) in state.msgs as any" :key="k" class="personal-info-li">
|
|
||||||
<a class="personal-info-li-title">{{ `[${$t(EnumValue.getLabelByValue(MsgTypeEnum, v.type))}] ${v.msg}` }}</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</el-card>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
<el-row :gutter="20" class="!mt-4 resource-info">
|
<el-row :gutter="20" class="!mt-4 resource-info">
|
||||||
@@ -236,39 +219,11 @@
|
|||||||
</el-card>
|
</el-card>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
<el-dialog width="900px" :title="$t('common.msg')" v-model="msgDialog.visible">
|
|
||||||
<el-table border :data="msgDialog.msgs.list" size="small">
|
|
||||||
<el-table-column property="type" :label="$t('common.type')" width="60">
|
|
||||||
<template #default="scope">
|
|
||||||
{{ $t(EnumValue.getLabelByValue(MsgTypeEnum, scope.row.type)) }}
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column property="msg" :label="$t('common.msg')"></el-table-column>
|
|
||||||
<el-table-column property="createTime" :label="$t('common.time')" width="150">
|
|
||||||
<template #default="scope">
|
|
||||||
{{ formatDate(scope.row.createTime) }}
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
<el-row type="flex" class="mt-1" justify="center">
|
|
||||||
<el-pagination
|
|
||||||
small
|
|
||||||
@current-change="searchMsg"
|
|
||||||
style="text-align: center"
|
|
||||||
background
|
|
||||||
layout="prev, pager, next, total, jumper"
|
|
||||||
:total="msgDialog.msgs.total"
|
|
||||||
v-model:current-page="msgDialog.query.pageNum"
|
|
||||||
:page-size="msgDialog.query.pageSize"
|
|
||||||
/>
|
|
||||||
</el-row>
|
|
||||||
</el-dialog>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, onMounted, reactive, toRefs } from 'vue';
|
import { computed, onMounted, reactive } from 'vue';
|
||||||
// import * as echarts from 'echarts';
|
// import * as echarts from 'echarts';
|
||||||
import { formatAxis, formatDate } from '@/common/utils/format';
|
import { formatAxis, formatDate } from '@/common/utils/format';
|
||||||
import { indexApi } from './api';
|
import { indexApi } from './api';
|
||||||
@@ -285,8 +240,6 @@ import { getAllTagInfoByCodePaths } from '../ops/component/tag';
|
|||||||
import { ElMessage } from 'element-plus';
|
import { ElMessage } from 'element-plus';
|
||||||
import { getFileUrl, getUploadFileUrl } from '@/common/request';
|
import { getFileUrl, getUploadFileUrl } from '@/common/request';
|
||||||
import { saveUser } from '@/common/utils/storage';
|
import { saveUser } from '@/common/utils/storage';
|
||||||
import EnumValue from '../../common/Enum';
|
|
||||||
import { MsgTypeEnum } from './enums';
|
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { userInfo } = storeToRefs(useUserInfo());
|
const { userInfo } = storeToRefs(useUserInfo());
|
||||||
@@ -296,17 +249,6 @@ const state = reactive({
|
|||||||
roles: [],
|
roles: [],
|
||||||
},
|
},
|
||||||
msgs: [],
|
msgs: [],
|
||||||
msgDialog: {
|
|
||||||
visible: false,
|
|
||||||
query: {
|
|
||||||
pageSize: 10,
|
|
||||||
pageNum: 1,
|
|
||||||
},
|
|
||||||
msgs: {
|
|
||||||
list: [],
|
|
||||||
total: null,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
resourceOpTableHeight: 180,
|
resourceOpTableHeight: 180,
|
||||||
defaultLogSize: 5,
|
defaultLogSize: 5,
|
||||||
machine: {
|
machine: {
|
||||||
@@ -331,8 +273,6 @@ const state = reactive({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const { msgDialog } = toRefs(state);
|
|
||||||
|
|
||||||
const roleInfo = computed(() => {
|
const roleInfo = computed(() => {
|
||||||
if (state.accountInfo.roles.length == 0) {
|
if (state.accountInfo.roles.length == 0) {
|
||||||
return '';
|
return '';
|
||||||
@@ -349,30 +289,12 @@ const currentTime = computed(() => {
|
|||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
initData();
|
initData();
|
||||||
getAccountInfo();
|
getAccountInfo();
|
||||||
|
|
||||||
getMsgs().then((res) => {
|
|
||||||
state.msgs = res.list;
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const showMsgs = async () => {
|
|
||||||
state.msgDialog.query.pageNum = 1;
|
|
||||||
searchMsg();
|
|
||||||
state.msgDialog.visible = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
const searchMsg = async () => {
|
|
||||||
state.msgDialog.msgs = await getMsgs();
|
|
||||||
};
|
|
||||||
|
|
||||||
const getAccountInfo = async () => {
|
const getAccountInfo = async () => {
|
||||||
state.accountInfo = await personApi.accountInfo.request();
|
state.accountInfo = await personApi.accountInfo.request();
|
||||||
};
|
};
|
||||||
|
|
||||||
const getMsgs = async () => {
|
|
||||||
return await personApi.getMsgs.request(state.msgDialog.query);
|
|
||||||
};
|
|
||||||
|
|
||||||
const beforeAvatarUpload = (rawFile: any) => {
|
const beforeAvatarUpload = (rawFile: any) => {
|
||||||
if (rawFile.size >= 512 * 1024) {
|
if (rawFile.size >= 512 * 1024) {
|
||||||
ElMessage.error('头像不能超过512KB!');
|
ElMessage.error('头像不能超过512KB!');
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
import { EnumValue } from '@/common/Enum';
|
|
||||||
|
|
||||||
export const MsgTypeEnum = {
|
|
||||||
Login: EnumValue.of(1, 'home.msgTypeLogin'),
|
|
||||||
Notify: EnumValue.of(2, 'home.msgTypeNotify'),
|
|
||||||
};
|
|
||||||
@@ -151,7 +151,7 @@ import { ElMessage } from 'element-plus';
|
|||||||
import { initRouter } from '@/router/index';
|
import { initRouter } from '@/router/index';
|
||||||
import { getRefreshToken, saveRefreshToken, saveToken, saveUser } from '@/common/utils/storage';
|
import { getRefreshToken, saveRefreshToken, saveToken, saveUser } from '@/common/utils/storage';
|
||||||
import openApi from '@/common/openApi';
|
import openApi from '@/common/openApi';
|
||||||
import { RsaEncrypt } from '@/common/rsa';
|
import { RsaEncrypt } from '@/common/crypto';
|
||||||
import { getAccountLoginSecurity, getLdapEnabled } from '@/common/sysconfig';
|
import { getAccountLoginSecurity, getLdapEnabled } from '@/common/sysconfig';
|
||||||
import { letterAvatar } from '@/common/utils/string';
|
import { letterAvatar } from '@/common/utils/string';
|
||||||
import { useUserInfo } from '@/store/userInfo';
|
import { useUserInfo } from '@/store/userInfo';
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import Api from '@/common/Api';
|
import Api from '@/common/Api';
|
||||||
import config from '@/common/config';
|
|
||||||
import { joinClientParams } from '@/common/request';
|
import { joinClientParams } from '@/common/request';
|
||||||
|
|
||||||
export const machineApi = {
|
export const machineApi = {
|
||||||
@@ -67,9 +66,9 @@ export const cmdConfApi = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export function getMachineTerminalSocketUrl(authCertName: any) {
|
export function getMachineTerminalSocketUrl(authCertName: any) {
|
||||||
return `${config.baseWsUrl}/machines/terminal/${authCertName}?${joinClientParams()}`;
|
return `/machines/terminal/${authCertName}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getMachineRdpSocketUrl(authCertName: any) {
|
export function getMachineRdpSocketUrl(authCertName: any) {
|
||||||
return `${config.baseWsUrl}/machines/rdp/${authCertName}`;
|
return `/machines/rdp/${authCertName}`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,5 +5,7 @@ export const personApi = {
|
|||||||
updateAccount: Api.newPut('/sys/accounts/self'),
|
updateAccount: Api.newPut('/sys/accounts/self'),
|
||||||
authStatus: Api.newGet('/auth/oauth2/status'),
|
authStatus: Api.newGet('/auth/oauth2/status'),
|
||||||
getMsgs: Api.newGet('/msgs/self'),
|
getMsgs: Api.newGet('/msgs/self'),
|
||||||
|
getUnreadMsgCount: Api.newGet('/msgs/self/unread/count'),
|
||||||
|
readMsg: Api.newGet('/msgs/self/read'),
|
||||||
unbindOauth2: Api.newGet('/auth/oauth2/unbind'),
|
unbindOauth2: Api.newGet('/auth/oauth2/unbind'),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,93 +1,87 @@
|
|||||||
import vue from '@vitejs/plugin-vue';
|
import vue from '@vitejs/plugin-vue';
|
||||||
import { resolve } from 'path';
|
import { resolve } from 'path';
|
||||||
import type { UserConfig } from 'vite';
|
|
||||||
import { loadEnv } from './src/common/utils/viteBuild';
|
|
||||||
import { CodeInspectorPlugin } from 'code-inspector-plugin';
|
import { CodeInspectorPlugin } from 'code-inspector-plugin';
|
||||||
import progress from 'vite-plugin-progress';
|
import progress from 'vite-plugin-progress';
|
||||||
import tailwindcss from '@tailwindcss/vite';
|
import tailwindcss from '@tailwindcss/vite';
|
||||||
|
import { ConfigEnv, defineConfig, loadEnv } from 'vite';
|
||||||
|
|
||||||
const pathResolve = (dir: string): any => {
|
export default defineConfig(({ mode }: ConfigEnv) => {
|
||||||
return resolve(__dirname, '.', dir);
|
const env = loadEnv(mode, process.cwd(), '');
|
||||||
};
|
const isProd = process.env.NODE_ENV === 'production';
|
||||||
|
const pathResolve = (dir: string): any => {
|
||||||
|
return resolve(__dirname, '.', dir);
|
||||||
|
};
|
||||||
|
|
||||||
const { VITE_PORT, VITE_OPEN, VITE_PUBLIC_PATH, VITE_EDITOR } = loadEnv();
|
return {
|
||||||
|
base: isProd ? env.VITE_PUBLIC_PATH : './',
|
||||||
const isProd = process.env.NODE_ENV === 'production';
|
resolve: {
|
||||||
|
alias: {
|
||||||
const alias: Record<string, string> = {
|
'@': pathResolve('src/'),
|
||||||
'@': pathResolve('src/'),
|
|
||||||
};
|
|
||||||
|
|
||||||
const viteConfig: UserConfig = {
|
|
||||||
plugins: [
|
|
||||||
vue(),
|
|
||||||
tailwindcss(),
|
|
||||||
CodeInspectorPlugin({
|
|
||||||
bundler: 'vite',
|
|
||||||
editor: VITE_EDITOR as any,
|
|
||||||
}),
|
|
||||||
progress(),
|
|
||||||
],
|
|
||||||
root: process.cwd(),
|
|
||||||
resolve: {
|
|
||||||
alias,
|
|
||||||
},
|
|
||||||
base: isProd ? VITE_PUBLIC_PATH : './',
|
|
||||||
optimizeDeps: {
|
|
||||||
include: ['element-plus/es/locale/lang/zh-cn'],
|
|
||||||
},
|
|
||||||
server: {
|
|
||||||
host: '0.0.0.0',
|
|
||||||
port: VITE_PORT,
|
|
||||||
open: VITE_OPEN,
|
|
||||||
proxy: {
|
|
||||||
'/api': {
|
|
||||||
target: 'http://localhost:18888',
|
|
||||||
ws: true,
|
|
||||||
changeOrigin: true,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
plugins: [
|
||||||
build: {
|
vue(),
|
||||||
outDir: 'dist',
|
tailwindcss(),
|
||||||
chunkSizeWarningLimit: 1500,
|
CodeInspectorPlugin({
|
||||||
rollupOptions: {
|
bundler: 'vite',
|
||||||
output: {
|
editor: env.VITE_EDITOR as any,
|
||||||
entryFileNames: `assets/[hash]-[name].js`,
|
}),
|
||||||
chunkFileNames: `assets/[hash]-[name].js`,
|
progress(),
|
||||||
assetFileNames: `assets/[hash]-[name].[ext]`,
|
],
|
||||||
hashCharacters: 'hex',
|
optimizeDeps: {
|
||||||
advancedChunks: {
|
include: ['element-plus/es/locale/lang/zh-cn'],
|
||||||
groups: [
|
},
|
||||||
{ name: 'vue-vendor', test: /[\\/]node_modules[\\/](vue|@vue|vue-router|pinia)[\\/]/ },
|
server: {
|
||||||
{ name: 'echarts', test: /(echarts)/i },
|
host: '0.0.0.0',
|
||||||
{ name: 'monaco', test: /(monaco-editor)/i },
|
port: Number.parseInt(env.VITE_PORT) || 8889,
|
||||||
],
|
open: env.VITE_OPEN === 'true',
|
||||||
|
proxy: {
|
||||||
|
'/api': {
|
||||||
|
target: 'http://localhost:18888',
|
||||||
|
ws: true,
|
||||||
|
changeOrigin: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
build: {
|
||||||
define: {
|
outDir: 'dist',
|
||||||
__VUE_I18N_LEGACY_API__: JSON.stringify(false),
|
chunkSizeWarningLimit: 1500,
|
||||||
__VUE_I18N_FULL_INSTALL__: JSON.stringify(false),
|
rollupOptions: {
|
||||||
__INTLIFY_PROD_DEVTOOLS__: JSON.stringify(false),
|
output: {
|
||||||
},
|
entryFileNames: `assets/js/[hash]-[name].js`,
|
||||||
css: {
|
chunkFileNames: `assets/js/[hash]-[name].js`,
|
||||||
postcss: {
|
assetFileNames: `assets/[ext]/[hash]-[name].[ext]`,
|
||||||
plugins: [
|
hashCharacters: 'hex',
|
||||||
{
|
advancedChunks: {
|
||||||
postcssPlugin: 'internal:charset-removal',
|
groups: [
|
||||||
AtRule: {
|
{ name: 'vue-vendor', test: /[\\/]node_modules[\\/](vue|@vue|vue-router|pinia)[\\/]/ },
|
||||||
charset: (atRule) => {
|
{ name: 'charts', test: /[\\/]node_modules[\\/](echarts)[\\/]/ },
|
||||||
if (atRule.name === 'charset') {
|
{ name: 'monaco', test: /[\\/]node_modules[\\/]monaco-editor[\\/]/ },
|
||||||
atRule.remove();
|
],
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
},
|
||||||
},
|
},
|
||||||
},
|
define: {
|
||||||
};
|
__VUE_I18N_LEGACY_API__: JSON.stringify(false),
|
||||||
|
__VUE_I18N_FULL_INSTALL__: JSON.stringify(false),
|
||||||
export default viteConfig;
|
__INTLIFY_PROD_DEVTOOLS__: JSON.stringify(false),
|
||||||
|
},
|
||||||
|
css: {
|
||||||
|
postcss: {
|
||||||
|
plugins: [
|
||||||
|
{
|
||||||
|
postcssPlugin: 'internal:charset-removal',
|
||||||
|
AtRule: {
|
||||||
|
charset: (atRule) => {
|
||||||
|
if (atRule.name === 'charset') {
|
||||||
|
atRule.remove();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|||||||
@@ -10,16 +10,16 @@ require (
|
|||||||
github.com/gin-gonic/gin v1.10.1
|
github.com/gin-gonic/gin v1.10.1
|
||||||
github.com/glebarez/sqlite v1.11.0
|
github.com/glebarez/sqlite v1.11.0
|
||||||
github.com/go-gormigrate/gormigrate/v2 v2.1.4
|
github.com/go-gormigrate/gormigrate/v2 v2.1.4
|
||||||
github.com/go-ldap/ldap/v3 v3.4.8
|
github.com/go-ldap/ldap/v3 v3.4.11
|
||||||
github.com/go-playground/locales v0.14.1
|
github.com/go-playground/locales v0.14.1
|
||||||
github.com/go-playground/universal-translator v0.18.1
|
github.com/go-playground/universal-translator v0.18.1
|
||||||
github.com/go-playground/validator/v10 v10.26.0
|
github.com/go-playground/validator/v10 v10.27.0
|
||||||
github.com/go-sql-driver/mysql v1.9.3
|
github.com/go-sql-driver/mysql v1.9.3
|
||||||
github.com/golang-jwt/jwt/v5 v5.2.2
|
github.com/golang-jwt/jwt/v5 v5.2.3
|
||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.6.0
|
||||||
github.com/gorilla/websocket v1.5.3
|
github.com/gorilla/websocket v1.5.3
|
||||||
github.com/lionsoul2014/ip2region/binding/golang v0.0.0-20250508043914-ed57fa5c5274
|
github.com/lionsoul2014/ip2region/binding/golang v0.0.0-20250630080345-f9402614f6ba
|
||||||
github.com/microsoft/go-mssqldb v1.9.1
|
github.com/microsoft/go-mssqldb v1.9.2
|
||||||
github.com/mojocn/base64Captcha v1.3.8 // 验证码
|
github.com/mojocn/base64Captcha v1.3.8 // 验证码
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
github.com/pkg/sftp v1.13.9
|
github.com/pkg/sftp v1.13.9
|
||||||
@@ -32,33 +32,32 @@ require (
|
|||||||
github.com/tidwall/gjson v1.18.0
|
github.com/tidwall/gjson v1.18.0
|
||||||
github.com/veops/go-ansiterm v0.0.5
|
github.com/veops/go-ansiterm v0.0.5
|
||||||
go.mongodb.org/mongo-driver/v2 v2.2.2 // mongo
|
go.mongodb.org/mongo-driver/v2 v2.2.2 // mongo
|
||||||
golang.org/x/crypto v0.39.0 // ssh
|
golang.org/x/crypto v0.40.0 // ssh
|
||||||
golang.org/x/oauth2 v0.30.0
|
golang.org/x/oauth2 v0.30.0
|
||||||
golang.org/x/sync v0.15.0
|
golang.org/x/sync v0.16.0
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
// gorm
|
// gorm
|
||||||
gorm.io/driver/mysql v1.6.0
|
gorm.io/driver/mysql v1.6.0
|
||||||
gorm.io/gorm v1.30.0
|
gorm.io/gorm v1.30.1
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
filippo.io/edwards25519 v1.1.0 // indirect
|
filippo.io/edwards25519 v1.1.0 // indirect
|
||||||
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect
|
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect
|
||||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
|
github.com/boombuler/barcode v1.1.0 // indirect
|
||||||
github.com/bytedance/sonic v1.11.6 // indirect
|
github.com/bytedance/sonic v1.14.0 // indirect
|
||||||
github.com/bytedance/sonic/loader v0.1.1 // indirect
|
github.com/bytedance/sonic/loader v0.3.0 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
github.com/cloudwego/base64x v0.1.4 // indirect
|
github.com/cloudwego/base64x v0.1.5 // indirect
|
||||||
github.com/cloudwego/iasm v0.2.0 // indirect
|
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||||
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
|
github.com/gabriel-vasile/mimetype v1.4.9 // indirect
|
||||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
github.com/gin-contrib/sse v1.1.0 // indirect
|
||||||
github.com/glebarez/go-sqlite v1.21.2 // indirect
|
github.com/glebarez/go-sqlite v1.22.0 // indirect
|
||||||
github.com/go-asn1-ber/asn1-ber v1.5.5 // indirect
|
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 // indirect
|
||||||
github.com/goccy/go-json v0.10.2 // indirect
|
github.com/goccy/go-json v0.10.5 // indirect
|
||||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
|
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
|
||||||
github.com/golang-sql/sqlexp v0.1.0 // indirect
|
github.com/golang-sql/sqlexp v0.1.0 // indirect
|
||||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
|
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
|
||||||
@@ -66,15 +65,16 @@ require (
|
|||||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||||
github.com/jinzhu/now v1.1.5 // indirect
|
github.com/jinzhu/now v1.1.5 // indirect
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
github.com/klauspost/compress v1.16.7 // indirect
|
github.com/klauspost/compress v1.18.0 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
|
||||||
github.com/kr/fs v0.1.0 // indirect
|
github.com/kr/fs v0.1.0 // indirect
|
||||||
github.com/leodido/go-urn v1.4.0 // indirect
|
github.com/leodido/go-urn v1.4.0 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.16 // indirect
|
github.com/mattn/go-runewidth v0.0.16 // indirect
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||||
|
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||||
github.com/rivo/uniseg v0.4.7 // indirect
|
github.com/rivo/uniseg v0.4.7 // indirect
|
||||||
@@ -83,20 +83,20 @@ require (
|
|||||||
github.com/tidwall/pretty v1.2.1 // indirect
|
github.com/tidwall/pretty v1.2.1 // indirect
|
||||||
github.com/tjfoc/gmsm v1.4.1 // indirect
|
github.com/tjfoc/gmsm v1.4.1 // indirect
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
github.com/ugorji/go/codec v1.2.12 // indirect
|
github.com/ugorji/go/codec v1.3.0 // indirect
|
||||||
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||||
github.com/xdg-go/scram v1.1.2 // indirect
|
github.com/xdg-go/scram v1.1.2 // indirect
|
||||||
github.com/xdg-go/stringprep v1.0.4 // indirect
|
github.com/xdg-go/stringprep v1.0.4 // indirect
|
||||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
|
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
|
||||||
golang.org/x/arch v0.14.0 // indirect
|
golang.org/x/arch v0.19.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac // indirect
|
golang.org/x/exp v0.0.0-20250718183923-645b1fa84792 // indirect
|
||||||
golang.org/x/image v0.23.0 // indirect
|
golang.org/x/image v0.29.0 // indirect
|
||||||
golang.org/x/net v0.40.0 // indirect
|
golang.org/x/net v0.42.0 // indirect
|
||||||
golang.org/x/sys v0.33.0 // indirect
|
golang.org/x/sys v0.34.0 // indirect
|
||||||
golang.org/x/text v0.26.0 // indirect
|
golang.org/x/text v0.27.0 // indirect
|
||||||
google.golang.org/protobuf v1.34.1 // indirect
|
google.golang.org/protobuf v1.36.6 // indirect
|
||||||
modernc.org/libc v1.22.5 // indirect
|
modernc.org/libc v1.66.4 // indirect
|
||||||
modernc.org/mathutil v1.5.0 // indirect
|
modernc.org/mathutil v1.7.1 // indirect
|
||||||
modernc.org/memory v1.5.0 // indirect
|
modernc.org/memory v1.11.0 // indirect
|
||||||
modernc.org/sqlite v1.23.1 // indirect
|
modernc.org/sqlite v1.38.1 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ func (a *AccountLogin) ReqConfs() *req.Confs {
|
|||||||
|
|
||||||
// @router /auth/accounts/login [post]
|
// @router /auth/accounts/login [post]
|
||||||
func (a *AccountLogin) Login(rc *req.Ctx) {
|
func (a *AccountLogin) Login(rc *req.Ctx) {
|
||||||
loginForm := req.BindJsonAndValid[*form.LoginForm](rc)
|
loginForm := req.BindJson[*form.LoginForm](rc)
|
||||||
ctx := rc.MetaCtx
|
ctx := rc.MetaCtx
|
||||||
|
|
||||||
accountLoginSecurity := config.GetAccountLoginSecurity()
|
accountLoginSecurity := config.GetAccountLoginSecurity()
|
||||||
@@ -96,7 +96,7 @@ type OtpVerifyInfo struct {
|
|||||||
|
|
||||||
// OTP双因素校验
|
// OTP双因素校验
|
||||||
func (a *AccountLogin) OtpVerify(rc *req.Ctx) {
|
func (a *AccountLogin) OtpVerify(rc *req.Ctx) {
|
||||||
otpVerify := req.BindJsonAndValid[*form.OtpVerfiy](rc)
|
otpVerify := req.BindJson[*form.OtpVerfiy](rc)
|
||||||
ctx := rc.MetaCtx
|
ctx := rc.MetaCtx
|
||||||
|
|
||||||
tokenKey := fmt.Sprintf("otp:token:%s", otpVerify.OtpToken)
|
tokenKey := fmt.Sprintf("otp:token:%s", otpVerify.OtpToken)
|
||||||
|
|||||||
@@ -6,13 +6,13 @@ import (
|
|||||||
"mayfly-go/internal/auth/config"
|
"mayfly-go/internal/auth/config"
|
||||||
"mayfly-go/internal/auth/imsg"
|
"mayfly-go/internal/auth/imsg"
|
||||||
"mayfly-go/internal/auth/pkg/otp"
|
"mayfly-go/internal/auth/pkg/otp"
|
||||||
msgapp "mayfly-go/internal/msg/application"
|
msgdto "mayfly-go/internal/msg/application/dto"
|
||||||
msgentity "mayfly-go/internal/msg/domain/entity"
|
"mayfly-go/internal/pkg/event"
|
||||||
sysapp "mayfly-go/internal/sys/application"
|
sysapp "mayfly-go/internal/sys/application"
|
||||||
sysentity "mayfly-go/internal/sys/domain/entity"
|
sysentity "mayfly-go/internal/sys/domain/entity"
|
||||||
"mayfly-go/pkg/biz"
|
"mayfly-go/pkg/biz"
|
||||||
"mayfly-go/pkg/cache"
|
"mayfly-go/pkg/cache"
|
||||||
"mayfly-go/pkg/i18n"
|
"mayfly-go/pkg/global"
|
||||||
"mayfly-go/pkg/req"
|
"mayfly-go/pkg/req"
|
||||||
"mayfly-go/pkg/utils/collx"
|
"mayfly-go/pkg/utils/collx"
|
||||||
"mayfly-go/pkg/utils/netx"
|
"mayfly-go/pkg/utils/netx"
|
||||||
@@ -114,14 +114,12 @@ func saveLogin(ctx context.Context, account *sysentity.Account, ip string) {
|
|||||||
// 偷懒为了方便直接获取accountApp
|
// 偷懒为了方便直接获取accountApp
|
||||||
biz.ErrIsNil(sysapp.GetAccountApp().Update(context.TODO(), updateAccount))
|
biz.ErrIsNil(sysapp.GetAccountApp().Update(context.TODO(), updateAccount))
|
||||||
|
|
||||||
// 创建登录消息
|
global.EventBus.Publish(ctx, event.EventTopicMsgTmplSend, &msgdto.MsgTmplSendEvent{
|
||||||
loginMsg := &msgentity.Msg{
|
TmplChannel: msgdto.MsgTmplLogin,
|
||||||
RecipientId: int64(account.Id),
|
Params: collx.M{
|
||||||
Msg: i18n.TC(ctx, imsg.LoginMsg, "ip", ip, "time", timex.DefaultFormat(now)),
|
"ip": ip,
|
||||||
Type: 1,
|
"time": timex.DefaultFormat(now),
|
||||||
}
|
},
|
||||||
loginMsg.CreateTime = &now
|
ReceiverIds: []uint64{account.Id},
|
||||||
loginMsg.Creator = account.Username
|
})
|
||||||
loginMsg.CreatorId = account.Id
|
|
||||||
msgapp.GetMsgApp().Create(context.TODO(), loginMsg)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ func (a *LdapLogin) GetLdapEnabled(rc *req.Ctx) {
|
|||||||
|
|
||||||
// @router /auth/ldap/login [post]
|
// @router /auth/ldap/login [post]
|
||||||
func (a *LdapLogin) Login(rc *req.Ctx) {
|
func (a *LdapLogin) Login(rc *req.Ctx) {
|
||||||
loginForm := req.BindJsonAndValid[*form.LoginForm](rc)
|
loginForm := req.BindJson[*form.LoginForm](rc)
|
||||||
ctx := rc.MetaCtx
|
ctx := rc.MetaCtx
|
||||||
accountLoginSecurity := config.GetAccountLoginSecurity()
|
accountLoginSecurity := config.GetAccountLoginSecurity()
|
||||||
// 判断是否有开启登录验证码校验
|
// 判断是否有开启登录验证码校验
|
||||||
@@ -197,14 +197,14 @@ func dial(ldapConf *config.LdapLogin) (*ldap.Conn, error) {
|
|||||||
InsecureSkipVerify: ldapConf.SkipTLSVerify,
|
InsecureSkipVerify: ldapConf.SkipTLSVerify,
|
||||||
}
|
}
|
||||||
if ldapConf.SecurityProtocol == "LDAPS" {
|
if ldapConf.SecurityProtocol == "LDAPS" {
|
||||||
conn, err := ldap.DialTLS("tcp", addr, tlsConfig)
|
conn, err := ldap.DialURL("ldaps://"+addr, ldap.DialWithTLSConfig(tlsConfig))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Errorf("dial TLS: %v", err)
|
return nil, errors.Errorf("dial TLS: %v", err)
|
||||||
}
|
}
|
||||||
return conn, nil
|
return conn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
conn, err := ldap.Dial("tcp", addr)
|
conn, err := ldap.DialURL("ldap://" + addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Errorf("dial: %v", err)
|
return nil, errors.Errorf("dial: %v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ var En = map[i18n.MsgId]string{
|
|||||||
ErrOtpCheckRestrict: "Two-factor validation failed more than 5 times. Try again in 10 minutes",
|
ErrOtpCheckRestrict: "Two-factor validation failed more than 5 times. Try again in 10 minutes",
|
||||||
ErrOtpCheckFail: "Two-factor authentication authorization code is incorrect",
|
ErrOtpCheckFail: "Two-factor authentication authorization code is incorrect",
|
||||||
ErrAccountNotAvailable: "Account is not available",
|
ErrAccountNotAvailable: "Account is not available",
|
||||||
LoginMsg: "Log in to [{{.ip}}]-[{{.time}}]",
|
|
||||||
ErrUsernameOrPwdErr: "Wrong username or password",
|
ErrUsernameOrPwdErr: "Wrong username or password",
|
||||||
ErrOauth2NoAutoRegister: "the system does not enable automatic registration, please ask the administrator to add the corresponding account first",
|
ErrOauth2NoAutoRegister: "the system does not enable automatic registration, please ask the administrator to add the corresponding account first",
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ const (
|
|||||||
ErrOtpCheckRestrict
|
ErrOtpCheckRestrict
|
||||||
ErrOtpCheckFail
|
ErrOtpCheckFail
|
||||||
ErrAccountNotAvailable
|
ErrAccountNotAvailable
|
||||||
LoginMsg
|
|
||||||
ErrUsernameOrPwdErr
|
ErrUsernameOrPwdErr
|
||||||
ErrOauth2NoAutoRegister
|
ErrOauth2NoAutoRegister
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ var Zh_CN = map[i18n.MsgId]string{
|
|||||||
ErrOtpCheckRestrict: "双因素校验失败超过5次, 请10分钟后再试",
|
ErrOtpCheckRestrict: "双因素校验失败超过5次, 请10分钟后再试",
|
||||||
ErrOtpCheckFail: "双因素认证授权码不正确",
|
ErrOtpCheckFail: "双因素认证授权码不正确",
|
||||||
ErrAccountNotAvailable: "账号不可用",
|
ErrAccountNotAvailable: "账号不可用",
|
||||||
LoginMsg: "于[{{.ip}}]-[{{.time}}]登录",
|
|
||||||
ErrUsernameOrPwdErr: "用户名或密码错误",
|
ErrUsernameOrPwdErr: "用户名或密码错误",
|
||||||
ErrOauth2NoAutoRegister: "系统未开启自动注册, 请先让管理员添加对应账号",
|
ErrOauth2NoAutoRegister: "系统未开启自动注册, 请先让管理员添加对应账号",
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"mayfly-go/initialize"
|
"mayfly-go/initialize"
|
||||||
"mayfly-go/internal/auth/api"
|
"mayfly-go/internal/auth/api"
|
||||||
"mayfly-go/internal/auth/application"
|
"mayfly-go/internal/auth/application"
|
||||||
"mayfly-go/internal/auth/infrastructure/persistence"
|
"mayfly-go/internal/auth/infra/persistence"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|||||||
@@ -11,15 +11,13 @@ import (
|
|||||||
"mayfly-go/internal/db/dbm/dbi"
|
"mayfly-go/internal/db/dbm/dbi"
|
||||||
"mayfly-go/internal/db/domain/entity"
|
"mayfly-go/internal/db/domain/entity"
|
||||||
"mayfly-go/internal/db/imsg"
|
"mayfly-go/internal/db/imsg"
|
||||||
"mayfly-go/internal/event"
|
|
||||||
msgapp "mayfly-go/internal/msg/application"
|
|
||||||
msgdto "mayfly-go/internal/msg/application/dto"
|
msgdto "mayfly-go/internal/msg/application/dto"
|
||||||
|
"mayfly-go/internal/pkg/event"
|
||||||
"mayfly-go/internal/pkg/utils"
|
"mayfly-go/internal/pkg/utils"
|
||||||
tagapp "mayfly-go/internal/tag/application"
|
tagapp "mayfly-go/internal/tag/application"
|
||||||
tagentity "mayfly-go/internal/tag/domain/entity"
|
tagentity "mayfly-go/internal/tag/domain/entity"
|
||||||
"mayfly-go/pkg/biz"
|
"mayfly-go/pkg/biz"
|
||||||
"mayfly-go/pkg/global"
|
"mayfly-go/pkg/global"
|
||||||
"mayfly-go/pkg/i18n"
|
|
||||||
"mayfly-go/pkg/logx"
|
"mayfly-go/pkg/logx"
|
||||||
"mayfly-go/pkg/model"
|
"mayfly-go/pkg/model"
|
||||||
"mayfly-go/pkg/req"
|
"mayfly-go/pkg/req"
|
||||||
@@ -36,7 +34,6 @@ type Db struct {
|
|||||||
instanceApp application.Instance `inject:"T"`
|
instanceApp application.Instance `inject:"T"`
|
||||||
dbApp application.Db `inject:"T"`
|
dbApp application.Db `inject:"T"`
|
||||||
dbSqlExecApp application.DbSqlExec `inject:"T"`
|
dbSqlExecApp application.DbSqlExec `inject:"T"`
|
||||||
msgApp msgapp.Msg `inject:"T"`
|
|
||||||
tagApp tagapp.TagTree `inject:"T"`
|
tagApp tagapp.TagTree `inject:"T"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,7 +132,7 @@ func (d *Db) DeleteDb(rc *req.Ctx) {
|
|||||||
/** 数据库操作相关、执行sql等 ***/
|
/** 数据库操作相关、执行sql等 ***/
|
||||||
|
|
||||||
func (d *Db) ExecSql(rc *req.Ctx) {
|
func (d *Db) ExecSql(rc *req.Ctx) {
|
||||||
form := req.BindJsonAndValid[*form.DbSqlExecForm](rc)
|
form := req.BindJson[*form.DbSqlExecForm](rc)
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(rc.MetaCtx, time.Duration(config.GetDbms().SqlExecTl)*time.Second)
|
ctx, cancel := context.WithTimeout(rc.MetaCtx, time.Duration(config.GetDbms().SqlExecTl)*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
@@ -167,17 +164,6 @@ func (d *Db) ExecSql(rc *req.Ctx) {
|
|||||||
rc.ResData = execRes
|
rc.ResData = execRes
|
||||||
}
|
}
|
||||||
|
|
||||||
// progressCategory sql文件执行进度消息类型
|
|
||||||
const progressCategory = "execSqlFileProgress"
|
|
||||||
|
|
||||||
// progressMsg sql文件执行进度消息
|
|
||||||
type progressMsg struct {
|
|
||||||
Id string `json:"id"`
|
|
||||||
Title string `json:"title"`
|
|
||||||
ExecutedStatements int `json:"executedStatements"`
|
|
||||||
Terminated bool `json:"terminated"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// 执行sql文件
|
// 执行sql文件
|
||||||
func (d *Db) ExecSqlFile(rc *req.Ctx) {
|
func (d *Db) ExecSqlFile(rc *req.Ctx) {
|
||||||
multipart, err := rc.GetRequest().MultipartReader()
|
multipart, err := rc.GetRequest().MultipartReader()
|
||||||
@@ -246,7 +232,11 @@ func (d *Db) DumpSql(rc *req.Ctx) {
|
|||||||
if len(msg) > 0 {
|
if len(msg) > 0 {
|
||||||
msg = "DB dump error: " + msg
|
msg = "DB dump error: " + msg
|
||||||
rc.GetWriter().Write([]byte(msg))
|
rc.GetWriter().Write([]byte(msg))
|
||||||
d.msgApp.CreateAndSend(la, msgdto.ErrSysMsg(i18n.T(imsg.DbDumpErr), msg))
|
global.EventBus.Publish(rc.MetaCtx, event.EventTopicMsgTmplSend, &msgdto.MsgTmplSendEvent{
|
||||||
|
TmplChannel: msgdto.MsgTmplDbDumpFail,
|
||||||
|
Params: collx.M{"error": msg},
|
||||||
|
ReceiverIds: []uint64{la.Id},
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ func (d *DataSyncTask) DeleteTask(rc *req.Ctx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *DataSyncTask) ChangeStatus(rc *req.Ctx) {
|
func (d *DataSyncTask) ChangeStatus(rc *req.Ctx) {
|
||||||
form := req.BindJsonAndValid[*form.DataSyncTaskStatusForm](rc)
|
form := req.BindJson[*form.DataSyncTaskStatusForm](rc)
|
||||||
rc.ReqParam = form
|
rc.ReqParam = form
|
||||||
|
|
||||||
task, err := d.dataSyncTaskApp.GetById(form.Id)
|
task, err := d.dataSyncTaskApp.GetById(form.Id)
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ func (d *DbSql) ReqConfs() *req.Confs {
|
|||||||
|
|
||||||
// @router /api/db/:dbId/sql [post]
|
// @router /api/db/:dbId/sql [post]
|
||||||
func (d *DbSql) SaveSql(rc *req.Ctx) {
|
func (d *DbSql) SaveSql(rc *req.Ctx) {
|
||||||
dbSqlForm := req.BindJsonAndValid[*form.DbSqlSaveForm](rc)
|
dbSqlForm := req.BindJson[*form.DbSqlSaveForm](rc)
|
||||||
rc.ReqParam = dbSqlForm
|
rc.ReqParam = dbSqlForm
|
||||||
|
|
||||||
dbId := getDbId(rc)
|
dbId := getDbId(rc)
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ func (d *DbTransferTask) DeleteTask(rc *req.Ctx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *DbTransferTask) ChangeStatus(rc *req.Ctx) {
|
func (d *DbTransferTask) ChangeStatus(rc *req.Ctx) {
|
||||||
form := req.BindJsonAndValid[*form.DbTransferTaskStatusForm](rc)
|
form := req.BindJson[*form.DbTransferTaskStatusForm](rc)
|
||||||
rc.ReqParam = form
|
rc.ReqParam = form
|
||||||
|
|
||||||
task, err := d.dbTransferTaskApp.GetById(form.Id)
|
task, err := d.dbTransferTaskApp.GetById(form.Id)
|
||||||
@@ -136,7 +136,7 @@ func (d *DbTransferTask) FileDel(rc *req.Ctx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *DbTransferTask) FileRun(rc *req.Ctx) {
|
func (d *DbTransferTask) FileRun(rc *req.Ctx) {
|
||||||
fm := req.BindJsonAndValid[*form.DbTransferFileRunForm](rc)
|
fm := req.BindJson[*form.DbTransferFileRunForm](rc)
|
||||||
|
|
||||||
rc.ReqParam = fm
|
rc.ReqParam = fm
|
||||||
|
|
||||||
|
|||||||
@@ -13,19 +13,19 @@ import (
|
|||||||
"mayfly-go/internal/db/imsg"
|
"mayfly-go/internal/db/imsg"
|
||||||
flowapp "mayfly-go/internal/flow/application"
|
flowapp "mayfly-go/internal/flow/application"
|
||||||
flowentity "mayfly-go/internal/flow/domain/entity"
|
flowentity "mayfly-go/internal/flow/domain/entity"
|
||||||
msgapp "mayfly-go/internal/msg/application"
|
|
||||||
msgdto "mayfly-go/internal/msg/application/dto"
|
msgdto "mayfly-go/internal/msg/application/dto"
|
||||||
|
"mayfly-go/internal/pkg/event"
|
||||||
"mayfly-go/pkg/contextx"
|
"mayfly-go/pkg/contextx"
|
||||||
"mayfly-go/pkg/errorx"
|
"mayfly-go/pkg/errorx"
|
||||||
"mayfly-go/pkg/i18n"
|
"mayfly-go/pkg/global"
|
||||||
"mayfly-go/pkg/logx"
|
"mayfly-go/pkg/logx"
|
||||||
"mayfly-go/pkg/model"
|
"mayfly-go/pkg/model"
|
||||||
"mayfly-go/pkg/utils/anyx"
|
"mayfly-go/pkg/utils/anyx"
|
||||||
"mayfly-go/pkg/utils/collx"
|
"mayfly-go/pkg/utils/collx"
|
||||||
"mayfly-go/pkg/utils/jsonx"
|
"mayfly-go/pkg/utils/jsonx"
|
||||||
"mayfly-go/pkg/utils/stringx"
|
"mayfly-go/pkg/utils/stringx"
|
||||||
"mayfly-go/pkg/ws"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type sqlExecParam struct {
|
type sqlExecParam struct {
|
||||||
@@ -71,7 +71,6 @@ type dbSqlExecAppImpl struct {
|
|||||||
dbSqlExecRepo repository.DbSqlExec `inject:"T"`
|
dbSqlExecRepo repository.DbSqlExec `inject:"T"`
|
||||||
|
|
||||||
flowProcdefApp flowapp.Procdef `inject:"T"`
|
flowProcdefApp flowapp.Procdef `inject:"T"`
|
||||||
msgApp msgapp.Msg `inject:"T"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func createSqlExecRecord(ctx context.Context, execSqlReq *dto.DbSqlExecReq, sql string) *entity.DbSqlExec {
|
func createSqlExecRecord(ctx context.Context, execSqlReq *dto.DbSqlExecReq, sql string) *entity.DbSqlExec {
|
||||||
@@ -206,38 +205,55 @@ func (d *dbSqlExecAppImpl) ExecReader(ctx context.Context, execReader *dto.SqlRe
|
|||||||
la := contextx.GetLoginAccount(ctx)
|
la := contextx.GetLoginAccount(ctx)
|
||||||
needSendMsg := la != nil && clientId != ""
|
needSendMsg := la != nil && clientId != ""
|
||||||
|
|
||||||
|
startTime := time.Now()
|
||||||
|
executedStatements := 0
|
||||||
|
progressId := stringx.Rand(32)
|
||||||
|
|
||||||
|
msgEvent := &msgdto.MsgTmplSendEvent{
|
||||||
|
TmplChannel: msgdto.MsgTmplSqlScriptRunSuccess,
|
||||||
|
Params: collx.M{"filename": filename, "db": dbConn.Info.GetLogDesc()},
|
||||||
|
}
|
||||||
|
|
||||||
|
progressMsgEvent := &msgdto.MsgTmplSendEvent{
|
||||||
|
TmplChannel: msgdto.MsgTmplSqlScriptRunProgress,
|
||||||
|
Params: collx.M{
|
||||||
|
"id": progressId,
|
||||||
|
"title": filename,
|
||||||
|
"executedStatements": executedStatements,
|
||||||
|
"terminated": false,
|
||||||
|
"clientId": clientId,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if needSendMsg {
|
||||||
|
msgEvent.ReceiverIds = []uint64{la.Id}
|
||||||
|
progressMsgEvent.ReceiverIds = []uint64{la.Id}
|
||||||
|
}
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
|
if needSendMsg {
|
||||||
|
progressMsgEvent.Params["terminated"] = true
|
||||||
|
global.EventBus.Publish(ctx, event.EventTopicMsgTmplSend, progressMsgEvent)
|
||||||
|
}
|
||||||
|
|
||||||
if err := recover(); err != nil {
|
if err := recover(); err != nil {
|
||||||
errInfo := anyx.ToString(err)
|
errInfo := anyx.ToString(err)
|
||||||
logx.Errorf("exec sql reader error: %s", errInfo)
|
logx.Errorf("exec sql reader error: %s", errInfo)
|
||||||
if needSendMsg {
|
if needSendMsg {
|
||||||
errInfo = stringx.Truncate(errInfo, 300, 10, "...")
|
errInfo = stringx.Truncate(errInfo, 300, 10, "...")
|
||||||
d.msgApp.CreateAndSend(la, msgdto.ErrSysMsg(i18n.T(imsg.SqlScriptRunFail), fmt.Sprintf("[%s][%s] execution failure: [%s]", filename, dbConn.Info.GetLogDesc(), errInfo)).WithClientId(clientId))
|
msgEvent.TmplChannel = msgdto.MsgTmplSqlScriptRunFail
|
||||||
|
msgEvent.Params["error"] = errInfo
|
||||||
|
global.EventBus.Publish(ctx, event.EventTopicMsgTmplSend, msgEvent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
executedStatements := 0
|
|
||||||
progressId := stringx.Rand(32)
|
|
||||||
if needSendMsg {
|
|
||||||
defer ws.SendJsonMsg(ws.UserId(la.Id), clientId, msgdto.InfoSysMsg(i18n.T(imsg.SqlScripRunProgress), &progressMsg{
|
|
||||||
Id: progressId,
|
|
||||||
Title: filename,
|
|
||||||
ExecutedStatements: executedStatements,
|
|
||||||
Terminated: true,
|
|
||||||
}).WithCategory(progressCategory))
|
|
||||||
}
|
|
||||||
|
|
||||||
tx, _ := dbConn.Begin()
|
tx, _ := dbConn.Begin()
|
||||||
err := sqlparser.SQLSplit(execReader.Reader, ';', func(sql string) error {
|
err := sqlparser.SQLSplit(execReader.Reader, ';', func(sql string) error {
|
||||||
if executedStatements%50 == 0 {
|
if executedStatements%50 == 0 {
|
||||||
if needSendMsg {
|
if needSendMsg {
|
||||||
ws.SendJsonMsg(ws.UserId(la.Id), clientId, msgdto.InfoSysMsg(i18n.T(imsg.SqlScripRunProgress), &progressMsg{
|
progressMsgEvent.Params["executedStatements"] = executedStatements
|
||||||
Id: progressId,
|
global.EventBus.Publish(ctx, event.EventTopicMsgTmplSend, progressMsgEvent)
|
||||||
Title: filename,
|
|
||||||
ExecutedStatements: executedStatements,
|
|
||||||
Terminated: false,
|
|
||||||
}).WithCategory(progressCategory))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -249,12 +265,18 @@ func (d *dbSqlExecAppImpl) ExecReader(ctx context.Context, execReader *dto.SqlRe
|
|||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = tx.Rollback()
|
_ = tx.Rollback()
|
||||||
|
if needSendMsg {
|
||||||
|
msgEvent.TmplChannel = msgdto.MsgTmplSqlScriptRunFail
|
||||||
|
msgEvent.Params["error"] = err.Error()
|
||||||
|
global.EventBus.Publish(ctx, event.EventTopicMsgTmplSend, msgEvent)
|
||||||
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
_ = tx.Commit()
|
_ = tx.Commit()
|
||||||
|
|
||||||
if needSendMsg {
|
if needSendMsg {
|
||||||
d.msgApp.CreateAndSend(la, msgdto.SuccessSysMsg(i18n.T(imsg.SqlScriptRunSuccess), "execution success").WithClientId(clientId))
|
msgEvent.Params["cost"] = fmt.Sprintf("%dms", time.Since(startTime).Milliseconds())
|
||||||
|
global.EventBus.Publish(ctx, event.EventTopicMsgTmplSend, msgEvent)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,10 +14,7 @@ var En = map[i18n.MsgId]string{
|
|||||||
LogDbRunSql: "DB - Run SQL",
|
LogDbRunSql: "DB - Run SQL",
|
||||||
LogDbDump: "DB - Export DB",
|
LogDbDump: "DB - Export DB",
|
||||||
|
|
||||||
SqlScriptRunFail: "sql script failed to execute",
|
|
||||||
SqlScriptRunSuccess: "sql script executed successfully",
|
|
||||||
SqlScripRunProgress: "sql execution progress",
|
SqlScripRunProgress: "sql execution progress",
|
||||||
DbDumpErr: "Database export failed",
|
|
||||||
ErrDbNameExist: "The database name already exists in this instance",
|
ErrDbNameExist: "The database name already exists in this instance",
|
||||||
ErrDbNotAccess: "The operation permissions of database [{{.dbName}}] are not configured",
|
ErrDbNotAccess: "The operation permissions of database [{{.dbName}}] are not configured",
|
||||||
|
|
||||||
|
|||||||
@@ -24,10 +24,7 @@ const (
|
|||||||
LogDbRunSqlFile
|
LogDbRunSqlFile
|
||||||
LogDbDump
|
LogDbDump
|
||||||
|
|
||||||
SqlScriptRunFail
|
|
||||||
SqlScriptRunSuccess
|
|
||||||
SqlScripRunProgress
|
SqlScripRunProgress
|
||||||
DbDumpErr
|
|
||||||
ErrDbNameExist
|
ErrDbNameExist
|
||||||
ErrDbNotAccess
|
ErrDbNotAccess
|
||||||
|
|
||||||
|
|||||||
@@ -14,10 +14,7 @@ var Zh_CN = map[i18n.MsgId]string{
|
|||||||
LogDbRunSql: "DB-运行SQL",
|
LogDbRunSql: "DB-运行SQL",
|
||||||
LogDbDump: "DB-导出数据库",
|
LogDbDump: "DB-导出数据库",
|
||||||
|
|
||||||
SqlScriptRunFail: "sql脚本执行失败",
|
|
||||||
SqlScriptRunSuccess: "sql脚本执行成功",
|
|
||||||
SqlScripRunProgress: "sql执行进度",
|
SqlScripRunProgress: "sql执行进度",
|
||||||
DbDumpErr: "数据库导出失败",
|
|
||||||
ErrDbNameExist: "该实例下数据库名已存在",
|
ErrDbNameExist: "该实例下数据库名已存在",
|
||||||
ErrDbNotAccess: "未配置数据库【{{.dbName}}】的操作权限",
|
ErrDbNotAccess: "未配置数据库【{{.dbName}}】的操作权限",
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"mayfly-go/initialize"
|
"mayfly-go/initialize"
|
||||||
"mayfly-go/internal/db/api"
|
"mayfly-go/internal/db/api"
|
||||||
"mayfly-go/internal/db/application"
|
"mayfly-go/internal/db/application"
|
||||||
"mayfly-go/internal/db/infrastructure/persistence"
|
"mayfly-go/internal/db/infra/persistence"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"mayfly-go/initialize"
|
"mayfly-go/initialize"
|
||||||
"mayfly-go/internal/es/api"
|
"mayfly-go/internal/es/api"
|
||||||
"mayfly-go/internal/es/application"
|
"mayfly-go/internal/es/application"
|
||||||
"mayfly-go/internal/es/infrastructure/persistence"
|
"mayfly-go/internal/es/infra/persistence"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
package persistence
|
|
||||||
|
|
||||||
import (
|
|
||||||
"mayfly-go/internal/file/domain/entity"
|
|
||||||
"mayfly-go/internal/file/domain/repository"
|
|
||||||
"mayfly-go/pkg/base"
|
|
||||||
)
|
|
||||||
|
|
||||||
type fileRepoImpl struct {
|
|
||||||
base.RepoImpl[*entity.File]
|
|
||||||
}
|
|
||||||
|
|
||||||
func newFileRepo() repository.File {
|
|
||||||
return &fileRepoImpl{}
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
package persistence
|
|
||||||
|
|
||||||
import (
|
|
||||||
"mayfly-go/pkg/ioc"
|
|
||||||
)
|
|
||||||
|
|
||||||
func InitIoc() {
|
|
||||||
ioc.Register(newFileRepo(), ioc.WithComponentName("FileRepo"))
|
|
||||||
}
|
|
||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"mayfly-go/initialize"
|
"mayfly-go/initialize"
|
||||||
"mayfly-go/internal/file/api"
|
"mayfly-go/internal/file/api"
|
||||||
"mayfly-go/internal/file/application"
|
"mayfly-go/internal/file/application"
|
||||||
"mayfly-go/internal/file/infrastructure/persistence"
|
"mayfly-go/internal/file/infra/persistence"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ func (a *Procdef) Save(rc *req.Ctx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *Procdef) SaveFlowDef(rc *req.Ctx) {
|
func (a *Procdef) SaveFlowDef(rc *req.Ctx) {
|
||||||
form := req.BindJsonAndValid[*form.ProcdefFlow](rc)
|
form := req.BindJson[*form.ProcdefFlow](rc)
|
||||||
rc.ReqParam = form
|
rc.ReqParam = form
|
||||||
|
|
||||||
biz.ErrIsNil(a.procdefApp.SaveFlowDef(rc.MetaCtx, &dto.SaveFlowDef{
|
biz.ErrIsNil(a.procdefApp.SaveFlowDef(rc.MetaCtx, &dto.SaveFlowDef{
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ func (p *Procinst) GetProcinstPage(rc *req.Ctx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *Procinst) ProcinstStart(rc *req.Ctx) {
|
func (p *Procinst) ProcinstStart(rc *req.Ctx) {
|
||||||
startForm := req.BindJsonAndValid[*form.ProcinstStart](rc)
|
startForm := req.BindJson[*form.ProcinstStart](rc)
|
||||||
_, err := p.procinstApp.StartProc(rc.MetaCtx, startForm.ProcdefId, &dto.StarProc{
|
_, err := p.procinstApp.StartProc(rc.MetaCtx, startForm.ProcdefId, &dto.StarProc{
|
||||||
BizType: startForm.BizType,
|
BizType: startForm.BizType,
|
||||||
BizForm: jsonx.ToStr(startForm.BizForm),
|
BizForm: jsonx.ToStr(startForm.BizForm),
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ func (p *ProcinstTask) GetTasks(rc *req.Ctx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *ProcinstTask) PassTask(rc *req.Ctx) {
|
func (p *ProcinstTask) PassTask(rc *req.Ctx) {
|
||||||
auditForm := req.BindJsonAndValid[*form.ProcinstTaskAudit](rc)
|
auditForm := req.BindJson[*form.ProcinstTaskAudit](rc)
|
||||||
rc.ReqParam = auditForm
|
rc.ReqParam = auditForm
|
||||||
|
|
||||||
la := rc.GetLoginAccount()
|
la := rc.GetLoginAccount()
|
||||||
@@ -84,7 +84,7 @@ func (p *ProcinstTask) PassTask(rc *req.Ctx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *ProcinstTask) RejectTask(rc *req.Ctx) {
|
func (p *ProcinstTask) RejectTask(rc *req.Ctx) {
|
||||||
auditForm := req.BindJsonAndValid[*form.ProcinstTaskAudit](rc)
|
auditForm := req.BindJson[*form.ProcinstTaskAudit](rc)
|
||||||
rc.ReqParam = auditForm
|
rc.ReqParam = auditForm
|
||||||
|
|
||||||
la := rc.GetLoginAccount()
|
la := rc.GetLoginAccount()
|
||||||
@@ -94,7 +94,7 @@ func (p *ProcinstTask) RejectTask(rc *req.Ctx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *ProcinstTask) BackTask(rc *req.Ctx) {
|
func (p *ProcinstTask) BackTask(rc *req.Ctx) {
|
||||||
auditForm := req.BindJsonAndValid[*form.ProcinstTaskAudit](rc)
|
auditForm := req.BindJson[*form.ProcinstTaskAudit](rc)
|
||||||
rc.ReqParam = auditForm
|
rc.ReqParam = auditForm
|
||||||
biz.ErrIsNil(p.procinstTaskApp.BackTask(rc.MetaCtx, dto.UserTaskOp{TaskId: auditForm.Id, Remark: auditForm.Remark}))
|
biz.ErrIsNil(p.procinstTaskApp.BackTask(rc.MetaCtx, dto.UserTaskOp{TaskId: auditForm.Id, Remark: auditForm.Remark}))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,11 +2,11 @@ package application
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"mayfly-go/internal/event"
|
|
||||||
"mayfly-go/internal/flow/domain/entity"
|
"mayfly-go/internal/flow/domain/entity"
|
||||||
"mayfly-go/internal/flow/imsg"
|
"mayfly-go/internal/flow/imsg"
|
||||||
"mayfly-go/internal/flow/infrastructure/persistence"
|
"mayfly-go/internal/flow/infra/persistence"
|
||||||
msgdto "mayfly-go/internal/msg/application/dto"
|
msgdto "mayfly-go/internal/msg/application/dto"
|
||||||
|
"mayfly-go/internal/pkg/event"
|
||||||
"mayfly-go/pkg/errorx"
|
"mayfly-go/pkg/errorx"
|
||||||
"mayfly-go/pkg/global"
|
"mayfly-go/pkg/global"
|
||||||
"strings"
|
"strings"
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"mayfly-go/initialize"
|
"mayfly-go/initialize"
|
||||||
"mayfly-go/internal/flow/api"
|
"mayfly-go/internal/flow/api"
|
||||||
"mayfly-go/internal/flow/application"
|
"mayfly-go/internal/flow/application"
|
||||||
"mayfly-go/internal/flow/infrastructure/persistence"
|
"mayfly-go/internal/flow/infra/persistence"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"mayfly-go/internal/event"
|
|
||||||
"mayfly-go/internal/machine/api/form"
|
"mayfly-go/internal/machine/api/form"
|
||||||
"mayfly-go/internal/machine/api/vo"
|
"mayfly-go/internal/machine/api/vo"
|
||||||
"mayfly-go/internal/machine/application"
|
"mayfly-go/internal/machine/application"
|
||||||
@@ -12,6 +11,7 @@ import (
|
|||||||
"mayfly-go/internal/machine/imsg"
|
"mayfly-go/internal/machine/imsg"
|
||||||
"mayfly-go/internal/machine/mcm"
|
"mayfly-go/internal/machine/mcm"
|
||||||
"mayfly-go/internal/pkg/consts"
|
"mayfly-go/internal/pkg/consts"
|
||||||
|
"mayfly-go/internal/pkg/event"
|
||||||
tagapp "mayfly-go/internal/tag/application"
|
tagapp "mayfly-go/internal/tag/application"
|
||||||
tagentity "mayfly-go/internal/tag/domain/entity"
|
tagentity "mayfly-go/internal/tag/domain/entity"
|
||||||
"mayfly-go/pkg/biz"
|
"mayfly-go/pkg/biz"
|
||||||
|
|||||||
@@ -12,15 +12,14 @@ import (
|
|||||||
"mayfly-go/internal/machine/domain/entity"
|
"mayfly-go/internal/machine/domain/entity"
|
||||||
"mayfly-go/internal/machine/imsg"
|
"mayfly-go/internal/machine/imsg"
|
||||||
"mayfly-go/internal/machine/mcm"
|
"mayfly-go/internal/machine/mcm"
|
||||||
msgapp "mayfly-go/internal/msg/application"
|
|
||||||
msgdto "mayfly-go/internal/msg/application/dto"
|
msgdto "mayfly-go/internal/msg/application/dto"
|
||||||
|
"mayfly-go/internal/pkg/event"
|
||||||
"mayfly-go/pkg/biz"
|
"mayfly-go/pkg/biz"
|
||||||
"mayfly-go/pkg/errorx"
|
"mayfly-go/pkg/errorx"
|
||||||
"mayfly-go/pkg/i18n"
|
"mayfly-go/pkg/global"
|
||||||
"mayfly-go/pkg/logx"
|
"mayfly-go/pkg/logx"
|
||||||
"mayfly-go/pkg/model"
|
"mayfly-go/pkg/model"
|
||||||
"mayfly-go/pkg/req"
|
"mayfly-go/pkg/req"
|
||||||
"mayfly-go/pkg/utils/anyx"
|
|
||||||
"mayfly-go/pkg/utils/collx"
|
"mayfly-go/pkg/utils/collx"
|
||||||
"mayfly-go/pkg/utils/timex"
|
"mayfly-go/pkg/utils/timex"
|
||||||
"mime/multipart"
|
"mime/multipart"
|
||||||
@@ -36,7 +35,6 @@ import (
|
|||||||
|
|
||||||
type MachineFile struct {
|
type MachineFile struct {
|
||||||
machineFileApp application.MachineFile `inject:"T"`
|
machineFileApp application.MachineFile `inject:"T"`
|
||||||
msgApp msgapp.Msg `inject:"T"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mf *MachineFile) ReqConfs() *req.Confs {
|
func (mf *MachineFile) ReqConfs() *req.Confs {
|
||||||
@@ -106,7 +104,7 @@ func (m *MachineFile) DeleteFile(rc *req.Ctx) {
|
|||||||
/*** sftp相关操作 */
|
/*** sftp相关操作 */
|
||||||
|
|
||||||
func (m *MachineFile) CreateFile(rc *req.Ctx) {
|
func (m *MachineFile) CreateFile(rc *req.Ctx) {
|
||||||
opForm := req.BindJsonAndValid[*form.CreateFileForm](rc)
|
opForm := req.BindJson[*form.CreateFileForm](rc)
|
||||||
path := opForm.Path
|
path := opForm.Path
|
||||||
|
|
||||||
attrs := collx.Kvs("path", path)
|
attrs := collx.Kvs("path", path)
|
||||||
@@ -239,7 +237,7 @@ func (m *MachineFile) GetFileStat(rc *req.Ctx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *MachineFile) WriteFileContent(rc *req.Ctx) {
|
func (m *MachineFile) WriteFileContent(rc *req.Ctx) {
|
||||||
opForm := req.BindJsonAndValid[*form.WriteFileContentForm](rc)
|
opForm := req.BindJson[*form.WriteFileContentForm](rc)
|
||||||
path := opForm.Path
|
path := opForm.Path
|
||||||
|
|
||||||
mi, err := m.machineFileApp.WriteFileContent(rc.MetaCtx, opForm.MachineFileOp, []byte(opForm.Content))
|
mi, err := m.machineFileApp.WriteFileContent(rc.MetaCtx, opForm.MachineFileOp, []byte(opForm.Content))
|
||||||
@@ -264,14 +262,6 @@ func (m *MachineFile) UploadFile(rc *req.Ctx) {
|
|||||||
file, _ := fileheader.Open()
|
file, _ := fileheader.Open()
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
|
|
||||||
la := rc.GetLoginAccount()
|
|
||||||
defer func() {
|
|
||||||
if anyx.ToString(recover()) != "" {
|
|
||||||
logx.Errorf("upload file error: %s", err)
|
|
||||||
m.msgApp.CreateAndSend(la, msgdto.ErrSysMsg(i18n.TC(ctx, imsg.ErrFileUploadFail), fmt.Sprintf("%s: \n<-e : %s", i18n.TC(ctx, imsg.ErrFileUploadFail), err)))
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
opForm := &dto.MachineFileOp{
|
opForm := &dto.MachineFileOp{
|
||||||
MachineId: machineId,
|
MachineId: machineId,
|
||||||
AuthCertName: authCertName,
|
AuthCertName: authCertName,
|
||||||
@@ -281,9 +271,27 @@ func (m *MachineFile) UploadFile(rc *req.Ctx) {
|
|||||||
|
|
||||||
mi, err := m.machineFileApp.UploadFile(ctx, opForm, fileheader.Filename, file)
|
mi, err := m.machineFileApp.UploadFile(ctx, opForm, fileheader.Filename, file)
|
||||||
rc.ReqParam = collx.Kvs("machine", mi, "path", fmt.Sprintf("%s/%s", path, fileheader.Filename))
|
rc.ReqParam = collx.Kvs("machine", mi, "path", fmt.Sprintf("%s/%s", path, fileheader.Filename))
|
||||||
|
|
||||||
|
// 发送文件上传结果消息
|
||||||
|
msgEvent := &msgdto.MsgTmplSendEvent{
|
||||||
|
TmplChannel: msgdto.MsgTmplMachineFileUploadSuccess,
|
||||||
|
Params: collx.M{
|
||||||
|
"filename": fileheader.Filename,
|
||||||
|
"path": path,
|
||||||
|
},
|
||||||
|
ReceiverIds: []uint64{rc.GetLoginAccount().Id},
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
msgEvent.Params["error"] = err.Error()
|
||||||
|
msgEvent.TmplChannel = msgdto.MsgTmplMachineFileUploadFail
|
||||||
|
}
|
||||||
|
if mi != nil {
|
||||||
|
msgEvent.Params["machineName"] = mi.Name
|
||||||
|
msgEvent.Params["machineIp"] = mi.Ip
|
||||||
|
}
|
||||||
|
global.EventBus.Publish(ctx, event.EventTopicMsgTmplSend, msgEvent)
|
||||||
|
|
||||||
biz.ErrIsNilAppendErr(err, "upload file error: %s")
|
biz.ErrIsNilAppendErr(err, "upload file error: %s")
|
||||||
// 保存消息并发送文件上传成功通知
|
|
||||||
m.msgApp.CreateAndSend(la, msgdto.SuccessSysMsg(i18n.TC(ctx, imsg.MsgUploadFileSuccess), fmt.Sprintf("[%s] -> %s[%s:%s]", fileheader.Filename, mi.Name, mi.Ip, path)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type FolderFile struct {
|
type FolderFile struct {
|
||||||
@@ -350,6 +358,17 @@ func (m *MachineFile) UploadFolder(rc *req.Ctx) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
msgEvent := &msgdto.MsgTmplSendEvent{
|
||||||
|
TmplChannel: msgdto.MsgTmplMachineFileUploadSuccess,
|
||||||
|
Params: collx.M{
|
||||||
|
"filename": folderName,
|
||||||
|
"path": basePath,
|
||||||
|
"machineName": mi.Name,
|
||||||
|
"machineIp": mi.Ip,
|
||||||
|
},
|
||||||
|
ReceiverIds: []uint64{rc.GetLoginAccount().Id},
|
||||||
|
}
|
||||||
|
|
||||||
// 分组处理
|
// 分组处理
|
||||||
groupNum := 30
|
groupNum := 30
|
||||||
chunks := collx.ArraySplit(folderFiles, groupNum)
|
chunks := collx.ArraySplit(folderFiles, groupNum)
|
||||||
@@ -359,7 +378,6 @@ func (m *MachineFile) UploadFolder(rc *req.Ctx) {
|
|||||||
wg.Add(len(chunks))
|
wg.Add(len(chunks))
|
||||||
|
|
||||||
isSuccess := true
|
isSuccess := true
|
||||||
la := rc.GetLoginAccount()
|
|
||||||
for _, chunk := range chunks {
|
for _, chunk := range chunks {
|
||||||
go func(files []FolderFile, wg *sync.WaitGroup) {
|
go func(files []FolderFile, wg *sync.WaitGroup) {
|
||||||
defer func() {
|
defer func() {
|
||||||
@@ -370,7 +388,11 @@ func (m *MachineFile) UploadFolder(rc *req.Ctx) {
|
|||||||
logx.Errorf("upload file error: %s", err)
|
logx.Errorf("upload file error: %s", err)
|
||||||
switch t := err.(type) {
|
switch t := err.(type) {
|
||||||
case *errorx.BizError:
|
case *errorx.BizError:
|
||||||
m.msgApp.CreateAndSend(la, msgdto.ErrSysMsg(i18n.TC(ctx, imsg.ErrFileUploadFail), fmt.Sprintf("%s: \n<-e errCode: %d, errMsg: %s", i18n.TC(ctx, imsg.ErrFileUploadFail), t.Code(), t.Error())))
|
{
|
||||||
|
msgEvent.TmplChannel = msgdto.MsgTmplMachineFileUploadFail
|
||||||
|
msgEvent.Params["error"] = t.Error()
|
||||||
|
global.EventBus.Publish(ctx, event.EventTopicMsgTmplSend, msgEvent)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
@@ -394,13 +416,12 @@ func (m *MachineFile) UploadFolder(rc *req.Ctx) {
|
|||||||
// 等待所有协程执行完成
|
// 等待所有协程执行完成
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
if isSuccess {
|
if isSuccess {
|
||||||
// 保存消息并发送文件上传成功通知
|
global.EventBus.Publish(ctx, event.EventTopicMsgTmplSend, msgEvent)
|
||||||
m.msgApp.CreateAndSend(la, msgdto.SuccessSysMsg(i18n.TC(ctx, imsg.MsgUploadFileSuccess), fmt.Sprintf("[%s] -> %s[%s:%s]", folderName, mi.Name, mi.Ip, basePath)))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MachineFile) RemoveFile(rc *req.Ctx) {
|
func (m *MachineFile) RemoveFile(rc *req.Ctx) {
|
||||||
opForm := req.BindJsonAndValid[*form.RemoveFileForm](rc)
|
opForm := req.BindJson[*form.RemoveFileForm](rc)
|
||||||
|
|
||||||
mi, err := m.machineFileApp.RemoveFile(rc.MetaCtx, opForm.MachineFileOp, opForm.Paths...)
|
mi, err := m.machineFileApp.RemoveFile(rc.MetaCtx, opForm.MachineFileOp, opForm.Paths...)
|
||||||
rc.ReqParam = collx.Kvs("machine", mi, "path", opForm)
|
rc.ReqParam = collx.Kvs("machine", mi, "path", opForm)
|
||||||
@@ -408,21 +429,21 @@ func (m *MachineFile) RemoveFile(rc *req.Ctx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *MachineFile) CopyFile(rc *req.Ctx) {
|
func (m *MachineFile) CopyFile(rc *req.Ctx) {
|
||||||
opForm := req.BindJsonAndValid[*form.CopyFileForm](rc)
|
opForm := req.BindJson[*form.CopyFileForm](rc)
|
||||||
mi, err := m.machineFileApp.Copy(rc.MetaCtx, opForm.MachineFileOp, opForm.ToPath, opForm.Paths...)
|
mi, err := m.machineFileApp.Copy(rc.MetaCtx, opForm.MachineFileOp, opForm.ToPath, opForm.Paths...)
|
||||||
biz.ErrIsNilAppendErr(err, "file copy error: %s")
|
biz.ErrIsNilAppendErr(err, "file copy error: %s")
|
||||||
rc.ReqParam = collx.Kvs("machine", mi, "cp", opForm)
|
rc.ReqParam = collx.Kvs("machine", mi, "cp", opForm)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MachineFile) MvFile(rc *req.Ctx) {
|
func (m *MachineFile) MvFile(rc *req.Ctx) {
|
||||||
opForm := req.BindJsonAndValid[*form.CopyFileForm](rc)
|
opForm := req.BindJson[*form.CopyFileForm](rc)
|
||||||
mi, err := m.machineFileApp.Mv(rc.MetaCtx, opForm.MachineFileOp, opForm.ToPath, opForm.Paths...)
|
mi, err := m.machineFileApp.Mv(rc.MetaCtx, opForm.MachineFileOp, opForm.ToPath, opForm.Paths...)
|
||||||
rc.ReqParam = collx.Kvs("machine", mi, "mv", opForm)
|
rc.ReqParam = collx.Kvs("machine", mi, "mv", opForm)
|
||||||
biz.ErrIsNilAppendErr(err, "file move error: %s")
|
biz.ErrIsNilAppendErr(err, "file move error: %s")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MachineFile) Rename(rc *req.Ctx) {
|
func (m *MachineFile) Rename(rc *req.Ctx) {
|
||||||
renameForm := req.BindJsonAndValid[*form.RenameForm](rc)
|
renameForm := req.BindJson[*form.RenameForm](rc)
|
||||||
mi, err := m.machineFileApp.Rename(rc.MetaCtx, renameForm.MachineFileOp, renameForm.Newname)
|
mi, err := m.machineFileApp.Rename(rc.MetaCtx, renameForm.MachineFileOp, renameForm.Newname)
|
||||||
rc.ReqParam = collx.Kvs("machine", mi, "rename", renameForm)
|
rc.ReqParam = collx.Kvs("machine", mi, "rename", renameForm)
|
||||||
biz.ErrIsNilAppendErr(err, "file rename error: %s")
|
biz.ErrIsNilAppendErr(err, "file rename error: %s")
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
"mayfly-go/internal/machine/domain/entity"
|
"mayfly-go/internal/machine/domain/entity"
|
||||||
"mayfly-go/internal/machine/domain/repository"
|
"mayfly-go/internal/machine/domain/repository"
|
||||||
"mayfly-go/internal/machine/imsg"
|
"mayfly-go/internal/machine/imsg"
|
||||||
"mayfly-go/internal/machine/infrastructure/cache"
|
"mayfly-go/internal/machine/infra/cache"
|
||||||
"mayfly-go/internal/machine/mcm"
|
"mayfly-go/internal/machine/mcm"
|
||||||
tagapp "mayfly-go/internal/tag/application"
|
tagapp "mayfly-go/internal/tag/application"
|
||||||
tagdto "mayfly-go/internal/tag/application/dto"
|
tagdto "mayfly-go/internal/tag/application/dto"
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"mayfly-go/initialize"
|
"mayfly-go/initialize"
|
||||||
"mayfly-go/internal/machine/api"
|
"mayfly-go/internal/machine/api"
|
||||||
"mayfly-go/internal/machine/application"
|
"mayfly-go/internal/machine/application"
|
||||||
"mayfly-go/internal/machine/infrastructure/persistence"
|
"mayfly-go/internal/machine/infra/persistence"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|||||||
@@ -2,13 +2,13 @@ package api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"mayfly-go/internal/event"
|
|
||||||
"mayfly-go/internal/mongo/api/form"
|
"mayfly-go/internal/mongo/api/form"
|
||||||
"mayfly-go/internal/mongo/api/vo"
|
"mayfly-go/internal/mongo/api/vo"
|
||||||
"mayfly-go/internal/mongo/application"
|
"mayfly-go/internal/mongo/application"
|
||||||
"mayfly-go/internal/mongo/domain/entity"
|
"mayfly-go/internal/mongo/domain/entity"
|
||||||
"mayfly-go/internal/mongo/imsg"
|
"mayfly-go/internal/mongo/imsg"
|
||||||
"mayfly-go/internal/pkg/consts"
|
"mayfly-go/internal/pkg/consts"
|
||||||
|
"mayfly-go/internal/pkg/event"
|
||||||
tagapp "mayfly-go/internal/tag/application"
|
tagapp "mayfly-go/internal/tag/application"
|
||||||
tagentity "mayfly-go/internal/tag/domain/entity"
|
tagentity "mayfly-go/internal/tag/domain/entity"
|
||||||
"mayfly-go/pkg/biz"
|
"mayfly-go/pkg/biz"
|
||||||
@@ -146,7 +146,7 @@ func (m *Mongo) Collections(rc *req.Ctx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *Mongo) RunCommand(rc *req.Ctx) {
|
func (m *Mongo) RunCommand(rc *req.Ctx) {
|
||||||
commandForm := req.BindJsonAndValid[*form.MongoRunCommand](rc)
|
commandForm := req.BindJson[*form.MongoRunCommand](rc)
|
||||||
|
|
||||||
conn, err := m.mongoApp.GetMongoConn(rc.MetaCtx, m.GetMongoId(rc))
|
conn, err := m.mongoApp.GetMongoConn(rc.MetaCtx, m.GetMongoId(rc))
|
||||||
biz.ErrIsNil(err)
|
biz.ErrIsNil(err)
|
||||||
@@ -176,7 +176,7 @@ func (m *Mongo) RunCommand(rc *req.Ctx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *Mongo) FindCommand(rc *req.Ctx) {
|
func (m *Mongo) FindCommand(rc *req.Ctx) {
|
||||||
commandForm := req.BindJsonAndValid[*form.MongoFindCommand](rc)
|
commandForm := req.BindJson[*form.MongoFindCommand](rc)
|
||||||
|
|
||||||
conn, err := m.mongoApp.GetMongoConn(rc.MetaCtx, m.GetMongoId(rc))
|
conn, err := m.mongoApp.GetMongoConn(rc.MetaCtx, m.GetMongoId(rc))
|
||||||
biz.ErrIsNil(err)
|
biz.ErrIsNil(err)
|
||||||
@@ -211,7 +211,7 @@ func (m *Mongo) FindCommand(rc *req.Ctx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *Mongo) UpdateByIdCommand(rc *req.Ctx) {
|
func (m *Mongo) UpdateByIdCommand(rc *req.Ctx) {
|
||||||
commandForm := req.BindJsonAndValid[*form.MongoUpdateByIdCommand](rc)
|
commandForm := req.BindJson[*form.MongoUpdateByIdCommand](rc)
|
||||||
|
|
||||||
conn, err := m.mongoApp.GetMongoConn(rc.MetaCtx, m.GetMongoId(rc))
|
conn, err := m.mongoApp.GetMongoConn(rc.MetaCtx, m.GetMongoId(rc))
|
||||||
biz.ErrIsNil(err)
|
biz.ErrIsNil(err)
|
||||||
@@ -235,7 +235,7 @@ func (m *Mongo) UpdateByIdCommand(rc *req.Ctx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *Mongo) DeleteByIdCommand(rc *req.Ctx) {
|
func (m *Mongo) DeleteByIdCommand(rc *req.Ctx) {
|
||||||
commandForm := req.BindJsonAndValid[*form.MongoUpdateByIdCommand](rc)
|
commandForm := req.BindJson[*form.MongoUpdateByIdCommand](rc)
|
||||||
|
|
||||||
conn, err := m.mongoApp.GetMongoConn(rc.MetaCtx, m.GetMongoId(rc))
|
conn, err := m.mongoApp.GetMongoConn(rc.MetaCtx, m.GetMongoId(rc))
|
||||||
biz.ErrIsNil(err)
|
biz.ErrIsNil(err)
|
||||||
@@ -258,7 +258,7 @@ func (m *Mongo) DeleteByIdCommand(rc *req.Ctx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *Mongo) InsertOneCommand(rc *req.Ctx) {
|
func (m *Mongo) InsertOneCommand(rc *req.Ctx) {
|
||||||
commandForm := req.BindJsonAndValid[*form.MongoInsertCommand](rc)
|
commandForm := req.BindJson[*form.MongoInsertCommand](rc)
|
||||||
|
|
||||||
conn, err := m.mongoApp.GetMongoConn(rc.MetaCtx, m.GetMongoId(rc))
|
conn, err := m.mongoApp.GetMongoConn(rc.MetaCtx, m.GetMongoId(rc))
|
||||||
biz.ErrIsNil(err)
|
biz.ErrIsNil(err)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"mayfly-go/initialize"
|
"mayfly-go/initialize"
|
||||||
"mayfly-go/internal/mongo/api"
|
"mayfly-go/internal/mongo/api"
|
||||||
"mayfly-go/internal/mongo/application"
|
"mayfly-go/internal/mongo/application"
|
||||||
"mayfly-go/internal/mongo/infrastructure/persistence"
|
"mayfly-go/internal/mongo/infra/persistence"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|||||||
@@ -14,12 +14,14 @@ type Msg struct {
|
|||||||
func (m *Msg) ReqConfs() *req.Confs {
|
func (m *Msg) ReqConfs() *req.Confs {
|
||||||
reqs := [...]*req.Conf{
|
reqs := [...]*req.Conf{
|
||||||
req.NewGet("/self", m.GetMsgs),
|
req.NewGet("/self", m.GetMsgs),
|
||||||
|
req.NewGet("/self/unread/count", m.GetUnreadCount),
|
||||||
|
req.NewGet("/self/read", m.ReadMsg),
|
||||||
}
|
}
|
||||||
|
|
||||||
return req.NewConfs("/msgs", reqs[:]...)
|
return req.NewConfs("/msgs", reqs[:]...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取账号接收的消息列表
|
// GetMsgs 获取账号接收的消息列表
|
||||||
func (m *Msg) GetMsgs(rc *req.Ctx) {
|
func (m *Msg) GetMsgs(rc *req.Ctx) {
|
||||||
condition := &entity.Msg{
|
condition := &entity.Msg{
|
||||||
RecipientId: int64(rc.GetLoginAccount().Id),
|
RecipientId: int64(rc.GetLoginAccount().Id),
|
||||||
@@ -28,3 +30,25 @@ func (m *Msg) GetMsgs(rc *req.Ctx) {
|
|||||||
biz.ErrIsNil(err)
|
biz.ErrIsNil(err)
|
||||||
rc.ResData = res
|
rc.ResData = res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetUnreadCount 获取账号接收的未读消息数量
|
||||||
|
func (m *Msg) GetUnreadCount(rc *req.Ctx) {
|
||||||
|
condition := &entity.Msg{
|
||||||
|
RecipientId: int64(rc.GetLoginAccount().Id),
|
||||||
|
Status: entity.MsgStatusUnRead,
|
||||||
|
}
|
||||||
|
rc.ResData = m.msgApp.CountByCond(condition)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadMsg 将账号接收的未读消息标记为已读
|
||||||
|
func (m *Msg) ReadMsg(rc *req.Ctx) {
|
||||||
|
cond := &entity.Msg{
|
||||||
|
RecipientId: int64(rc.GetLoginAccount().Id),
|
||||||
|
Status: entity.MsgStatusUnRead,
|
||||||
|
}
|
||||||
|
cond.Id = uint64(rc.QueryInt("id"))
|
||||||
|
|
||||||
|
biz.ErrIsNil(m.msgApp.UpdateByCond(rc.MetaCtx, &entity.Msg{
|
||||||
|
Status: entity.MsgStatusRead,
|
||||||
|
}, cond))
|
||||||
|
}
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ func (m *MsgTmpl) DelMsgTmpls(rc *req.Ctx) {
|
|||||||
|
|
||||||
func (m *MsgTmpl) SendMsg(rc *req.Ctx) {
|
func (m *MsgTmpl) SendMsg(rc *req.Ctx) {
|
||||||
code := rc.PathParam("code")
|
code := rc.PathParam("code")
|
||||||
form := req.BindJsonAndValid[*form.SendMsg](rc)
|
form := req.BindJson[*form.SendMsg](rc)
|
||||||
|
|
||||||
rc.ReqParam = form
|
rc.ReqParam = form
|
||||||
|
|
||||||
|
|||||||
@@ -15,3 +15,7 @@ func InitIoc() {
|
|||||||
func GetMsgApp() Msg {
|
func GetMsgApp() Msg {
|
||||||
return ioc.Get[Msg]("MsgApp")
|
return ioc.Get[Msg]("MsgApp")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetMsgTmplApp() MsgTmpl {
|
||||||
|
return ioc.Get[MsgTmpl]("MsgTmplApp")
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,35 +2,93 @@ package dto
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"mayfly-go/internal/msg/domain/entity"
|
"mayfly-go/internal/msg/domain/entity"
|
||||||
|
"mayfly-go/internal/msg/imsg"
|
||||||
"mayfly-go/internal/msg/msgx"
|
"mayfly-go/internal/msg/msgx"
|
||||||
|
"mayfly-go/pkg/i18n"
|
||||||
"mayfly-go/pkg/model"
|
"mayfly-go/pkg/model"
|
||||||
|
"mayfly-go/pkg/utils/collx"
|
||||||
)
|
)
|
||||||
|
|
||||||
type MsgTmplSave struct {
|
type MsgTmplSendEvent struct {
|
||||||
model.ExtraData
|
TmplChannel *MsgTmplChannel
|
||||||
|
|
||||||
Id uint64 `json:"id"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
Remark string `json:"remark"`
|
|
||||||
Status entity.MsgTmplStatus `json:"status" `
|
|
||||||
Title string `json:"title"`
|
|
||||||
Tmpl string `json:"type"`
|
|
||||||
MsgType msgx.MsgType `json:"msgType"`
|
|
||||||
|
|
||||||
ChannelIds []uint64 `json:"channelIds"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// MsgTmplBizSave 消息模板关联业务信息
|
|
||||||
type MsgTmplBizSave struct {
|
|
||||||
TmplId uint64 // 消息模板id
|
|
||||||
BizId uint64 // 业务id
|
|
||||||
BizType string
|
|
||||||
}
|
|
||||||
|
|
||||||
// BizMsgTmplSend 业务消息模板发送消息
|
|
||||||
type BizMsgTmplSend struct {
|
|
||||||
BizId uint64 // 业务id
|
|
||||||
BizType string
|
|
||||||
Params map[string]any // 模板占位符参数
|
Params map[string]any // 模板占位符参数
|
||||||
ReceiverIds []uint64 // 接收人id
|
ReceiverIds []uint64 // 接收人id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type MsgTmplChannel struct {
|
||||||
|
Tmpl *entity.MsgTmpl
|
||||||
|
Channels []*entity.MsgChannel
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
MsgChannelSite = &entity.MsgChannel{
|
||||||
|
Type: msgx.ChannelTypeSiteMsg,
|
||||||
|
Status: entity.ChannelStatusEnable,
|
||||||
|
}
|
||||||
|
|
||||||
|
MsgChannelWs = &entity.MsgChannel{
|
||||||
|
Type: msgx.ChannelTypeWs,
|
||||||
|
Status: entity.ChannelStatusEnable,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
MsgTmplLogin = newMsgTmpl(entity.MsgTypeNotify,
|
||||||
|
entity.MsgSubtypeUserLogin,
|
||||||
|
entity.MsgStatusRead,
|
||||||
|
imsg.LoginMsg,
|
||||||
|
MsgChannelSite)
|
||||||
|
|
||||||
|
MsgTmplMachineFileUploadSuccess = newMsgTmpl(entity.MsgTypeNotify,
|
||||||
|
entity.MsgSubtypeMachineFileUploadSuccess,
|
||||||
|
entity.MsgStatusRead,
|
||||||
|
imsg.MachineFileUploadSuccessMsg,
|
||||||
|
MsgChannelSite, MsgChannelWs)
|
||||||
|
|
||||||
|
MsgTmplMachineFileUploadFail = newMsgTmpl(entity.MsgTypeNotify,
|
||||||
|
entity.MsgSubtypeMachineFileUploadFail,
|
||||||
|
entity.MsgStatusRead,
|
||||||
|
imsg.MachineFileUploadFailMsg,
|
||||||
|
MsgChannelSite, MsgChannelWs)
|
||||||
|
|
||||||
|
MsgTmplDbDumpFail = newMsgTmpl(entity.MsgTypeNotify,
|
||||||
|
entity.MsgSubtypeDbDumpFail,
|
||||||
|
entity.MsgStatusRead,
|
||||||
|
imsg.DbDumpFailMsg,
|
||||||
|
MsgChannelSite, MsgChannelWs)
|
||||||
|
|
||||||
|
MsgTmplSqlScriptRunFail = newMsgTmpl(entity.MsgTypeNotify,
|
||||||
|
entity.MsgSubtypeSqlScriptRunFail,
|
||||||
|
entity.MsgStatusRead,
|
||||||
|
imsg.SqlScriptRunFailMsg,
|
||||||
|
MsgChannelSite, MsgChannelWs)
|
||||||
|
|
||||||
|
MsgTmplSqlScriptRunSuccess = newMsgTmpl(entity.MsgTypeNotify,
|
||||||
|
entity.MsgSubtypeSqlScriptRunSuccess,
|
||||||
|
entity.MsgStatusRead,
|
||||||
|
imsg.SqlScriptRunSuccessMsg,
|
||||||
|
MsgChannelSite, MsgChannelWs)
|
||||||
|
|
||||||
|
MsgTmplSqlScriptRunProgress = &MsgTmplChannel{
|
||||||
|
Tmpl: &entity.MsgTmpl{
|
||||||
|
ExtraData: model.ExtraData{
|
||||||
|
Extra: collx.M{
|
||||||
|
"category": "sqlScriptRunProgress",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Channels: []*entity.MsgChannel{MsgChannelWs},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func newMsgTmpl(mtype entity.MsgType, subtype entity.MsgSubtype, status entity.MsgStatus, msgId i18n.MsgId, channels ...*entity.MsgChannel) *MsgTmplChannel {
|
||||||
|
msgTmpl := &entity.MsgTmpl{}
|
||||||
|
msgTmpl.SetExtraValue("msgId", msgId)
|
||||||
|
msgTmpl.SetExtraValue("subtype", subtype)
|
||||||
|
msgTmpl.SetExtraValue("type", mtype)
|
||||||
|
msgTmpl.SetExtraValue("status", status)
|
||||||
|
return &MsgTmplChannel{
|
||||||
|
Tmpl: msgTmpl,
|
||||||
|
Channels: channels,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
43
server/internal/msg/application/dto/msg_tmpl.go
Normal file
43
server/internal/msg/application/dto/msg_tmpl.go
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
package dto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"mayfly-go/internal/msg/domain/entity"
|
||||||
|
"mayfly-go/internal/msg/msgx"
|
||||||
|
"mayfly-go/pkg/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MsgTmplSave struct {
|
||||||
|
model.ExtraData
|
||||||
|
|
||||||
|
Id uint64 `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Remark string `json:"remark"`
|
||||||
|
Status entity.MsgTmplStatus `json:"status" `
|
||||||
|
Title string `json:"title"`
|
||||||
|
Tmpl string `json:"type"`
|
||||||
|
MsgType msgx.MsgType `json:"msgType"`
|
||||||
|
|
||||||
|
ChannelIds []uint64 `json:"channelIds"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MsgTmplBizSave 消息模板关联业务信息
|
||||||
|
type MsgTmplBizSave struct {
|
||||||
|
TmplId uint64 // 消息模板id
|
||||||
|
BizId uint64 // 业务id
|
||||||
|
BizType string
|
||||||
|
}
|
||||||
|
|
||||||
|
// BizMsgTmplSend 业务消息模板发送消息
|
||||||
|
type BizMsgTmplSend struct {
|
||||||
|
BizId uint64 // 业务id
|
||||||
|
BizType string
|
||||||
|
Params map[string]any // 模板占位符参数
|
||||||
|
ReceiverIds []uint64 // 接收人id
|
||||||
|
}
|
||||||
|
|
||||||
|
type MsgTmplSend struct {
|
||||||
|
Tmpl *entity.MsgTmpl
|
||||||
|
Channels []*entity.MsgChannel
|
||||||
|
Params map[string]any // 模板占位符参数
|
||||||
|
ReceiverIds []uint64 // 接收人id
|
||||||
|
}
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
package dto
|
|
||||||
|
|
||||||
import "mayfly-go/pkg/utils/anyx"
|
|
||||||
|
|
||||||
// ************** 系统消息 **************
|
|
||||||
|
|
||||||
const SuccessSysMsgType = 1
|
|
||||||
const ErrorSysMsgType = 0
|
|
||||||
const InfoSysMsgType = 2
|
|
||||||
|
|
||||||
// websocket消息
|
|
||||||
type SysMsg struct {
|
|
||||||
Type int `json:"type"` // 消息类型
|
|
||||||
Category string `json:"category"` // 消息类别
|
|
||||||
Title string `json:"title"` // 消息标题
|
|
||||||
Msg string `json:"msg"` // 消息内容
|
|
||||||
|
|
||||||
ClientId string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sm *SysMsg) WithTitle(title string) *SysMsg {
|
|
||||||
sm.Title = title
|
|
||||||
return sm
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sm *SysMsg) WithCategory(category string) *SysMsg {
|
|
||||||
sm.Category = category
|
|
||||||
return sm
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sm *SysMsg) WithMsg(msg any) *SysMsg {
|
|
||||||
sm.Msg = anyx.ToString(msg)
|
|
||||||
return sm
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sm *SysMsg) WithClientId(clientId string) *SysMsg {
|
|
||||||
sm.ClientId = clientId
|
|
||||||
return sm
|
|
||||||
}
|
|
||||||
|
|
||||||
// 普通消息
|
|
||||||
func InfoSysMsg(title string, msg any) *SysMsg {
|
|
||||||
return &SysMsg{Type: InfoSysMsgType, Title: title, Msg: anyx.ToString(msg)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 成功消息
|
|
||||||
func SuccessSysMsg(title string, msg any) *SysMsg {
|
|
||||||
return &SysMsg{Type: SuccessSysMsgType, Title: title, Msg: anyx.ToString(msg)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 错误消息
|
|
||||||
func ErrSysMsg(title string, msg any) *SysMsg {
|
|
||||||
return &SysMsg{Type: ErrorSysMsgType, Title: title, Msg: anyx.ToString(msg)}
|
|
||||||
}
|
|
||||||
@@ -1,27 +1,30 @@
|
|||||||
package application
|
package application
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"cmp"
|
||||||
"context"
|
"context"
|
||||||
"mayfly-go/internal/msg/application/dto"
|
|
||||||
"mayfly-go/internal/msg/domain/entity"
|
"mayfly-go/internal/msg/domain/entity"
|
||||||
"mayfly-go/internal/msg/domain/repository"
|
"mayfly-go/internal/msg/domain/repository"
|
||||||
|
"mayfly-go/internal/msg/msgx"
|
||||||
|
"mayfly-go/pkg/base"
|
||||||
|
"mayfly-go/pkg/i18n"
|
||||||
"mayfly-go/pkg/model"
|
"mayfly-go/pkg/model"
|
||||||
"mayfly-go/pkg/ws"
|
"mayfly-go/pkg/utils/stringx"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Msg interface {
|
type Msg interface {
|
||||||
|
msgx.MsgSender
|
||||||
|
|
||||||
|
base.App[*entity.Msg]
|
||||||
|
|
||||||
GetPageList(condition *entity.Msg, pageParam model.PageParam, orderBy ...string) (*model.PageResult[*entity.Msg], error)
|
GetPageList(condition *entity.Msg, pageParam model.PageParam, orderBy ...string) (*model.PageResult[*entity.Msg], error)
|
||||||
|
|
||||||
Create(ctx context.Context, msg *entity.Msg)
|
|
||||||
|
|
||||||
// 创建消息,并通过ws发送
|
|
||||||
CreateAndSend(la *model.LoginAccount, msg *dto.SysMsg)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ (Msg) = (*msgAppImpl)(nil)
|
var _ (Msg) = (*msgAppImpl)(nil)
|
||||||
|
|
||||||
type msgAppImpl struct {
|
type msgAppImpl struct {
|
||||||
|
base.AppImpl[*entity.Msg, repository.Msg]
|
||||||
|
|
||||||
msgRepo repository.Msg `inject:"T"`
|
msgRepo repository.Msg `inject:"T"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,13 +32,29 @@ func (a *msgAppImpl) GetPageList(condition *entity.Msg, pageParam model.PagePara
|
|||||||
return a.msgRepo.GetPageList(condition, pageParam)
|
return a.msgRepo.GetPageList(condition, pageParam)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *msgAppImpl) Create(ctx context.Context, msg *entity.Msg) {
|
func (a *msgAppImpl) Send(ctx context.Context, channel *msgx.Channel, msg *msgx.Msg) error {
|
||||||
a.msgRepo.Insert(ctx, msg)
|
// 存在i18n msgId,content则使用msgId翻译
|
||||||
}
|
if msgId := msg.TmplExtra.GetInt("msgId"); msgId != 0 {
|
||||||
|
msg.Content = i18n.TC(ctx, i18n.MsgId(msgId))
|
||||||
|
}
|
||||||
|
content, err := stringx.TemplateParse(msg.Content, msg.Params)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
func (a *msgAppImpl) CreateAndSend(la *model.LoginAccount, wmsg *dto.SysMsg) {
|
for _, receiver := range msg.Receivers {
|
||||||
now := time.Now()
|
msgEntity := &entity.Msg{
|
||||||
msg := &entity.Msg{Type: 2, Msg: wmsg.Msg, RecipientId: int64(la.Id), CreateTime: &now, CreatorId: la.Id, Creator: la.Username}
|
Msg: content,
|
||||||
a.msgRepo.Insert(context.TODO(), msg)
|
RecipientId: int64(receiver.Id),
|
||||||
ws.SendJsonMsg(ws.UserId(la.Id), wmsg.ClientId, wmsg)
|
Type: entity.MsgType(msg.TmplExtra.GetInt("type")),
|
||||||
|
Subtype: entity.MsgSubtype(msg.TmplExtra.GetStr("subtype")),
|
||||||
|
Status: cmp.Or(entity.MsgStatus(msg.TmplExtra.GetInt("status")), entity.MsgStatusRead),
|
||||||
|
}
|
||||||
|
msgEntity.Extra = msg.Params
|
||||||
|
if err := a.Save(ctx, msgEntity); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,8 @@ type MsgTmpl interface {
|
|||||||
// Send 发送消息
|
// Send 发送消息
|
||||||
Send(ctx context.Context, tmplCode string, params map[string]any, receiverId ...uint64) error
|
Send(ctx context.Context, tmplCode string, params map[string]any, receiverId ...uint64) error
|
||||||
|
|
||||||
|
SendMsg(ctx context.Context, mts *dto.MsgTmplSend) error
|
||||||
|
|
||||||
// DeleteTmplChannel 删除指定渠道关联的模板
|
// DeleteTmplChannel 删除指定渠道关联的模板
|
||||||
DeleteTmplChannel(ctx context.Context, channelId uint64) error
|
DeleteTmplChannel(ctx context.Context, channelId uint64) error
|
||||||
}
|
}
|
||||||
@@ -153,47 +155,52 @@ func (m *msgTmplAppImpl) Send(ctx context.Context, tmplCode string, params map[s
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// content, err := stringx.TemplateParse(tmpl.Tmpl, params)
|
return m.SendMsg(ctx, &dto.MsgTmplSend{
|
||||||
// if err != nil {
|
Tmpl: tmpl,
|
||||||
// return err
|
Channels: channels,
|
||||||
// }
|
Params: params,
|
||||||
|
ReceiverIds: receiverId,
|
||||||
// toAll := len(receiverId) == 0
|
})
|
||||||
accounts, err := m.accountApp.GetByIds(receiverId)
|
}
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
|
func (m *msgTmplAppImpl) SendMsg(ctx context.Context, mts *dto.MsgTmplSend) error {
|
||||||
|
tmpl := mts.Tmpl
|
||||||
msg := &msgx.Msg{
|
msg := &msgx.Msg{
|
||||||
Content: tmpl.Tmpl,
|
Content: tmpl.Tmpl,
|
||||||
Params: params,
|
Params: mts.Params,
|
||||||
Title: tmpl.Title,
|
Title: tmpl.Title,
|
||||||
Type: tmpl.MsgType,
|
Type: tmpl.MsgType,
|
||||||
ExtraData: tmpl.ExtraData,
|
TmplExtra: tmpl.Extra,
|
||||||
|
}
|
||||||
|
|
||||||
|
accounts, err := m.accountApp.GetByIds(mts.ReceiverIds)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(accounts) > 0 {
|
if len(accounts) > 0 {
|
||||||
msg.Receivers = collx.ArrayMap(accounts, func(account *sysentity.Account) msgx.Receiver {
|
msg.Receivers = collx.ArrayMap(accounts, func(account *sysentity.Account) msgx.Receiver {
|
||||||
return msgx.Receiver{
|
return msgx.Receiver{
|
||||||
ExtraData: account.ExtraData,
|
Id: account.Id,
|
||||||
Email: account.Email,
|
Extra: account.Extra,
|
||||||
Mobile: account.Mobile,
|
Email: account.Email,
|
||||||
|
Mobile: account.Mobile,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, channel := range channels {
|
for _, channel := range mts.Channels {
|
||||||
if channel.Status != entity.ChannelStatusEnable {
|
if channel.Status != entity.ChannelStatusEnable {
|
||||||
logx.Warnf("channel is disabled => %s", channel.Code)
|
logx.Warnf("channel is disabled => %s", channel.Code)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
go func(channel *entity.MsgChannel) {
|
go func(channel *entity.MsgChannel) {
|
||||||
if err := msgx.Send(&msgx.Channel{
|
if err := msgx.Send(ctx, &msgx.Channel{
|
||||||
Type: channel.Type,
|
Type: channel.Type,
|
||||||
Name: channel.Name,
|
Name: channel.Name,
|
||||||
URL: channel.Url,
|
URL: channel.Url,
|
||||||
ExtraData: channel.ExtraData,
|
Extra: channel.Extra,
|
||||||
}, msg); err != nil {
|
}, msg); err != nil {
|
||||||
logx.Errorf("send msg error => channel=%s, msg=%s, err -> %v", channel.Code, msg.Content, err)
|
logx.Errorf("send msg error => channel=%s, msg=%s, err -> %v", channel.Code, msg.Content, err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,21 +2,49 @@ package entity
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"mayfly-go/pkg/model"
|
"mayfly-go/pkg/model"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Msg struct {
|
type Msg struct {
|
||||||
model.DeletedModel
|
model.ExtraData
|
||||||
|
model.CreateModel
|
||||||
|
|
||||||
CreateTime *time.Time `json:"createTime"`
|
Type MsgType `json:"type"` // 消息类型
|
||||||
CreatorId uint64 `json:"creatorId"`
|
Subtype MsgSubtype `json:"subtype" gorm:"size:100"` // 消息子类型
|
||||||
Creator string `json:"creator" gorm:"size:50"`
|
Status MsgStatus `json:"status"`
|
||||||
|
Msg string `json:"msg" gorm:"size:2000"`
|
||||||
Type int8 `json:"type"`
|
RecipientId int64 `json:"recipientId"` // 接收人id,-1为所有接收
|
||||||
Msg string `json:"msg" gorm:"size:2000"`
|
|
||||||
RecipientId int64 `json:"recipientId"` // 接收人id,-1为所有接收
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Msg) TableName() string {
|
func (a *Msg) TableName() string {
|
||||||
return "t_sys_msg"
|
return "t_sys_msg"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type MsgType int8
|
||||||
|
|
||||||
|
const (
|
||||||
|
MsgTypeNotify MsgType = 1 // 通知
|
||||||
|
MsgTypeTodo MsgType = 2 // 代办
|
||||||
|
)
|
||||||
|
|
||||||
|
type MsgSubtype string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// sys
|
||||||
|
MsgSubtypeUserLogin MsgSubtype = "user.login"
|
||||||
|
|
||||||
|
// machine
|
||||||
|
MsgSubtypeMachineFileUploadSuccess MsgSubtype = "machine.file.upload.success"
|
||||||
|
MsgSubtypeMachineFileUploadFail MsgSubtype = "machine.file.upload.fail"
|
||||||
|
|
||||||
|
// db
|
||||||
|
MsgSubtypeDbDumpFail MsgSubtype = "db.dump.fail"
|
||||||
|
MsgSubtypeSqlScriptRunFail MsgSubtype = "db.sqlscript.run.fail"
|
||||||
|
MsgSubtypeSqlScriptRunSuccess MsgSubtype = "db.sqlscript.run.success"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MsgStatus int8
|
||||||
|
|
||||||
|
const (
|
||||||
|
MsgStatusRead MsgStatus = 1 // 已读
|
||||||
|
MsgStatusUnRead MsgStatus = -1 // 未读
|
||||||
|
)
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user