抽奖系统lottery-system

项目背景

随着数字营销的兴起,企业越来越重视通过在线活动来吸引和留住客⼾。抽奖活动作为⼀种有效的营 销⼿段,能够显著提升⽤⼾参与度和品牌曝光率。于是我们就开发了以抽奖活动作为背景的Spring Boot项⽬

应用技术:利⽤MySQL、Redis、RabbitMQ等常⽤组件

项目的模块设计

项目展示

定义Jackson工具类用来做JSON和Java对象的转换

public class JacksonUtil {
    public JacksonUtil(){
    }

    private final static ObjectMapper OBJECT_MAPPER;

    static{
        OBJECT_MAPPER=new ObjectMapper();
    }

    private static ObjectMapper getObjectMapper(){
        return OBJECT_MAPPER;
    }

    private static  <T> T tryParse(Callable<T> parser) {
        return tryParse(parser, JacksonException.class);
    }

    private static  <T> T tryParse(Callable<T> parser, Class<? extends Exception> check) {
        try {
            return parser.call();
        } catch (Exception var4) {
            if (check.isAssignableFrom(var4.getClass())) {
                throw new JsonParseException(var4);
            }

            throw new IllegalStateException(var4);
        }
    }

//    序列化方法

    public static String writeValueAsString(Object object){
        return JacksonUtil.tryParse(()->{
           return JacksonUtil.getObjectMapper().writeValueAsString(object);
        });
    }

//    反序列化方法

    public static <T> T readValue(String str,Class<T> valueType){
        return JacksonUtil.tryParse(()->{
            return JacksonUtil.getObjectMapper().readValue(str,valueType);
        });
    }


//    反序列化List的方法
    public static <T> List<T> readListValue(String str,Class<?> paramClasses){
        JavaType javaType = JacksonUtil.getObjectMapper().getTypeFactory()
                .constructParametricType(List.class, paramClasses);
        return JacksonUtil.tryParse(()->{
            return JacksonUtil.getObjectMapper().readValue(str,javaType);
        });
    }

}

用户板块

用户注册

约定好前后端交互的接口

设计contoller层接口

@RequestMapping("/register")
    public CommonResult<UserRegisterResult> register(@RequestBody UserRegisterParam param){
        logger.info("注册用户信息:{}", JacksonUtil.writeValueAsString(param));
        UserRegisterDTO userRegisterDTO=userService.register(param);
        UserRegisterResult result=convertUserRegisterToResult(userRegisterDTO);
        return CommonResult.success(result);
    }

//将UserRegisterDTO类型  转换为UserRegisterResult后进行返回
private UserRegisterResult convertUserRegisterToResult(UserRegisterDTO userRegisterDTO) {
        if(userRegisterDTO==null){
            throw new ControllerException(ControllerErrorCodeConstants.REGISTER_ERROR);
        }

        UserRegisterResult result=new UserRegisterResult();
        result.setUserId(userRegisterDTO.getUserId());
        return result;
    }

将传递的参数定义为UserRegisterParam类,用于接受用户注册时提交的数据,并且通过@Validated 注解,来检查参数

@Data
public class UserRegisterParam implements Serializable {
    /**
     * 姓名
     */
    @NotBlank(message = "用户姓名不能为空")
    private String name;

    /**
     * 邮箱
     */
    @NotBlank(message = "用户邮箱不能为空")
    private String email;

    /**
     * 电话号码
     */
    @NotBlank(message = "用户手机号不能为空")
    private String phoneNumber;

    /**
     * 密码
     */
    private String password;

    /**
     * 身份信息
     */
    @NotBlank(message = "用户身份信息不能为空")
    private String identity;


}

由于前后端交互接口中数据中传输的是userId

@Data
public class UserRegisterResult implements Serializable {
    /**
     * 用户Id
     */
    private Long userId;

}

创建UserRegisterDTO用于controller和service层之间用来传输对象的,通常是因为数据库实体中可能有敏感信息,而VO更多是展示给前端的,所以他们需要一个中间层对象DTO

在用户注册的接口交互中,约定好返回给前端的数据是userId

@Data
public class UserRegisterDTO implements Serializable {

    private Long userId;
}

定义好UserService接口中的register方法

public interface UserService {

