广丰卷烟厂数采质量分析系统
zhuguifei
2026-03-02 974c7aa4010d77bb410b99931b4435d5442deb4b
feat(module(qa)): 新增

批次管理
已添加59个文件
4937 ■■■■■ 文件已修改
.gitignore 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qm/controller/QmBatchController.java 105 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qm/domain/QmBatch.java 263 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qm/domain/bo/QmBatchBo.java 262 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qm/domain/vo/QmBatchVo.java 316 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qm/mapper/QmBatchMapper.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qm/service/IQmBatchService.java 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qm/service/impl/QmBatchServiceImpl.java 144 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/resources/mapper/qm/QmBatchMapper.xml 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
TimescaleDB-Utils/HELP.md 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
TimescaleDB-Utils/pom.xml 98 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
TimescaleDB-Utils/readMe.md 509 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/TimescaleDbUtilsApplication.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/config/MybatisPlusConfig.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/config/StartupStatusPrinter.java 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/constant/AppConstants.java 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/entity/BoxTimeData.java 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/entity/FeedmatchTimeData.java 148 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/entity/HoisterTimeData.java 208 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/entity/MakeupTimeData.java 78 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/entity/PackerTimeData.java 138 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/entity/PointData.java 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/entity/RollerTimeData.java 118 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/entity/TransTimeData.java 153 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/mapper/BoxTimeDataMapper.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/mapper/FeedmatchTimeDataMapper.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/mapper/HoisterTimeDataMapper.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/mapper/MakeupTimeDataMapper.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/mapper/PackerTimeDataMapper.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/mapper/PointDataMapper.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/mapper/RollerTimeDataMapper.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/mapper/TransTimeDataMapper.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/IBoxTimeDataService.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/IFeedmatchTimeDataService.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/IHoisterTimeDataService.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/IMakeupTimeDataService.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/IPackerTimeDataService.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/IPointDataService.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/IRollerTimeDataService.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/ITransTimeDataService.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/impl/BoxTimeDataServiceImpl.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/impl/FeedmatchTimeDataServiceImpl.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/impl/HoisterTimeDataServiceImpl.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/impl/MakeupTimeDataServiceImpl.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/impl/PackerTimeDataServiceImpl.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/impl/PointDataServiceImpl.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/impl/RollerTimeDataServiceImpl.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/service/impl/TransTimeDataServiceImpl.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/task/RedisToPostgresSyncTask.java 278 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
TimescaleDB-Utils/src/main/java/com/shlb/timescaledbutils/utils/RedisUtils.java 114 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
TimescaleDB-Utils/src/main/resources/application.yml 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
TimescaleDB-Utils/src/test/java/com/shlb/timescaledbutils/PostgresDataMonitorTest.java 80 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
TimescaleDB-Utils/src/test/java/com/shlb/timescaledbutils/RollerTimeDataTest.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
TimescaleDB-Utils/src/test/java/com/shlb/timescaledbutils/TimescaleDbUtilsApplicationTests.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/service/api/qm/batch.ts 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/typings/api/qm.batch.api.d.ts 185 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/qm/batch/index.vue 474 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/qm/batch/modules/batch-operate-drawer.vue 373 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/qm/batch/modules/batch-search.vue 150 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.gitignore
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,4 @@
**/.idea
**/logs
**/target
**/.vscode
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));
    }
}
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;
    /**
     * æ‰¹æ¬¡ä»£ç 
     */
    private String batchCode;
    /**
     * æ‰¹æ¬¡åç§°
     */
    private String batchName;
    /**
     * A-制丝 B-成型 C-卷包 D-封箱 E-糖香料
     */
    private String typ;
    /**
     * æœºå°ä»£ç 
     */
    private String eqpCode;
    /**
     * ç‰Œå·
     */
    private String matCode;
    /**
     * åˆ¤å®šä¾æ®ä»£ç 
     */
    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-未上传mes,1-已上传, 3-从MES下载
     */
    private String flag;
    /**
     * ä¸Šä¼ MES时间
     */
    private Date toMesDate;
    /**
     * ä»ŽMES时间下载
     */
    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;
}
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;
    /**
     * æ‰¹æ¬¡ä»£ç 
     */
    private String batchCode;
    /**
     * æ‰¹æ¬¡åç§°
     */
    private String batchName;
    /**
     * A-制丝 B-成型 C-卷包 D-封箱 E-糖香料
     */
    private String typ;
    /**
     * æœºå°ä»£ç 
     */
    private String eqpCode;
    /**
     * ç‰Œå·
     */
    private String matCode;
    /**
     * åˆ¤å®šä¾æ®ä»£ç 
     */
    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-未上传mes,1-已上传, 3-从MES下载
     */
    private String flag;
    /**
     * ä¸Šä¼ MES时间
     */
    private Date toMesDate;
    /**
     * ä»ŽMES时间下载
     */
    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;
}
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;
    /**
     * æ‰¹æ¬¡ä»£ç 
     */
    @ExcelProperty(value = "批次代码")
    private String batchCode;
    /**
     * æ‰¹æ¬¡åç§°
     */
    @ExcelProperty(value = "批次名称")
    private String batchName;
    /**
     * A-制丝 B-成型 C-卷包 D-封箱 E-糖香料
     */
    @ExcelProperty(value = "A-制丝 B-成型 C-卷包 D-封箱 E-糖香料")
    private String typ;
    /**
     * æœºå°ä»£ç 
     */
    @ExcelProperty(value = "机台代码")
    private String eqpCode;
    /**
     * ç‰Œå·
     */
    @ExcelProperty(value = "牌号")
    private String matCode;
    /**
     * åˆ¤å®šä¾æ®ä»£ç 
     */
    @ExcelProperty(value = "判定依据代码")
    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-未上传mes,1-已上传, 3-从MES下载
     */
    @ExcelProperty(value = "0-未上传mes,1-已上传, 3-从MES下载")
    private String flag;
    /**
     * ä¸Šä¼ MES时间
     */
    @ExcelProperty(value = "上传MES时间")
    private Date toMesDate;
    /**
     * ä»ŽMES时间下载
     */
    @ExcelProperty(value = "从MES时间下载")
    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;
}
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;
/**
 * æ£€éªŒæ‰¹æ¬¡Mapper接口
 *
 * @author zhuguifei
 * @date 2026-03-02
 */
