fix(iot): 完善场景联动配置必填校验
- Vue3 抽取场景联动校验工具,统一触发器、附加条件和执行器校验 - Vben5 antd/ele 同步场景联动提交前兜底校验 - 补充 CRON 表达式、JSON 参数和动态字段必填校验 - 保留 deviceId=0 表示全部设备的业务语义pull/881/head
parent
8405a07dd0
commit
2e68890c7d
|
|
@ -39,8 +39,7 @@ import ActionSection from './sections/ActionSection.vue'
|
||||||
import { IotSceneRule } from '@/api/iot/rule/scene'
|
import { IotSceneRule } from '@/api/iot/rule/scene'
|
||||||
import { RuleSceneApi } from '@/api/iot/rule/scene'
|
import { RuleSceneApi } from '@/api/iot/rule/scene'
|
||||||
import { IotRuleSceneTriggerTypeEnum } from '@/views/iot/utils/constants'
|
import { IotRuleSceneTriggerTypeEnum } from '@/views/iot/utils/constants'
|
||||||
import { validateTriggerItem } from './utils/triggerConditionRules'
|
import { validateActionItem, validateTriggerItem } from '@/views/iot/utils/sceneRule'
|
||||||
import { validateActionItem } from './utils/actionRules'
|
|
||||||
import type { Trigger, Action } from '@/api/iot/rule/scene'
|
import type { Trigger, Action } from '@/api/iot/rule/scene'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
import { CommonStatusEnum } from '@/utils/constants'
|
import { CommonStatusEnum } from '@/utils/constants'
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,12 @@
|
||||||
<!-- 告警配置组件 -->
|
<!-- 告警配置组件 -->
|
||||||
<template>
|
<template>
|
||||||
<el-form ref="innerFormRef" :model="formModel" :rules="formRules" label-width="110px" class="w-full">
|
<el-form
|
||||||
|
ref="innerFormRef"
|
||||||
|
:model="formModel"
|
||||||
|
:rules="formRules"
|
||||||
|
label-width="110px"
|
||||||
|
class="w-full"
|
||||||
|
>
|
||||||
<el-form-item label="告警配置" prop="alertConfigId" required>
|
<el-form-item label="告警配置" prop="alertConfigId" required>
|
||||||
<el-select
|
<el-select
|
||||||
v-model="localValue"
|
v-model="localValue"
|
||||||
|
|
@ -33,7 +39,7 @@
|
||||||
import type { FormInstance } from 'element-plus'
|
import type { FormInstance } from 'element-plus'
|
||||||
import { useVModel } from '@vueuse/core'
|
import { useVModel } from '@vueuse/core'
|
||||||
import { AlertConfigApi } from '@/api/iot/alert/config'
|
import { AlertConfigApi } from '@/api/iot/alert/config'
|
||||||
import { buildAlertConfigRules } from '../utils/actionRules'
|
import { buildAlertConfigRules } from '@/views/iot/utils/sceneRule'
|
||||||
|
|
||||||
/** 告警配置组件 */
|
/** 告警配置组件 */
|
||||||
defineOptions({ name: 'AlertConfig' })
|
defineOptions({ name: 'AlertConfig' })
|
||||||
|
|
|
||||||
|
|
@ -166,7 +166,7 @@ import {
|
||||||
getConditionTypeOptions,
|
getConditionTypeOptions,
|
||||||
IoTDeviceStatusEnum
|
IoTDeviceStatusEnum
|
||||||
} from '@/views/iot/utils/constants'
|
} from '@/views/iot/utils/constants'
|
||||||
import { buildSubConditionRules } from '../utils/triggerConditionRules'
|
import { buildSubConditionRules } from '@/views/iot/utils/sceneRule'
|
||||||
|
|
||||||
/** 单个条件配置组件 */
|
/** 单个条件配置组件 */
|
||||||
defineOptions({ name: 'ConditionConfig' })
|
defineOptions({ name: 'ConditionConfig' })
|
||||||
|
|
|
||||||
|
|
@ -92,7 +92,7 @@ import {
|
||||||
IoTDataSpecsDataTypeEnum
|
IoTDataSpecsDataTypeEnum
|
||||||
} from '@/views/iot/utils/constants'
|
} from '@/views/iot/utils/constants'
|
||||||
import { ThingModelApi } from '@/api/iot/thingmodel'
|
import { ThingModelApi } from '@/api/iot/thingmodel'
|
||||||
import { buildDeviceControlRules } from '../utils/actionRules'
|
import { buildDeviceControlRules } from '@/views/iot/utils/sceneRule'
|
||||||
|
|
||||||
/** 设备控制配置组件 */
|
/** 设备控制配置组件 */
|
||||||
defineOptions({ name: 'DeviceControlConfig' })
|
defineOptions({ name: 'DeviceControlConfig' })
|
||||||
|
|
|
||||||
|
|
@ -203,7 +203,7 @@ import {
|
||||||
IotRuleSceneTriggerConditionParameterOperatorEnum,
|
IotRuleSceneTriggerConditionParameterOperatorEnum,
|
||||||
IoTDeviceStatusEnum
|
IoTDeviceStatusEnum
|
||||||
} from '@/views/iot/utils/constants'
|
} from '@/views/iot/utils/constants'
|
||||||
import { buildMainConditionRules } from '../utils/triggerConditionRules'
|
import { buildMainConditionRules } from '@/views/iot/utils/sceneRule'
|
||||||
import { useVModel } from '@vueuse/core'
|
import { useVModel } from '@vueuse/core'
|
||||||
|
|
||||||
/** 主条件内部配置组件 */
|
/** 主条件内部配置组件 */
|
||||||
|
|
|
||||||
|
|
@ -1,110 +0,0 @@
|
||||||
import type { FormItemRule } from 'element-plus'
|
|
||||||
import type { Action } from '@/api/iot/rule/scene'
|
|
||||||
import { IotRuleSceneActionTypeEnum } from '@/views/iot/utils/constants'
|
|
||||||
|
|
||||||
const requiredRule = (message: string): FormItemRule => ({
|
|
||||||
required: true,
|
|
||||||
message,
|
|
||||||
trigger: ['change', 'blur']
|
|
||||||
})
|
|
||||||
|
|
||||||
/** 判断执行器参数是否为空 */
|
|
||||||
export const isActionParamsEmpty = (params?: string): boolean => {
|
|
||||||
if (!params || !String(params).trim()) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
const parsed = JSON.parse(String(params))
|
|
||||||
if (typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)) {
|
|
||||||
return Object.keys(parsed).length === 0
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 设备控制执行器表单项校验规则 */
|
|
||||||
export function buildDeviceControlRules(actionType: number): Record<string, FormItemRule[]> {
|
|
||||||
const rules: Record<string, FormItemRule[]> = {
|
|
||||||
productId: [requiredRule('请选择产品')],
|
|
||||||
deviceId: [requiredRule('请选择设备')],
|
|
||||||
params: [createParamsRule()]
|
|
||||||
}
|
|
||||||
|
|
||||||
if (actionType === IotRuleSceneActionTypeEnum.DEVICE_SERVICE_INVOKE) {
|
|
||||||
rules.identifier = [requiredRule('请选择服务')]
|
|
||||||
}
|
|
||||||
|
|
||||||
return rules
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 告警配置表单项校验规则 */
|
|
||||||
export function buildAlertConfigRules(): Record<string, FormItemRule[]> {
|
|
||||||
return {
|
|
||||||
alertConfigId: [requiredRule('请选择告警配置')]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function createParamsRule(): FormItemRule {
|
|
||||||
return {
|
|
||||||
validator: (_rule, value, callback) => {
|
|
||||||
if (isActionParamsEmpty(value)) {
|
|
||||||
callback(new Error('请填写参数配置'))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
JSON.parse(String(value))
|
|
||||||
} catch {
|
|
||||||
callback(new Error('参数格式须为合法 JSON'))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
callback()
|
|
||||||
},
|
|
||||||
trigger: ['change', 'blur']
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 校验单个执行器(兜底,与 UI 一致)
|
|
||||||
* @returns 错误信息,通过则返回 null
|
|
||||||
*/
|
|
||||||
export function validateActionItem(action: Action, index: number): string | null {
|
|
||||||
const prefix = `执行器 ${index + 1}`
|
|
||||||
|
|
||||||
if (!action.type) {
|
|
||||||
return `${prefix}: 执行器类型不能为空`
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
action.type === IotRuleSceneActionTypeEnum.DEVICE_PROPERTY_SET ||
|
|
||||||
action.type === IotRuleSceneActionTypeEnum.DEVICE_SERVICE_INVOKE
|
|
||||||
) {
|
|
||||||
if (!action.productId) {
|
|
||||||
return `${prefix}: 产品不能为空`
|
|
||||||
}
|
|
||||||
if (!action.deviceId) {
|
|
||||||
return `${prefix}: 设备不能为空`
|
|
||||||
}
|
|
||||||
if (action.type === IotRuleSceneActionTypeEnum.DEVICE_SERVICE_INVOKE && !action.identifier) {
|
|
||||||
return `${prefix}: 服务不能为空`
|
|
||||||
}
|
|
||||||
if (isActionParamsEmpty(action.params)) {
|
|
||||||
return `${prefix}: 参数配置不能为空`
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
JSON.parse(String(action.params))
|
|
||||||
} catch {
|
|
||||||
return `${prefix}: 参数格式须为合法 JSON`
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
if (action.type === IotRuleSceneActionTypeEnum.ALERT_RECOVER) {
|
|
||||||
if (!action.alertConfigId) {
|
|
||||||
return `${prefix}: 告警配置不能为空`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
@ -1,277 +0,0 @@
|
||||||
import type { FormItemRule } from 'element-plus'
|
|
||||||
import type { Trigger, TriggerCondition } from '@/api/iot/rule/scene'
|
|
||||||
import {
|
|
||||||
IotRuleSceneTriggerTypeEnum,
|
|
||||||
IotRuleSceneTriggerConditionTypeEnum,
|
|
||||||
IotRuleSceneTriggerTimeOperatorEnum,
|
|
||||||
isDeviceTrigger
|
|
||||||
} from '@/views/iot/utils/constants'
|
|
||||||
|
|
||||||
const requiredRule = (message: string): FormItemRule => ({
|
|
||||||
required: true,
|
|
||||||
message,
|
|
||||||
trigger: ['change', 'blur']
|
|
||||||
})
|
|
||||||
|
|
||||||
const isEmpty = (val: unknown) => val === undefined || val === null || val === ''
|
|
||||||
|
|
||||||
/** 主条件表单项校验规则(与 MainConditionInnerConfig 展示字段对齐) */
|
|
||||||
export function buildMainConditionRules(triggerType: number): Record<string, FormItemRule[]> {
|
|
||||||
const base: Record<string, FormItemRule[]> = {
|
|
||||||
productId: [requiredRule('请选择产品')],
|
|
||||||
deviceId: [requiredRule('请选择设备')]
|
|
||||||
}
|
|
||||||
|
|
||||||
if (triggerType === IotRuleSceneTriggerTypeEnum.DEVICE_STATE_UPDATE) {
|
|
||||||
return {
|
|
||||||
...base,
|
|
||||||
operator: [requiredRule('请选择操作符')],
|
|
||||||
value: [requiredRule('请选择设备状态')]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
triggerType === IotRuleSceneTriggerTypeEnum.DEVICE_PROPERTY_POST ||
|
|
||||||
triggerType === IotRuleSceneTriggerTypeEnum.DEVICE_EVENT_POST ||
|
|
||||||
triggerType === IotRuleSceneTriggerTypeEnum.DEVICE_SERVICE_INVOKE
|
|
||||||
) {
|
|
||||||
const rules: Record<string, FormItemRule[]> = {
|
|
||||||
...base,
|
|
||||||
identifier: [requiredRule('请选择监控项')]
|
|
||||||
}
|
|
||||||
|
|
||||||
const isEventOrService =
|
|
||||||
triggerType === IotRuleSceneTriggerTypeEnum.DEVICE_EVENT_POST ||
|
|
||||||
triggerType === IotRuleSceneTriggerTypeEnum.DEVICE_SERVICE_INVOKE
|
|
||||||
|
|
||||||
if (!isEventOrService) {
|
|
||||||
rules.operator = [requiredRule('请选择操作符')]
|
|
||||||
rules.value = [requiredRule('请填写比较值')]
|
|
||||||
}
|
|
||||||
return rules
|
|
||||||
}
|
|
||||||
|
|
||||||
return {}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 校验单个触发器(与主条件 UI 一致,供 RuleSceneForm 兜底)
|
|
||||||
* @returns 错误信息,通过则返回 null
|
|
||||||
*/
|
|
||||||
export function validateTriggerItem(trigger: Trigger, index: number): string | null {
|
|
||||||
if (!trigger.type) {
|
|
||||||
return `触发器 ${index + 1}: 触发器类型不能为空`
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isDeviceTrigger(trigger.type)) {
|
|
||||||
if (!trigger.productId) {
|
|
||||||
return `触发器 ${index + 1}: 产品不能为空`
|
|
||||||
}
|
|
||||||
if (!trigger.deviceId) {
|
|
||||||
return `触发器 ${index + 1}: 设备不能为空`
|
|
||||||
}
|
|
||||||
|
|
||||||
if (trigger.type === IotRuleSceneTriggerTypeEnum.DEVICE_STATE_UPDATE) {
|
|
||||||
if (!trigger.operator) {
|
|
||||||
return `触发器 ${index + 1}: 操作符不能为空`
|
|
||||||
}
|
|
||||||
if (isEmpty(trigger.value)) {
|
|
||||||
return `触发器 ${index + 1}: 设备状态不能为空`
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!trigger.identifier) {
|
|
||||||
return `触发器 ${index + 1}: 物模型标识符不能为空`
|
|
||||||
}
|
|
||||||
|
|
||||||
const isEventOrService =
|
|
||||||
trigger.type === IotRuleSceneTriggerTypeEnum.DEVICE_EVENT_POST ||
|
|
||||||
trigger.type === IotRuleSceneTriggerTypeEnum.DEVICE_SERVICE_INVOKE
|
|
||||||
if (!isEventOrService) {
|
|
||||||
if (!trigger.operator) {
|
|
||||||
return `触发器 ${index + 1}: 操作符不能为空`
|
|
||||||
}
|
|
||||||
if (isEmpty(trigger.value)) {
|
|
||||||
return `触发器 ${index + 1}: 参数值不能为空`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
if (trigger.type === IotRuleSceneTriggerTypeEnum.TIMER) {
|
|
||||||
if (!trigger.cronExpression) {
|
|
||||||
return `触发器 ${index + 1}: CRON表达式不能为空`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const groupError = validateTriggerConditionGroups(trigger.conditionGroups, index)
|
|
||||||
if (groupError) {
|
|
||||||
return groupError
|
|
||||||
}
|
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 子条件表单项校验规则(与 ConditionConfig 展示字段对齐) */
|
|
||||||
export function buildSubConditionRules(
|
|
||||||
conditionType?: number,
|
|
||||||
getOperator?: () => string
|
|
||||||
): Record<string, FormItemRule[]> {
|
|
||||||
const rules: Record<string, FormItemRule[]> = {
|
|
||||||
type: [requiredRule('请选择条件类型')]
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!conditionType) {
|
|
||||||
return rules
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
conditionType === IotRuleSceneTriggerConditionTypeEnum.DEVICE_STATUS ||
|
|
||||||
conditionType === IotRuleSceneTriggerConditionTypeEnum.DEVICE_PROPERTY
|
|
||||||
) {
|
|
||||||
rules.productId = [requiredRule('请选择产品')]
|
|
||||||
rules.deviceId = [requiredRule('请选择设备')]
|
|
||||||
}
|
|
||||||
|
|
||||||
if (conditionType === IotRuleSceneTriggerConditionTypeEnum.DEVICE_STATUS) {
|
|
||||||
rules.operator = [requiredRule('请选择操作符')]
|
|
||||||
rules.param = [requiredRule('请选择设备状态')]
|
|
||||||
}
|
|
||||||
|
|
||||||
if (conditionType === IotRuleSceneTriggerConditionTypeEnum.DEVICE_PROPERTY) {
|
|
||||||
rules.identifier = [requiredRule('请选择监控项')]
|
|
||||||
rules.operator = [requiredRule('请选择操作符')]
|
|
||||||
rules.param = [requiredRule('请填写比较值')]
|
|
||||||
}
|
|
||||||
|
|
||||||
if (conditionType === IotRuleSceneTriggerConditionTypeEnum.CURRENT_TIME) {
|
|
||||||
rules.operator = [requiredRule('请选择时间条件')]
|
|
||||||
rules.param = [createCurrentTimeParamRule(getOperator ?? (() => ''))]
|
|
||||||
}
|
|
||||||
|
|
||||||
return rules
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 当前时间条件的 param 校验 */
|
|
||||||
function createCurrentTimeParamRule(getOperator: () => string): FormItemRule {
|
|
||||||
return {
|
|
||||||
validator: (_rule, value, callback) => {
|
|
||||||
const operator = getOperator()
|
|
||||||
if (operator === IotRuleSceneTriggerTimeOperatorEnum.TODAY.value) {
|
|
||||||
callback()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (isEmpty(value)) {
|
|
||||||
callback(new Error('请填写时间值'))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (operator === IotRuleSceneTriggerTimeOperatorEnum.BETWEEN_TIME.value) {
|
|
||||||
const parts = String(value).split(',')
|
|
||||||
if (!parts[0]?.trim() || !parts[1]?.trim()) {
|
|
||||||
callback(new Error('请填写开始和结束时间'))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
callback()
|
|
||||||
},
|
|
||||||
trigger: ['change', 'blur']
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 校验单个子条件
|
|
||||||
* @returns 错误信息,通过则返回 null
|
|
||||||
*/
|
|
||||||
export function validateTriggerCondition(
|
|
||||||
condition: TriggerCondition,
|
|
||||||
path: string
|
|
||||||
): string | null {
|
|
||||||
if (!condition.type) {
|
|
||||||
return `${path}: 条件类型不能为空`
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
condition.type === IotRuleSceneTriggerConditionTypeEnum.DEVICE_STATUS ||
|
|
||||||
condition.type === IotRuleSceneTriggerConditionTypeEnum.DEVICE_PROPERTY
|
|
||||||
) {
|
|
||||||
if (!condition.productId) {
|
|
||||||
return `${path}: 产品不能为空`
|
|
||||||
}
|
|
||||||
if (!condition.deviceId) {
|
|
||||||
return `${path}: 设备不能为空`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (condition.type === IotRuleSceneTriggerConditionTypeEnum.DEVICE_STATUS) {
|
|
||||||
if (!condition.operator) {
|
|
||||||
return `${path}: 操作符不能为空`
|
|
||||||
}
|
|
||||||
if (isEmpty(condition.param)) {
|
|
||||||
return `${path}: 设备状态不能为空`
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
if (condition.type === IotRuleSceneTriggerConditionTypeEnum.DEVICE_PROPERTY) {
|
|
||||||
if (!condition.identifier) {
|
|
||||||
return `${path}: 监控项不能为空`
|
|
||||||
}
|
|
||||||
if (!condition.operator) {
|
|
||||||
return `${path}: 操作符不能为空`
|
|
||||||
}
|
|
||||||
if (isEmpty(condition.param)) {
|
|
||||||
return `${path}: 比较值不能为空`
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
if (condition.type === IotRuleSceneTriggerConditionTypeEnum.CURRENT_TIME) {
|
|
||||||
if (!condition.operator) {
|
|
||||||
return `${path}: 时间条件不能为空`
|
|
||||||
}
|
|
||||||
if (condition.operator === IotRuleSceneTriggerTimeOperatorEnum.TODAY.value) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
if (isEmpty(condition.param)) {
|
|
||||||
return `${path}: 时间值不能为空`
|
|
||||||
}
|
|
||||||
if (condition.operator === IotRuleSceneTriggerTimeOperatorEnum.BETWEEN_TIME.value) {
|
|
||||||
const parts = String(condition.param).split(',')
|
|
||||||
if (!parts[0]?.trim() || !parts[1]?.trim()) {
|
|
||||||
return `${path}: 开始和结束时间不能为空`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 校验触发器的附加条件组 */
|
|
||||||
export function validateTriggerConditionGroups(
|
|
||||||
groups: TriggerCondition[][] | undefined,
|
|
||||||
triggerIndex: number
|
|
||||||
): string | null {
|
|
||||||
if (!groups?.length) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let g = 0; g < groups.length; g++) {
|
|
||||||
const group = groups[g]
|
|
||||||
if (!group?.length) {
|
|
||||||
return `触发器 ${triggerIndex + 1} 子条件组 ${g + 1}: 至少需要一个条件`
|
|
||||||
}
|
|
||||||
for (let c = 0; c < group.length; c++) {
|
|
||||||
const error = validateTriggerCondition(
|
|
||||||
group[c],
|
|
||||||
`触发器 ${triggerIndex + 1} 子条件组 ${g + 1} 条件 ${c + 1}`
|
|
||||||
)
|
|
||||||
if (error) {
|
|
||||||
return error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,503 @@
|
||||||
|
import type { FormItemRule } from 'element-plus'
|
||||||
|
import type { Action, Trigger, TriggerCondition } from '@/api/iot/rule/scene'
|
||||||
|
import { isEmptyVal } from '@/utils/is'
|
||||||
|
import {
|
||||||
|
IotRuleSceneActionTypeEnum,
|
||||||
|
IotRuleSceneTriggerConditionTypeEnum,
|
||||||
|
IotRuleSceneTriggerTimeOperatorEnum,
|
||||||
|
IotRuleSceneTriggerTypeEnum,
|
||||||
|
isDeviceTrigger
|
||||||
|
} from './constants'
|
||||||
|
|
||||||
|
/** 创建 Element Plus 表单必填规则,统一触发方式和错误提示。 */
|
||||||
|
const requiredRule = (message: string): FormItemRule => ({
|
||||||
|
required: true,
|
||||||
|
message,
|
||||||
|
trigger: ['change', 'blur']
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建触发器主条件的表单校验规则。
|
||||||
|
*
|
||||||
|
* 该规则与 MainConditionInnerConfig 中实际展示的字段保持一致:
|
||||||
|
* - 设备状态变化:需要产品、设备、操作符、设备状态;
|
||||||
|
* - 属性上报:需要产品、设备、监控项、操作符、比较值;
|
||||||
|
* - 事件上报、服务调用:只需要产品、设备、监控项。
|
||||||
|
*
|
||||||
|
* @param triggerType 触发器类型
|
||||||
|
* @returns Element Plus 表单 rules
|
||||||
|
*/
|
||||||
|
export function buildMainConditionRules(triggerType: number): Record<string, FormItemRule[]> {
|
||||||
|
const base: Record<string, FormItemRule[]> = {
|
||||||
|
productId: [requiredRule('请选择产品')],
|
||||||
|
deviceId: [requiredRule('请选择设备')]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设备状态变化使用固定状态枚举作为比较值。
|
||||||
|
if (triggerType === IotRuleSceneTriggerTypeEnum.DEVICE_STATE_UPDATE) {
|
||||||
|
return {
|
||||||
|
...base,
|
||||||
|
operator: [requiredRule('请选择操作符')],
|
||||||
|
value: [requiredRule('请选择设备状态')]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
triggerType === IotRuleSceneTriggerTypeEnum.DEVICE_PROPERTY_POST ||
|
||||||
|
triggerType === IotRuleSceneTriggerTypeEnum.DEVICE_EVENT_POST ||
|
||||||
|
triggerType === IotRuleSceneTriggerTypeEnum.DEVICE_SERVICE_INVOKE
|
||||||
|
) {
|
||||||
|
const rules: Record<string, FormItemRule[]> = {
|
||||||
|
...base,
|
||||||
|
identifier: [requiredRule('请选择监控项')]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 事件上报和服务调用只监听是否发生,不需要额外的操作符和比较值。
|
||||||
|
const isEventOrService =
|
||||||
|
triggerType === IotRuleSceneTriggerTypeEnum.DEVICE_EVENT_POST ||
|
||||||
|
triggerType === IotRuleSceneTriggerTypeEnum.DEVICE_SERVICE_INVOKE
|
||||||
|
|
||||||
|
if (!isEventOrService) {
|
||||||
|
rules.operator = [requiredRule('请选择操作符')]
|
||||||
|
rules.value = [requiredRule('请填写比较值')]
|
||||||
|
}
|
||||||
|
return rules
|
||||||
|
}
|
||||||
|
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验单个触发器配置。
|
||||||
|
*
|
||||||
|
* 该方法用于 RuleSceneForm 提交前兜底校验,避免子组件表单没有触发 validate 时漏掉必填项。
|
||||||
|
* 校验逻辑需要和触发器主条件 UI 保持一致,同时继续校验附加条件组。
|
||||||
|
*
|
||||||
|
* @param trigger 触发器配置
|
||||||
|
* @param index 触发器在列表中的下标,用于生成可定位的错误提示
|
||||||
|
* @returns 错误信息,通过则返回 null
|
||||||
|
*/
|
||||||
|
export function validateTriggerItem(trigger: Trigger, index: number): string | null {
|
||||||
|
if (!trigger.type) {
|
||||||
|
return `触发器 ${index + 1}: 触发器类型不能为空`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设备类触发器都有产品、设备两个基础字段。
|
||||||
|
if (isDeviceTrigger(trigger.type)) {
|
||||||
|
if (!trigger.productId) {
|
||||||
|
return `触发器 ${index + 1}: 产品不能为空`
|
||||||
|
}
|
||||||
|
if (!trigger.deviceId) {
|
||||||
|
return `触发器 ${index + 1}: 设备不能为空`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设备状态变化不依赖物模型标识符,只校验操作符和状态值。
|
||||||
|
if (trigger.type === IotRuleSceneTriggerTypeEnum.DEVICE_STATE_UPDATE) {
|
||||||
|
if (!trigger.operator) {
|
||||||
|
return `触发器 ${index + 1}: 操作符不能为空`
|
||||||
|
}
|
||||||
|
if (isEmptyVal(trigger.value)) {
|
||||||
|
return `触发器 ${index + 1}: 设备状态不能为空`
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!trigger.identifier) {
|
||||||
|
return `触发器 ${index + 1}: 物模型标识符不能为空`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 属性上报需要比较条件;事件上报、服务调用只需要物模型标识符。
|
||||||
|
const isEventOrService =
|
||||||
|
trigger.type === IotRuleSceneTriggerTypeEnum.DEVICE_EVENT_POST ||
|
||||||
|
trigger.type === IotRuleSceneTriggerTypeEnum.DEVICE_SERVICE_INVOKE
|
||||||
|
if (!isEventOrService) {
|
||||||
|
if (!trigger.operator) {
|
||||||
|
return `触发器 ${index + 1}: 操作符不能为空`
|
||||||
|
}
|
||||||
|
if (isEmptyVal(trigger.value)) {
|
||||||
|
return `触发器 ${index + 1}: 参数值不能为空`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
// 定时触发器本身需要 CRON 表达式,附加条件组在下方统一校验。
|
||||||
|
if (trigger.type === IotRuleSceneTriggerTypeEnum.TIMER) {
|
||||||
|
if (!trigger.cronExpression) {
|
||||||
|
return `触发器 ${index + 1}: CRON表达式不能为空`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const groupError = validateTriggerConditionGroups(trigger.conditionGroups, index)
|
||||||
|
if (groupError) {
|
||||||
|
return groupError
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建附加子条件的表单校验规则。
|
||||||
|
*
|
||||||
|
* 该规则与 ConditionConfig 中不同条件类型展示的字段保持一致:
|
||||||
|
* - 设备状态:需要产品、设备、操作符、设备状态;
|
||||||
|
* - 设备属性:需要产品、设备、监控项、操作符、比较值;
|
||||||
|
* - 当前时间:需要时间条件,并按时间条件动态校验时间值。
|
||||||
|
*
|
||||||
|
* @param conditionType 子条件类型
|
||||||
|
* @param getOperator 获取当前时间条件操作符的方法,用于动态判断 param 是否必填
|
||||||
|
* @returns Element Plus 表单 rules
|
||||||
|
*/
|
||||||
|
export function buildSubConditionRules(
|
||||||
|
conditionType?: number,
|
||||||
|
getOperator?: () => string
|
||||||
|
): Record<string, FormItemRule[]> {
|
||||||
|
const rules: Record<string, FormItemRule[]> = {
|
||||||
|
type: [requiredRule('请选择条件类型')]
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!conditionType) {
|
||||||
|
return rules
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设备状态和设备属性都需要先确定具体产品、设备。
|
||||||
|
if (
|
||||||
|
conditionType === IotRuleSceneTriggerConditionTypeEnum.DEVICE_STATUS ||
|
||||||
|
conditionType === IotRuleSceneTriggerConditionTypeEnum.DEVICE_PROPERTY
|
||||||
|
) {
|
||||||
|
rules.productId = [requiredRule('请选择产品')]
|
||||||
|
rules.deviceId = [requiredRule('请选择设备')]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设备状态使用设备在线状态作为比较值。
|
||||||
|
if (conditionType === IotRuleSceneTriggerConditionTypeEnum.DEVICE_STATUS) {
|
||||||
|
rules.operator = [requiredRule('请选择操作符')]
|
||||||
|
rules.param = [requiredRule('请选择设备状态')]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设备属性需要选择物模型属性,再填写对应的比较值。
|
||||||
|
if (conditionType === IotRuleSceneTriggerConditionTypeEnum.DEVICE_PROPERTY) {
|
||||||
|
rules.identifier = [requiredRule('请选择监控项')]
|
||||||
|
rules.operator = [requiredRule('请选择操作符')]
|
||||||
|
rules.param = [requiredRule('请填写比较值')]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 当前时间的 param 是否必填取决于具体操作符,例如“今天”不需要额外值。
|
||||||
|
if (conditionType === IotRuleSceneTriggerConditionTypeEnum.CURRENT_TIME) {
|
||||||
|
rules.operator = [requiredRule('请选择时间条件')]
|
||||||
|
rules.param = [createCurrentTimeParamRule(getOperator ?? (() => ''))]
|
||||||
|
}
|
||||||
|
|
||||||
|
return rules
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建当前时间条件的 param 自定义校验规则。
|
||||||
|
*
|
||||||
|
* 时间条件的字段展示是动态的:
|
||||||
|
* - 今天:不需要 param;
|
||||||
|
* - 时间区间:param 使用逗号拼接开始时间和结束时间;
|
||||||
|
* - 其它时间条件:param 需要填写单个时间值。
|
||||||
|
*
|
||||||
|
* @param getOperator 获取当前时间条件操作符的方法
|
||||||
|
* @returns Element Plus 表单项校验规则
|
||||||
|
*/
|
||||||
|
function createCurrentTimeParamRule(getOperator: () => string): FormItemRule {
|
||||||
|
return {
|
||||||
|
validator: (_rule, value, callback) => {
|
||||||
|
const operator = getOperator()
|
||||||
|
|
||||||
|
// “今天”没有附加输入项,直接通过。
|
||||||
|
if (operator === IotRuleSceneTriggerTimeOperatorEnum.TODAY.value) {
|
||||||
|
callback()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isEmptyVal(value)) {
|
||||||
|
callback(new Error('请填写时间值'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 时间区间需要同时存在开始和结束时间。
|
||||||
|
if (operator === IotRuleSceneTriggerTimeOperatorEnum.BETWEEN_TIME.value) {
|
||||||
|
const parts = String(value).split(',')
|
||||||
|
if (!parts[0]?.trim() || !parts[1]?.trim()) {
|
||||||
|
callback(new Error('请填写开始和结束时间'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
callback()
|
||||||
|
},
|
||||||
|
trigger: ['change', 'blur']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验单个附加子条件。
|
||||||
|
*
|
||||||
|
* 该方法用于提交前兜底校验,错误提示会带上 path,方便定位到第几个触发器、
|
||||||
|
* 第几个条件组和第几个条件。
|
||||||
|
*
|
||||||
|
* @param condition 子条件配置
|
||||||
|
* @param path 错误提示前缀
|
||||||
|
* @returns 错误信息,通过则返回 null
|
||||||
|
*/
|
||||||
|
export function validateTriggerCondition(
|
||||||
|
condition: TriggerCondition,
|
||||||
|
path: string
|
||||||
|
): string | null {
|
||||||
|
if (!condition.type) {
|
||||||
|
return `${path}: 条件类型不能为空`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设备状态和设备属性都必须先选择产品、设备。
|
||||||
|
if (
|
||||||
|
condition.type === IotRuleSceneTriggerConditionTypeEnum.DEVICE_STATUS ||
|
||||||
|
condition.type === IotRuleSceneTriggerConditionTypeEnum.DEVICE_PROPERTY
|
||||||
|
) {
|
||||||
|
if (!condition.productId) {
|
||||||
|
return `${path}: 产品不能为空`
|
||||||
|
}
|
||||||
|
if (!condition.deviceId) {
|
||||||
|
return `${path}: 设备不能为空`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设备状态只校验操作符和状态枚举值。
|
||||||
|
if (condition.type === IotRuleSceneTriggerConditionTypeEnum.DEVICE_STATUS) {
|
||||||
|
if (!condition.operator) {
|
||||||
|
return `${path}: 操作符不能为空`
|
||||||
|
}
|
||||||
|
if (isEmptyVal(condition.param)) {
|
||||||
|
return `${path}: 设备状态不能为空`
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设备属性需要校验物模型标识符、操作符和比较值。
|
||||||
|
if (condition.type === IotRuleSceneTriggerConditionTypeEnum.DEVICE_PROPERTY) {
|
||||||
|
if (!condition.identifier) {
|
||||||
|
return `${path}: 监控项不能为空`
|
||||||
|
}
|
||||||
|
if (!condition.operator) {
|
||||||
|
return `${path}: 操作符不能为空`
|
||||||
|
}
|
||||||
|
if (isEmptyVal(condition.param)) {
|
||||||
|
return `${path}: 比较值不能为空`
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
// 当前时间按操作符动态判断 param 是否需要填写。
|
||||||
|
if (condition.type === IotRuleSceneTriggerConditionTypeEnum.CURRENT_TIME) {
|
||||||
|
if (!condition.operator) {
|
||||||
|
return `${path}: 时间条件不能为空`
|
||||||
|
}
|
||||||
|
|
||||||
|
if (condition.operator === IotRuleSceneTriggerTimeOperatorEnum.TODAY.value) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isEmptyVal(condition.param)) {
|
||||||
|
return `${path}: 时间值不能为空`
|
||||||
|
}
|
||||||
|
|
||||||
|
if (condition.operator === IotRuleSceneTriggerTimeOperatorEnum.BETWEEN_TIME.value) {
|
||||||
|
const parts = String(condition.param).split(',')
|
||||||
|
if (!parts[0]?.trim() || !parts[1]?.trim()) {
|
||||||
|
return `${path}: 开始和结束时间不能为空`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验触发器的附加条件组。
|
||||||
|
*
|
||||||
|
* 条件组结构是二维数组:外层是 OR 关系的条件组,内层是 AND 关系的条件列表。
|
||||||
|
* 这里逐组、逐条件返回第一条错误,避免一次性弹出过多提示。
|
||||||
|
*
|
||||||
|
* @param groups 附加条件组
|
||||||
|
* @param triggerIndex 触发器在列表中的下标,用于生成可定位的错误提示
|
||||||
|
* @returns 错误信息,通过则返回 null
|
||||||
|
*/
|
||||||
|
export function validateTriggerConditionGroups(
|
||||||
|
groups: TriggerCondition[][] | undefined,
|
||||||
|
triggerIndex: number
|
||||||
|
): string | null {
|
||||||
|
if (!groups?.length) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let g = 0; g < groups.length; g++) {
|
||||||
|
const group = groups[g]
|
||||||
|
|
||||||
|
// 空条件组没有实际过滤条件,提交后语义不明确,需要拦截。
|
||||||
|
if (!group?.length) {
|
||||||
|
return `触发器 ${triggerIndex + 1} 子条件组 ${g + 1}: 至少需要一个条件`
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let c = 0; c < group.length; c++) {
|
||||||
|
const error = validateTriggerCondition(
|
||||||
|
group[c],
|
||||||
|
`触发器 ${triggerIndex + 1} 子条件组 ${g + 1} 条件 ${c + 1}`
|
||||||
|
)
|
||||||
|
if (error) {
|
||||||
|
return error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断执行器参数是否为空。
|
||||||
|
*
|
||||||
|
* params 当前以 JSON 字符串存储:
|
||||||
|
* - 空字符串、空白字符串视为空;
|
||||||
|
* - 空对象 `{}` 视为空;
|
||||||
|
* - 非空 JSON 对象视为已配置;
|
||||||
|
* - 非法 JSON 不在这里判空,交给 JSON 格式校验返回更准确的错误。
|
||||||
|
*
|
||||||
|
* @param params 执行器参数 JSON 字符串
|
||||||
|
* @returns 是否为空
|
||||||
|
*/
|
||||||
|
export const isActionParamsEmpty = (params?: string): boolean => {
|
||||||
|
if (!params || !String(params).trim()) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const parsed = JSON.parse(String(params))
|
||||||
|
if (typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)) {
|
||||||
|
return Object.keys(parsed).length === 0
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建设备控制执行器的表单校验规则。
|
||||||
|
*
|
||||||
|
* 属性设置和服务调用都需要产品、设备和参数配置;
|
||||||
|
* 服务调用还需要选择具体服务标识符。
|
||||||
|
*
|
||||||
|
* @param actionType 执行器类型
|
||||||
|
* @returns Element Plus 表单 rules
|
||||||
|
*/
|
||||||
|
export function buildDeviceControlRules(actionType: number): Record<string, FormItemRule[]> {
|
||||||
|
const rules: Record<string, FormItemRule[]> = {
|
||||||
|
productId: [requiredRule('请选择产品')],
|
||||||
|
deviceId: [requiredRule('请选择设备')],
|
||||||
|
params: [createParamsRule()]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 服务调用需要额外选择物模型服务。
|
||||||
|
if (actionType === IotRuleSceneActionTypeEnum.DEVICE_SERVICE_INVOKE) {
|
||||||
|
rules.identifier = [requiredRule('请选择服务')]
|
||||||
|
}
|
||||||
|
|
||||||
|
return rules
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建告警恢复执行器的表单校验规则。
|
||||||
|
*
|
||||||
|
* 告警恢复需要绑定一个告警配置,后端据此定位需要恢复的告警类型。
|
||||||
|
*
|
||||||
|
* @returns Element Plus 表单 rules
|
||||||
|
*/
|
||||||
|
export function buildAlertConfigRules(): Record<string, FormItemRule[]> {
|
||||||
|
return {
|
||||||
|
alertConfigId: [requiredRule('请选择告警配置')]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建设备控制参数的自定义校验规则。
|
||||||
|
*
|
||||||
|
* 参数配置来自物模型属性或服务参数,提交前需要同时满足:
|
||||||
|
* - 已经填写了参数;
|
||||||
|
* - 参数字符串是合法 JSON。
|
||||||
|
*
|
||||||
|
* @returns Element Plus 表单项校验规则
|
||||||
|
*/
|
||||||
|
function createParamsRule(): FormItemRule {
|
||||||
|
return {
|
||||||
|
validator: (_rule, value, callback) => {
|
||||||
|
if (isActionParamsEmpty(value)) {
|
||||||
|
callback(new Error('请填写参数配置'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 这里只校验 JSON 语法,具体参数结构由物模型参数配置组件负责生成。
|
||||||
|
try {
|
||||||
|
JSON.parse(String(value))
|
||||||
|
} catch {
|
||||||
|
callback(new Error('参数格式须为合法 JSON'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
callback()
|
||||||
|
},
|
||||||
|
trigger: ['change', 'blur']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验单个执行器配置。
|
||||||
|
*
|
||||||
|
* 该方法用于 RuleSceneForm 提交前兜底校验,避免子组件表单没有触发 validate 时漏掉必填项。
|
||||||
|
* 校验逻辑需要和执行器 UI 保持一致。
|
||||||
|
*
|
||||||
|
* @param action 执行器配置
|
||||||
|
* @param index 执行器在列表中的下标,用于生成可定位的错误提示
|
||||||
|
* @returns 错误信息,通过则返回 null
|
||||||
|
*/
|
||||||
|
export function validateActionItem(action: Action, index: number): string | null {
|
||||||
|
const prefix = `执行器 ${index + 1}`
|
||||||
|
|
||||||
|
if (!action.type) {
|
||||||
|
return `${prefix}: 执行器类型不能为空`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设备属性设置和设备服务调用都需要指定设备,并填写物模型参数。
|
||||||
|
if (
|
||||||
|
action.type === IotRuleSceneActionTypeEnum.DEVICE_PROPERTY_SET ||
|
||||||
|
action.type === IotRuleSceneActionTypeEnum.DEVICE_SERVICE_INVOKE
|
||||||
|
) {
|
||||||
|
if (!action.productId) {
|
||||||
|
return `${prefix}: 产品不能为空`
|
||||||
|
}
|
||||||
|
if (!action.deviceId) {
|
||||||
|
return `${prefix}: 设备不能为空`
|
||||||
|
}
|
||||||
|
if (action.type === IotRuleSceneActionTypeEnum.DEVICE_SERVICE_INVOKE && !action.identifier) {
|
||||||
|
return `${prefix}: 服务不能为空`
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isActionParamsEmpty(action.params)) {
|
||||||
|
return `${prefix}: 参数配置不能为空`
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
JSON.parse(String(action.params))
|
||||||
|
} catch {
|
||||||
|
return `${prefix}: 参数格式须为合法 JSON`
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
// 告警恢复执行器需要绑定具体告警配置。
|
||||||
|
if (action.type === IotRuleSceneActionTypeEnum.ALERT_RECOVER) {
|
||||||
|
if (!action.alertConfigId) {
|
||||||
|
return `${prefix}: 告警配置不能为空`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue