后台登录页、首页优化

This commit is contained in:
慕下 2024-08-07 12:30:54 +08:00
parent 872159a859
commit 227e80b4c8
33 changed files with 1359 additions and 117 deletions

View File

@ -1,5 +1,5 @@
# 标题 # 标题
VITE_APP_TITLE=芋道管理系统 VITE_APP_TITLE=众悦e家管理系统
# 项目本地运行端口号 # 项目本地运行端口号
VITE_PORT=80 VITE_PORT=80
@ -20,6 +20,6 @@ VITE_APP_DOCALERT_ENABLE=true
VITE_APP_BAIDU_CODE = a1ff8825baa73c3a78eb96aa40325abc VITE_APP_BAIDU_CODE = a1ff8825baa73c3a78eb96aa40325abc
# 默认账户密码 # 默认账户密码
VITE_APP_DEFAULT_LOGIN_TENANT = 芋道源码 VITE_APP_DEFAULT_LOGIN_TENANT = 众悦e家
VITE_APP_DEFAULT_LOGIN_USERNAME = admin VITE_APP_DEFAULT_LOGIN_USERNAME = admin
VITE_APP_DEFAULT_LOGIN_PASSWORD = admin123 VITE_APP_DEFAULT_LOGIN_PASSWORD = admin123

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

