From eb54eea87c39f35b5d0476f146bfd29d7c7841be Mon Sep 17 00:00:00 2001
From: baoshiwei <baoshiwei@shlanbao.cn>
Date: 星期四, 05 六月 2025 13:51:49 +0800
Subject: [PATCH] feat(空调管理): 添加空调管理相关功能

---
 zhitan-airconditioner/src/main/java/com/zhitan/airconditioner/service/impl/AirConditionerServiceImpl.java         |  216 ++++
 zhitan-admin/pom.xml                                                                                              |    6 
 zhitan-airconditioner/src/main/java/com/zhitan/airconditioner/service/IAirConditionerLogService.java              |   59 +
 zhitan-airconditioner/pom.xml                                                                                     |   44 
 zhitan-airconditioner/src/main/java/com/zhitan/airconditioner/service/IAirConditionerService.java                 |   78 +
 zhitan-airconditioner/src/main/java/com/zhitan/airconditioner/mapper/AirConditionerMapper.java                    |   72 +
 zhitan-vue/src/api/airconditioner/airconditioner.js                                                               |   57 +
 zhitan-quartz/pom.xml                                                                                             |    6 
 pom.xml                                                                                                           |    1 
 zhitan-airconditioner/src/main/java/com/zhitan/airconditioner/domain/AirConditionerLog.java                       |  148 ++
 zhitan-airconditioner/src/main/java/com/zhitan/airconditioner/service/IAirConditionerScheduleService.java         |   83 +
 zhitan-admin/src/main/java/com/zhitan/web/controller/airconditioner/AirConditionerScheduleController.java         |   99 +
 zhitan-airconditioner/src/main/java/com/zhitan/airconditioner/service/impl/AirConditionerScheduleServiceImpl.java |  506 +++++++++
 zhitan-vue/src/api/airconditioner/log.js                                                                          |   26 
 zhitan-vue/src/views/airconditioner/list.vue                                                                      |  409 +++++++
 zhitan-vue/src/views/airconditioner/schedule.vue                                                                  |  434 ++++++++
 zhitan-airconditioner/src/main/java/com/zhitan/airconditioner/mapper/AirConditionerScheduleMapper.java            |   99 +
 zhitan-airconditioner/src/main/java/com/zhitan/airconditioner/domain/AirConditioner.java                          |   39 
 zhitan-admin/src/main/java/com/zhitan/web/controller/airconditioner/AirConditionerController.java                 |  100 +
 zhitan-airconditioner/src/main/java/com/zhitan/airconditioner/service/impl/AirConditionerLogServiceImpl.java      |   85 +
 zhitan-airconditioner/src/main/java/com/zhitan/airconditioner/config/SchedulingConfig.java                        |   33 
 zhitan-admin/src/main/java/com/zhitan/web/controller/airconditioner/AirConditionerLogController.java              |   59 +
 zhitan-vue/src/views/airconditioner/index.vue                                                                     |   76 +
 zhitan-vue/src/api/airconditioner/schedule.js                                                                     |   52 +
 zhitan-airconditioner/src/main/java/com/zhitan/airconditioner/domain/AirConditionerSchedule.java                  |   48 
 zhitan-quartz/src/main/java/com/zhitan/quartz/task/AirConditionerTask.java                                        |   25 
 zhitan-airconditioner/src/main/java/com/zhitan/airconditioner/mapper/AirConditionerLogMapper.java                 |   72 +
 zhitan-vue/src/views/airconditioner/log.vue                                                                       |  166 +++
 28 files changed, 3,098 insertions(+), 0 deletions(-)

diff --git a/pom.xml b/pom.xml
index 44c53af..efaf44d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -278,6 +278,7 @@
         <module>zhitan-quartz</module>
         <module>zhitan-generator</module>
         <module>zhitan-common</module>
+        <module>zhitan-airconditioner</module>
     </modules>
 
     <profiles>
diff --git a/zhitan-admin/pom.xml b/zhitan-admin/pom.xml
index d5c71d9..78cea4f 100644
--- a/zhitan-admin/pom.xml
+++ b/zhitan-admin/pom.xml
@@ -71,6 +71,12 @@
             <version>1.16.7</version>
             <scope>compile</scope>
         </dependency>
+        <dependency>
+            <groupId>com.zhitan</groupId>
+            <artifactId>zhitan-airconditioner</artifactId>
+            <version>2.5.2</version>
+            <scope>compile</scope>
+        </dependency>
     </dependencies>
 
     <build>
