Zimo před 1 dnem
rodič
revize
a7925c29fa

+ 1 - 0
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/DateIntervalEnum.java

@@ -16,6 +16,7 @@ import java.util.Arrays;
 @AllArgsConstructor
 public enum DateIntervalEnum implements ArrayValuable<Integer> {
 
+    HOUR(0, "小时"), // 特殊:字典里,暂时不会有这个枚举!!!因为大多数情况下,用不到这个间隔
     DAY(1, "天"),
     WEEK(2, "周"),
     MONTH(3, "月"),

+ 70 - 7
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/LocalDateTimeUtils.java

@@ -3,19 +3,25 @@ package cn.iocoder.yudao.framework.common.util.date;
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.date.DatePattern;
 import cn.hutool.core.date.LocalDateTimeUtil;
+import cn.hutool.core.date.TemporalAccessorUtil;
 import cn.hutool.core.lang.Assert;
 import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.common.enums.DateIntervalEnum;
 
+import java.sql.Timestamp;
 import java.time.*;
+import java.time.format.DateTimeFormatter;
 import java.time.format.DateTimeParseException;
 import java.time.temporal.ChronoUnit;
 import java.time.temporal.TemporalAdjusters;
 import java.util.ArrayList;
 import java.util.List;
 
+import static cn.hutool.core.date.DatePattern.UTC_MS_WITH_XXX_OFFSET_PATTERN;
+import static cn.hutool.core.date.DatePattern.createFormatter;
+
 /**
- * 时间工具类,用于 {@link java.time.LocalDateTime}
+ * 时间工具类,用于 {@link LocalDateTime}
  *
  * @author 芋道源码
  */
@@ -26,6 +32,8 @@ public class LocalDateTimeUtils {
      */
     public static LocalDateTime EMPTY = buildTime(1970, 1, 1);
 
+    public static DateTimeFormatter UTC_MS_WITH_XXX_OFFSET_FORMATTER = createFormatter(UTC_MS_WITH_XXX_OFFSET_PATTERN);
+
     /**
      * 解析时间
      *
@@ -62,17 +70,32 @@ public class LocalDateTimeUtils {
      * 创建指定时间
      *
      * @param year  年
-     * @param mouth 月
+     * @param month 月
      * @param day   日
      * @return 指定时间
      */
-    public static LocalDateTime buildTime(int year, int mouth, int day) {
-        return LocalDateTime.of(year, mouth, day, 0, 0, 0);
+    public static LocalDateTime buildTime(int year, int month, int day) {
+        return LocalDateTime.of(year, month, day, 0, 0, 0);
     }
 
-    public static LocalDateTime[] buildBetweenTime(int year1, int mouth1, int day1,
-                                                   int year2, int mouth2, int day2) {
-        return new LocalDateTime[]{buildTime(year1, mouth1, day1), buildTime(year2, mouth2, day2)};
+    public static LocalDateTime[] buildBetweenTime(int year1, int month1, int day1,
+                                                   int year2, int month2, int day2) {
+        return new LocalDateTime[]{buildTime(year1, month1, day1), buildTime(year2, month2, day2)};
+    }
+
+    /**
+     * 判指定断时间,是否在该时间范围内
+     *
+     * @param startTime 开始时间
+     * @param endTime 结束时间
+     * @param time 指定时间
+     * @return 是否
+     */
+    public static boolean isBetween(LocalDateTime startTime, LocalDateTime endTime, Timestamp time) {
+        if (startTime == null || endTime == null || time == null) {
+            return false;
+        }
+        return LocalDateTimeUtil.isIn(LocalDateTimeUtil.of(time), startTime, endTime);
     }
 
     /**
@@ -227,6 +250,11 @@ public class LocalDateTimeUtils {
         // 2. 循环,生成时间范围
         List<LocalDateTime[]> timeRanges = new ArrayList<>();
         switch (intervalEnum) {
+            case HOUR:
+                while (startTime.isBefore(endTime)) {
+                    timeRanges.add(new LocalDateTime[]{startTime, startTime.plusHours(1).minusNanos(1)});
+                    startTime = startTime.plusHours(1);
+                }
             case DAY:
                 while (startTime.isBefore(endTime)) {
                     timeRanges.add(new LocalDateTime[]{startTime, startTime.plusDays(1).minusNanos(1)});
@@ -290,6 +318,8 @@ public class LocalDateTimeUtils {
 
         // 2. 循环,生成时间范围
         switch (intervalEnum) {
+            case HOUR:
+                return LocalDateTimeUtil.format(startTime, DatePattern.NORM_DATETIME_MINUTE_PATTERN);
             case DAY:
                 return LocalDateTimeUtil.format(startTime, DatePattern.NORM_DATE_PATTERN);
             case WEEK:
@@ -306,4 +336,37 @@ public class LocalDateTimeUtils {
         }
     }
 
+    /**
+     * 获取指定日期所在季度的第一天
+     *
+     * @param date 日期
+     * @return 所在季度的第一天
+     */
+    public static LocalDate getQuarterStart(LocalDate date) {
+        Month firstMonthOfQuarter = date.getMonth().firstMonthOfQuarter();
+        return LocalDate.of(date.getYear(), firstMonthOfQuarter, 1);
+    }
+
+    /**
+     * 获取指定日期所在周的第一天(周一)
+     *
+     * @param date 日期
+     * @return 所在周的周一
+     */
+    public static LocalDate getWeekStart(LocalDate date) {
+        return date.with(DayOfWeek.MONDAY);
+    }
+
+    /**
+     * 将给定的 {@link LocalDateTime} 转换为自 Unix 纪元时间(1970-01-01T00:00:00Z)以来的秒数。
+     *
+     * @param sourceDateTime 需要转换的本地日期时间,不能为空
+     * @return 自 1970-01-01T00:00:00Z 起的秒数(epoch second)
+     * @throws NullPointerException 如果 {@code sourceDateTime} 为 {@code null}
+     * @throws DateTimeException 如果转换过程中发生时间超出范围或其他时间处理异常
+     */
+    public static Long toEpochSecond(LocalDateTime sourceDateTime) {
+        return TemporalAccessorUtil.toInstant(sourceDateTime).getEpochSecond();
+    }
+
 }

+ 2 - 0
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotSceneRuleDO.java

@@ -16,6 +16,7 @@ import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
 import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
 import lombok.AllArgsConstructor;
 import lombok.Builder;
 import lombok.Data;
@@ -199,6 +200,7 @@ public class IotSceneRuleDO extends TenantBaseDO {
      * 场景动作配置
      */
     @Data
+    @JsonIgnoreProperties(ignoreUnknown = true)
     public static class Action {
 
         /**

+ 1 - 2
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/model/ThingModelEvent.java

@@ -6,7 +6,6 @@ import lombok.Data;
 
 import javax.validation.Valid;
 import javax.validation.constraints.NotEmpty;
-import javax.validation.constraints.Pattern;
 import java.util.List;
 
 /**
@@ -21,7 +20,7 @@ public class ThingModelEvent {
      * 事件标识符
      */
     @NotEmpty(message = "事件标识符不能为空")
-//    @Pattern(regexp = "^[a-zA-Z][a-zA-Z0-9_]*$", message = "属性标识符只能由字母、数字和下划线组成,必须以字母开头")
+//    @Pattern(regexp = "^[a-zA-Z][a-zA-Z0-9_]{0,31}$", message = "事件标识符只能由字母、数字和下划线组成,必须以字母开头,长度不超过 32 个字符")
     private String identifier;
     /**
      * 事件名称

+ 1 - 1
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/model/ThingModelParam.java

@@ -22,7 +22,7 @@ public class ThingModelParam {
      * 参数标识符
      */
     @NotEmpty(message = "参数标识符不能为空")
-//    @Pattern(regexp = "^[a-zA-Z][a-zA-Z0-9_]*$", message = "属性标识符只能由字母、数字和下划线组成,必须以字母开头")
+//    @Pattern(regexp = "^[a-zA-Z][a-zA-Z0-9_]{0,31}$", message = "参数标识符只能由字母、数字和下划线组成,必须以字母开头,长度不超过 32 个字符")
     private String identifier;
     /**
      * 参数名称

+ 1 - 1
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/model/dataType/ThingModelStructDataSpecs.java

@@ -22,7 +22,7 @@ import java.util.List;
 public class ThingModelStructDataSpecs extends ThingModelDataSpecs {
 
     @NotEmpty(message = "属性标识符不能为空")
-//    @Pattern(regexp = "^[a-zA-Z][a-zA-Z0-9_]*$", message = "属性标识符只能由字母、数字和下划线组成,必须以字母开头")
+//    @Pattern(regexp = "^[a-zA-Z][a-zA-Z0-9_]{0,31}$", message = "属性标识符只能由字母、数字和下划线组成,必须以字母开头,长度不超过 32 个字符")
     private String identifier;
 
     @NotEmpty(message = "属性名称不能为空")

+ 1 - 1
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/dal/dataobject/iotprojecttaskattrs/IotTaskAttrModelProperty.java

@@ -27,7 +27,7 @@ public class IotTaskAttrModelProperty {
      * 属性标识符
      */
     @NotEmpty(message = "属性标识符不能为空")
-    @Pattern(regexp = "^[a-zA-Z][a-zA-Z0-9_]{0,31}$", message = "属性标识符只能由字母、数字和下划线组成,必须以字母开头,长度不超过 32 个字符")
+//    @Pattern(regexp = "^[a-zA-Z][a-zA-Z0-9_]{0,31}$", message = "属性标识符只能由字母、数字和下划线组成,必须以字母开头,长度不超过 32 个字符")
     private String identifier;
 
     /**