From 974c7aa4010d77bb410b99931b4435d5442deb4b Mon Sep 17 00:00:00 2001
From: zhuguifei <312353457@qq.com>
Date: 星期一, 02 三月 2026 12:55:52 +0800
Subject: [PATCH] feat(module(qa)): 新增

---
 TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/impl/MakeupTimeDataServiceImpl.java    |   11 
 TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/mapper/PointDataMapper.java                    |    9 
 TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/IHoisterTimeDataService.java           |    7 
 TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/mapper/FeedmatchTimeDataMapper.java            |    9 
 ruoyi-plus-soybean/src/views/qm/batch/modules/batch-operate-drawer.vue                                   |  373 ++++
 TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/impl/HoisterTimeDataServiceImpl.java   |   11 
 TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/impl/BoxTimeDataServiceImpl.java       |   11 
 TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/impl/FeedmatchTimeDataServiceImpl.java |   11 
 TimescaleDB-Utils/HELP.md                                                                                |   10 
 TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/mapper/HoisterTimeDataMapper.java              |    9 
 TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/entity/FeedmatchTimeData.java                  |  148 +
 ruoyi-plus-soybean/src/service/api/qm/batch.ts                                                           |   35 
 TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/entity/MakeupTimeData.java                     |   78 
 TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/IPackerTimeDataService.java            |    7 
 TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/IMakeupTimeDataService.java            |    7 
 RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qm/controller/QmBatchController.java     |  105 +
 TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/config/MybatisPlusConfig.java                  |    9 
 TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/config/StartupStatusPrinter.java               |   53 
 TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/IFeedmatchTimeDataService.java         |    7 
 TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/IRollerTimeDataService.java            |    7 
 TimescaleDB-Utils/src/main/resources/application.yml                                                     |   37 
 RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qm/service/impl/QmBatchServiceImpl.java  |  144 +
 TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/mapper/RollerTimeDataMapper.java               |    9 
 .gitignore                                                                                               |    4 
 TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/mapper/MakeupTimeDataMapper.java               |    9 
 RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/resources/mapper/qm/QmBatchMapper.xml                     |    6 
 TimescaleDB-Utils/pom.xml                                                                                |   98 +
 TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/mapper/PackerTimeDataMapper.java               |    9 
 ruoyi-plus-soybean/src/typings/api/qm.batch.api.d.ts                                                     |  185 ++
 TimescaleDB-Utils/readMe.md                                                                              |  509 ++++++
 TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/impl/TransTimeDataServiceImpl.java     |   11 
 TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/impl/RollerTimeDataServiceImpl.java    |   11 
 TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/impl/PackerTimeDataServiceImpl.java    |   11 
 TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/mapper/BoxTimeDataMapper.java                  |    9 
 RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qm/service/IQmBatchService.java          |   68 
 TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/impl/PointDataServiceImpl.java         |   11 
 TimescaleDB-Utils/src/test/java/com/shlb/timescaledbutils/RollerTimeDataTest.java                        |   29 
 ruoyi-plus-soybean/src/views/qm/batch/index.vue                                                          |  474 +++++
 TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/mapper/TransTimeDataMapper.java                |    9 
 TimescaleDB-Utils/src/test/java/com/shlb/timescaledbutils/TimescaleDbUtilsApplicationTests.java          |   40 
 TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/constant/AppConstants.java                     |   92 +
 RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qm/domain/vo/QmBatchVo.java              |  316 +++
 TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/TimescaleDbUtilsApplication.java               |   23 
 TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/task/RedisToPostgresSyncTask.java              |  278 +++
 TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/entity/PackerTimeData.java                     |  138 +
 TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/ITransTimeDataService.java             |    7 
 TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/entity/HoisterTimeData.java                    |  208 ++
 RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qm/domain/QmBatch.java                   |  263 +++
 TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/IPointDataService.java                 |    7 
 TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/entity/PointData.java                          |   53 
 TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/entity/RollerTimeData.java                     |  118 +
 TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/IBoxTimeDataService.java               |    7 
 RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qm/mapper/QmBatchMapper.java             |   15 
 TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/entity/BoxTimeData.java                        |   53 
 TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/utils/RedisUtils.java                          |  114 +
 ruoyi-plus-soybean/src/views/qm/batch/modules/batch-search.vue                                           |  150 +
 TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/entity/TransTimeData.java                      |  153 +
 RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qm/domain/bo/QmBatchBo.java              |  262 +++
 TimescaleDB-Utils/src/test/java/com/shlb/timescaledbutils/PostgresDataMonitorTest.java                   |   80 
 59 files changed, 4,937 insertions(+), 0 deletions(-)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..440a664
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+**/.idea
+**/logs
+**/target
+**/.vscode
\ No newline at end of file
diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qm/controller/QmBatchController.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qm/controller/QmBatchController.java
new file mode 100644
index 0000000..fba4085
--- /dev/null
+++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qm/controller/QmBatchController.java
@@ -0,0 +1,105 @@
+package org.dromara.qm.controller;
+
+import java.util.List;
+
+import lombok.RequiredArgsConstructor;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.validation.constraints.*;
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.validation.annotation.Validated;
+import org.dromara.common.idempotent.annotation.RepeatSubmit;
+import org.dromara.common.log.annotation.Log;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.core.validate.AddGroup;
+import org.dromara.common.core.validate.EditGroup;
+import org.dromara.common.log.enums.BusinessType;
+import org.dromara.common.excel.utils.ExcelUtil;
+import org.dromara.qm.domain.vo.QmBatchVo;
+import org.dromara.qm.domain.bo.QmBatchBo;
+import org.dromara.qm.service.IQmBatchService;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+
+/**
+ * 妫�楠屾壒娆�
+ *
+ * @author zhuguifei
+ * @date 2026-03-02
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/qm/batch")
+public class QmBatchController extends BaseController {
+
+    private final IQmBatchService qmBatchService;
+
+    /**
+     * 鏌ヨ妫�楠屾壒娆″垪琛�
+     */
+    @SaCheckPermission("qm:batch:list")
+    @GetMapping("/list")
+    public TableDataInfo<QmBatchVo> list(QmBatchBo bo, PageQuery pageQuery) {
+        return qmBatchService.queryPageList(bo, pageQuery);
+    }
+
+    /**
+     * 瀵煎嚭妫�楠屾壒娆″垪琛�
+     */
+    @SaCheckPermission("qm:batch:export")
+    @Log(title = "妫�楠屾壒娆�", businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    public void export(QmBatchBo bo, HttpServletResponse response) {
+        List<QmBatchVo> list = qmBatchService.queryList(bo);
+        ExcelUtil.exportExcel(list, "妫�楠屾壒娆�", QmBatchVo.class, response);
+    }
+
+    /**
+     * 鑾峰彇妫�楠屾壒娆¤缁嗕俊鎭�
+     *
+     * @param id 涓婚敭
+     */
+    @SaCheckPermission("qm:batch:query")
+    @GetMapping("/{id}")
+    public R<QmBatchVo> getInfo(@NotNull(message = "涓婚敭涓嶈兘涓虹┖")
+                                     @PathVariable String id) {
+        return R.ok(qmBatchService.queryById(id));
+    }
+
+    /**
+     * 鏂板妫�楠屾壒娆�
+     */
+    @SaCheckPermission("qm:batch:add")
+    @Log(title = "妫�楠屾壒娆�", businessType = BusinessType.INSERT)
+    @RepeatSubmit()
+    @PostMapping()
+    public R<Void> add(@Validated(AddGroup.class) @RequestBody QmBatchBo bo) {
+        return toAjax(qmBatchService.insertByBo(bo));
+    }
+
+    /**
+     * 淇敼妫�楠屾壒娆�
+     */
+    @SaCheckPermission("qm:batch:edit")
+    @Log(title = "妫�楠屾壒娆�", businessType = BusinessType.UPDATE)
+    @RepeatSubmit()
+    @PutMapping()
+    public R<Void> edit(@Validated(EditGroup.class) @RequestBody QmBatchBo bo) {
+        return toAjax(qmBatchService.updateByBo(bo));
+    }
+
+    /**
+     * 鍒犻櫎妫�楠屾壒娆�
+     *
+     * @param ids 涓婚敭涓�
+     */
+    @SaCheckPermission("qm:batch:remove")
+    @Log(title = "妫�楠屾壒娆�", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{ids}")
+    public R<Void> remove(@NotEmpty(message = "涓婚敭涓嶈兘涓虹┖")
+                          @PathVariable String[] ids) {
+        return toAjax(qmBatchService.deleteWithValidByIds(List.of(ids), true));
+    }
+}
diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qm/domain/QmBatch.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qm/domain/QmBatch.java
new file mode 100644
index 0000000..709a2ae
--- /dev/null
+++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qm/domain/QmBatch.java
@@ -0,0 +1,263 @@
+package org.dromara.qm.domain;
+
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import java.util.Date;
+import com.fasterxml.jackson.annotation.JsonFormat;
+
+import java.io.Serial;
+
+/**
+ * 妫�楠屾壒娆″璞� qm_batch
+ *
+ * @author zhuguifei
+ * @date 2026-03-02
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("qm_batch")
+public class QmBatch extends BaseEntity {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 缂栫爜
+     */
+    @TableId(value = "id")
+    private String id;
+
+    /**
+     * 鎵规浠g爜
+     */
+    private String batchCode;
+
+    /**
+     * 鎵规鍚嶇О
+     */
+    private String batchName;
+
+    /**
+     * A-鍒朵笣 B-鎴愬瀷 C-鍗峰寘 D-灏佺 E-绯栭鏂�
+     */
+    private String typ;
+
+    /**
+     * 鏈哄彴浠g爜
+     */
+    private String eqpCode;
+
+    /**
+     * 鐗屽彿
+     */
+    private String matCode;
+
+    /**
+     * 鍒ゅ畾渚濇嵁浠g爜
+     */
+    private String judgeCode;
+
+    /**
+     * 鎵规鐢熸垚鏃ユ湡
+     */
+    private Date batchDate;
+
+    /**
+     * 浣跨敤鏍囧織
+     */
+    private String isflag;
+
+    /**
+     * 鍚敤鏍囧織
+     */
+    private String enabled;
+
+    /**
+     * 鍒拌揣鎬婚噺
+     */
+    private Long totalNum;
+
+    /**
+     * 缁煎悎鍒ゅ畾
+     */
+    private String results;
+
+    /**
+     * 鎵瑰噯浜�
+     */
+    private String approver;
+
+    /**
+     * 瀹℃牳浜�
+     */
+    private String auditor;
+
+    /**
+     * 鍒涘缓浜�
+     */
+    private String creater;
+
+    /**
+     * 鍒惰〃鏃ユ湡
+     */
+    private Date tabDate;
+
+    /**
+     * 鐗堟湰鍚嶇О
+     */
+    private String verName;
+
+    /**
+     * 鐗堟湰缂栧彿
+     */
+    private String verCode;
+
+    /**
+     * 淇濆瓨鏈�
+     */
+    private String archDate;
+
+    /**
+     * 0-鏈笂浼爉es,1-宸蹭笂浼�, 3-浠嶮ES涓嬭浇
+     */
+    private String flag;
+
+    /**
+     * 涓婁紶MES鏃堕棿
+     */
+    private Date toMesDate;
+
+    /**
+     * 浠嶮ES鏃堕棿涓嬭浇
+     */
+    private Date fromMesDate;
+
+    /**
+     * 鍒犻櫎鏍囧織
+     */
+    private Long deleted;
+
+    /**
+     * 鎵规鎻忚堪
+     */
+    private String batchDes;
+
+    /**
+     * 绫诲埆 0锛氭垚鍝� 1锛氳緟鏉�
+     */
+    private String category;
+
+    /**
+     * 鍗峰埗宸ュ彿
+     */
+    private String makeno;
+
+    /**
+     * 鐝鏈哄彿
+     */
+    private String shifteqpno;
+
+    /**
+     * 瑁呯鍙�
+     */
+    private String boxno;
+
+    /**
+     * 鐖舵壒娆″彿
+     */
+    private String pid;
+
+    /**
+     * 澶嶆牳浜�
+     */
+    private String reviewer;
+
+    /**
+     * 澶嶆娆℃暟
+     */
+    private Long rvcount;
+
+    /**
+     * 鎵规鐘舵��
+     */
+    private String state;
+
+    /**
+     * 澶嶆牳鏃ユ湡
+     */
+    private Date reviewTime;
+
+    /**
+     * 瀹℃牳鏃ユ湡
+     */
+    private Date auditTime;
+
+    /**
+     * 瑙勬牸
+     */
+    private String spec;
+
+    /**
+     * 鎵瑰噯鏃堕棿
+     */
+    private Date approveTime;
+
+    /**
+     * 鍒拌揣鍗曚綅
+     */
+    private String unit;
+
+    /**
+     * 鍒拌揣鏃ユ湡
+     */
+    private Date arrivalTime;
+
+    /**
+     * 瀛樻斁鍦扮偣
+     */
+    private String storagePlace;
+
+    /**
+     * 妫�楠屽憳
+     */
+    private String checker;
+
+    /**
+     * 鎺ュ崟鏃ユ湡
+     */
+    private Date receiveTime;
+
+    /**
+     * 鎶ユ鏃ユ湡
+     */
+    private Date inspTime;
+
+    /**
+     * 浠撳簱淇濈鍛�
+     */
+    private String storer;
+
+    /**
+     * 鏄惁楠岃瘉
+     */
+    private String isverify;
+
+    /**
+     * 鏄惁妫�楠�
+     */
+    private String ischk;
+
+    /**
+     * 澶囩敤1
+     */
+    private String bak1;
+
+    /**
+     * 澶囩敤2
+     */
+    private String bak2;
+
+
+}
diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qm/domain/bo/QmBatchBo.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qm/domain/bo/QmBatchBo.java
new file mode 100644
index 0000000..36bf7af
--- /dev/null
+++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qm/domain/bo/QmBatchBo.java
@@ -0,0 +1,262 @@
+package org.dromara.qm.domain.bo;
+
+import org.dromara.qm.domain.QmBatch;
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import org.dromara.common.core.validate.AddGroup;
+import org.dromara.common.core.validate.EditGroup;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import jakarta.validation.constraints.*;
+import java.util.Date;
+import com.fasterxml.jackson.annotation.JsonFormat;
+
+/**
+ * 妫�楠屾壒娆′笟鍔″璞� qm_batch
+ *
+ * @author zhuguifei
+ * @date 2026-03-02
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = QmBatch.class, reverseConvertGenerate = false)
+public class QmBatchBo extends BaseEntity {
+
+    /**
+     * 缂栫爜
+     */
+    @NotBlank(message = "缂栫爜涓嶈兘涓虹┖", groups = { EditGroup.class })
+    private String id;
+
+    /**
+     * 鎵规浠g爜
+     */
+    private String batchCode;
+
+    /**
+     * 鎵规鍚嶇О
+     */
+    private String batchName;
+
+    /**
+     * A-鍒朵笣 B-鎴愬瀷 C-鍗峰寘 D-灏佺 E-绯栭鏂�
+     */
+    private String typ;
+
+    /**
+     * 鏈哄彴浠g爜
+     */
+    private String eqpCode;
+
+    /**
+     * 鐗屽彿
+     */
+    private String matCode;
+
+    /**
+     * 鍒ゅ畾渚濇嵁浠g爜
+     */
+    private String judgeCode;
+
+    /**
+     * 鎵规鐢熸垚鏃ユ湡
+     */
+    private Date batchDate;
+
+    /**
+     * 浣跨敤鏍囧織
+     */
+    private String isflag;
+
+    /**
+     * 鍚敤鏍囧織
+     */
+    private String enabled;
+
+    /**
+     * 鍒拌揣鎬婚噺
+     */
+    private Long totalNum;
+
+    /**
+     * 缁煎悎鍒ゅ畾
+     */
+    private String results;
+
+    /**
+     * 鎵瑰噯浜�
+     */
+    private String approver;
+
+    /**
+     * 瀹℃牳浜�
+     */
+    private String auditor;
+
+    /**
+     * 鍒涘缓浜�
+     */
+    private String creater;
+
+    /**
+     * 鍒惰〃鏃ユ湡
+     */
+    private Date tabDate;
+
+    /**
+     * 鐗堟湰鍚嶇О
+     */
+    private String verName;
+
+    /**
+     * 鐗堟湰缂栧彿
+     */
+    private String verCode;
+
+    /**
+     * 淇濆瓨鏈�
+     */
+    private String archDate;
+
+    /**
+     * 0-鏈笂浼爉es,1-宸蹭笂浼�, 3-浠嶮ES涓嬭浇
+     */
+    private String flag;
+
+    /**
+     * 涓婁紶MES鏃堕棿
+     */
+    private Date toMesDate;
+
+    /**
+     * 浠嶮ES鏃堕棿涓嬭浇
+     */
+    private Date fromMesDate;
+
+    /**
+     * 鍒犻櫎鏍囧織
+     */
+    private Long deleted;
+
+    /**
+     * 鎵规鎻忚堪
+     */
+    private String batchDes;
+
+    /**
+     * 绫诲埆 0锛氭垚鍝� 1锛氳緟鏉�
+     */
+    private String category;
+
+    /**
+     * 鍗峰埗宸ュ彿
+     */
+    private String makeno;
+
+    /**
+     * 鐝鏈哄彿
+     */
+    private String shifteqpno;
+
+    /**
+     * 瑁呯鍙�
+     */
+    private String boxno;
+
+    /**
+     * 鐖舵壒娆″彿
+     */
+    private String pid;
+
+    /**
+     * 澶嶆牳浜�
+     */
+    private String reviewer;
+
+    /**
+     * 澶嶆娆℃暟
+     */
+    private Long rvcount;
+
+    /**
+     * 鎵规鐘舵��
+     */
+    private String state;
+
+    /**
+     * 澶嶆牳鏃ユ湡
+     */
+    private Date reviewTime;
+
+    /**
+     * 瀹℃牳鏃ユ湡
+     */
+    private Date auditTime;
+
+    /**
+     * 瑙勬牸
+     */
+    private String spec;
+
+    /**
+     * 鎵瑰噯鏃堕棿
+     */
+    private Date approveTime;
+
+    /**
+     * 鍒拌揣鍗曚綅
+     */
+    private String unit;
+
+    /**
+     * 鍒拌揣鏃ユ湡
+     */
+    private Date arrivalTime;
+
+    /**
+     * 瀛樻斁鍦扮偣
+     */
+    private String storagePlace;
+
+    /**
+     * 妫�楠屽憳
+     */
+    private String checker;
+
+    /**
+     * 鎺ュ崟鏃ユ湡
+     */
+    private Date receiveTime;
+
+    /**
+     * 鎶ユ鏃ユ湡
+     */
+    private Date inspTime;
+
+    /**
+     * 浠撳簱淇濈鍛�
+     */
+    private String storer;
+
+    /**
+     * 鏄惁楠岃瘉
+     */
+    private String isverify;
+
+    /**
+     * 鏄惁妫�楠�
+     */
+    private String ischk;
+
+    /**
+     * 澶囩敤1
+     */
+    private String bak1;
+
+    /**
+     * 澶囩敤2
+     */
+    private String bak2;
+
+
+}
diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qm/domain/vo/QmBatchVo.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qm/domain/vo/QmBatchVo.java
new file mode 100644
index 0000000..ae17bed
--- /dev/null
+++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qm/domain/vo/QmBatchVo.java
@@ -0,0 +1,316 @@
+package org.dromara.qm.domain.vo;
+
+import java.util.Date;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import org.dromara.qm.domain.QmBatch;
+import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
+import cn.idev.excel.annotation.ExcelProperty;
+import org.dromara.common.excel.annotation.ExcelDictFormat;
+import org.dromara.common.excel.convert.ExcelDictConvert;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+
+
+
+/**
+ * 妫�楠屾壒娆¤鍥惧璞� qm_batch
+ *
+ * @author zhuguifei
+ * @date 2026-03-02
+ */
+@Data
+@ExcelIgnoreUnannotated
+@AutoMapper(target = QmBatch.class)
+public class QmBatchVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 缂栫爜
+     */
+    @ExcelProperty(value = "缂栫爜")
+    private String id;
+
+    /**
+     * 鎵规浠g爜
+     */
+    @ExcelProperty(value = "鎵规浠g爜")
+    private String batchCode;
+
+    /**
+     * 鎵规鍚嶇О
+     */
+    @ExcelProperty(value = "鎵规鍚嶇О")
+    private String batchName;
+
+    /**
+     * A-鍒朵笣 B-鎴愬瀷 C-鍗峰寘 D-灏佺 E-绯栭鏂�
+     */
+    @ExcelProperty(value = "A-鍒朵笣 B-鎴愬瀷 C-鍗峰寘 D-灏佺 E-绯栭鏂�")
+    private String typ;
+
+    /**
+     * 鏈哄彴浠g爜
+     */
+    @ExcelProperty(value = "鏈哄彴浠g爜")
+    private String eqpCode;
+
+    /**
+     * 鐗屽彿
+     */
+    @ExcelProperty(value = "鐗屽彿")
+    private String matCode;
+
+    /**
+     * 鍒ゅ畾渚濇嵁浠g爜
+     */
+    @ExcelProperty(value = "鍒ゅ畾渚濇嵁浠g爜")
+    private String judgeCode;
+
+    /**
+     * 鎵规鐢熸垚鏃ユ湡
+     */
+    @ExcelProperty(value = "鎵规鐢熸垚鏃ユ湡")
+    private Date batchDate;
+
+    /**
+     * 浣跨敤鏍囧織
+     */
+    @ExcelProperty(value = "浣跨敤鏍囧織")
+    private String isflag;
+
+    /**
+     * 鍚敤鏍囧織
+     */
+    @ExcelProperty(value = "鍚敤鏍囧織")
+    private String enabled;
+
+    /**
+     * 鍒拌揣鎬婚噺
+     */
+    @ExcelProperty(value = "鍒拌揣鎬婚噺")
+    private Long totalNum;
+
+    /**
+     * 缁煎悎鍒ゅ畾
+     */
+    @ExcelProperty(value = "缁煎悎鍒ゅ畾")
+    private String results;
+
+    /**
+     * 鎵瑰噯浜�
+     */
+    @ExcelProperty(value = "鎵瑰噯浜�")
+    private String approver;
+
+    /**
+     * 瀹℃牳浜�
+     */
+    @ExcelProperty(value = "瀹℃牳浜�")
+    private String auditor;
+
+    /**
+     * 鍒涘缓浜�
+     */
+    @ExcelProperty(value = "鍒涘缓浜�")
+    private String creater;
+
+    /**
+     * 鍒惰〃鏃ユ湡
+     */
+    @ExcelProperty(value = "鍒惰〃鏃ユ湡")
+    private Date tabDate;
+
+    /**
+     * 鐗堟湰鍚嶇О
+     */
+    @ExcelProperty(value = "鐗堟湰鍚嶇О")
+    private String verName;
+
+    /**
+     * 鐗堟湰缂栧彿
+     */
+    @ExcelProperty(value = "鐗堟湰缂栧彿")
+    private String verCode;
+
+    /**
+     * 淇濆瓨鏈�
+     */
+    @ExcelProperty(value = "淇濆瓨鏈�")
+    private String archDate;
+
+    /**
+     * 0-鏈笂浼爉es,1-宸蹭笂浼�, 3-浠嶮ES涓嬭浇
+     */
+    @ExcelProperty(value = "0-鏈笂浼爉es,1-宸蹭笂浼�, 3-浠嶮ES涓嬭浇")
+    private String flag;
+
+    /**
+     * 涓婁紶MES鏃堕棿
+     */
+    @ExcelProperty(value = "涓婁紶MES鏃堕棿")
+    private Date toMesDate;
+
+    /**
+     * 浠嶮ES鏃堕棿涓嬭浇
+     */
+    @ExcelProperty(value = "浠嶮ES鏃堕棿涓嬭浇")
+    private Date fromMesDate;
+
+    /**
+     * 鍒犻櫎鏍囧織
+     */
+    @ExcelProperty(value = "鍒犻櫎鏍囧織")
+    private Long deleted;
+
+    /**
+     * 鎵规鎻忚堪
+     */
+    @ExcelProperty(value = "鎵规鎻忚堪")
+    private String batchDes;
+
+    /**
+     * 绫诲埆 0锛氭垚鍝� 1锛氳緟鏉�
+     */
+    @ExcelProperty(value = "绫诲埆 0锛氭垚鍝� 1锛氳緟鏉�")
+    private String category;
+
+    /**
+     * 鍗峰埗宸ュ彿
+     */
+    @ExcelProperty(value = "鍗峰埗宸ュ彿")
+    private String makeno;
+
+    /**
+     * 鐝鏈哄彿
+     */
+    @ExcelProperty(value = "鐝鏈哄彿")
+    private String shifteqpno;
+
+    /**
+     * 瑁呯鍙�
+     */
+    @ExcelProperty(value = "瑁呯鍙�")
+    private String boxno;
+
+    /**
+     * 鐖舵壒娆″彿
+     */
+    @ExcelProperty(value = "鐖舵壒娆″彿")
+    private String pid;
+
+    /**
+     * 澶嶆牳浜�
+     */
+    @ExcelProperty(value = "澶嶆牳浜�")
+    private String reviewer;
+
+    /**
+     * 澶嶆娆℃暟
+     */
+    @ExcelProperty(value = "澶嶆娆℃暟")
+    private Long rvcount;
+
+    /**
+     * 鎵规鐘舵��
+     */
+    @ExcelProperty(value = "鎵规鐘舵��")
+    private String state;
+
+    /**
+     * 澶嶆牳鏃ユ湡
+     */
+    @ExcelProperty(value = "澶嶆牳鏃ユ湡")
+    private Date reviewTime;
+
+    /**
+     * 瀹℃牳鏃ユ湡
+     */
+    @ExcelProperty(value = "瀹℃牳鏃ユ湡")
+    private Date auditTime;
+
+    /**
+     * 瑙勬牸
+     */
+    @ExcelProperty(value = "瑙勬牸")
+    private String spec;
+
+    /**
+     * 鎵瑰噯鏃堕棿
+     */
+    @ExcelProperty(value = "鎵瑰噯鏃堕棿")
+    private Date approveTime;
+
+    /**
+     * 鍒拌揣鍗曚綅
+     */
+    @ExcelProperty(value = "鍒拌揣鍗曚綅")
+    private String unit;
+
+    /**
+     * 鍒拌揣鏃ユ湡
+     */
+    @ExcelProperty(value = "鍒拌揣鏃ユ湡")
+    private Date arrivalTime;
+
+    /**
+     * 瀛樻斁鍦扮偣
+     */
+    @ExcelProperty(value = "瀛樻斁鍦扮偣")
+    private String storagePlace;
+
+    /**
+     * 妫�楠屽憳
+     */
+    @ExcelProperty(value = "妫�楠屽憳")
+    private String checker;
+
+    /**
+     * 鎺ュ崟鏃ユ湡
+     */
+    @ExcelProperty(value = "鎺ュ崟鏃ユ湡")
+    private Date receiveTime;
+
+    /**
+     * 鎶ユ鏃ユ湡
+     */
+    @ExcelProperty(value = "鎶ユ鏃ユ湡")
+    private Date inspTime;
+
+    /**
+     * 浠撳簱淇濈鍛�
+     */
+    @ExcelProperty(value = "浠撳簱淇濈鍛�")
+    private String storer;
+
+    /**
+     * 鏄惁楠岃瘉
+     */
+    @ExcelProperty(value = "鏄惁楠岃瘉")
+    private String isverify;
+
+    /**
+     * 鏄惁妫�楠�
+     */
+    @ExcelProperty(value = "鏄惁妫�楠�")
+    private String ischk;
+
+    /**
+     * 澶囩敤1
+     */
+    @ExcelProperty(value = "澶囩敤1")
+    private String bak1;
+
+    /**
+     * 澶囩敤2
+     */
+    @ExcelProperty(value = "澶囩敤2")
+    private String bak2;
+
+
+}
diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qm/mapper/QmBatchMapper.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qm/mapper/QmBatchMapper.java
new file mode 100644
index 0000000..0539e73
--- /dev/null
+++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qm/mapper/QmBatchMapper.java
@@ -0,0 +1,15 @@
+package org.dromara.qm.mapper;
+
+import org.dromara.qm.domain.QmBatch;
+import org.dromara.qm.domain.vo.QmBatchVo;
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+
+/**
+ * 妫�楠屾壒娆apper鎺ュ彛
+ *
+ * @author zhuguifei
+ * @date 2026-03-02
+ */
+public interface QmBatchMapper extends BaseMapperPlus<QmBatch, QmBatchVo> {
+
+}
diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qm/service/IQmBatchService.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qm/service/IQmBatchService.java
new file mode 100644
index 0000000..5d99bea
--- /dev/null
+++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qm/service/IQmBatchService.java
@@ -0,0 +1,68 @@
+package org.dromara.qm.service;
+
+import org.dromara.qm.domain.vo.QmBatchVo;
+import org.dromara.qm.domain.bo.QmBatchBo;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.mybatis.core.page.PageQuery;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 妫�楠屾壒娆ervice鎺ュ彛
+ *
+ * @author zhuguifei
+ * @date 2026-03-02
+ */
+public interface IQmBatchService {
+
+    /**
+     * 鏌ヨ妫�楠屾壒娆�
+     *
+     * @param id 涓婚敭
+     * @return 妫�楠屾壒娆�
+     */
+    QmBatchVo queryById(String id);
+
+    /**
+     * 鍒嗛〉鏌ヨ妫�楠屾壒娆″垪琛�
+     *
+     * @param bo        鏌ヨ鏉′欢
+     * @param pageQuery 鍒嗛〉鍙傛暟
+     * @return 妫�楠屾壒娆″垎椤靛垪琛�
+     */
+    TableDataInfo<QmBatchVo> queryPageList(QmBatchBo bo, PageQuery pageQuery);
+
+    /**
+     * 鏌ヨ绗﹀悎鏉′欢鐨勬楠屾壒娆″垪琛�
+     *
+     * @param bo 鏌ヨ鏉′欢
+     * @return 妫�楠屾壒娆″垪琛�
+     */
+    List<QmBatchVo> queryList(QmBatchBo bo);
+
+    /**
+     * 鏂板妫�楠屾壒娆�
+     *
+     * @param bo 妫�楠屾壒娆�
+     * @return 鏄惁鏂板鎴愬姛
+     */
+    Boolean insertByBo(QmBatchBo bo);
+
+    /**
+     * 淇敼妫�楠屾壒娆�
+     *
+     * @param bo 妫�楠屾壒娆�
+     * @return 鏄惁淇敼鎴愬姛
+     */
+    Boolean updateByBo(QmBatchBo bo);
+
+    /**
+     * 鏍¢獙骞舵壒閲忓垹闄ゆ楠屾壒娆′俊鎭�
+     *
+     * @param ids     寰呭垹闄ょ殑涓婚敭闆嗗悎
+     * @param isValid 鏄惁杩涜鏈夋晥鎬ф牎楠�
+     * @return 鏄惁鍒犻櫎鎴愬姛
+     */
+    Boolean deleteWithValidByIds(Collection<String> ids, Boolean isValid);
+}
diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qm/service/impl/QmBatchServiceImpl.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qm/service/impl/QmBatchServiceImpl.java
new file mode 100644
index 0000000..afa7942
--- /dev/null
+++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qm/service/impl/QmBatchServiceImpl.java
@@ -0,0 +1,144 @@
+package org.dromara.qm.service.impl;
+
+import org.dromara.common.core.utils.MapstructUtils;
+import org.dromara.common.core.utils.StringUtils;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.dromara.qm.domain.bo.QmBatchBo;
+import org.dromara.qm.domain.vo.QmBatchVo;
+import org.dromara.qm.domain.QmBatch;
+import org.dromara.qm.mapper.QmBatchMapper;
+import org.dromara.qm.service.IQmBatchService;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Collection;
+
+/**
+ * 妫�楠屾壒娆ervice涓氬姟灞傚鐞�
+ *
+ * @author zhuguifei
+ * @date 2026-03-02
+ */
+@Slf4j
+@RequiredArgsConstructor
+@Service
+public class QmBatchServiceImpl implements IQmBatchService {
+
+    private final QmBatchMapper baseMapper;
+
+    /**
+     * 鏌ヨ妫�楠屾壒娆�
+     *
+     * @param id 涓婚敭
+     * @return 妫�楠屾壒娆�
+     */
+    @Override
+    public QmBatchVo queryById(String id){
+        return baseMapper.selectVoById(id);
+    }
+
+    /**
+     * 鍒嗛〉鏌ヨ妫�楠屾壒娆″垪琛�
+     *
+     * @param bo        鏌ヨ鏉′欢
+     * @param pageQuery 鍒嗛〉鍙傛暟
+     * @return 妫�楠屾壒娆″垎椤靛垪琛�
+     */
+    @Override
+    public TableDataInfo<QmBatchVo> queryPageList(QmBatchBo bo, PageQuery pageQuery) {
+        LambdaQueryWrapper<QmBatch> lqw = buildQueryWrapper(bo);
+        Page<QmBatchVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
+        return TableDataInfo.build(result);
+    }
+
+    /**
+     * 鏌ヨ绗﹀悎鏉′欢鐨勬楠屾壒娆″垪琛�
+     *
+     * @param bo 鏌ヨ鏉′欢
+     * @return 妫�楠屾壒娆″垪琛�
+     */
+    @Override
+    public List<QmBatchVo> queryList(QmBatchBo bo) {
+        LambdaQueryWrapper<QmBatch> lqw = buildQueryWrapper(bo);
+        return baseMapper.selectVoList(lqw);
+    }
+
+    private LambdaQueryWrapper<QmBatch> buildQueryWrapper(QmBatchBo bo) {
+        Map<String, Object> params = bo.getParams();
+        LambdaQueryWrapper<QmBatch> lqw = Wrappers.lambdaQuery();
+        lqw.orderByAsc(QmBatch::getId);
+        lqw.like(StringUtils.isNotBlank(bo.getBatchCode()), QmBatch::getBatchCode, bo.getBatchCode());
+        lqw.eq(StringUtils.isNotBlank(bo.getTyp()), QmBatch::getTyp, bo.getTyp());
+        lqw.eq(StringUtils.isNotBlank(bo.getEqpCode()), QmBatch::getEqpCode, bo.getEqpCode());
+        lqw.eq(StringUtils.isNotBlank(bo.getMatCode()), QmBatch::getMatCode, bo.getMatCode());
+        lqw.eq(bo.getBatchDate() != null, QmBatch::getBatchDate, bo.getBatchDate());
+        lqw.eq(StringUtils.isNotBlank(bo.getFlag()), QmBatch::getFlag, bo.getFlag());
+        lqw.between(params.get("beginToMesDate") != null && params.get("endToMesDate") != null,
+            QmBatch::getToMesDate ,params.get("beginToMesDate"), params.get("endToMesDate"));
+        lqw.between(params.get("beginFromMesDate") != null && params.get("endFromMesDate") != null,
+            QmBatch::getFromMesDate ,params.get("beginFromMesDate"), params.get("endFromMesDate"));
+        lqw.eq(bo.getDeleted() != null, QmBatch::getDeleted, bo.getDeleted());
+        lqw.eq(StringUtils.isNotBlank(bo.getCategory()), QmBatch::getCategory, bo.getCategory());
+        lqw.eq(StringUtils.isNotBlank(bo.getState()), QmBatch::getState, bo.getState());
+        return lqw;
+    }
+
+    /**
+     * 鏂板妫�楠屾壒娆�
+     *
+     * @param bo 妫�楠屾壒娆�
+     * @return 鏄惁鏂板鎴愬姛
+     */
+    @Override
+    public Boolean insertByBo(QmBatchBo bo) {
+        QmBatch add = MapstructUtils.convert(bo, QmBatch.class);
+        validEntityBeforeSave(add);
+        boolean flag = baseMapper.insert(add) > 0;
+        if (flag) {
+            bo.setId(add.getId());
+        }
+        return flag;
+    }
+
+    /**
+     * 淇敼妫�楠屾壒娆�
+     *
+     * @param bo 妫�楠屾壒娆�
+     * @return 鏄惁淇敼鎴愬姛
+     */
+    @Override
+    public Boolean updateByBo(QmBatchBo bo) {
+        QmBatch update = MapstructUtils.convert(bo, QmBatch.class);
+        validEntityBeforeSave(update);
+        return baseMapper.updateById(update) > 0;
+    }
+
+    /**
+     * 淇濆瓨鍓嶇殑鏁版嵁鏍¢獙
+     */
+    private void validEntityBeforeSave(QmBatch entity){
+        //TODO 鍋氫竴浜涙暟鎹牎楠�,濡傚敮涓�绾︽潫
+    }
+
+    /**
+     * 鏍¢獙骞舵壒閲忓垹闄ゆ楠屾壒娆′俊鎭�
+     *
+     * @param ids     寰呭垹闄ょ殑涓婚敭闆嗗悎
+     * @param isValid 鏄惁杩涜鏈夋晥鎬ф牎楠�
+     * @return 鏄惁鍒犻櫎鎴愬姛
+     */
+    @Override
+    public Boolean deleteWithValidByIds(Collection<String> ids, Boolean isValid) {
+        if(isValid){
+            //TODO 鍋氫竴浜涗笟鍔′笂鐨勬牎楠�,鍒ゆ柇鏄惁闇�瑕佹牎楠�
+        }
+        return baseMapper.deleteByIds(ids) > 0;
+    }
+}
diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/resources/mapper/qm/QmBatchMapper.xml b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/resources/mapper/qm/QmBatchMapper.xml
new file mode 100644
index 0000000..0092e5f
--- /dev/null
+++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/resources/mapper/qm/QmBatchMapper.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="org.dromara.qm.mapper.QmBatchMapper">
+</mapper>
diff --git a/TimescaleDB-Utils/HELP.md b/TimescaleDB-Utils/HELP.md
new file mode 100755
index 0000000..b7509c2
--- /dev/null
+++ b/TimescaleDB-Utils/HELP.md
@@ -0,0 +1,10 @@
+# Getting Started
+
+### Reference Documentation
+
+For further reference, please consider the following sections:
+
+* [Official Apache Maven documentation](https://maven.apache.org/guides/index.html)
+* [Spring Boot Maven Plugin Reference Guide](https://docs.spring.io/spring-boot/docs/2.6.13/maven-plugin/reference/html/)
+* [Create an OCI image](https://docs.spring.io/spring-boot/docs/2.6.13/maven-plugin/reference/html/#build-image)
+
diff --git a/TimescaleDB-Utils/pom.xml b/TimescaleDB-Utils/pom.xml
new file mode 100755
index 0000000..2088e09
--- /dev/null
+++ b/TimescaleDB-Utils/pom.xml
@@ -0,0 +1,98 @@
+<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>com.shlb</groupId>
+    <artifactId>TimescaleDB-Utils</artifactId>
+    <version>0.0.1-SNAPSHOT</version>
+    <name>TimescaleDB-Utils</name>
+    <description>TimescaleDB-Utils</description>
+    <properties>
+        <java.version>1.8</java.version>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+        <spring-boot.version>2.6.13</spring-boot.version>
+    </properties>
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus-boot-starter</artifactId>
+            <version>3.5.3.1</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.postgresql</groupId>
+            <artifactId>postgresql</artifactId>
+            <scope>runtime</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-redis</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>fastjson</artifactId>
+            <version>1.2.83</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-dependencies</artifactId>
+                <version>${spring-boot.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>3.8.1</version>
+                <configuration>
+                    <source>1.8</source>
+                    <target>1.8</target>
+                    <encoding>UTF-8</encoding>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <version>${spring-boot.version}</version>
+                <configuration>
+                    <mainClass>com.shlb.timescaledbutils.TimescaleDbUtilsApplication</mainClass>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>repackage</id>
+                        <goals>
+                            <goal>repackage</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/TimescaleDB-Utils/readMe.md b/TimescaleDB-Utils/readMe.md
new file mode 100755
index 0000000..1544f02
--- /dev/null
+++ b/TimescaleDB-Utils/readMe.md
@@ -0,0 +1,509 @@
+
+鐗瑰埆娉ㄦ剰锛�
+postgres鏃跺尯闂锛屽畨瑁呭悗濡傛灉鏄疷TC瑕佹敼鎴� Asia/Shanghai(+8)
+
+
+鍔ㄦ�佽繍琛屽弬鏁� my_new_key - > 闇�瑕佸瓨鍌ㄦ暟鎹簱鐨勪笂绾ey锛� sync-interval-ms - > 瀛樺偍鏃堕棿闂撮殧   
+java -jar TimescaleDB-Utils.jar \
+--spring.redis.queue-key=my_new_key \
+--spring.redis.sync-interval-ms=2000
+
+
+
+//鍗锋帴鏈鸿〃
+-- 鍒涘缓涓昏〃
+CREATE TABLE roller_time_data (
+-- 鏃跺簭琛ㄥ繀闇�鐨勫瓧娈�
+time TIMESTAMPTZ NOT NULL,
+key TEXT NOT NULL,  -- 璁句负 NOT NULL 鍥犱负鎮ㄦ湁鍥哄畾鐨� key 鍒楄〃
+shift INTEGER,      -- 鐝 (1浣嶆暟瀛�)
+equ_no INTEGER,     -- 璁惧鍙� (3浣嶆暟瀛�)
+
+    -- 鎮ㄧ殑涓氬姟瀛楁
+    online INTEGER,                    -- 缃戠粶鐘舵��(0寮傚父 1姝e父)
+    qty DOUBLE PRECISION,             -- 浜ч噺
+    bad_qty DOUBLE PRECISION,         -- 鍓旈櫎浜ч噺
+    lvbang_val DOUBLE PRECISION,      -- 婊ゆ娑堣��
+    juanyanzhi_val DOUBLE PRECISION,  -- 鍗风儫绾告秷鑰�
+    shuisongzhi_val DOUBLE PRECISION, -- 姘存澗绾告秷鑰�
+    run_time DOUBLE PRECISION,        -- 杩愯鏃堕棿
+    stop_time DOUBLE PRECISION,       -- 鍋滄満鏃堕棿
+    stop_times INTEGER,               -- 鍋滄満娆℃暟
+    speed INTEGER,                    -- 杞﹂��
+    run_status INTEGER,               -- 杩愯鐘舵��(-1鏂綉 0鍋滄 1浣庨�熻繍琛� 2姝e父杩愯)
+    cy DOUBLE PRECISION,              -- 鍌ㄧ儫璁惧鍌ㄩ噺
+    cy_cs INTEGER,                    -- 鍌ㄧ儫璁惧杞﹂��(1-鍖呰鏈� 6-鍗风儫鏈�)
+    cy_online TEXT,                   -- (22-鍗风儫鏈鸿繍琛岀姸鎬� 23-鍖呰鏈鸿繍琛岀姸鎬�)
+    rec_qty1 DOUBLE PRECISION,        -- 鎺ユ敹鏈洪噺
+    rec_qty2 DOUBLE PRECISION         -- 鎺ユ敹鏈洪噺2
+);
+
+-- 灏嗚〃杞崲涓鸿秴琛紙鎸夊ぉ鍒嗗尯锛屾寜key杩涜绌洪棿鍒嗗尯锛�
+SELECT create_hypertable(
+'roller_time_data',
+'time',
+chunk_time_interval => INTERVAL '1 day'
+);
+
+-- 娣诲姞绌洪棿鍒嗗尯缁村害锛堟寜key鍒嗗尯锛�
+-- 鍒嗘垚 8 涓垎鍖猴紝姣忎釜鍒嗗尯澶х害鍖呭惈 4-5 涓� key
+SELECT add_dimension(
+'roller_time_data',
+'key',
+number_partitions => 8
+);
+
+-- 鍒涘缓绱㈠紩浠ユ彁楂樻煡璇㈡�ц兘
+-- 澶嶅悎绱㈠紩锛岄�傚悎鎸夋椂闂村拰key鏌ヨ
+CREATE INDEX idx_roller_time_data_time_key ON roller_time_data (time DESC, key);
+-- 鍗曠嫭绱㈠紩锛岄�傚悎鎸塳ey鏌ヨ
+CREATE INDEX idx_roller_time_data_key_time ON roller_time_data (key, time DESC);
+-- 涓氬姟鏌ヨ甯哥敤瀛楁绱㈠紩
+CREATE INDEX idx_roller_time_data_run_status_key ON roller_time_data (run_status, key, time DESC);
+CREATE INDEX idx_roller_time_data_online_key ON roller_time_data (online, key, time DESC);
+CREATE INDEX idx_roller_time_data_speed_key ON roller_time_data (speed, key, time DESC);
+
+-- 鐝鍜岃澶囧彿绱㈠紩
+CREATE INDEX idx_roller_time_data_shift ON roller_time_data (shift);
+CREATE INDEX idx_roller_time_data_equ_no ON roller_time_data (equ_no);
+
+-- 鍙�夛細濡傛灉鏌愪簺璁惧鏁版嵁閲忕壒鍒ぇ锛屽彲浠ヤ负鐗瑰畾璁惧鍒涘缓涓撶敤绱㈠紩
+-- CREATE INDEX idx_roller_1101 ON roller_time_data (time DESC) WHERE key = '1101';
+
+
+
+
+
+
+//鍖呰鏈�
+-- 鍒涘缓涓昏〃
+CREATE TABLE packer_time_data (
+-- 鏃跺簭琛ㄥ繀闇�鐨勫瓧娈�
+time TIMESTAMPTZ NOT NULL,
+key TEXT NOT NULL,
+shift INTEGER,      -- 鐝 (1浣嶆暟瀛�)
+equ_no INTEGER,     -- 璁惧鍙� (3浣嶆暟瀛�)
+
+    -- 鎮ㄧ殑涓氬姟瀛楁
+    online INTEGER,                    -- 缃戠粶鐘舵��(0寮傚父锛�1姝e父)
+    qty DOUBLE PRECISION,              -- 浜ч噺
+    bad_qty DOUBLE PRECISION,          -- 鍓旈櫎浜ч噺
+    xiaohemo_val DOUBLE PRECISION,     -- 灏忕洅鑶滄秷鑰�
+    tiaohemo_val DOUBLE PRECISION,     -- 鏉$洅鑶滄秷鑰�
+    xiaohezhi_val DOUBLE PRECISION,    -- 灏忕洅绾告秷鑰�
+    tiaohezhi_val DOUBLE PRECISION,    -- 鏉$洅绾告秷鑰�
+    neichenzhi_val DOUBLE PRECISION,   -- 鍐呰‖绾告秷鑰�
+    run_time DOUBLE PRECISION,         -- 杩愯鏃堕棿
+    stop_time DOUBLE PRECISION,        -- 鍋滄満鏃堕棿
+    stop_times INTEGER,                -- 鍋滄満娆℃暟
+    speed INTEGER,                     -- 杞﹂��
+    run_status INTEGER,                -- 杩愯鐘舵��(-1 鏂綉 0鍋滄 1浣庨�熻繍琛� 2姝e父杩愯)
+    ts_qty DOUBLE PRECISION,           -- 鎻愬崌鏈轰骇閲�
+    main_qty DOUBLE PRECISION,         -- 涓绘満浜ч噺锛堝皬鍖呮満锛�
+    main_bad_qty DOUBLE PRECISION,     -- 涓绘満鍓旈櫎閲�
+    tbj_qty DOUBLE PRECISION,          -- 閫忓寘鏈轰骇閲�
+    tbj_gd_qty DOUBLE PRECISION,       -- 閫忓寘鏈哄墧闄ゅソ鍖�
+    tbj_bad_qty DOUBLE PRECISION,      -- 閫忓寘鏈哄墧闄ゅ潖鍖�
+    pbj_qty DOUBLE PRECISION           -- 鎺掑寘鏈轰骇閲�
+);
+
+-- 灏嗚〃杞崲涓鸿秴琛紙鎸夊ぉ鍒嗗尯锛�
+SELECT create_hypertable(
+'packer_time_data',
+'time',
+chunk_time_interval => INTERVAL '1 day'
+);
+
+-- 娣诲姞绌洪棿鍒嗗尯缁村害锛堟寜key鍒嗗尯锛屽垎鎴�8涓垎鍖猴級
+SELECT add_dimension(
+'packer_time_data',
+'key',
+number_partitions => 8
+);
+
+-- 鍒涘缓澶嶅悎绱㈠紩锛堟椂闂�+key锛�
+CREATE INDEX idx_packer_time_data_time_key ON packer_time_data (time DESC, key);
+
+-- 鍒涘缓鍙嶅悜澶嶅悎绱㈠紩锛坘ey+鏃堕棿锛�
+CREATE INDEX idx_packer_time_data_key_time ON packer_time_data (key, time DESC);
+
+-- 鍒涘缓杩愯鐘舵�佺浉鍏崇储寮�
+CREATE INDEX idx_packer_run_status_key ON packer_time_data (run_status, key, time DESC);
+
+-- 鍒涘缓缃戠粶鐘舵�佺储寮�
+CREATE INDEX idx_packer_online_key ON packer_time_data (online, key, time DESC);
+
+-- 鍒涘缓杞﹂�熺储寮�
+CREATE INDEX idx_packer_speed_key ON packer_time_data (speed, key, time DESC);
+
+-- 鍒涘缓浜ч噺鐩稿叧绱㈠紩锛堝父鐢ㄧ粺璁℃煡璇級
+CREATE INDEX idx_packer_qty_key ON packer_time_data (qty, key, time DESC);
+
+-- 鍒涘缓鍋滄満鐩稿叧绱㈠紩
+CREATE INDEX idx_packer_stop_key ON packer_time_data (stop_times, key, time DESC);
+
+-- 鐝鍜岃澶囧彿绱㈠紩
+CREATE INDEX idx_packer_time_data_shift ON packer_time_data (shift);
+CREATE INDEX idx_packer_time_data_equ_no ON packer_time_data (equ_no);
+
+
+
+
+
+//瑁呯鏈�
+-- 鍒涘缓涓昏〃
+CREATE TABLE box_time_data (
+-- 鏃跺簭琛ㄥ繀闇�鐨勫瓧娈�
+time TIMESTAMPTZ NOT NULL,
+key TEXT NOT NULL,
+shift INTEGER,      -- 鐝 (1浣嶆暟瀛�)
+equ_no INTEGER,     -- 璁惧鍙� (3浣嶆暟瀛�)
+
+    -- 鎮ㄧ殑涓氬姟瀛楁
+    online INTEGER,                    -- 缃戠粶鐘舵�� 0寮傚父锛�1姝e父
+    qty1 DOUBLE PRECISION,             -- 1#瑁呭皝绠辨満浜ч噺
+    pbj_qty DOUBLE PRECISION           -- 鎺掑寘鏈轰骇閲�
+);
+
+-- 灏嗚〃杞崲涓鸿秴琛紙鎸夊ぉ鍒嗗尯锛�
+SELECT create_hypertable(
+'box_time_data',
+'time',
+chunk_time_interval => INTERVAL '1 day'
+);
+
+-- 娣诲姞绌洪棿鍒嗗尯缁村害锛堟寜key鍒嗗尯锛屽垎鎴�8涓垎鍖猴級
+SELECT add_dimension(
+'box_time_data',
+'key',
+number_partitions => 8
+);
+
+-- 鍒涘缓澶嶅悎绱㈠紩锛堟椂闂�+key锛� - 涓绘煡璇㈡ā寮�
+CREATE INDEX idx_box_time_data_time_key ON box_time_data (time DESC, key);
+
+-- 鍒涘缓鍙嶅悜澶嶅悎绱㈠紩锛坘ey+鏃堕棿锛� - 鎸夎澶囨煡璇�
+CREATE INDEX idx_box_time_data_key_time ON box_time_data (key, time DESC);
+
+-- 鍒涘缓缃戠粶鐘舵�佺储寮�
+CREATE INDEX idx_box_online_key ON box_time_data (online, key, time DESC);
+
+-- 鍒涘缓浜ч噺绱㈠紩锛堝父鐢ㄤ簬缁熻鏌ヨ锛�
+CREATE INDEX idx_box_qty1_key ON box_time_data (qty1, key, time DESC);
+CREATE INDEX idx_box_pbj_qty_key ON box_time_data (pbj_qty, key, time DESC);
+
+-- 鍒涘缓澶嶅悎浜ч噺绱㈠紩锛堝悓鏃舵煡璇袱涓骇閲忓瓧娈碉級
+CREATE INDEX idx_box_qtys_key ON box_time_data (qty1, pbj_qty, key, time DESC);
+
+-- 鐝鍜岃澶囧彿绱㈠紩
+CREATE INDEX idx_box_time_data_shift ON box_time_data (shift);
+CREATE INDEX idx_box_time_data_equ_no ON box_time_data (equ_no);
+
+
+
+
+
+//鎴愬瀷鏈�
+-- 鍒涘缓涓昏〃
+CREATE TABLE makeup_time_data (
+-- 鏃跺簭琛ㄥ繀闇�鐨勫瓧娈�
+time TIMESTAMPTZ NOT NULL,
+key TEXT NOT NULL,
+shift INTEGER,      -- 鐝 (1浣嶆暟瀛�)
+equ_no INTEGER,     -- 璁惧鍙� (3浣嶆暟瀛�)
+
+    -- 鎮ㄧ殑涓氬姟瀛楁
+    online INTEGER,                    -- 缃戠粶鐘舵�� 0寮傚父锛�1姝e父
+    qty DOUBLE PRECISION,              -- 浜ч噺
+    bad_qty DOUBLE PRECISION,          -- 鍓旈櫎浜ч噺
+    panzhi_val DOUBLE PRECISION,       -- 鐩樼焊娑堣��
+    run_time DOUBLE PRECISION,         -- 杩愯鏃堕棿
+    stop_time DOUBLE PRECISION,        -- 鍋滄満鏃堕棿
+    stop_times INTEGER,                -- 鍋滄満娆℃暟
+    speed INTEGER                      -- 杞﹂��
+);
+
+-- 灏嗚〃杞崲涓鸿秴琛紙鎸夊ぉ鍒嗗尯锛�
+SELECT create_hypertable(
+'makeup_time_data',
+'time',
+chunk_time_interval => INTERVAL '1 day'
+);
+
+-- 娣诲姞绌洪棿鍒嗗尯缁村害锛堟寜key鍒嗗尯锛屽垎鎴�8涓垎鍖猴級
+SELECT add_dimension(
+'makeup_time_data',
+'key',
+number_partitions => 8
+);
+
+-- 鍒涘缓涓绘煡璇㈠鍚堢储寮曪紙鏃堕棿+key锛�
+CREATE INDEX idx_makeup_time_data_time_key ON makeup_time_data (time DESC, key);
+
+-- 鍒涘缓鎸夎澶囨煡璇㈠鍚堢储寮曪紙key+鏃堕棿锛�
+CREATE INDEX idx_makeup_time_data_key_time ON makeup_time_data (key, time DESC);
+
+-- 鍒涘缓缃戠粶鐘舵�佺储寮�
+CREATE INDEX idx_makeup_online_key ON makeup_time_data (online, key, time DESC);
+
+-- 鐝鍜岃澶囧彿绱㈠紩
+CREATE INDEX idx_makeup_time_data_shift ON makeup_time_data (shift);
+CREATE INDEX idx_makeup_time_data_equ_no ON makeup_time_data (equ_no);
+
+
+
+
+
+//鍙戝皠鏈�
+-- 鍒涘缓涓昏〃
+CREATE TABLE trans_time_data (
+-- 鏃跺簭琛ㄥ繀闇�鐨勫瓧娈�
+time TIMESTAMPTZ NOT NULL,
+key TEXT NOT NULL,
+shift INTEGER,      -- 鐝 (1浣嶆暟瀛�)
+equ_no INTEGER,     -- 璁惧鍙� (3浣嶆暟瀛�)
+
+    -- 鎮ㄧ殑涓氬姟瀛楁
+    online INTEGER,                    -- 缃戠粶鐘舵�� 0寮傚父锛�1姝e父
+    
+    -- 绠¢亾婊ゆ璁℃暟锛�1-10鍙风閬擄級
+    p_qty1 DOUBLE PRECISION,          -- 绠¢亾1婊ゆ璁℃暟
+    p_qty2 DOUBLE PRECISION,          -- 绠¢亾2婊ゆ璁℃暟
+    p_qty3 DOUBLE PRECISION,          -- 绠¢亾3婊ゆ璁℃暟
+    p_qty4 DOUBLE PRECISION,          -- 绠¢亾4婊ゆ璁℃暟
+    p_qty5 DOUBLE PRECISION,          -- 绠¢亾5婊ゆ璁℃暟
+    p_qty6 DOUBLE PRECISION,          -- 绠¢亾6婊ゆ璁℃暟
+    p_qty7 DOUBLE PRECISION,          -- 绠¢亾7婊ゆ璁℃暟
+    p_qty8 DOUBLE PRECISION,          -- 绠¢亾8婊ゆ璁℃暟
+    p_qty9 DOUBLE PRECISION,          -- 绠¢亾9婊ゆ璁℃暟
+    p_qty10 DOUBLE PRECISION,         -- 绠¢亾10婊ゆ璁℃暟
+    
+    -- 绠¢亾婊ゆ閫熷害锛�1-10鍙风閬擄級
+    speed1 DOUBLE PRECISION,          -- 绠¢亾1婊ゆ閫熷害
+    speed2 DOUBLE PRECISION,          -- 绠¢亾2婊ゆ閫熷害
+    speed3 DOUBLE PRECISION,          -- 绠¢亾3婊ゆ閫熷害
+    speed4 DOUBLE PRECISION,          -- 绠¢亾4婊ゆ閫熷害
+    speed5 DOUBLE PRECISION,          -- 绠¢亾5婊ゆ閫熷害
+    speed6 DOUBLE PRECISION,          -- 绠¢亾6婊ゆ閫熷害
+    speed7 DOUBLE PRECISION,          -- 绠¢亾7婊ゆ閫熷害
+    speed8 DOUBLE PRECISION,          -- 绠¢亾8婊ゆ閫熷害
+    speed9 DOUBLE PRECISION,          -- 绠¢亾9婊ゆ閫熷害
+    speed10 DOUBLE PRECISION,         -- 绠¢亾10婊ゆ閫熷害
+    
+    -- 鍗哥洏鏈虹浉鍏�
+    xp_state INTEGER,                 -- 鍗哥洏鏈虹姸鎬�
+    tray_qty1 DOUBLE PRECISION        -- 鍗哥洏鏈轰骇閲�
+);
+
+-- 灏嗚〃杞崲涓鸿秴琛紙鎸夊ぉ鍒嗗尯锛�
+SELECT create_hypertable(
+'trans_time_data',
+'time',
+chunk_time_interval => INTERVAL '1 day'
+);
+
+-- 娣诲姞绌洪棿鍒嗗尯缁村害锛堟寜key鍒嗗尯锛屽垎鎴�8涓垎鍖猴級
+SELECT add_dimension(
+'trans_time_data',
+'key',
+number_partitions => 8
+);
+
+-- 鍒涘缓涓绘煡璇㈠鍚堢储寮�
+CREATE INDEX idx_trans_time_data_time_key ON trans_time_data (time DESC, key);
+CREATE INDEX idx_trans_time_data_key_time ON trans_time_data (key, time DESC);
+-- 鐝鍜岃澶囧彿绱㈠紩
+CREATE INDEX idx_trans_time_data_shift ON trans_time_data (shift);
+CREATE INDEX idx_trans_time_data_equ_no ON trans_time_data (equ_no);
+
+-- 鍒涘缓缃戠粶鐘舵�佺储寮�
+CREATE INDEX idx_trans_online_key ON trans_time_data (online, key, time DESC);
+
+-- 鍒涘缓鍗哥洏鏈虹姸鎬佺储寮�
+CREATE INDEX idx_trans_xp_state_key ON trans_time_data (xp_state, key, time DESC);
+
+
+
+
+
+//鎻愬崌鏈�
+-- 鍒涘缓涓昏〃
+CREATE TABLE hoister_time_data (
+-- 鏃跺簭琛ㄥ繀闇�鐨勫瓧娈�
+time TIMESTAMPTZ NOT NULL,
+key TEXT NOT NULL,
+shift INTEGER,      -- 鐝 (1浣嶆暟瀛�)
+equ_no INTEGER,     -- 璁惧鍙� (3浣嶆暟瀛�)
+
+    -- 鍩虹瀛楁
+    online INTEGER,                    -- 缃戠粶鐘舵�� 0寮傚父锛�1姝e父
+    qty DOUBLE PRECISION,              -- 浜ч噺锛堟�讳骇閲忥紵锛�
+    
+    -- 鎻愬崌鏈虹姸鎬侊紙1-12鍙凤級
+    t_state1 DOUBLE PRECISION,         -- 1#鎻愬崌鏈虹姸鎬�
+    t_state2 DOUBLE PRECISION,         -- 2#鎻愬崌鏈虹姸鎬�
+    t_state3 DOUBLE PRECISION,         -- 3#鎻愬崌鏈虹姸鎬�
+    t_state4 DOUBLE PRECISION,         -- 4#鎻愬崌鏈虹姸鎬�
+    t_state5 DOUBLE PRECISION,         -- 5#鎻愬崌鏈虹姸鎬�
+    t_state6 DOUBLE PRECISION,         -- 6#鎻愬崌鏈虹姸鎬�
+    t_state7 DOUBLE PRECISION,         -- 7#鎻愬崌鏈虹姸鎬�
+    t_state8 DOUBLE PRECISION,         -- 8#鎻愬崌鏈虹姸鎬�
+    t_state9 DOUBLE PRECISION,         -- 9#鎻愬崌鏈虹姸鎬�
+    t_state10 DOUBLE PRECISION,        -- 10#鎻愬崌鏈虹姸鎬�
+    t_state11 DOUBLE PRECISION,        -- 11#鎻愬崌鏈虹姸鎬�
+    t_state12 DOUBLE PRECISION,        -- 12#鎻愬崌鏈虹姸鎬�
+    
+    -- 鎺掑寘鏈虹姸鎬侊紙1-4鍙凤級
+    p_state1 DOUBLE PRECISION,         -- 1#鎺掑寘鏈虹姸鎬�
+    p_state2 DOUBLE PRECISION,         -- 2#鎺掑寘鏈虹姸鎬�
+    p_state3 DOUBLE PRECISION,         -- 3#鎺掑寘鏈虹姸鎬�
+    p_state4 DOUBLE PRECISION,         -- 4#鎺掑寘鏈虹姸鎬�
+    
+    -- 鎻愬崌鏈轰骇閲忥紙1-12鍙凤級
+    t_qty1 DOUBLE PRECISION,           -- 1#鎻愬崌鏈轰骇閲�
+    t_qty2 DOUBLE PRECISION,           -- 2#鎻愬崌鏈轰骇閲�
+    t_qty3 DOUBLE PRECISION,           -- 3#鎻愬崌鏈轰骇閲�
+    t_qty4 DOUBLE PRECISION,           -- 4#鎻愬崌鏈轰骇閲�
+    t_qty5 DOUBLE PRECISION,           -- 5#鎻愬崌鏈轰骇閲�
+    t_qty6 DOUBLE PRECISION,           -- 6#鎻愬崌鏈轰骇閲�
+    t_qty7 DOUBLE PRECISION,           -- 7#鎻愬崌鏈轰骇閲�
+    t_qty8 DOUBLE PRECISION,           -- 8#鎻愬崌鏈轰骇閲�
+    t_qty9 DOUBLE PRECISION,           -- 9#鎻愬崌鏈轰骇閲�
+    t_qty10 DOUBLE PRECISION,          -- 10#鎻愬崌鏈轰骇閲�
+    t_qty11 DOUBLE PRECISION,          -- 11#鎻愬崌鏈轰骇閲�
+    t_qty12 DOUBLE PRECISION,          -- 12#鎻愬崌鏈轰骇閲�
+    
+    -- 鎺掑寘鏈轰骇閲忥紙1-4鍙凤級
+    p_qty1 DOUBLE PRECISION,           -- 1#鎺掑寘鏈轰骇閲�
+    p_qty2 DOUBLE PRECISION,           -- 2#鎺掑寘鏈轰骇閲�
+    p_qty3 DOUBLE PRECISION,           -- 3#鎺掑寘鏈轰骇閲�
+    p_qty4 DOUBLE PRECISION            -- 4#鎺掑寘鏈轰骇閲�
+);
+
+-- 灏嗚〃杞崲涓鸿秴琛紙鎸夊ぉ鍒嗗尯锛�
+SELECT create_hypertable(
+'hoister_time_data',
+'time',
+chunk_time_interval => INTERVAL '1 day'
+);
+
+-- 娣诲姞绌洪棿鍒嗗尯缁村害锛堟寜key鍒嗗尯锛屽垎鎴�8涓垎鍖猴級
+SELECT add_dimension(
+'hoister_time_data',
+'key',
+number_partitions => 8
+);
+
+-- 鍒涘缓涓绘煡璇㈠鍚堢储寮�
+CREATE INDEX idx_hoister_time_data_time_key ON hoister_time_data (time DESC, key);
+CREATE INDEX idx_hoister_time_data_key_time ON hoister_time_data (key, time DESC);
+
+-- 鍒涘缓缃戠粶鐘舵�佺储寮�
+CREATE INDEX idx_hoister_online_key ON hoister_time_data (online, key, time DESC);
+
+-- 鍒涘缓鎬讳骇閲忕储寮�
+CREATE INDEX idx_hoister_qty_key ON hoister_time_data (qty, key, time DESC);
+
+-- 鍒涘缓鎻愬崌鏈虹姸鎬侀�氱敤绱㈠紩锛堟渶甯哥敤鐨勫嚑涓級
+CREATE INDEX idx_hoister_t_state1_key ON hoister_time_data (t_state1, key, time DESC);
+CREATE INDEX idx_hoister_t_state2_key ON hoister_time_data (t_state2, key, time DESC);
+CREATE INDEX idx_hoister_t_state3_key ON hoister_time_data (t_state3, key, time DESC);
+
+-- 鍒涘缓鎺掑寘鏈虹姸鎬佺储寮�
+CREATE INDEX idx_hoister_p_state1_key ON hoister_time_data (p_state1, key, time DESC);
+CREATE INDEX idx_hoister_p_state2_key ON hoister_time_data (p_state2, key, time DESC);
+
+-- 鐝鍜岃澶囧彿绱㈠紩
+CREATE INDEX idx_hoister_time_data_shift ON hoister_time_data (shift);
+CREATE INDEX idx_hoister_time_data_equ_no ON hoister_time_data (equ_no);
+
+
+
+
+//鍠備笣鏈�
+-- 鍒涘缓涓昏〃
+CREATE TABLE feedmatch_time_data (
+time TIMESTAMPTZ NOT NULL,
+key TEXT NOT NULL,
+shift INTEGER,      -- 鐝 (1浣嶆暟瀛�)
+equ_no INTEGER,     -- 璁惧鍙� (3浣嶆暟瀛�)
+
+    -- 鏁版嵁鏇存柊鏃堕棿锛堝瓧绗︿覆鏍煎紡锛�
+    dac_up_time TEXT,                 -- 鏁版嵁鏇存柊鏃堕棿 鏍煎紡锛�'2024-11-29 09:13:43'
+    
+    -- 鍠備笣鏈哄搴旂殑鍌ㄤ笣鏌滐紙1#-4#鍠備笣鏈猴紝姣忎釜瀵瑰簲2涓偍涓濇煖锛�- 鏁村瀷
+    fs11 INTEGER,                     -- 1#鍠備笣鏈哄搴旂殑绗竴涓偍涓濇煖
+    fs12 INTEGER,                     -- 1#鍠備笣鏈哄搴旂殑绗簩涓偍涓濇煖
+    fs21 INTEGER,                     -- 2#鍠備笣鏈哄搴旂殑绗竴涓偍涓濇煖
+    fs22 INTEGER,                     -- 2#鍠備笣鏈哄搴旂殑绗簩涓偍涓濇煖
+    fs31 INTEGER,                     -- 3#鍠備笣鏈哄搴旂殑绗竴涓偍涓濇煖
+    fs32 INTEGER,                     -- 3#鍠備笣鏈哄搴旂殑绗簩涓偍涓濇煖
+    fs41 INTEGER,                     -- 4#鍠備笣鏈哄搴旂殑绗竴涓偍涓濇煖
+    fs42 INTEGER,                     -- 4#鍠備笣鏈哄搴旂殑绗簩涓偍涓濇煖
+    
+    -- 鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬擄紙1#-12#鏈虹粍锛�- 鏁村瀷
+    pipe01 INTEGER,                   -- 1#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬�
+    pipe02 INTEGER,                   -- 2#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬�
+    pipe03 INTEGER,                   -- 3#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬�
+    pipe04 INTEGER,                   -- 4#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬�
+    pipe05 INTEGER,                   -- 5#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬�
+    pipe06 INTEGER,                   -- 6#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬�
+    pipe07 INTEGER,                   -- 7#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬�
+    pipe08 INTEGER,                   -- 8#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬�
+    pipe09 INTEGER,                   -- 9#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬�
+    pipe10 INTEGER,                   -- 10#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬�
+    pipe11 INTEGER,                   -- 11#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬�
+    pipe12 INTEGER,                   -- 12#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬�
+    
+    -- 鍠備笣鏈虹姸鎬� - 鏁村瀷
+    wsj_state INTEGER                 -- 鍠備笣鏈虹姸鎬� 1-杩炴帴 0-鏂紑
+);
+
+-- 灏嗚〃杞崲涓鸿秴琛紙鎸夊ぉ鍒嗗尯锛�
+SELECT create_hypertable(
+'feedmatch_time_data',
+'time',
+chunk_time_interval => INTERVAL '1 day'
+);
+
+-- 娣诲姞绌洪棿鍒嗗尯缁村害锛堟寜key鍒嗗尯锛屽垎鎴�8涓垎鍖猴級
+SELECT add_dimension(
+'feedmatch_time_data',
+'key',
+number_partitions => 8
+);
+
+-- 鍒涘缓涓绘煡璇㈠鍚堢储寮�
+CREATE INDEX idx_feedmatch_time_data_time_key ON feedmatch_time_data (time DESC, key);
+CREATE INDEX idx_feedmatch_time_data_key_time ON feedmatch_time_data (key, time DESC);
+
+-- 鐝鍜岃澶囧彿绱㈠紩
+CREATE INDEX idx_feedmatch_time_data_shift ON feedmatch_time_data (shift);
+CREATE INDEX idx_feedmatch_time_data_equ_no ON feedmatch_time_data (equ_no);
+
+//鍚勮〃娣诲姞閫氱敤瀛楁
+ALTER TABLE public.trans_time_data
+ADD COLUMN IF NOT EXISTS create_dept integer,
+ADD COLUMN IF NOT EXISTS create_by bigint,
+ADD COLUMN IF NOT EXISTS create_time timestamp,
+ADD COLUMN IF NOT EXISTS update_by bigint,
+ADD COLUMN IF NOT EXISTS update_time timestamp,
+ADD COLUMN IF NOT EXISTS remark varchar(255);
+
+
+
+// 鍘嗗彶鏁版嵁鏇存柊鑴氭湰锛堟墽琛屼竴娆″嵆鍙級
+-- 鏇存柊 roller_time_data
+UPDATE roller_time_data SET shift = CAST(SUBSTRING(key FROM 1 FOR 1) AS INTEGER), equ_no = CAST(SUBSTRING(key FROM 2 FOR 3) AS INTEGER) WHERE shift IS NULL;
+-- 鏇存柊 packer_time_data
+UPDATE packer_time_data SET shift = CAST(SUBSTRING(key FROM 1 FOR 1) AS INTEGER), equ_no = CAST(SUBSTRING(key FROM 2 FOR 3) AS INTEGER) WHERE shift IS NULL;
+-- 鏇存柊 box_time_data
+UPDATE box_time_data SET shift = CAST(SUBSTRING(key FROM 1 FOR 1) AS INTEGER), equ_no = CAST(SUBSTRING(key FROM 2 FOR 3) AS INTEGER) WHERE shift IS NULL;
+-- 鏇存柊 makeup_time_data
+UPDATE makeup_time_data SET shift = CAST(SUBSTRING(key FROM 1 FOR 1) AS INTEGER), equ_no = CAST(SUBSTRING(key FROM 2 FOR 3) AS INTEGER) WHERE shift IS NULL;
+-- 鏇存柊 trans_time_data
+UPDATE trans_time_data SET shift = CAST(SUBSTRING(key FROM 1 FOR 1) AS INTEGER), equ_no = CAST(SUBSTRING(key FROM 2 FOR 3) AS INTEGER) WHERE shift IS NULL;
+-- 鏇存柊 hoister_time_data
+UPDATE hoister_time_data SET shift = CAST(SUBSTRING(key FROM 1 FOR 1) AS INTEGER), equ_no = CAST(SUBSTRING(key FROM 2 FOR 3) AS INTEGER) WHERE shift IS NULL;
+-- 鏇存柊 feedmatch_time_data
+UPDATE feedmatch_time_data SET shift = CAST(SUBSTRING(key FROM 1 FOR 1) AS INTEGER), equ_no = CAST(SUBSTRING(key FROM 2 FOR 3) AS INTEGER) WHERE shift IS NULL;
diff --git a/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/TimescaleDbUtilsApplication.java b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/TimescaleDbUtilsApplication.java
new file mode 100755
index 0000000..b4a7afc
--- /dev/null
+++ b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/TimescaleDbUtilsApplication.java
@@ -0,0 +1,23 @@
+package com.shlb.timescaledbutils;
+
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+import javax.annotation.PostConstruct;
+import java.util.TimeZone;
+
+@SpringBootApplication
+@MapperScan("com.shlb.timescaledbutils.mapper")
+public class TimescaleDbUtilsApplication {
+
+    public static void main(String[] args) {
+        SpringApplication.run(TimescaleDbUtilsApplication.class, args);
+    }
+
+    @PostConstruct
+    void started() {
+        TimeZone.setDefault(TimeZone.getTimeZone("Asia/Shanghai"));
+    }
+
+}
diff --git a/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/config/MybatisPlusConfig.java b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/config/MybatisPlusConfig.java
new file mode 100755
index 0000000..3dc34dc
--- /dev/null
+++ b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/config/MybatisPlusConfig.java
@@ -0,0 +1,9 @@
+package com.shlb.timescaledbutils.config;
+
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+@MapperScan("com.shlb.timescaledbutils.mapper")
+public class MybatisPlusConfig {
+}
diff --git a/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/config/StartupStatusPrinter.java b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/config/StartupStatusPrinter.java
new file mode 100755
index 0000000..b8561dd
--- /dev/null
+++ b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/config/StartupStatusPrinter.java
@@ -0,0 +1,53 @@
+package com.shlb.timescaledbutils.config;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.ApplicationArguments;
+import org.springframework.boot.ApplicationRunner;
+import org.springframework.core.env.Environment;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.stereotype.Component;
+
+import javax.sql.DataSource;
+import java.sql.Connection;
+
+@Component
+@Slf4j
+public class StartupStatusPrinter implements ApplicationRunner {
+
+    @Autowired
+    private Environment environment;
+
+    @Autowired
+    private DataSource dataSource;
+
+    @Autowired
+    private RedisConnectionFactory redisConnectionFactory;
+
+    @Override
+    public void run(ApplicationArguments args) throws Exception {
+        String port = environment.getProperty("server.port");
+        
+        boolean postgresConnected = false;
+        try (Connection connection = dataSource.getConnection()) {
+            postgresConnected = connection.isValid(1000);
+        } catch (Exception e) {
+            log.error("PostgreSQL 杩炴帴澶辫触: {}", e.getMessage());
+        }
+
+        boolean redisConnected = false;
+        try {
+            String ping = redisConnectionFactory.getConnection().ping();
+            redisConnected = "PONG".equalsIgnoreCase(ping);
+        } catch (Exception e) {
+            log.error("Redis 杩炴帴澶辫触: {}", e.getMessage());
+        }
+
+        System.out.println("\n----------------------------------------------------------");
+        System.out.println("\tApplication is running! Access URLs:");
+        System.out.println("\tLocal: \t\thttp://localhost:" + port);
+        System.out.println("\tPostgreSQL:\t" + (postgresConnected ? "Connected 鉁�" : "Failed 鉂�"));
+        System.out.println("\tRedis:\t\t" + (redisConnected ? "Connected 鉁�" : "Failed 鉂�"));
+        System.out.println("----------------------------------------------------------\n");
+    }
+}
diff --git a/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/constant/AppConstants.java b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/constant/AppConstants.java
new file mode 100755
index 0000000..06c8636
--- /dev/null
+++ b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/constant/AppConstants.java
@@ -0,0 +1,92 @@
+package com.shlb.timescaledbutils.constant;
+
+import java.util.*;
+
+/**
+ * 绯荤粺甯搁噺绫�
+ */
+public class AppConstants {
+    private AppConstants() {
+        // 闃叉瀹炰緥鍖�
+    }
+
+    /**
+     * 鐝 1銆�2銆�3
+     * 鍗锋帴鏈虹粍 101锝�200
+     * 鍖呰鏈虹粍 201锝�300
+     * 瑁呭皝绠辨満缁� 301锝�400
+     * 鎴愬瀷鏈虹粍 401锝�500
+     * 鍙戝皠鏈虹粍 501锝�600
+     * 鎻愬崌鏈� 601
+     * 鍠備笣鏈� 603
+     */
+
+
+    // 鎵�鏈夎澶囩紪鍙� (涓嶅惈鐝鍓嶇紑)
+    public static final List<String> EQUIPMENT_LIST = Arrays.asList(
+            // 鍗锋帴鏈虹粍
+            "101", "102", "103", "105", "106", "107", "108", "109", "110", "111", "113",
+            // 鍖呰鏈虹粍
+            "201", "202", "203", "205", "206", "207", "208", "209", "210", "211", "213",
+            // 瑁呭皝绠辨満缁�
+            "301", "302", "303", "304", "305",
+            // 鎴愬瀷鏈虹粍
+            "401", "402", "403",
+            // 鍙戝皠鏈虹粍
+            "501", "502", "503",
+            // 鎻愬崌鏈�
+            "601",
+            // 鍠備笣鏈�
+            "603"
+    );
+
+
+    // 璁惧绫诲瀷甯搁噺
+    public static final int TYPE_ROLLING = 1;     // 鍗锋帴鏈虹粍
+    public static final int TYPE_PACKAGING = 2;   // 鍖呰鏈虹粍
+    public static final int TYPE_SEALING = 3;     // 瑁呭皝绠辨満缁�
+    public static final int TYPE_FORMING = 4;     // 鎴愬瀷鏈虹粍
+    public static final int TYPE_LAUNCHING = 5;   // 鍙戝皠鏈虹粍
+    public static final int TYPE_LIFTING = 6;     // 鎻愬崌鏈�
+    public static final int TYPE_FEED = 7;        // 鍠備笣鏈�
+
+    // 鐝甯搁噺
+    public static final int SHIFT_MORNING = 1;    // 鏃╃彮
+    public static final int SHIFT_MIDDLE = 2;     // 涓彮
+    public static final int SHIFT_NIGHT = 3;      // 鏅氱彮
+
+    /**
+     * 鑾峰彇璁惧绫诲瀷
+     * @param equipmentCode 璁惧缂栧彿锛屽彲浠ユ槸3浣�(濡�"101")鎴�4浣�(濡�"1101")
+     * @return 1-鍗锋帴鏈虹粍, 2-鍖呰鏈虹粍, 3-瑁呭皝绠辨満缁�, 4-鎴愬瀷鏈虹粍, 5-鍙戝皠鏈虹粍, 6-鎻愬崌鏈�, 7-鍠備笣鏈�, 0-鏃犳晥
+     */
+    public static int getEquipmentType(String equipmentCode) {
+        if (equipmentCode == null || equipmentCode.length() < 3) {
+            return 0;
+        }
+
+        // 鐗规畩璁惧鍒ゆ柇锛�603涓哄杺涓濇満(TYPE=7)锛�601涓烘彁鍗囨満(TYPE=6)
+        // 鏃犺鏄� "603" 杩樻槸 "1603"锛宔ndsWith 閮借兘姝g‘鍒ゆ柇
+        if (equipmentCode.endsWith("603")) {
+            return TYPE_FEED;
+        }
+        if (equipmentCode.endsWith("601")) {
+            return TYPE_LIFTING;
+        }
+
+        // 鑾峰彇绫诲瀷浣嶇殑绱㈠紩
+        // 濡傛灉鏄�4浣嶇紪鐮侊紙濡�1101锛夛紝绫诲瀷浣嶅湪绱㈠紩1
+        // 濡傛灉鏄�3浣嶇紪鐮侊紙濡�101锛夛紝绫诲瀷浣嶅湪绱㈠紩0
+        int typeIndex = equipmentCode.length() == 4 ? 1 : 0;
+        
+        if (typeIndex >= equipmentCode.length()) {
+            return 0;
+        }
+
+        char typeChar = equipmentCode.charAt(typeIndex);
+        int type = Character.getNumericValue(typeChar);
+        
+        return (type >= 1 && type <= 7) ? type : 0;
+    }
+
+}
diff --git a/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/entity/BoxTimeData.java b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/entity/BoxTimeData.java
new file mode 100755
index 0000000..6a444d2
--- /dev/null
+++ b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/entity/BoxTimeData.java
@@ -0,0 +1,53 @@
+package com.shlb.timescaledbutils.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.util.Date;
+
+/**
+ * 瑁呯鏈鸿〃
+ */
+@Data
+@Accessors(chain = true)
+@TableName("box_time_data")
+public class BoxTimeData {
+
+    /**
+     * 鏃堕棿鎴�
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date time;
+
+    /**
+     * 璁惧缂栧彿
+     */
+    private String key;
+
+    /**
+     * 缃戠粶鐘舵�� 0寮傚父锛�1姝e父
+     */
+    private Integer online;
+
+    /**
+     * 1#瑁呭皝绠辨満浜ч噺
+     */
+    private Double qty1;
+
+    /**
+     * 鎺掑寘鏈轰骇閲�
+     */
+    private Double pbjQty;
+
+    /**
+     * 鐝 (1浣嶆暟瀛�)
+     */
+    private Integer shift;
+
+    /**
+     * 璁惧鍙� (3浣嶆暟瀛�)
+     */
+    private Integer equNo;
+}
diff --git a/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/entity/FeedmatchTimeData.java b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/entity/FeedmatchTimeData.java
new file mode 100755
index 0000000..f5e65ed
--- /dev/null
+++ b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/entity/FeedmatchTimeData.java
@@ -0,0 +1,148 @@
+package com.shlb.timescaledbutils.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.util.Date;
+
+/**
+ * 鍠備笣鏈鸿〃
+ */
+@Data
+@Accessors(chain = true)
+@TableName("feedmatch_time_data")
+public class FeedmatchTimeData {
+
+    /**
+     * 鏃堕棿鎴�
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date time;
+
+    /**
+     * 璁惧缂栧彿
+     */
+    private String key;
+
+    /**
+     * 鏁版嵁鏇存柊鏃堕棿 鏍煎紡锛�'2024-11-29 09:13:43'
+     */
+    private String dacUpTime;
+
+    /**
+     * 1#鍠備笣鏈哄搴旂殑绗竴涓偍涓濇煖
+     */
+    private String fs11;
+
+    /**
+     * 1#鍠備笣鏈哄搴旂殑绗簩涓偍涓濇煖
+     */
+    private String fs12;
+
+    /**
+     * 2#鍠備笣鏈哄搴旂殑绗竴涓偍涓濇煖
+     */
+    private String fs21;
+
+    /**
+     * 2#鍠備笣鏈哄搴旂殑绗簩涓偍涓濇煖
+     */
+    private String fs22;
+
+    /**
+     * 3#鍠備笣鏈哄搴旂殑绗竴涓偍涓濇煖
+     */
+    private String fs31;
+
+    /**
+     * 3#鍠備笣鏈哄搴旂殑绗簩涓偍涓濇煖
+     */
+    private String fs32;
+
+    /**
+     * 4#鍠備笣鏈哄搴旂殑绗竴涓偍涓濇煖
+     */
+    private String fs41;
+
+    /**
+     * 4#鍠備笣鏈哄搴旂殑绗簩涓偍涓濇煖
+     */
+    private String fs42;
+
+    /**
+     * 1#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬�
+     */
+    private Integer pipe01;
+
+    /**
+     * 2#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬�
+     */
+    private Integer pipe02;
+
+    /**
+     * 3#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬�
+     */
+    private Integer pipe03;
+
+    /**
+     * 4#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬�
+     */
+    private Integer pipe04;
+
+    /**
+     * 5#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬�
+     */
+    private Integer pipe05;
+
+    /**
+     * 6#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬�
+     */
+    private Integer pipe06;
+
+    /**
+     * 7#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬�
+     */
+    private Integer pipe07;
+
+    /**
+     * 8#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬�
+     */
+    private Integer pipe08;
+
+    /**
+     * 9#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬�
+     */
+    private Integer pipe09;
+
+    /**
+     * 10#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬�
+     */
+    private Integer pipe10;
+
+    /**
+     * 11#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬�
+     */
+    private Integer pipe11;
+
+    /**
+     * 12#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬�
+     */
+    private Integer pipe12;
+
+    /**
+     * 鍠備笣鏈虹姸鎬� 1-杩炴帴 0-鏂紑
+     */
+    private Integer wsjState;
+
+    /**
+     * 鐝 (1浣嶆暟瀛�)
+     */
+    private Integer shift;
+
+    /**
+     * 璁惧鍙� (3浣嶆暟瀛�)
+     */
+    private Integer equNo;
+}
diff --git a/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/entity/HoisterTimeData.java b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/entity/HoisterTimeData.java
new file mode 100755
index 0000000..79e0781
--- /dev/null
+++ b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/entity/HoisterTimeData.java
@@ -0,0 +1,208 @@
+package com.shlb.timescaledbutils.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.util.Date;
+
+/**
+ * 鎻愬崌鏈鸿〃
+ */
+@Data
+@Accessors(chain = true)
+@TableName("hoister_time_data")
+public class HoisterTimeData {
+
+    /**
+     * 鏃堕棿鎴�
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date time;
+
+    /**
+     * 璁惧缂栧彿
+     */
+    private String key;
+
+    /**
+     * 缃戠粶鐘舵�� 0寮傚父锛�1姝e父
+     */
+    private Integer online;
+
+    /**
+     * 浜ч噺锛堟�讳骇閲忥級
+     */
+    private Double qty;
+
+    /**
+     * 1#鎻愬崌鏈虹姸鎬�
+     */
+    private Double tState1;
+
+    /**
+     * 2#鎻愬崌鏈虹姸鎬�
+     */
+    private Double tState2;
+
+    /**
+     * 3#鎻愬崌鏈虹姸鎬�
+     */
+    private Double tState3;
+
+    /**
+     * 4#鎻愬崌鏈虹姸鎬�
+     */
+    private Double tState4;
+
+    /**
+     * 5#鎻愬崌鏈虹姸鎬�
+     */
+    private Double tState5;
+
+    /**
+     * 6#鎻愬崌鏈虹姸鎬�
+     */
+    private Double tState6;
+
+    /**
+     * 7#鎻愬崌鏈虹姸鎬�
+     */
+    private Double tState7;
+
+    /**
+     * 8#鎻愬崌鏈虹姸鎬�
+     */
+    private Double tState8;
+
+    /**
+     * 9#鎻愬崌鏈虹姸鎬�
+     */
+    private Double tState9;
+
+    /**
+     * 10#鎻愬崌鏈虹姸鎬�
+     */
+    private Double tState10;
+
+    /**
+     * 11#鎻愬崌鏈虹姸鎬�
+     */
+    private Double tState11;
+
+    /**
+     * 12#鎻愬崌鏈虹姸鎬�
+     */
+    private Double tState12;
+
+    /**
+     * 1#鎺掑寘鏈虹姸鎬�
+     */
+    private Double pState1;
+
+    /**
+     * 2#鎺掑寘鏈虹姸鎬�
+     */
+    private Double pState2;
+
+    /**
+     * 3#鎺掑寘鏈虹姸鎬�
+     */
+    private Double pState3;
+
+    /**
+     * 4#鎺掑寘鏈虹姸鎬�
+     */
+    private Double pState4;
+
+    /**
+     * 1#鎻愬崌鏈轰骇閲�
+     */
+    private Double tQty1;
+
+    /**
+     * 2#鎻愬崌鏈轰骇閲�
+     */
+    private Double tQty2;
+
+    /**
+     * 3#鎻愬崌鏈轰骇閲�
+     */
+    private Double tQty3;
+
+    /**
+     * 4#鎻愬崌鏈轰骇閲�
+     */
+    private Double tQty4;
+
+    /**
+     * 5#鎻愬崌鏈轰骇閲�
+     */
+    private Double tQty5;
+
+    /**
+     * 6#鎻愬崌鏈轰骇閲�
+     */
+    private Double tQty6;
+
+    /**
+     * 7#鎻愬崌鏈轰骇閲�
+     */
+    private Double tQty7;
+
+    /**
+     * 8#鎻愬崌鏈轰骇閲�
+     */
+    private Double tQty8;
+
+    /**
+     * 9#鎻愬崌鏈轰骇閲�
+     */
+    private Double tQty9;
+
+    /**
+     * 10#鎻愬崌鏈轰骇閲�
+     */
+    private Double tQty10;
+
+    /**
+     * 11#鎻愬崌鏈轰骇閲�
+     */
+    private Double tQty11;
+
+    /**
+     * 12#鎻愬崌鏈轰骇閲�
+     */
+    private Double tQty12;
+
+    /**
+     * 1#鎺掑寘鏈轰骇閲�
+     */
+    private Double pQty1;
+
+    /**
+     * 2#鎺掑寘鏈轰骇閲�
+     */
+    private Double pQty2;
+
+    /**
+     * 3#鎺掑寘鏈轰骇閲�
+     */
+    private Double pQty3;
+
+    /**
+     * 4#鎺掑寘鏈轰骇閲�
+     */
+    private Double pQty4;
+
+    /**
+     * 鐝 (1浣嶆暟瀛�)
+     */
+    private Integer shift;
+
+    /**
+     * 璁惧鍙� (3浣嶆暟瀛�)
+     */
+    private Integer equNo;
+}
diff --git a/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/entity/MakeupTimeData.java b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/entity/MakeupTimeData.java
new file mode 100755
index 0000000..f8c640e
--- /dev/null
+++ b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/entity/MakeupTimeData.java
@@ -0,0 +1,78 @@
+package com.shlb.timescaledbutils.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.util.Date;
+
+/**
+ * 鎴愬瀷鏈鸿〃
+ */
+@Data
+@Accessors(chain = true)
+@TableName("makeup_time_data")
+public class MakeupTimeData {
+
+    /**
+     * 鏃堕棿鎴�
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date time;
+
+    /**
+     * 璁惧缂栧彿
+     */
+    private String key;
+
+    /**
+     * 缃戠粶鐘舵�� 0寮傚父锛�1姝e父
+     */
+    private Integer online;
+
+    /**
+     * 浜ч噺
+     */
+    private Double qty;
+
+    /**
+     * 鍓旈櫎浜ч噺
+     */
+    private Double badQty;
+
+    /**
+     * 鐩樼焊娑堣��
+     */
+    private Double panzhiVal;
+
+    /**
+     * 杩愯鏃堕棿
+     */
+    private Double runTime;
+
+    /**
+     * 鍋滄満鏃堕棿
+     */
+    private Double stopTime;
+
+    /**
+     * 鍋滄満娆℃暟
+     */
+    private Integer stopTimes;
+
+    /**
+     * 杞﹂��
+     */
+    private Integer speed;
+
+    /**
+     * 鐝 (1浣嶆暟瀛�)
+     */
+    private Integer shift;
+
+    /**
+     * 璁惧鍙� (3浣嶆暟瀛�)
+     */
+    private Integer equNo;
+}
diff --git a/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/entity/PackerTimeData.java b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/entity/PackerTimeData.java
new file mode 100755
index 0000000..bf42d2a
--- /dev/null
+++ b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/entity/PackerTimeData.java
@@ -0,0 +1,138 @@
+package com.shlb.timescaledbutils.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.util.Date;
+
+/**
+ * 鍖呰鏈鸿〃
+ */
+@Data
+@Accessors(chain = true)
+@TableName("packer_time_data")
+public class PackerTimeData {
+
+    /**
+     * 鏃堕棿鎴�
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date time;
+
+    /**
+     * 璁惧缂栧彿
+     */
+    private String key;
+
+    /**
+     * 缃戠粶鐘舵��(0寮傚父锛�1姝e父)
+     */
+    private Integer online;
+
+    /**
+     * 浜ч噺
+     */
+    private Double qty;
+
+    /**
+     * 鍓旈櫎浜ч噺
+     */
+    private Double badQty;
+
+    /**
+     * 灏忕洅鑶滄秷鑰�
+     */
+    private Double xiaohemoVal;
+
+    /**
+     * 鏉$洅鑶滄秷鑰�
+     */
+    private Double tiaohemoVal;
+
+    /**
+     * 灏忕洅绾告秷鑰�
+     */
+    private Double xiaohezhiVal;
+
+    /**
+     * 鏉$洅绾告秷鑰�
+     */
+    private Double tiaohezhiVal;
+
+    /**
+     * 鍐呰‖绾告秷鑰�
+     */
+    private Double neichenzhiVal;
+
+    /**
+     * 杩愯鏃堕棿
+     */
+    private Double runTime;
+
+    /**
+     * 鍋滄満鏃堕棿
+     */
+    private Double stopTime;
+
+    /**
+     * 鍋滄満娆℃暟
+     */
+    private Integer stopTimes;
+
+    /**
+     * 杞﹂��
+     */
+    private Integer speed;
+
+    /**
+     * 杩愯鐘舵��(-1 鏂綉 0鍋滄 1浣庨�熻繍琛� 2姝e父杩愯)
+     */
+    private Integer runStatus;
+
+    /**
+     * 鎻愬崌鏈轰骇閲�
+     */
+    private Double tsQty;
+
+    /**
+     * 涓绘満浜ч噺锛堝皬鍖呮満锛�
+     */
+    private Double mainQty;
+
+    /**
+     * 涓绘満鍓旈櫎閲�
+     */
+    private Double mainBadQty;
+
+    /**
+     * 閫忓寘鏈轰骇閲�
+     */
+    private Double tbjQty;
+
+    /**
+     * 閫忓寘鏈哄墧闄ゅソ鍖�
+     */
+    private Double tbjGdQty;
+
+    /**
+     * 閫忓寘鏈哄墧闄ゅ潖鍖�
+     */
+    private Double tbjBadQty;
+
+    /**
+     * 鎺掑寘鏈轰骇閲�
+     */
+    private Double pbjQty;
+
+    /**
+     * 鐝 (1浣嶆暟瀛�)
+     */
+    private Integer shift;
+
+    /**
+     * 璁惧鍙� (3浣嶆暟瀛�)
+     */
+    private Integer equNo;
+}
diff --git a/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/entity/PointData.java b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/entity/PointData.java
new file mode 100755
index 0000000..3fe31bc
--- /dev/null
+++ b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/entity/PointData.java
@@ -0,0 +1,53 @@
+package com.shlb.timescaledbutils.entity;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.extension.handlers.FastjsonTypeHandler;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.util.Date;
+import java.util.Map;
+
+/**
+ * 鐐逛綅鏃跺簭鏁版嵁瀹炰綋绫�
+ * 瀵瑰簲 TimescaleDB 涓殑 point_time_data 瓒呰〃
+ */
+@Data
+@Accessors(chain = true)
+@TableName(value = "point_time_data", autoResultMap = true)
+public class PointData {
+
+    /**
+     * 鏃堕棿鎴� (TimescaleDB 鐨勬牳蹇冨垎鍖洪敭)
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date time;
+
+    /**
+     * 鐐逛綅 Key
+     */
+    private String pointKey;
+
+    /**
+     * 鐐逛綅鍊�
+     */
+    private String pointValue;
+
+    /**
+     * 鍙�夌殑鏍囩/鍏冩暟鎹� (JSONB)
+     */
+    @TableField(typeHandler = FastjsonTypeHandler.class)
+    private Map<String, Object> tags;
+
+    /**
+     * 鐝 (1浣嶆暟瀛�)
+     */
+    private Integer shift;
+
+    /**
+     * 璁惧鍙� (3浣嶆暟瀛�)
+     */
+    private Integer equNo;
+}
diff --git a/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/entity/RollerTimeData.java b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/entity/RollerTimeData.java
new file mode 100755
index 0000000..67f021b
--- /dev/null
+++ b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/entity/RollerTimeData.java
@@ -0,0 +1,118 @@
+package com.shlb.timescaledbutils.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.util.Date;
+
+/**
+ * 鍗锋帴鏈鸿〃
+ */
+@Data
+@Accessors(chain = true)
+@TableName("roller_time_data")
+public class RollerTimeData {
+
+    /**
+     * 鏃堕棿鎴�
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date time;
+
+    /**
+     * 璁惧缂栧彿
+     */
+    private String key;
+
+    /**
+     * 缃戠粶鐘舵��(0寮傚父 1姝e父)
+     */
+    private Integer online;
+
+    /**
+     * 浜ч噺
+     */
+    private Double qty;
+
+    /**
+     * 鍓旈櫎浜ч噺
+     */
+    private Double badQty;
+
+    /**
+     * 婊ゆ娑堣��
+     */
+    private Double lvbangVal;
+
+    /**
+     * 鍗风儫绾告秷鑰�
+     */
+    private Double juanyanzhiVal;
+
+    /**
+     * 姘存澗绾告秷鑰�
+     */
+    private Double shuisongzhiVal;
+
+    /**
+     * 杩愯鏃堕棿
+     */
+    private Double runTime;
+
+    /**
+     * 鍋滄満鏃堕棿
+     */
+    private Double stopTime;
+
+    /**
+     * 鍋滄満娆℃暟
+     */
+    private Integer stopTimes;
+
+    /**
+     * 杞﹂��
+     */
+    private Integer speed;
+
+    /**
+     * 杩愯鐘舵��(-1鏂綉 0鍋滄 1浣庨�熻繍琛� 2姝e父杩愯)
+     */
+    private Integer runStatus;
+
+    /**
+     * 鍌ㄧ儫璁惧鍌ㄩ噺
+     */
+    private Double cy;
+
+    /**
+     * 鍌ㄧ儫璁惧杞﹂��(1-鍖呰鏈� 6-鍗风儫鏈�)
+     */
+    private Integer cyCs;
+
+    /**
+     * 22-鍗风儫鏈鸿繍琛岀姸鎬� 23-鍖呰鏈鸿繍琛岀姸鎬�
+     */
+    private String cyOnline;
+
+    /**
+     * 鎺ユ敹鏈洪噺
+     */
+    private Double recQty1;
+
+    /**
+     * 鎺ユ敹鏈洪噺2
+     */
+    private Double recQty2;
+
+    /**
+     * 鐝 (1浣嶆暟瀛�)
+     */
+    private Integer shift;
+
+    /**
+     * 璁惧鍙� (3浣嶆暟瀛�)
+     */
+    private Integer equNo;
+}
diff --git a/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/entity/TransTimeData.java b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/entity/TransTimeData.java
new file mode 100755
index 0000000..afa903a
--- /dev/null
+++ b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/entity/TransTimeData.java
@@ -0,0 +1,153 @@
+package com.shlb.timescaledbutils.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.util.Date;
+
+/**
+ * 鍙戝皠鏈鸿〃
+ */
+@Data
+@Accessors(chain = true)
+@TableName("trans_time_data")
+public class TransTimeData {
+
+    /**
+     * 鏃堕棿鎴�
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date time;
+
+    /**
+     * 璁惧缂栧彿
+     */
+    private String key;
+
+    /**
+     * 缃戠粶鐘舵�� 0寮傚父锛�1姝e父
+     */
+    private Integer online;
+
+    /**
+     * 绠¢亾1婊ゆ璁℃暟
+     */
+    private Double pQty1;
+
+    /**
+     * 绠¢亾2婊ゆ璁℃暟
+     */
+    private Double pQty2;
+
+    /**
+     * 绠¢亾3婊ゆ璁℃暟
+     */
+    private Double pQty3;
+
+    /**
+     * 绠¢亾4婊ゆ璁℃暟
+     */
+    private Double pQty4;
+
+    /**
+     * 绠¢亾5婊ゆ璁℃暟
+     */
+    private Double pQty5;
+
+    /**
+     * 绠¢亾6婊ゆ璁℃暟
+     */
+    private Double pQty6;
+
+    /**
+     * 绠¢亾7婊ゆ璁℃暟
+     */
+    private Double pQty7;
+
+    /**
+     * 绠¢亾8婊ゆ璁℃暟
+     */
+    private Double pQty8;
+
+    /**
+     * 绠¢亾9婊ゆ璁℃暟
+     */
+    private Double pQty9;
+
+    /**
+     * 绠¢亾10婊ゆ璁℃暟
+     */
+    private Double pQty10;
+
+    /**
+     * 绠¢亾1婊ゆ閫熷害
+     */
+    private Double speed1;
+
+    /**
+     * 绠¢亾2婊ゆ閫熷害
+     */
+    private Double speed2;
+
+    /**
+     * 绠¢亾3婊ゆ閫熷害
+     */
+    private Double speed3;
+
+    /**
+     * 绠¢亾4婊ゆ閫熷害
+     */
+    private Double speed4;
+
+    /**
+     * 绠¢亾5婊ゆ閫熷害
+     */
+    private Double speed5;
+
+    /**
+     * 绠¢亾6婊ゆ閫熷害
+     */
+    private Double speed6;
+
+    /**
+     * 绠¢亾7婊ゆ閫熷害
+     */
+    private Double speed7;
+
+    /**
+     * 绠¢亾8婊ゆ閫熷害
+     */
+    private Double speed8;
+
+    /**
+     * 绠¢亾9婊ゆ閫熷害
+     */
+    private Double speed9;
+
+    /**
+     * 绠¢亾10婊ゆ閫熷害
+     */
+    private Double speed10;
+
+    /**
+     * 鍗哥洏鏈虹姸鎬�
+     */
+    private Integer xpState;
+
+    /**
+     * 鍗哥洏鏈轰骇閲�
+     */
+    private Double trayQty1;
+
+    /**
+     * 鐝 (1浣嶆暟瀛�)
+     */
+    private Integer shift;
+
+    /**
+     * 璁惧鍙� (3浣嶆暟瀛�)
+     */
+    private Integer equNo;
+}
diff --git a/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/mapper/BoxTimeDataMapper.java b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/mapper/BoxTimeDataMapper.java
new file mode 100755
index 0000000..f0fe726
--- /dev/null
+++ b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/mapper/BoxTimeDataMapper.java
@@ -0,0 +1,9 @@
+package com.shlb.timescaledbutils.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.shlb.timescaledbutils.entity.BoxTimeData;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface BoxTimeDataMapper extends BaseMapper<BoxTimeData> {
+}
diff --git a/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/mapper/FeedmatchTimeDataMapper.java b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/mapper/FeedmatchTimeDataMapper.java
new file mode 100755
index 0000000..49cb75a
--- /dev/null
+++ b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/mapper/FeedmatchTimeDataMapper.java
@@ -0,0 +1,9 @@
+package com.shlb.timescaledbutils.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.shlb.timescaledbutils.entity.FeedmatchTimeData;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface FeedmatchTimeDataMapper extends BaseMapper<FeedmatchTimeData> {
+}
diff --git a/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/mapper/HoisterTimeDataMapper.java b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/mapper/HoisterTimeDataMapper.java
new file mode 100755
index 0000000..2a5a0e2
--- /dev/null
+++ b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/mapper/HoisterTimeDataMapper.java
@@ -0,0 +1,9 @@
+package com.shlb.timescaledbutils.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.shlb.timescaledbutils.entity.HoisterTimeData;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface HoisterTimeDataMapper extends BaseMapper<HoisterTimeData> {
+}
diff --git a/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/mapper/MakeupTimeDataMapper.java b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/mapper/MakeupTimeDataMapper.java
new file mode 100755
index 0000000..94083c3
--- /dev/null
+++ b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/mapper/MakeupTimeDataMapper.java
@@ -0,0 +1,9 @@
+package com.shlb.timescaledbutils.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.shlb.timescaledbutils.entity.MakeupTimeData;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface MakeupTimeDataMapper extends BaseMapper<MakeupTimeData> {
+}
diff --git a/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/mapper/PackerTimeDataMapper.java b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/mapper/PackerTimeDataMapper.java
new file mode 100755
index 0000000..beeb3cd
--- /dev/null
+++ b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/mapper/PackerTimeDataMapper.java
@@ -0,0 +1,9 @@
+package com.shlb.timescaledbutils.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.shlb.timescaledbutils.entity.PackerTimeData;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface PackerTimeDataMapper extends BaseMapper<PackerTimeData> {
+}
diff --git a/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/mapper/PointDataMapper.java b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/mapper/PointDataMapper.java
new file mode 100755
index 0000000..994ba82
--- /dev/null
+++ b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/mapper/PointDataMapper.java
@@ -0,0 +1,9 @@
+package com.shlb.timescaledbutils.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.shlb.timescaledbutils.entity.PointData;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface PointDataMapper extends BaseMapper<PointData> {
+}
diff --git a/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/mapper/RollerTimeDataMapper.java b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/mapper/RollerTimeDataMapper.java
new file mode 100755
index 0000000..1e3147f
--- /dev/null
+++ b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/mapper/RollerTimeDataMapper.java
@@ -0,0 +1,9 @@
+package com.shlb.timescaledbutils.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.shlb.timescaledbutils.entity.RollerTimeData;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface RollerTimeDataMapper extends BaseMapper<RollerTimeData> {
+}
diff --git a/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/mapper/TransTimeDataMapper.java b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/mapper/TransTimeDataMapper.java
new file mode 100755
index 0000000..c245c3f
--- /dev/null
+++ b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/mapper/TransTimeDataMapper.java
@@ -0,0 +1,9 @@
+package com.shlb.timescaledbutils.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.shlb.timescaledbutils.entity.TransTimeData;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface TransTimeDataMapper extends BaseMapper<TransTimeData> {
+}
diff --git a/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/IBoxTimeDataService.java b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/IBoxTimeDataService.java
new file mode 100755
index 0000000..cc880d6
--- /dev/null
+++ b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/IBoxTimeDataService.java
@@ -0,0 +1,7 @@
+package com.shlb.timescaledbutils.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.shlb.timescaledbutils.entity.BoxTimeData;
+
+public interface IBoxTimeDataService extends IService<BoxTimeData> {
+}
diff --git a/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/IFeedmatchTimeDataService.java b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/IFeedmatchTimeDataService.java
new file mode 100755
index 0000000..5f4128c
--- /dev/null
+++ b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/IFeedmatchTimeDataService.java
@@ -0,0 +1,7 @@
+package com.shlb.timescaledbutils.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.shlb.timescaledbutils.entity.FeedmatchTimeData;
+
+public interface IFeedmatchTimeDataService extends IService<FeedmatchTimeData> {
+}
diff --git a/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/IHoisterTimeDataService.java b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/IHoisterTimeDataService.java
new file mode 100755
index 0000000..97a3167
--- /dev/null
+++ b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/IHoisterTimeDataService.java
@@ -0,0 +1,7 @@
+package com.shlb.timescaledbutils.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.shlb.timescaledbutils.entity.HoisterTimeData;
+
+public interface IHoisterTimeDataService extends IService<HoisterTimeData> {
+}
diff --git a/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/IMakeupTimeDataService.java b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/IMakeupTimeDataService.java
new file mode 100755
index 0000000..bfc34cf
--- /dev/null
+++ b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/IMakeupTimeDataService.java
@@ -0,0 +1,7 @@
+package com.shlb.timescaledbutils.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.shlb.timescaledbutils.entity.MakeupTimeData;
+
+public interface IMakeupTimeDataService extends IService<MakeupTimeData> {
+}
diff --git a/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/IPackerTimeDataService.java b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/IPackerTimeDataService.java
new file mode 100755
index 0000000..94452cf
--- /dev/null
+++ b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/IPackerTimeDataService.java
@@ -0,0 +1,7 @@
+package com.shlb.timescaledbutils.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.shlb.timescaledbutils.entity.PackerTimeData;
+
+public interface IPackerTimeDataService extends IService<PackerTimeData> {
+}
diff --git a/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/IPointDataService.java b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/IPointDataService.java
new file mode 100755
index 0000000..39bb77c
--- /dev/null
+++ b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/IPointDataService.java
@@ -0,0 +1,7 @@
+package com.shlb.timescaledbutils.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.shlb.timescaledbutils.entity.PointData;
+
+public interface IPointDataService extends IService<PointData> {
+}
diff --git a/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/IRollerTimeDataService.java b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/IRollerTimeDataService.java
new file mode 100755
index 0000000..2295d90
--- /dev/null
+++ b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/IRollerTimeDataService.java
@@ -0,0 +1,7 @@
+package com.shlb.timescaledbutils.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.shlb.timescaledbutils.entity.RollerTimeData;
+
+public interface IRollerTimeDataService extends IService<RollerTimeData> {
+}
diff --git a/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/ITransTimeDataService.java b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/ITransTimeDataService.java
new file mode 100755
index 0000000..82f6027
--- /dev/null
+++ b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/ITransTimeDataService.java
@@ -0,0 +1,7 @@
+package com.shlb.timescaledbutils.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.shlb.timescaledbutils.entity.TransTimeData;
+
+public interface ITransTimeDataService extends IService<TransTimeData> {
+}
diff --git a/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/impl/BoxTimeDataServiceImpl.java b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/impl/BoxTimeDataServiceImpl.java
new file mode 100755
index 0000000..9a9d950
--- /dev/null
+++ b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/impl/BoxTimeDataServiceImpl.java
@@ -0,0 +1,11 @@
+package com.shlb.timescaledbutils.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.shlb.timescaledbutils.entity.BoxTimeData;
+import com.shlb.timescaledbutils.mapper.BoxTimeDataMapper;
+import com.shlb.timescaledbutils.service.IBoxTimeDataService;
+import org.springframework.stereotype.Service;
+
+@Service
+public class BoxTimeDataServiceImpl extends ServiceImpl<BoxTimeDataMapper, BoxTimeData> implements IBoxTimeDataService {
+}
diff --git a/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/impl/FeedmatchTimeDataServiceImpl.java b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/impl/FeedmatchTimeDataServiceImpl.java
new file mode 100755
index 0000000..e12346f
--- /dev/null
+++ b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/impl/FeedmatchTimeDataServiceImpl.java
@@ -0,0 +1,11 @@
+package com.shlb.timescaledbutils.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.shlb.timescaledbutils.entity.FeedmatchTimeData;
+import com.shlb.timescaledbutils.mapper.FeedmatchTimeDataMapper;
+import com.shlb.timescaledbutils.service.IFeedmatchTimeDataService;
+import org.springframework.stereotype.Service;
+
+@Service
+public class FeedmatchTimeDataServiceImpl extends ServiceImpl<FeedmatchTimeDataMapper, FeedmatchTimeData> implements IFeedmatchTimeDataService {
+}
diff --git a/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/impl/HoisterTimeDataServiceImpl.java b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/impl/HoisterTimeDataServiceImpl.java
new file mode 100755
index 0000000..7ccff9c
--- /dev/null
+++ b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/impl/HoisterTimeDataServiceImpl.java
@@ -0,0 +1,11 @@
+package com.shlb.timescaledbutils.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.shlb.timescaledbutils.entity.HoisterTimeData;
+import com.shlb.timescaledbutils.mapper.HoisterTimeDataMapper;
+import com.shlb.timescaledbutils.service.IHoisterTimeDataService;
+import org.springframework.stereotype.Service;
+
+@Service
+public class HoisterTimeDataServiceImpl extends ServiceImpl<HoisterTimeDataMapper, HoisterTimeData> implements IHoisterTimeDataService {
+}
diff --git a/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/impl/MakeupTimeDataServiceImpl.java b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/impl/MakeupTimeDataServiceImpl.java
new file mode 100755
index 0000000..cd4e912
--- /dev/null
+++ b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/impl/MakeupTimeDataServiceImpl.java
@@ -0,0 +1,11 @@
+package com.shlb.timescaledbutils.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.shlb.timescaledbutils.entity.MakeupTimeData;
+import com.shlb.timescaledbutils.mapper.MakeupTimeDataMapper;
+import com.shlb.timescaledbutils.service.IMakeupTimeDataService;
+import org.springframework.stereotype.Service;
+
+@Service
+public class MakeupTimeDataServiceImpl extends ServiceImpl<MakeupTimeDataMapper, MakeupTimeData> implements IMakeupTimeDataService {
+}
diff --git a/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/impl/PackerTimeDataServiceImpl.java b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/impl/PackerTimeDataServiceImpl.java
new file mode 100755
index 0000000..6d86033
--- /dev/null
+++ b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/impl/PackerTimeDataServiceImpl.java
@@ -0,0 +1,11 @@
+package com.shlb.timescaledbutils.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.shlb.timescaledbutils.entity.PackerTimeData;
+import com.shlb.timescaledbutils.mapper.PackerTimeDataMapper;
+import com.shlb.timescaledbutils.service.IPackerTimeDataService;
+import org.springframework.stereotype.Service;
+
+@Service
+public class PackerTimeDataServiceImpl extends ServiceImpl<PackerTimeDataMapper, PackerTimeData> implements IPackerTimeDataService {
+}
diff --git a/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/impl/PointDataServiceImpl.java b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/impl/PointDataServiceImpl.java
new file mode 100755
index 0000000..bca547f
--- /dev/null
+++ b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/impl/PointDataServiceImpl.java
@@ -0,0 +1,11 @@
+package com.shlb.timescaledbutils.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.shlb.timescaledbutils.entity.PointData;
+import com.shlb.timescaledbutils.mapper.PointDataMapper;
+import com.shlb.timescaledbutils.service.IPointDataService;
+import org.springframework.stereotype.Service;
+
+@Service
+public class PointDataServiceImpl extends ServiceImpl<PointDataMapper, PointData> implements IPointDataService {
+}
diff --git a/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/impl/RollerTimeDataServiceImpl.java b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/impl/RollerTimeDataServiceImpl.java
new file mode 100755
index 0000000..1973a63
--- /dev/null
+++ b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/impl/RollerTimeDataServiceImpl.java
@@ -0,0 +1,11 @@
+package com.shlb.timescaledbutils.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.shlb.timescaledbutils.entity.RollerTimeData;
+import com.shlb.timescaledbutils.mapper.RollerTimeDataMapper;
+import com.shlb.timescaledbutils.service.IRollerTimeDataService;
+import org.springframework.stereotype.Service;
+
+@Service
+public class RollerTimeDataServiceImpl extends ServiceImpl<RollerTimeDataMapper, RollerTimeData> implements IRollerTimeDataService {
+}
diff --git a/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/impl/TransTimeDataServiceImpl.java b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/impl/TransTimeDataServiceImpl.java
new file mode 100755
index 0000000..b5d9010
--- /dev/null
+++ b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/impl/TransTimeDataServiceImpl.java
@@ -0,0 +1,11 @@
+package com.shlb.timescaledbutils.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.shlb.timescaledbutils.entity.TransTimeData;
+import com.shlb.timescaledbutils.mapper.TransTimeDataMapper;
+import com.shlb.timescaledbutils.service.ITransTimeDataService;
+import org.springframework.stereotype.Service;
+
+@Service
+public class TransTimeDataServiceImpl extends ServiceImpl<TransTimeDataMapper, TransTimeData> implements ITransTimeDataService {
+}
diff --git a/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/task/RedisToPostgresSyncTask.java b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/task/RedisToPostgresSyncTask.java
new file mode 100755
index 0000000..c19005e
--- /dev/null
+++ b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/task/RedisToPostgresSyncTask.java
@@ -0,0 +1,278 @@
+package com.shlb.timescaledbutils.task;
+
+import com.alibaba.fastjson.JSONObject;
+import com.shlb.timescaledbutils.constant.AppConstants;
+import com.shlb.timescaledbutils.entity.*;
+import com.shlb.timescaledbutils.service.*;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.ApplicationArguments;
+import org.springframework.boot.ApplicationRunner;
+import org.springframework.dao.DataAccessException;
+import org.springframework.data.redis.core.RedisOperations;
+import org.springframework.data.redis.core.SessionCallback;
+import org.springframework.data.redis.core.StringRedisTemplate;
+import org.springframework.stereotype.Component;
+
+import java.util.*;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Redis 鏁版嵁鍚屾浠诲姟
+ * 瀹氭湡浠� Redis 鑾峰彇璁惧瀹炴椂鏁版嵁骞跺垎娴佸啓鍏ヤ笉鍚� PostgreSQL 琛�
+ */
+@Component
+@Slf4j
+public class RedisToPostgresSyncTask implements ApplicationRunner {
+
+    @Autowired
+    private StringRedisTemplate stringRedisTemplate;
+
+    // 鍚勮澶囧搴旂殑 Service
+    @Autowired
+    private IRollerTimeDataService rollerService;
+    @Autowired
+    private IPackerTimeDataService packerService;
+    @Autowired
+    private IBoxTimeDataService boxService;
+    @Autowired
+    private IMakeupTimeDataService makeupService;
+    @Autowired
+    private ITransTimeDataService transService;
+    @Autowired
+    private IHoisterTimeDataService hoisterService;
+    @Autowired
+    private IFeedmatchTimeDataService feedmatchService;
+
+    @Value("${spring.redis.sync-interval-ms:5000}")
+    private long intervalMs;
+
+    private final ExecutorService executorService = Executors.newSingleThreadExecutor();
+
+    private volatile boolean running = true;
+
+    @Override
+    public void run(ApplicationArguments args) {
+        // 寮哄埗闄愬埗鏈�灏忛棿闅斾负 1000ms
+        if (intervalMs < 1000) {
+            log.warn("閰嶇疆鐨勫悓姝ラ棿闅� {}ms 灏忎簬鏈�灏忛檺鍒� 1000ms锛屽凡鑷姩淇涓� 1000ms", intervalMs);
+            intervalMs = 1000;
+        }
+
+        executorService.submit(this::processSync);
+        log.info("Redis -> PostgreSQL 澶氳〃鍒嗘祦鏁版嵁閲囬泦浠诲姟宸插惎鍔紝閲囬泦闂撮殧: {}ms", intervalMs);
+    }
+
+    private void processSync() {
+        while (running) {
+            try {
+                long startTime = System.currentTimeMillis();
+
+                // 1. 鑾峰彇鐝淇℃伅
+                // Redis Key: "shift", Field: "ID"
+                Object shiftIdObj = stringRedisTemplate.opsForHash().get("shift", "ID");
+                String shiftId = shiftIdObj != null ? shiftIdObj.toString() : null;
+
+                if (shiftId == null || shiftId.isEmpty()) {
+                    // 濡傛灉鑾峰彇涓嶅埌鐝锛屽彲鑳芥槸Redis鏆傛椂娌℃暟鎹紝璁板綍error鏃ュ織骞剁瓑寰呬笅涓�娆�
+                    log.error("Redis涓湭鎵惧埌鐝淇℃伅 (key='shift', field='id')锛岃烦杩囨湰娆¢噰闆�");
+                    sleepRemaining(startTime);
+                    continue;
+                }
+
+                // 2. 鏋勯�犳墍鏈夎澶囩殑 Redis Key (鐝 + 璁惧3浣嶇爜)
+                // 渚嬪锛氱彮娆�"2" + 璁惧"101" = "2101"
+                List<String> equipmentKeys = new ArrayList<>(AppConstants.EQUIPMENT_LIST.size());
+                for (String code : AppConstants.EQUIPMENT_LIST) {
+                    equipmentKeys.add(shiftId + code);
+                }
+
+                // 3. 鎵归噺鑾峰彇璁惧鏁版嵁 (Pipeline)
+                // 浣跨敤 Pipeline 鍑忓皯缃戠粶 RTT
+                List<Object> results = stringRedisTemplate.executePipelined(new SessionCallback<Object>() {
+                    @Override
+                    public Object execute(RedisOperations operations) throws DataAccessException {
+                        for (String key : equipmentKeys) {
+                            operations.opsForHash().entries(key);
+                        }
+                        return null;
+                    }
+                });
+
+                // 灏� results 杞负 Map锛屾柟渚垮悗缁�氳繃 Key 鑾峰彇鍏朵粬璁惧鏁版嵁
+                Map<String, Map<Object, Object>> equipmentDataMap = new HashMap<>();
+                if (results != null) {
+                    for (int i = 0; i < results.size(); i++) {
+                        Object res = results.get(i);
+                        if (res instanceof Map && i < equipmentKeys.size()) {
+                            equipmentDataMap.put(equipmentKeys.get(i), (Map<Object, Object>) res);
+                        }
+                    }
+                }
+
+                // 4. 鏁版嵁鍒嗙被涓庢槧灏�
+                Date now = new Date();
+                
+                List<RollerTimeData> rollerList = new ArrayList<>();
+                List<PackerTimeData> packerList = new ArrayList<>();
+                List<BoxTimeData> boxList = new ArrayList<>();
+                List<MakeupTimeData> makeupList = new ArrayList<>();
+                List<TransTimeData> transList = new ArrayList<>();
+                List<HoisterTimeData> hoisterList = new ArrayList<>();
+                List<FeedmatchTimeData> feedmatchList = new ArrayList<>();
+
+                for (int i = 0; i < results.size(); i++) {
+                    Object result = results.get(i);
+                    // executePipelined 杩斿洖鐨勭粨鏋滀腑锛宧ash entries 杩斿洖鐨勬槸 Map<Object, Object>
+                    if (!(result instanceof Map)) {
+                        continue;
+                    }
+                    Map<Object, Object> map = (Map<Object, Object>) result;
+                    if (map.isEmpty()) {
+                        continue;
+                    }
+
+                    String fullKey = equipmentKeys.get(i);
+                    int type = AppConstants.getEquipmentType(fullKey);
+
+                    try {
+                        // 瑙f瀽鐝鍜岃澶囧彿
+                        Integer shift = null;
+                        Integer equNo = null;
+                        if (fullKey != null && fullKey.length() >= 4) {
+                             try {
+                                 shift = Integer.parseInt(fullKey.substring(0, 1));
+                                 equNo = Integer.parseInt(fullKey.substring(1));
+                             } catch (NumberFormatException e) {
+                                 log.warn("瑙f瀽鐝/璁惧鍙峰け璐� Key: {}", fullKey);
+                             }
+                        }
+
+                        // 灏� Map 杞负 JSONObject 浠ヤ究鍒╃敤 fastjson 鐨勭被鍨嬭浆鎹㈠姛鑳�
+                        JSONObject json = new JSONObject();
+                        for (Map.Entry<Object, Object> entry : map.entrySet()) {
+                            json.put(entry.getKey().toString(), entry.getValue());
+                        }
+
+                        // 鏍规嵁璁惧绫诲瀷杞崲骞舵坊鍔犲埌瀵瑰簲鐨勫垪琛�
+                        switch (type) {
+                            case AppConstants.TYPE_ROLLING:
+                                RollerTimeData roller = json.toJavaObject(RollerTimeData.class);
+                                roller.setTime(now).setKey(fullKey).setShift(shift).setEquNo(equNo);
+                                rollerList.add(roller);
+                                break;
+                            case AppConstants.TYPE_PACKAGING:
+                                PackerTimeData packer = json.toJavaObject(PackerTimeData.class);
+                                packer.setTime(now).setKey(fullKey).setShift(shift).setEquNo(equNo);
+                                try {
+                                    if(json.containsKey("chcQty")){
+                                        packer.setQty(Double.parseDouble(json.get("chcQty").toString()));
+                                    }
+                                    if(json.containsKey("ptchQty")){
+                                        packer.setMainQty(Double.parseDouble(json.get("ptchQty").toString()));
+                                    }
+                                    // 鍖呰鏈洪渶瑕佽幏鍙栨彁鍗囨満鐨勪骇閲忥紝鎻愬崌鏈虹殑key涓� 鏃�1601銆佷腑2601銆佹櫄3601
+                                    // 閫昏緫锛�1-3鍙峰寘瑁呮満瀵瑰簲tQty1-3锛�4鍙锋棤锛�5-13鍙峰搴攖Qty4-12 (鍗冲彿鏁�-1)
+                                    if (equNo != null && equNo > 0) {
+                                        int machineNo = equNo % 100;
+                                        String targetField =  "tQty" + machineNo;;
+//                                        if (machineNo < 4) {
+//                                            targetField = "tQty" + machineNo;
+//                                        } else if (machineNo > 4) {
+//                                            targetField = "tQty" + (machineNo - 1);
+//                                        }
+
+                                        Map<Object, Object> tsjMap = equipmentDataMap.get(shiftId + "601");
+                                        if (tsjMap != null) {
+                                            Object qtyObj = tsjMap.get(targetField);
+                                            if (qtyObj != null) {
+                                                packer.setTsQty(Double.parseDouble(qtyObj.toString()));
+                                            }
+                                        }
+                                    }
+                                }catch (Exception e){}
+                                packerList.add(packer);
+                                break;
+                            case AppConstants.TYPE_SEALING:
+                                BoxTimeData box = json.toJavaObject(BoxTimeData.class);
+                                box.setTime(now).setKey(fullKey).setShift(shift).setEquNo(equNo);
+                                boxList.add(box);
+                                break;
+                            case AppConstants.TYPE_FORMING:
+                                MakeupTimeData makeup = json.toJavaObject(MakeupTimeData.class);
+                                makeup.setTime(now).setKey(fullKey).setShift(shift).setEquNo(equNo);
+                                makeupList.add(makeup);
+                                break;
+                            case AppConstants.TYPE_LAUNCHING:
+                                TransTimeData trans = json.toJavaObject(TransTimeData.class);
+                                trans.setTime(now).setKey(fullKey).setShift(shift).setEquNo(equNo);
+                                transList.add(trans);
+                                break;
+                            case AppConstants.TYPE_LIFTING:
+                                HoisterTimeData hoister = json.toJavaObject(HoisterTimeData.class);
+                                hoister.setTime(now).setKey(fullKey).setShift(shift).setEquNo(equNo);
+                                hoisterList.add(hoister);
+                                break;
+                            case AppConstants.TYPE_FEED:
+                                FeedmatchTimeData feed = json.toJavaObject(FeedmatchTimeData.class);
+                                feed.setTime(now).setKey(fullKey).setShift(shift).setEquNo(equNo);
+                                feedmatchList.add(feed);
+                                break;
+                            default:
+                                log.warn("鏈煡鐨勮澶囩被鍨嬶紝Key: {}", fullKey);
+                        }
+                    } catch (Exception e) {
+                        log.error("鏁版嵁杞崲澶辫触 Key: {}", fullKey, e);
+                    }
+                }
+
+                // 5. 鎵归噺鍏ュ簱
+                if (!rollerList.isEmpty()) rollerService.saveBatch(rollerList);
+                if (!packerList.isEmpty()) packerService.saveBatch(packerList);
+                if (!boxList.isEmpty()) boxService.saveBatch(boxList);
+                if (!makeupList.isEmpty()) makeupService.saveBatch(makeupList);
+                if (!transList.isEmpty()) transService.saveBatch(transList);
+                if (!hoisterList.isEmpty()) hoisterService.saveBatch(hoisterList);
+                if (!feedmatchList.isEmpty()) feedmatchService.saveBatch(feedmatchList);
+
+                int total = rollerList.size() + packerList.size() + boxList.size() + 
+                            makeupList.size() + transList.size() + hoisterList.size() + feedmatchList.size();
+                
+                if (total > 0) {
+                    log.info("閲囬泦瀹屾垚: Shift={}, Total={}, Cost={}ms", shiftId, total, System.currentTimeMillis() - startTime);
+                }
+
+                sleepRemaining(startTime);
+
+            } catch (Exception e) {
+                log.error("鍚屾浠诲姟寮傚父", e);
+                try {
+                    TimeUnit.SECONDS.sleep(5);
+                } catch (InterruptedException ex) {
+                    Thread.currentThread().interrupt();
+                }
+            }
+        }
+    }
+
+    private void sleepRemaining(long startTime) {
+        long elapsed = System.currentTimeMillis() - startTime;
+        long sleepTime = intervalMs - elapsed;
+        if (sleepTime > 0) {
+            try {
+                TimeUnit.MILLISECONDS.sleep(sleepTime);
+            } catch (InterruptedException e) {
+                Thread.currentThread().interrupt();
+            }
+        }
+    }
+
+    // 搴旂敤鍏抽棴鏃跺仠姝换鍔�
+    public void stop() {
+        this.running = false;
+        executorService.shutdown();
+    }
+}
diff --git a/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/utils/RedisUtils.java b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/utils/RedisUtils.java
new file mode 100755
index 0000000..23ff341
--- /dev/null
+++ b/TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/utils/RedisUtils.java
@@ -0,0 +1,114 @@
+package com.shlb.timescaledbutils.utils;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.StringRedisTemplate;
+import org.springframework.stereotype.Component;
+
+import java.util.concurrent.TimeUnit;
+
+@Component
+public class RedisUtils {
+
+    @Autowired
+    private StringRedisTemplate stringRedisTemplate;
+
+    /**
+     * 鍐欏叆缂撳瓨
+     *
+     * @param key   閿�
+     * @param value 鍊�
+     * @return true鎴愬姛 false澶辫触
+     */
+    public boolean set(String key, String value) {
+        try {
+            stringRedisTemplate.opsForValue().set(key, value);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 鍐欏叆缂撳瓨骞惰缃繃鏈熸椂闂�
+     *
+     * @param key   閿�
+     * @param value 鍊�
+     * @param time  鏃堕棿(绉�) time瑕佸ぇ浜�0 濡傛灉time灏忎簬绛変簬0 灏嗚缃棤闄愭湡
+     * @return true鎴愬姛 false澶辫触
+     */
+    public boolean set(String key, String value, long time) {
+        try {
+            if (time > 0) {
+                stringRedisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
+            } else {
+                set(key, value);
+            }
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 璇诲彇缂撳瓨
+     *
+     * @param key 閿�
+     * @return 鍊�
+     */
+    public String get(String key) {
+        return key == null ? null : stringRedisTemplate.opsForValue().get(key);
+    }
+
+    /**
+     * 鍒犻櫎缂撳瓨
+     *
+     * @param key 閿�
+     */
+    public boolean del(String key) {
+        try {
+            if (key != null && key.length() > 0) {
+                stringRedisTemplate.delete(key);
+            }
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 鎸囧畾缂撳瓨澶辨晥鏃堕棿
+     *
+     * @param key  閿�
+     * @param time 鏃堕棿(绉�)
+     * @return true鎴愬姛 false澶辫触
+     */
+    public boolean expire(String key, long time) {
+        try {
+            if (time > 0) {
+                stringRedisTemplate.expire(key, time, TimeUnit.SECONDS);
+            }
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 鍒ゆ柇key鏄惁瀛樺湪
+     *
+     * @param key 閿�
+     * @return true 瀛樺湪 false涓嶅瓨鍦�
+     */
+    public boolean hasKey(String key) {
+        try {
+            return Boolean.TRUE.equals(stringRedisTemplate.hasKey(key));
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+}
diff --git a/TimescaleDB-Utils/src/main/resources/application.yml b/TimescaleDB-Utils/src/main/resources/application.yml
new file mode 100755
index 0000000..a5996be
--- /dev/null
+++ b/TimescaleDB-Utils/src/main/resources/application.yml
@@ -0,0 +1,37 @@
+spring:
+  datasource:
+    driver-class-name: org.postgresql.Driver
+    # PostgreSQL 椹卞姩涓嶈瘑鍒� serverTimezone锛岄渶瑕佷娇鐢� options=-c timezone=...
+    url: jdbc:postgresql://10.39.186.6:5432/postgres?currentSchema=public&serverTimezone=Asia/Shanghai";
+    username: postgres
+    password: 123456 
+  jackson:
+    time-zone: Asia/Shanghai
+    date-format: yyyy-MM-dd HH:mm:ss 
+  redis:
+    host: 10.39.186.3
+    port: 6677
+    password: 
+    database: 0
+    timeout: 3000
+    queue-key: redis # Redis Hash Key
+    sync-interval-ms: 5000 # 鍚屾闂撮殧锛堟绉掞級锛屾渶灏� 1000
+
+logging:
+  file:
+    name: logs/timescaledb-utils.log
+  logback:
+    rollingpolicy:
+      max-file-size: 10MB
+      max-history: 30
+  level:
+    root: INFO
+
+server:
+  port: 8642
+
+mybatis-plus:
+  mapper-locations: classpath*:mapper/**/*.xml
+  type-aliases-package: com.shlb.timescaledbutils.entity
+#  configuration:
+#    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 寮�鍚帶鍒跺彴鎵撳嵃 SQL
diff --git a/TimescaleDB-Utils/src/test/java/com/shlb/timescaledbutils/PostgresDataMonitorTest.java b/TimescaleDB-Utils/src/test/java/com/shlb/timescaledbutils/PostgresDataMonitorTest.java
new file mode 100755
index 0000000..66e103a
--- /dev/null
+++ b/TimescaleDB-Utils/src/test/java/com/shlb/timescaledbutils/PostgresDataMonitorTest.java
@@ -0,0 +1,80 @@
+package com.shlb.timescaledbutils;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.shlb.timescaledbutils.entity.PointData;
+import com.shlb.timescaledbutils.service.IPointDataService;
+import lombok.extern.slf4j.Slf4j;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+@SpringBootTest
+@Slf4j
+class PostgresDataMonitorTest {
+
+    @Autowired
+    private IPointDataService pointDataService;
+
+    /**
+     * 鐩戞帶鏁版嵁搴撴暟鎹�
+     * 姣�5绉掓煡璇竴娆℃渶鏂扮殑5鏉℃暟鎹拰鎬绘潯鏁�
+     */
+    @Test
+    void monitorData() {
+        log.info("=== 寮�濮嬬洃鎺� PostgreSQL 鏁版嵁鍙樺寲 (鎸� Ctrl+C 鍋滄) ===");
+        
+        while (true) {
+            try {
+                // 璁板綍寮�濮嬫椂闂�
+                long start = System.currentTimeMillis();
+
+                // 鏌ヨ鎬绘潯鏁�
+                long count = pointDataService.count();
+                
+                // 鏌ヨ鏈�鏂扮殑 5 鏉℃暟鎹� (浣跨敤 LIMIT 5 閬垮厤鍏ㄨ〃鎵弿)
+                LambdaQueryWrapper<PointData> wrapper = new LambdaQueryWrapper<>();
+                wrapper.orderByDesc(PointData::getTime);
+                wrapper.last("LIMIT 5");
+                
+                List<PointData> latestData = pointDataService.list(wrapper);
+
+                // 鎵撳嵃缁撴灉
+                log.info("--------------------------------------------------");
+                log.info("褰撳墠鏃堕棿: {}", new java.util.Date());
+                log.info("鏁版嵁搴撴�绘潯鏁�: {}", count);
+                log.info("鏈�鏂� 5 鏉℃暟鎹�:");
+                if (latestData.isEmpty()) {
+                    log.info("  (鏆傛棤鏁版嵁)");
+                } else {
+                    for (PointData data : latestData) {
+                        log.info("  {}", data);
+                    }
+                }
+                log.info("--------------------------------------------------");
+
+                // 璁$畻浼戠湢鏃堕棿
+                long elapsed = System.currentTimeMillis() - start;
+                long sleepTime = 5000 - elapsed;
+                if (sleepTime > 0) {
+                    TimeUnit.MILLISECONDS.sleep(sleepTime);
+                }
+
+            } catch (InterruptedException e) {
+                log.warn("鐩戞帶浠诲姟琚腑鏂�");
+                Thread.currentThread().interrupt();
+                break;
+            } catch (Exception e) {
+                log.error("鏌ヨ鏁版嵁寮傚父", e);
+                try {
+                    TimeUnit.SECONDS.sleep(5);
+                } catch (InterruptedException ex) {
+                    Thread.currentThread().interrupt();
+                    break;
+                }
+            }
+        }
+    }
+}
diff --git a/TimescaleDB-Utils/src/test/java/com/shlb/timescaledbutils/RollerTimeDataTest.java b/TimescaleDB-Utils/src/test/java/com/shlb/timescaledbutils/RollerTimeDataTest.java
new file mode 100755
index 0000000..50ff48e
--- /dev/null
+++ b/TimescaleDB-Utils/src/test/java/com/shlb/timescaledbutils/RollerTimeDataTest.java
@@ -0,0 +1,29 @@
+package com.shlb.timescaledbutils;
+
+import com.shlb.timescaledbutils.entity.RollerTimeData;
+import com.shlb.timescaledbutils.service.IRollerTimeDataService;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+
+import java.util.List;
+
+@SpringBootTest
+public class RollerTimeDataTest {
+
+    @Autowired
+    private IRollerTimeDataService rollerTimeDataService;
+
+    @Test
+    public void testQueryAll() {
+        long startTime = System.currentTimeMillis();
+
+        List<RollerTimeData> list = rollerTimeDataService.list();
+
+        long endTime = System.currentTimeMillis();
+        long duration = endTime - startTime;
+        
+        System.out.println("鏌ヨ鑰楁椂: " + duration + " ms");
+        System.out.println("鏌ヨ鍒扮殑璁板綍鏁�: " + (list != null ? list.size() : 0));
+    }
+}
diff --git a/TimescaleDB-Utils/src/test/java/com/shlb/timescaledbutils/TimescaleDbUtilsApplicationTests.java b/TimescaleDB-Utils/src/test/java/com/shlb/timescaledbutils/TimescaleDbUtilsApplicationTests.java
new file mode 100755
index 0000000..1bc21fd
--- /dev/null
+++ b/TimescaleDB-Utils/src/test/java/com/shlb/timescaledbutils/TimescaleDbUtilsApplicationTests.java
@@ -0,0 +1,40 @@
+package com.shlb.timescaledbutils;
+
+import com.shlb.timescaledbutils.utils.RedisUtils;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+
+@SpringBootTest
+class TimescaleDbUtilsApplicationTests {
+
+    @Autowired
+    private RedisUtils redisUtils;
+
+    @Test
+    void contextLoads() {
+    }
+
+    @Test
+    void testRedis() {
+        String key = "testKey";
+        String value = "hello redis";
+        
+        // 娴嬭瘯鍐欏叆
+        boolean set = redisUtils.set(key, value);
+        Assertions.assertTrue(set, "Redis鍐欏叆澶辫触");
+        
+        // 娴嬭瘯璇诲彇
+        String result = redisUtils.get(key);
+        Assertions.assertEquals(value, result, "Redis璇诲彇鍊间笉鍖归厤");
+        
+        // 娴嬭瘯鍒犻櫎
+        boolean del = redisUtils.del(key);
+        Assertions.assertTrue(del, "Redis鍒犻櫎澶辫触");
+        
+        // 楠岃瘉鍒犻櫎
+        Assertions.assertFalse(redisUtils.hasKey(key), "Redis key搴斿凡琚垹闄�");
+    }
+
+}
diff --git a/ruoyi-plus-soybean/src/service/api/qm/batch.ts b/ruoyi-plus-soybean/src/service/api/qm/batch.ts
new file mode 100644
index 0000000..461638f
--- /dev/null
+++ b/ruoyi-plus-soybean/src/service/api/qm/batch.ts
@@ -0,0 +1,35 @@
+import { request } from '@/service/request';
+
+/** 鑾峰彇妫�楠屾壒娆″垪琛� */
+export function fetchGetBatchList (params?: Api.Qm.BatchSearchParams) {
+    return request<Api.Qm.BatchList>({
+        url: '/qm/batch/list',
+        method: 'get',
+        params
+    });
+}
+/** 鏂板妫�楠屾壒娆� */
+export function fetchCreateBatch (data: Api.Qm.BatchOperateParams) {
+    return request<boolean>({
+        url: '/qm/batch',
+        method: 'post',
+        data
+    });
+}
+
+/** 淇敼妫�楠屾壒娆� */
+export function fetchUpdateBatch (data: Api.Qm.BatchOperateParams) {
+    return request<boolean>({
+        url: '/qm/batch',
+        method: 'put',
+        data
+    });
+}
+
+/** 鎵归噺鍒犻櫎妫�楠屾壒娆� */
+export function fetchBatchDeleteBatch (ids: CommonType.IdType[]) {
+    return request<boolean>({
+        url: `/qm/batch/${ids.join(',')}`,
+        method: 'delete'
+    });
+}
diff --git a/ruoyi-plus-soybean/src/typings/api/qm.batch.api.d.ts b/ruoyi-plus-soybean/src/typings/api/qm.batch.api.d.ts
new file mode 100644
index 0000000..8998bcc
--- /dev/null
+++ b/ruoyi-plus-soybean/src/typings/api/qm.batch.api.d.ts
@@ -0,0 +1,185 @@
+/**
+ * Namespace Api
+ *
+ * All backend api type
+ */
+declare namespace Api {
+    /**
+     * namespace Qm
+     *
+     * backend api module: "Qm"
+     */
+    namespace Qm {
+        /** batch */
+        type Batch = Common.CommonRecord<{
+            /** 缂栫爜 */
+                id: CommonType.IdType; 
+            /** 鎵规浠g爜 */
+                batchCode: string; 
+            /** 鎵规鍚嶇О */
+                batchName: string; 
+            /** A-鍒朵笣 B-鎴愬瀷 C-鍗峰寘 D-灏佺 E-绯栭鏂� */
+                typ: string; 
+            /** 鏈哄彴浠g爜 */
+                eqpCode: string; 
+            /** 鐗屽彿 */
+                matCode: string; 
+            /** 鍒ゅ畾渚濇嵁浠g爜 */
+                judgeCode: string; 
+            /** 鎵规鐢熸垚鏃ユ湡 */
+                batchDate: string; 
+            /** 浣跨敤鏍囧織 */
+                isflag: string; 
+            /** 鍚敤鏍囧織 */
+                enabled: string; 
+            /** 鍒拌揣鎬婚噺 */
+                totalNum: number; 
+            /** 缁煎悎鍒ゅ畾 */
+                results: string; 
+            /** 鎵瑰噯浜� */
+                approver: string; 
+            /** 瀹℃牳浜� */
+                auditor: string; 
+            /** 鍒涘缓浜� */
+                creater: string; 
+            /** 鍒惰〃鏃ユ湡 */
+                tabDate: string; 
+            /** 鐗堟湰鍚嶇О */
+                verName: string; 
+            /** 鐗堟湰缂栧彿 */
+                verCode: string; 
+            /** 淇濆瓨鏈� */
+                archDate: string; 
+            /** 0-鏈笂浼爉es,1-宸蹭笂浼�, 3-浠嶮ES涓嬭浇 */
+                flag: string; 
+            /** 涓婁紶MES鏃堕棿 */
+                toMesDate: string; 
+            /** 浠嶮ES鏃堕棿涓嬭浇 */
+                fromMesDate: string; 
+            /** 鍒犻櫎鏍囧織 */
+                deleted: number; 
+            /** 鎵规鎻忚堪 */
+                batchDes: string; 
+            /** 绫诲埆 0锛氭垚鍝� 1锛氳緟鏉� */
+                category: string; 
+            /** 鍗峰埗宸ュ彿 */
+                makeno: string; 
+            /** 鐝鏈哄彿 */
+                shifteqpno: string; 
+            /** 瑁呯鍙� */
+                boxno: string; 
+            /** 鐖舵壒娆″彿 */
+                pid: CommonType.IdType; 
+            /** 澶嶆牳浜� */
+                reviewer: string; 
+            /** 澶嶆娆℃暟 */
+                rvcount: number; 
+            /** 鎵规鐘舵�� */
+                state: string; 
+            /** 澶嶆牳鏃ユ湡 */
+                reviewTime: string; 
+            /** 瀹℃牳鏃ユ湡 */
+                auditTime: string; 
+            /** 瑙勬牸 */
+                spec: string; 
+            /** 鎵瑰噯鏃堕棿 */
+                approveTime: string; 
+            /** 鍒拌揣鍗曚綅 */
+                unit: string; 
+            /** 鍒拌揣鏃ユ湡 */
+                arrivalTime: string; 
+            /** 瀛樻斁鍦扮偣 */
+                storagePlace: string; 
+            /** 妫�楠屽憳 */
+                checker: string; 
+            /** 鎺ュ崟鏃ユ湡 */
+                receiveTime: string; 
+            /** 鎶ユ鏃ユ湡 */
+                inspTime: string; 
+            /** 浠撳簱淇濈鍛� */
+                storer: string; 
+            /** 鏄惁楠岃瘉 */
+                isverify: string; 
+            /** 鏄惁妫�楠� */
+                ischk: string; 
+            /** 澶囩敤1 */
+                bak1: string; 
+            /** 澶囩敤2 */
+                bak2: string; 
+        }>;
+
+        /** batch search params */
+        type BatchSearchParams = CommonType.RecordNullable<
+            Pick<
+                Api.Qm.Batch,
+                        | 'batchCode'
+                        | 'typ'
+                        | 'eqpCode'
+                        | 'matCode'
+                        | 'batchDate'
+                        | 'flag'
+                        | 'deleted'
+                        | 'category'
+                        | 'state'
+            > &
+            Api.Common.CommonSearchParams
+        >;
+
+        /** batch operate params */
+        type BatchOperateParams = CommonType.RecordNullable<
+            Pick<
+                Api.Qm.Batch,
+                        | 'id'
+                        | 'batchCode'
+                        | 'batchName'
+                        | 'typ'
+                        | 'eqpCode'
+                        | 'matCode'
+                        | 'judgeCode'
+                        | 'batchDate'
+                        | 'isflag'
+                        | 'enabled'
+                        | 'totalNum'
+                        | 'results'
+                        | 'approver'
+                        | 'auditor'
+                        | 'creater'
+                        | 'tabDate'
+                        | 'verName'
+                        | 'verCode'
+                        | 'archDate'
+                        | 'flag'
+                        | 'toMesDate'
+                        | 'fromMesDate'
+                        | 'deleted'
+                        | 'batchDes'
+                        | 'category'
+                        | 'makeno'
+                        | 'shifteqpno'
+                        | 'boxno'
+                        | 'pid'
+                        | 'reviewer'
+                        | 'rvcount'
+                        | 'state'
+                        | 'reviewTime'
+                        | 'auditTime'
+                        | 'spec'
+                        | 'approveTime'
+                        | 'unit'
+                        | 'arrivalTime'
+                        | 'storagePlace'
+                        | 'checker'
+                        | 'receiveTime'
+                        | 'inspTime'
+                        | 'storer'
+                        | 'isverify'
+                        | 'ischk'
+                        | 'bak1'
+                        | 'bak2'
+            >
+        >;
+
+        /** batch list */
+        type BatchList = Api.Common.PaginatingQueryRecord<Batch>;
+    }
+}
diff --git a/ruoyi-plus-soybean/src/views/qm/batch/index.vue b/ruoyi-plus-soybean/src/views/qm/batch/index.vue
new file mode 100644
index 0000000..06ebbff
--- /dev/null
+++ b/ruoyi-plus-soybean/src/views/qm/batch/index.vue
@@ -0,0 +1,474 @@
+<script setup lang="tsx">
+import { ref } from 'vue';
+import { NDivider } from 'naive-ui';
+import { fetchBatchDeleteBatch, fetchGetBatchList } from '@/service/api/qm/batch';
+import { useAppStore } from '@/store/modules/app';
+import { useAuth } from '@/hooks/business/auth';
+import { useDownload } from '@/hooks/business/download';
+import { defaultTransform, useNaivePaginatedTable, useTableOperate } from '@/hooks/common/table';
+import { $t } from '@/locales';
+import ButtonIcon from '@/components/custom/button-icon.vue';
+import BatchOperateDrawer from './modules/batch-operate-drawer.vue';
+import BatchSearch from './modules/batch-search.vue';
+
+defineOptions({
+  name: 'BatchList'
+});
+
+
+const appStore = useAppStore();
+const { download } = useDownload();
+const { hasAuth } = useAuth();
+
+// 绫诲瀷/鍙嶉MES/绫诲埆鐨� value->label 鏄犲皠锛堢敤浜庤〃鏍兼樉绀猴級
+const TYP_MAP: Record<string, string> = { A: '鍒朵笣', B: '鎴愬瀷', C: '鍗峰寘', D: '灏佺', E: '绯栭鏂�' };
+const FLAG_MAP: Record<string, string> = { '0': '鏈笂浼爉es', '1': '宸蹭笂浼�', '3': '浠嶮ES涓嬭浇' };
+const CATEGORY_MAP: Record<string, string> = { '0': '鎴愬搧', '1': '杈呮潗' };
+
+const searchParams = ref<Api.Qm.BatchSearchParams>({
+  pageNum: 1,
+  pageSize: 10,
+  batchCode: null,
+  typ: null,
+  eqpCode: null,
+  matCode: null,
+  batchDate: null,
+  flag: null,
+  toMesDate: null,
+  fromMesDate: null,
+  deleted: null,
+  category: null,
+  state: null,
+  params: {}
+});
+
+const { columns, columnChecks, data, getData, getDataByPage, loading, mobilePagination, scrollX } =
+  useNaivePaginatedTable({
+  api: () => fetchGetBatchList(searchParams.value),
+  transform: response => defaultTransform(response),
+  onPaginationParamsChange: params => {
+    searchParams.value.pageNum = params.page;
+    searchParams.value.pageSize = params.pageSize;
+  },
+  columns: () => [
+    {
+      type: 'selection',
+      align: 'center',
+      width: 48
+    },
+    {
+      key: 'index',
+      title: $t('common.index'),
+      align: 'center',
+      width: 64,
+      render: (_, index) => index + 1
+    },
+    {
+      key: 'id',
+      title: '缂栫爜',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'batchCode',
+      title: '鎵规浠g爜',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'batchName',
+      title: '鎵规鍚嶇О',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'typ',
+      title: '绫诲瀷',
+      align: 'center',
+      minWidth: 120,
+      render: row => TYP_MAP[row.typ] ?? row.typ
+    },
+    {
+      key: 'eqpCode',
+      title: '鏈哄彴浠g爜',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'matCode',
+      title: '鐗屽彿',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'judgeCode',
+      title: '鍒ゅ畾渚濇嵁浠g爜',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'batchDate',
+      title: '鎵规鐢熸垚鏃ユ湡',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'isflag',
+      title: '浣跨敤鏍囧織',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'enabled',
+      title: '鍚敤鏍囧織',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'totalNum',
+      title: '鍒拌揣鎬婚噺',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'results',
+      title: '缁煎悎鍒ゅ畾',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'approver',
+      title: '鎵瑰噯浜�',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'auditor',
+      title: '瀹℃牳浜�',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'creater',
+      title: '鍒涘缓浜�',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'tabDate',
+      title: '鍒惰〃鏃ユ湡',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'verName',
+      title: '鐗堟湰鍚嶇О',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'verCode',
+      title: '鐗堟湰缂栧彿',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'archDate',
+      title: '淇濆瓨鏈�',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'flag',
+      title: '鍙嶉MES',
+      align: 'center',
+      minWidth: 120,
+      render: row => FLAG_MAP[String(row.flag)] ?? row.flag
+    },
+    {
+      key: 'toMesDate',
+      title: '涓婁紶MES鏃堕棿',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'fromMesDate',
+      title: '浠嶮ES鏃堕棿涓嬭浇',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'deleted',
+      title: '鍒犻櫎鏍囧織',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'batchDes',
+      title: '鎵规鎻忚堪',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'category',
+      title: '绫诲埆',
+      align: 'center',
+      minWidth: 120,
+      render: row => CATEGORY_MAP[String(row.category)] ?? row.category
+    },
+    {
+      key: 'makeno',
+      title: '鍗峰埗宸ュ彿',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'shifteqpno',
+      title: '鐝鏈哄彿',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'boxno',
+      title: '瑁呯鍙�',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'pid',
+      title: '鐖舵壒娆″彿',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'reviewer',
+      title: '澶嶆牳浜�',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'rvcount',
+      title: '澶嶆娆℃暟',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'state',
+      title: '鎵规鐘舵��',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'reviewTime',
+      title: '澶嶆牳鏃ユ湡',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'auditTime',
+      title: '瀹℃牳鏃ユ湡',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'spec',
+      title: '瑙勬牸',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'approveTime',
+      title: '鎵瑰噯鏃堕棿',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'unit',
+      title: '鍒拌揣鍗曚綅',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'arrivalTime',
+      title: '鍒拌揣鏃ユ湡',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'storagePlace',
+      title: '瀛樻斁鍦扮偣',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'checker',
+      title: '妫�楠屽憳',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'receiveTime',
+      title: '鎺ュ崟鏃ユ湡',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'inspTime',
+      title: '鎶ユ鏃ユ湡',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'storer',
+      title: '浠撳簱淇濈鍛�',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'isverify',
+      title: '鏄惁楠岃瘉',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'ischk',
+      title: '鏄惁妫�楠�',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'bak1',
+      title: '澶囩敤1',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'bak2',
+      title: '澶囩敤2',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'operate',
+      title: $t('common.operate'),
+      align: 'center',
+      width: 130,
+      render: row => {
+        const divider = () => {
+          if (!hasAuth('qm:batch:edit') || !hasAuth('qm:batch:remove')) {
+            return null;
+          }
+          return <NDivider vertical />;
+        };
+
+        const editBtn = () => {
+          if (!hasAuth('qm:batch:edit')) {
+            return null;
+          }
+          return (
+            <ButtonIcon
+              text
+              type="primary"
+              icon="material-symbols:drive-file-rename-outline-outline"
+              tooltipContent={$t('common.edit')}
+              onClick={() => edit(row.id)}
+            />
+          );
+        };
+
+        const deleteBtn = () => {
+          if (!hasAuth('qm:batch:remove')) {
+            return null;
+          }
+          return (
+            <ButtonIcon
+              text
+              type="error"
+              icon="material-symbols:delete-outline"
+              tooltipContent={$t('common.delete')}
+              popconfirmContent={$t('common.confirmDelete')}
+              onPositiveClick={() => handleDelete(row.id)}
+            />
+          );
+        };
+
+        return (
+          <div class="flex-center gap-8px">
+            {editBtn()}
+            {divider()}
+            {deleteBtn()}
+          </div>
+        );
+      }
+    }
+  ]
+});
+
+const { drawerVisible, operateType, editingData, handleAdd, handleEdit, checkedRowKeys, onBatchDeleted, onDeleted } =
+  useTableOperate(data, 'id', getData);
+
+async function handleBatchDelete() {
+  // request
+  const { error } = await fetchBatchDeleteBatch(checkedRowKeys.value);
+  if (error) return;
+  onBatchDeleted();
+}
+
+async function handleDelete(id: CommonType.IdType) {
+  // request
+  const { error } = await fetchBatchDeleteBatch([id]);
+  if (error) return;
+  onDeleted();
+}
+
+function edit(id: CommonType.IdType) {
+  handleEdit(id);
+}
+
+function handleExport() {
+  download('/qm/batch/export', searchParams.value, `妫�楠屾壒娆${new Date().getTime()}.xlsx`);
+}
+</script>
+
+<template>
+  <div class="min-h-500px flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto">
+    <BatchSearch v-model:model="searchParams" @search="getDataByPage" />
+    <NCard title="妫�楠屾壒娆″垪琛�" :bordered="false" size="small" class="card-wrapper sm:flex-1-hidden">
+      <template #header-extra>
+        <TableHeaderOperation
+          v-model:columns="columnChecks"
+          :disabled-delete="checkedRowKeys.length === 0"
+          :loading="loading"
+          :show-add="hasAuth('qm:batch:add')"
+          :show-delete="hasAuth('qm:batch:remove')"
+          :show-export="hasAuth('qm:batch:export')"
+          @add="handleAdd"
+          @delete="handleBatchDelete"
+          @export="handleExport"
+          @refresh="getData"
+        />
+      </template>
+      <NDataTable
+        v-model:checked-row-keys="checkedRowKeys"
+        :columns="columns"
+        :data="data"
+        size="small"
+        :flex-height="!appStore.isMobile"
+        :scroll-x="scrollX"
+        :loading="loading"
+        remote
+        :row-key="row => row.id"
+        :pagination="mobilePagination"
+        class="sm:h-full"
+      />
+      <BatchOperateDrawer
+        v-model:visible="drawerVisible"
+        :operate-type="operateType"
+        :row-data="editingData"
+        @submitted="getDataByPage"
+      />
+    </NCard>
+  </div>
+</template>
+
+<style scoped></style>
diff --git a/ruoyi-plus-soybean/src/views/qm/batch/modules/batch-operate-drawer.vue b/ruoyi-plus-soybean/src/views/qm/batch/modules/batch-operate-drawer.vue
new file mode 100644
index 0000000..501cb94
--- /dev/null
+++ b/ruoyi-plus-soybean/src/views/qm/batch/modules/batch-operate-drawer.vue
@@ -0,0 +1,373 @@
+<script setup lang="ts">
+import { computed, ref, watch } from 'vue';
+import { jsonClone } from '@sa/utils';
+import { fetchCreateBatch, fetchUpdateBatch } from '@/service/api/qm/batch';
+import { useFormRules, useNaiveForm } from '@/hooks/common/form';
+import { $t } from '@/locales';
+
+defineOptions({
+  name: 'BatchOperateDrawer'
+});
+
+interface Props {
+  /** the type of operation */
+  operateType: NaiveUI.TableOperateType;
+  /** the edit row data */
+  rowData?: Api.Qm.Batch | null;
+}
+
+const props = defineProps<Props>();
+
+interface Emits {
+  (e: 'submitted'): void;
+}
+
+const emit = defineEmits<Emits>();
+
+const visible = defineModel<boolean>('visible', {
+  default: false
+});
+
+const { formRef, validate, restoreValidation } = useNaiveForm();
+const { createRequiredRule } = useFormRules();
+
+// 绫诲瀷閫夐」锛欰-鍒朵笣 B-鎴愬瀷 C-鍗峰寘 D-灏佺 E-绯栭鏂�
+const typOptions = [
+  { label: '鍒朵笣', value: 'A' },
+  { label: '鎴愬瀷', value: 'B' },
+  { label: '鍗峰寘', value: 'C' },
+  { label: '灏佺', value: 'D' },
+  { label: '绯栭鏂�', value: 'E' }
+];
+
+// 鍙嶉MES閫夐」锛�0-鏈笂浼爉es 1-宸蹭笂浼� 3-浠嶮ES涓嬭浇
+const flagOptions = [
+  { label: '鏈笂浼爉es', value: '0' },
+  { label: '宸蹭笂浼�', value: '1' },
+  { label: '浠嶮ES涓嬭浇', value: '3' }
+];
+
+// 绫诲埆閫夐」锛�0-鎴愬搧 1-杈呮潗
+const categoryOptions = [
+  { label: '鎴愬搧', value: '0' },
+  { label: '杈呮潗', value: '1' }
+];
+
+const title = computed(() => {
+  const titles: Record<NaiveUI.TableOperateType, string> = {
+    add: '鏂板妫�楠屾壒娆�',
+    edit: '缂栬緫妫�楠屾壒娆�'
+  };
+  return titles[props.operateType];
+});
+
+type Model = Api.Qm.BatchOperateParams;
+
+const model = ref<Model>(createDefaultModel());
+
+function createDefaultModel(): Model {
+  return {
+      id: '',
+      batchCode: '',
+      batchName: '',
+      typ: '',
+      eqpCode: '',
+      matCode: '',
+      judgeCode: '',
+      batchDate: null,
+      isflag: '',
+      enabled: '',
+      totalNum: null,
+      results: '',
+      approver: '',
+      auditor: '',
+      creater: '',
+      tabDate: null,
+      verName: '',
+      verCode: '',
+      archDate: '',
+      flag: '',
+      toMesDate: null,
+      fromMesDate: null,
+      deleted: null,
+      batchDes: '',
+      category: '',
+      makeno: '',
+      shifteqpno: '',
+      boxno: '',
+      pid: '',
+      reviewer: '',
+      rvcount: null,
+      state: '',
+      reviewTime: null,
+      auditTime: null,
+      spec: '',
+      approveTime: null,
+      unit: '',
+      arrivalTime: null,
+      storagePlace: '',
+      checker: '',
+      receiveTime: null,
+      inspTime: null,
+      storer: '',
+      isverify: '',
+      ischk: '',
+      bak1: '',
+      bak2: ''
+  };
+}
+
+type RuleKey = Extract<
+  keyof Model,
+  | 'id'
+>;
+
+const rules: Record<RuleKey, App.Global.FormRule> = {
+  id: createRequiredRule('缂栫爜涓嶈兘涓虹┖'),
+};
+
+function handleUpdateModelWhenEdit() {
+  model.value = createDefaultModel();
+
+  if (props.operateType === 'edit' && props.rowData) {
+    Object.assign(model.value, jsonClone(props.rowData));
+  }
+}
+
+function closeDrawer() {
+  visible.value = false;
+}
+
+async function handleSubmit() {
+  await validate();
+
+  const { id, batchCode, batchName, typ, eqpCode, matCode, judgeCode, batchDate, isflag, enabled, totalNum, results, approver, auditor, creater, tabDate, verName, verCode, archDate, flag, toMesDate, fromMesDate, deleted, batchDes, category, makeno, shifteqpno, boxno, pid, reviewer, rvcount, state, reviewTime, auditTime, spec, approveTime, unit, arrivalTime, storagePlace, checker, receiveTime, inspTime, storer, isverify, ischk, bak1, bak2 } = model.value;
+
+  // request
+  if (props.operateType === 'add') {
+    const { error } = await fetchCreateBatch({ batchCode, batchName, typ, eqpCode, matCode, judgeCode, batchDate, isflag, enabled, totalNum, results, approver, auditor, creater, tabDate, verName, verCode, archDate, flag, toMesDate, fromMesDate, deleted, batchDes, category, makeno, shifteqpno, boxno, pid, reviewer, rvcount, state, reviewTime, auditTime, spec, approveTime, unit, arrivalTime, storagePlace, checker, receiveTime, inspTime, storer, isverify, ischk, bak1, bak2 });
+    if (error) return;
+  }
+
+  if (props.operateType === 'edit') {
+    const { error } = await fetchUpdateBatch({ id, batchCode, batchName, typ, eqpCode, matCode, judgeCode, batchDate, isflag, enabled, totalNum, results, approver, auditor, creater, tabDate, verName, verCode, archDate, flag, toMesDate, fromMesDate, deleted, batchDes, category, makeno, shifteqpno, boxno, pid, reviewer, rvcount, state, reviewTime, auditTime, spec, approveTime, unit, arrivalTime, storagePlace, checker, receiveTime, inspTime, storer, isverify, ischk, bak1, bak2 });
+    if (error) return;
+  }
+
+  window.$message?.success($t('common.updateSuccess'));
+  closeDrawer();
+  emit('submitted');
+}
+
+watch(visible, () => {
+  if (visible.value) {
+    handleUpdateModelWhenEdit();
+    restoreValidation();
+  }
+});
+</script>
+
+<template>
+  <NDrawer v-model:show="visible" :title="title" display-directive="show" :width="800" class="max-w-90%">
+    <NDrawerContent :title="title" :native-scrollbar="false" closable>
+      <NForm ref="formRef" :model="model" :rules="rules">
+        <NFormItem label="鎵规浠g爜" path="batchCode">
+          <NInput v-model:value="model.batchCode" placeholder="璇疯緭鍏ユ壒娆′唬鐮�" />
+        </NFormItem>
+        <NFormItem label="鎵规鍚嶇О" path="batchName">
+          <NInput v-model:value="model.batchName" placeholder="璇疯緭鍏ユ壒娆″悕绉�" />
+        </NFormItem>
+        <NFormItem label="绫诲瀷" path="typ">
+          <NSelect v-model:value="model.typ" :options="typOptions" placeholder="璇烽�夋嫨绫诲瀷" clearable />
+        </NFormItem>
+        <NFormItem label="鏈哄彴浠g爜" path="eqpCode">
+          <NInput v-model:value="model.eqpCode" placeholder="璇疯緭鍏ユ満鍙颁唬鐮�" />
+        </NFormItem>
+        <NFormItem label="鐗屽彿" path="matCode">
+          <NInput v-model:value="model.matCode" placeholder="璇疯緭鍏ョ墝鍙�" />
+        </NFormItem>
+        <NFormItem label="鍒ゅ畾渚濇嵁浠g爜" path="judgeCode">
+          <NInput v-model:value="model.judgeCode" placeholder="璇疯緭鍏ュ垽瀹氫緷鎹唬鐮�" />
+        </NFormItem>
+        <NFormItem label="鎵规鐢熸垚鏃ユ湡" path="batchDate">
+          <NDatePicker
+            v-model:formatted-value="model.batchDate"
+            type="datetime"
+            value-format="yyyy-MM-dd HH:mm:ss"
+            clearable
+          />
+        </NFormItem>
+        <NFormItem label="浣跨敤鏍囧織" path="isflag">
+          <NInput v-model:value="model.isflag" placeholder="璇疯緭鍏ヤ娇鐢ㄦ爣蹇�" />
+        </NFormItem>
+        <NFormItem label="鍚敤鏍囧織" path="enabled">
+          <NInput v-model:value="model.enabled" placeholder="璇疯緭鍏ュ惎鐢ㄦ爣蹇�" />
+        </NFormItem>
+        <NFormItem label="鍒拌揣鎬婚噺" path="totalNum">
+          <NInput v-model:value="model.totalNum" placeholder="璇疯緭鍏ュ埌璐ф�婚噺" />
+        </NFormItem>
+        <NFormItem label="缁煎悎鍒ゅ畾" path="results">
+          <NInput v-model:value="model.results" placeholder="璇疯緭鍏ョ患鍚堝垽瀹�" />
+        </NFormItem>
+        <NFormItem label="鎵瑰噯浜�" path="approver">
+          <NInput v-model:value="model.approver" placeholder="璇疯緭鍏ユ壒鍑嗕汉" />
+        </NFormItem>
+        <NFormItem label="瀹℃牳浜�" path="auditor">
+          <NInput v-model:value="model.auditor" placeholder="璇疯緭鍏ュ鏍镐汉" />
+        </NFormItem>
+        <NFormItem label="鍒涘缓浜�" path="creater">
+          <NInput v-model:value="model.creater" placeholder="璇疯緭鍏ュ垱寤轰汉" />
+        </NFormItem>
+        <NFormItem label="鍒惰〃鏃ユ湡" path="tabDate">
+          <NDatePicker
+            v-model:formatted-value="model.tabDate"
+            type="datetime"
+            value-format="yyyy-MM-dd HH:mm:ss"
+            clearable
+          />
+        </NFormItem>
+        <NFormItem label="鐗堟湰鍚嶇О" path="verName">
+          <NInput v-model:value="model.verName" placeholder="璇疯緭鍏ョ増鏈悕绉�" />
+        </NFormItem>
+        <NFormItem label="鐗堟湰缂栧彿" path="verCode">
+          <NInput v-model:value="model.verCode" placeholder="璇疯緭鍏ョ増鏈紪鍙�" />
+        </NFormItem>
+        <NFormItem label="淇濆瓨鏈�" path="archDate">
+          <NInput v-model:value="model.archDate" placeholder="璇疯緭鍏ヤ繚瀛樻湡" />
+        </NFormItem>
+        <NFormItem label="鍙嶉MES" path="flag">
+          <NSelect v-model:value="model.flag" :options="flagOptions" placeholder="璇烽�夋嫨鍙嶉MES" clearable />
+        </NFormItem>
+        <NFormItem label="涓婁紶MES鏃堕棿" path="toMesDate">
+          <NDatePicker
+            v-model:formatted-value="model.toMesDate"
+            type="datetime"
+            value-format="yyyy-MM-dd HH:mm:ss"
+            clearable
+          />
+        </NFormItem>
+        <NFormItem label="浠嶮ES鏃堕棿涓嬭浇" path="fromMesDate">
+          <NDatePicker
+            v-model:formatted-value="model.fromMesDate"
+            type="datetime"
+            value-format="yyyy-MM-dd HH:mm:ss"
+            clearable
+          />
+        </NFormItem>
+        <NFormItem label="鍒犻櫎鏍囧織" path="deleted">
+          <NInput v-model:value="model.deleted" placeholder="璇疯緭鍏ュ垹闄ゆ爣蹇�" />
+        </NFormItem>
+        <NFormItem label="鎵规鎻忚堪" path="batchDes">
+          <NInput v-model:value="model.batchDes" placeholder="璇疯緭鍏ユ壒娆℃弿杩�" />
+        </NFormItem>
+        <NFormItem label="绫诲埆" path="category">
+          <NSelect v-model:value="model.category" :options="categoryOptions" placeholder="璇烽�夋嫨绫诲埆" clearable />
+        </NFormItem>
+        <NFormItem label="鍗峰埗宸ュ彿" path="makeno">
+          <NInput v-model:value="model.makeno" placeholder="璇疯緭鍏ュ嵎鍒跺伐鍙�" />
+        </NFormItem>
+        <NFormItem label="鐝鏈哄彿" path="shifteqpno">
+          <NInput v-model:value="model.shifteqpno" placeholder="璇疯緭鍏ョ彮娆℃満鍙�" />
+        </NFormItem>
+        <NFormItem label="瑁呯鍙�" path="boxno">
+          <NInput v-model:value="model.boxno" placeholder="璇疯緭鍏ヨ绠卞彿" />
+        </NFormItem>
+        <NFormItem label="鐖舵壒娆″彿" path="pid">
+          <NInput v-model:value="model.pid" placeholder="璇疯緭鍏ョ埗鎵规鍙�" />
+        </NFormItem>
+        <NFormItem label="澶嶆牳浜�" path="reviewer">
+          <NInput v-model:value="model.reviewer" placeholder="璇疯緭鍏ュ鏍镐汉" />
+        </NFormItem>
+        <NFormItem label="澶嶆娆℃暟" path="rvcount">
+          <NInput v-model:value="model.rvcount" placeholder="璇疯緭鍏ュ妫�娆℃暟" />
+        </NFormItem>
+        <NFormItem label="鎵规鐘舵��" path="state">
+          <NInput v-model:value="model.state" placeholder="璇疯緭鍏ユ壒娆$姸鎬�" />
+        </NFormItem>
+        <NFormItem label="澶嶆牳鏃ユ湡" path="reviewTime">
+          <NDatePicker
+            v-model:formatted-value="model.reviewTime"
+            type="datetime"
+            value-format="yyyy-MM-dd HH:mm:ss"
+            clearable
+          />
+        </NFormItem>
+        <NFormItem label="瀹℃牳鏃ユ湡" path="auditTime">
+          <NDatePicker
+            v-model:formatted-value="model.auditTime"
+            type="datetime"
+            value-format="yyyy-MM-dd HH:mm:ss"
+            clearable
+          />
+        </NFormItem>
+        <NFormItem label="瑙勬牸" path="spec">
+          <NInput v-model:value="model.spec" placeholder="璇疯緭鍏ヨ鏍�" />
+        </NFormItem>
+        <NFormItem label="鎵瑰噯鏃堕棿" path="approveTime">
+          <NDatePicker
+            v-model:formatted-value="model.approveTime"
+            type="datetime"
+            value-format="yyyy-MM-dd HH:mm:ss"
+            clearable
+          />
+        </NFormItem>
+        <NFormItem label="鍒拌揣鍗曚綅" path="unit">
+          <NInput v-model:value="model.unit" placeholder="璇疯緭鍏ュ埌璐у崟浣�" />
+        </NFormItem>
+        <NFormItem label="鍒拌揣鏃ユ湡" path="arrivalTime">
+          <NDatePicker
+            v-model:formatted-value="model.arrivalTime"
+            type="datetime"
+            value-format="yyyy-MM-dd HH:mm:ss"
+            clearable
+          />
+        </NFormItem>
+        <NFormItem label="瀛樻斁鍦扮偣" path="storagePlace">
+          <NInput v-model:value="model.storagePlace" placeholder="璇疯緭鍏ュ瓨鏀惧湴鐐�" />
+        </NFormItem>
+        <NFormItem label="妫�楠屽憳" path="checker">
+          <NInput v-model:value="model.checker" placeholder="璇疯緭鍏ユ楠屽憳" />
+        </NFormItem>
+        <NFormItem label="鎺ュ崟鏃ユ湡" path="receiveTime">
+          <NDatePicker
+            v-model:formatted-value="model.receiveTime"
+            type="datetime"
+            value-format="yyyy-MM-dd HH:mm:ss"
+            clearable
+          />
+        </NFormItem>
+        <NFormItem label="鎶ユ鏃ユ湡" path="inspTime">
+          <NDatePicker
+            v-model:formatted-value="model.inspTime"
+            type="datetime"
+            value-format="yyyy-MM-dd HH:mm:ss"
+            clearable
+          />
+        </NFormItem>
+        <NFormItem label="浠撳簱淇濈鍛�" path="storer">
+          <NInput v-model:value="model.storer" placeholder="璇疯緭鍏ヤ粨搴撲繚绠″憳" />
+        </NFormItem>
+        <NFormItem label="鏄惁楠岃瘉" path="isverify">
+          <NInput v-model:value="model.isverify" placeholder="璇疯緭鍏ユ槸鍚﹂獙璇�" />
+        </NFormItem>
+        <NFormItem label="鏄惁妫�楠�" path="ischk">
+          <NInput v-model:value="model.ischk" placeholder="璇疯緭鍏ユ槸鍚︽楠�" />
+        </NFormItem>
+        <NFormItem label="澶囩敤1" path="bak1">
+          <NInput v-model:value="model.bak1" placeholder="璇疯緭鍏ュ鐢�1" />
+        </NFormItem>
+        <NFormItem label="澶囩敤2" path="bak2">
+          <NInput v-model:value="model.bak2" placeholder="璇疯緭鍏ュ鐢�2" />
+        </NFormItem>
+      </NForm>
+      <template #footer>
+        <NSpace :size="16">
+          <NButton @click="closeDrawer">{{ $t('common.cancel') }}</NButton>
+          <NButton type="primary" @click="handleSubmit">{{ $t('common.confirm') }}</NButton>
+        </NSpace>
+      </template>
+    </NDrawerContent>
+  </NDrawer>
+</template>
+
+<style scoped></style>
diff --git a/ruoyi-plus-soybean/src/views/qm/batch/modules/batch-search.vue b/ruoyi-plus-soybean/src/views/qm/batch/modules/batch-search.vue
new file mode 100644
index 0000000..599f7ec
--- /dev/null
+++ b/ruoyi-plus-soybean/src/views/qm/batch/modules/batch-search.vue
@@ -0,0 +1,150 @@
+<script setup lang="ts">
+import { ref, toRaw } from 'vue';
+import { jsonClone } from '@sa/utils';
+import { useNaiveForm } from '@/hooks/common/form';
+import { $t } from '@/locales';
+
+defineOptions({
+  name: 'BatchSearch'
+});
+
+interface Emits {
+  (e: 'search'): void;
+}
+
+const emit = defineEmits<Emits>();
+
+const { formRef, validate, restoreValidation } = useNaiveForm();
+
+const dateRangeToMesDate = ref<[string, string] | null>(null);
+const dateRangeFromMesDate = ref<[string, string] | null>(null);
+const model = defineModel<Api.Qm.BatchSearchParams>('model', { required: true });
+
+const defaultModel = jsonClone(toRaw(model.value));
+
+// 绫诲瀷閫夐」锛欰-鍒朵笣 B-鎴愬瀷 C-鍗峰寘 D-灏佺 E-绯栭鏂�
+const typOptions = [
+  { label: '鍒朵笣', value: 'A' },
+  { label: '鎴愬瀷', value: 'B' },
+  { label: '鍗峰寘', value: 'C' },
+  { label: '灏佺', value: 'D' },
+  { label: '绯栭鏂�', value: 'E' }
+];
+
+// 鍙嶉MES閫夐」锛�0-鏈笂浼爉es 1-宸蹭笂浼� 3-浠嶮ES涓嬭浇
+const flagOptions = [
+  { label: '鏈笂浼爉es', value: '0' },
+  { label: '宸蹭笂浼�', value: '1' },
+  { label: '浠嶮ES涓嬭浇', value: '3' }
+];
+
+// 绫诲埆閫夐」锛�0-鎴愬搧 1-杈呮潗
+const categoryOptions = [
+  { label: '鎴愬搧', value: '0' },
+  { label: '杈呮潗', value: '1' }
+];
+
+function resetModel() {
+  dateRangeToMesDate.value = null;
+  dateRangeFromMesDate.value = null;
+  Object.assign(model.value, defaultModel);
+}
+
+async function reset() {
+  await restoreValidation();
+  resetModel();
+  emit('search');
+}
+
+async function search() {
+  await validate();
+  if (dateRangeToMesDate.value?.length) {
+    model.value.params!.beginToMesDate = dateRangeToMesDate.value[0];
+    model.value.params!.endToMesDate = dateRangeToMesDate.value[1];
+  }
+  if (dateRangeFromMesDate.value?.length) {
+    model.value.params!.beginFromMesDate = dateRangeFromMesDate.value[0];
+    model.value.params!.endFromMesDate = dateRangeFromMesDate.value[1];
+  }
+  emit('search');
+}
+</script>
+
+<template>
+  <NCard :bordered="false" size="small" class="card-wrapper">
+    <NCollapse>
+      <NCollapseItem :title="$t('common.search')" name="qm-batch-search">
+        <NForm ref="formRef" :model="model" label-placement="left" :label-width="80">
+          <NGrid responsive="screen" item-responsive>
+            <NFormItemGi span="24 s:12 m:8" label="鎵规浠g爜" label-width="auto" path="batchCode" class="pr-24px">
+              <NInput v-model:value="model.batchCode" placeholder="璇疯緭鍏ユ壒娆′唬鐮�" />
+            </NFormItemGi>
+            <NFormItemGi span="24 s:12 m:8" label="绫诲瀷" label-width="auto" path="typ" class="pr-24px">
+              <NSelect v-model:value="model.typ" :options="typOptions" placeholder="璇烽�夋嫨绫诲瀷" clearable />
+            </NFormItemGi>
+            <NFormItemGi span="24 s:12 m:8" label="鏈哄彴浠g爜" label-width="auto" path="eqpCode" class="pr-24px">
+              <NInput v-model:value="model.eqpCode" placeholder="璇疯緭鍏ユ満鍙颁唬鐮�" />
+            </NFormItemGi>
+            <NFormItemGi span="24 s:12 m:8" label="鐗屽彿" label-width="auto" path="matCode" class="pr-24px">
+              <NInput v-model:value="model.matCode" placeholder="璇疯緭鍏ョ墝鍙�" />
+            </NFormItemGi>
+            <NFormItemGi span="24 s:12 m:8" label="鎵规鐢熸垚鏃ユ湡" label-width="auto" path="batchDate" class="pr-24px">
+              <NDatePicker
+                v-model:formatted-value="model.batchDate"
+                type="datetime"
+                value-format="yyyy-MM-dd HH:mm:ss"
+                clearable
+              />
+            </NFormItemGi>
+            <NFormItemGi span="24 s:12 m:8" label="鍙嶉MES" label-width="auto" path="flag" class="pr-24px">
+              <NSelect v-model:value="model.flag" :options="flagOptions" placeholder="璇烽�夋嫨鍙嶉MES" clearable />
+            </NFormItemGi>
+            <NFormItemGi span="24 s:12 m:8" label="涓婁紶MES鏃堕棿" label-width="auto" path="toMesDate" class="pr-24px">
+              <NDatePicker
+                v-model:formatted-value="dateRangeToMesDate"
+                type="datetimerange"
+                value-format="yyyy-MM-dd HH:mm:ss"
+                clearable
+              />
+            </NFormItemGi>
+            <NFormItemGi span="24 s:12 m:8" label="浠嶮ES鏃堕棿涓嬭浇" label-width="auto" path="fromMesDate" class="pr-24px">
+              <NDatePicker
+                v-model:formatted-value="dateRangeFromMesDate"
+                type="datetimerange"
+                value-format="yyyy-MM-dd HH:mm:ss"
+                clearable
+              />
+            </NFormItemGi>
+            <NFormItemGi span="24 s:12 m:8" label="鍒犻櫎鏍囧織" label-width="auto" path="deleted" class="pr-24px">
+              <NInput v-model:value="model.deleted" placeholder="璇疯緭鍏ュ垹闄ゆ爣蹇�" />
+            </NFormItemGi>
+            <NFormItemGi span="24 s:12 m:8" label="绫诲埆" label-width="auto" path="category" class="pr-24px">
+              <NSelect v-model:value="model.category" :options="categoryOptions" placeholder="璇烽�夋嫨绫诲埆" clearable />
+            </NFormItemGi>
+            <NFormItemGi span="24 s:12 m:8" label="鎵规鐘舵��" label-width="auto" path="state" class="pr-24px">
+              <NInput v-model:value="model.state" placeholder="璇疯緭鍏ユ壒娆$姸鎬�" />
+            </NFormItemGi>
+            <NFormItemGi :show-feedback="false" span="24" class="pr-24px">
+              <NSpace class="w-full" justify="end">
+                <NButton @click="reset">
+                  <template #icon>
+                    <icon-ic-round-refresh class="text-icon" />
+                  </template>
+                  {{ $t('common.reset') }}
+                </NButton>
+                <NButton type="primary" ghost @click="search">
+                  <template #icon>
+                    <icon-ic-round-search class="text-icon" />
+                  </template>
+                  {{ $t('common.search') }}
+                </NButton>
+              </NSpace>
+            </NFormItemGi>
+          </NGrid>
+        </NForm>
+      </NCollapseItem>
+    </NCollapse>
+  </NCard>
+</template>
+
+<style scoped></style>

--
Gitblit v1.9.3