   UserRegisterDTO register(UserRegisterParam param);

通过UserServiceImpl进行实现

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;

    @Override
    public UserRegisterDTO register(UserRegisterParam param) {
        checkUserInfo(param);

//        进行用户的添加
        UserDO userDO=new UserDO();
        userDO.setUserName(param.getName());
        userDO.setEmail(param.getEmail());

        userDO.setPhoneNumber(new Encrypt(param.getPhoneNumber()));
        userDO.setIdentity(param.getIdentity());
        if(StringUtils.hasText(param.getPassword())){
            userDO.setPassword(DigestUtil.sha256Hex(param.getPassword()));
        }
//        存储数据
        userMapper.insert(userDO);

//        构造返回
        UserRegisterDTO userRegisterDTO=new UserRegisterDTO();
        userRegisterDTO.setUserId(userDO.getId());
        return userRegisterDTO;
    }

UserDO实体对象,用来存储数据,包含注册业务的相关字段,与数据库中注册后的用户信息字段一一对应

@Data
@EqualsAndHashCode(callSuper = true)//会将父类中的字段也会考虑进去
public class UserDO extends BaseDO {

    private String userName;

    private String email;

    private Encrypt phoneNumber;

    private String password;

    private String identity;
}

BaseDO是抽象父类,里面通常存放所有表公共的字段

@Data
public class BaseDO implements Serializable {

    private Long id;

    private Date gmtCrate;

    private Date gmtModified;
}

注:我们在设置密码的时候使用的Hutool中加密的摘要算法 DigestUtil.sha256Hex()详细了解请看加密解密-(Hutool-crypto)-优快云博客

在注册的时候我们需要判断注册的信息是否满足条件

public void checkUserInfo(UserRegisterParam param) {
//        检查姓名
        if(param==null){
            throw new ServiceException(ServiceErrorCodeConstants.REGISTER_INFO_IS_EMPTY);
        }

//        检查邮箱格式
        if(!RegexUtil.checkMail(param.getEmail())){
            throw new ServiceException(ServiceErrorCodeConstants.MAIL_ERROR);
        }
//        检查手机号码格式
        if(!RegexUtil.checkMobile(param.getPhoneNumber())){
            throw new ServiceException(ServiceErrorCodeConstants.PHONE_NUMBER_ERROR);

        }
//        检查⾝份信息
        if(UserIdentityEnum.fromName(param.getIdentity())==null){
            throw new ServiceException(ServiceErrorCodeConstants.IDENTITY_ERROR);
        }
//        管理员必须设置密码
        if(UserIdentityEnum.fromName(param.getIdentity())==UserIdentityEnum.ADMIN && !StringUtils.hasText(param.getPassword())){
            throw new ServiceException(ServiceErrorCodeConstants.PASSWORD_IS_EMPTY);
        }
//        检查密码格式,最少六位
        if(RegexUtil.checkPassword(param.getPassword())){
            throw new ServiceException(ServiceErrorCodeConstants.PASSWORD_ERROR);
        }

        //      检查邮箱是否被使用
        if(checkEmailUsed(param.getEmail())){
            throw new ServiceException(ServiceErrorCodeConstants.MAIL_USED);
        }

//      检查手机号是否被使用
        if(checkPhoneUsed(param.getPhoneNumber())){
            throw new ServiceException(ServiceErrorCodeConstants.PHONE_NUMBER_USED);
        }


    }

定义Service层的自定义异常

@Data
@EqualsAndHashCode(callSuper = true)
public class ServiceException extends RuntimeException{
    private Integer code;

    private String message;

    public ServiceException() {

    }

    public ServiceException(Integer code, String message) {
        this.code = code;
        this.message = message;
    }

    public ServiceException(ErrorCode errorCode) {
        this.code = errorCode.getCode();
        this.message = errorCode.getMsg();
    }

}

通过ErrorCode对象类,用来实现service层的错误码常量,方便统一管理,用于service层手动抛出异常

@Data
public class ErrorCode {
    private final Integer code;

    private final String msg;

    public ErrorCode(Integer code, String msg) {
        this.code = code;
        this.msg = msg;
    }
}
public interface ServiceErrorCodeConstants {