diff --git a/zhitan-admin/src/main/java/com/zhitan/web/controller/airconditioner/AirConditionerController.java b/zhitan-admin/src/main/java/com/zhitan/web/controller/airconditioner/AirConditionerController.java
new file mode 100644
index 0000000..022a727
--- /dev/null
+++ b/zhitan-admin/src/main/java/com/zhitan/web/controller/airconditioner/AirConditionerController.java
@@ -0,0 +1,100 @@
+package com.zhitan.web.controller.airconditioner;
+
+import java.util.List;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.zhitan.common.annotation.Log;
+import com.zhitan.common.core.controller.BaseController;
+import com.zhitan.common.core.domain.AjaxResult;
+import com.zhitan.common.enums.BusinessType;
+import com.zhitan.common.utils.poi.ExcelUtil;
+import com.zhitan.airconditioner.domain.AirConditioner;
+import com.zhitan.airconditioner.service.IAirConditionerService;
+import com.zhitan.common.core.page.TableDataInfo;
+
+/**
+ * 绌鸿皟绠$悊Controller
+ * 
+ * @author zhitan
+ */
+@RestController
+@RequestMapping("/system/airconditioner")
+public class AirConditionerController extends BaseController
+{
+    @Autowired
+    private IAirConditionerService airConditionerService;
+
+    /**
+     * 鏌ヨ绌鸿皟鍒楄〃
+     */
+    @PreAuthorize("@ss.hasPermi('system:airconditioner:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(AirConditioner airConditioner)
+    {
+        startPage();
+        List<AirConditioner> list = airConditionerService.selectAirConditionerList(airConditioner);
+        return getDataTable(list);
+    }
+
+    /**
+     * 鑾峰彇绌鸿皟璇︾粏淇℃伅
+     */
+    @PreAuthorize("@ss.hasPermi('system:airconditioner:query')")
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id)
+    {
+        return success(airConditionerService.selectAirConditionerById(id));
+    }
+
+    /**
+     * 鏂板绌鸿皟
+     */
+    @PreAuthorize("@ss.hasPermi('system:airconditioner:add')")
+    @Log(title = "绌鸿皟绠$悊", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody AirConditioner airConditioner)
+    {
+        return toAjax(airConditionerService.insertAirConditioner(airConditioner));
+    }
+
+    /**
+     * 淇敼绌鸿皟
+     */
+    @PreAuthorize("@ss.hasPermi('system:airconditioner:edit')")
+    @Log(title = "绌鸿皟绠$悊", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody AirConditioner airConditioner)
+    {
+        return toAjax(airConditionerService.updateAirConditioner(airConditioner));
+    }
+
+    /**
+     * 鍒犻櫎绌鸿皟
+     */
+    @PreAuthorize("@ss.hasPermi('system:airconditioner:remove')")
+    @Log(title = "绌鸿皟绠$悊", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids)
+    {
+        return toAjax(airConditionerService.deleteAirConditionerByIds(ids));
+    }
+    
+    /**
+     * 鎺у埗绌鸿皟寮�鍏虫満
+     */
+    @PreAuthorize("@ss.hasPermi('system:airconditioner:control')")
+    @Log(title = "绌鸿皟鎺у埗", businessType = BusinessType.OTHER)
+    @PostMapping("/control")
+    public AjaxResult control(@RequestBody AirConditioner airConditioner)
+    {
+        return toAjax(airConditionerService.controlAirConditioner(airConditioner.getId(),  airConditioner.getMode(), "0"));
+    }
+}
\ No newline at end of file
diff --git a/zhitan-admin/src/main/java/com/zhitan/web/controller/airconditioner/AirConditionerLogController.java b/zhitan-admin/src/main/java/com/zhitan/web/controller/airconditioner/AirConditionerLogController.java
new file mode 100644
index 0000000..bd598f0
--- /dev/null
+++ b/zhitan-admin/src/main/java/com/zhitan/web/controller/airconditioner/AirConditionerLogController.java
@@ -0,0 +1,59 @@
+package com.zhitan.web.controller.airconditioner;
+
+import java.util.List;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.zhitan.common.annotation.Log;
+import com.zhitan.common.core.controller.BaseController;
+import com.zhitan.common.core.domain.AjaxResult;
+import com.zhitan.common.core.page.TableDataInfo;
+import com.zhitan.common.enums.BusinessType;
+import com.zhitan.airconditioner.domain.AirConditionerLog;
+import com.zhitan.airconditioner.service.IAirConditionerLogService;
+
+/**
+ * 绌鸿皟鎿嶄綔鏃ュ織Controller
+ * 
+ * @author zhitan
+ */
+@RestController
+@RequestMapping("/system/airconditioner/log")
+public class AirConditionerLogController extends BaseController {
+    @Autowired
+    private IAirConditionerLogService airConditionerLogService;
+
+    /**
+     * 鏌ヨ绌鸿皟鎿嶄綔鏃ュ織鍒楄〃
+     */
+    @PreAuthorize("@ss.hasPermi('airconditioner:log:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(AirConditionerLog airConditionerLog) {
+        startPage();
+        List<AirConditionerLog> list = airConditionerLogService.selectAirConditionerLogList(airConditionerLog);
+        return getDataTable(list);
+    }
+
+    /**
+     * 鑾峰彇绌鸿皟鎿嶄綔鏃ュ織璇︾粏淇℃伅
+     */
+    @PreAuthorize("@ss.hasPermi('airconditioner:log:query')")
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id) {
+        return success(airConditionerLogService.selectAirConditionerLogById(id));
+    }
+
+    /**
+     * 鏍规嵁绌鸿皟ID鏌ヨ鎿嶄綔鏃ュ織鍒楄〃
+     */
+    @PreAuthorize("@ss.hasPermi('airconditioner:log:list')")
+    @GetMapping("/listByAirConditionerId/{airConditionerId}")
+    public TableDataInfo listByAirConditionerId(@PathVariable("airConditionerId") Long airConditionerId) {
+        startPage();
+        List<AirConditionerLog> list = airConditionerLogService.selectAirConditionerLogByAirConditionerId(airConditionerId);
+        return getDataTable(list);
+    }
+}
\ No newline at end of file
diff --git a/zhitan-admin/src/main/java/com/zhitan/web/controller/airconditioner/AirConditionerScheduleController.java b/zhitan-admin/src/main/java/com/zhitan/web/controller/airconditioner/AirConditionerScheduleController.java
new file mode 100644
index 0000000..0585f08
--- /dev/null
+++ b/zhitan-admin/src/main/java/com/zhitan/web/controller/airconditioner/AirConditionerScheduleController.java
@@ -0,0 +1,99 @@
+package com.zhitan.web.controller.airconditioner;
+
+import java.util.List;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.zhitan.common.annotation.Log;
+import com.zhitan.common.core.controller.BaseController;
+import com.zhitan.common.core.domain.AjaxResult;
+import com.zhitan.common.enums.BusinessType;
+import com.zhitan.airconditioner.domain.AirConditionerSchedule;
+import com.zhitan.airconditioner.service.IAirConditionerScheduleService;
+import com.zhitan.common.core.page.TableDataInfo;
+
+/**
+ * 绌鸿皟瀹氭椂浠诲姟Controller
+ * 
+ * @author zhitan
+ */
+@RestController
+@RequestMapping("/system/airconditioner/schedule")
+public class AirConditionerScheduleController extends BaseController
+{
+    @Autowired
+    private IAirConditionerScheduleService airConditionerScheduleService;
+
+    /**
+     * 鏌ヨ绌鸿皟瀹氭椂浠诲姟鍒楄〃
+     */
+    @PreAuthorize("@ss.hasPermi('system:airconditioner:schedule:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(AirConditionerSchedule airConditionerSchedule)
+    {
+        startPage();
+        List<AirConditionerSchedule> list = airConditionerScheduleService.selectAirConditionerScheduleList(airConditionerSchedule);
+        return getDataTable(list);
+    }
+
+    /**
+     * 鑾峰彇绌鸿皟瀹氭椂浠诲姟璇︾粏淇℃伅
+     */
+    @PreAuthorize("@ss.hasPermi('system:airconditioner:schedule:query')")
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id)
+    {
+        return success(airConditionerScheduleService.selectAirConditionerScheduleById(id));
+    }
+    
+    /**
+     * 鏍规嵁绌鸿皟ID鏌ヨ瀹氭椂浠诲姟鍒楄〃
+     */
+    @PreAuthorize("@ss.hasPermi('system:airconditioner:schedule:list')")
+    @GetMapping("/byAirConditioner/{airConditionerId}")
+    public AjaxResult getByAirConditionerId(@PathVariable("airConditionerId") Long airConditionerId)
+    {
+        List<AirConditionerSchedule> list = airConditionerScheduleService.selectAirConditionerScheduleByAirConditionerId(airConditionerId);
+        return success(list);
+    }
+
+    /**
+     * 鏂板绌鸿皟瀹氭椂浠诲姟
+     */
+    @PreAuthorize("@ss.hasPermi('system:airconditioner:schedule:add')")
+    @Log(title = "绌鸿皟瀹氭椂浠诲姟", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody AirConditionerSchedule airConditionerSchedule)
+    {
+        return toAjax(airConditionerScheduleService.insertAirConditionerSchedule(airConditionerSchedule));
+    }
+
+    /**
+     * 淇敼绌鸿皟瀹氭椂浠诲姟
+     */
+    @PreAuthorize("@ss.hasPermi('system:airconditioner:schedule:edit')")
+    @Log(title = "绌鸿皟瀹氭椂浠诲姟", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody AirConditionerSchedule airConditionerSchedule)
+    {
+        return toAjax(airConditionerScheduleService.updateAirConditionerSchedule(airConditionerSchedule));
+    }
+
+    /**
+     * 鍒犻櫎绌鸿皟瀹氭椂浠诲姟
+     */
+    @PreAuthorize("@ss.hasPermi('system:airconditioner:schedule:remove')")
+    @Log(title = "绌鸿皟瀹氭椂浠诲姟", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids)
+    {
+        return toAjax(airConditionerScheduleService.deleteAirConditionerScheduleByIds(ids));
+    }
+}
\ No newline at end of file
diff --git a/zhitan-airconditioner/pom.xml b/zhitan-airconditioner/pom.xml
new file mode 100644
index 0000000..192661d
--- /dev/null
+++ b/zhitan-airconditioner/pom.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>BaseAdminAPI</artifactId>
+        <groupId>com.zhitan</groupId>
+        <version>2.5.2</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>zhitan-airconditioner</artifactId>
+
+    <description>
+        绌鸿皟鎺у埗妯″潡
+    </description>
+
+    <dependencies>
+        <!-- 閫氱敤宸ュ叿-->
+        <dependency>
+            <groupId>com.zhitan</groupId>
+            <artifactId>zhitan-common</artifactId>
+        </dependency>
+
+        <!-- framework妗嗘灦-->
+        <dependency>
+            <groupId>com.zhitan</groupId>
+            <artifactId>zhitan-framework</artifactId>
+        </dependency>
+
+        <!-- MQTT瀹㈡埛绔� -->
+        <dependency>
+            <groupId>org.eclipse.paho</groupId>
+            <artifactId>org.eclipse.paho.client.mqttv3</artifactId>
+            <version>1.2.5</version>
+        </dependency>
+
+        <!-- SpringBoot闆嗘垚MQTT -->
+        <dependency>
+            <groupId>org.springframework.integration</groupId>
+            <artifactId>spring-integration-mqtt</artifactId>
+        </dependency>
+    </dependencies>
+</project>
\ No newline at end of file
diff --git a/zhitan-airconditioner/src/main/java/com/zhitan/airconditioner/config/SchedulingConfig.java b/zhitan-airconditioner/src/main/java/com/zhitan/airconditioner/config/SchedulingConfig.java
new file mode 100644
index 0000000..e019f86
--- /dev/null
+++ b/zhitan-airconditioner/src/main/java/com/zhitan/airconditioner/config/SchedulingConfig.java
@@ -0,0 +1,33 @@
+package com.zhitan.airconditioner.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.TaskScheduler;
+import org.springframework.scheduling.annotation.EnableScheduling;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
+
+/**
+ * 瀹氭椂浠诲姟璋冨害閰嶇疆
+ * 
+ * @author zhitan
+ */
+@Configuration
+@EnableScheduling
+public class SchedulingConfig {
+    
+    /**
+     * 閰嶇疆TaskScheduler锛岀敤浜庡姩鎬佽皟搴︿换鍔�
+     * 璁剧疆绾跨▼姹犲ぇ灏忎负5锛屽彲浠ユ牴鎹疄闄呴渶姹傝皟鏁�
+     * 
+     * @return TaskScheduler瀹炰緥
+     */
+    @Bean
+    public TaskScheduler taskScheduler() {
+        ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
+        scheduler.setPoolSize(5);
+        scheduler.setThreadNamePrefix("air-conditioner-scheduler-");
+        scheduler.setWaitForTasksToCompleteOnShutdown(true);
+        scheduler.setAwaitTerminationSeconds(60);
+        return scheduler;
+    }
+}
\ No newline at end of file
diff --git a/zhitan-airconditioner/src/main/java/com/zhitan/airconditioner/domain/AirConditioner.java b/zhitan-airconditioner/src/main/java/com/zhitan/airconditioner/domain/AirConditioner.java
new file mode 100644
index 0000000..c01cb92
--- /dev/null
+++ b/zhitan-airconditioner/src/main/java/com/zhitan/airconditioner/domain/AirConditioner.java
@@ -0,0 +1,39 @@
+package com.zhitan.airconditioner.domain;
+
+import com.zhitan.common.annotation.Excel;
+import com.zhitan.common.core.domain.BaseEntity;
+import lombok.Data;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
+/**
+ * 绌鸿皟鎺у埗鍣ㄥ璞� sys_air_conditioner
+ * 
+ * @author zhitan
+ */
+@Data
+public class AirConditioner extends BaseEntity
+{
+    private static final long serialVersionUID = 1L;
+
+    /** 绌鸿皟ID */
+    private Long id;
+
+    /** 绌鸿皟鍚嶇О */
+    @Excel(name = "绌鸿皟鍚嶇О")
+    private String name;
+
+    private String mode;
+
+    /** 鎺у埗鍣↖D */
+    @Excel(name = "鎺у埗鍣↖D")
+    private String controllerId;
+
+    /** 瀹夎浣嶇疆 */
+    @Excel(name = "瀹夎浣嶇疆")
+    private String location;
+
+    /** 鐘舵�侊紙0姝e父 1鍋滅敤锛� */
+    @Excel(name = "鐘舵��", readConverterExp = "0=姝e父,1=鍋滅敤")
+    private String status;
+}
\ No newline at end of file
diff --git a/zhitan-airconditioner/src/main/java/com/zhitan/airconditioner/domain/AirConditionerLog.java b/zhitan-airconditioner/src/main/java/com/zhitan/airconditioner/domain/AirConditionerLog.java
new file mode 100644
index 0000000..3049ad7
--- /dev/null
+++ b/zhitan-airconditioner/src/main/java/com/zhitan/airconditioner/domain/AirConditionerLog.java
@@ -0,0 +1,148 @@
+package com.zhitan.airconditioner.domain;
+
+import com.zhitan.common.annotation.Excel;
+import com.zhitan.common.core.domain.BaseEntity;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
+import java.util.Date;
+
+/**
+ * 绌鸿皟鎿嶄綔鏃ュ織瀵硅薄 sys_air_conditioner_log
+ * 
+ * @author zhitan
+ */
+public class AirConditionerLog extends BaseEntity
+{
+    private static final long serialVersionUID = 1L;
+
+    /** 鏃ュ織ID */
+    private Long id;
+
+    /** 绌鸿皟ID */
+    @Excel(name = "绌鸿皟ID")
+    private Long airConditionerId;
+
+    /** 绌鸿皟鍚嶇О */
+    @Excel(name = "绌鸿皟鍚嶇О")
+    private String airConditionerName;
+
+    /** 鎿嶄綔绫诲瀷锛�0鎵嬪姩 1鑷姩锛� */
+    @Excel(name = "鎿嶄綔绫诲瀷", readConverterExp = "0=鎵嬪姩,1=鑷姩")
+    private String operateType;
+
+    /** 鎿嶄綔妯″紡锛�0鍒跺喎寮�鏈� 1鍒剁儹寮�鏈� 2鍏虫満锛� */
+    @Excel(name = "鎿嶄綔妯″紡", readConverterExp = "0=鍒跺喎寮�鏈�,1=鍒剁儹寮�鏈�,2=鍏虫満")
+    private String operateMode;
+
+    /** 鎿嶄綔鏃堕棿 */
+    @Excel(name = "鎿嶄綔鏃堕棿", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
+    private Date operateTime;
+
+    /** 鎿嶄綔缁撴灉锛�0鎴愬姛 1澶辫触锛� */
+    @Excel(name = "鎿嶄綔缁撴灉", readConverterExp = "0=鎴愬姛,1=澶辫触")
+    private String operateResult;
+
+    /** 鎿嶄綔娑堟伅 */
+    @Excel(name = "鎿嶄綔娑堟伅")
+    private String operateMsg;
+
+    public void setId(Long id) 
+    {
+        this.id = id;
+    }
+
+    public Long getId() 
+    {
+        return id;
+    }
+
+    public void setAirConditionerId(Long airConditionerId)
+    {
+        this.airConditionerId = airConditionerId;
+    }
+
+    public Long getAirConditionerId()
+    {
+        return airConditionerId;
+    }
+
+    public void setAirConditionerName(String airConditionerName) 
+    {
+        this.airConditionerName = airConditionerName;
+    }
+
+    public String getAirConditionerName() 
+    {
+        return airConditionerName;
+    }
+
+    public void setOperateType(String operateType) 
+    {
+        this.operateType = operateType;
+    }
+
+    public String getOperateType() 
+    {
+        return operateType;
+    }
+
+    public void setOperateMode(String operateMode) 
+    {
+        this.operateMode = operateMode;
+    }
+
+    public String getOperateMode() 
+    {
+        return operateMode;
+    }
+
+    public void setOperateTime(Date operateTime) 
+    {
+        this.operateTime = operateTime;
+    }
+
+    public Date getOperateTime() 
+    {
+        return operateTime;
+    }
+
+    public void setOperateResult(String operateResult) 
+    {
+        this.operateResult = operateResult;
+    }
+
+    public String getOperateResult() 
+    {
+        return operateResult;
+    }
+
+    public void setOperateMsg(String operateMsg) 
+    {
+        this.operateMsg = operateMsg;
+    }
+
+    public String getOperateMsg() 
+    {
+        return operateMsg;
+    }
+
+    @Override
+    public String toString() {
+        return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
+            .append("id", getId())
+            .append("airConditionerId", getAirConditionerId())
+            .append("airConditionerName", getAirConditionerName())
+            .append("operateType", getOperateType())
+            .append("operateMode", getOperateMode())
+            .append("operateTime", getOperateTime())
+            .append("operateResult", getOperateResult())
+            .append("operateMsg", getOperateMsg())
+            .append("createBy", getCreateBy())
+            .append("createTime", getCreateTime())
+            .append("updateBy", getUpdateBy())
+            .append("updateTime", getUpdateTime())
+            .append("remark", getRemark())
+            .toString();
+    }
+}
\ No newline at end of file
diff --git a/zhitan-airconditioner/src/main/java/com/zhitan/airconditioner/domain/AirConditionerSchedule.java b/zhitan-airconditioner/src/main/java/com/zhitan/airconditioner/domain/AirConditionerSchedule.java
new file mode 100644
index 0000000..616e6f6
--- /dev/null
+++ b/zhitan-airconditioner/src/main/java/com/zhitan/airconditioner/domain/AirConditionerSchedule.java
@@ -0,0 +1,48 @@
+package com.zhitan.airconditioner.domain;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.zhitan.common.annotation.Excel;
+import com.zhitan.common.core.domain.BaseEntity;
+import lombok.Data;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
+import java.util.Date;
+
+/**
+ * 绌鸿皟瀹氭椂浠诲姟瀵硅薄 sys_air_conditioner_schedule
+ * 
+ * @author zhitan
+ */
+@Data
+public class AirConditionerSchedule extends BaseEntity
+{
+    private static final long serialVersionUID = 1L;
+
+    /** 瀹氭椂浠诲姟ID */
+    private Long id;
+
+    /** 绌鸿皟ID */
+    @Excel(name = "绌鸿皟ID")
+    private Long airConditionerId;
+
+    /** 寮�濮嬫椂闂� */
+    @Excel(name = "寮�濮嬫椂闂�", width = 30, dateFormat = "HH:mm:ss")
+    @JsonFormat(pattern = "HH:mm:ss")
+    private Date startTime;
+
+    /** 缁撴潫鏃堕棿 */
+    @Excel(name = "缁撴潫鏃堕棿", width = 30, dateFormat = "HH:mm:ss")
+    @JsonFormat(pattern = "HH:mm:ss")
+    private Date offTime;
+
+    /** 鎺у埗妯″紡锛�0鍒跺喎 1鍒剁儹锛� */
+    @Excel(name = "鎺у埗妯″紡", readConverterExp = "0=鍒跺喎,1=鍒剁儹")
+    private String controlMode;
+
+    /** 鐘舵�侊紙0姝e父 1鍋滅敤锛� */
+    @Excel(name = "鐘舵��", readConverterExp = "0=姝e父,1=鍋滅敤")
+    private String status;
+
+
+}
\ No newline at end of file
diff --git a/zhitan-airconditioner/src/main/java/com/zhitan/airconditioner/mapper/AirConditionerLogMapper.java b/zhitan-airconditioner/src/main/java/com/zhitan/airconditioner/mapper/AirConditionerLogMapper.java
new file mode 100644
index 0000000..471f60d
--- /dev/null
+++ b/zhitan-airconditioner/src/main/java/com/zhitan/airconditioner/mapper/AirConditionerLogMapper.java
@@ -0,0 +1,72 @@
+package com.zhitan.airconditioner.mapper;
+
+import java.util.List;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.zhitan.airconditioner.domain.AirConditionerLog;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 绌鸿皟鎿嶄綔鏃ュ織Mapper鎺ュ彛
+ * 
+ * @author zhitan
+ */
+@Mapper
+public interface AirConditionerLogMapper extends BaseMapper<AirConditionerLog>
+{
+    /**
+     * 鏌ヨ绌鸿皟鎿嶄綔鏃ュ織鍒楄〃
+     * 
+     * @param airConditionerLog 绌鸿皟鎿嶄綔鏃ュ織淇℃伅
+     * @return 绌鸿皟鎿嶄綔鏃ュ織闆嗗悎
+     */
+    public List<AirConditionerLog> selectAirConditionerLogList(AirConditionerLog airConditionerLog);
+
+    /**
+     * 鏌ヨ绌鸿皟鎿嶄綔鏃ュ織淇℃伅
+     * 
+     * @param id 绌鸿皟鎿嶄綔鏃ュ織ID
+     * @return 绌鸿皟鎿嶄綔鏃ュ織淇℃伅
+     */
+    public AirConditionerLog selectAirConditionerLogById(Long id);
+    
+    /**
+     * 鏍规嵁绌鸿皟ID鏌ヨ鎿嶄綔鏃ュ織鍒楄〃
+     * 
+     * @param airConditionerId 绌鸿皟ID
+     * @return 鎿嶄綔鏃ュ織鍒楄〃
+     */
+    public List<AirConditionerLog> selectAirConditionerLogByAirConditionerId(Long airConditionerId);
+
+    /**
+     * 鏂板绌鸿皟鎿嶄綔鏃ュ織
+     * 
+     * @param airConditionerLog 绌鸿皟鎿嶄綔鏃ュ織淇℃伅
+     * @return 缁撴灉
+     */
+    public int insertAirConditionerLog(AirConditionerLog airConditionerLog);
+
+    /**
+     * 鍒犻櫎绌鸿皟鎿嶄綔鏃ュ織
+     * 
+     * @param id 绌鸿皟鎿嶄綔鏃ュ織ID
+     * @return 缁撴灉
+     */
+    public int deleteAirConditionerLogById(Long id);
+
+    /**
+     * 鎵归噺鍒犻櫎绌鸿皟鎿嶄綔鏃ュ織
+     * 
+     * @param ids 闇�瑕佸垹闄ょ殑鏁版嵁ID
+     * @return 缁撴灉
+     */
+    public int deleteAirConditionerLogByIds(Long[] ids);
+    
+    /**
+     * 鏍规嵁绌鸿皟ID鍒犻櫎鎿嶄綔鏃ュ織
+     * 
+     * @param airConditionerId 绌鸿皟ID
+     * @return 缁撴灉
+     */
+    public int deleteAirConditionerLogByAirConditionerId(Long airConditionerId);
+}
\ No newline at end of file
diff --git a/zhitan-airconditioner/src/main/java/com/zhitan/airconditioner/mapper/AirConditionerMapper.java b/zhitan-airconditioner/src/main/java/com/zhitan/airconditioner/mapper/AirConditionerMapper.java
new file mode 100644
index 0000000..f97f01e
--- /dev/null
+++ b/zhitan-airconditioner/src/main/java/com/zhitan/airconditioner/mapper/AirConditionerMapper.java
@@ -0,0 +1,72 @@
+package com.zhitan.airconditioner.mapper;
+
+import java.util.List;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.zhitan.airconditioner.domain.AirConditioner;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 绌鸿皟鎺у埗鍣∕apper鎺ュ彛
+ * 
+ * @author zhitan
+ */
+@Mapper
+public interface AirConditionerMapper extends BaseMapper<AirConditioner>
+{
+    /**
+     * 鏌ヨ绌鸿皟鎺у埗鍣ㄥ垪琛�
+     * 
+     * @param airConditioner 绌鸿皟鎺у埗鍣ㄤ俊鎭�
+     * @return 绌鸿皟鎺у埗鍣ㄩ泦鍚�
+     */
+    public List<AirConditioner> selectAirConditionerList(AirConditioner airConditioner);
+
+    /**
+     * 鏌ヨ绌鸿皟鎺у埗鍣ㄤ俊鎭�
+     * 
+     * @param id 绌鸿皟鎺у埗鍣↖D
+     * @return 绌鸿皟鎺у埗鍣ㄤ俊鎭�
+     */
+    public AirConditioner selectAirConditionerById(Long id);
+    
+    /**
+     * 鏍规嵁鎺у埗鍣↖D鏌ヨ绌鸿皟鎺у埗鍣ㄤ俊鎭�
+     * 
+     * @param controllerId 鎺у埗鍣↖D
+     * @return 绌鸿皟鎺у埗鍣ㄤ俊鎭�
+     */
+    public AirConditioner selectAirConditionerByControllerId(String controllerId);
+
+    /**
+     * 鏂板绌鸿皟鎺у埗鍣�
+     * 
+     * @param airConditioner 绌鸿皟鎺у埗鍣ㄤ俊鎭�
+     * @return 缁撴灉
+     */
+    public int insertAirConditioner(AirConditioner airConditioner);
+
+    /**
+     * 淇敼绌鸿皟鎺у埗鍣�
+     * 
+     * @param airConditioner 绌鸿皟鎺у埗鍣ㄤ俊鎭�
+     * @return 缁撴灉
+     */
+    public int updateAirConditioner(AirConditioner airConditioner);
+
+    /**
+     * 鍒犻櫎绌鸿皟鎺у埗鍣�
+     * 
+     * @param id 绌鸿皟鎺у埗鍣↖D
+     * @return 缁撴灉
+     */
+    public int deleteAirConditionerById(Long id);
+
+    /**
+     * 鎵归噺鍒犻櫎绌鸿皟鎺у埗鍣�
+     * 
+     * @param ids 闇�瑕佸垹闄ょ殑鏁版嵁ID
+     * @return 缁撴灉
+     */
+    public int deleteAirConditionerByIds(Long[] ids);
+}
\ No newline at end of file
diff --git a/zhitan-airconditioner/src/main/java/com/zhitan/airconditioner/mapper/AirConditionerScheduleMapper.java b/zhitan-airconditioner/src/main/java/com/zhitan/airconditioner/mapper/AirConditionerScheduleMapper.java
new file mode 100644
index 0000000..d2ee5c7
--- /dev/null
+++ b/zhitan-airconditioner/src/main/java/com/zhitan/airconditioner/mapper/AirConditionerScheduleMapper.java
@@ -0,0 +1,99 @@
+package com.zhitan.airconditioner.mapper;
+
+import java.util.List;
+import java.util.Date;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.zhitan.airconditioner.domain.AirConditionerSchedule;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+/**
+ * 绌鸿皟瀹氭椂浠诲姟Mapper鎺ュ彛
+ * 
+ * @author zhitan
+ */
+@Mapper
+public interface AirConditionerScheduleMapper extends BaseMapper<AirConditionerSchedule>
+{
+    /**
+     * 鏌ヨ绌鸿皟瀹氭椂浠诲姟鍒楄〃
+     * 
+     * @param airConditionerSchedule 绌鸿皟瀹氭椂浠诲姟淇℃伅
+     * @return 绌鸿皟瀹氭椂浠诲姟闆嗗悎
+     */
+    public List<AirConditionerSchedule> selectAirConditionerScheduleList(AirConditionerSchedule airConditionerSchedule);
+
+    /**
+     * 鏌ヨ绌鸿皟瀹氭椂浠诲姟淇℃伅
+     * 
+     * @param id 绌鸿皟瀹氭椂浠诲姟ID
+     * @return 绌鸿皟瀹氭椂浠诲姟淇℃伅
+     */
+    public AirConditionerSchedule selectAirConditionerScheduleById(Long id);
+    
+    /**
+     * 鏍规嵁绌鸿皟ID鏌ヨ瀹氭椂浠诲姟鍒楄〃
+     * 
+     * @param airConditionerId 绌鸿皟ID
+     * @return 瀹氭椂浠诲姟鍒楄〃
+     */
+    public List<AirConditionerSchedule> selectAirConditionerScheduleByAirConditionerId(Long airConditionerId);
+    
+    /**
+     * 鏌ヨ褰撳墠鏃堕棿鍐呴渶瑕佹墽琛岀殑瀹氭椂浠诲姟
+     * 
+     * @param currentTime 褰撳墠鏃堕棿
+     * @return 瀹氭椂浠诲姟鍒楄〃
+     */
+    public List<AirConditionerSchedule> selectActiveSchedules(@Param("currentTime") Date currentTime);
+    
+    /**
+     * 鏍规嵁鏃堕棿鐐规煡璇㈤渶瑕佹墽琛岀殑瀹氭椂浠诲姟
+     * 鏌ヨ寮�濮嬫椂闂存垨缁撴潫鏃堕棿涓庢寚瀹氭椂闂寸偣鍖归厤鐨勪换鍔�
+     * 
+     * @param timePoint 鏃堕棿鐐癸紝鏍煎紡涓篐H:mm
+     * @return 瀹氭椂浠诲姟鍒楄〃
+     */
+    public List<AirConditionerSchedule> selectSchedulesByTimePoint(@Param("timePoint") String timePoint);
+
+    /**
+     * 鏂板绌鸿皟瀹氭椂浠诲姟
+     * 
+     * @param airConditionerSchedule 绌鸿皟瀹氭椂浠诲姟淇℃伅
+     * @return 缁撴灉
+     */
+    public int insertAirConditionerSchedule(AirConditionerSchedule airConditionerSchedule);
+
+    /**
+     * 淇敼绌鸿皟瀹氭椂浠诲姟
+     * 
+     * @param airConditionerSchedule 绌鸿皟瀹氭椂浠诲姟淇℃伅
+     * @return 缁撴灉
+     */
+    public int updateAirConditionerSchedule(AirConditionerSchedule airConditionerSchedule);
+
+    /**
+     * 鍒犻櫎绌鸿皟瀹氭椂浠诲姟
+     * 
+     * @param id 绌鸿皟瀹氭椂浠诲姟ID
+     * @return 缁撴灉
+     */
+    public int deleteAirConditionerScheduleById(Long id);
+
+    /**
+     * 鎵归噺鍒犻櫎绌鸿皟瀹氭椂浠诲姟
+     * 
+     * @param ids 闇�瑕佸垹闄ょ殑鏁版嵁ID
+     * @return 缁撴灉
+     */
+    public int deleteAirConditionerScheduleByIds(Long[] ids);
+    
+    /**
+     * 鏍规嵁绌鸿皟ID鍒犻櫎瀹氭椂浠诲姟
+     * 
+     * @param airConditionerId 绌鸿皟ID
+     * @return 缁撴灉
+     */
+    public int deleteAirConditionerScheduleByAirConditionerId(String airConditionerId);
+}
\ No newline at end of file
diff --git a/zhitan-airconditioner/src/main/java/com/zhitan/airconditioner/service/IAirConditionerLogService.java b/zhitan-airconditioner/src/main/java/com/zhitan/airconditioner/service/IAirConditionerLogService.java
new file mode 100644
index 0000000..dde2c51
--- /dev/null
+++ b/zhitan-airconditioner/src/main/java/com/zhitan/airconditioner/service/IAirConditionerLogService.java
@@ -0,0 +1,59 @@
+package com.zhitan.airconditioner.service;
+
+import java.util.List;
+import com.zhitan.airconditioner.domain.AirConditionerLog;
+
+/**
+ * 绌鸿皟鎿嶄綔鏃ュ織Service鎺ュ彛
+ * 
+ * @author zhitan
+ */
+public interface IAirConditionerLogService {
+    /**
+     * 鏌ヨ绌鸿皟鎿嶄綔鏃ュ織鍒楄〃
+     * 
+     * @param airConditionerLog 绌鸿皟鎿嶄綔鏃ュ織淇℃伅
+     * @return 绌鸿皟鎿嶄綔鏃ュ織闆嗗悎
+     */
+    public List<AirConditionerLog> selectAirConditionerLogList(AirConditionerLog airConditionerLog);
+
+    /**
+     * 鏌ヨ绌鸿皟鎿嶄綔鏃ュ織淇℃伅
+     * 
+     * @param id 绌鸿皟鎿嶄綔鏃ュ織ID
+     * @return 绌鸿皟鎿嶄綔鏃ュ織淇℃伅
+     */
+    public AirConditionerLog selectAirConditionerLogById(Long id);
+    
+    /**
+     * 鏍规嵁绌鸿皟ID鏌ヨ鎿嶄綔鏃ュ織鍒楄〃
+     * 
+     * @param airConditionerId 绌鸿皟ID
+     * @return 鎿嶄綔鏃ュ織鍒楄〃
+     */
+    public List<AirConditionerLog> selectAirConditionerLogByAirConditionerId(Long airConditionerId);
+
+    /**
+     * 鏂板绌鸿皟鎿嶄綔鏃ュ織
+     * 
+     * @param airConditionerLog 绌鸿皟鎿嶄綔鏃ュ織淇℃伅
+     * @return 缁撴灉
+     */
+    public int insertAirConditionerLog(AirConditionerLog airConditionerLog);
+
+    /**
+     * 鍒犻櫎绌鸿皟鎿嶄綔鏃ュ織淇℃伅
+     * 
+     * @param id 绌鸿皟鎿嶄綔鏃ュ織ID
+     * @return 缁撴灉
+     */
+    public int deleteAirConditionerLogById(Long id);
+
+    /**
+     * 鎵归噺鍒犻櫎绌鸿皟鎿嶄綔鏃ュ織淇℃伅
+     * 
+     * @param ids 闇�瑕佸垹闄ょ殑绌鸿皟鎿嶄綔鏃ュ織ID
+     * @return 缁撴灉
+     */
+    public int deleteAirConditionerLogByIds(Long[] ids);
+}
\ No newline at end of file
diff --git a/zhitan-airconditioner/src/main/java/com/zhitan/airconditioner/service/IAirConditionerScheduleService.java b/zhitan-airconditioner/src/main/java/com/zhitan/airconditioner/service/IAirConditionerScheduleService.java
new file mode 100644
index 0000000..52176e2
--- /dev/null
+++ b/zhitan-airconditioner/src/main/java/com/zhitan/airconditioner/service/IAirConditionerScheduleService.java
@@ -0,0 +1,83 @@
+package com.zhitan.airconditioner.service;
+
+import java.util.List;
+import com.zhitan.airconditioner.domain.AirConditionerSchedule;
+
+/**
+ * 绌鸿皟瀹氭椂浠诲姟Service鎺ュ彛
+ * 
+ * @author zhitan
+ */
+public interface IAirConditionerScheduleService 
+{
+    /**
+     * 鏌ヨ绌鸿皟瀹氭椂浠诲姟鍒楄〃
+     * 
+     * @param airConditionerSchedule 绌鸿皟瀹氭椂浠诲姟淇℃伅
+     * @return 绌鸿皟瀹氭椂浠诲姟闆嗗悎
+     */
+    public List<AirConditionerSchedule> selectAirConditionerScheduleList(AirConditionerSchedule airConditionerSchedule);
+
+    /**
+     * 鏌ヨ绌鸿皟瀹氭椂浠诲姟淇℃伅
+     * 
+     * @param id 绌鸿皟瀹氭椂浠诲姟ID
+     * @return 绌鸿皟瀹氭椂浠诲姟淇℃伅
+     */
+    public AirConditionerSchedule selectAirConditionerScheduleById(Long id);
+    
+    /**
+     * 鏍规嵁绌鸿皟ID鏌ヨ瀹氭椂浠诲姟鍒楄〃
+     * 
+     * @param airConditionerId 绌鸿皟ID
+     * @return 瀹氭椂浠诲姟鍒楄〃
+     */
+    public List<AirConditionerSchedule> selectAirConditionerScheduleByAirConditionerId(Long airConditionerId);
+
+    /**
+     * 鏂板绌鸿皟瀹氭椂浠诲姟
+     * 
+     * @param airConditionerSchedule 绌鸿皟瀹氭椂浠诲姟淇℃伅
+     * @return 缁撴灉
+     */
+    public int insertAirConditionerSchedule(AirConditionerSchedule airConditionerSchedule);
+
+    /**
+     * 淇敼绌鸿皟瀹氭椂浠诲姟
+     * 
+     * @param airConditionerSchedule 绌鸿皟瀹氭椂浠诲姟淇℃伅
+     * @return 缁撴灉
+     */
+    public int updateAirConditionerSchedule(AirConditionerSchedule airConditionerSchedule);
+
+    /**
+     * 鎵归噺鍒犻櫎绌鸿皟瀹氭椂浠诲姟
+     * 
+     * @param ids 闇�瑕佸垹闄ょ殑绌鸿皟瀹氭椂浠诲姟ID
+     * @return 缁撴灉
+     */
+    public int deleteAirConditionerScheduleByIds(Long[] ids);
+
+    /**
+     * 鍒犻櫎绌鸿皟瀹氭椂浠诲姟淇℃伅
+     * 
+     * @param id 绌鸿皟瀹氭椂浠诲姟ID
+     * @return 缁撴灉
+     */
+    public int deleteAirConditionerScheduleById(Long id);
+    
+    /**
+     * 鏍规嵁绌鸿皟ID鍒犻櫎瀹氭椂浠诲姟
+     * 
+     * @param airConditionerId 绌鸿皟ID
+     * @return 缁撴灉
+     */
+    public int deleteAirConditionerScheduleByAirConditionerId(String airConditionerId);
+    
+    /**
+     * 鎵ц绌鸿皟瀹氭椂浠诲姟
+     * 
+     * @return 缁撴灉
+     */
+    public void executeScheduleTasks();
+}
\ No newline at end of file
diff --git a/zhitan-airconditioner/src/main/java/com/zhitan/airconditioner/service/IAirConditionerService.java b/zhitan-airconditioner/src/main/java/com/zhitan/airconditioner/service/IAirConditionerService.java
new file mode 100644
index 0000000..b101773
--- /dev/null
+++ b/zhitan-airconditioner/src/main/java/com/zhitan/airconditioner/service/IAirConditionerService.java
@@ -0,0 +1,78 @@
+package com.zhitan.airconditioner.service;
+
+import java.util.List;
+import com.zhitan.airconditioner.domain.AirConditioner;
+
+/**
+ * 绌鸿皟鎺у埗鍣⊿ervice鎺ュ彛
+ * 
+ * @author zhitan
+ */
+public interface IAirConditionerService 
+{
+    /**
+     * 鏌ヨ绌鸿皟鎺у埗鍣ㄥ垪琛�
+     * 
+     * @param airConditioner 绌鸿皟鎺у埗鍣ㄤ俊鎭�
+     * @return 绌鸿皟鎺у埗鍣ㄩ泦鍚�
+     */
+    public List<AirConditioner> selectAirConditionerList(AirConditioner airConditioner);
+
+    /**
+     * 鏌ヨ绌鸿皟鎺у埗鍣ㄤ俊鎭�
+     * 
+     * @param id 绌鸿皟鎺у埗鍣↖D
+     * @return 绌鸿皟鎺у埗鍣ㄤ俊鎭�
+     */
+    public AirConditioner selectAirConditionerById(Long id);
+    
+    /**
+     * 鏍规嵁鎺у埗鍣↖D鏌ヨ绌鸿皟鎺у埗鍣ㄤ俊鎭�
+     * 
+     * @param controllerId 鎺у埗鍣↖D
+     * @return 绌鸿皟鎺у埗鍣ㄤ俊鎭�
+     */
+    public AirConditioner selectAirConditionerByControllerId(String controllerId);
+
+    /**
+     * 鏂板绌鸿皟鎺у埗鍣�
+     * 
+     * @param airConditioner 绌鸿皟鎺у埗鍣ㄤ俊鎭�
+     * @return 缁撴灉
+     */
+    public int insertAirConditioner(AirConditioner airConditioner);
+
+    /**
+     * 淇敼绌鸿皟鎺у埗鍣�
+     * 
+     * @param airConditioner 绌鸿皟鎺у埗鍣ㄤ俊鎭�
+     * @return 缁撴灉
+     */
+    public int updateAirConditioner(AirConditioner airConditioner);
+
+    /**
+     * 鎵归噺鍒犻櫎绌鸿皟鎺у埗鍣�
+     * 
+     * @param ids 闇�瑕佸垹闄ょ殑绌鸿皟鎺у埗鍣↖D
+     * @return 缁撴灉
+     */
+    public int deleteAirConditionerByIds(Long[] ids);
+
+    /**
+     * 鍒犻櫎绌鸿皟鎺у埗鍣ㄤ俊鎭�
+     * 
+     * @param id 绌鸿皟鎺у埗鍣↖D
+     * @return 缁撴灉
+     */
+    public int deleteAirConditionerById(Long id);
+    
+    /**
+     * 鎺у埗绌鸿皟寮�鍏虫満
+     * 
+     * @param id 绌鸿皟ID
+     * @param mode 鎺у埗妯″紡锛�0鍒跺喎 1鍒剁儹 2鍏虫満锛�
+     * @param operateType 鎿嶄綔绫诲瀷锛�0鎵嬪姩 1鑷姩锛�
+     * @return 缁撴灉
+     */
+    public boolean controlAirConditioner(Long id, String mode, String operateType);
+}
\ No newline at end of file
diff --git a/zhitan-airconditioner/src/main/java/com/zhitan/airconditioner/service/impl/AirConditionerLogServiceImpl.java b/zhitan-airconditioner/src/main/java/com/zhitan/airconditioner/service/impl/AirConditionerLogServiceImpl.java
new file mode 100644
index 0000000..d0b939a
--- /dev/null
+++ b/zhitan-airconditioner/src/main/java/com/zhitan/airconditioner/service/impl/AirConditionerLogServiceImpl.java
@@ -0,0 +1,85 @@
+package com.zhitan.airconditioner.service.impl;
+
+import java.util.List;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.zhitan.airconditioner.mapper.AirConditionerLogMapper;
+import com.zhitan.airconditioner.domain.AirConditionerLog;
+import com.zhitan.airconditioner.service.IAirConditionerLogService;
+
+/**
+ * 绌鸿皟鎿嶄綔鏃ュ織Service涓氬姟灞傚鐞�
+ * 
+ * @author zhitan
+ */
+@Service
+public class AirConditionerLogServiceImpl implements IAirConditionerLogService {
+    @Autowired
+    private AirConditionerLogMapper airConditionerLogMapper;
+
+    /**
+     * 鏌ヨ绌鸿皟鎿嶄綔鏃ュ織鍒楄〃
+     * 
+     * @param airConditionerLog 绌鸿皟鎿嶄綔鏃ュ織淇℃伅
+     * @return 绌鸿皟鎿嶄綔鏃ュ織闆嗗悎
+     */
+    @Override
+    public List<AirConditionerLog> selectAirConditionerLogList(AirConditionerLog airConditionerLog) {
+        return airConditionerLogMapper.selectAirConditionerLogList(airConditionerLog);
+    }
+
+    /**
+     * 鏌ヨ绌鸿皟鎿嶄綔鏃ュ織淇℃伅
+     * 
+     * @param id 绌鸿皟鎿嶄綔鏃ュ織ID
+     * @return 绌鸿皟鎿嶄綔鏃ュ織淇℃伅
+     */
+    @Override
+    public AirConditionerLog selectAirConditionerLogById(Long id) {
+        return airConditionerLogMapper.selectAirConditionerLogById(id);
+    }
+    
+    /**
+     * 鏍规嵁绌鸿皟ID鏌ヨ鎿嶄綔鏃ュ織鍒楄〃
+     * 
+     * @param airConditionerId 绌鸿皟ID
+     * @return 鎿嶄綔鏃ュ織鍒楄〃
+     */
+    @Override
+    public List<AirConditionerLog> selectAirConditionerLogByAirConditionerId(Long airConditionerId) {
+        return airConditionerLogMapper.selectAirConditionerLogByAirConditionerId(airConditionerId);
+    }
+
+    /**
+     * 鏂板绌鸿皟鎿嶄綔鏃ュ織
+     * 
+     * @param airConditionerLog 绌鸿皟鎿嶄綔鏃ュ織淇℃伅
+     * @return 缁撴灉
+     */
+    @Override
+    public int insertAirConditionerLog(AirConditionerLog airConditionerLog) {
+        return airConditionerLogMapper.insertAirConditionerLog(airConditionerLog);
+    }
+
+    /**
+     * 鍒犻櫎绌鸿皟鎿嶄綔鏃ュ織淇℃伅
+     * 
+     * @param id 绌鸿皟鎿嶄綔鏃ュ織ID
+     * @return 缁撴灉
+     */
+    @Override
+    public int deleteAirConditionerLogById(Long id) {
+        return airConditionerLogMapper.deleteAirConditionerLogById(id);
+    }
+
+    /**
+     * 鎵归噺鍒犻櫎绌鸿皟鎿嶄綔鏃ュ織淇℃伅
+     * 
+     * @param ids 闇�瑕佸垹闄ょ殑绌鸿皟鎿嶄綔鏃ュ織ID
+     * @return 缁撴灉
+     */
+    @Override
+    public int deleteAirConditionerLogByIds(Long[] ids) {
+        return airConditionerLogMapper.deleteAirConditionerLogByIds(ids);
+    }
+}
\ No newline at end of file
diff --git a/zhitan-airconditioner/src/main/java/com/zhitan/airconditioner/service/impl/AirConditionerScheduleServiceImpl.java b/zhitan-airconditioner/src/main/java/com/zhitan/airconditioner/service/impl/AirConditionerScheduleServiceImpl.java
new file mode 100644
index 0000000..d62ee18
--- /dev/null
+++ b/zhitan-airconditioner/src/main/java/com/zhitan/airconditioner/service/impl/AirConditionerScheduleServiceImpl.java
@@ -0,0 +1,506 @@
+package com.zhitan.airconditioner.service.impl;
+
+import java.text.SimpleDateFormat;
+import java.time.Duration;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.ZoneId;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ScheduledFuture;
+
+import com.zhitan.airconditioner.domain.AirConditionerSchedule;
+import com.zhitan.airconditioner.mapper.AirConditionerScheduleMapper;
+import com.zhitan.airconditioner.service.IAirConditionerScheduleService;
+import com.zhitan.airconditioner.service.IAirConditionerService;
+import com.zhitan.common.utils.DateTimeUtil;
+import com.zhitan.common.utils.DateUtils;
+import com.zhitan.common.utils.SecurityUtils;
+import com.zhitan.system.service.ISysHolidayService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.TaskScheduler;
+import org.springframework.stereotype.Service;
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+
+/**
+ * 绌鸿皟瀹氭椂浠诲姟Service涓氬姟灞傚鐞�
+ * 
+ * @author zhitan
+ */
+/**
+ * 绌鸿皟瀹氭椂浠诲姟Service涓氬姟灞傚鐞�
+ * 
+ * @author zhitan
+ */
+@Service
+public class AirConditionerScheduleServiceImpl implements IAirConditionerScheduleService 
+{    
+    private static final Logger log = LoggerFactory.getLogger(AirConditionerScheduleServiceImpl.class);
+    
+    @Autowired
+    private AirConditionerScheduleMapper airConditionerScheduleMapper;
+    
+    @Autowired
+    private IAirConditionerService airConditionerService;
+    
+    @Autowired
+    private TaskScheduler taskScheduler;
+    
+    @Autowired
+    private ISysHolidayService sysHolidayService;
+    
+    // 瀛樺偍鎵�鏈夎皟搴︾殑浠诲姟锛宬ey涓轰换鍔D锛寁alue涓鸿皟搴︾殑Future瀵硅薄
+    private final Map<String, ScheduledFuture<?>> scheduledTasks = new ConcurrentHashMap<>();
+    
+    /**
+     * 鍒濆鍖栧畾鏃朵换鍔�
+     * 浠庢暟鎹簱鍔犺浇鎵�鏈夊畾鏃朵换鍔″苟杩涜璋冨害
+     */
+    @PostConstruct
+    public void init() {
+        log.info("绌鸿皟瀹氭椂浠诲姟璋冨害鍣ㄥ垵濮嬪寲寮�濮�");
+        
+        // 鏌ヨ鎵�鏈夋湁鏁堢殑瀹氭椂浠诲姟
+        AirConditionerSchedule query = new AirConditionerSchedule();
+        query.setStatus("0"); // 鐘舵�佹甯哥殑浠诲姟
+        List<AirConditionerSchedule> schedules = airConditionerScheduleMapper.selectAirConditionerScheduleList(query);
+        
+        if (schedules.isEmpty()) {
+            log.info("娌℃湁鎵惧埌闇�瑕佽皟搴︾殑绌鸿皟瀹氭椂浠诲姟");
+            return;
+        }
+        
+        log.info("鎵惧埌{}涓渶瑕佽皟搴︾殑绌鸿皟瀹氭椂浠诲姟", schedules.size());
+        
+        // 涓烘瘡涓换鍔″垱寤鸿皟搴�
+        for (AirConditionerSchedule schedule : schedules) {
+            scheduleTask(schedule);
+        }
+        
+        log.info("绌鸿皟瀹氭椂浠诲姟璋冨害鍣ㄥ凡鍚姩锛屼娇鐢ㄥ熀浜庢暟鎹簱鐨勭簿纭皟搴︽満鍒�");
+    }
+    
+    /**
+     * 搴旂敤鍏抽棴鏃跺彇娑堟墍鏈夊畾鏃朵换鍔�
+     */
+    @PreDestroy
+    public void destroy() {
+        for (Map.Entry<String, ScheduledFuture<?>> entry : scheduledTasks.entrySet()) {
+            entry.getValue().cancel(false);
+        }
+        scheduledTasks.clear();
+        log.info("绌鸿皟瀹氭椂浠诲姟璋冨害鍣ㄥ凡鍏抽棴锛屾墍鏈変换鍔″凡鍙栨秷");
+    }
+
+    /**
+     * 鏌ヨ绌鸿皟瀹氭椂浠诲姟鍒楄〃
+     * 
+     * @param airConditionerSchedule 绌鸿皟瀹氭椂浠诲姟淇℃伅
+     * @return 绌鸿皟瀹氭椂浠诲姟闆嗗悎
+     */
+    @Override
+    public List<AirConditionerSchedule> selectAirConditionerScheduleList(AirConditionerSchedule airConditionerSchedule)
+    {
+        return airConditionerScheduleMapper.selectAirConditionerScheduleList(airConditionerSchedule);
+    }
+
+    /**
+     * 鏌ヨ绌鸿皟瀹氭椂浠诲姟淇℃伅
+     * 
+     * @param id 绌鸿皟瀹氭椂浠诲姟ID
+     * @return 绌鸿皟瀹氭椂浠诲姟淇℃伅
+     */
+    @Override
+    public AirConditionerSchedule selectAirConditionerScheduleById(Long id)
+    {
+        return airConditionerScheduleMapper.selectAirConditionerScheduleById(id);
+    }
+    
+    /**
+     * 鏍规嵁绌鸿皟ID鏌ヨ瀹氭椂浠诲姟鍒楄〃
+     * 
+     * @param airConditionerId 绌鸿皟ID
+     * @return 瀹氭椂浠诲姟鍒楄〃
+     */
+    @Override
+    public List<AirConditionerSchedule> selectAirConditionerScheduleByAirConditionerId(Long airConditionerId)
+    {
+        return airConditionerScheduleMapper.selectAirConditionerScheduleByAirConditionerId(airConditionerId);
+    }
+
+    /**
+     * 鏂板绌鸿皟瀹氭椂浠诲姟
+     * 
+     * @param airConditionerSchedule 绌鸿皟瀹氭椂浠诲姟淇℃伅
+     * @return 缁撴灉
+     */
+    @Override
+    public int insertAirConditionerSchedule(AirConditionerSchedule airConditionerSchedule)
+    {
+        airConditionerSchedule.setCreateTime(DateUtils.getNowDate());
+        airConditionerSchedule.setCreateBy(SecurityUtils.getUsername());
+        int rows = airConditionerScheduleMapper.insertAirConditionerSchedule(airConditionerSchedule);
+        if (rows > 0) {
+            // 閲嶆柊璋冨害浠诲姟锛屼娇鏂版坊鍔犵殑瀹氭椂浠诲姟鐢熸晥
+            rescheduleTask();
+        }
+        return rows;
+    }
+
+    /**
+     * 淇敼绌鸿皟瀹氭椂浠诲姟
+     * 
+     * @param airConditionerSchedule 绌鸿皟瀹氭椂浠诲姟淇℃伅
+     * @return 缁撴灉
+     */
+    @Override
+    public int updateAirConditionerSchedule(AirConditionerSchedule airConditionerSchedule)
+    {
+        airConditionerSchedule.setUpdateTime(DateUtils.getNowDate());
+        airConditionerSchedule.setUpdateBy(SecurityUtils.getUsername());
+        int rows = airConditionerScheduleMapper.updateAirConditionerSchedule(airConditionerSchedule);
+        if (rows > 0) {
+            // 閲嶆柊璋冨害浠诲姟锛屼娇淇敼鍚庣殑瀹氭椂浠诲姟鐢熸晥
+            rescheduleTask();
+        }
+        return rows;
+    }
+
+    /**
+     * 鎵归噺鍒犻櫎绌鸿皟瀹氭椂浠诲姟
+     * 
+     * @param ids 闇�瑕佸垹闄ょ殑绌鸿皟瀹氭椂浠诲姟ID
+     * @return 缁撴灉
+     */
+    @Override
+    public int deleteAirConditionerScheduleByIds(Long[] ids)
+    {
+        int rows = airConditionerScheduleMapper.deleteAirConditionerScheduleByIds(ids);
+        if (rows > 0) {
+            // 閲嶆柊璋冨害浠诲姟锛屼娇鍒犻櫎鎿嶄綔鐢熸晥
+            rescheduleTask();
+        }
+        return rows;
+    }
+
+    /**
+     * 鍒犻櫎绌鸿皟瀹氭椂浠诲姟淇℃伅
+     * 
+     * @param id 绌鸿皟瀹氭椂浠诲姟ID
+     * @return 缁撴灉
+     */
+    @Override
+    public int deleteAirConditionerScheduleById(Long id)
+    {
+        int rows = airConditionerScheduleMapper.deleteAirConditionerScheduleById(id);
+        if (rows > 0) {
+            // 閲嶆柊璋冨害浠诲姟锛屼娇鍒犻櫎鎿嶄綔鐢熸晥
+            rescheduleTask();
+        }
+        return rows;
+    }
+    
+    /**
+     * 鏍规嵁绌鸿皟ID鍒犻櫎瀹氭椂浠诲姟
+     * 
+     * @param airConditionerId 绌鸿皟ID
+     * @return 缁撴灉
+     */
+    @Override
+    public int deleteAirConditionerScheduleByAirConditionerId(String airConditionerId)
+    {
+        int rows = airConditionerScheduleMapper.deleteAirConditionerScheduleByAirConditionerId(airConditionerId);
+        if (rows > 0) {
+            // 閲嶆柊璋冨害浠诲姟锛屼娇鍒犻櫎鎿嶄綔鐢熸晥
+            rescheduleTask();
+        }
+        return rows;
+    }
+    
+    /**
+     * 鎵ц绌鸿皟瀹氭椂浠诲姟
+     * 姝ゆ柟娉曚繚鐣欑敤浜庡吋瀹规帴鍙o紝鍦ㄦ柊鐨勫疄鐜颁腑涓嶅啀浣跨敤
+     * 鏂扮殑瀹炵幇涓烘瘡涓叿浣撶殑鏃堕棿鐐瑰垱寤虹嫭绔嬬殑璋冨害浠诲姟锛屼笉鍐嶉渶瑕佹瘡鍒嗛挓妫�鏌ヤ竴娆�
+     */
+    @Override
+    public void executeScheduleTasks()
+    {
+        log.info("executeScheduleTasks鏂规硶宸茶璋冪敤锛屼絾鍦ㄦ柊鐨勫疄鐜颁腑涓嶅啀浣跨敤");
+        log.info("褰撳墠浣跨敤鍩轰簬鏁版嵁搴撶殑绮剧‘瀹氭椂浠诲姟璋冨害鏈哄埗锛屼负姣忎釜鏃堕棿鐐瑰垱寤虹嫭绔嬬殑璋冨害浠诲姟");
+        
+        // 濡傛灉鏈夊繀瑕侊紝鍙互鍦ㄨ繖閲岄噸鏂拌皟搴︽墍鏈変换鍔�
+        // rescheduleTask();
+    }
+    
+    /**
+     * 閲嶆柊璋冨害浠诲姟
+     * 褰撳畾鏃朵换鍔″彂鐢熷彉鍖栨椂锛堝娣诲姞銆佷慨鏀广�佸垹闄ゅ畾鏃朵换鍔★級璋冪敤姝ゆ柟娉�
+     * 鍙栨秷鎵�鏈夊綋鍓嶇殑璋冨害浠诲姟骞堕噸鏂板垱寤�
+     */
+    public void rescheduleTask() {
+        // 鍙栨秷鎵�鏈夌幇鏈夌殑璋冨害浠诲姟
+        for (Map.Entry<String, ScheduledFuture<?>> entry : scheduledTasks.entrySet()) {
+            entry.getValue().cancel(false);
+        }
+        scheduledTasks.clear();
+        
+        // 鏌ヨ鎵�鏈夋湁鏁堢殑瀹氭椂浠诲姟骞堕噸鏂拌皟搴�
+        AirConditionerSchedule query = new AirConditionerSchedule();
+        query.setStatus("0"); // 鐘舵�佹甯哥殑浠诲姟
+        List<AirConditionerSchedule> schedules = airConditionerScheduleMapper.selectAirConditionerScheduleList(query);
+        
+        if (schedules.isEmpty()) {
+            log.info("娌℃湁鎵惧埌闇�瑕佽皟搴︾殑绌鸿皟瀹氭椂浠诲姟");
+            return;
+        }
+        
+        log.info("閲嶆柊璋冨害{}涓┖璋冨畾鏃朵换鍔�", schedules.size());
+        
+        // 涓烘瘡涓换鍔″垱寤鸿皟搴�
+        for (AirConditionerSchedule schedule : schedules) {
+            scheduleTask(schedule);
+        }
+        
+        log.info("绌鸿皟瀹氭椂浠诲姟宸查噸鏂拌皟搴﹀畬鎴�");
+    }
+    
+    /**
+     * 涓哄崟涓畾鏃朵换鍔″垱寤鸿皟搴�
+     * 鍒嗗埆涓哄紑鏈烘椂闂村拰鍏虫満鏃堕棿鍒涘缓鐙珛鐨勮皟搴︿换鍔�
+     * 寮�鏈烘寚浠ゅ彧鍦ㄦ硶瀹氬伐浣滄棩娣诲姞瀹氭椂浠诲姟锛屽叧鏈烘寚浠ゆ瘡澶╅兘娣诲姞瀹氭椂浠诲姟
+     * 
+     * @param schedule 瀹氭椂浠诲姟淇℃伅
+     */
+    private void scheduleTask(AirConditionerSchedule schedule) {
+        if (schedule == null || schedule.getId() == null) {
+            log.error("鏃犳硶璋冨害绌烘垨鏃營D鐨勫畾鏃朵换鍔�");
+            return;
+        }
+        
+        try {
+            // 涓哄紑鏈烘椂闂村垱寤鸿皟搴�
+            if (schedule.getStartTime() != null) {
+                // 寮�鏈烘寚浠ゅ彧鍦ㄦ硶瀹氬伐浣滄棩娣诲姞瀹氭椂浠诲姟
+                scheduleSpecificTask(schedule, schedule.getStartTime(), true);
+            }
+            
+            // 涓哄叧鏈烘椂闂村垱寤鸿皟搴�
+            if (schedule.getOffTime() != null) {
+                // 鍏虫満鎸囦护姣忓ぉ閮芥坊鍔犲畾鏃朵换鍔�
+                scheduleSpecificTask(schedule, schedule.getOffTime(), false);
+            }
+        } catch (Exception e) {
+            log.error("涓哄畾鏃朵换鍔″垱寤鸿皟搴﹀け璐ワ紝浠诲姟ID锛歿}锛岄敊璇俊鎭細{}", schedule.getId(), e.getMessage());
+        }
+    }
+    
+    /**
+     * 涓虹壒瀹氭椂闂寸偣鍒涘缓璋冨害浠诲姟
+     * 
+     * @param schedule 瀹氭椂浠诲姟淇℃伅
+     * @param targetTime 鐩爣鏃堕棿鐐�
+     * @param isStartTime 鏄惁涓哄紑鏈烘椂闂达紙true涓哄紑鏈猴紝false涓哄叧鏈猴級
+     */
+    private void scheduleSpecificTask(AirConditionerSchedule schedule, Date targetTime, boolean isStartTime) {
+        // 璁$畻涓嬩竴娆℃墽琛屾椂闂�
+        Date nextExecutionTime = calculateNextExecutionTime(targetTime);
+        
+        if (nextExecutionTime == null) {
+            log.error("璁$畻涓嬩竴娆℃墽琛屾椂闂村け璐ワ紝浠诲姟ID锛歿}", schedule.getId());
+            return;
+        }
+        
+        // 濡傛灉鏄紑鏈轰换鍔★紝妫�鏌ユ墽琛屾棩鏈熸槸鍚︿负宸ヤ綔鏃�
+        if (isStartTime) {
+            // 濡傛灉涓嶆槸宸ヤ綔鏃ワ紝鍒欒绠椾笅涓�涓伐浣滄棩
+            while (!isWorkDay(nextExecutionTime)) {
+                // 涓嶆槸宸ヤ綔鏃ワ紝璺冲埌涓嬩竴澶╁啀妫�鏌�
+                nextExecutionTime = DateUtils.addDays(nextExecutionTime, 1);
+                
+                // 鏇存柊鏃堕棿閮ㄥ垎锛堜繚鎸佸師鏉ョ殑鏃跺垎绉掍笉鍙橈級
+                Calendar cal = Calendar.getInstance();
+                cal.setTime(nextExecutionTime);
+                
+                Calendar targetCal = Calendar.getInstance();
+                targetCal.setTime(targetTime);
+                
+                cal.set(Calendar.HOUR_OF_DAY, targetCal.get(Calendar.HOUR_OF_DAY));
+                cal.set(Calendar.MINUTE, targetCal.get(Calendar.MINUTE));
+                cal.set(Calendar.SECOND, targetCal.get(Calendar.SECOND));
+                
+                nextExecutionTime = cal.getTime();
+            }
+            
+            log.info("寮�鏈轰换鍔″凡璋冩暣涓轰笅涓�涓伐浣滄棩鎵ц锛屾墽琛屾椂闂达細{}", 
+                    new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(nextExecutionTime));
+        }
+        
+        // 鍒涘缓浠诲姟ID锛屾牸寮忎负锛氫换鍔D + "_start"鎴�"_off"
+        String taskKey = schedule.getId() + (isStartTime ? "_start" : "_off");
+        
+        // 鍙栨秷宸插瓨鍦ㄧ殑鐩稿悓浠诲姟锛堝鏋滄湁锛�
+        ScheduledFuture<?> existingTask = scheduledTasks.get(taskKey);
+        if (existingTask != null) {
+            existingTask.cancel(false);
+            scheduledTasks.remove(taskKey);
+        }
+        
+        // 鍒涘缓鏂扮殑璋冨害浠诲姟
+        ScheduledFuture<?> future = taskScheduler.schedule(
+            () -> executeSpecificTask(schedule, isStartTime),
+            nextExecutionTime
+        );
+        
+        // 淇濆瓨璋冨害浠诲姟
+        scheduledTasks.put(taskKey, future);
+        
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+        log.info("宸蹭负绌鸿皟ID锛歿}鍒涘缓{}瀹氭椂浠诲姟锛屾墽琛屾椂闂达細{}", 
+                schedule.getAirConditionerId(), 
+                isStartTime ? "寮�鏈�" : "鍏虫満", 
+                sdf.format(nextExecutionTime));
+    }
+    
+    /**
+     * 璁$畻涓嬩竴娆℃墽琛屾椂闂�
+     * 濡傛灉浠婂ぉ鐨勬墽琛屾椂闂村凡杩囷紝鍒欏畨鎺掑湪鏄庡ぉ鐨勫悓涓�鏃堕棿鎵ц
+     * 
+     * @param targetTime 鐩爣鏃堕棿
+     * @return 涓嬩竴娆℃墽琛屾椂闂�
+     */
+    private Date calculateNextExecutionTime(Date targetTime) {
+        if (targetTime == null) {
+            return null;
+        }
+        
+        // 灏咲ate杞崲涓篖ocalTime锛堝彧淇濈暀鏃跺垎绉掞級
+        LocalTime targetLocalTime = targetTime.toInstant()
+                .atZone(ZoneId.systemDefault())
+                .toLocalTime();
+        
+        // 鑾峰彇褰撳墠鏃ユ湡鍜屾椂闂�
+        LocalDateTime now = LocalDateTime.now();
+        LocalDate today = now.toLocalDate();
+        
+        // 鍒涘缓浠婂ぉ鐨勭洰鏍囨墽琛屾椂闂�
+        LocalDateTime targetDateTime = LocalDateTime.of(today, targetLocalTime);
+        
+        // 濡傛灉鐩爣鏃堕棿宸茶繃锛屽畨鎺掑湪鏄庡ぉ鐨勫悓涓�鏃堕棿鎵ц
+        if (targetDateTime.isBefore(now)) {
+            targetDateTime = targetDateTime.plusDays(1);
+        }
+        
+        // 杞崲鍥濪ate绫诲瀷
+        return Date.from(targetDateTime.atZone(ZoneId.systemDefault()).toInstant());
+    }
+    
+    /**
+     * 鎵ц鐗瑰畾鐨勫畾鏃朵换鍔�
+     * 
+     * @param schedule 瀹氭椂浠诲姟淇℃伅
+     * @param isStartTime 鏄惁涓哄紑鏈烘椂闂达紙true涓哄紑鏈猴紝false涓哄叧鏈猴級
+     */
+    private void executeSpecificTask(AirConditionerSchedule schedule, boolean isStartTime) {
+        try {
+            if (isStartTime) {
+                // 寮�鏈烘搷浣� - 鏍规嵁瀛h妭鑷姩鍒ゆ柇鍒剁儹鎴栧埗鍐锋ā寮�
+                String controlMode = determineControlModeByMonth();
+                
+                log.info("鎵ц绌鸿皟寮�鏈轰换鍔★紝绌鸿皟ID锛歿}锛屾帶鍒舵ā寮忥細{}", schedule.getAirConditionerId(), controlMode);
+                
+                // 鎵ц绌鸿皟寮�鏈烘帶鍒�
+                airConditionerService.controlAirConditioner(
+                    schedule.getAirConditionerId(), 
+                    controlMode, // 0鍒跺喎鎴�1鍒剁儹
+                    "1" // 鑷姩鎿嶄綔
+                );
+            } else {
+                // 鍏虫満鎿嶄綔
+                log.info("鎵ц绌鸿皟鍏虫満浠诲姟锛岀┖璋僆D锛歿}", schedule.getAirConditionerId());
+                
+                // 鎵ц绌鸿皟鍏虫満鎺у埗
+                airConditionerService.controlAirConditioner(
+                    schedule.getAirConditionerId(), 
+                    "2", // 鍏虫満
+                    "1" // 鑷姩鎿嶄綔
+                );
+            }
+            
+            // 浠诲姟鎵ц瀹屾垚鍚庯紝閲嶆柊璋冨害涓嬩竴澶╃殑鍚屼竴鏃堕棿鐐�
+            // 鑾峰彇鍘熷鐨勬椂闂寸偣
+            Date originalTime = isStartTime ? schedule.getStartTime() : schedule.getOffTime();
+            // 閲嶆柊璋冨害
+            scheduleSpecificTask(schedule, originalTime, isStartTime);
+            
+        } catch (Exception e) {
+            log.error("鎵ц绌鸿皟{}浠诲姟澶辫触锛岀┖璋僆D锛歿}锛岄敊璇俊鎭細{}", 
+                    isStartTime ? "寮�鏈�" : "鍏虫満", 
+                    schedule.getAirConditionerId(), 
+                    e.getMessage());
+        }
+    }
+    
+    /**
+     * 鍒ゆ柇鏃ユ湡鏄惁涓哄伐浣滄棩
+     * 宸ヤ綔鏃ュ垽鏂�昏緫锛�
+     * 1. 濡傛灉鏄懆涓�鑷冲懆浜旓紝涓斾笉鏄硶瀹氳妭鍋囨棩锛屽垯涓哄伐浣滄棩
+     * 2. 濡傛灉鏄懆鍏棩锛屼絾鏄负璋冧紤宸ヤ綔鏃ワ紝鍒欎负宸ヤ綔鏃�
+     * 
+     * @param date 闇�瑕佸垽鏂殑鏃ユ湡
+     * @return 濡傛灉鏄伐浣滄棩杩斿洖true锛屽惁鍒欒繑鍥瀎alse
+     */
+    private boolean isWorkDay(Date date) {
+        // 璋冪敤鑺傚亣鏃ユ湇鍔¤繘琛岀簿纭殑宸ヤ綔鏃ュ垽鏂�
+        return sysHolidayService.isWorkDay(date);
+    }
+    
+    /**
+     * 鏍规嵁鏈堜唤鑷姩鍒ゆ柇鍒剁儹鎴栧埗鍐锋ā寮�
+     * 5-9鏈堜负澶忓锛屼娇鐢ㄥ埗鍐锋ā寮�
+     * 11-3鏈堜负鍐锛屼娇鐢ㄥ埗鐑ā寮�
+     * 4鏈堝拰10鏈堜负杩囨浮瀛h妭锛屾牴鎹綋鍓嶆俯搴﹀垽鏂�
+     * 
+     * @return 鎺у埗妯″紡锛�0鍒跺喎 1鍒剁儹锛�
+     */
+    private String determineControlModeByMonth()
+    {
+        // 鑾峰彇褰撳墠鏈堜唤
+        int currentMonth = DateTimeUtil.getMonthOfYear(new Date());
+        
+        // 鏍规嵁鏈堜唤鍒ゆ柇瀛h妭
+        if (currentMonth >= 5 && currentMonth <= 9)
+        {
+            // 澶忓浣跨敤鍒跺喎妯″紡
+            return "0";
+        }
+        else if ((currentMonth >= 11 && currentMonth <= 12) || (currentMonth >= 1 && currentMonth <= 3))
+        {
+            // 鍐浣跨敤鍒剁儹妯″紡
+            return "1";
+        }
+        else
+        {
+            // 杩囨浮瀛h妭锛岄粯璁や娇鐢ㄥ埗鍐锋ā寮�
+            // 瀹為檯搴旂敤涓彲浠ユ牴鎹綋鍓嶆俯搴﹀垽鏂�
+            return "0";
+        }
+    }
+    
+    /**
+     * 娉ㄦ剰锛氬浜庡彧瀛樺偍鏃跺垎绉掔殑鏃堕棿瀛楁锛堝寮�濮嬫椂闂村拰缁撴潫鏃堕棿锛夛紝
+     * 鏁版嵁搴撳簲璇ヤ娇鐢═IME绫诲瀷鑰岄潪DATETIME鎴朤IMESTAMP绫诲瀷銆�
+     * 
+     * MySQL涓殑TIME绫诲瀷鏍煎紡涓�'HH:MM:SS'锛屽彧瀛樺偍鏃堕棿閮ㄥ垎锛屼笉鍖呭惈鏃ユ湡淇℃伅銆�
+     * 杩欐牱鍙互鍑忓皯瀛樺偍绌洪棿锛屽苟涓旀洿绗﹀悎涓氬姟闇�姹傘��
+     * 
+     * 鍦↗ava瀹炰綋绫讳腑锛岃櫧鐒朵娇鐢―ate绫诲瀷琛ㄧず锛屼絾鍦ㄤ笌鏁版嵁搴撲氦浜掓椂锛�
+     * 鍙湁鏃跺垎绉掗儴鍒嗕細琚繚瀛樺拰璇诲彇銆�
+     */
+}
\ No newline at end of file
diff --git a/zhitan-airconditioner/src/main/java/com/zhitan/airconditioner/service/impl/AirConditionerServiceImpl.java b/zhitan-airconditioner/src/main/java/com/zhitan/airconditioner/service/impl/AirConditionerServiceImpl.java
new file mode 100644
index 0000000..9852b18
--- /dev/null
+++ b/zhitan-airconditioner/src/main/java/com/zhitan/airconditioner/service/impl/AirConditionerServiceImpl.java
@@ -0,0 +1,216 @@
+package com.zhitan.airconditioner.service.impl;
+
+import java.util.Date;
+import java.util.List;
+
+import com.alibaba.fastjson2.JSONObject;
+import com.zhitan.airconditioner.domain.AirConditioner;
+import com.zhitan.airconditioner.domain.AirConditionerLog;
+import com.zhitan.airconditioner.mapper.AirConditionerLogMapper;
+import com.zhitan.airconditioner.mapper.AirConditionerMapper;
+import com.zhitan.airconditioner.service.IAirConditionerService;
+import com.zhitan.common.utils.DateUtils;
+import com.zhitan.common.utils.SecurityUtils;
+import com.zhitan.framework.mqtt.MqttClientUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * 绌鸿皟鎺у埗鍣⊿ervice涓氬姟灞傚鐞�
+ * 
+ * @author zhitan
+ */
+@Service
+public class AirConditionerServiceImpl implements IAirConditionerService
+{
+    @Autowired
+    private AirConditionerMapper airConditionerMapper;
+    
+    @Autowired
+    private AirConditionerLogMapper airConditionerLogMapper;
+    
+    @Autowired
+    private MqttClientUtil mqttClientUtil;
+
+    /**
+     * 鏌ヨ绌鸿皟鎺у埗鍣ㄥ垪琛�
+     * 
+     * @param airConditioner 绌鸿皟鎺у埗鍣ㄤ俊鎭�
+     * @return 绌鸿皟鎺у埗鍣ㄩ泦鍚�
+     */
+    @Override
+    public List<AirConditioner> selectAirConditionerList(AirConditioner airConditioner)
+    {
+        return airConditionerMapper.selectAirConditionerList(airConditioner);
+    }
+
+    /**
+     * 鏌ヨ绌鸿皟鎺у埗鍣ㄤ俊鎭�
+     * 
+     * @param id 绌鸿皟鎺у埗鍣↖D
+     * @return 绌鸿皟鎺у埗鍣ㄤ俊鎭�
+     */
+    @Override
+    public AirConditioner selectAirConditionerById(Long id)
+    {
+        return airConditionerMapper.selectAirConditionerById(id);
+    }
+    
+    /**
+     * 鏍规嵁鎺у埗鍣↖D鏌ヨ绌鸿皟鎺у埗鍣ㄤ俊鎭�
+     * 
+     * @param controllerId 鎺у埗鍣↖D
+     * @return 绌鸿皟鎺у埗鍣ㄤ俊鎭�
+     */
+    @Override
+    public AirConditioner selectAirConditionerByControllerId(String controllerId)
+    {
+        return airConditionerMapper.selectAirConditionerByControllerId(controllerId);
+    }
+
+    /**
+     * 鏂板绌鸿皟鎺у埗鍣�
+     * 
+     * @param airConditioner 绌鸿皟鎺у埗鍣ㄤ俊鎭�
+     * @return 缁撴灉
+     */
+    @Override
+    public int insertAirConditioner(AirConditioner airConditioner)
+    {
+        airConditioner.setCreateTime(DateUtils.getNowDate());
+        airConditioner.setCreateBy(SecurityUtils.getUsername());
+        return airConditionerMapper.insertAirConditioner(airConditioner);
+    }
+
+    /**
+     * 淇敼绌鸿皟鎺у埗鍣�
+     * 
+     * @param airConditioner 绌鸿皟鎺у埗鍣ㄤ俊鎭�
+     * @return 缁撴灉
+     */
+    @Override
+    public int updateAirConditioner(AirConditioner airConditioner)
+    {
+        airConditioner.setUpdateTime(DateUtils.getNowDate());
+        airConditioner.setUpdateBy(SecurityUtils.getUsername());
+        return airConditionerMapper.updateAirConditioner(airConditioner);
+    }
+
+    /**
+     * 鎵归噺鍒犻櫎绌鸿皟鎺у埗鍣�
+     * 
+     * @param ids 闇�瑕佸垹闄ょ殑绌鸿皟鎺у埗鍣↖D
+     * @return 缁撴灉
+     */
+    @Override
+    public int deleteAirConditionerByIds(Long[] ids)
+    {
+        return airConditionerMapper.deleteAirConditionerByIds(ids);
+    }
+
+    /**
+     * 鍒犻櫎绌鸿皟鎺у埗鍣ㄤ俊鎭�
+     * 
+     * @param id 绌鸿皟鎺у埗鍣↖D
+     * @return 缁撴灉
+     */
+    @Override
+    public int deleteAirConditionerById(Long id)
+    {
+        return airConditionerMapper.deleteAirConditionerById(id);
+    }
+    
+    /**
+     * 鎺у埗绌鸿皟寮�鍏虫満
+     * 
+     * @param id 绌鸿皟ID
+     * @param mode 鎺у埗妯″紡锛�0鍒跺喎 1鍒剁儹 2鍏虫満锛�
+     * @param operateType 鎿嶄綔绫诲瀷锛�0鎵嬪姩 1鑷姩锛�
+     * @return 缁撴灉
+     */
+    @Override
+    public boolean controlAirConditioner(Long id, String mode, String operateType)
+    {
+        try {
+            // 鏌ヨ绌鸿皟淇℃伅
+            AirConditioner airConditioner = airConditionerMapper.selectAirConditionerById(id);
+            if (airConditioner == null) {
+                return false;
+            }
+
+            String msg = "{\n" +
+                    "  \"rw_prot\": {\n" +
+                    "    \"Ver\": \"1.0.1\",\n" +
+                    "    \"dir\": \"down\",\n" +
+                    "    \"id\": \"00601925021000024737\",\n" +
+                    "    \"r_data\": [\n" +
+                    "      {\"name\":\"hum\"},\n" +
+                    "      {\"name\":\"tmp\"},\n" +
+                    "      {\n" +
+                    "        \"name\": \"cold\"\n" +
+                    "      },\n" +
+                    "      {\n" +
+                    "        \"name\": \"hot\"\n" +
+                    "      },\n" +
+                    "      {\n" +
+                    "        \"name\": \"off\"\n" +
+                    "      }\n" +
+                    "    ],\n" +
+                    "    \"w_data\": [\n" +
+                    "      {\n" +
+                    "        \"name\": \"cold\",\n" +
+                    "        \"value\": \"0\"\n" +
+                    "      },\n" +
+                    "      {\n" +
+                    "        \"name\": \"hot\",\n" +
+                    "        \"value\": \"0\"\n" +
+                    "      },\n" +
+                    "      {\n" +
+                    "        \"name\": \"off\",\n" +
+                    "        \"value\": \"0\"\n" +
+                    "      }\n" +
+                    "    ]\n" +
+                    "  }\n" +
+                    "}";
+            // 灏嗗瓧绗︿覆杞琷son
+            JSONObject jsonObject = JSONObject.parseObject(msg);
+            JSONObject rwProt = jsonObject.getJSONObject("rw_prot");
+            rwProt.put("id", airConditioner.getControllerId());
+            if ("0".equals(mode)) {
+                rwProt.getJSONArray("w_data").getJSONObject(0).put("value", "1");
+            } else if ("1".equals(mode)) {
+                rwProt.getJSONArray("w_data").getJSONObject(1).put("value", "1");
+            } else if ("2".equals(mode)) {
+                rwProt.getJSONArray("w_data").getJSONObject(2).put("value", "1");
+            }
+            // 鏋勫缓MQTT娑堟伅
+            String topic = "lanbao/nygl/sevice/kt1/down";
+            String message = jsonObject.toJSONString();
+            
+            // 鍙戦�丮QTT娑堟伅
+            mqttClientUtil.sendMessage(topic, message, 2);
+            
+            // 璁板綍鎿嶄綔鏃ュ織
+            AirConditionerLog log = new AirConditionerLog();
+            log.setAirConditionerId(id);
+            log.setAirConditionerName(airConditioner.getName());
+            log.setOperateType(operateType);
+            log.setOperateMode(mode);
+            log.setOperateTime(new Date());
+            log.setOperateResult("1"); // 鎴愬姛
+            log.setOperateMsg("鎿嶄綔鎴愬姛");
+            log.setCreateTime(DateUtils.getNowDate());
+            if ("1".equals(operateType)) {
+                log.setCreateBy("绯荤粺");
+            } else {
+                log.setCreateBy(SecurityUtils.getUsername());
+            }
+            airConditionerLogMapper.insertAirConditionerLog(log);
+            
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+}
\ No newline at end of file
diff --git a/zhitan-quartz/pom.xml b/zhitan-quartz/pom.xml
index 703bea4..89bd872 100644
--- a/zhitan-quartz/pom.xml
+++ b/zhitan-quartz/pom.xml
@@ -34,6 +34,12 @@
             <groupId>com.zhitan</groupId>
             <artifactId>zhitan-common</artifactId>
         </dependency>
+        <dependency>
+            <groupId>com.zhitan</groupId>
+            <artifactId>zhitan-airconditioner</artifactId>
+            <version>2.5.2</version>
+            <scope>compile</scope>
+        </dependency>
 
     </dependencies>
 
diff --git a/zhitan-quartz/src/main/java/com/zhitan/quartz/task/AirConditionerTask.java b/zhitan-quartz/src/main/java/com/zhitan/quartz/task/AirConditionerTask.java
new file mode 100644
index 0000000..307c428
--- /dev/null
+++ b/zhitan-quartz/src/main/java/com/zhitan/quartz/task/AirConditionerTask.java
@@ -0,0 +1,25 @@
+package com.zhitan.quartz.task;
+
+import com.zhitan.airconditioner.service.IAirConditionerScheduleService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+/**
+ * 绌鸿皟瀹氭椂浠诲姟
+ *
+ * @author zhitan
+ */
+@Component("airConditionerTask")
+public class AirConditionerTask
+{
+    @Autowired
+    private IAirConditionerScheduleService airConditionerScheduleService;
+
+    /**
+     * 鎵ц绌鸿皟瀹氭椂浠诲姟
+     */
+    public void executeScheduleTasks()
+    {
+        airConditionerScheduleService.executeScheduleTasks();
+    }
+}
\ No newline at end of file
diff --git a/zhitan-vue/src/api/airconditioner/airconditioner.js b/zhitan-vue/src/api/airconditioner/airconditioner.js
new file mode 100644
index 0000000..49ff29b
--- /dev/null
+++ b/zhitan-vue/src/api/airconditioner/airconditioner.js
@@ -0,0 +1,57 @@
+import request from '@/utils/request'
+
+// 鏌ヨ绌鸿皟鍒楄〃
+export function listAirConditioner(query) {
+  return request({
+    url: '/system/airconditioner/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 鏌ヨ绌鸿皟璇︾粏
+export function getAirConditioner(id) {
+  return request({
+    url: '/system/airconditioner/' + id,
+    method: 'get'
+  })
+}
+
+// 鏂板绌鸿皟
+export function addAirConditioner(data) {
+  return request({
+    url: '/system/airconditioner',
+    method: 'post',
+    data: data
+  })
+}
+
+// 淇敼绌鸿皟
+export function updateAirConditioner(data) {
+  return request({
+    url: '/system/airconditioner',
+    method: 'put',
+    data: data
+  })
+}
+
+// 鍒犻櫎绌鸿皟
+export function delAirConditioner(id) {
+  return request({
+    url: '/system/airconditioner/' + id,
+    method: 'delete'
+  })
+}
+
+// 鎺у埗绌鸿皟寮�鍏虫満
+export function controlAirConditioner(id, mode, operateType) {
+  return request({
+    url: '/system/airconditioner/control',
+    method: 'post',
+    data: {
+      id: id,
+      mode: mode, // 0鍒跺喎 1鍒剁儹 2鍏虫満
+      operateType: operateType // 0鎵嬪姩 1鑷姩
+    }
+  })
+}
\ No newline at end of file
diff --git a/zhitan-vue/src/api/airconditioner/log.js b/zhitan-vue/src/api/airconditioner/log.js
new file mode 100644
index 0000000..2728526
--- /dev/null
+++ b/zhitan-vue/src/api/airconditioner/log.js
@@ -0,0 +1,26 @@
+import request from '@/utils/request'
+
+// 鏌ヨ绌鸿皟鎿嶄綔鏃ュ織鍒楄〃
+export function listLog(query) {
+  return request({
+    url: '/system/airconditioner/log/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 鏌ヨ绌鸿皟鎿嶄綔鏃ュ織璇︾粏
+export function getLog(id) {
+  return request({
+    url: '/system/airconditioner/log/' + id,
+    method: 'get'
+  })
+}
+
+// 鏍规嵁绌鸿皟ID鏌ヨ鎿嶄綔鏃ュ織鍒楄〃
+export function getLogByAirConditionerId(airConditionerId) {
+  return request({
+    url: '/system/airconditioner/log/byAirConditioner/' + airConditionerId,
+    method: 'get'
+  })
+}
\ No newline at end of file
diff --git a/zhitan-vue/src/api/airconditioner/schedule.js b/zhitan-vue/src/api/airconditioner/schedule.js
new file mode 100644
index 0000000..4a9b050
--- /dev/null
+++ b/zhitan-vue/src/api/airconditioner/schedule.js
@@ -0,0 +1,52 @@
+import request from '@/utils/request'
+
+// 鏌ヨ绌鸿皟瀹氭椂浠诲姟鍒楄〃
+export function listSchedule(query) {
+  return request({
+    url: '/system/airconditioner/schedule/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 鏌ヨ绌鸿皟瀹氭椂浠诲姟璇︾粏
+export function getSchedule(id) {
+  return request({
+    url: '/system/airconditioner/schedule/' + id,
+    method: 'get'
+  })
+}
+
+// 鏍规嵁绌鸿皟ID鏌ヨ瀹氭椂浠诲姟鍒楄〃
+export function getScheduleByAirConditionerId(airConditionerId) {
+  return request({
+    url: '/system/airconditioner/schedule/byAirConditioner/' + airConditionerId,
+    method: 'get'
+  })
+}
+
+// 鏂板绌鸿皟瀹氭椂浠诲姟
+export function addSchedule(data) {
+  return request({
+    url: '/system/airconditioner/schedule',
+    method: 'post',
+    data: data
+  })
+}
+
+// 淇敼绌鸿皟瀹氭椂浠诲姟
+export function updateSchedule(data) {
+  return request({
+    url: '/system/airconditioner/schedule',
+    method: 'put',
+    data: data
+  })
+}
+
+// 鍒犻櫎绌鸿皟瀹氭椂浠诲姟
+export function delSchedule(id) {
+  return request({
+    url: '/system/airconditioner/schedule/' + id,
+    method: 'delete'
+  })
+}
\ No newline at end of file
diff --git a/zhitan-vue/src/views/airconditioner/index.vue b/zhitan-vue/src/views/airconditioner/index.vue
new file mode 100644
index 0000000..fe9d91f
--- /dev/null
+++ b/zhitan-vue/src/views/airconditioner/index.vue
@@ -0,0 +1,76 @@
+<template>
+  <div class="app-container">
+    <el-card class="box-card">
+      <template #header>
+        <div class="card-header">
+          <span>绌鸿皟鎺у埗绯荤粺</span>
+        </div>
+      </template>
+      <el-tabs v-model="activeTab">
+        <el-tab-pane label="绌鸿皟鍒楄〃" name="list">
+          <air-conditioner-list ref="airConditionerList" />
+        </el-tab-pane>
+        <el-tab-pane label="瀹氭椂浠诲姟绠$悊" name="schedule">
+          <air-conditioner-schedule ref="airConditionerSchedule" />
+        </el-tab-pane>
+        <el-tab-pane label="鎿嶄綔鏃ュ織" name="log">
+          <air-conditioner-log ref="airConditionerLog" />
+        </el-tab-pane>
+      </el-tabs>
+    </el-card>
+    
+    
+  </div>
+</template>
+
+<script setup name="AirConditioner">
+import { ref, onMounted, nextTick } from 'vue'
+import AirConditionerList from './list.vue'
+import AirConditionerSchedule from './schedule.vue'
+import AirConditionerLog from './log.vue'
+
+const activeTab = ref('list')
+const airConditionerList = ref(null)
+const airConditionerSchedule = ref(null)
+const scheduleDialogRef = ref(null)
+
+// 瀹氭椂浠诲姟寮圭獥鏄剧ず鐘舵��
+const scheduleDialogVisible = ref(false)
+
+// 澶勭悊浠庣┖璋冨垪琛ㄥ垏鎹㈠埌瀹氭椂浠诲姟椤甸潰鐨勬柟娉曪紙閫氳繃Tab鍒囨崲锛�
+function handleTabSchedule(airConditionerId) {
+  activeTab.value = 'schedule'
+  // 绛夊緟DOM鏇存柊鍚庤皟鐢ㄥ畾鏃朵换鍔$粍浠剁殑鏂规硶
+  nextTick(() => {
+    if (airConditionerSchedule.value) {
+      airConditionerSchedule.value.loadScheduleByAirConditioner(airConditionerId)
+    }
+  })
+}
+
+// 澶勭悊鏄剧ず瀹氭椂浠诲姟寮圭獥鐨勬柟娉曪紙閫氳繃鎸夐挳鐐瑰嚮锛�
+function handleShowSchedule(airConditionerId) {
+  scheduleDialogVisible.value = true
+  // 绛夊緟DOM鏇存柊鍚庤皟鐢ㄥ畾鏃朵换鍔$粍浠剁殑鏂规硶
+  nextTick(() => {
+    if (scheduleDialogRef.value) {
+      scheduleDialogRef.value.loadScheduleByAirConditioner(airConditionerId)
+    }
+  })
+}
+
+// 鏆撮湶鏂规硶缁欏瓙缁勪欢璋冪敤
+defineExpose({
+  activeTab,
+  handleShowSchedule,
+  handleTabSchedule
+})
+</script>
+
+<style scoped>
+.card-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+</style>
\ No newline at end of file
diff --git a/zhitan-vue/src/views/airconditioner/list.vue b/zhitan-vue/src/views/airconditioner/list.vue
new file mode 100644
index 0000000..22a6b7a
--- /dev/null
+++ b/zhitan-vue/src/views/airconditioner/list.vue
@@ -0,0 +1,409 @@
+<template>
+  <div class="app-container">
+    <el-row :gutter="20">
+      <el-col :span="24">
+        <el-form :model="queryParams" ref="queryFormRef" :inline="true" v-show="showSearch" label-width="68px">
+          <el-form-item label="绌鸿皟鍚嶇О" prop="name">
+            <el-input
+              v-model="queryParams.name"
+              placeholder="璇疯緭鍏ョ┖璋冨悕绉�"
+              clearable
+              @keyup.enter="handleQuery"
+            />
+          </el-form-item>
+          <el-form-item label="瀹夎浣嶇疆" prop="location">
+            <el-input
+              v-model="queryParams.location"
+              placeholder="璇疯緭鍏ュ畨瑁呬綅缃�"
+              clearable
+              @keyup.enter="handleQuery"
+            />
+          </el-form-item>
+          <el-form-item label="鐘舵��" prop="status">
+            <el-select v-model="queryParams.status" placeholder="绌鸿皟鐘舵��" clearable>
+              <el-option
+                v-for="dict in statusOptions"
+                :key="dict.label"
+                :label="dict.label"
+                :value="dict.label"
+              />
+            </el-select>
+          </el-form-item>
+          <el-form-item>
+            <el-button type="primary" :icon="Search" @click="handleQuery">鎼滅储</el-button>
+            <el-button :icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
+          </el-form-item>
+        </el-form>
+
+        <el-row :gutter="10" class="mb8">
+          <el-col :span="1.5">
+            <el-button
+              type="primary"
+              plain
+              :icon="Plus"
+              @click="handleAdd"
+              v-hasPermi="['system:airconditioner:add']"
+            >鏂板</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button
+              type="success"
+              plain
+              :icon="Edit"
+              :disabled="single"
+              @click="handleUpdate"
+              v-hasPermi="['system:airconditioner:edit']"
+            >淇敼</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button
+              type="danger"
+              plain
+              :icon="Delete"
+              :disabled="multiple"
+              @click="handleDelete"
+              v-hasPermi="['system:airconditioner:remove']"
+            >鍒犻櫎</el-button>
+          </el-col>
+          <right-toolbar v-model:show-search="showSearch" @queryTable="getList"></right-toolbar>
+        </el-row>
+
+        <el-table v-loading="loading" :data="airConditionerList" @selection-change="handleSelectionChange">
+          <el-table-column type="selection" width="55" align="center" />
+          <el-table-column label="绌鸿皟ID" align="center" prop="id" />
+          <el-table-column label="绌鸿皟鍚嶇О" align="center" prop="name" />
+          <el-table-column label="鎺у埗鍣↖D" align="center" prop="controllerId" />
+          <el-table-column label="瀹夎浣嶇疆" align="center" prop="location" />
+          <el-table-column label="鐘舵��" align="center" prop="status">
+            <template #default="scope">
+              <dict-tag :options="statusOptions" :value="scope.row.status"/>
+            </template>
+          </el-table-column>
+          <el-table-column label="鎺у埗" align="center"  width="300">
+            <template #default="scope">
+              <el-button
+                type="primary"
+                size="small"
+               
+                @click="handleControl('0', scope.row)"
+              >鍒跺喎寮�鏈�</el-button>
+              <el-button
+                type="success"
+                size="small"
+                
+                @click="handleControl('1', scope.row)"
+              >鍒剁儹寮�鏈�</el-button>
+              <el-button
+                type="warning"
+                size="small"
+                
+                @click="handleControl('2', scope.row)"
+              >鍏虫満</el-button>
+            </template>
+          </el-table-column>
+          <el-table-column label="鎿嶄綔" align="center" class-name="small-padding fixed-width">
+            <template #default="scope">
+              <el-button
+                type="primary"
+                link
+                :icon="Edit"
+                @click="handleUpdate(scope.row)"
+                v-hasPermi="['system:airconditioner:edit']"
+              >淇敼</el-button>
+              <el-button
+                type="primary"
+                link
+                :icon="Delete"
+                @click="handleDelete(scope.row)"
+                v-hasPermi="['system:airconditioner:remove']"
+              >鍒犻櫎</el-button>
+              <el-button
+                type="primary"
+                link
+                :icon="Timer"
+                @click="handleShowSchedule(scope.row)"
+              >瀹氭椂</el-button>
+            </template>
+          </el-table-column>
+        </el-table>
+        
+        <pagination
+          v-show="total>0"
+          v-model:page="queryParams.pageNum"
+          v-model:limit="queryParams.pageSize"
+          :total="total"
+          @pagination="getList"
+        />
+      </el-col>
+    </el-row>
+
+    <!-- 娣诲姞鎴栦慨鏀圭┖璋冨璇濇 -->
+    <el-dialog :title="title" v-model="open" width="500px" append-to-body>
+      <el-form ref="formRef" :model="form" :rules="rules" label-width="80px">
+        <el-form-item label="绌鸿皟鍚嶇О" prop="name">
+          <el-input v-model="form.name" placeholder="璇疯緭鍏ョ┖璋冨悕绉�" />
+        </el-form-item>
+        <el-form-item label="鎺у埗鍣↖D" prop="controllerId">
+          <el-input v-model="form.controllerId" placeholder="璇疯緭鍏ユ帶鍒跺櫒ID" />
+        </el-form-item>
+        <el-form-item label="瀹夎浣嶇疆" prop="location">
+          <el-input v-model="form.location" placeholder="璇疯緭鍏ュ畨瑁呬綅缃�" />
+        </el-form-item>
+        <el-form-item label="鐘舵��">
+          <el-radio-group v-model="form.status">
+            <el-radio
+              v-for="dict in statusOptions"
+              :key="dict.value"
+              :label="dict.value"
+            >{{dict.label}}</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="澶囨敞" prop="remark">
+          <el-input v-model="form.remark" type="textarea" placeholder="璇疯緭鍏ュ唴瀹�" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button type="primary" @click="submitForm">纭� 瀹�</el-button>
+          <el-button @click="cancel">鍙� 娑�</el-button>
+        </div>
+      </template>
+    </el-dialog>
+    <!-- 绌鸿皟瀹氭椂浠诲姟寮圭獥 -->
+    <el-dialog
+      title="绌鸿皟瀹氭椂浠诲姟"
+      v-model="scheduleDialogVisible"
+      width="900px"
+      append-to-body
+
+    >
+      <air-conditioner-schedule ref="scheduleDialogRef" />
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="AirConditionerList">
+import { ref, reactive, onMounted, getCurrentInstance, nextTick } from 'vue'
+import { Search, Refresh, Plus, Edit, Delete, Setting, Timer, ArrowDown } from '@element-plus/icons-vue'
+import { ElMessage, ElMessageBox } from 'element-plus'
+import { listAirConditioner, getAirConditioner, addAirConditioner, updateAirConditioner, delAirConditioner, controlAirConditioner } from "@/api/airconditioner/airconditioner"
+import AirConditionerSchedule from './schedule.vue'
+// 鑾峰彇鐖剁粍浠跺疄渚�
+const { proxy } = getCurrentInstance()
+const scheduleDialogRef = ref(null)
+// 閬僵灞�
+const loading = ref(true)
+// 閫変腑鏁扮粍
+const ids = ref([])
+// 闈炲崟涓鐢�
+const single = ref(true)
+// 闈炲涓鐢�
+const multiple = ref(true)
+// 鏄剧ず鎼滅储鏉′欢
+const showSearch = ref(true)
+// 鎬绘潯鏁�
+const total = ref(0)
+// 绌鸿皟琛ㄦ牸鏁版嵁
+const airConditionerList = ref([])
+// 寮瑰嚭灞傛爣棰�
+const title = ref('')
+// 鏄惁鏄剧ず寮瑰嚭灞�
+const open = ref(false)
+// 琛ㄥ崟鍙傛暟
+const form = ref({})
+// 琛ㄥ崟鏍¢獙
+const rules = {
+  name: [
+    { required: true, message: "绌鸿皟鍚嶇О涓嶈兘涓虹┖", trigger: "blur" }
+  ],
+  controllerId: [
+    { required: true, message: "鎺у埗鍣↖D涓嶈兘涓虹┖", trigger: "blur" }
+  ],
+  location: [
+    { required: true, message: "瀹夎浣嶇疆涓嶈兘涓虹┖", trigger: "blur" }
+  ]
+}
+
+// 鐘舵�佸瓧鍏�
+const statusOptions = [
+  {
+    label: "鍒跺喎",
+    value: "0"
+  },
+  {
+    label: "鍒剁儹",
+    value: "1"
+  },
+  {
+    label: "鍏虫満",
+    value: "2"
+  }
+]
+
+// 鏌ヨ鍙傛暟
+const queryParams = ref({
+  pageNum: 1,
+  pageSize: 10,
+  name: null,
+  location: null,
+  status: null
+})
+
+// 琛ㄥ崟ref
+const formRef = ref()
+const queryFormRef = ref()
+
+/** 鏌ヨ绌鸿皟鍒楄〃 */
+function getList() {
+  loading.value = true
+  listAirConditioner(queryParams.value).then(response => {
+    airConditionerList.value = response.rows
+    total.value = response.total
+    loading.value = false
+  })
+}
+
+// 鍙栨秷鎸夐挳
+function cancel() {
+  open.value = false
+  reset()
+}
+
+// 琛ㄥ崟閲嶇疆
+function reset() {
+  form.value = {
+    id: null,
+    name: null,
+    controllerId: null,
+    location: null,
+    status: "2", // 榛樿涓哄叧鏈虹姸鎬�
+    remark: null
+  }
+  nextTick(() => {
+    if (formRef.value) {
+      formRef.value.resetFields()
+    }
+  })
+}
+
+/** 鎼滅储鎸夐挳鎿嶄綔 */
+function handleQuery() {
+  queryParams.value.pageNum = 1
+  getList()
+}
+
+/** 閲嶇疆鎸夐挳鎿嶄綔 */
+function resetQuery() {
+  queryFormRef.value.resetFields()
+  handleQuery()
+}
+
+// 澶氶�夋閫変腑鏁版嵁
+function handleSelectionChange(selection) {
+  ids.value = selection.map(item => item.id)
+  single.value = selection.length !== 1
+  multiple.value = !selection.length
+}
+
+/** 鏂板鎸夐挳鎿嶄綔 */
+function handleAdd() {
+  reset()
+  open.value = true
+  title.value = "娣诲姞绌鸿皟"
+}
+
+/** 淇敼鎸夐挳鎿嶄綔 */
+function handleUpdate(row) {
+  reset()
+  const id = row.id || ids.value[0]
+  getAirConditioner(id).then(response => {
+    form.value = response.data
+    open.value = true
+    title.value = "淇敼绌鸿皟"
+  })
+}
+
+/** 鎻愪氦鎸夐挳 */
+function submitForm() {
+  formRef.value.validate(valid => {
+    if (valid) {
+      if (form.value.id != null) {
+        updateAirConditioner(form.value).then(response => {
+          ElMessage.success("淇敼鎴愬姛")
+          open.value = false
+          getList()
+        })
+      } else {
+        addAirConditioner(form.value).then(response => {
+          ElMessage.success("鏂板鎴愬姛")
+          open.value = false
+          getList()
+        })
+      }
+    }
+  })
+}
+
+/** 鍒犻櫎鎸夐挳鎿嶄綔 */
+function handleDelete(row) {
+  const deleteIds = row.id || ids.value
+  ElMessageBox.confirm('鏄惁纭鍒犻櫎绌鸿皟缂栧彿涓�"' + deleteIds + '"鐨勬暟鎹」?')
+    .then(() => {
+      return delAirConditioner(deleteIds)
+    })
+    .then(() => {
+      getList()
+      ElMessage.success("鍒犻櫎鎴愬姛")
+    })
+    .catch(() => {})
+}
+
+/** 鎺у埗绌鸿皟鎿嶄綔 */
+function handleControl(command, row) {
+  const id = row.id
+  const modeName = command === '0' ? '鍒跺喎寮�鏈�' : command === '1' ? '鍒剁儹寮�鏈�' : '鍏虫満'
+  ElMessageBox.confirm('鏄惁纭瀵圭┖璋�"' + row.name + '"鎵ц' + modeName + '鎿嶄綔?')
+    .then(() => {
+      return controlAirConditioner(id, command, '0') // 0琛ㄧず鎵嬪姩鎿嶄綔
+    })
+    .then(() => {
+      // 鏇存柊鏈湴鐘舵�侊紝閬垮厤绛夊緟鍒锋柊
+      row.status = command
+      ElMessage.success(modeName + "鎿嶄綔鎴愬姛")
+      // 鍒锋柊鍒楄〃鏁版嵁
+      getList()
+    })
+    .catch(() => {})
+}
+
+/** 瀹氭椂浠诲姟鎿嶄綔 */
+function handleSchedule(row) {
+  // 璋冪敤鐖剁粍浠剁殑handleShowSchedule鏂规硶锛屼紶閫掔┖璋僆D
+  proxy.$parent.handleShowSchedule(row.id)
+}
+
+// 瀹氭椂浠诲姟寮圭獥鏄剧ず鐘舵��
+const scheduleDialogVisible = ref(false)
+
+
+
+// 澶勭悊鏄剧ず瀹氭椂浠诲姟寮圭獥鐨勬柟娉曪紙閫氳繃鎸夐挳鐐瑰嚮锛�
+function handleShowSchedule(airConditionerId) {
+  scheduleDialogVisible.value = true
+  // 绛夊緟DOM鏇存柊鍚庤皟鐢ㄥ畾鏃朵换鍔$粍浠剁殑鏂规硶
+  nextTick(() => {
+    if (scheduleDialogRef.value) {
+      scheduleDialogRef.value.loadScheduleByAirConditioner(airConditionerId)
+    }
+  })
+}
+
+onMounted(() => {
+  getList()
+})
+
+// 鏆撮湶鏂规硶缁欑埗缁勪欢璋冪敤
+defineExpose({
+  getList
+})
+</script>
\ No newline at end of file
diff --git a/zhitan-vue/src/views/airconditioner/log.vue b/zhitan-vue/src/views/airconditioner/log.vue
new file mode 100644
index 0000000..5ae9f71
--- /dev/null
+++ b/zhitan-vue/src/views/airconditioner/log.vue
@@ -0,0 +1,166 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryFormRef" :inline="true" v-show="showSearch" label-width="68px">
+      <el-form-item label="绌鸿皟ID" prop="airConditionerId">
+        <el-select v-model="queryParams.airConditionerId" placeholder="璇烽�夋嫨绌鸿皟" clearable>
+          <el-option
+            v-for="item in airConditionerOptions"
+            :key="item.id"
+            :label="item.name"
+            :value="item.id"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="鎿嶄綔绫诲瀷" prop="operationType">
+        <el-select v-model="queryParams.operationType" placeholder="璇烽�夋嫨鎿嶄綔绫诲瀷" clearable>
+          <el-option
+            v-for="dict in operationTypeOptions"
+            :key="dict.value"
+            :label="dict.label"
+            :value="dict.value"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="鏃堕棿鑼冨洿" prop="timeRange">
+        <el-date-picker
+          v-model="queryParams.timeRange"
+          type="datetimerange"
+          range-separator="鑷�"
+          start-placeholder="寮�濮嬫椂闂�"
+          end-placeholder="缁撴潫鏃堕棿"
+        />
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" :icon="Search" @click="handleQuery">鎼滅储</el-button>
+        <el-button :icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-table
+      v-loading="loading"
+      :data="logList"
+      style="width: 100%"
+    >
+      <el-table-column type="index" width="50" align="center" />
+      <el-table-column label="鎿嶄綔鏃堕棿" align="center" prop="operateTime" width="160">
+        <template #default="scope">
+          <span>{{ parseTime(scope.row.operateTime) }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="绌鸿皟淇℃伅" align="center" prop="airConditionerName" />
+      <el-table-column label="鎿嶄綔绫诲瀷" align="center" prop="operateType">
+        <template #default="scope">
+          <dict-tag :options="operationTypeOptions" :value="scope.row.operateType"/>
+        </template>
+      </el-table-column>
+      <el-table-column label="宸ヤ綔妯″紡" align="center" prop="operateMode" >
+        <template #default="scope">
+          <dict-tag :options="operationModeOptions" :value="scope.row.operateMode"/>
+        </template>
+      </el-table-column>
+      <el-table-column label="鎿嶄綔缁撴灉" align="center" prop="operateResult">
+        <template #default="scope">
+          <el-tag :type="scope.row.operateResult === '1' ? 'success' : 'danger'">
+            {{ scope.row.operateResult === '1' ? '鎴愬姛' : '澶辫触' }}
+          </el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="鎿嶄綔浜哄憳" align="center" prop="operatorName" />
+    </el-table>
+    
+    <pagination
+      v-show="total > 0"
+      :total="total"
+      v-model:page="queryParams.pageNum"
+      v-model:limit="queryParams.pageSize"
+      @pagination="getList"
+    />
+  </div>
+</template>
+
+<script setup name="AirConditionerLog">
+import { ref, reactive, onMounted, getCurrentInstance } from 'vue'
+import { Search, Refresh } from '@element-plus/icons-vue'
+import { parseTime } from '@/utils/ruoyi'
+import { listAirConditioner } from '@/api/airconditioner/airconditioner'
+import { listLog } from '@/api/airconditioner/log'
+
+// 鑾峰彇鐖剁粍浠跺疄渚�
+const { proxy } = getCurrentInstance()
+
+// 閬僵灞�
+const loading = ref(false)
+// 鏄剧ず鎼滅储鏉′欢
+const showSearch = ref(true)
+// 鎬绘潯鏁�
+const total = ref(0)
+// 鏃ュ織琛ㄦ牸鏁版嵁
+const logList = ref([])
+// 绌鸿皟閫夐」
+const airConditionerOptions = ref([])
+// 鎿嶄綔绫诲瀷閫夐」
+const operationTypeOptions = ref([
+  { label: '鎵嬪姩', value: '0' },
+  { label: '鑷姩', value: '1' }
+])
+
+const operationModeOptions = ref([
+  { label: '鍒跺喎', value: '0' },
+  { label: '鍒剁儹', value: '1' },
+    { label: '鍏虫満', value: '2' },
+])
+// 琛ㄥ崟ref
+const queryFormRef = ref()
+
+// 鏌ヨ鍙傛暟
+const queryParams = ref({
+  pageNum: 1,
+  pageSize: 10,
+  airConditionerId: undefined,
+  operationType: undefined,
+  timeRange: []
+})
+
+/** 鏌ヨ鏃ュ織鍒楄〃 */
+function getList() {
+  loading.value = true
+  listLog(queryParams.value).then(response => {
+    logList.value = response.rows
+    total.value = response.total
+    loading.value = false
+  })
+}
+
+/** 鏌ヨ绌鸿皟鍒楄〃 */
+function getAirConditioners() {
+  listAirConditioner().then(response => {
+    airConditionerOptions.value = response.rows
+  })
+}
+
+/** 鎼滅储鎸夐挳鎿嶄綔 */
+function handleQuery() {
+  queryParams.value.pageNum = 1
+  getList()
+}
+
+/** 閲嶇疆鎸夐挳鎿嶄綔 */
+function resetQuery() {
+  queryFormRef.value.resetFields()
+  handleQuery()
+}
+
+onMounted(() => {
+  getList()
+  getAirConditioners()
+  // 鑾峰彇瀛楀吀鏁版嵁
+//   proxy.getDicts('airconditioner_operation_type').then(response => {
+//     operationTypeOptions.value = response.data
+//   })
+})
+
+// 鏆撮湶鏂规硶缁欑埗缁勪欢璋冪敤
+defineExpose({
+  getList
+})
+</script>
\ No newline at end of file
diff --git a/zhitan-vue/src/views/airconditioner/schedule.vue b/zhitan-vue/src/views/airconditioner/schedule.vue
new file mode 100644
index 0000000..c3a6114
--- /dev/null
+++ b/zhitan-vue/src/views/airconditioner/schedule.vue
@@ -0,0 +1,434 @@
+<template>
+  <div class="app-container">
+    <el-row :gutter="20">
+      <el-col :span="24">
+        <el-form :model="queryParams" ref="queryFormRef" :inline="true" v-show="showSearch" label-width="68px">
+          <el-form-item label="鐘舵��" prop="status">
+            <el-select v-model="queryParams.status" placeholder="瀹氭椂浠诲姟鐘舵��" clearable>
+              <el-option
+                v-for="dict in statusOptions"
+                :key="dict.dictValue"
+                :label="dict.label"
+                :value="dict.dictValue"
+              />
+            </el-select>
+          </el-form-item>
+          <el-form-item>
+            <el-button type="primary" :icon="Search" @click="handleQuery">鎼滅储</el-button>
+            <el-button :icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
+          </el-form-item>
+        </el-form>
+
+        <el-row :gutter="10" class="mb8">
+          <el-col :span="1.5">
+            <el-button
+              type="primary"
+              plain
+              :icon="Plus"
+              @click="handleAdd"
+              v-hasPermi="['system:airconditioner:schedule:add']"
+            >鏂板</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button
+              type="success"
+              plain
+              :icon="Edit"
+              :disabled="single"
+              @click="handleUpdate"
+              v-hasPermi="['system:airconditioner:schedule:edit']"
+            >淇敼</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button
+              type="danger"
+              plain
+              :icon="Delete"
+              :disabled="multiple"
+              @click="handleDelete"
+              v-hasPermi="['system:airconditioner:schedule:remove']"
+            >鍒犻櫎</el-button>
+          </el-col>
+          <right-toolbar v-model:show-search="showSearch" @queryTable="getList"></right-toolbar>
+        </el-row>
+
+        <el-table v-loading="loading" :data="scheduleList" @selection-change="handleSelectionChange">
+          <el-table-column type="selection" width="55" align="center" />
+          <el-table-column label="浠诲姟ID" align="center" prop="id" />
+          <el-table-column label="绌鸿皟鍚嶇О" align="center" prop="airConditionerName">
+            {{ currentAirConditionerName }}
+            <!-- <template #default="scope">
+              <span>{{ getAirConditionerName(scope.row.airConditionerId) }}</span>
+            </template> -->
+          </el-table-column>
+          <el-table-column label="寮�濮嬫椂闂�" align="center" prop="startTime" width="120">
+<!--            <template #default="scope">-->
+<!--              <span>{{ formatTimeOnly(scope.row.startTime) }}</span>-->
+<!--            </template>-->
+          </el-table-column>
+          <el-table-column label="缁撴潫鏃堕棿" align="center" prop="offTime" width="120">
+<!--            <template #default="scope">-->
+<!--              <span>{{ formatTimeOnly(scope.row.offTime) }}</span>-->
+<!--            </template>-->
+          </el-table-column>
+          <el-table-column label="鐘舵��" align="center" prop="status">
+            <template #default="scope">
+              <dict-tag :options="statusOptions" :value="scope.row.status"/>
+            </template>
+          </el-table-column>
+          <el-table-column label="鎿嶄綔" align="center" class-name="small-padding fixed-width">
+            <template #default="scope">
+              <el-button
+                type="primary"
+                link
+                :icon="Edit"
+                @click="handleUpdate(scope.row)"
+                v-hasPermi="['system:airconditioner:schedule:edit']"
+              >淇敼</el-button>
+              <el-button
+                type="primary"
+                link
+                :icon="Delete"
+                @click="handleDelete(scope.row)"
+                v-hasPermi="['system:airconditioner:schedule:remove']"
+              >鍒犻櫎</el-button>
+            </template>
+          </el-table-column>
+        </el-table>
+        
+        <pagination
+          v-show="total>0"
+          :total="total"
+          v-model:page="queryParams.pageNum"
+          v-model:limit="queryParams.pageSize"
+          @pagination="getList"
+        />
+      </el-col>
+    </el-row>
+
+    <!-- 娣诲姞鎴栦慨鏀圭┖璋冨畾鏃朵换鍔″璇濇 -->
+    <el-dialog :title="title" v-model="open" width="500px" append-to-body>
+      <el-form ref="formRef" :model="form" :rules="rules" label-width="100px">
+        <el-form-item label="绌鸿皟" prop="airConditionerId">
+          <el-select v-model="form.airConditionerId" placeholder="璇烽�夋嫨绌鸿皟" style="width: 100%" :disabled="true">
+            <el-option
+              v-for="item in airConditionerOptions"
+              :key="item.id"
+              :label="item.name"
+              :value="item.id"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="寮�濮嬫椂闂�" prop="startTime">
+          <el-time-picker
+            v-model="form.startTime"
+            format="HH:mm:ss"
+            placeholder="閫夋嫨寮�濮嬫椂闂�"
+            style="width: 100%"
+          />
+        </el-form-item>
+        <el-form-item label="缁撴潫鏃堕棿" prop="offTime">
+          <el-time-picker
+            v-model="form.offTime"
+            format="HH:mm:ss"
+            placeholder="閫夋嫨缁撴潫鏃堕棿"
+            style="width: 100%"
+          />
+        </el-form-item>
+        <el-form-item label="鐘舵��">
+          <el-radio-group v-model="form.status">
+            <el-radio
+              v-for="dict in statusOptions"
+              :key="dict.value"
+              :label="dict.value"
+            >{{dict.label}}</el-radio>
+          </el-radio-group>
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button type="primary" @click="submitForm">纭� 瀹�</el-button>
+          <el-button @click="cancel">鍙� 娑�</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="AirConditionerSchedule">
+import { ref, reactive, onMounted, getCurrentInstance, nextTick } from 'vue'
+import { Search, Refresh, Plus, Edit, Delete } from '@element-plus/icons-vue'
+import { ElMessage, ElMessageBox } from 'element-plus'
+import { listSchedule, getSchedule, addSchedule, updateSchedule, delSchedule, getScheduleByAirConditionerId } from "@/api/airconditioner/schedule"
+import { listAirConditioner } from "@/api/airconditioner/airconditioner"
+import { parseTime } from '@/utils/ruoyi'
+
+/** 鏍煎紡鍖栨椂闂翠负鏃�:鍒�:绉� */
+function formatTimeOnly(time) {
+  if (!time) return ''
+  const date = new Date(time)
+  return `${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}:${date.getSeconds().toString().padStart(2, '0')}`
+}
+
+// 鑾峰彇鐖剁粍浠跺疄渚�
+const { proxy } = getCurrentInstance()
+
+// 閬僵灞�
+const loading = ref(true)
+// 閫変腑鏁扮粍
+const ids = ref([])
+// 闈炲崟涓鐢�
+const single = ref(true)
+// 闈炲涓鐢�
+const multiple = ref(true)
+// 鏄剧ず鎼滅储鏉′欢
+const showSearch = ref(true)
+// 鎬绘潯鏁�
+const total = ref(0)
+// 瀹氭椂浠诲姟琛ㄦ牸鏁版嵁
+const scheduleList = ref([])
+// 绌鸿皟閫夐」
+const airConditionerOptions = ref([])
+// 褰撳墠閫変腑鐨勭┖璋僆D
+const currentAirConditionerId = ref(null)
+
+const currentAirConditionerName= ref('')
+// 寮瑰嚭灞傛爣棰�
+const title = ref('')
+// 鏄惁鏄剧ず寮瑰嚭灞�
+const open = ref(false)
+// 琛ㄥ崟鍙傛暟
+const form = ref({})
+// 琛ㄥ崟ref
+const formRef = ref()
+const queryFormRef = ref()
+
+// 鐘舵�佸瓧鍏�
+const statusOptions = [
+  {
+    label: "姝e父",
+    value: "0"
+  },
+  {
+    label: "鍋滅敤",
+    value: "1"
+  }
+]
+
+// 鏌ヨ鍙傛暟
+const queryParams = ref({
+  pageNum: 1,
+  pageSize: 10,
+  airConditionerId: null,
+  status: null
+})
+
+// 琛ㄥ崟鏍¢獙
+const rules = {
+  airConditionerId: [
+    { required: true, message: "绌鸿皟涓嶈兘涓虹┖", trigger: "change" }
+  ],
+  startTime: [
+    { required: true, message: "寮�濮嬫椂闂翠笉鑳戒负绌�", trigger: "blur" }
+  ],
+  offTime: [
+    { required: true, message: "缁撴潫鏃堕棿涓嶈兘涓虹┖", trigger: "blur" }
+  ]
+}
+
+/** 鏌ヨ瀹氭椂浠诲姟鍒楄〃 */
+function getList() {
+  loading.value = true
+  // 濡傛灉鏈夊綋鍓嶉�変腑鐨勭┖璋僆D锛屽垯鍙煡璇㈣绌鸿皟鐨勫畾鏃朵换鍔�
+  if (currentAirConditionerId.value) {
+    getScheduleByAirConditionerId(currentAirConditionerId.value).then(response => {
+
+      scheduleList.value = response.data.sort((a, b) => {
+        return new Date(a.startTime) - new Date(b.startTime)
+      })
+      total.value = response.data.length
+      loading.value = false
+    })
+  } else {
+    // 鍏煎鍘熸湁閫昏緫锛屼絾姝e父鎯呭喌涓嬩笉浼氭墽琛屽埌杩欓噷
+    listSchedule(queryParams.value).then(response => {
+
+      // 鍏堟寜绌鸿皟鍚嶇О鎺掑簭锛屽啀鎸夊紑濮嬫椂闂存帓搴�
+      scheduleList.value = response.rows.sort((a, b) => {
+        const nameA = getAirConditionerName(a.airConditionerId)
+        const nameB = getAirConditionerName(b.airConditionerId)
+        if (nameA !== nameB) {
+          return nameA.localeCompare(nameB)
+        }
+        return new Date(a.startTime) - new Date(b.startTime)
+      })
+      total.value = response.total
+      loading.value = false
+    })
+  }
+}
+
+/** 鏌ヨ绌鸿皟鍒楄〃 */
+function getAirConditioners() {
+  listAirConditioner().then(response => {
+    airConditionerOptions.value = response.rows
+  })
+}
+
+/** 鏍规嵁绌鸿皟ID鑾峰彇绌鸿皟鍚嶇О */
+// function getAirConditionerName(airConditionerId) {
+//   const airConditioner = airConditionerOptions.value.find(item => item.id === airConditionerId)
+//   return airConditioner ? airConditioner.name : ''
+// }
+
+// 鍙栨秷鎸夐挳
+function cancel() {
+  open.value = false
+  reset()
+}
+
+// 琛ㄥ崟閲嶇疆
+function reset() {
+  form.value = {
+    id: null,
+    airConditionerId: null,
+    startTime: null,
+    offTime: null,
+    status: "0"
+  }
+  nextTick(() => {
+    if (formRef.value) {
+      formRef.value.resetFields()
+    }
+  })
+}
+
+/** 鎼滅储鎸夐挳鎿嶄綔 */
+function handleQuery() {
+  queryParams.value.pageNum = 1
+  getList()
+}
+
+/** 閲嶇疆鎸夐挳鎿嶄綔 */
+function resetQuery() {
+  queryFormRef.value.resetFields()
+  handleQuery()
+}
+
+// 澶氶�夋閫変腑鏁版嵁
+function handleSelectionChange(selection) {
+  ids.value = selection.map(item => item.id)
+  single.value = selection.length !== 1
+  multiple.value = !selection.length
+}
+
+/** 鏂板鎸夐挳鎿嶄綔 */
+function handleAdd() {
+  reset()
+  // 璁剧疆褰撳墠閫変腑鐨勭┖璋僆D
+  form.value.airConditionerId = currentAirConditionerId.value
+  open.value = true
+  title.value = "娣诲姞瀹氭椂浠诲姟"
+}
+
+/** 淇敼鎸夐挳鎿嶄綔 */
+function handleUpdate(row) {
+  reset()
+  const id = row.id || ids.value[0]
+  getSchedule(id).then(response => {
+    const data = response.data
+    
+    // 澶勭悊鏃堕棿鏍煎紡锛屽皢HH:MM:SS鏍煎紡杞崲涓篋ate瀵硅薄
+    if (data.startTime) {
+      const [hours, minutes, seconds] = data.startTime.split(':').map(Number)
+      const startTime = new Date()
+      startTime.setHours(hours, minutes, seconds)
+      data.startTime = startTime
+    }
+    
+    if (data.offTime) {
+      const [hours, minutes, seconds] = data.offTime.split(':').map(Number)
+      const offTime = new Date()
+      offTime.setHours(hours, minutes, seconds)
+      data.offTime = offTime
+    }
+    
+    form.value = data
+    open.value = true
+    title.value = "淇敼瀹氭椂浠诲姟"
+  })
+}
+
+/** 鎻愪氦鎸夐挳 */
+function submitForm() {
+  formRef.value.validate(valid => {
+    if (valid) {
+      // 鍒涘缓鎻愪氦鏁版嵁鐨勫壇鏈�
+      const submitData = { ...form.value }
+      
+      // 澶勭悊鏃堕棿鏍煎紡锛屽彧淇濈暀鏃跺垎绉�
+      if (submitData.startTime) {
+        const startTime = new Date(submitData.startTime)
+        submitData.startTime = `${startTime.getHours().toString().padStart(2, '0')}:${startTime.getMinutes().toString().padStart(2, '0')}:${startTime.getSeconds().toString().padStart(2, '0')}`
+      }
+      
+      if (submitData.offTime) {
+        const offTime = new Date(submitData.offTime)
+        submitData.offTime = `${offTime.getHours().toString().padStart(2, '0')}:${offTime.getMinutes().toString().padStart(2, '0')}:${offTime.getSeconds().toString().padStart(2, '0')}`
+      }
+      
+      if (submitData.id != null) {
+        updateSchedule(submitData).then(response => {
+          ElMessage.success("淇敼鎴愬姛")
+          open.value = false
+          getList()
+        })
+      } else {
+        addSchedule(submitData).then(response => {
+          ElMessage.success("鏂板鎴愬姛")
+          open.value = false
+          getList()
+        })
+      }
+    }
+  })
+}
+
+/** 鍒犻櫎鎸夐挳鎿嶄綔 */
+function handleDelete(row) {
+  const deleteIds = row.id || ids.value
+  ElMessageBox.confirm('鏄惁纭鍒犻櫎瀹氭椂浠诲姟缂栧彿涓�"' + deleteIds + '"鐨勬暟鎹」?')
+    .then(() => {
+      return delSchedule(deleteIds)
+    })
+    .then(() => {
+      getList()
+      ElMessage.success("鍒犻櫎鎴愬姛")
+    })
+    .catch(() => {})
+}
+
+/** 鏍规嵁绌鸿皟ID鍔犺浇瀹氭椂浠诲姟 */
+function loadScheduleByAirConditioner(airConditioner) {
+  console.log('loadScheduleByAirConditioner', airConditioner)
+  if (airConditioner) {
+    currentAirConditionerName.value = airConditioner.name
+    // 璁剧疆褰撳墠閫変腑鐨勭┖璋僆D
+    currentAirConditionerId.value = airConditioner.id
+    queryParams.value.airConditionerId = airConditioner.controllerId
+    handleQuery()
+  }
+}
+
+onMounted(() => {
+  // 椤甸潰鍔犺浇鏃跺厛鑾峰彇绌鸿皟鍒楄〃锛屼絾涓嶈嚜鍔ㄥ姞杞藉畾鏃朵换鍔″垪琛�
+  // 瀹氭椂浠诲姟鍒楄〃灏嗛�氳繃鐖剁粍浠朵紶閫掔殑绌鸿皟ID鍔犺浇
+  getAirConditioners()
+})
+
+// 鏆撮湶鏂规硶缁欑埗缁勪欢璋冪敤
+defineExpose({
+  getList,
+  loadScheduleByAirConditioner
+})
+</script>
\ No newline at end of file

--
Gitblit v1.9.3