perf:【IoT 物联网】场景联动触发器数据结构优化对齐后端
This commit is contained in:
@ -107,7 +107,7 @@ interface ActionConfig {
|
||||
alertConfigId?: number // 告警配置ID(告警恢复时必填)
|
||||
}
|
||||
|
||||
// 表单数据接口
|
||||
// 表单数据接口 - 直接对应后端 DO 结构
|
||||
interface RuleSceneFormData {
|
||||
id?: number
|
||||
name: string
|
||||
@ -117,57 +117,38 @@ interface RuleSceneFormData {
|
||||
actions: ActionFormData[]
|
||||
}
|
||||
|
||||
// 触发器表单数据 - 直接对应 TriggerDO
|
||||
interface TriggerFormData {
|
||||
type: number
|
||||
productId?: number
|
||||
deviceId?: number
|
||||
identifier?: string
|
||||
operator?: string
|
||||
value?: string
|
||||
cronExpression?: string
|
||||
// 新的条件结构
|
||||
mainCondition?: ConditionFormData // 主条件(必须满足)
|
||||
conditionGroup?: ConditionGroupContainerFormData // 条件组容器(可选,与主条件为且关系)
|
||||
type: number // 触发类型
|
||||
productId?: number // 产品编号
|
||||
deviceId?: number // 设备编号
|
||||
identifier?: string // 物模型标识符
|
||||
operator?: string // 操作符
|
||||
value?: string // 参数值
|
||||
cronExpression?: string // CRON 表达式
|
||||
conditionGroups?: TriggerConditionFormData[][] // 条件组(二维数组)
|
||||
}
|
||||
|
||||
interface ActionFormData {
|
||||
type: number
|
||||
productId?: number
|
||||
deviceId?: number
|
||||
params?: Record<string, any>
|
||||
alertConfigId?: number
|
||||
}
|
||||
|
||||
// 条件组容器(包含多个子条件组,子条件组间为或关系)
|
||||
interface ConditionGroupContainerFormData {
|
||||
subGroups: SubConditionGroupFormData[] // 子条件组数组,子条件组间为或关系
|
||||
}
|
||||
|
||||
// 子条件组(内部条件为且关系)
|
||||
interface SubConditionGroupFormData {
|
||||
conditions: ConditionFormData[] // 条件数组,条件间为且关系
|
||||
}
|
||||
|
||||
// 保留原有接口用于兼容性
|
||||
interface ConditionGroupFormData {
|
||||
conditions: ConditionFormData[]
|
||||
// 注意:条件组内部的条件固定为"且"关系,条件组之间固定为"或"关系
|
||||
// logicOperator 字段保留用于兼容性,但在UI中固定为 'AND'
|
||||
logicOperator: 'AND' | 'OR'
|
||||
}
|
||||
|
||||
interface ConditionFormData {
|
||||
// 触发条件表单数据 - 直接对应 TriggerConditionDO
|
||||
interface TriggerConditionFormData {
|
||||
type: number // 条件类型:1-设备状态,2-设备属性,3-当前时间
|
||||
productId?: number // 产品ID(设备状态和设备属性时必填)
|
||||
deviceId?: number // 设备ID(设备状态和设备属性时必填)
|
||||
identifier?: string // 标识符(设备属性时必填)
|
||||
productId?: number // 产品编号
|
||||
deviceId?: number // 设备编号
|
||||
identifier?: string // 标识符
|
||||
operator: string // 操作符
|
||||
param: string // 参数值
|
||||
timeValue?: string // 时间值(当前时间条件时使用)
|
||||
timeValue2?: string // 第二个时间值(时间范围条件时使用)
|
||||
}
|
||||
|
||||
// 主接口
|
||||
// 执行器表单数据 - 直接对应 ActionDO
|
||||
interface ActionFormData {
|
||||
type: number // 执行类型
|
||||
productId?: number // 产品编号
|
||||
deviceId?: number // 设备编号
|
||||
params?: Record<string, any> // 请求参数
|
||||
alertConfigId?: number // 告警配置编号
|
||||
}
|
||||
|
||||
// 主接口 - 原有的 API 接口格式(保持兼容性)
|
||||
interface IotRuleScene extends TenantBaseDO {
|
||||
id?: number // 场景编号(新增时为空)
|
||||
name: string // 场景名称(必填)
|
||||
@ -177,6 +158,47 @@ interface IotRuleScene extends TenantBaseDO {
|
||||
actions: ActionConfig[] // 执行器数组(必填,至少一个)
|
||||
}
|
||||
|
||||
// 后端 DO 接口 - 匹配后端数据结构
|
||||
interface IotRuleSceneDO {
|
||||
id?: number // 场景编号
|
||||
name: string // 场景名称
|
||||
description?: string // 场景描述
|
||||
status: number // 场景状态:0-开启,1-关闭
|
||||
triggers: TriggerDO[] // 触发器数组
|
||||
actions: ActionDO[] // 执行器数组
|
||||
}
|
||||
|
||||
// 触发器 DO 结构
|
||||
interface TriggerDO {
|
||||
type: number // 触发类型
|
||||
productId?: number // 产品编号
|
||||
deviceId?: number // 设备编号
|
||||
identifier?: string // 物模型标识符
|
||||
operator?: string // 操作符
|
||||
value?: string // 参数值
|
||||
cronExpression?: string // CRON 表达式
|
||||
conditionGroups?: TriggerConditionDO[][] // 条件组(二维数组)
|
||||
}
|
||||
|
||||
// 触发条件 DO 结构
|
||||
interface TriggerConditionDO {
|
||||
type: number // 条件类型
|
||||
productId?: number // 产品编号
|
||||
deviceId?: number // 设备编号
|
||||
identifier?: string // 标识符
|
||||
operator: string // 操作符
|
||||
param: string // 参数
|
||||
}
|
||||
|
||||
// 执行器 DO 结构
|
||||
interface ActionDO {
|
||||
type: number // 执行类型
|
||||
productId?: number // 产品编号
|
||||
deviceId?: number // 设备编号
|
||||
params?: Record<string, any> // 请求参数
|
||||
alertConfigId?: number // 告警配置编号
|
||||
}
|
||||
|
||||
// 工具类型 - 从枚举中提取类型
|
||||
// TriggerType 现在从 constants.ts 中的枚举提取
|
||||
export type ActionType =
|
||||
@ -202,6 +224,10 @@ interface FormValidationRules {
|
||||
|
||||
export {
|
||||
IotRuleScene,
|
||||
IotRuleSceneDO,
|
||||
TriggerDO,
|
||||
TriggerConditionDO,
|
||||
ActionDO,
|
||||
TriggerConfig,
|
||||
TriggerCondition,
|
||||
TriggerConditionParameter,
|
||||
@ -209,11 +235,8 @@ export {
|
||||
ActionDeviceControl,
|
||||
RuleSceneFormData,
|
||||
TriggerFormData,
|
||||
TriggerConditionFormData,
|
||||
ActionFormData,
|
||||
ConditionGroupFormData,
|
||||
ConditionGroupContainerFormData,
|
||||
SubConditionGroupFormData,
|
||||
ConditionFormData,
|
||||
IotRuleSceneActionTypeEnum,
|
||||
IotDeviceMessageTypeEnum,
|
||||
IotRuleSceneTriggerConditionParameterOperatorEnum,
|
||||
|
||||
@ -41,9 +41,11 @@ import TriggerSection from './sections/TriggerSection.vue'
|
||||
import ActionSection from './sections/ActionSection.vue'
|
||||
import {
|
||||
IotRuleScene,
|
||||
IotRuleSceneDO,
|
||||
IotRuleSceneActionTypeEnum,
|
||||
RuleSceneFormData,
|
||||
TriggerFormData
|
||||
TriggerFormData,
|
||||
TriggerConditionFormData
|
||||
} from '@/api/iot/rule/scene/scene.types'
|
||||
import { IotRuleSceneTriggerTypeEnum } from '@/views/iot/utils/constants'
|
||||
import { ElMessage } from 'element-plus'
|
||||
@ -91,8 +93,7 @@ const createDefaultFormData = (): RuleSceneFormData => {
|
||||
operator: undefined,
|
||||
value: undefined,
|
||||
cronExpression: undefined,
|
||||
mainCondition: undefined,
|
||||
conditionGroup: undefined
|
||||
conditionGroups: [] // 空的条件组数组
|
||||
}
|
||||
],
|
||||
actions: []
|
||||
@ -100,49 +101,10 @@ const createDefaultFormData = (): RuleSceneFormData => {
|
||||
}
|
||||
|
||||
/**
|
||||
* 将表单数据转换为 API 请求格式
|
||||
* 将表单数据转换为后端 DO 格式
|
||||
* 由于数据结构已对齐,转换变得非常简单
|
||||
*/
|
||||
const convertFormToVO = (formData: RuleSceneFormData): IotRuleScene => {
|
||||
// 构建单个触发器的条件
|
||||
const buildTriggerConditions = (trigger: TriggerFormData) => {
|
||||
const conditions: any[] = []
|
||||
|
||||
// 处理主条件
|
||||
if (trigger.mainCondition) {
|
||||
const mainCondition = trigger.mainCondition
|
||||
conditions.push({
|
||||
type: mainCondition.type === 2 ? 'property' : 'event',
|
||||
identifier: mainCondition.identifier || '',
|
||||
parameters: [
|
||||
{
|
||||
operator: mainCondition.operator,
|
||||
value: mainCondition.param
|
||||
}
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
// 处理条件组
|
||||
if (trigger.conditionGroup?.subGroups) {
|
||||
trigger.conditionGroup.subGroups.forEach((subGroup) => {
|
||||
subGroup.conditions.forEach((condition) => {
|
||||
conditions.push({
|
||||
type: condition.type === 2 ? 'property' : 'event',
|
||||
identifier: condition.identifier || '',
|
||||
parameters: [
|
||||
{
|
||||
operator: condition.operator,
|
||||
value: condition.param
|
||||
}
|
||||
]
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
return conditions
|
||||
}
|
||||
|
||||
const convertFormToVO = (formData: RuleSceneFormData): IotRuleSceneDO => {
|
||||
return {
|
||||
id: formData.id,
|
||||
name: formData.name,
|
||||
@ -150,79 +112,41 @@ const convertFormToVO = (formData: RuleSceneFormData): IotRuleScene => {
|
||||
status: Number(formData.status),
|
||||
triggers: formData.triggers.map((trigger) => ({
|
||||
type: trigger.type,
|
||||
productKey: trigger.productId ? `product_${trigger.productId}` : undefined,
|
||||
deviceNames: trigger.deviceId ? [`device_${trigger.deviceId}`] : undefined,
|
||||
productId: trigger.productId,
|
||||
deviceId: trigger.deviceId,
|
||||
identifier: trigger.identifier,
|
||||
operator: trigger.operator,
|
||||
value: trigger.value,
|
||||
cronExpression: trigger.cronExpression,
|
||||
conditions: buildTriggerConditions(trigger)
|
||||
conditionGroups: trigger.conditionGroups || []
|
||||
})),
|
||||
actions:
|
||||
formData.actions?.map((action) => ({
|
||||
type: action.type,
|
||||
alertConfigId: action.alertConfigId,
|
||||
deviceControl:
|
||||
action.type === IotRuleSceneActionTypeEnum.DEVICE_PROPERTY_SET ||
|
||||
action.type === IotRuleSceneActionTypeEnum.DEVICE_SERVICE_INVOKE
|
||||
? {
|
||||
productKey: action.productId ? `product_${action.productId}` : '',
|
||||
deviceNames: action.deviceId ? [`device_${action.deviceId}`] : [],
|
||||
type:
|
||||
action.type === IotRuleSceneActionTypeEnum.DEVICE_PROPERTY_SET
|
||||
? 'property'
|
||||
: 'service',
|
||||
identifier:
|
||||
action.type === IotRuleSceneActionTypeEnum.DEVICE_PROPERTY_SET ? 'set' : 'invoke',
|
||||
params: action.params || {}
|
||||
}
|
||||
: undefined
|
||||
})) || []
|
||||
} as IotRuleScene
|
||||
actions: formData.actions?.map((action) => ({
|
||||
type: action.type,
|
||||
productId: action.productId,
|
||||
deviceId: action.deviceId,
|
||||
params: action.params,
|
||||
alertConfigId: action.alertConfigId
|
||||
})) || []
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 API 响应数据转换为表单格式
|
||||
* 将后端 DO 数据转换为表单格式
|
||||
* 由于数据结构已对齐,转换变得非常简单
|
||||
*/
|
||||
const convertVOToForm = (apiData: IotRuleScene): RuleSceneFormData => {
|
||||
// 解析单个触发器的条件
|
||||
const parseConditions = (trigger: any) => {
|
||||
if (!trigger?.conditions?.length) {
|
||||
return {
|
||||
mainCondition: undefined,
|
||||
conditionGroup: undefined
|
||||
}
|
||||
}
|
||||
|
||||
// 简化处理:将第一个条件作为主条件
|
||||
const firstCondition = trigger.conditions[0]
|
||||
const mainCondition = {
|
||||
type: firstCondition.type === 'property' ? 2 : 3,
|
||||
productId: undefined, // 需要从 productKey 解析
|
||||
deviceId: undefined, // 需要从 deviceNames 解析
|
||||
identifier: firstCondition.identifier,
|
||||
operator: firstCondition.parameters?.[0]?.operator || '=',
|
||||
param: firstCondition.parameters?.[0]?.value || ''
|
||||
}
|
||||
|
||||
return {
|
||||
mainCondition,
|
||||
conditionGroup: undefined // 暂时简化处理
|
||||
}
|
||||
}
|
||||
|
||||
const convertVOToForm = (apiData: IotRuleSceneDO): RuleSceneFormData => {
|
||||
// 转换所有触发器
|
||||
const triggers = apiData.triggers?.length
|
||||
? apiData.triggers.map((trigger) => {
|
||||
const conditionData = parseConditions(trigger)
|
||||
return {
|
||||
type: Number(trigger.type),
|
||||
productId: undefined, // 需要从 productKey 解析
|
||||
deviceId: undefined, // 需要从 deviceNames 解析
|
||||
identifier: undefined,
|
||||
operator: undefined,
|
||||
value: undefined,
|
||||
cronExpression: trigger.cronExpression,
|
||||
...conditionData
|
||||
}
|
||||
})
|
||||
? apiData.triggers.map((trigger: any) => ({
|
||||
type: Number(trigger.type),
|
||||
productId: trigger.productId,
|
||||
deviceId: trigger.deviceId,
|
||||
identifier: trigger.identifier,
|
||||
operator: trigger.operator,
|
||||
value: trigger.value,
|
||||
cronExpression: trigger.cronExpression,
|
||||
conditionGroups: trigger.conditionGroups || []
|
||||
}))
|
||||
: [
|
||||
{
|
||||
type: IotRuleSceneTriggerTypeEnum.DEVICE_PROPERTY_POST,
|
||||
@ -232,22 +156,23 @@ const convertVOToForm = (apiData: IotRuleScene): RuleSceneFormData => {
|
||||
operator: undefined,
|
||||
value: undefined,
|
||||
cronExpression: undefined,
|
||||
mainCondition: undefined,
|
||||
conditionGroup: undefined
|
||||
conditionGroups: []
|
||||
}
|
||||
]
|
||||
|
||||
return {
|
||||
...apiData,
|
||||
id: apiData.id,
|
||||
name: apiData.name,
|
||||
description: apiData.description,
|
||||
status: Number(apiData.status),
|
||||
triggers,
|
||||
actions:
|
||||
apiData.actions?.map((action) => ({
|
||||
...action,
|
||||
apiData.actions?.map((action: any) => ({
|
||||
type: Number(action.type),
|
||||
productId: undefined, // 需要从 deviceControl.productKey 解析
|
||||
deviceId: undefined, // 需要从 deviceControl.deviceNames 解析
|
||||
params: action.deviceControl?.params || {},
|
||||
productId: action.productId,
|
||||
deviceId: action.deviceId,
|
||||
params: action.params || {},
|
||||
alertConfigId: action.alertConfigId,
|
||||
// 为每个执行器添加唯一标识符,解决组件索引重用问题
|
||||
key: generateUUID()
|
||||
})) || []
|
||||
@ -321,9 +246,13 @@ const handleSubmit = async () => {
|
||||
// 提交请求
|
||||
submitLoading.value = true
|
||||
try {
|
||||
console.log(formData.value)
|
||||
// 转换数据格式
|
||||
const apiData = convertFormToVO(formData.value)
|
||||
|
||||
if (true) {
|
||||
console.log('转换后', apiData)
|
||||
return
|
||||
}
|
||||
// 调用API保存数据
|
||||
if (isEdit.value) {
|
||||
// 更新场景联动规则
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
<!-- 设备触发配置组件 - 优化版本 -->
|
||||
<!-- 设备触发配置组件 -->
|
||||
<template>
|
||||
<div class="flex flex-col gap-16px">
|
||||
<!-- 主条件配置 - 默认直接展示 -->
|
||||
<div class="space-y-16px">
|
||||
<MainConditionConfig
|
||||
v-model="trigger.mainCondition"
|
||||
v-model="trigger"
|
||||
:trigger-type="trigger.type"
|
||||
@validate="handleMainConditionValidate"
|
||||
/>
|
||||
|
||||
@ -40,37 +40,37 @@
|
||||
<script setup lang="ts">
|
||||
import MainConditionInnerConfig from './MainConditionInnerConfig.vue'
|
||||
import {
|
||||
ConditionFormData,
|
||||
IotRuleSceneTriggerConditionTypeEnum
|
||||
IotRuleSceneTriggerConditionTypeEnum,
|
||||
TriggerFormData
|
||||
} from '@/api/iot/rule/scene/scene.types'
|
||||
|
||||
/** 主条件配置组件 */
|
||||
defineOptions({ name: 'MainConditionConfig' })
|
||||
|
||||
defineProps<{
|
||||
modelValue?: ConditionFormData
|
||||
modelValue?: TriggerFormData
|
||||
triggerType: number
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:modelValue', value?: ConditionFormData): void
|
||||
(e: 'update:modelValue', value?: TriggerFormData): void
|
||||
(e: 'validate', result: { valid: boolean; message: string }): void
|
||||
}>()
|
||||
|
||||
// 事件处理
|
||||
const addMainCondition = () => {
|
||||
const newCondition: ConditionFormData = {
|
||||
const newCondition: TriggerFormData = {
|
||||
type: IotRuleSceneTriggerConditionTypeEnum.DEVICE_PROPERTY, // 默认为设备属性
|
||||
productId: undefined,
|
||||
deviceId: undefined,
|
||||
identifier: '',
|
||||
operator: '=',
|
||||
param: ''
|
||||
value: ''
|
||||
}
|
||||
emit('update:modelValue', newCondition)
|
||||
}
|
||||
|
||||
const updateCondition = (condition: ConditionFormData) => {
|
||||
const updateCondition = (condition: TriggerFormData) => {
|
||||
emit('update:modelValue', condition)
|
||||
}
|
||||
|
||||
|
||||
@ -148,8 +148,7 @@ const addTrigger = () => {
|
||||
operator: undefined,
|
||||
value: undefined,
|
||||
cronExpression: undefined,
|
||||
mainCondition: undefined,
|
||||
conditionGroup: undefined
|
||||
conditionGroups: [] // 空的条件组数组
|
||||
}
|
||||
triggers.value.push(newTrigger)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user