    ErrorCode REGISTER_INFO_IS_EMPTY = new ErrorCode(100, "注册信息为空");
    ErrorCode MAIL_ERROR = new ErrorCode(101, "邮箱错误");
    ErrorCode PHONE_NUMBER_ERROR = new ErrorCode(102, "手机号错误");
    ErrorCode IDENTITY_ERROR = new ErrorCode(103, "身份错误");
    ErrorCode PASSWORD_IS_EMPTY = new ErrorCode(104, "密码为空");
    ErrorCode PASSWORD_ERROR = new ErrorCode(105, "密码错误");
    ErrorCode MAIL_USED = new ErrorCode(106, "邮箱被使用");
    ErrorCode PHONE_NUMBER_USED = new ErrorCode(107, "手机号被使用");
    ErrorCode LOGIN_INFO_NOT_EXIST = new ErrorCode(108, "登录信息不存在");
    ErrorCode LOGIN_NOT_EXIST = new ErrorCode(109, "登录方式不存在");
    ErrorCode USER_INFO_IS_EMPTY = new ErrorCode(110, "用户信息为空");
    ErrorCode VERIFICATION_CODE_ERROR = new ErrorCode(111, "验证码校验失败");

}

在校验用户的邮箱,电话,密码格式,封装成工具类来完成

public class RegexUtil {

    /**
     * 邮箱:xxx@xx.xxx(形如:abc@qq.com)
     *
     * @param content
     * @return
     */
    public static boolean checkMail(String content) {
        if (!StringUtils.hasText(content)) {
            return false;
        }
        /**
         * ^ 表示匹配字符串的开始。
         * [a-z0-9]+ 表示匹配一个或多个小写字母或数字。
         * ([._\\-]*[a-z0-9])* 表示匹配零次或多次下述模式:一个点、下划线、反斜杠或短横线,后面跟着一个或多个小写字母或数字。这部分是可选的,并且可以重复出现。
         * @ 字符字面量,表示电子邮件地址中必须包含的"@"符号。
         * ([a-z0-9]+[-a-z0-9]*[a-z0-9]+.) 表示匹配一个或多个小写字母或数字,后面可以跟着零个或多个短横线或小写字母和数字,然后是一个小写字母或数字,最后是一个点。这是匹配域名的一部分。
         * {1,63} 表示前面的模式重复1到63次,这是对顶级域名长度的限制。
         * [a-z0-9]+ 表示匹配一个或多个小写字母或数字,这是顶级域名的开始部分。
         * $ 表示匹配字符串的结束。
         */
        String regex = "^[a-z0-9]+([._\\\\-]*[a-z0-9])*@([a-z0-9]+[-a-z0-9]*[a-z0-9]+.){1,63}[a-z0-9]+$";
        return Pattern.matches(regex, content);
    }

    /**
     * 手机号码以1开头的11位数字
     *
     * @param content
     * @return
     */
    public static boolean checkMobile(String content) {
        if (!StringUtils.hasText(content)) {
            return false;
        }
        /**
         * ^ 表示匹配字符串的开始。
         * 1 表示手机号码以数字1开头。
         * [3|4|5|6|7|8|9] 表示接下来的数字是3到9之间的任意一个数字。这是中国大陆手机号码的第二位数字,通常用来区分不同的运营商。
         * [0-9]{9} 表示后面跟着9个0到9之间的任意数字,这代表手机号码的剩余部分。
         * $ 表示匹配字符串的结束。
         */
        String regex = "^1[3|4|5|6|7|8|9][0-9]{9}$";
        return Pattern.matches(regex, content);
    }

    /**
     * 密码强度正则,6到12位
     *
     * @param content
     * @return
     */
    public static boolean checkPassword(String content){
        if (!StringUtils.hasText(content)) {
            return false;
        }
        /**
         * ^ 表示匹配字符串的开始。
         * [0-9A-Za-z] 表示匹配的字符可以是:
         * 0-9:任意一个数字(0到9)。
         * A-Z:任意一个大写字母(从A到Z)。
         * a-z:任意一个小写字母(从a到z)。
         * {6,12} 表示前面的字符集合(数字、大写字母和小写字母)可以重复出现6到12次。
         * $ 表示匹配字符串的结束。
         */
        String regex= "^[0-9A-Za-z]{6,12}$";
        return Pattern.matches(regex, content);
    }
}

在校验身份信息,因为用户的身份信息有两种,管理员注册时候不需要输入密码,用户注册的时候需要输入密码,所以我们使用了枚举类

formName方法用于将输入的身份和枚举中的身份进行匹配

@AllArgsConstructor
@Getter
public enum UserIdentityEnum {
    NORMAL(" 普通⽤⼾ "),
    ADMIN(" 管理员 ");