@ -0,0 +1,47 @@
import request from '@/config/axios'
// 告警记录 VO
export interface OperateWarningVO {
id: number // 告警编号
alarmPolicy: string // 告警策略
monitoringObject: string // 监控对象
triggeringCondition: string // 触发条件
alarmFrequency: string // 告警频率
notificationChannel: string // 通知渠道
duration: string // 持续时间
alarmLevel: string // 告警级别
alarmStatus: number // 告警状态
}
// 告警记录 API
export const OperateWarningApi = {
// 查询告警记录分页
getOperateWarningPage: async (params: any) => {
return await request.get({ url: `/system/operate-warning/page`, params })
},
// 查询告警记录详情
getOperateWarning: async (id: number) => {
return await request.get({ url: `/system/operate-warning/get?id=` + id })
},
// 新增告警记录
createOperateWarning: async (data: OperateWarningVO) => {
return await request.post({ url: `/system/operate-warning/create`, data })
},
// 修改告警记录
updateOperateWarning: async (data: OperateWarningVO) => {
return await request.put({ url: `/system/operate-warning/update`, data })
},
// 删除告警记录
deleteOperateWarning: async (id: number) => {
return await request.delete({ url: `/system/operate-warning/delete?id=` + id })
},
// 导出告警记录 Excel
exportOperateWarning: async (params) => {
return await request.download({ url: `/system/operate-warning/export-excel`, params })
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

@ -111,22 +111,81 @@ watchEffect(() => {
// const jsonObject = JSON.parse(content); // const jsonObject = JSON.parse(content);
// const sValue = ref(jsonObject); // const sValue = ref(jsonObject);
// console.log("content:", JSON.parse(jsonObject)); // console.log("content:", JSON.parse(jsonObject));
//磁盘告警
if(content === '"diskPull"'){ if(content === '"diskPull"'){
const open = () => { const open = () => {
ElMessageBox.alert('This is a message', 'Title', { ElMessageBox.alert('磁盘存储已达溢值,请联系管理员查看原因', '磁盘告警', {
// if you want to disable its autofocus // if you want to disable its autofocus
// autofocus: false, // autofocus: false,
confirmButtonText: 'OK', confirmButtonText: '确定',
callback: (action: Action) => { callback: (action: Action) => {
ElMessage({ ElMessage({
type: 'info', type: 'warning',
message: `action: ${action}`, message: `关闭成功`,
// message: `action: ${action}`,
}) })
}, },
}) })
} }
open(); open();
} }
//进程cpu利用率告警
if(content === '"processPull"'){
const open = () => {
ElMessageBox.alert('进程cpu利用率已达溢值请联系管理员查看原因', '进程cpu利用率告警', {
// if you want to disable its autofocus
// autofocus: false,
confirmButtonText: '确定',
callback: (action: Action) => {
ElMessage({
type: 'warning',
message: `关闭成功`,
// message: `action: ${action}`,
})
},
})
}
open();
}
//系统cpu利用率告警
if(content === '"systemPull"'){
const open = () => {
ElMessageBox.alert('系统cpu利用率已达溢值请联系管理员查看原因', '系统cpu利用率告警', {
// if you want to disable its autofocus
// autofocus: false,
confirmButtonText: '确定',
callback: (action: Action) => {
ElMessage({
type: 'warning',
message: `关闭成功`,
// message: `action: ${action}`,
})
},
})
}
open();
}
//系统内存告警
if(content === '"HeapPull"'){
const open = () => {
ElMessageBox.alert('系统内存已达溢值,请联系管理员查看原因', '系统内存告警', {
// if you want to disable its autofocus
// autofocus: false,
confirmButtonText: '确定',
callback: (action: Action) => {
ElMessage({
type: 'warning',
message: `关闭成功`,
// message: `action: ${action}`,
})
},
})
}
open();
}
if (!type) { if (!type) {
message.error('未知的消息类型:' + data.value) message.error('未知的消息类型:' + data.value)
return return

View File

@ -18,7 +18,7 @@
</div> </div>
</div> </div>
</el-col> </el-col>
<el-col :xl="12" :lg="12" :md="12" :sm="24" :xs="24"> <!-- <el-col :xl="12" :lg="12" :md="12" :sm="24" :xs="24">
<div class="h-70px flex items-center justify-end lt-sm:mt-10px"> <div class="h-70px flex items-center justify-end lt-sm:mt-10px">
<div class="px-8px text-right"> <div class="px-8px text-right">
<div class="mb-16px text-14px text-gray-400">{{ t('workplace.project') }}</div> <div class="mb-16px text-14px text-gray-400">{{ t('workplace.project') }}</div>
@ -50,7 +50,7 @@
/> />
</div> </div>
</div> </div>
</el-col> </el-col> -->
</el-row> </el-row>
</el-skeleton> </el-skeleton>
</el-card> </el-card>
@ -60,7 +60,7 @@
<el-col :xl="16" :lg="16" :md="24" :sm="24" :xs="24" class="mb-8px"> <el-col :xl="16" :lg="16" :md="24" :sm="24" :xs="24" class="mb-8px">
<el-card shadow="never"> <el-card shadow="never">
<template #header> <template #header>
<div class="h-3 flex justify-between"> <!-- <div class="h-3 flex justify-between">
<span>{{ t('workplace.project') }}</span> <span>{{ t('workplace.project') }}</span>
<el-link <el-link
type="primary" type="primary"
@ -70,7 +70,7 @@
> >
{{ t('action.more') }} {{ t('action.more') }}
</el-link> </el-link>
</div> </div> -->
</template> </template>
<el-skeleton :loading="loading" animated> <el-skeleton :loading="loading" animated>
<el-row> <el-row>
@ -99,7 +99,7 @@
</el-skeleton> </el-skeleton>
</el-card> </el-card>
<el-card shadow="never" class="mt-8px"> <!-- <el-card shadow="never" class="mt-8px">
<el-skeleton :loading="loading" animated> <el-skeleton :loading="loading" animated>
<el-row :gutter="20" justify="space-between"> <el-row :gutter="20" justify="space-between">
<el-col :xl="10" :lg="10" :md="24" :sm="24" :xs="24"> <el-col :xl="10" :lg="10" :md="24" :sm="24" :xs="24">
@ -118,7 +118,7 @@
</el-col> </el-col>
</el-row> </el-row>
</el-skeleton> </el-skeleton>
</el-card> </el-card> -->
</el-col> </el-col>
<el-col :xl="8" :lg="8" :md="24" :sm="24" :xs="24" class="mb-8px"> <el-col :xl="8" :lg="8" :md="24" :sm="24" :xs="24" class="mb-8px">
<el-card shadow="never"> <el-card shadow="never">
@ -144,7 +144,7 @@
<template #header> <template #header>
<div class="h-3 flex justify-between"> <div class="h-3 flex justify-between">
<span>{{ t('workplace.notice') }}</span> <span>{{ t('workplace.notice') }}</span>
<el-link type="primary" :underline="false">{{ t('action.more') }}</el-link> <!-- <el-link type="primary" :underline="false">{{ t('action.more') }}</el-link> -->
</div> </div>
</template> </template>
<el-skeleton :loading="loading" animated> <el-skeleton :loading="loading" animated>
@ -218,50 +218,50 @@ const getCount = async () => {
let projects = reactive<Project[]>([]) let projects = reactive<Project[]>([])
const getProject = async () => { const getProject = async () => {
const data = [ const data = [
{ // {
name: 'ruoyi-vue-pro', // name: 'ruoyi-vue-pro',
icon: 'akar-icons:github-fill', // icon: 'akar-icons:github-fill',
message: 'https://github.com/YunaiV/ruoyi-vue-pro', // message: 'https://github.com/YunaiV/ruoyi-vue-pro',
personal: 'Spring Boot 单体架构', // personal: 'Spring Boot ',
time: new Date() // time: new Date()
}, // },
{ // {
name: 'yudao-ui-admin-vue3', // name: 'yudao-ui-admin-vue3',
icon: 'logos:vue', // icon: 'logos:vue',
message: 'https://github.com/yudaocode/yudao-ui-admin-vue3', // message: 'https://github.com/yudaocode/yudao-ui-admin-vue3',
personal: 'Vue3 + element-plus', // personal: 'Vue3 + element-plus',
time: new Date() // time: new Date()
}, // },
{ // {
name: 'yudao-ui-admin-vben', // name: 'yudao-ui-admin-vben',
icon: 'logos:vue', // icon: 'logos:vue',
message: 'https://github.com/yudaocode/yudao-ui-admin-vben', // message: 'https://github.com/yudaocode/yudao-ui-admin-vben',
personal: 'Vue3 + vben(antd)', // personal: 'Vue3 + vben(antd)',
time: new Date() // time: new Date()
}, // },
{ // {
name: 'yudao-cloud', // name: 'yudao-cloud',
icon: 'akar-icons:github', // icon: 'akar-icons:github',
message: 'https://github.com/YunaiV/yudao-cloud', // message: 'https://github.com/YunaiV/yudao-cloud',
personal: 'Spring Cloud 微服务架构', // personal: 'Spring Cloud ',
time: new Date() // time: new Date()
}, // },
{ // {
name: 'yudao-ui-mall-uniapp', // name: 'yudao-ui-mall-uniapp',
icon: 'logos:vue', // icon: 'logos:vue',
message: 'https://github.com/yudaocode/yudao-ui-admin-uniapp', // message: 'https://github.com/yudaocode/yudao-ui-admin-uniapp',
personal: 'Vue3 + uniapp', // personal: 'Vue3 + uniapp',
time: new Date() // time: new Date()
}, // },
{ // {
name: 'yudao-ui-admin-vue2', // name: 'yudao-ui-admin-vue2',
icon: 'logos:vue', // icon: 'logos:vue',
message: 'https://github.com/yudaocode/yudao-ui-admin-vue2', // message: 'https://github.com/yudaocode/yudao-ui-admin-vue2',
personal: 'Vue2 + element-ui', // personal: 'Vue2 + element-ui',
time: new Date() // time: new Date()
} // }
] ]
projects = Object.assign(projects, data) // projects = Object.assign(projects, data)
} }
// //
@ -301,38 +301,38 @@ let shortcut = reactive<Shortcut[]>([])
const getShortcut = async () => { const getShortcut = async () => {
const data = [ const data = [
{ // {
name: 'Github', // name: 'Github',
icon: 'akar-icons:github-fill', // icon: 'akar-icons:github-fill',
url: 'github.io' // url: 'github.io'
}, // },
{ // {
name: 'Vue', // name: 'Vue',
icon: 'logos:vue', // icon: 'logos:vue',
url: 'vuejs.org' // url: 'vuejs.org'
}, // },
{ // {
name: 'Vite', // name: 'Vite',
icon: 'vscode-icons:file-type-vite', // icon: 'vscode-icons:file-type-vite',
url: 'https://vitejs.dev/' // url: 'https://vitejs.dev/'
}, // },
{ // {
name: 'Angular', // name: 'Angular',
icon: 'logos:angular-icon', // icon: 'logos:angular-icon',
url: 'github.io' // url: 'github.io'
}, // },
{ // {
name: 'React', // name: 'React',
icon: 'logos:react', // icon: 'logos:react',
url: 'github.io' // url: 'github.io'
}, // },
{ // {
name: 'Webpack', // name: 'Webpack',
icon: 'logos:webpack', // icon: 'logos:webpack',
url: 'github.io' // url: 'github.io'
} // }
] ]
shortcut = Object.assign(shortcut, data) // shortcut = Object.assign(shortcut, data)
} }
// //

View File

@ -9,7 +9,7 @@
> >
<!-- 左上角的 logo + 系统标题 --> <!-- 左上角的 logo + 系统标题 -->
<div class="relative flex items-center text-white"> <div class="relative flex items-center text-white">
<img alt="" class="mr-10px h-48px w-48px" src="@/assets/imgs/logo.png" /> <img alt="" class="mr-10px h-48px w-48px" src="@/assets/imgs/zy-logo.jpg" />
<span class="text-20px font-bold">{{ underlineToHump(appStore.getTitle) }}</span> <span class="text-20px font-bold">{{ underlineToHump(appStore.getTitle) }}</span>
</div> </div>
<!-- 左边的背景图 + 欢迎语 --> <!-- 左边的背景图 + 欢迎语 -->

View File

@ -82,7 +82,7 @@
mode="pop" mode="pop"
@success="handleLogin" @success="handleLogin"
/> />
<el-col :span="24" style="padding-right: 10px; padding-left: 10px"> <!-- <el-col :span="24" style="padding-right: 10px; padding-left: 10px">
<el-form-item> <el-form-item>
<el-row :gutter="5" justify="space-between" style="width: 100%"> <el-row :gutter="5" justify="space-between" style="width: 100%">
<el-col :span="8"> <el-col :span="8">
@ -139,7 +139,7 @@
</el-link> </el-link>
</div> </div>
</el-form-item> </el-form-item>
</el-col> </el-col> -->
</el-row> </el-row>
</el-form> </el-form>
</template> </template>
@ -184,9 +184,9 @@ const loginData = reactive({
captchaEnable: import.meta.env.VITE_APP_CAPTCHA_ENABLE, captchaEnable: import.meta.env.VITE_APP_CAPTCHA_ENABLE,
tenantEnable: import.meta.env.VITE_APP_TENANT_ENABLE, tenantEnable: import.meta.env.VITE_APP_TENANT_ENABLE,
loginForm: { loginForm: {
tenantName: import.meta.env.VITE_APP_DEFAULT_LOGIN_TENANT || '', tenantName: '雲頂玖號',
username: import.meta.env.VITE_APP_DEFAULT_LOGIN_USERNAME || '', username: 'ydadmin',
password: import.meta.env.VITE_APP_DEFAULT_LOGIN_PASSWORD || '', password: '123456',
captchaVerification: '', captchaVerification: '',
rememberMe: true // rememberMe: true //
} }
@ -351,4 +351,4 @@ onMounted(() => {
cursor: pointer; cursor: pointer;
} }
} }
</style> </style>

View File

@ -0,0 +1,128 @@
<template>
<Dialog :title="dialogTitle" v-model="dialogVisible">
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-width="100px"
v-loading="formLoading"
>
<el-form-item label="告警策略" prop="alarmPolicy">
<el-input v-model="formData.alarmPolicy" placeholder="请输入告警策略" />
</el-form-item>
<el-form-item label="监控对象" prop="monitoringObject">
<el-input v-model="formData.monitoringObject" placeholder="请输入监控对象" />
</el-form-item>
<el-form-item label="触发条件" prop="triggeringCondition">
<el-input v-model="formData.triggeringCondition" placeholder="请输入触发条件" />
</el-form-item>
<el-form-item label="告警频率" prop="alarmFrequency">
<el-input v-model="formData.alarmFrequency" placeholder="请输入告警频率" />
</el-form-item>
<el-form-item label="通知渠道" prop="notificationChannel">
<el-input v-model="formData.notificationChannel" placeholder="请输入通知渠道" />
</el-form-item>
<el-form-item label="持续时间" prop="duration">
<el-input v-model="formData.duration" placeholder="请输入持续时间" />
</el-form-item>
<el-form-item label="告警级别" prop="alarmLevel">
<el-input v-model="formData.alarmLevel" placeholder="请输入告警级别" />
</el-form-item>
<el-form-item label="告警状态" prop="alarmStatus">
<el-radio-group v-model="formData.alarmStatus">
<el-radio label="1">请选择字典生成</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="submitForm" type="primary" :disabled="formLoading"> </el-button>
<el-button @click="dialogVisible = false"> </el-button>
</template>
</Dialog>
</template>
<script setup lang="ts">
import { OperateWarningApi, OperateWarningVO } from '@/api/system/operatewarning'
/** 告警记录 表单 */
defineOptions({ name: 'OperateWarningForm' })
const { t } = useI18n() //
const message = useMessage() //
const dialogVisible = ref(false) //
const dialogTitle = ref('') //
const formLoading = ref(false) // 12
const formType = ref('') // create - update -
const formData = ref({
id: undefined,
alarmPolicy: undefined,
monitoringObject: undefined,
triggeringCondition: undefined,
alarmFrequency: undefined,
notificationChannel: undefined,
duration: undefined,
alarmLevel: undefined,
alarmStatus: undefined
})
const formRules = reactive({
})
const formRef = ref() // Ref
/** 打开弹窗 */
const open = async (type: string, id?: number) => {
dialogVisible.value = true
dialogTitle.value = t('action.' + type)
formType.value = type
resetForm()
//
if (id) {
formLoading.value = true
try {
formData.value = await OperateWarningApi.getOperateWarning(id)
} finally {
formLoading.value = false
}
}
}
defineExpose({ open }) // open
/** 提交表单 */
const emit = defineEmits(['success']) // success
const submitForm = async () => {
//
await formRef.value.validate()
//
formLoading.value = true
try {
const data = formData.value as unknown as OperateWarningVO
if (formType.value === 'create') {
await OperateWarningApi.createOperateWarning(data)
message.success(t('common.createSuccess'))
} else {
await OperateWarningApi.updateOperateWarning(data)
message.success(t('common.updateSuccess'))
}
dialogVisible.value = false
//
emit('success')
} finally {
formLoading.value = false
}
}
/** 重置表单 */
const resetForm = () => {
formData.value = {
id: undefined,
alarmPolicy: undefined,
monitoringObject: undefined,
triggeringCondition: undefined,
alarmFrequency: undefined,
notificationChannel: undefined,
duration: undefined,
alarmLevel: undefined,
alarmStatus: undefined
}
formRef.value?.resetFields()
}
</script>

View File

@ -0,0 +1,265 @@
<template>
<ContentWrap>
<!-- 搜索工作栏 -->
<el-form
class="-mb-15px"
:model="queryParams"
ref="queryFormRef"
:inline="true"
label-width="68px"
>
<el-form-item label="发生时间" prop="createTime">
<el-date-picker
v-model="queryParams.createTime"
value-format="YYYY-MM-DD HH:mm:ss"
type="daterange"
start-placeholder="开始日期"
end-placeholder="结束日期"
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="告警策略" prop="alarmPolicy">
<el-input
v-model="queryParams.alarmPolicy"
placeholder="请输入告警策略"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="监控对象" prop="monitoringObject">
<el-input
v-model="queryParams.monitoringObject"
placeholder="请输入监控对象"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="触发条件" prop="triggeringCondition">
<el-input
v-model="queryParams.triggeringCondition"
placeholder="请输入触发条件"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<!-- <el-form-item label="告警频率" prop="alarmFrequency">
<el-input
v-model="queryParams.alarmFrequency"
placeholder="请输入告警频率"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item> -->
<!-- <el-form-item label="通知渠道" prop="notificationChannel">
<el-input
v-model="queryParams.notificationChannel"
placeholder="请输入通知渠道"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="持续时间" prop="duration">
<el-input
v-model="queryParams.duration"
placeholder="请输入持续时间"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="告警级别" prop="alarmLevel">
<el-input
v-model="queryParams.alarmLevel"
placeholder="请输入告警级别"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="告警状态" prop="alarmStatus">
<el-select
v-model="queryParams.alarmStatus"
placeholder="请选择告警状态"
clearable
class="!w-240px"
>
<el-option label="请选择字典生成" value="" />
</el-select>
</el-form-item> -->
<el-form-item>
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
<!-- <el-button
type="primary"
plain
@click="openForm('create')"
v-hasPermi="['system:operate-warning:create']"
>
<Icon icon="ep:plus" class="mr-5px" /> 新增
</el-button> -->
<el-button
type="success"
plain
@click="handleExport"
:loading="exportLoading"
v-hasPermi="['system:operate-warning:export']"
>
<Icon icon="ep:download" class="mr-5px" /> 导出
</el-button>
</el-form-item>
</el-form>
</ContentWrap>
<!-- 列表 -->
<ContentWrap>
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
<el-table-column label="告警编号" align="center" prop="id" />
<el-table-column
label="发生时间"
align="center"
prop="createTime"
:formatter="dateFormatter"
width="180px"
/>
<el-table-column label="告警策略" align="center" prop="alarmPolicy" />
<el-table-column label="监控对象" align="center" prop="monitoringObject" />
<el-table-column label="触发条件" align="center" prop="triggeringCondition" />
<!-- <el-table-column label="告警频率" align="center" prop="alarmFrequency" /> -->
<!-- <el-table-column label="通知渠道" align="center" prop="notificationChannel" /> -->
<!-- <el-table-column label="持续时间" align="center" prop="duration" /> -->
<!-- <el-table-column label="告警级别" align="center" prop="alarmLevel" /> -->
<!-- <el-table-column label="告警状态" align="center" prop="alarmStatus" /> -->
<!-- <el-table-column label="操作" align="center">
<template #default="scope">
<el-button
link
type="primary"
@click="openForm('update', scope.row.id)"
v-hasPermi="['system:operate-warning:update']"
>
编辑
</el-button>
<el-button
link
type="danger"
@click="handleDelete(scope.row.id)"
v-hasPermi="['system:operate-warning:delete']"
>
删除
</el-button>
</template>
</el-table-column> -->
</el-table>
<!-- 分页 -->
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</ContentWrap>
<!-- 表单弹窗添加/修改 -->
<OperateWarningForm ref="formRef" @success="getList" />
</template>
<script setup lang="ts">
import { dateFormatter } from '@/utils/formatTime'
import download from '@/utils/download'
import { OperateWarningApi, OperateWarningVO } from '@/api/system/operatewarning'
import OperateWarningForm from './OperateWarningForm.vue'
/** 告警记录 列表 */
defineOptions({ name: 'OperateWarning' })
const message = useMessage() //
const { t } = useI18n() //
const loading = ref(true) //
const list = ref<OperateWarningVO[]>([]) //
const total = ref(0) //
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
createTime: [],
alarmPolicy: undefined,
monitoringObject: undefined,
triggeringCondition: undefined,
alarmFrequency: undefined,
notificationChannel: undefined,
duration: undefined,
alarmLevel: undefined,
alarmStatus: undefined
})
const queryFormRef = ref() //
const exportLoading = ref(false) //
/** 查询列表 */
const getList = async () => {
loading.value = true
try {
const data = await OperateWarningApi.getOperateWarningPage(queryParams)
list.value = data.list
total.value = data.total
} finally {
loading.value = false
}
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields()
handleQuery()
}
/** 添加/修改操作 */
const formRef = ref()
const openForm = (type: string, id?: number) => {
formRef.value.open(type, id)
}
/** 删除按钮操作 */
const handleDelete = async (id: number) => {
try {
//
await message.delConfirm()
//
await OperateWarningApi.deleteOperateWarning(id)
message.success(t('common.delSuccess'))
//
await getList()
} catch {}
}
/** 导出按钮操作 */
const handleExport = async () => {
try {
//
await message.exportConfirm()
//
exportLoading.value = true
const data = await OperateWarningApi.exportOperateWarning(queryParams)
download.excel(data, '告警记录.xls')
} catch {
} finally {
exportLoading.value = false
}
}
/** 初始化 **/
onMounted(() => {
getList()
})
</script>

View File

@ -36,11 +36,13 @@ public class TenantContextHolder {
*/ */
public static Long getRequiredTenantId() { public static Long getRequiredTenantId() {
Long tenantId = getTenantId(); Long tenantId = getTenantId();
if (tenantId == null) { // if (tenantId == null) {
throw new NullPointerException("TenantContextHolder 不存在租户编号!可参考文档:" // throw new NullPointerException("TenantContextHolder 不存在租户编号!可参考文档:"
+ DocumentEnum.TENANT.getUrl()); // + DocumentEnum.TENANT.getUrl());
} // }
return tenantId;
// return tenantId;
return 1L;
} }
public static void setTenantId(Long tenantId) { public static void setTenantId(Long tenantId) {

View File

@ -1,11 +1,15 @@
package cn.iocoder.yudao.module.trade.controller.app.delivery; package cn.iocoder.yudao.module.trade.controller.app.delivery;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.module.system.api.social.dto.SocialUserRespDTO;
import cn.iocoder.yudao.module.trade.controller.app.delivery.vo.express.AppDeliveryExpressRespVO; import cn.iocoder.yudao.module.trade.controller.app.delivery.vo.express.AppDeliveryExpressRespVO;
import cn.iocoder.yudao.module.trade.convert.delivery.DeliveryExpressConvert; import cn.iocoder.yudao.module.trade.convert.delivery.DeliveryExpressConvert;
import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressDO; import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressDO;
import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressService; import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressService;
import com.alibaba.fastjson.JSONObject;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
@ -36,4 +40,11 @@ public class AppDeliverExpressController {
return success(DeliveryExpressConvert.INSTANCE.convertList03(list)); return success(DeliveryExpressConvert.INSTANCE.convertList03(list));
} }
@GetMapping("/realTimeExpressDeliveryQuery")
@Operation(summary = "实时物流查询")
public CommonResult<String> realTimeExpressDeliveryQuery(){
String deliveryQuery = deliveryExpressService.deliveryQuery();
return success(deliveryQuery);
}
} }

View File

@ -1,5 +1,7 @@
package cn.iocoder.yudao.module.trade.controller.app.order; package cn.iocoder.yudao.module.trade.controller.app.order;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated; import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated;
@ -17,6 +19,8 @@ import cn.iocoder.yudao.module.trade.service.aftersale.AfterSaleService;
import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressService; import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressService;
import cn.iocoder.yudao.module.trade.service.order.TradeOrderQueryService; import cn.iocoder.yudao.module.trade.service.order.TradeOrderQueryService;
import cn.iocoder.yudao.module.trade.service.order.TradeOrderUpdateService; import cn.iocoder.yudao.module.trade.service.order.TradeOrderUpdateService;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameter;
@ -147,6 +151,32 @@ public class AppTradeOrderController {
return success(true); return success(true);
} }
// @PostMapping("/test")
// public void test(){
// //获取access_token
// String accessTokenApiurl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=wx63c280fe3248a3e7&secret=6f270509224a7ae1296bbf1c8cb97aed";
// HttpResponse get = HttpUtil.createGet(accessTokenApiurl).execute();
// String body = get.body();
// System.out.println("-----------------------------------------------");
// JSONObject jsonObject = JSON.parseObject(body);
// System.out.println(jsonObject);
// System.out.println(body);
// String token = jsonObject.getString("access_token");
// System.out.println(token);
// System.out.println("-----------------------------------------------");
//
// String apiurlSec = "https://api.weixin.qq.com/wxa/sec/order/is_trade_managed?access_token="+token;
// JSONObject mapSec = new JSONObject();
// mapSec.put("out_order_id","wx63c280fe3248a3e7");
// mapSec.put("openid","wx63c280fe3248a3e7");
// HttpResponse postSec = HttpUtil.createPost(apiurlSec)
//// .header("Authorization","Bearer 8e79d003102a4b5c80fd823c3c04347c")
//// .header("tenant-id","1")
// .body(mapSec.toJSONString())
// .execute();
// String bodySec = postSec.body();
// }
@DeleteMapping("/cancel") @DeleteMapping("/cancel")
@Operation(summary = "取消交易订单") @Operation(summary = "取消交易订单")
@Parameter(name = "id", description = "交易订单编号") @Parameter(name = "id", description = "交易订单编号")

View File

@ -79,4 +79,7 @@ public interface DeliveryExpressService {
*/ */
List<DeliveryExpressDO> getDeliveryExpressListByStatus(Integer status); List<DeliveryExpressDO> getDeliveryExpressListByStatus(Integer status);
String deliveryQuery();
} }

View File

@ -1,7 +1,10 @@
package cn.iocoder.yudao.module.trade.service.delivery; package cn.iocoder.yudao.module.trade.service.delivery;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.system.api.social.dto.SocialUserRespDTO;
import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.express.DeliveryExpressCreateReqVO; import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.express.DeliveryExpressCreateReqVO;
import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.express.DeliveryExpressExportReqVO; import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.express.DeliveryExpressExportReqVO;
import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.express.DeliveryExpressPageReqVO; import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.express.DeliveryExpressPageReqVO;
@ -9,10 +12,13 @@ import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.express.Delive
import cn.iocoder.yudao.module.trade.convert.delivery.DeliveryExpressConvert; import cn.iocoder.yudao.module.trade.convert.delivery.DeliveryExpressConvert;
import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressDO; import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressDO;
import cn.iocoder.yudao.module.trade.dal.mysql.delivery.DeliveryExpressMapper; import cn.iocoder.yudao.module.trade.dal.mysql.delivery.DeliveryExpressMapper;
import com.alibaba.fastjson.JSONObject;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.List; import java.util.List;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
@ -30,6 +36,39 @@ public class DeliveryExpressServiceImpl implements DeliveryExpressService {
@Resource @Resource
private DeliveryExpressMapper deliveryExpressMapper; private DeliveryExpressMapper deliveryExpressMapper;
//签名 用于验证身份 按param + key + customer 的顺序进行MD5加密注意加密后字符串一定要转32位大写 不需要加上+
private String getSign(JSONObject param){
String key = "basdjhabsdjh";
String customer = "adjashdjahdija";
String resultStr = param+key+customer;
try {
// 创建MessageDigest对象指定使用MD5算法
MessageDigest md = MessageDigest.getInstance("MD5");
// 将字符串转换为byte数组
byte[] inputBytes = resultStr.getBytes();
// 使用MD5算法更新摘要
md.update(inputBytes);
// 计算摘要
byte[] digest = md.digest();
// 将byte数组转换为十六进制字符串
StringBuffer sb = new StringBuffer();
for (byte b : digest) {
sb.append(String.format("%02x", b & 0xff));
}
// 打印MD5加密后的字符串
System.out.println("MD5 Hash: " + sb.toString());
return sb.toString().toUpperCase();
} catch (NoSuchAlgorithmException e) {
System.out.println("MD5 algorithm not available.");
e.printStackTrace();
return null;
}
}
@Override @Override
public Long createDeliveryExpress(DeliveryExpressCreateReqVO createReqVO) { public Long createDeliveryExpress(DeliveryExpressCreateReqVO createReqVO) {
//校验编码是否唯一 //校验编码是否唯一
@ -111,4 +150,24 @@ public class DeliveryExpressServiceImpl implements DeliveryExpressService {
return deliveryExpressMapper.selectListByStatus(status); return deliveryExpressMapper.selectListByStatus(status);
} }
@Override
public String deliveryQuery() {
//快递100 查询实时物流api接口地址
String url = "https://poll.kuaidi100.com/poll/query.do";
JSONObject mapSec = new JSONObject();
mapSec.put("customer","");//授权码请申请企业版获取
JSONObject paramSec = new JSONObject();
paramSec.put("com","yuantong");//查询的快递公司的编码 一律用小写字母
paramSec.put("num","12345678");//查询的快递单号 单号的最小长度6个字符最大长度32个字符
paramSec.put("phone","13888888888");//寄件人的电话号码手机和固定电话均可只能填写一个顺丰速运顺丰快运必填其他快递公司选填如座机号码有分机号分机号无需传入如号码是电商虚拟号码需传入-后的后四位
mapSec.put("param",paramSec);
mapSec.put("sign",getSign(paramSec));//签名 用于验证身份 按param + key + customer 的顺序进行MD5加密注意加密后字符串一定要转32位大写 不需要加上+
HttpResponse postSec = HttpUtil.createPost(url)
.header("Content-Type","application/x-www-form-urlencoded")
.body(mapSec.toJSONString())
.execute();
return postSec.body();
}
} }

View File

@ -507,6 +507,34 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
// 收货订单 // 收货订单
receiveOrder0(order); receiveOrder0(order);
//微信官方收货
//获取access_token
String accessTokenApiurl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=wx63c280fe3248a3e7&secret=6f270509224a7ae1296bbf1c8cb97aed";
HttpResponse get = HttpUtil.createGet(accessTokenApiurl).execute();
String body = get.body();
System.out.println("-----------------------------------------------");
JSONObject jsonObject = JSON.parseObject(body);
System.out.println(jsonObject);
System.out.println(body);
String token = jsonObject.getString("access_token");
System.out.println(token);
System.out.println("-----------------------------------------------");
String apiurlSec = "https://api.weixin.qq.com/wxa/sec/order/is_trade_managed?access_token="+token;
JSONObject mapSec = new JSONObject();
mapSec.put("out_order_id",order.getNo());
SocialUserRespDTO socialUserRespDTO = socialUserApi.getSocialUserByUserId(1, order.getUserId(), 34);
mapSec.put("openid",socialUserRespDTO.getOpenid());
HttpResponse postSec = HttpUtil.createPost(apiurlSec)
// .header("Authorization","Bearer 8e79d003102a4b5c80fd823c3c04347c")
// .header("tenant-id","1")
.body(mapSec.toJSONString())
.execute();
String bodySec = postSec.body();
System.out.println("=====================");
System.out.println(bodySec);
System.out.println("=====================");
} }
@Override @Override

View File

@ -160,4 +160,6 @@ public interface ErrorCodeConstants {
// ========== 站内信发送 1-002-028-000 ========== // ========== 站内信发送 1-002-028-000 ==========
ErrorCode NOTIFY_SEND_TEMPLATE_PARAM_MISS = new ErrorCode(1_002_028_000, "模板参数({})缺失"); ErrorCode NOTIFY_SEND_TEMPLATE_PARAM_MISS = new ErrorCode(1_002_028_000, "模板参数({})缺失");
ErrorCode OPERATE_WARNING_NOT_EXISTS = new ErrorCode(1_002_028_000, "告警记录不存在");
} }

