diff --git a/tcctyn-api/tcctyn-api-iot/pom.xml b/tcctyn-api/tcctyn-api-iot/pom.xml
new file mode 100644
index 0000000..18f706f
--- /dev/null
+++ b/tcctyn-api/tcctyn-api-iot/pom.xml
@@ -0,0 +1,30 @@
+
+
+ 4.0.0
+
+ com.tcctyn
+ tcctyn-api
+ 1.0.0
+
+
+ tcctyn-api-iot
+
+
+ 11
+ 11
+ UTF-8
+
+
+
+ org.springframework.cloud
+ spring-cloud-openfeign-core
+
+
+ com.tcctyn
+ tcctyn-common-core
+
+
+
+
\ No newline at end of file
diff --git a/tcctyn-api/tcctyn-api-iot/src/main/java/com/tcctyn/iot/api/RemoteDeviceService.java b/tcctyn-api/tcctyn-api-iot/src/main/java/com/tcctyn/iot/api/RemoteDeviceService.java
new file mode 100644
index 0000000..ca19be9
--- /dev/null
+++ b/tcctyn-api/tcctyn-api-iot/src/main/java/com/tcctyn/iot/api/RemoteDeviceService.java
@@ -0,0 +1,29 @@
+package com.tcctyn.iot.api;
+
+import com.tcctyn.common.core.constant.ServiceNameConstants;
+import com.tcctyn.common.core.web.domain.R;
+import com.tcctyn.common.core.web.domain.model.LoginUser;
+import com.tcctyn.iot.api.factory.RemoteDeviceFallbackFactory;
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+
+/**
+ * 用户服务
+ *
+ * @author tcctyn
+ */
+@FeignClient(contextId = "remoteDeviceService", value = ServiceNameConstants.SYSTEM_SERVICE, fallbackFactory = RemoteDeviceFallbackFactory.class)
+public interface RemoteDeviceService
+{
+ /**
+ * 通过id获取设备信息
+ *
+ * @param username 用户名
+ * @return 结果
+ */
+ @GetMapping("/device/info/{deviceNo}")
+ public R getDeviceInfoByDeviceNo(@PathVariable("deviceNo") String deviceNo);
+
+
+}
diff --git a/tcctyn-api/tcctyn-api-iot/src/main/java/com/tcctyn/iot/api/domain/DeviceConfig.java b/tcctyn-api/tcctyn-api-iot/src/main/java/com/tcctyn/iot/api/domain/DeviceConfig.java
new file mode 100644
index 0000000..f2d8bc6
--- /dev/null
+++ b/tcctyn-api/tcctyn-api-iot/src/main/java/com/tcctyn/iot/api/domain/DeviceConfig.java
@@ -0,0 +1,58 @@
+package com.tcctyn.iot.api.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+
+/**
+ *
+ * 设备配置表
+ *
+ *
+ * @author 宋国强
+ * @since 2025-04-14
+ */
+
+@Data
+public class DeviceConfig {
+
+ /**
+ * 主键id
+ */
+ @TableId(value = "id", type = IdType.AUTO)
+ private Long id;
+
+ /**
+ * 设备编号
+ */
+ @NotBlank(message = "设备编号不能为空")
+ private String deviceNo;
+
+ /**
+ * 用户账户
+ */
+ private String username;
+
+ /**
+ * 密码
+ */
+ private String password;
+
+ /**
+ * IP地址
+ */
+ private String ip;
+
+ /**
+ * 端口号
+ */
+ private String port;
+
+ /**
+ * 备注
+ */
+ private String remark;
+
+}
diff --git a/tcctyn-api/tcctyn-api-iot/src/main/java/com/tcctyn/iot/api/domain/DeviceInfo.java b/tcctyn-api/tcctyn-api-iot/src/main/java/com/tcctyn/iot/api/domain/DeviceInfo.java
new file mode 100644
index 0000000..7ca9b0c
--- /dev/null
+++ b/tcctyn-api/tcctyn-api-iot/src/main/java/com/tcctyn/iot/api/domain/DeviceInfo.java
@@ -0,0 +1,161 @@
+package com.tcctyn.iot.api.domain;
+
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import javax.validation.constraints.NotBlank;
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ *
+ * 设备信息表
+ *
+ *
+ * @author 代超
+ * @since 2024-10-09
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@TableName("device_info")
+public class DeviceInfo implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 主键id
+ */
+ @TableId(value = "id", type = IdType.AUTO)
+ private Long id;
+
+ /**
+ * 设备编号
+ */
+ @NotBlank(message = "设备编号不能为空")
+ private String deviceNo;
+
+ /**
+ * 设备名称
+ */
+ private String deviceName;
+
+ /**
+ * 设备型号
+ */
+ private String deviceModel;
+
+ /**
+ * 设备厂商
+ */
+ private String deviceManufacturer;
+
+ /**
+ * 设备协议
+ */
+ private String deviceProtocol;
+
+ /**
+ * 接入方式
+ */
+ private String accessMethod;
+
+ /**
+ * 设备类型
+ */
+ private String deviceType;
+
+ /**
+ * 设备状态:在线/离线
+ */
+ private String deviceStatus;
+
+ /**
+ * 归属区域id,机场需要挂在地区下面
+ */
+ private Long regionId;
+
+ /**
+ * 归属云台/卡口id
+ */
+ private Long pointId;
+
+ /**
+ * 林区id
+ */
+ private Long forestId;
+
+ /**
+ * 林场id
+ */
+ private Long farmId;
+
+ /**
+ * 电子围栏id
+ */
+ private Long fenceId;
+
+ /**
+ * 机场id
+ */
+ private Long airportId;
+
+ /**
+ * 地址
+ */
+ private String address;
+
+ /**
+ * 经度
+ */
+ private String longitude;
+
+ /**
+ * 纬度
+ */
+ private String latitude;
+
+ /**
+ * 高度
+ */
+ private String height;
+
+ /**
+ * 设备启用状态:0-停用/1-启用
+ */
+ private Integer enableFlag;
+
+ /**
+ * 备注信息
+ */
+ private String remark;
+
+ /**
+ * 该条记录是否被删除,1-已删除,0-未删除
+ */
+ @TableLogic
+ private Integer delFlag;
+
+ /**
+ * 创建人员姓名
+ */
+ private String createdBy;
+
+ /**
+ * 记录创建时间,默认用服务器时间
+ */
+ @TableField(fill = FieldFill.INSERT)
+ private Date createdTime;
+
+ /**
+ * 修改人员姓名
+ */
+ private String updatedBy;
+
+ /**
+ * 记录修改时间,默认用服务器时间
+ */
+ @TableField(fill = FieldFill.UPDATE)
+ private Date updatedTime;
+
+}
diff --git a/tcctyn-api/tcctyn-api-iot/src/main/java/com/tcctyn/iot/api/factory/RemoteDeviceFallbackFactory.java b/tcctyn-api/tcctyn-api-iot/src/main/java/com/tcctyn/iot/api/factory/RemoteDeviceFallbackFactory.java
new file mode 100644
index 0000000..df0e420
--- /dev/null
+++ b/tcctyn-api/tcctyn-api-iot/src/main/java/com/tcctyn/iot/api/factory/RemoteDeviceFallbackFactory.java
@@ -0,0 +1,35 @@
+package com.tcctyn.iot.api.factory;
+
+import com.tcctyn.common.core.web.domain.R;
+import com.tcctyn.common.core.web.domain.model.LoginUser;
+import com.tcctyn.iot.api.RemoteDeviceService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.cloud.openfeign.FallbackFactory;
+import org.springframework.stereotype.Component;
+
+/**
+ * 用户服务降级处理
+ *
+ * @author tcctyn
+ */
+@Component
+public class RemoteDeviceFallbackFactory implements FallbackFactory
+{
+ private static final Logger log = LoggerFactory.getLogger(RemoteDeviceFallbackFactory.class);
+
+ @Override
+ public RemoteDeviceService create(Throwable throwable)
+ {
+ log.error("物联网服务调用失败:{}", throwable.getMessage());
+ return new RemoteDeviceService()
+ {
+
+
+ @Override
+ public R getDeviceInfoByDeviceNo(String deviceNo) {
+ return R.fail("获取设备信息失败:" + throwable.getMessage());
+ }
+ };
+ }
+}
diff --git a/tcctyn-api/tcctyn-api-iot/src/main/resources/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/tcctyn-api/tcctyn-api-iot/src/main/resources/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
new file mode 100644
index 0000000..b2ff48e
--- /dev/null
+++ b/tcctyn-api/tcctyn-api-iot/src/main/resources/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -0,0 +1 @@
+com.tcctyn.iot.api.factory.RemoteDeviceFallbackFactory
\ No newline at end of file
diff --git a/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/annotation/Anonymous.java b/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/annotation/Anonymous.java
new file mode 100644
index 0000000..a0b31a7
--- /dev/null
+++ b/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/annotation/Anonymous.java
@@ -0,0 +1,15 @@
+package com.tcctyn.common.core.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * 匿名访问不鉴权注解
+ *
+ * @author tcctyn
+ */
+@Target({ ElementType.METHOD, ElementType.TYPE })
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface Anonymous
+{
+}
diff --git a/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/annotation/DataScope.java b/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/annotation/DataScope.java
new file mode 100644
index 0000000..92fb450
--- /dev/null
+++ b/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/annotation/DataScope.java
@@ -0,0 +1,29 @@
+package com.tcctyn.common.core.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * 数据权限过滤注解
+ *
+ * @author tcctyn
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface DataScope
+{
+ /**
+ * 部门表的别名
+ */
+ public String deptAlias() default "";
+
+ /**
+ * 用户表的别名
+ */
+ public String userAlias() default "";
+
+ /**
+ * 权限字符(用于多个角色匹配符合要求的权限)默认根据权限注解@ss获取,多个权限用逗号分隔开来
+ */
+ public String permission() default "";
+}
diff --git a/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/annotation/DataSource.java b/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/annotation/DataSource.java
new file mode 100644
index 0000000..63fabfd
--- /dev/null
+++ b/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/annotation/DataSource.java
@@ -0,0 +1,24 @@
+package com.tcctyn.common.core.annotation;
+
+import com.tcctyn.common.core.enums.DataSourceType;
+
+import java.lang.annotation.*;
+
+/**
+ * 自定义多数据源切换注解
+ *
+ * 优先级:先方法,后类,如果方法覆盖了类上的数据源类型,以方法的为准,否则以类上的为准
+ *
+ * @author tcctyn
+ */
+@Target({ ElementType.METHOD, ElementType.TYPE })
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Inherited
+public @interface DataSource
+{
+ /**
+ * 切换数据源名称
+ */
+ public DataSourceType value() default DataSourceType.MASTER;
+}
diff --git a/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/annotation/Log.java b/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/annotation/Log.java
new file mode 100644
index 0000000..c1493db
--- /dev/null
+++ b/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/annotation/Log.java
@@ -0,0 +1,48 @@
+package com.tcctyn.common.core.annotation;
+
+import com.tcctyn.common.core.enums.BusinessType;
+import com.tcctyn.common.core.enums.OperatorType;
+
+import java.lang.annotation.*;
+
+/**
+ * 自定义操作日志记录注解
+ *
+ * @author tcctyn
+ *
+ */
+@Target({ ElementType.PARAMETER, ElementType.METHOD })
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface Log
+{
+ /**
+ * 模块
+ */
+ public String title() default "";
+
+ /**
+ * 功能
+ */
+ public BusinessType businessType() default BusinessType.OTHER;
+
+ /**
+ * 操作人类别
+ */
+ public OperatorType operatorType() default OperatorType.MANAGE;
+
+ /**
+ * 是否保存请求的参数
+ */
+ public boolean isSaveRequestData() default true;
+
+ /**
+ * 是否保存响应的参数
+ */
+ public boolean isSaveResponseData() default true;
+
+ /**
+ * 排除指定的请求参数
+ */
+ public String[] excludeParamNames() default {};
+}
diff --git a/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/annotation/RateLimiter.java b/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/annotation/RateLimiter.java
new file mode 100644
index 0000000..ea3a15d
--- /dev/null
+++ b/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/annotation/RateLimiter.java
@@ -0,0 +1,37 @@
+package com.tcctyn.common.core.annotation;
+
+import com.tcctyn.common.core.constant.CacheConstants;
+import com.tcctyn.common.core.enums.LimitType;
+
+import java.lang.annotation.*;
+
+/**
+ * 限流注解
+ *
+ * @author tcctyn
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface RateLimiter
+{
+ /**
+ * 限流key
+ */
+ public String key() default CacheConstants.RATE_LIMIT_KEY;
+
+ /**
+ * 限流时间,单位秒
+ */
+ public int time() default 60;
+
+ /**
+ * 限流次数
+ */
+ public int count() default 100;
+
+ /**
+ * 限流类型
+ */
+ public LimitType limitType() default LimitType.DEFAULT;
+}
diff --git a/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/annotation/RepeatSubmit.java b/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/annotation/RepeatSubmit.java
new file mode 100644
index 0000000..0b14526
--- /dev/null
+++ b/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/annotation/RepeatSubmit.java
@@ -0,0 +1,26 @@
+package com.tcctyn.common.core.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * 自定义注解防止表单重复提交
+ *
+ * @author tcctyn
+ *
+ */
+@Inherited
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface RepeatSubmit
+{
+ /**
+ * 间隔时间(ms),小于此时间视为重复提交
+ */
+ public int interval() default 5000;
+
+ /**
+ * 提示消息
+ */
+ public String message() default "不允许重复提交,请稍候再试";
+}
diff --git a/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/annotation/Sensitive.java b/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/annotation/Sensitive.java
new file mode 100644
index 0000000..973ac28
--- /dev/null
+++ b/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/annotation/Sensitive.java
@@ -0,0 +1,25 @@
+package com.tcctyn.common.core.annotation;
+
+import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.tcctyn.common.core.config.serializer.SensitiveJsonSerializer;
+import com.tcctyn.common.core.enums.DesensitizedType;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 数据脱敏注解
+ *
+ * @author tcctyn
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+@JacksonAnnotationsInside
+@JsonSerialize(using = SensitiveJsonSerializer.class)
+public @interface Sensitive
+{
+ DesensitizedType desensitizedType();
+}
diff --git a/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/config/TcctynConfig.java b/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/config/TcctynConfig.java
new file mode 100644
index 0000000..01d16f3
--- /dev/null
+++ b/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/config/TcctynConfig.java
@@ -0,0 +1,122 @@
+package com.tcctyn.common.core.config;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+/**
+ * 读取项目相关配置
+ *
+ * @author tcctyn
+ */
+@Component
+@ConfigurationProperties(prefix = "tcctyn")
+public class TcctynConfig
+{
+ /** 项目名称 */
+ private String name;
+
+ /** 版本 */
+ private String version;
+
+ /** 版权年份 */
+ private String copyrightYear;
+
+ /** 上传路径 */
+ private static String profile;
+
+ /** 获取地址开关 */
+ private static boolean addressEnabled;
+
+ /** 验证码类型 */
+ private static String captchaType;
+
+ public String getName()
+ {
+ return name;
+ }
+
+ public void setName(String name)
+ {
+ this.name = name;
+ }
+
+ public String getVersion()
+ {
+ return version;
+ }
+
+ public void setVersion(String version)
+ {
+ this.version = version;
+ }
+
+ public String getCopyrightYear()
+ {
+ return copyrightYear;
+ }
+
+ public void setCopyrightYear(String copyrightYear)
+ {
+ this.copyrightYear = copyrightYear;
+ }
+
+ public static String getProfile()
+ {
+ return profile;
+ }
+
+ public void setProfile(String profile)
+ {
+ TcctynConfig.profile = profile;
+ }
+
+ public static boolean isAddressEnabled()
+ {
+ return addressEnabled;
+ }
+
+ public void setAddressEnabled(boolean addressEnabled)
+ {
+ TcctynConfig.addressEnabled = addressEnabled;
+ }
+
+ public static String getCaptchaType() {
+ return captchaType;
+ }
+
+ public void setCaptchaType(String captchaType) {
+ TcctynConfig.captchaType = captchaType;
+ }
+
+ /**
+ * 获取导入上传路径
+ */
+ public static String getImportPath()
+ {
+ return getProfile() + "/import";
+ }
+
+ /**
+ * 获取头像上传路径
+ */
+ public static String getAvatarPath()
+ {
+ return getProfile() + "/avatar";
+ }
+
+ /**
+ * 获取下载路径
+ */
+ public static String getDownloadPath()
+ {
+ return getProfile() + "/download/";
+ }
+
+ /**
+ * 获取上传路径
+ */
+ public static String getUploadPath()
+ {
+ return getProfile() + "/upload";
+ }
+}
diff --git a/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/config/serializer/SensitiveJsonSerializer.java b/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/config/serializer/SensitiveJsonSerializer.java
new file mode 100644
index 0000000..272f5be
--- /dev/null
+++ b/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/config/serializer/SensitiveJsonSerializer.java
@@ -0,0 +1,68 @@
+package com.tcctyn.common.core.config.serializer;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.BeanProperty;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.ser.ContextualSerializer;
+import com.tcctyn.common.core.annotation.Sensitive;
+import com.tcctyn.common.core.web.domain.model.LoginUser;
+import com.tcctyn.common.core.enums.DesensitizedType;
+import com.tcctyn.common.core.utils.SecurityUtils;
+
+import java.io.IOException;
+import java.util.Objects;
+
+/**
+ * 数据脱敏序列化过滤
+ *
+ * @author tcctyn
+ */
+public class SensitiveJsonSerializer extends JsonSerializer implements ContextualSerializer
+{
+ private DesensitizedType desensitizedType;
+
+ @Override
+ public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException
+ {
+ if (desensitization())
+ {
+ gen.writeString(desensitizedType.desensitizer().apply(value));
+ }
+ else
+ {
+ gen.writeString(value);
+ }
+ }
+
+ @Override
+ public JsonSerializer> createContextual(SerializerProvider prov, BeanProperty property)
+ throws JsonMappingException
+ {
+ Sensitive annotation = property.getAnnotation(Sensitive.class);
+ if (Objects.nonNull(annotation) && Objects.equals(String.class, property.getType().getRawClass()))
+ {
+ this.desensitizedType = annotation.desensitizedType();
+ return this;
+ }
+ return prov.findValueSerializer(property.getType(), property);
+ }
+
+ /**
+ * 是否需要脱敏处理
+ */
+ private boolean desensitization()
+ {
+ try
+ {
+ LoginUser securityUser = SecurityUtils.getLoginUser();
+ // 管理员不脱敏
+ return !securityUser.getUser().isAdmin();
+ }
+ catch (Exception e)
+ {
+ return true;
+ }
+ }
+}
diff --git a/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/constant/RegionConstants.java b/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/constant/RegionConstants.java
new file mode 100644
index 0000000..3a1bda2
--- /dev/null
+++ b/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/constant/RegionConstants.java
@@ -0,0 +1,9 @@
+package com.tcctyn.common.core.constant;
+
+/**
+ * 地区常量
+ */
+public interface RegionConstants {
+
+ String DIVISION_COMMA = ",";
+}
diff --git a/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/enums/BusinessStatus.java b/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/enums/BusinessStatus.java
new file mode 100644
index 0000000..b25dcb9
--- /dev/null
+++ b/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/enums/BusinessStatus.java
@@ -0,0 +1,20 @@
+package com.tcctyn.common.core.enums;
+
+/**
+ * 操作状态
+ *
+ * @author tcctyn
+ *
+ */
+public enum BusinessStatus
+{
+ /**
+ * 成功
+ */
+ SUCCESS,
+
+ /**
+ * 失败
+ */
+ FAIL,
+}
diff --git a/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/enums/BusinessType.java b/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/enums/BusinessType.java
new file mode 100644
index 0000000..41b2446
--- /dev/null
+++ b/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/enums/BusinessType.java
@@ -0,0 +1,59 @@
+package com.tcctyn.common.core.enums;
+
+/**
+ * 业务操作类型
+ *
+ * @author tcctyn
+ */
+public enum BusinessType
+{
+ /**
+ * 其它
+ */
+ OTHER,
+
+ /**
+ * 新增
+ */
+ INSERT,
+
+ /**
+ * 修改
+ */
+ UPDATE,
+
+ /**
+ * 删除
+ */
+ DELETE,
+
+ /**
+ * 授权
+ */
+ GRANT,
+
+ /**
+ * 导出
+ */
+ EXPORT,
+
+ /**
+ * 导入
+ */
+ IMPORT,
+
+ /**
+ * 强退
+ */
+ FORCE,
+
+ /**
+ * 生成代码
+ */
+ GENCODE,
+
+ /**
+ * 清空数据
+ */
+ CLEAN,
+}
diff --git a/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/enums/DataSourceType.java b/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/enums/DataSourceType.java
new file mode 100644
index 0000000..23a5c13
--- /dev/null
+++ b/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/enums/DataSourceType.java
@@ -0,0 +1,19 @@
+package com.tcctyn.common.core.enums;
+
+/**
+ * 数据源
+ *
+ * @author tcctyn
+ */
+public enum DataSourceType
+{
+ /**
+ * 主库
+ */
+ MASTER,
+
+ /**
+ * 从库
+ */
+ SLAVE
+}
diff --git a/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/enums/DesensitizedType.java b/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/enums/DesensitizedType.java
new file mode 100644
index 0000000..5253532
--- /dev/null
+++ b/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/enums/DesensitizedType.java
@@ -0,0 +1,60 @@
+package com.tcctyn.common.core.enums;
+
+import com.tcctyn.common.core.utils.DesensitizedUtil;
+
+import java.util.function.Function;
+
+/**
+ * 脱敏类型
+ *
+ * @author tcctyn
+ */
+public enum DesensitizedType
+{
+ /**
+ * 姓名,第2位星号替换
+ */
+ USERNAME(s -> s.replaceAll("(\\S)\\S(\\S*)", "$1*$2")),
+
+ /**
+ * 密码,全部字符都用*代替
+ */
+ PASSWORD(DesensitizedUtil::password),
+
+ /**
+ * 身份证,中间10位星号替换
+ */
+ ID_CARD(s -> s.replaceAll("(\\d{4})\\d{10}(\\d{4})", "$1** **** ****$2")),
+
+ /**
+ * 手机号,中间4位星号替换
+ */
+ PHONE(s -> s.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2")),
+
+ /**
+ * 电子邮箱,仅显示第一个字母和@后面的地址显示,其他星号替换
+ */
+ EMAIL(s -> s.replaceAll("(^.)[^@]*(@.*$)", "$1****$2")),
+
+ /**
+ * 银行卡号,保留最后4位,其他星号替换
+ */
+ BANK_CARD(s -> s.replaceAll("\\d{15}(\\d{3})", "**** **** **** **** $1")),
+
+ /**
+ * 车牌号码,包含普通车辆、新能源车辆
+ */
+ CAR_LICENSE(DesensitizedUtil::carLicense);
+
+ private final Function desensitizer;
+
+ DesensitizedType(Function desensitizer)
+ {
+ this.desensitizer = desensitizer;
+ }
+
+ public Function desensitizer()
+ {
+ return desensitizer;
+ }
+}
diff --git a/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/enums/HttpMethod.java b/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/enums/HttpMethod.java
new file mode 100644
index 0000000..94e07b7
--- /dev/null
+++ b/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/enums/HttpMethod.java
@@ -0,0 +1,37 @@
+package com.tcctyn.common.core.enums;
+
+import org.springframework.lang.Nullable;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 请求方式
+ *
+ * @author tcctyn
+ */
+public enum HttpMethod
+{
+ GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE;
+
+ private static final Map mappings = new HashMap<>(16);
+
+ static
+ {
+ for (HttpMethod httpMethod : values())
+ {
+ mappings.put(httpMethod.name(), httpMethod);
+ }
+ }
+
+ @Nullable
+ public static HttpMethod resolve(@Nullable String method)
+ {
+ return (method != null ? mappings.get(method) : null);
+ }
+
+ public boolean matches(String method)
+ {
+ return (this == resolve(method));
+ }
+}
diff --git a/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/enums/LimitType.java b/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/enums/LimitType.java
new file mode 100644
index 0000000..488ee5d
--- /dev/null
+++ b/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/enums/LimitType.java
@@ -0,0 +1,20 @@
+package com.tcctyn.common.core.enums;
+
+/**
+ * 限流类型
+ *
+ * @author tcctyn
+ */
+
+public enum LimitType
+{
+ /**
+ * 默认策略全局限流
+ */
+ DEFAULT,
+
+ /**
+ * 根据请求者IP进行限流
+ */
+ IP
+}
diff --git a/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/enums/OperatorType.java b/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/enums/OperatorType.java
new file mode 100644
index 0000000..20a873e
--- /dev/null
+++ b/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/enums/OperatorType.java
@@ -0,0 +1,24 @@
+package com.tcctyn.common.core.enums;
+
+/**
+ * 操作人类别
+ *
+ * @author tcctyn
+ */
+public enum OperatorType
+{
+ /**
+ * 其它
+ */
+ OTHER,
+
+ /**
+ * 后台用户
+ */
+ MANAGE,
+
+ /**
+ * 手机端用户
+ */
+ MOBILE
+}
diff --git a/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/exception/user/BlackListException.java b/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/exception/user/BlackListException.java
new file mode 100644
index 0000000..3e34167
--- /dev/null
+++ b/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/exception/user/BlackListException.java
@@ -0,0 +1,16 @@
+package com.tcctyn.common.core.exception.user;
+
+/**
+ * 黑名单IP异常类
+ *
+ * @author tcctyn
+ */
+public class BlackListException extends UserException
+{
+ private static final long serialVersionUID = 1L;
+
+ public BlackListException()
+ {
+ super("login.blocked", null);
+ }
+}
diff --git a/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/exception/user/CaptchaException.java b/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/exception/user/CaptchaException.java
new file mode 100644
index 0000000..59e9a69
--- /dev/null
+++ b/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/exception/user/CaptchaException.java
@@ -0,0 +1,16 @@
+package com.tcctyn.common.core.exception.user;
+
+/**
+ * 验证码错误异常类
+ *
+ * @author tcctyn
+ */
+public class CaptchaException extends UserException
+{
+ private static final long serialVersionUID = 1L;
+
+ public CaptchaException()
+ {
+ super("user.jcaptcha.error", null);
+ }
+}
diff --git a/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/exception/user/UserNotExistsException.java b/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/exception/user/UserNotExistsException.java
new file mode 100644
index 0000000..4583c7c
--- /dev/null
+++ b/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/exception/user/UserNotExistsException.java
@@ -0,0 +1,16 @@
+package com.tcctyn.common.core.exception.user;
+
+/**
+ * 用户不存在异常类
+ *
+ * @author tcctyn
+ */
+public class UserNotExistsException extends UserException
+{
+ private static final long serialVersionUID = 1L;
+
+ public UserNotExistsException()
+ {
+ super("user.not.exists", null);
+ }
+}
diff --git a/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/exception/user/UserPasswordRetryLimitExceedException.java b/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/exception/user/UserPasswordRetryLimitExceedException.java
new file mode 100644
index 0000000..1a879d7
--- /dev/null
+++ b/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/exception/user/UserPasswordRetryLimitExceedException.java
@@ -0,0 +1,16 @@
+package com.tcctyn.common.core.exception.user;
+
+/**
+ * 用户错误最大次数异常类
+ *
+ * @author tcctyn
+ */
+public class UserPasswordRetryLimitExceedException extends UserException
+{
+ private static final long serialVersionUID = 1L;
+
+ public UserPasswordRetryLimitExceedException(int retryLimitCount, int lockTime)
+ {
+ super("user.password.retry.limit.exceed", new Object[] { retryLimitCount, lockTime });
+ }
+}
diff --git a/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/filter/PropertyPreExcludeFilter.java b/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/filter/PropertyPreExcludeFilter.java
new file mode 100644
index 0000000..d2cf613
--- /dev/null
+++ b/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/filter/PropertyPreExcludeFilter.java
@@ -0,0 +1,24 @@
+package com.tcctyn.common.core.filter;
+
+import com.alibaba.fastjson2.filter.SimplePropertyPreFilter;
+
+/**
+ * 排除JSON敏感属性
+ *
+ * @author tcctyn
+ */
+public class PropertyPreExcludeFilter extends SimplePropertyPreFilter
+{
+ public PropertyPreExcludeFilter()
+ {
+ }
+
+ public PropertyPreExcludeFilter addExcludes(String... filters)
+ {
+ for (int i = 0; i < filters.length; i++)
+ {
+ this.getExcludes().add(filters[i]);
+ }
+ return this;
+ }
+}
diff --git a/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/filter/RepeatableFilter.java b/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/filter/RepeatableFilter.java
new file mode 100644
index 0000000..8314c03
--- /dev/null
+++ b/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/filter/RepeatableFilter.java
@@ -0,0 +1,48 @@
+package com.tcctyn.common.core.filter;
+
+import com.tcctyn.common.core.utils.StringUtils;
+import org.springframework.http.MediaType;
+
+import javax.servlet.*;
+import javax.servlet.http.HttpServletRequest;
+import java.io.IOException;
+
+/**
+ * Repeatable 过滤器
+ *
+ * @author tcctyn
+ */
+public class RepeatableFilter implements Filter
+{
+ @Override
+ public void init(FilterConfig filterConfig) throws ServletException
+ {
+
+ }
+
+ @Override
+ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+ throws IOException, ServletException
+ {
+ ServletRequest requestWrapper = null;
+ if (request instanceof HttpServletRequest
+ && StringUtils.startsWithIgnoreCase(request.getContentType(), MediaType.APPLICATION_JSON_VALUE))
+ {
+ requestWrapper = new RepeatedlyRequestWrapper((HttpServletRequest) request, response);
+ }
+ if (null == requestWrapper)
+ {
+ chain.doFilter(request, response);
+ }
+ else
+ {
+ chain.doFilter(requestWrapper, response);
+ }
+ }
+
+ @Override
+ public void destroy()
+ {
+
+ }
+}
diff --git a/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/filter/RepeatedlyRequestWrapper.java b/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/filter/RepeatedlyRequestWrapper.java
new file mode 100644
index 0000000..162f2a1
--- /dev/null
+++ b/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/filter/RepeatedlyRequestWrapper.java
@@ -0,0 +1,77 @@
+package com.tcctyn.common.core.filter;
+
+import com.tcctyn.common.core.constant.Constants;
+import com.tcctyn.common.core.utils.http.HttpHelper;
+
+import javax.servlet.ReadListener;
+import javax.servlet.ServletInputStream;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+
+/**
+ * 构建可重复读取inputStream的request
+ *
+ * @author tcctyn
+ */
+public class RepeatedlyRequestWrapper extends HttpServletRequestWrapper
+{
+ private final byte[] body;
+
+ public RepeatedlyRequestWrapper(HttpServletRequest request, ServletResponse response) throws IOException
+ {
+ super(request);
+ request.setCharacterEncoding(Constants.UTF8);
+ response.setCharacterEncoding(Constants.UTF8);
+
+ body = HttpHelper.getBodyString(request).getBytes(Constants.UTF8);
+ }
+
+ @Override
+ public BufferedReader getReader() throws IOException
+ {
+ return new BufferedReader(new InputStreamReader(getInputStream()));
+ }
+
+ @Override
+ public ServletInputStream getInputStream() throws IOException
+ {
+ final ByteArrayInputStream bais = new ByteArrayInputStream(body);
+ return new ServletInputStream()
+ {
+ @Override
+ public int read() throws IOException
+ {
+ return bais.read();
+ }
+
+ @Override
+ public int available() throws IOException
+ {
+ return body.length;
+ }
+
+ @Override
+ public boolean isFinished()
+ {
+ return false;
+ }
+
+ @Override
+ public boolean isReady()
+ {
+ return false;
+ }
+
+ @Override
+ public void setReadListener(ReadListener readListener)
+ {
+
+ }
+ };
+ }
+}
diff --git a/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/filter/XssFilter.java b/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/filter/XssFilter.java
new file mode 100644
index 0000000..a44b712
--- /dev/null
+++ b/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/filter/XssFilter.java
@@ -0,0 +1,71 @@
+package com.tcctyn.common.core.filter;
+
+import com.tcctyn.common.core.enums.HttpMethod;
+import com.tcctyn.common.core.utils.StringUtils;
+
+import javax.servlet.*;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 防止XSS攻击的过滤器
+ *
+ * @author tcctyn
+ */
+public class XssFilter implements Filter
+{
+ /**
+ * 排除链接
+ */
+ public List excludes = new ArrayList<>();
+
+ @Override
+ public void init(FilterConfig filterConfig) throws ServletException
+ {
+ String tempExcludes = filterConfig.getInitParameter("excludes");
+ if (StringUtils.isNotEmpty(tempExcludes))
+ {
+ String[] urls = tempExcludes.split(",");
+ for (String url : urls)
+ {
+ excludes.add(url);
+ }
+ }
+ }
+
+ @Override
+ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+ throws IOException, ServletException
+ {
+ HttpServletRequest req = (HttpServletRequest) request;
+ HttpServletResponse resp = (HttpServletResponse) response;
+ if (handleExcludeURL(req, resp))
+ {
+ chain.doFilter(request, response);
+ return;
+ }
+ XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper((HttpServletRequest) request);
+ chain.doFilter(xssRequest, response);
+ }
+
+ private boolean handleExcludeURL(HttpServletRequest request, HttpServletResponse response)
+ {
+ String url = request.getServletPath();
+ String method = request.getMethod();
+ // GET DELETE 不过滤
+ if (method == null || HttpMethod.GET.matches(method) || HttpMethod.DELETE.matches(method))
+ {
+ return true;
+ }
+ return StringUtils.matches(url, excludes);
+ }
+
+ @Override
+ public void destroy()
+ {
+
+ }
+}
\ No newline at end of file
diff --git a/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/filter/XssHttpServletRequestWrapper.java b/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/filter/XssHttpServletRequestWrapper.java
new file mode 100644
index 0000000..fc59e3a
--- /dev/null
+++ b/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/filter/XssHttpServletRequestWrapper.java
@@ -0,0 +1,112 @@
+package com.tcctyn.common.core.filter;
+
+import com.tcctyn.common.core.utils.StringUtils;
+import com.tcctyn.common.core.utils.html.EscapeUtil;
+import org.apache.commons.io.IOUtils;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+
+import javax.servlet.ReadListener;
+import javax.servlet.ServletInputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+
+/**
+ * XSS过滤处理
+ *
+ * @author tcctyn
+ */
+public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper
+{
+ /**
+ * @param request
+ */
+ public XssHttpServletRequestWrapper(HttpServletRequest request)
+ {
+ super(request);
+ }
+
+ @Override
+ public String[] getParameterValues(String name)
+ {
+ String[] values = super.getParameterValues(name);
+ if (values != null)
+ {
+ int length = values.length;
+ String[] escapesValues = new String[length];
+ for (int i = 0; i < length; i++)
+ {
+ // 防xss攻击和过滤前后空格
+ escapesValues[i] = EscapeUtil.clean(values[i]).trim();
+ }
+ return escapesValues;
+ }
+ return super.getParameterValues(name);
+ }
+
+ @Override
+ public ServletInputStream getInputStream() throws IOException
+ {
+ // 非json类型,直接返回
+ if (!isJsonRequest())
+ {
+ return super.getInputStream();
+ }
+
+ // 为空,直接返回
+ String json = IOUtils.toString(super.getInputStream(), "utf-8");
+ if (StringUtils.isEmpty(json))
+ {
+ return super.getInputStream();
+ }
+
+ // xss过滤
+ json = EscapeUtil.clean(json).trim();
+ byte[] jsonBytes = json.getBytes("utf-8");
+ final ByteArrayInputStream bis = new ByteArrayInputStream(jsonBytes);
+ return new ServletInputStream()
+ {
+ @Override
+ public boolean isFinished()
+ {
+ return true;
+ }
+
+ @Override
+ public boolean isReady()
+ {
+ return true;
+ }
+
+ @Override
+ public int available() throws IOException
+ {
+ return jsonBytes.length;
+ }
+
+ @Override
+ public void setReadListener(ReadListener readListener)
+ {
+ }
+
+ @Override
+ public int read() throws IOException
+ {
+ return bis.read();
+ }
+ };
+ }
+
+ /**
+ * 是否是Json请求
+ *
+ * @param request
+ */
+ public boolean isJsonRequest()
+ {
+ String header = super.getHeader(HttpHeaders.CONTENT_TYPE);
+ return StringUtils.startsWithIgnoreCase(header, MediaType.APPLICATION_JSON_VALUE);
+ }
+}
\ No newline at end of file
diff --git a/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/redis/RedisCache.java b/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/redis/RedisCache.java
new file mode 100644
index 0000000..97e4b5f
--- /dev/null
+++ b/tcctyn-common/tcctyn-common-core/src/main/java/com/tcctyn/common/core/redis/RedisCache.java
@@ -0,0 +1,265 @@
+package com.tcctyn.common.core.redis;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.BoundSetOperations;
+import org.springframework.data.redis.core.HashOperations;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.core.ValueOperations;
+import org.springframework.stereotype.Component;
+
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * spring redis 工具类
+ *
+ * @author tcctyn
+ **/
+@SuppressWarnings(value = { "unchecked", "rawtypes" })
+@Component
+public class RedisCache
+{
+ @Autowired
+ public RedisTemplate redisTemplate;
+
+ /**
+ * 缓存基本的对象,Integer、String、实体类等
+ *
+ * @param key 缓存的键值
+ * @param value 缓存的值
+ */
+ public void setCacheObject(final String key, final T value)
+ {
+ redisTemplate.opsForValue().set(key, value);
+ }
+
+ /**
+ * 缓存基本的对象,Integer、String、实体类等
+ *
+ * @param key 缓存的键值
+ * @param value 缓存的值
+ * @param timeout 时间
+ * @param timeUnit 时间颗粒度
+ */
+ public void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit)
+ {
+ redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
+ }
+
+ /**
+ * 设置有效时间
+ *
+ * @param key Redis键
+ * @param timeout 超时时间
+ * @return true=设置成功;false=设置失败
+ */
+ public boolean expire(final String key, final long timeout)
+ {
+ return expire(key, timeout, TimeUnit.SECONDS);
+ }
+
+ /**
+ * 设置有效时间
+ *
+ * @param key Redis键
+ * @param timeout 超时时间
+ * @param unit 时间单位
+ * @return true=设置成功;false=设置失败
+ */
+ public boolean expire(final String key, final long timeout, final TimeUnit unit)
+ {
+ return redisTemplate.expire(key, timeout, unit);
+ }
+
+ /**
+ * 获取有效时间
+ *
+ * @param key Redis键
+ * @return 有效时间
+ */
+ public long getExpire(final String key)
+ {
+ return redisTemplate.getExpire(key);
+ }
+
+ /**
+ * 判断 key是否存在
+ *
+ * @param key 键
+ * @return true 存在 false不存在
+ */
+ public Boolean hasKey(String key)
+ {
+ return redisTemplate.hasKey(key);
+ }
+
+ /**
+ * 获得缓存的基本对象。
+ *
+ * @param key 缓存键值
+ * @return 缓存键值对应的数据
+ */
+ public T getCacheObject(final String key)
+ {
+ ValueOperations operation = redisTemplate.opsForValue();
+ return operation.get(key);
+ }
+
+ /**
+ * 删除单个对象
+ *
+ * @param key
+ */
+ public boolean deleteObject(final String key)
+ {
+ return redisTemplate.delete(key);
+ }
+
+ /**
+ * 删除集合对象
+ *
+ * @param collection 多个对象
+ * @return
+ */
+ public boolean deleteObject(final Collection collection)
+ {
+ return redisTemplate.delete(collection) > 0;
+ }
+
+ /**
+ * 缓存List数据
+ *
+ * @param key 缓存的键值
+ * @param dataList 待缓存的List数据
+ * @return 缓存的对象
+ */
+ public long setCacheList(final String key, final List dataList)
+ {
+ Long count = redisTemplate.opsForList().rightPushAll(key, dataList);
+ return count == null ? 0 : count;
+ }
+
+ /**
+ * 获得缓存的list对象
+ *
+ * @param key 缓存的键值
+ * @return 缓存键值对应的数据
+ */
+ public List getCacheList(final String key)
+ {
+ return redisTemplate.opsForList().range(key, 0, -1);
+ }
+
+ /**
+ * 缓存Set
+ *
+ * @param key 缓存键值
+ * @param dataSet 缓存的数据
+ * @return 缓存数据的对象
+ */
+ public BoundSetOperations setCacheSet(final String key, final Set dataSet)
+ {
+ BoundSetOperations setOperation = redisTemplate.boundSetOps(key);
+ Iterator it = dataSet.iterator();
+ while (it.hasNext())
+ {
+ setOperation.add(it.next());
+ }
+ return setOperation;
+ }
+
+ /**
+ * 获得缓存的set
+ *
+ * @param key
+ * @return
+ */
+ public Set getCacheSet(final String key)
+ {
+ return redisTemplate.opsForSet().members(key);
+ }
+
+ /**
+ * 缓存Map
+ *
+ * @param key
+ * @param dataMap
+ */
+ public void setCacheMap(final String key, final Map dataMap)
+ {
+ if (dataMap != null) {
+ redisTemplate.opsForHash().putAll(key, dataMap);
+ }
+ }
+
+ /**
+ * 获得缓存的Map
+ *
+ * @param key
+ * @return
+ */
+ public Map getCacheMap(final String key)
+ {
+ return redisTemplate.opsForHash().entries(key);
+ }
+
+ /**
+ * 往Hash中存入数据
+ *
+ * @param key Redis键
+ * @param hKey Hash键
+ * @param value 值
+ */
+ public void setCacheMapValue(final String key, final String hKey, final T value)
+ {
+ redisTemplate.opsForHash().put(key, hKey, value);
+ }
+
+ /**
+ * 获取Hash中的数据
+ *
+ * @param key Redis键
+ * @param hKey Hash键
+ * @return Hash中的对象
+ */
+ public T getCacheMapValue(final String key, final String hKey)
+ {
+ HashOperations opsForHash = redisTemplate.opsForHash();
+ return opsForHash.get(key, hKey);
+ }
+
+ /**
+ * 获取多个Hash中的数据
+ *
+ * @param key Redis键
+ * @param hKeys Hash键集合
+ * @return Hash对象集合
+ */
+ public List getMultiCacheMapValue(final String key, final Collection