    private final String message;

    public static UserIdentityEnum fromName(String name) {
        for (UserIdentityEnum userIdentityEnum : UserIdentityEnum.values()) {
            if (userIdentityEnum.name().equalsIgnoreCase(name)) {
                return userIdentityEnum;
            }
        }
        return null;
    }
}

在检查邮箱是否已经被使用,我们需要从数据库中进行查询该邮箱是否存在

@Mapper
public interface UserMapper {

    @Select("select count(*) from user where email = #{email}")
    int countByMail(@Param("email") String email);

然后写一个方法,如果将数据库中拿出的数据返回定义为int类型,如果大于0,则说明该邮箱已经存在

public boolean checkEmailUsed(String email){
        int count=userMapper.countByMail(email);
        return count>0;
    }

在检查电话号码是否被使用,我们需要从数据库中进行查询该电话是否存在

    @Select("select count(*) from user where phone_number = #{phoneNumber}")
    int countByPhone(@Param("phoneNumber") Encrypt phoneNumber);

然后写一个方法,如果将数据库中拿出的数据返回定义为int类型,如果大于0,则说明该电话号码已经存在

private boolean checkPhoneUsed(String phoneNumber) {
        int count=userMapper.countByPhone(new Encrypt(phoneNumber));
        return count>0;
    }

但是特别需要注意的是:

我们新定义了一个Encrypt类,用于表示需要加密的字符串,因为电话号码我们进行加密,所以我们将电话号写入数据库时候需要进行加密,从数据库中拿出的时候需要进行解密

@Data
public class Encrypt {
    private String value;

    public Encrypt(){

    }
    public Encrypt(String value){
        this.value=value;
    }
}

处理加密的类型,也就是java层Encrypt类型和数据库层的VARCHAR进行转换,并且加密解密的逻辑代码

写入数据库前把 Encrypt.value 加密(变成十六进制字符串),写入 VARCHAR 列

从数据库读取值时把密文 解密 回原始字符串,并封装成 Encrypt 对象返回给 Java

@MappedTypes(Encrypt.class) //被处理的类型
@MappedJdbcTypes(JdbcType.VARCHAR)//转换后JDBC的类型
public class EncryptTypeHandle extends BaseTypeHandler<Encrypt> {
    private final byte[] KEYS= "123456789abcdefg".getBytes(StandardCharsets.UTF_8);

    /**设置参数
     *
     * @param ps   SQL的预设对象
     * @param i    需要赋值的索引位置
     * @param parameter 原本位置i需要赋的值
     * @param jdbcType  jdbc 类型
     * @throws SQLException
     */
    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, Encrypt parameter, JdbcType jdbcType) throws SQLException {
        if(parameter==null || parameter.getValue()==null){
            ps.setString(i,null);
            return;
        }
        System.out.println("加密的内容"+parameter.getValue());

        AES aes= SecureUtil.aes(KEYS);
        String resultEncrypt=aes.encryptHex(parameter.getValue());
        ps.setString(i,resultEncrypt);
    }

    /**获得值
     *
     * @param rs    结果集
     * @param columnName    索引名
     * @return
     * @throws SQLException
     */
    @Override
    public Encrypt getNullableResult(ResultSet rs, String columnName) throws SQLException {
        return decrypt(rs.getString(columnName));
    }


    /**获得值
     *
     * @param rs    结果集合
     * @param columnIndex   索引
     * @return
     * @throws SQLException
     */
    @Override
    public Encrypt getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        return decrypt(rs.getString(columnIndex));
    }

    /**获得值
     *
     * @param cs    结果集合
     * @param columnIndex   索引
     * @return
     * @throws SQLException
     */
    @Override
    public Encrypt getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        return decrypt(cs.getString(columnIndex));    }


    /**
     * 解密
     */

    public Encrypt decrypt(String s){
        if(!StringUtils.hasText(s)){
            return null;
        }
        return new Encrypt( SecureUtil.aes(KEYS).decryptStr(s));
    }
}

