فهرست منبع

review:【IoT 物联网】场景联动的部分 review

YunaiV 10 ماه پیش
والد
کامیت
a72c297e86

+ 6 - 0
src/api/iot/rule/scene/scene.types.ts

@@ -2,6 +2,7 @@
  * IoT 场景联动接口定义
  */
 
+// TODO @puhui999:枚举挪到 views/iot/utils/constants.ts 里
 // 枚举定义
 const IotRuleSceneTriggerTypeEnum = {
   DEVICE_STATE_UPDATE: 1, // 设备上下线变更
@@ -24,6 +25,7 @@ const IotDeviceMessageTypeEnum = {
   EVENT: 'event' // 事件
 } as const
 
+// TODO @puhui999:这个貌似可以不要?
 const IotDeviceMessageIdentifierEnum = {
   PROPERTY_SET: 'set', // 属性设置
   SERVICE_INVOKE: '${identifier}' // 服务调用
@@ -44,6 +46,7 @@ const IotRuleSceneTriggerConditionParameterOperatorEnum = {
   NOT_NULL: { name: '非空', value: 'not null' } // 非空
 } as const
 
+// TODO @puhui999:下面 IotAlertConfigReceiveTypeEnum、DeviceStateEnum 没用到,貌似可以删除下?
 const IotAlertConfigReceiveTypeEnum = {
   SMS: 1, // 短信
   MAIL: 2, // 邮箱
@@ -57,6 +60,7 @@ const DeviceStateEnum = {
   OFFLINE: 2 // 离线
 } as const
 
+// TODO @puhui999:这个全局已经有啦
 // 通用状态枚举
 const CommonStatusEnum = {
   ENABLE: 0, // 开启
@@ -64,6 +68,7 @@ const CommonStatusEnum = {
 } as const
 
 // 基础接口
+// TODO @puhui999:这个貌似可以不要?
 interface TenantBaseDO {
   createTime?: Date // 创建时间
   updateTime?: Date // 更新时间
@@ -169,6 +174,7 @@ interface IotRuleScene extends TenantBaseDO {
 }
 
 // 工具类型
+// TODO @puhui999:这些在瞅瞅~
 type TriggerType = (typeof IotRuleSceneTriggerTypeEnum)[keyof typeof IotRuleSceneTriggerTypeEnum]
 type ActionType = (typeof IotRuleSceneActionTypeEnum)[keyof typeof IotRuleSceneActionTypeEnum]
 type MessageType = (typeof IotDeviceMessageTypeEnum)[keyof typeof IotDeviceMessageTypeEnum]

+ 1 - 0
src/views/iot/rule/scene/IoT场景联动规则表单设计思路文档.md

@@ -1,3 +1,4 @@
+// TODO @puhui999:这些后续需要删除哈
 # IoT场景联动规则表单设计思路文档
 
 ## 概述

+ 1 - 0
src/views/iot/rule/scene/IotThingModelTSLRespVO数据结构文档.md

@@ -1,3 +1,4 @@
+// TODO @puhui999:这些后续需要删除哈
 # IotThingModelTSLRespVO 数据结构文档
 
 ## 概述

+ 2 - 0
src/views/iot/rule/scene/components/selectors/types.ts

@@ -1,5 +1,7 @@
 // IoT物模型TSL数据类型定义
 
+// TODO @puhui999:看看这些里面,是不是一些已经有了哈?可以复用下~
+
 /** 物模型TSL响应数据结构 */
 export interface IotThingModelTSLRespVO {
   productId: number

+ 6 - 10
src/views/iot/rule/scene/index.vue

@@ -36,6 +36,7 @@
             class="!w-240px"
           />
         </el-form-item>
+        <!-- TODO @puhui999:字典 -->
         <el-form-item label="规则状态">
           <el-select
             v-model="queryParams.status"
@@ -61,6 +62,7 @@
     </el-card>
 
     <!-- 统计卡片 -->
+    <!-- TODO @puhui999:这种需要服用的 stats-content、stats-info 的属性,到底 unocss 好,还是现有的 style css 好~ -->
     <el-row :gutter="16" class="stats-row">
       <el-col :span="6">
         <el-card class="stats-card" shadow="hover">
@@ -124,6 +126,7 @@
           <template #default="{ row }">
             <div class="rule-name-cell">
               <span class="rule-name">{{ row.name }}</span>
+              <!-- TODO @puhui999:字典 -->
               <el-tag
                 :type="row.status === 0 ? 'success' : 'danger'"
                 size="small"
@@ -137,7 +140,6 @@
             </div>
           </template>
         </el-table-column>
-
         <el-table-column label="触发条件" min-width="250">
           <template #default="{ row }">
             <div class="trigger-summary">
@@ -153,7 +155,6 @@
             </div>
           </template>
         </el-table-column>
-
         <el-table-column label="执行动作" min-width="250">
           <template #default="{ row }">
             <div class="action-summary">
@@ -169,7 +170,7 @@
             </div>
           </template>
         </el-table-column>
-
+        <!-- TODO @puhui999:貌似要新增一个字段? -->
         <el-table-column label="最近触发" prop="lastTriggeredTime" width="180">
           <template #default="{ row }">
             <span v-if="row.lastTriggeredTime">
@@ -178,13 +179,11 @@
             <span v-else class="text-gray-400">未触发</span>
           </template>
         </el-table-column>
-
         <el-table-column label="创建时间" prop="createTime" width="180">
           <template #default="{ row }">
             {{ formatDate(row.createTime) }}
           </template>
         </el-table-column>
-
         <el-table-column label="操作" width="200" fixed="right">
           <template #default="{ row }">
             <div class="action-buttons">
@@ -242,11 +241,7 @@
     </div>
 
     <!-- 表单对话框 -->
-    <RuleSceneForm
-      v-model="formVisible"
-      :rule-scene="currentRule"
-      @success="handleFormSuccess"
-    />
+    <RuleSceneForm v-model="formVisible" :rule-scene="currentRule" @success="handleFormSuccess" />
   </ContentWrap>
 </template>
 
@@ -435,6 +430,7 @@ const handleSelectionChange = (selection: IotRuleScene[]) => {
   selectedRows.value = selection
 }
 
+// TODO @puhui999:batch 操作的逻辑,要不和其它 UI 界面保持一致,或者相对一致哈;
 const handleBatchEnable = async () => {
   try {
     await ElMessageBox.confirm(`确定要启用选中的 ${selectedRows.value.length} 个规则吗?`, '提示', {

+ 2 - 0
src/views/iot/rule/scene/utils/errorHandler.ts

@@ -2,6 +2,8 @@
  * IoT 场景联动错误处理和用户反馈工具
  */
 
+// TODO @puhui999:这个貌似用不到?
+
 import { ElMessage, ElMessageBox, ElNotification } from 'element-plus'
 
 // 错误类型枚举

+ 14 - 7
src/views/iot/rule/scene/utils/transform.ts

@@ -1,7 +1,6 @@
 /**
  * IoT 场景联动数据转换工具函数
  */
-
 import {
   IotRuleScene,
   TriggerConfig,
@@ -12,6 +11,8 @@ import {
 } from '@/api/iot/rule/scene/scene.types'
 import { generateUUID } from '@/utils'
 
+// TODO @puhui999:这些是不是放到对应的界面,会好一丢丢哈?
+
 /**
  * 创建默认的表单数据
  */
@@ -19,7 +20,7 @@ export function createDefaultFormData(): RuleSceneFormData {
   return {
     name: '',
     description: '',
-    status: 0,
+    status: 0, // TODO @puhui999:枚举值
     triggers: [],
     actions: []
   }
@@ -30,7 +31,7 @@ export function createDefaultFormData(): RuleSceneFormData {
  */
 export function createDefaultTriggerData(): TriggerFormData {
   return {
-    type: 2, // 默认为属性上报
+    type: 2, // 默认为属性上报 TODO @puhui999:枚举值
     productId: undefined,
     deviceId: undefined,
     identifier: undefined,
@@ -46,7 +47,7 @@ export function createDefaultTriggerData(): TriggerFormData {
  */
 export function createDefaultActionData(): ActionFormData {
   return {
-    type: 1, // 默认为属性设置
+    type: 1, // 默认为属性设置 TODO @puhui999:枚举值
     productId: undefined,
     deviceId: undefined,
     params: {},
@@ -58,7 +59,8 @@ export function createDefaultActionData(): ActionFormData {
  * 将表单数据转换为API请求格式
  */
 export function transformFormToApi(formData: RuleSceneFormData): IotRuleScene {
-  // 这里需要根据实际API结构进行转换
+  // TODO @puhui999:这个关注下
+  // 这里需要根据实际 API 结构进行转换
   // 暂时返回基本结构
   return {
     id: formData.id,
@@ -71,7 +73,7 @@ export function transformFormToApi(formData: RuleSceneFormData): IotRuleScene {
 }
 
 /**
- * 将API响应数据转换为表单格式
+ * 将 API 响应数据转换为表单格式
  */
 export function transformApiToForm(apiData: IotRuleScene): RuleSceneFormData {
   return {
@@ -94,6 +96,7 @@ export function transformApiToForm(apiData: IotRuleScene): RuleSceneFormData {
   }
 }
 
+// TODO @puhui999:貌似没用到;
 /**
  * 创建默认的触发器配置
  */
@@ -144,6 +147,7 @@ export function createDefaultTriggerConfig(type?: number): TriggerConfig {
   }
 }
 
+// TODO @puhui999:貌似没用到;
 /**
  * 创建默认的执行器配置
  */
@@ -174,6 +178,7 @@ export function createDefaultActionConfig(type?: number): ActionConfig {
   }
 }
 
+// TODO @puhui999:全局已经有类似的
 /**
  * 深度克隆对象(用于避免引用问题)
  */
@@ -203,6 +208,7 @@ export function deepClone<T>(obj: T): T {
   return obj
 }
 
+// TODO @puhui999:貌似没用到;
 /**
  * 清理空值和无效数据
  */
@@ -297,6 +303,7 @@ export function formatCronExpression(cron: string): string {
   return description || cron
 }
 
+// TODO @puhui999:貌似没用到;
 /**
  * 验证并修复数据结构
  */
@@ -351,6 +358,7 @@ export function validateAndFixData(data: IotRuleScene): IotRuleScene {
   return fixed
 }
 
+// TODO @puhui999:貌似没用到;
 /**
  * 比较两个场景联动规则是否相等(忽略key字段)
  */
@@ -401,6 +409,5 @@ export function getRuleSceneSummary(ruleScene: IotRuleScene): {
           return '未知执行类型'
       }
     }) || []
-
   return { triggerSummary, actionSummary }
 }

+ 51 - 65
src/views/iot/rule/scene/utils/validation.ts

@@ -1,13 +1,18 @@
 /**
  * IoT 场景联动表单验证工具函数
  */
-
-import { FormValidationRules, IotRuleScene, TriggerConfig, ActionConfig } from '@/api/iot/rule/scene/scene.types'
-import { IotRuleSceneTriggerTypeEnum, IotRuleSceneActionTypeEnum } from '@/api/iot/rule/scene/scene.types'
-
-/**
- * 基础表单验证规则
- */
+import {
+  FormValidationRules,
+  IotRuleScene,
+  TriggerConfig,
+  ActionConfig
+} from '@/api/iot/rule/scene/scene.types'
+import {
+  IotRuleSceneTriggerTypeEnum,
+  IotRuleSceneActionTypeEnum
+} from '@/api/iot/rule/scene/scene.types'
+
+/** 基础表单验证规则 */
 export const getBaseValidationRules = (): FormValidationRules => ({
   name: [
     { required: true, message: '场景名称不能为空', trigger: 'blur' },
@@ -30,55 +35,47 @@ export const getBaseValidationRules = (): FormValidationRules => ({
   ]
 })
 
-/**
- * 验证CRON表达式格式
- */
+/** 验证CRON表达式格式 */
 export function validateCronExpression(cron: string): boolean {
   if (!cron || cron.trim().length === 0) return false
-  
-  // 基础的CRON表达式正则验证(支持6位和7位格式)
-  const cronRegex = /^(\*|([0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-9]|5[0-9])|\*\/([0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-9]|5[0-9])) (\*|([0-9]|1[0-9]|2[0-3])|\*\/([0-9]|1[0-9]|2[0-3])) (\*|([1-9]|1[0-9]|2[0-9]|3[0-1])|\*\/([1-9]|1[0-9]|2[0-9]|3[0-1])) (\*|([1-9]|1[0-2])|\*\/([1-9]|1[0-2])) (\*|([0-6])|\*\/([0-6]))( (\*|([1-9][0-9]{3})|\*\/([1-9][0-9]{3})))?$/
-  
+  // 基础的 CRON 表达式正则验证(支持6位和7位格式)
+  const cronRegex =
+    /^(\*|([0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-9]|5[0-9])|\*\/([0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-9]|5[0-9])) (\*|([0-9]|1[0-9]|2[0-3])|\*\/([0-9]|1[0-9]|2[0-3])) (\*|([1-9]|1[0-9]|2[0-9]|3[0-1])|\*\/([1-9]|1[0-9]|2[0-9]|3[0-1])) (\*|([1-9]|1[0-2])|\*\/([1-9]|1[0-2])) (\*|([0-6])|\*\/([0-6]))( (\*|([1-9][0-9]{3})|\*\/([1-9][0-9]{3})))?$/
   return cronRegex.test(cron.trim())
 }
 
-/**
- * 验证设备名称数组
- */
+/** 验证设备名称数组 */
 export function validateDeviceNames(deviceNames: string[]): boolean {
-  return Array.isArray(deviceNames) && 
-         deviceNames.length > 0 && 
-         deviceNames.every(name => name && name.trim().length > 0)
+  return (
+    Array.isArray(deviceNames) &&
+    deviceNames.length > 0 &&
+    deviceNames.every((name) => name && name.trim().length > 0)
+  )
 }
 
-/**
- * 验证比较值格式
- */
+/** 验证比较值格式 */
 export function validateCompareValue(operator: string, value: string): boolean {
   if (!value || value.trim().length === 0) return false
-
   const trimmedValue = value.trim()
-
   switch (operator) {
     case 'between':
     case 'not between':
       const betweenValues = trimmedValue.split(',')
-      return betweenValues.length === 2 && 
-             betweenValues.every(v => v.trim().length > 0) &&
-             !isNaN(Number(betweenValues[0].trim())) &&
-             !isNaN(Number(betweenValues[1].trim()))
-
+      return (
+        betweenValues.length === 2 &&
+        betweenValues.every((v) => v.trim().length > 0) &&
+        !isNaN(Number(betweenValues[0].trim())) &&
+        !isNaN(Number(betweenValues[1].trim()))
+      )
     case 'in':
     case 'not in':
       const inValues = trimmedValue.split(',')
-      return inValues.length > 0 && inValues.every(v => v.trim().length > 0)
-
+      return inValues.length > 0 && inValues.every((v) => v.trim().length > 0)
     case '>':
     case '>=':
     case '<':
     case '<=':
       return !isNaN(Number(trimmedValue))
-
     case '=':
     case '!=':
     case 'like':
@@ -88,14 +85,14 @@ export function validateCompareValue(operator: string, value: string): boolean {
   }
 }
 
-/**
- * 验证触发器配置
- */
-export function validateTriggerConfig(trigger: TriggerConfig): { valid: boolean; message?: string } {
+/** 验证触发器配置 */
+export function validateTriggerConfig(trigger: TriggerConfig): {
+  valid: boolean
+  message?: string
+} {
   if (!trigger.type) {
     return { valid: false, message: '触发类型不能为空' }
   }
-
   // 定时触发验证
   if (trigger.type === IotRuleSceneTriggerTypeEnum.TIMER) {
     if (!trigger.cronExpression) {
@@ -106,32 +103,26 @@ export function validateTriggerConfig(trigger: TriggerConfig): { valid: boolean;
     }
     return { valid: true }
   }
-
   // 设备触发验证
   if (!trigger.productKey) {
     return { valid: false, message: '产品标识不能为空' }
   }
-
   if (!trigger.deviceNames || !validateDeviceNames(trigger.deviceNames)) {
     return { valid: false, message: '设备名称不能为空' }
   }
-
   // 设备状态变更无需额外条件验证
   if (trigger.type === IotRuleSceneTriggerTypeEnum.DEVICE_STATE_UPDATE) {
     return { valid: true }
   }
-
   // 其他设备触发类型需要验证条件
   if (!trigger.conditions || trigger.conditions.length === 0) {
     return { valid: false, message: '触发条件不能为空' }
   }
-
   // 验证每个条件的参数
   for (const condition of trigger.conditions) {
     if (!condition.parameters || condition.parameters.length === 0) {
       return { valid: false, message: '触发条件参数不能为空' }
     }
-
     for (const param of condition.parameters) {
       if (!param.operator) {
         return { valid: false, message: '操作符不能为空' }
@@ -141,34 +132,32 @@ export function validateTriggerConfig(trigger: TriggerConfig): { valid: boolean;
       }
     }
   }
-
   return { valid: true }
 }
 
-/**
- * 验证执行器配置
- */
+/** 验证执行器配置 */
 export function validateActionConfig(action: ActionConfig): { valid: boolean; message?: string } {
   if (!action.type) {
     return { valid: false, message: '执行类型不能为空' }
   }
-
   // 告警触发/恢复验证
-  if (action.type === IotRuleSceneActionTypeEnum.ALERT_TRIGGER || 
-      action.type === IotRuleSceneActionTypeEnum.ALERT_RECOVER) {
+  if (
+    action.type === IotRuleSceneActionTypeEnum.ALERT_TRIGGER ||
+    action.type === IotRuleSceneActionTypeEnum.ALERT_RECOVER
+  ) {
     if (!action.alertConfigId) {
       return { valid: false, message: '告警配置ID不能为空' }
     }
     return { valid: true }
   }
-
   // 设备控制验证
-  if (action.type === IotRuleSceneActionTypeEnum.DEVICE_PROPERTY_SET || 
-      action.type === IotRuleSceneActionTypeEnum.DEVICE_SERVICE_INVOKE) {
+  if (
+    action.type === IotRuleSceneActionTypeEnum.DEVICE_PROPERTY_SET ||
+    action.type === IotRuleSceneActionTypeEnum.DEVICE_SERVICE_INVOKE
+  ) {
     if (!action.deviceControl) {
       return { valid: false, message: '设备控制配置不能为空' }
     }
-
     const { deviceControl } = action
     if (!deviceControl.productKey) {
       return { valid: false, message: '产品标识不能为空' }
@@ -185,34 +174,28 @@ export function validateActionConfig(action: ActionConfig): { valid: boolean; me
     if (!deviceControl.params || Object.keys(deviceControl.params).length === 0) {
       return { valid: false, message: '参数不能为空' }
     }
-
     return { valid: true }
   }
 
   return { valid: false, message: '未知的执行类型' }
 }
 
-/**
- * 验证完整的场景联动规则
- */
+// TODO @puhui999:貌似没用到?
+/** 验证完整的场景联动规则 */
 export function validateRuleScene(ruleScene: IotRuleScene): { valid: boolean; message?: string } {
   // 基础字段验证
   if (!ruleScene.name || ruleScene.name.trim().length === 0) {
     return { valid: false, message: '场景名称不能为空' }
   }
-
   if (ruleScene.status !== 0 && ruleScene.status !== 1) {
     return { valid: false, message: '场景状态必须为0或1' }
   }
-
   if (!ruleScene.triggers || ruleScene.triggers.length === 0) {
     return { valid: false, message: '至少需要一个触发器' }
   }
-
   if (!ruleScene.actions || ruleScene.actions.length === 0) {
     return { valid: false, message: '至少需要一个执行器' }
   }
-
   // 验证每个触发器
   for (let i = 0; i < ruleScene.triggers.length; i++) {
     const triggerResult = validateTriggerConfig(ruleScene.triggers[i])
@@ -220,7 +203,6 @@ export function validateRuleScene(ruleScene: IotRuleScene): { valid: boolean; me
       return { valid: false, message: `触发器${i + 1}: ${triggerResult.message}` }
     }
   }
-
   // 验证每个执行器
   for (let i = 0; i < ruleScene.actions.length; i++) {
     const actionResult = validateActionConfig(ruleScene.actions[i])
@@ -228,14 +210,16 @@ export function validateRuleScene(ruleScene: IotRuleScene): { valid: boolean; me
       return { valid: false, message: `执行器${i + 1}: ${actionResult.message}` }
     }
   }
-
   return { valid: true }
 }
 
+// TODO @puhui999:下面 getOperatorOptions、getTriggerTypeOptions、getActionTypeOptions 三个貌似没用到?如果用到的话,要不放到 yudao-ui-admin-vue3/src/views/iot/utils/constants.ts 里
+
 /**
  * 获取操作符选项
  */
 export function getOperatorOptions() {
+  // TODO @puhui999:这个能不能从枚举计算出来,减少后续添加枚举的维护
   return [
     { value: '=', label: '等于' },
     { value: '!=', label: '不等于' },
@@ -256,6 +240,7 @@ export function getOperatorOptions() {
  * 获取触发类型选项
  */
 export function getTriggerTypeOptions() {
+  // TODO @puhui999:这个能不能从枚举计算出来,减少后续添加枚举的维护
   return [
     { value: IotRuleSceneTriggerTypeEnum.DEVICE_STATE_UPDATE, label: '设备上下线变更' },
     { value: IotRuleSceneTriggerTypeEnum.DEVICE_PROPERTY_POST, label: '物模型属性上报' },
@@ -269,6 +254,7 @@ export function getTriggerTypeOptions() {
  * 获取执行类型选项
  */
 export function getActionTypeOptions() {
+  // TODO @puhui999:这个能不能从枚举计算出来,减少后续添加枚举的维护
   return [
     { value: IotRuleSceneActionTypeEnum.DEVICE_PROPERTY_SET, label: '设备属性设置' },
     { value: IotRuleSceneActionTypeEnum.DEVICE_SERVICE_INVOKE, label: '设备服务调用' },