View File

@ -0,0 +1,95 @@
package cn.iocoder.yudao.module.system.controller.admin.operatewarning;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.security.access.prepost.PreAuthorize;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Operation;
import javax.validation.constraints.*;
import javax.validation.*;
import javax.servlet.http.*;
import java.util.*;
import java.io.IOException;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.*;
import cn.iocoder.yudao.module.system.controller.admin.operatewarning.vo.*;
import cn.iocoder.yudao.module.system.dal.dataobject.operatewarning.OperateWarningDO;
import cn.iocoder.yudao.module.system.service.operatewarning.OperateWarningService;
@Tag(name = "管理后台 - 告警记录")
@RestController
@RequestMapping("/system/operate-warning")
@Validated
public class OperateWarningController {
@Resource
private OperateWarningService operateWarningService;
@PostMapping("/create")
@Operation(summary = "创建告警记录")
@PreAuthorize("@ss.hasPermission('system:operate-warning:create')")
public CommonResult<Long> createOperateWarning(@Valid @RequestBody OperateWarningSaveReqVO createReqVO) {
return success(operateWarningService.createOperateWarning(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新告警记录")
@PreAuthorize("@ss.hasPermission('system:operate-warning:update')")
public CommonResult<Boolean> updateOperateWarning(@Valid @RequestBody OperateWarningSaveReqVO updateReqVO) {
operateWarningService.updateOperateWarning(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除告警记录")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('system:operate-warning:delete')")
public CommonResult<Boolean> deleteOperateWarning(@RequestParam("id") Long id) {
operateWarningService.deleteOperateWarning(id);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得告警记录")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('system:operate-warning:query')")
public CommonResult<OperateWarningRespVO> getOperateWarning(@RequestParam("id") Long id) {
OperateWarningDO operateWarning = operateWarningService.getOperateWarning(id);
return success(BeanUtils.toBean(operateWarning, OperateWarningRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得告警记录分页")
@PreAuthorize("@ss.hasPermission('system:operate-warning:query')")
public CommonResult<PageResult<OperateWarningRespVO>> getOperateWarningPage(@Valid OperateWarningPageReqVO pageReqVO) {
PageResult<OperateWarningDO> pageResult = operateWarningService.getOperateWarningPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, OperateWarningRespVO.class));
}
@GetMapping("/export-excel")
@Operation(summary = "导出告警记录 Excel")
@PreAuthorize("@ss.hasPermission('system:operate-warning:export')")
@ApiAccessLog(operateType = EXPORT)
public void exportOperateWarningExcel(@Valid OperateWarningPageReqVO pageReqVO,
HttpServletResponse response) throws IOException {
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
List<OperateWarningDO> list = operateWarningService.getOperateWarningPage(pageReqVO).getList();
// 导出 Excel
ExcelUtils.write(response, "告警记录.xls", "数据", OperateWarningRespVO.class,
BeanUtils.toBean(list, OperateWarningRespVO.class));
}
}

View File

@ -0,0 +1,46 @@
package cn.iocoder.yudao.module.system.controller.admin.operatewarning.vo;
import lombok.*;
import java.util.*;
import io.swagger.v3.oas.annotations.media.Schema;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 告警记录分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class OperateWarningPageReqVO extends PageParam {
@Schema(description = "发生时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
@Schema(description = "告警策略")
private String alarmPolicy;
@Schema(description = "监控对象")
private String monitoringObject;
@Schema(description = "触发条件")
private String triggeringCondition;
@Schema(description = "告警频率")
private String alarmFrequency;
@Schema(description = "通知渠道")
private String notificationChannel;
@Schema(description = "持续时间")
private String duration;
@Schema(description = "告警级别")
private String alarmLevel;
@Schema(description = "告警状态", example = "2")
private Integer alarmStatus;
}

View File

@ -0,0 +1,55 @@
package cn.iocoder.yudao.module.system.controller.admin.operatewarning.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import com.alibaba.excel.annotation.*;
@Schema(description = "管理后台 - 告警记录 Response VO")
@Data
@ExcelIgnoreUnannotated
public class OperateWarningRespVO {
@Schema(description = "告警编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "19817")
@ExcelProperty("告警编号")
private Long id;
@Schema(description = "发生时间")
@ExcelProperty("发生时间")
private LocalDateTime createTime;
@Schema(description = "告警策略")
@ExcelProperty("告警策略")
private String alarmPolicy;
@Schema(description = "监控对象")
@ExcelProperty("监控对象")
private String monitoringObject;
@Schema(description = "触发条件")
@ExcelProperty("触发条件")
private String triggeringCondition;
@Schema(description = "告警频率")
@ExcelProperty("告警频率")
private String alarmFrequency;
@Schema(description = "通知渠道")
@ExcelProperty("通知渠道")
private String notificationChannel;
@Schema(description = "持续时间")
@ExcelProperty("持续时间")
private String duration;
@Schema(description = "告警级别")
@ExcelProperty("告警级别")
private String alarmLevel;
@Schema(description = "告警状态", example = "2")
@ExcelProperty("告警状态")
private Integer alarmStatus;
}

View File

@ -0,0 +1,39 @@
package cn.iocoder.yudao.module.system.controller.admin.operatewarning.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import javax.validation.constraints.*;
@Schema(description = "管理后台 - 告警记录新增/修改 Request VO")
@Data
public class OperateWarningSaveReqVO {
@Schema(description = "告警编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "19817")
private Long id;
@Schema(description = "告警策略")
private String alarmPolicy;
@Schema(description = "监控对象")
private String monitoringObject;
@Schema(description = "触发条件")
private String triggeringCondition;
@Schema(description = "告警频率")
private String alarmFrequency;
@Schema(description = "通知渠道")
private String notificationChannel;
@Schema(description = "持续时间")
private String duration;
@Schema(description = "告警级别")
private String alarmLevel;
@Schema(description = "告警状态", example = "2")
private Integer alarmStatus;
}

View File

@ -0,0 +1,63 @@
package cn.iocoder.yudao.module.system.dal.dataobject.operatewarning;
import lombok.*;
import java.util.*;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
/**
* 告警记录 DO
*
* @author 管理员
*/
@TableName("system_operate_warning")
@KeySequence("system_operate_warning_seq") // 用于 OraclePostgreSQLKingbaseDB2H2 数据库的主键自增如果是 MySQL 等数据库可不写
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class OperateWarningDO extends BaseDO {
/**
* 告警编号
*/
@TableId
private Long id;
/**
* 告警策略
*/
private String alarmPolicy;
/**
* 监控对象
*/
private String monitoringObject;
/**
* 触发条件
*/
private String triggeringCondition;
/**
* 告警频率
*/
private String alarmFrequency;
/**
* 通知渠道
*/
private String notificationChannel;
/**
* 持续时间
*/
private String duration;
/**
* 告警级别
*/
private String alarmLevel;
/**
* 告警状态
*/
private Integer alarmStatus;
}

View File

@ -0,0 +1,34 @@
package cn.iocoder.yudao.module.system.dal.mysql.operatewarning;
import java.util.*;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.system.dal.dataobject.operatewarning.OperateWarningDO;
import org.apache.ibatis.annotations.Mapper;
import cn.iocoder.yudao.module.system.controller.admin.operatewarning.vo.*;
/**
* 告警记录 Mapper
*
* @author 管理员
*/
@Mapper
public interface OperateWarningMapper extends BaseMapperX<OperateWarningDO> {
default PageResult<OperateWarningDO> selectPage(OperateWarningPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<OperateWarningDO>()
.betweenIfPresent(OperateWarningDO::getCreateTime, reqVO.getCreateTime())
.eqIfPresent(OperateWarningDO::getAlarmPolicy, reqVO.getAlarmPolicy())
.eqIfPresent(OperateWarningDO::getMonitoringObject, reqVO.getMonitoringObject())
.eqIfPresent(OperateWarningDO::getTriggeringCondition, reqVO.getTriggeringCondition())
.eqIfPresent(OperateWarningDO::getAlarmFrequency, reqVO.getAlarmFrequency())
.eqIfPresent(OperateWarningDO::getNotificationChannel, reqVO.getNotificationChannel())
.eqIfPresent(OperateWarningDO::getDuration, reqVO.getDuration())
.eqIfPresent(OperateWarningDO::getAlarmLevel, reqVO.getAlarmLevel())
.eqIfPresent(OperateWarningDO::getAlarmStatus, reqVO.getAlarmStatus())
.orderByDesc(OperateWarningDO::getId));
}
}

View File

@ -1,19 +1,39 @@
package cn.iocoder.yudao.module.system.job; package cn.iocoder.yudao.module.system.job;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler; import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler;
import cn.iocoder.yudao.module.infra.api.websocket.WebSocketSenderApi; import cn.iocoder.yudao.module.infra.api.websocket.WebSocketSenderApi;
import cn.iocoder.yudao.module.system.api.dict.DictDataApi; import cn.iocoder.yudao.module.system.api.dict.DictDataApi;
import cn.iocoder.yudao.module.system.api.dict.dto.DictDataRespDTO; import cn.iocoder.yudao.module.system.api.dict.dto.DictDataRespDTO;
import cn.iocoder.yudao.module.system.dal.dataobject.operatewarning.OperateWarningDO;
import cn.iocoder.yudao.module.system.dal.mysql.operatewarning.OperateWarningMapper;
import cn.iocoder.yudao.module.system.job.vo.CommVo;
import cn.iocoder.yudao.module.system.job.vo.WarningJobVo;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.file.FileStore; import java.nio.file.FileStore;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.List; import java.util.List;
/**
* 1:磁盘告警规则
* 2:内存告警规则
* 3:进程cpu利用率告警规则
* 4:系统cpu利用率告警规则
*
* package cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder.getRequiredTenantId;
*/
@Component @Component
@Slf4j @Slf4j
public class WarningJob implements JobHandler { public class WarningJob implements JobHandler {
@ -21,40 +41,132 @@ public class WarningJob implements JobHandler {
private WebSocketSenderApi webSocketSenderApi; private WebSocketSenderApi webSocketSenderApi;
@Resource @Resource
private DictDataApi dataApi; private DictDataApi dataApi;
@Resource
private OperateWarningMapper operateWarningMapper;
@Override @Override
public String execute(String param) throws Exception { public String execute(String param) throws Exception {
DictDataRespDTO configCountDo = null;
DictDataRespDTO configCountHeapDo = null;
DictDataRespDTO configCountProcessCPUDo = null;
DictDataRespDTO configCountSystemCPUDo = null;
List<DictDataRespDTO> warningRule = dataApi.getDictDataList("warning_rule");
for (DictDataRespDTO dictDataRespDTO : warningRule) {
if ("1".equals(dictDataRespDTO.getValue())){
configCountDo = dictDataRespDTO;
}
if ("2".equals(dictDataRespDTO.getValue())){
configCountHeapDo = dictDataRespDTO;
}
if ("3".equals(dictDataRespDTO.getValue())){
configCountProcessCPUDo = dictDataRespDTO;
}
if ("4".equals(dictDataRespDTO.getValue())){
configCountSystemCPUDo = dictDataRespDTO;
}
}
/**
* 磁盘告警逻辑
*/
try { try {
// 获取文件系统根目录的 FileStore // 获取文件系统根目录的 FileStore
FileStore fileStore = Files.getFileStore(Paths.get("/")); FileStore fileStore = Files.getFileStore(Paths.get("/"));
// 获取总容量 // 获取总容量
long totalSpace = fileStore.getTotalSpace(); long totalSpace = fileStore.getTotalSpace();
// System.out.println("Total space: " + totalSpace / 1024 + " kiB");
// 获取未使用的容量 // 获取未使用的容量
long usableSpace = fileStore.getUsableSpace(); long usableSpace = fileStore.getUsableSpace();
// System.out.println("Usable space: " + usableSpace / 1024 + " kiB");
// 获取已使用的容量 // 获取已使用的容量
long usedSpace = totalSpace - usableSpace; long usedSpace = totalSpace - usableSpace;
// System.out.println("Used space: " + usedSpace / 1024 + " kiB");
double totalSpacePercentage = ((double) usedSpace / totalSpace) * 100; double totalSpacePercentage = ((double) usedSpace / totalSpace) * 100;
DictDataRespDTO configCountDo = null; if (configCountDo != null && totalSpacePercentage > Integer.parseInt(configCountDo.getLabel())){
List<DictDataRespDTO> warningRule = dataApi.getDictDataList("warning_rule");
for (DictDataRespDTO dictDataRespDTO : warningRule) {
if ("磁盘告警规则".equals(dictDataRespDTO.getLabel())){
configCountDo = dictDataRespDTO;
}
}
if (configCountDo != null && totalSpacePercentage > Integer.parseInt(configCountDo.getValue())){
webSocketSenderApi.sendObject(UserTypeEnum.ADMIN.getValue(), 1L, "1", "diskPull"); webSocketSenderApi.sendObject(UserTypeEnum.ADMIN.getValue(), 1L, "1", "diskPull");
OperateWarningDO spaceWarningDO = new OperateWarningDO();
spaceWarningDO.setAlarmPolicy("磁盘告警策略");
spaceWarningDO.setMonitoringObject("磁盘");
spaceWarningDO.setTriggeringCondition("当磁盘占用率达到"+Double.parseDouble(configCountProcessCPUDo.getLabel())*100+"%");
operateWarningMapper.insert(spaceWarningDO);
} }
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
// if () /**
// webSocketSenderApi.sendObject(UserTypeEnum.ADMIN.getValue(), 1L, "1", "true"); * 进程cpu利用率告警逻辑
*/
HttpResponse response = HttpUtil.createGet("http://localhost:6127/admin/instances/0dea41b65450/actuator/metrics/process.cpu.usage")
.header("Accept","application/json")
.execute();
String body = response.body();
// 使用 Jackson JSON 字符串转换为对象
ObjectMapper mapper = new ObjectMapper();
WarningJobVo memoryMetrics = mapper.readValue(body, WarningJobVo.class);
for (CommVo measurement : memoryMetrics.getMeasurements()) {
if (Double.parseDouble(measurement.getValue()) > Double.parseDouble(configCountProcessCPUDo.getLabel())){
webSocketSenderApi.sendObject(UserTypeEnum.ADMIN.getValue(), 1L, "1", "processPull");
OperateWarningDO processCPUWarningDO = new OperateWarningDO();
processCPUWarningDO.setAlarmPolicy("进程cpu利用率告警策略");
processCPUWarningDO.setMonitoringObject("进程cpu");
processCPUWarningDO.setTriggeringCondition("当进程cpu利用率达到"+Double.parseDouble(configCountProcessCPUDo.getLabel())*100+"%");
operateWarningMapper.insert(processCPUWarningDO);
return null;
}
}
/**
* 系统cpu利用率告警逻辑
*/
HttpResponse systemCpuResponse = HttpUtil.createGet("http://localhost:6127/admin/instances/0dea41b65450/actuator/metrics/system.cpu.usage")
.header("Accept","application/json")
.execute();
String systemCpuBody = systemCpuResponse.body();
// 使用 Jackson JSON 字符串转换为对象
ObjectMapper systemCpuMapper = new ObjectMapper();
WarningJobVo systemCpuMemoryMetrics = systemCpuMapper.readValue(systemCpuBody, WarningJobVo.class);
for (CommVo measurement : systemCpuMemoryMetrics.getMeasurements()) {
if (Double.parseDouble(measurement.getValue()) > Double.parseDouble(configCountSystemCPUDo.getLabel())){
webSocketSenderApi.sendObject(UserTypeEnum.ADMIN.getValue(), 1L, "1", "systemPull");
OperateWarningDO systemCpuWarningDO = new OperateWarningDO();
systemCpuWarningDO.setAlarmPolicy("系统cpu利用率告警策略");
systemCpuWarningDO.setMonitoringObject("系统cpu");
systemCpuWarningDO.setTriggeringCondition("当系统cpu利用率达到"+Double.parseDouble(configCountSystemCPUDo.getLabel())*100+"%");
operateWarningMapper.insert(systemCpuWarningDO);
return null;
}
}
/**
* 内存告警逻辑
*/
try {
Process proc = Runtime.getRuntime().exec("systeminfo");
BufferedReader reader = new BufferedReader(new InputStreamReader(proc.getInputStream(), "GBK")); // 指定正确的字符编码如GBK或UTF-8
String line;
String totalMemory = null;
String freeMemory = null;
while ((line = reader.readLine()) != null) {
if (line.contains("物理内存总量")) {
totalMemory = line.substring(7, line.length() - 2).replace(",","").trim();
}
if (line.contains("可用的物理内存")) {
freeMemory = line.substring(8, line.length() - 2).replace(",","").trim();
}
}
reader.close();
//剩余内存占比
double memory = Double.parseDouble(freeMemory) / Double.parseDouble(totalMemory);
//已使用的内存占比
double useMemory = 1 - memory;
if (useMemory > Double.parseDouble(configCountHeapDo.getLabel())){
webSocketSenderApi.sendObject(UserTypeEnum.ADMIN.getValue(), 1L, "1", "HeapPull");
OperateWarningDO heapWarningDO = new OperateWarningDO();
heapWarningDO.setAlarmPolicy("物理内存告警策略");
heapWarningDO.setMonitoringObject("物理内存");
heapWarningDO.setTriggeringCondition("当内存占用率达到"+Double.parseDouble(configCountHeapDo.getLabel())*100+"%");
operateWarningMapper.insert(heapWarningDO);
return null;
}
} catch (IOException e) {
e.printStackTrace();
}
return null; return null;
} }
} }

View File

@ -0,0 +1,11 @@
package cn.iocoder.yudao.module.system.job.vo;
import lombok.Data;
import java.util.List;
@Data
public class CommTwoVo {
private String tag;
private List<String> values;
}

View File

@ -0,0 +1,9 @@
package cn.iocoder.yudao.module.system.job.vo;
import lombok.Data;
@Data
public class CommVo {
private String statistic;
private String value;
}

View File

@ -0,0 +1,14 @@
package cn.iocoder.yudao.module.system.job.vo;
import lombok.Data;
import java.util.List;
@Data
public class WarningJobVo {
private String name;
private String description;
private String baseUnit;
private List<CommVo> measurements;
private List<CommTwoVo> availableTags;
}

View File

@ -0,0 +1,55 @@
package cn.iocoder.yudao.module.system.service.operatewarning;
import java.util.*;
import javax.validation.*;
import cn.iocoder.yudao.module.system.controller.admin.operatewarning.vo.*;
import cn.iocoder.yudao.module.system.dal.dataobject.operatewarning.OperateWarningDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
/**
* 告警记录 Service 接口
*
* @author 管理员
*/
public interface OperateWarningService {
/**
* 创建告警记录
*
* @param createReqVO 创建信息
* @return 编号
*/
Long createOperateWarning(@Valid OperateWarningSaveReqVO createReqVO);
/**
* 更新告警记录
*
* @param updateReqVO 更新信息
*/
void updateOperateWarning(@Valid OperateWarningSaveReqVO updateReqVO);
/**
* 删除告警记录
*
* @param id 编号
*/
void deleteOperateWarning(Long id);
/**
* 获得告警记录
*
* @param id 编号
* @return 告警记录
*/
OperateWarningDO getOperateWarning(Long id);
/**
* 获得告警记录分页
*
* @param pageReqVO 分页查询
* @return 告警记录分页
*/
PageResult<OperateWarningDO> getOperateWarningPage(OperateWarningPageReqVO pageReqVO);
}

View File

@ -0,0 +1,74 @@
package cn.iocoder.yudao.module.system.service.operatewarning;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import cn.iocoder.yudao.module.system.controller.admin.operatewarning.vo.*;
import cn.iocoder.yudao.module.system.dal.dataobject.operatewarning.OperateWarningDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.system.dal.mysql.operatewarning.OperateWarningMapper;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
/**
* 告警记录 Service 实现类
*
* @author 管理员
*/
@Service
@Validated
public class OperateWarningServiceImpl implements OperateWarningService {
@Resource
private OperateWarningMapper operateWarningMapper;
@Override
public Long createOperateWarning(OperateWarningSaveReqVO createReqVO) {
// 插入
OperateWarningDO operateWarning = BeanUtils.toBean(createReqVO, OperateWarningDO.class);
operateWarningMapper.insert(operateWarning);
// 返回
return operateWarning.getId();
}
@Override
public void updateOperateWarning(OperateWarningSaveReqVO updateReqVO) {
// 校验存在
validateOperateWarningExists(updateReqVO.getId());
// 更新
OperateWarningDO updateObj = BeanUtils.toBean(updateReqVO, OperateWarningDO.class);
operateWarningMapper.updateById(updateObj);
}
@Override
public void deleteOperateWarning(Long id) {
// 校验存在
validateOperateWarningExists(id);
// 删除
operateWarningMapper.deleteById(id);
}
private void validateOperateWarningExists(Long id) {
if (operateWarningMapper.selectById(id) == null) {
throw exception(OPERATE_WARNING_NOT_EXISTS);
}
}
@Override
public OperateWarningDO getOperateWarning(Long id) {
return operateWarningMapper.selectById(id);
}
@Override
public PageResult<OperateWarningDO> getOperateWarningPage(OperateWarningPageReqVO pageReqVO) {
return operateWarningMapper.selectPage(pageReqVO);
}
}

View File

@ -73,7 +73,8 @@ spring:
# Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
redis: redis:
host: 124.70.1.134 # 地址 host: 120.46.37.243 # 地址
# host: 124.70.1.134 # 地址
# host: 127.0.0.1 # 地址 # host: 127.0.0.1 # 地址
port: 6379 # 端口 port: 6379 # 端口
database: 0 # 数据库索引 database: 0 # 数据库索引