在注册完成后,我们需要将新注册的用户添加到数据库中

        userMapper.insert(userDO);

@Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id"),将数据库表中自增生成的 id 自动回填到 Java 对象的 id 属性中,实现对象属性和数据库列的“绑定。

useGeneratedKeys = true使用数据库的自增主键,

    @Insert("insert into user (user_name, email, phone_number, password, identity)" +
            " values (#{userName}, #{email}, #{phoneNumber}, #{password}, #{identity})")
    @Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id")
    void insert(UserDO userDO);

用户登录

发送验证码

我们在之前可以使用阿里云的短信服务,但是现在只能用于企业,个人已经无法使用

引入阿里云短信服务的依赖

        <!--阿里云短信服务-->

        <dependency>
            <groupId>com.aliyun</groupId>
            <artifactId>dysmsapi20170525</artifactId>
            <version>2.0.24</version>
        </dependency>

定义SMS短信工具类

@Component
public class SMSUtil {
    private static final Logger logger = LoggerFactory.getLogger(SMSUtil.class);

    @Value(value = "${sms.sign-name}")
    private String signName;
    @Value(value = "${sms.access-key-id}")
    private String accessKeyId;
    @Value(value = "${sms.access-key-secret}")
    private String accessKeySecret;

    /**
     * 发送短信
     *
     * @param templateCode 模板号
     * @param phoneNumbers 手机号
     * @param templateParam 模板参数 {"key":"value"}
     */
    public void sendMessage(String templateCode, String phoneNumbers, String templateParam) {
        try {
            Client client = createClient();
            com.aliyun.dysmsapi20170525.models.SendSmsRequest sendSmsRequest = new SendSmsRequest()
                    .setSignName(signName)
                    .setTemplateCode(templateCode)
                    .setPhoneNumbers(phoneNumbers)
                    .setTemplateParam(templateParam);
            RuntimeOptions runtime = new RuntimeOptions();
            SendSmsResponse response = client.sendSmsWithOptions(sendSmsRequest, runtime);
            if (null != response.getBody()
                    && null != response.getBody().getMessage()
                    && "OK".equals(response.getBody().getMessage())) {
                logger.info("向{}发送信息成功,templateCode={}", phoneNumbers, templateCode);
                return;
            }
            logger.error("向{}发送信息失败,templateCode={},失败原因:{}",
                    phoneNumbers, templateCode, response.getBody().getMessage());
        } catch (TeaException error) {
            logger.error("向{}发送信息失败,templateCode={}", phoneNumbers, templateCode, error);
        } catch (Exception _error) {
            TeaException error = new TeaException(_error.getMessage(), _error);
            logger.error("向{}发送信息失败,templateCode={}", phoneNumbers, templateCode, error);
        }
    }
}

配置 .properties文件

sms.access-key-id=
sms.access-key-secret=
sms.sign-name=

我们可以把验证码的生成也封装成一个工具类CaptchaUtil,也是依靠Hutool中的随机验证码功能完成的

@Configuration
public class CaptchaUtil {


//    根据Hutool中的工具生成验证码
    public String getCaptcha(int  length){
        // 自定义纯数字的验证码(随机length位数字,可重复)
        RandomGenerator randomGenerator = new RandomGenerator("0123456789", length);
        LineCaptcha lineCaptcha = cn.hutool.captcha.CaptchaUtil.createLineCaptcha(200, 100);
        lineCaptcha.setGenerator(randomGenerator);
        // 重新生成code
        lineCaptcha.createCode();
        return lineCaptcha.getCode();
    }
}

约定前后端的交互接口

设置controller层接口,调用VerificationCodeService

@RequestMapping("/verification-code/send")
    public CommonResult<Boolean> sendVerificationCode(String phoneNumber){
        logger.info("sendVerificationCode:{}",JacksonUtil.writeValueAsString(phoneNumber));
        verificationCodeService.setVerificationCode(phoneNumber);
        return CommonResult.success(Boolean.TRUE);
    }

