Browse Source

perf:【IoT 物联网】场景联动触发器数据结构优化对齐后端

puhui999 10 months ago
parent
commit
858f1cdb0b

+ 73 - 50
src/api/iot/rule/scene/scene.types.ts

@@ -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 // 条件组容器(可选,与主条件为且关系)
-}
-
-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 {
+  type: number // 触发类型
+  productId?: number // 产品编号
+  deviceId?: number // 设备编号
+  identifier?: string // 物模型标识符
+  operator?: string // 操作符
+  value?: string // 参数值
+  cronExpression?: string // CRON 表达式
+  conditionGroups?: TriggerConditionFormData[][] // 条件组(二维数组)
+}
+
+// 触发条件表单数据 - 直接对应 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,

+ 48 - 119
src/views/iot/rule/scene/form/RuleSceneForm.vue

@@ -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) {
       // 更新场景联动规则

+ 2 - 2
src/views/iot/rule/scene/form/configs/DeviceTriggerConfig.vue

@@ -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"
       />

+ 7 - 7
src/views/iot/rule/scene/form/configs/MainConditionConfig.vue

@@ -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)
 }
 

+ 1 - 2
src/views/iot/rule/scene/form/sections/TriggerSection.vue

@@ -148,8 +148,7 @@ const addTrigger = () => {
     operator: undefined,
     value: undefined,
     cronExpression: undefined,
-    mainCondition: undefined,
-    conditionGroup: undefined
+    conditionGroups: [] // 空的条件组数组
   }
   triggers.value.push(newTrigger)
 }