public interface QmBatchMapper extends BaseMapperPlus<QmBatch, QmBatchVo> {
}
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;
/**
 * æ£€éªŒæ‰¹æ¬¡Service接口
 *
 * @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);
}
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;
/**
 * æ£€éªŒæ‰¹æ¬¡Service业务层处理
 *
 * @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;
    }
}
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>
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)
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>
TimescaleDB-Utils/readMe.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,509 @@
特别注意:
postgres时区问题,安装后如果是UTC要改成 Asia/Shanghai(+8)
动态运行参数 my_new_key - > éœ€è¦å­˜å‚¨æ•°æ®åº“的上级key; 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正常)
    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正常运行)
    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);
-- å•独索引,适合按key查询
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正常)
    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正常运行)
    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);
-- åˆ›å»ºåå‘复合索引(key+时间)
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正常
    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);
-- åˆ›å»ºåå‘复合索引(key+时间) - æŒ‰è®¾å¤‡æŸ¥è¯¢
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正常
    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正常
    -- ç®¡é“滤棒计数(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正常
    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;
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"));
    }
}
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 {
}
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");
    }
}
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",endsWith éƒ½èƒ½æ­£ç¡®åˆ¤æ–­
        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;
    }
}
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正常
     */
    private Integer online;
    /**
     * 1#装封箱机产量
     */
    private Double qty1;
    /**
     * æŽ’包机产量
     */
    private Double pbjQty;
    /**
     * ç­æ¬¡ (1位数字)
     */
    private Integer shift;
    /**
     * è®¾å¤‡å· (3位数字)
     */
    private Integer equNo;
}
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;
}
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正常
     */
    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;
}
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正常
     */
    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;
}
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正常)
     */
    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正常运行)
     */
    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;
}
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;
}
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正常)
     */
    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正常运行)
     */
    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;
}
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正常
     */
    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;
}
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> {
}
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> {
}
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> {
}
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> {
}
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> {
}
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> {
}
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> {
}
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> {
}
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> {
}
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> {
}
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> {
}
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> {
}
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> {
}
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> {
}
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> {
}
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> {
}
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 {
}
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 {
}
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 {
}
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 {
}
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 {
}
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 {
}
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 {
}
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 {
}
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 è¿”回的结果中,hash 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 {
                        // è§£æžç­æ¬¡å’Œè®¾å¤‡å·
                        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("解析班次/设备号失败 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号对应tQty4-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();
    }
}
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;
        }
    }
}
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
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;
                }
            }
        }
    }
}
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));
    }
}
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应已被删除");
    }
}
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'
    });
}
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;
            /** æ‰¹æ¬¡ä»£ç  */
                batchCode: string;
            /** æ‰¹æ¬¡åç§° */
                batchName: string;
            /** A-制丝 B-成型 C-卷包 D-封箱 E-糖香料 */
                typ: string;
            /** æœºå°ä»£ç  */
                eqpCode: string;
            /** ç‰Œå· */
                matCode: string;
            /** åˆ¤å®šä¾æ®ä»£ç  */
                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-未上传mes,1-已上传, 3-从MES下载 */
                flag: string;
            /** ä¸Šä¼ MES时间 */
                toMesDate: string;
            /** ä»ŽMES时间下载 */
                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>;
    }
}
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': '未上传mes', '1': '已上传', '3': '从MES下载' };
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: '批次代码',
      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: '机台代码',
      align: 'center',
      minWidth: 120
    },
    {
      key: 'matCode',
      title: '牌号',
      align: 'center',
      minWidth: 120
    },
    {
      key: 'judgeCode',
      title: '判定依据代码',
      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: '从MES时间下载',
      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>
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();
// ç±»åž‹é€‰é¡¹ï¼šA-制丝 B-成型 C-卷包 D-封箱 E-糖香料
const typOptions = [
  { label: '制丝', value: 'A' },
  { label: '成型', value: 'B' },
  { label: '卷包', value: 'C' },
  { label: '封箱', value: 'D' },
  { label: '糖香料', value: 'E' }
];
// åé¦ˆMES选项:0-未上传mes 1-已上传 3-从MES下载
const flagOptions = [
  { label: '未上传mes', value: '0' },
  { label: '已上传', value: '1' },
  { label: '从MES下载', 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="批次代码" 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="机台代码" path="eqpCode">
          <NInput v-model:value="model.eqpCode" placeholder="请输入机台代码" />
        </NFormItem>
        <NFormItem label="牌号" path="matCode">
          <NInput v-model:value="model.matCode" placeholder="请输入牌号" />
        </NFormItem>
        <NFormItem label="判定依据代码" 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="从MES时间下载" 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>
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));
// ç±»åž‹é€‰é¡¹ï¼šA-制丝 B-成型 C-卷包 D-封箱 E-糖香料
const typOptions = [
  { label: '制丝', value: 'A' },
  { label: '成型', value: 'B' },
  { label: '卷包', value: 'C' },
  { label: '封箱', value: 'D' },
  { label: '糖香料', value: 'E' }
];
// åé¦ˆMES选项:0-未上传mes 1-已上传 3-从MES下载
const flagOptions = [
  { label: '未上传mes', value: '0' },
  { label: '已上传', value: '1' },
  { label: '从MES下载', 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="批次代码" 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="机台代码" 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="从MES时间下载" 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>