定义VerificationCodeService中的生成验证码和获得验证码两个方法,通过VerificationCodeServiceImpl来实现两个方法

public interface VerificationCodeService {
//    生成验证码
    void setVerificationCode(String phoneNumber);

//    活得验证码
    String getVerificationCode(String phoneNumber);
}

由于短信服务无法用于个人,所以我们无法发送验证码,只能从后端打印出验证码进行使用,将发送验证码的代码进行注释

由于将验证码通常只有几分钟有效,而且redis可以帮助我们进行快速读取,所以我们使用redis缓存进行存储验证码

@Service
public class VerificationCodeServiceImpl implements VerificationCodeService {

    private static final String VERIFICATION_CODE_PREFIX="VERIFICATION_CODE_";
    private static final Long VERIFICATION_CODE_TIMEOUT=60L;
    private static final String VERIFICATION_CODE_TEMPLATE_CODE="SMS_465324787";

    @Autowired
    private CaptchaUtil captchaUtil;

    @Autowired
    private SMSUtil smsUtil;

    @Autowired
    private RedisUtil redisUtil;

    @Override
    public void setVerificationCode(String phoneNumber) {
//        校验手机号
        if (!RegexUtil.checkMobile(phoneNumber)){
            throw new ServiceException(ServiceErrorCodeConstants.PHONE_NUMBER_ERROR);
        }
//        生成随机验证码
        String captcha =captchaUtil.getCaptcha(6);
        System.out.println("手机号 " + phoneNumber + " 的验证码是: " + captcha);

//      因为个人无法目前发送验证码,所以使用随机数来代替
//        发送验证码
//        Map<String,String> map=new HashMap<>();
//        map.put("code",captcha);
//        smsUtil.sendMessage(VERIFICATION_CODE_TEMPLATE_CODE,
//                phoneNumber,
//                JacksonUtil.writeValueAsString(map));

//        将验证码存入到redis中
        redisUtil.set(VERIFICATION_CODE_PREFIX+phoneNumber,captcha,VERIFICATION_CODE_TIMEOUT);

    }

    @Override
    public String getVerificationCode(String phoneNumber) {
        //        校验手机号
        if (!RegexUtil.checkMobile(phoneNumber)){
            throw new ServiceException(ServiceErrorCodeConstants.PHONE_NUMBER_ERROR);
        }

//        从缓存中拿出验证码
        return redisUtil.get(VERIFICATION_CODE_PREFIX+phoneNumber);
    }
}

记得在使用时引入redis的依赖

<!--        redis的依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

我们在定义redis工具类时,写了五个方法,关于key的存储,读取,删除,查看是否存在,还有带有时间限制的存储,到一定的时间限制后,会自动删除。

@Configuration
public class RedisUtil {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    private static final Logger logger= LoggerFactory.getLogger(RedisUtil.class);

    public boolean set(String key,String value){
        try {
            stringRedisTemplate.opsForValue().set(key,value);
            return true;
        }catch (Exception e){
            logger.error("setError key,value:{},{}",key,value,e);
            return false;
        }

    }

    public boolean set(String key,String value,Long time){
        try {
            stringRedisTemplate.opsForValue().set(key,value,time, TimeUnit.SECONDS);
            return true;
        }catch (Exception e){
            logger.error("setError key,value,time:{},{},{}",key,value,time,e);
            return false;
        }
    }

    public String get(String key){
        try {
            return StringUtils.hasText(key)?stringRedisTemplate.opsForValue().get(key):null;
        }catch (Exception e){
            logger.error("getError:{}",key,e);
            return null;
        }
    }

    public boolean deleteKey(String ... key){
        try {
            if(key!=null && key.length>0){
                if(key.length==1){
                    stringRedisTemplate.delete(key[0]);
                }else {
                    stringRedisTemplate.delete((Collection<String>) CollectionUtils.arrayToList(key));
                }
            }

            return true;
        }catch (Exception e){
            logger.error("deleteError:{}",key,e);
            return false;
        }
    }


    public Boolean hasKey(String key){
        try {
            if(key!=null){
                return stringRedisTemplate.hasKey(key);
            }
            return false;
        }catch (Exception e){
            logger.error("hasKeyError:{}",key,e);
            return false;
        }
    }

}

设置时间限制的存储

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值