From 07048dc1608c84651e7a46dfe89f03f376b22ab6 Mon Sep 17 00:00:00 2001
From: Helius <wangdoubleone@gmail.com>
Date: Thu, 16 Sep 2021 15:24:04 +0800
Subject: [PATCH] add sms code verify

---
 src/main/java/cc/mrbird/febs/common/utils/RedisUtils.java             |  549 ++++++++++++++++++++++++++
 src/main/java/cc/mrbird/febs/common/utils/RequestEncoder.java         |   71 +++
 src/main/java/cc/mrbird/febs/mall/controller/CommonController.java    |   71 +++
 src/main/java/cc/mrbird/febs/common/utils/AppContants.java            |   80 +++
 src/main/java/cc/mrbird/febs/common/utils/ZzSmsSend.java              |   68 +++
 src/main/java/cc/mrbird/febs/mall/service/CommonService.java          |   32 +
 pom.xml                                                               |   41 +
 src/main/java/cc/mrbird/febs/common/configure/WebMvcConfigure.java    |   23 +
 src/main/java/cc/mrbird/febs/common/interceptor/LoginInterceptor.java |   97 ++++
 src/main/java/cc/mrbird/febs/common/utils/SubMailSend.java            |  169 ++++++++
 10 files changed, 1,201 insertions(+), 0 deletions(-)

diff --git a/pom.xml b/pom.xml
index a84b44e..3ca16cf 100644
--- a/pom.xml
+++ b/pom.xml
@@ -21,9 +21,16 @@
         <mybatis.plus.version>3.3.1</mybatis.plus.version>
         <swagger.ui>2.9.2</swagger.ui>
         <tomcat.version>9.0.31</tomcat.version>
+        <hutool.version>5.3.1</hutool.version>
     </properties>
 
     <dependencies>
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-all</artifactId>
+            <version>${hutool.version}</version>
+        </dependency>
+
         <!-- Spring系列 -->
         <dependency>
             <groupId>org.springframework.boot</groupId>
@@ -213,6 +220,40 @@
             <artifactId>xml-apis</artifactId>
             <version>1.4.01</version>
         </dependency>
+
+        <!-- submail邮件 start -->
+        <dependency>
+            <groupId>commons-logging</groupId>
+            <artifactId>commons-logging</artifactId>
+            <version>1.1.1</version>
+        </dependency>
+        <dependency>
+            <groupId>commons-collections</groupId>
+            <artifactId>commons-collections</artifactId>
+            <version>3.2.1</version>
+        </dependency>
+<!--        <dependency>-->
+<!--            <groupId>org.apache.commons</groupId>-->
+<!--            <artifactId>commons-lang3</artifactId>-->
+<!--            <version>3.3.2</version>-->
+<!--        </dependency>-->
+        <dependency>
+            <groupId>net.sf.ezmorph</groupId>
+            <artifactId>ezmorph</artifactId>
+            <version>1.0.3</version>
+        </dependency>
+        <dependency>
+            <groupId>net.sf.json-lib</groupId>
+            <artifactId>json-lib</artifactId>
+            <version>2.2.3</version>
+            <classifier>jdk15</classifier>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpmime</artifactId>
+            <version>4.3.5</version>
+        </dependency>
+        <!-- submail邮件 end -->
     </dependencies>
 
     <build>
diff --git a/src/main/java/cc/mrbird/febs/common/configure/WebMvcConfigure.java b/src/main/java/cc/mrbird/febs/common/configure/WebMvcConfigure.java
new file mode 100644
index 0000000..ce2e742
--- /dev/null
+++ b/src/main/java/cc/mrbird/febs/common/configure/WebMvcConfigure.java
@@ -0,0 +1,23 @@
+package cc.mrbird.febs.common.configure;
+
+import cc.mrbird.febs.common.interceptor.LoginInterceptor;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistration;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+/**
+ * @author xxx
+ * @date 2020-08-24
+ **/
+@Configuration
+public class WebMvcConfigure implements WebMvcConfigurer {
+
+    @Override
+    public void addInterceptors(InterceptorRegistry registry) {
+        InterceptorRegistration registration = registry.addInterceptor(new LoginInterceptor());
+        registration.addPathPatterns("/api/**");
+        registration.excludePathPatterns("/api/login/**");
+        registration.excludePathPatterns("/api/common/**");
+    }
+}
diff --git a/src/main/java/cc/mrbird/febs/common/interceptor/LoginInterceptor.java b/src/main/java/cc/mrbird/febs/common/interceptor/LoginInterceptor.java
new file mode 100644
index 0000000..81c706b
--- /dev/null
+++ b/src/main/java/cc/mrbird/febs/common/interceptor/LoginInterceptor.java
@@ -0,0 +1,97 @@
+package cc.mrbird.febs.common.interceptor;
+
+import cc.mrbird.febs.common.entity.FebsResponse;
+import cc.mrbird.febs.common.utils.AppContants;
+import cc.mrbird.febs.common.utils.RedisUtils;
+import cc.mrbird.febs.common.utils.SpringContextUtil;
+import cc.mrbird.febs.mall.entity.MallMember;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.crypto.asymmetric.KeyType;
+import cn.hutool.crypto.asymmetric.RSA;
+import com.alibaba.fastjson.JSON;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.servlet.HandlerInterceptor;
+import org.springframework.web.servlet.ModelAndView;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+/**
+ * @author xxx
+ * @date 2020-08-24
+ **/
+@Slf4j
+public class LoginInterceptor implements HandlerInterceptor {
+
+    private final RedisUtils redisUtils = SpringContextUtil.getBean(RedisUtils.class);
+
+    @Override
+    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
+        String headerToken = request.getHeader("token");
+        if (StringUtils.isBlank(headerToken)) {
+            responseUnAuth(response);
+            return false;
+        }
+
+        String token = resolveToken(headerToken);
+
+        if (token == null || AppContants.TIME_OUT.equals(token)) {
+            responseUnAuth(response);
+            return false;
+        }
+
+        String userJsonStr = redisUtils.getString(token);
+        if (StringUtils.isBlank(userJsonStr)) {
+            responseUnAuth(response);
+            return false;
+        }
+        MallMember member = JSON.parseObject(userJsonStr, MallMember.class);
+
+        request.getSession().setAttribute("member", member);
+        return true;
+    }
+
+    @Override
+    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
+
+    }
+
+    @Override
+    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
+
+    }
+
+    private void responseUnAuth(HttpServletResponse response) throws IOException {
+        response.setCharacterEncoding("UTF-8");
+        response.setContentType("application/json; charset=utf-8");
+        response.getWriter().write(new ObjectMapper().writeValueAsString(new FebsResponse().code(HttpStatus.UNAUTHORIZED)));
+    }
+
+    private String resolveToken(String token) {
+        try {
+            RSA rsa = new RSA(AppContants.PRIVATE_KEY, null);
+            String[] tokens = StrUtil.split(rsa.decryptStr(token, KeyType.PrivateKey), "_");
+            if (verifyTokenExpired(Long.parseLong(tokens[1]))) {
+                return tokens[0];
+            } else {
+                return AppContants.TIME_OUT;
+            }
+        } catch (Exception e) {
+            log.error("#解析token异常#", e);
+            return null;
+        }
+    }
+
+    private Boolean verifyTokenExpired(Long time) {
+        boolean isDebug = false;
+        if (!isDebug) {
+            long currentTime = System.currentTimeMillis();
+            return currentTime - time <= 30000;
+        }
+        return true;
+    }
+}
diff --git a/src/main/java/cc/mrbird/febs/common/utils/AppContants.java b/src/main/java/cc/mrbird/febs/common/utils/AppContants.java
new file mode 100644
index 0000000..4ac326b
--- /dev/null
+++ b/src/main/java/cc/mrbird/febs/common/utils/AppContants.java
@@ -0,0 +1,80 @@
+package cc.mrbird.febs.common.utils;
+
+import java.math.BigDecimal;
+
+public class AppContants {
+
+    /**
+     * 系统用户
+     */
+    public static final String SYSTEM_USER = "system";
+    
+    public static final String PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCzIfAF0gZs9zn9ENRtz6ocHa8MGljmMmCsjLsRvTIAilBMypMJz/VNooAOhd8GTdsWm8FNGVhRauv7RfxorFJ4Um2UbweUQBIZP2pzZMnclHxhUmYZsn/6IaPzijiUNfEjygtE7ezvso/67ecZJwqfrtlbEjqUbRgo17Qj23suwQIDAQAB";
+
+    public static final String PRIVATE_KEY = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALMh8AXSBmz3Of0Q1G3PqhwdrwwaWOYyYKyMuxG9MgCKUEzKkwnP9U2igA6F3wZN2xabwU0ZWFFq6/tF/GisUnhSbZRvB5RAEhk/anNkydyUfGFSZhmyf/oho/OKOJQ18SPKC0Tt7O+yj/rt5xknCp+u2VsSOpRtGCjXtCPbey7BAgMBAAECgYAAgn+23IalJ7z0AejA2T7wLotxet+24/zPcOUVY1bxlnMtDMSHWh6mFmjL4cilMXKGqXHO4NwV+zppsCTMPXVMniI1IhfcyECgFjUrpWNCk30DlqhKePtIUCHdyLrc21mMLLMOQD/Hbga5kHZpR8r5poUAJ5Tnm5rjeyggwDj3MwJBAOnY+dyd39cRPtNLH0ANuR0Hd/WuA/RSRNbBTlXIVlc0hF1QXkgIWT2zA4uvwrFsz3F11YdPdfLgUhkFHlQuhVMCQQDEGhJELXqZ3AMlE9ykhUgv9HaCofGuCvzJnBGDKh4B3ufWG728gCNruoaRmzU8TOeVCABIQ2Un3SAenq0ylYUbAkB1y9PJm0lneAtyulPKm18VTW7TNk5No6eDmqqQMbO0iALpUpO7q2Dw4J03n1jusUYp/FaMq61ZpEAW1Go7s5d7AkEArgsJjTLj7ewDaoPfPrD/6XfJOpVqPvKHepOmQ0g9C6H/FtrWIZeEWFdamZ4ruFH08yL/xSLzg1bQ6/wecZecYwJALY4OP1Z81fhNjzg1AQd1CQQJJXUIkQpxXD/zAS5Cgf7XWfELIA4+86WA8qU1ILYHClFuV0SfxyGvI4ZEmpFosg==";
+
+    /**
+     * app用户登陆redis前缀
+     */
+    public static final String APP_LOGIN_PREFIX = "app_";
+
+    public static final String PC_LOGIN_PREFIX = "pc_";
+
+    /**
+     * token头部
+     */
+    public static final String TOKEN_HEADER = "Authorization";
+
+    /**
+     * token start with
+     */
+    public static final String TOKEN_START_WITH = "Bearer ";
+
+    /**
+     * 账号类型-手机号
+     */
+    public static final String ACCOUNT_TYPE_MOBILE = "1";
+
+    /**
+     * 账号类型-邮箱
+     */
+    public static final String ACCOUNT_TYPE_EMAIL = "2";
+
+    /**
+     * 系统推荐人id
+     */
+    public static final String SYSTEM_REFERER = "rxadr3";
+
+    /**
+     * 初始化金额
+     */
+    public static final BigDecimal INIT_MONEY = BigDecimal.ZERO;
+
+
+    public static final Integer INIT_SIMULATE_MONEY = 5000;
+
+    /**
+     * homeSymbols 接口状态值 币币
+     */
+    public static final int HOME_SYMBOLS_COIN = 1;
+
+    /**
+     * homeSymbols 接口状态值 合约
+     */
+    public static final int HOME_SYMBOLS_CONTRACT = 2;
+
+    /**
+     * 验证码前缀 手机
+     */
+    public static final String VERIFY_CODE_PREFIX = "CODE_SMS_";
+
+    /**
+     * 图片后缀
+     */
+    public static final String UPLOAD_IMAGE_SUFFIX = ".jpg";
+
+    public static final String TIME_OUT = "time_out";
+
+    public static final String CLOSING_ORDER_PREFIX = "closing_cnt_";
+
+}
diff --git a/src/main/java/cc/mrbird/febs/common/utils/RedisUtils.java b/src/main/java/cc/mrbird/febs/common/utils/RedisUtils.java
new file mode 100644
index 0000000..1ef5240
--- /dev/null
+++ b/src/main/java/cc/mrbird/febs/common/utils/RedisUtils.java
@@ -0,0 +1,549 @@
+package cc.mrbird.febs.common.utils;
+
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.stereotype.Component;
+import org.springframework.util.CollectionUtils;
+
+import javax.annotation.Resource;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+
+@Component
+public class RedisUtils {
+
+
+    @Resource
+    private RedisTemplate<String, Object> redisTemplate;
+
+
+    // =============================common============================
+    /**
+     * 指定缓存失效时间
+     * @param key 键
+     * @param time 时间(秒)
+     * @return
+     */
+    public boolean expire(String key, long time) {
+        try {
+            if (time > 0) {
+                redisTemplate.expire(key, time, TimeUnit.SECONDS);
+            }
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 根据key 获取过期时间
+     * @param key 键 不能为null
+     * @return 时间(秒) 返回0代表为永久有效
+     */
+    public long getExpire(String key) {
+        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
+    }
+
+    /**
+     * 判断key是否存在
+     * @param key 键
+     * @return true 存在 false不存在
+     */
+    public boolean hasKey(String key) {
+        try {
+            return redisTemplate.hasKey(key);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 删除缓存
+     * @param key 可以传一个值 或多个
+     */
+    @SuppressWarnings("unchecked")
+    public void del(String... key) {
+        if (key != null && key.length > 0) {
+            if (key.length == 1) {
+                redisTemplate.delete(key[0]);
+            } else {
+                redisTemplate.delete(CollectionUtils.arrayToList(key));
+            }
+        }
+    }
+
+    // ============================String=============================
+    /**
+     * 普通缓存获取
+     * @param key 键
+     * @return 值
+     */
+    public Object get(String key) {
+        return key == null ? null : redisTemplate.opsForValue().get(key);
+    }
+
+
+    /**
+     * 普通缓存获取
+     * @param key 键
+     * @return 值
+     */
+    public String getString(String key) {
+        Object obj = key == null ? null : redisTemplate.opsForValue().get(key);
+        if(obj!=null){
+            return obj.toString();
+        }
+        return null;
+    }
+
+    /**
+     * 普通缓存放入
+     * @param key 键
+     * @param value 值
+     * @return true成功 false失败
+     */
+    public boolean set(String key, Object value) {
+        try {
+            redisTemplate.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, Object value, long time) {
+        try {
+            if (time > 0) {
+                redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
+            } else {
+                set(key, value);
+            }
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    public boolean setNotExist(String key, Object value, long time) {
+        return redisTemplate.opsForValue().setIfAbsent(key, value, time, TimeUnit.SECONDS);
+    }
+
+    /**
+     * 递增
+     * @param key 键
+     * @param delta 要增加几(大于0)
+     * @return
+     */
+    public long incr(String key, long delta) {
+        if (delta < 0) {
+            throw new RuntimeException("递增因子必须大于0");
+        }
+        return redisTemplate.opsForValue().increment(key, delta);
+    }
+
+    /**
+     * 递减
+     * @param key 键
+     * @param delta 要减少几(小于0)
+     * @return
+     */
+    public long decr(String key, long delta) {
+        if (delta < 0) {
+            throw new RuntimeException("递减因子必须大于0");
+        }
+        return redisTemplate.opsForValue().increment(key, -delta);
+    }
+
+
+    // ================================Map=================================
+
+    /**
+     * HashGet
+     * @param key 键 不能为null
+     * @param item 项 不能为null
+     * @return 值
+     */
+    public Object hget(String key, String item) {
+        return redisTemplate.opsForHash().get(key, item);
+    }
+
+    /**
+     * 获取hashKey对应的所有键值
+     * @param key 键
+     * @return 对应的多个键值
+     */
+    public Map<Object, Object> hmget(String key) {
+        return redisTemplate.opsForHash().entries(key);
+    }
+    /**
+     * HashSet
+
+     * @param key 键
+     * @param map 对应多个键值
+     * @return true 成功 false 失败
+     */
+    public boolean hmset(String key, Map<String, Object> map) {
+        try {
+            redisTemplate.opsForHash().putAll(key, map);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+    /**
+     * HashSet 并设置时间
+     * @param key 键
+     * @param map 应多个键值
+     * @param time 时间(秒)
+     * @return true成功 false失败
+     */
+    public boolean hmset(String key, Map<String, Object> map, long time) {
+        try {
+            redisTemplate.opsForHash().putAll(key, map);
+            if (time > 0) {
+                expire(key, time);
+            }
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+
+    /**
+     * 向一张hash表中放入数据,如果不存在将创建
+     * @param key 键
+     * @param item 项
+     * @param value 值
+     * @return true 成功 false失败
+     */
+    public boolean hset(String key, String item, Object value) {
+        try {
+            redisTemplate.opsForHash().put(key, item, value);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+    /**
+     * 向一张hash表中放入数据,如果不存在将创建
+     * @param key 键
+     * @param item 项
+     * @param value 值
+     * @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
+     * @return true 成功 false失败
+     */
+    public boolean hset(String key, String item, Object value, long time) {
+        try {
+            redisTemplate.opsForHash().put(key, item, value);
+            if (time > 0) {
+                expire(key, time);
+            }
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+    /**
+     * 删除hash表中的值
+     * @param key 键 不能为null
+     * @param item 项 可以使多个 不能为null
+     */
+    public void hdel(String key, Object... item) {
+        redisTemplate.opsForHash().delete(key, item);
+    }
+    /**
+     * 判断hash表中是否有该项的值
+     * @param key 键 不能为null
+     * @param item 项 不能为null
+     * @return true 存在 false不存在
+     */
+    public boolean hHasKey(String key, String item) {
+        return redisTemplate.opsForHash().hasKey(key, item);
+    }
+
+    /**
+     * hash递增 如果不存在,就会创建一个 并把新增后的值返回
+     * @param key 键
+     * @param item 项
+     * @param by 要增加几(大于0)
+     * @return
+     */
+    public double hincr(String key, String item, double by) {
+        return redisTemplate.opsForHash().increment(key, item, by);
+    }
+
+    /**
+     * hash递减
+     * @param key 键
+     * @param item 项
+     * @param by 要减少记(小于0)
+     * @return
+     */
+    public double hdecr(String key, String item, double by) {
+        return redisTemplate.opsForHash().increment(key, item, -by);
+    }
+    // ============================set=============================
+    /**
+     * 根据key获取Set中的所有值
+     * @param key 键
+     * @return
+     */
+    public Set<Object> sGet(String key) {
+        try {
+            return redisTemplate.opsForSet().members(key);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+    /**
+     * 根据value从一个set中查询,是否存在
+     * @param key 键
+     * @param value 值
+     * @return true 存在 false不存在
+     */
+    public boolean sHasKey(String key, Object value) {
+        try {
+            return redisTemplate.opsForSet().isMember(key, value);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+    /**
+     * 将数据放入set缓存
+     * @param key 键
+     * @param values 值 可以是多个
+     * @return 成功个数
+     */
+    public long sSet(String key, Object... values) {
+        try {
+            return redisTemplate.opsForSet().add(key, values);
+        } catch (Exception e) {
+            e.printStackTrace();
+
+            return 0;
+        }
+    }
+    /**
+     336
+     * 将set数据放入缓存
+     337
+     * @param key 键
+     * @param time 时间(秒)
+     * @param values 值 可以是多个
+     * @return 成功个数
+     */
+    public long sSetAndTime(String key, long time, Object... values) {
+        try {
+            Long count = redisTemplate.opsForSet().add(key, values);
+            if (time > 0)
+                expire(key, time);
+            return count;
+
+        }catch(Exception e) {
+            e.printStackTrace();
+            return 0;
+        }
+    }
+    /**
+     * 获取set缓存的长度
+     * @param key 键
+     * @return
+     */
+
+    public long sGetSetSize(String key) {
+        try {
+            return redisTemplate.opsForSet().size(key);
+
+        } catch (Exception e) {
+            e.printStackTrace();
+            return 0;
+        }
+
+    }
+
+    /**
+     * 移除值为value的
+     * @param key 键
+     * @param values 值 可以是多个
+     * @return 移除的个数
+
+     */
+    public long setRemove(String key, Object... values) {
+        try {
+            Long count = redisTemplate.opsForSet().remove(key, values);
+            return count;
+
+        } catch (Exception e) {
+            e.printStackTrace();
+            return 0;
+        }
+    }
+    // ===============================list=================================
+    /**
+     * 获取list缓存的内容     * @param key 键
+     * @param start 开始
+     * @param end 结束 0 到 -1代表所有值
+     * @return
+     */
+    public List<Object> lGet(String key, long start, long end) {
+        try {
+            return redisTemplate.opsForList().range(key, start, end);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+
+    }
+
+    /**
+     * 获取list缓存的长度
+     * @param key 键
+
+     * @return
+     */
+    public long lGetListSize(String key) {        try {
+        return redisTemplate.opsForList().size(key);
+    } catch (Exception e) {
+
+        e.printStackTrace();
+        return 0;
+    }
+    }    /**
+     * 通过索引 获取list中的值
+     * @param key 键
+     * @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
+     * @return
+     */
+    public Object lGetIndex(String key, long index) {
+
+        try {
+            return redisTemplate.opsForList().index(key, index);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    /**
+     * 将list放入缓存
+     * @param key 键
+     * @param value 值
+     * @return
+     */
+    public boolean lSet(String key, Object value) {
+        try {
+            redisTemplate.opsForList().rightPush(key, value);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 将list放入缓存
+     * @param key 键
+     * @param value 值
+     * @param time 时间(秒)
+     * @return
+     */
+    public boolean lSet(String key, Object value, long time) {
+        try {
+            redisTemplate.opsForList().rightPush(key, value);
+            if (time > 0)
+                expire(key, time);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+    /**
+     * 将list放入缓存
+     * @param key 键
+     * @param value 值
+     * @return
+     */
+    public boolean lSet(String key, List<Object> value) {
+        try {
+            redisTemplate.opsForList().rightPushAll(key, value);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+    /**
+     * 将list放入缓存
+     *
+     * @param key 键
+     * @param value 值
+     * @param time 时间(秒)
+     * @return
+     */
+    public boolean lSet(String key, List<Object> value, long time) {
+        try {
+            redisTemplate.opsForList().rightPushAll(key, value);
+            if (time > 0)
+                expire(key, time);
+            return true;
+
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+    /**
+     * 根据索引修改list中的某条数据
+     * @param key 键
+     * @param index 索引
+     * @param value 值
+     * @return
+     */
+    public boolean lUpdateIndex(String key, long index, Object value) {
+        try {
+            redisTemplate.opsForList().set(key, index, value);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+
+    /**
+     * 移除N个值为value
+     * @param key 键
+     * @param count 移除多少个
+     * @param value 值
+     * @return 移除的个数
+     */
+    public long lRemove(String key, long count, Object value) {
+        try {
+            return redisTemplate.opsForList().remove(key, count, value);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return 0;
+        }
+    }
+}
diff --git a/src/main/java/cc/mrbird/febs/common/utils/RequestEncoder.java b/src/main/java/cc/mrbird/febs/common/utils/RequestEncoder.java
new file mode 100644
index 0000000..71cae5d
--- /dev/null
+++ b/src/main/java/cc/mrbird/febs/common/utils/RequestEncoder.java
@@ -0,0 +1,71 @@
+package cc.mrbird.febs.common.utils;
+
+import java.security.MessageDigest;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * 处理请求数据
+ * @author submail
+ *
+ */
+public class RequestEncoder {
+	
+	private static final char[] HEX_DIGITS = { '0', '1', '2', '3', '4', '5',
+			'6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
+	public static final String MD5 = "MD5";
+	public static final String SHA1 = "SHA1";
+	/**
+	 * 编码的字符串
+	 *
+	 * @param algorithm
+	 * @param str
+	 * @return String
+	 */
+	public static String encode(String algorithm, String str) {
+		if (str == null) {
+			return null;
+		}
+		try {
+			MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
+			messageDigest.update(str.getBytes("UTF-8"));
+			return getFormattedText(messageDigest.digest());
+		} catch (Exception e) {
+			throw new RuntimeException(e);
+		}
+
+	}
+	
+	/**
+	 *获取原始字节并将其格式化。
+	 * @param bytes
+	 *            the raw bytes from the digest.
+	 * @return the formatted bytes.
+	 */
+	private static String getFormattedText(byte[] bytes) {
+		int len = bytes.length;
+		StringBuilder buf = new StringBuilder(len * 2);
+		for (int j = 0; j < len; j++) { 			buf.append(HEX_DIGITS[(bytes[j] >> 4) & 0x0f]);
+			buf.append(HEX_DIGITS[bytes[j] & 0x0f]);
+		}
+		return buf.toString();
+	}
+	
+	public static String formatRequest(Map<String, Object> data){
+		Set<String> keySet = data.keySet();
+		Iterator<String> it = keySet.iterator();
+		StringBuffer sb = new StringBuffer();
+		while(it.hasNext()){
+			String key = it.next();
+			Object value = data.get(key);
+			if(value instanceof String){
+				sb.append(key + "=" + value + "&");
+			}
+		}
+		if(sb.length() != 0){
+			return sb.substring(0, sb.length() - 1);
+		}
+		return null;
+	}
+}
diff --git a/src/main/java/cc/mrbird/febs/common/utils/SubMailSend.java b/src/main/java/cc/mrbird/febs/common/utils/SubMailSend.java
new file mode 100644
index 0000000..9ff0e30
--- /dev/null
+++ b/src/main/java/cc/mrbird/febs/common/utils/SubMailSend.java
@@ -0,0 +1,169 @@
+package cc.mrbird.febs.common.utils;
+
+
+import cn.hutool.core.date.DatePattern;
+import cn.hutool.core.date.DateUtil;
+import net.sf.json.JSONObject;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.mime.MultipartEntityBuilder;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.protocol.HTTP;
+import org.apache.http.util.EntityUtils;
+
+import java.io.IOException;
+import java.util.Date;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * 邮件发送
+ *
+ * @author wzy
+ * @date 2020-05-27
+ **/
+public class SubMailSend {
+
+    /**
+     * 时间戳接口配置
+     */
+    public static final String TIMESTAMP = "https://api.mysubmail.com/service/timestamp";
+    /**
+     * API 请求接口配置
+     */
+    private static final String URL = "https://api.mysubmail.com/mail/xsend";
+
+    public static final String TYPE_MD5 = "md5";
+    public static final String TYPE_SHA1 = "sha1";
+
+    private static final String APP_ID = "16082";
+    private static final String APP_KEY = "f34c792a1112c16c190ed7190d386c4f";
+    private static final String FROM = "hibit@submail.hibit.cc";
+    private static final String SIGN_TYPE = "";
+
+    /**
+     * 发送验证码邮件
+     *
+     * @param to   对象
+     * @param code 验证码
+     * @return true or false
+     */
+    public static boolean sendMail(String to, String code) {
+        JSONObject vars = new JSONObject();
+        vars.put("code", code);
+
+        String project = "CVj6P";
+        return request(vars, project, to);
+    }
+
+    /**
+     * 发送充值成功邮件
+     *
+     * @param to      对象
+     * @param time    成功时间
+     * @param orderNo 订单编号
+     * @return true or false
+     */
+    public static boolean sendRechargeMail(String to, String time, String orderNo) {
+        JSONObject vars = new JSONObject();
+        vars.put("time", time);
+        vars.put("orderNo", orderNo);
+        String project = "4DvTC2";
+        return request(vars, project, to);
+    }
+
+    public static boolean sendWithdrawalMail(String to, String time) {
+        JSONObject vars = new JSONObject();
+        vars.put("time", time);
+        String project = "e3BO91";
+        return request(vars, project, to);
+    }
+
+
+    private static boolean request(JSONObject vars, String project, String to) {
+        TreeMap<String, Object> requestData = new TreeMap<String, Object>();
+        requestData.put("appid", APP_ID);
+        requestData.put("project", project);
+        requestData.put("to", to);
+        requestData.put("from", FROM);
+        if (!vars.isEmpty()) {
+            requestData.put("vars", vars.toString());
+        }
+
+        MultipartEntityBuilder builder = MultipartEntityBuilder.create();
+        @SuppressWarnings("deprecation")
+        ContentType contentType = ContentType.create(HTTP.PLAIN_TEXT_TYPE, HTTP.UTF_8);
+        for (Map.Entry<String, Object> entry : requestData.entrySet()) {
+            String key = entry.getKey();
+            Object value = entry.getValue();
+            if (value instanceof String) {
+                builder.addTextBody(key, String.valueOf(value), contentType);
+            }
+        }
+        if (SIGN_TYPE.equals(TYPE_MD5) || SIGN_TYPE.equals(TYPE_SHA1)) {
+            String timestamp = getTimestamp();
+            requestData.put("timestamp", timestamp);
+            requestData.put("sign_type", SIGN_TYPE);
+            String signStr = APP_ID + APP_KEY + RequestEncoder.formatRequest(requestData) + APP_ID + APP_KEY;
+
+            builder.addTextBody("timestamp", timestamp);
+            builder.addTextBody("sign_type", SIGN_TYPE);
+            builder.addTextBody("signature", RequestEncoder.encode(SIGN_TYPE, signStr), contentType);
+        } else {
+            builder.addTextBody("signature", APP_KEY, contentType);
+        }
+
+        HttpPost httpPost = new HttpPost(URL);
+        httpPost.addHeader("charset", "UTF-8");
+        httpPost.setEntity(builder.build());
+        try {
+            CloseableHttpClient closeableHttpClient = HttpClientBuilder.create().build();
+            HttpResponse response = closeableHttpClient.execute(httpPost);
+            HttpEntity httpEntity = response.getEntity();
+            if (httpEntity != null) {
+                String jsonStr = EntityUtils.toString(httpEntity, "UTF-8");
+                if ("success".equals(JSONObject.fromObject(jsonStr).getString("status"))) {
+                    return true;
+                }
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return false;
+    }
+
+
+    /**
+     * 获取时间戳
+     *
+     * @return
+     */
+    private static String getTimestamp() {
+        CloseableHttpClient closeableHttpClient = HttpClientBuilder.create().build();
+        HttpGet httpget = new HttpGet(TIMESTAMP);
+        try {
+            HttpResponse response = closeableHttpClient.execute(httpget);
+            HttpEntity httpEntity = response.getEntity();
+            String jsonStr = EntityUtils.toString(httpEntity, "UTF-8");
+            if (jsonStr != null) {
+                JSONObject json = JSONObject.fromObject(jsonStr);
+                return json.getString("timestamp");
+            }
+            closeableHttpClient.close();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+
+        return null;
+    }
+
+    public static void main(String[] args) {
+//        System.out.println(sendMail("546766039@qq.com", "123456"));
+
+        System.out.println(sendRechargeMail("546766039@qq.com", DateUtil.format(new Date(), DatePattern.NORM_DATETIME_PATTERN), "123456"));
+    }
+}
diff --git a/src/main/java/cc/mrbird/febs/common/utils/ZzSmsSend.java b/src/main/java/cc/mrbird/febs/common/utils/ZzSmsSend.java
new file mode 100644
index 0000000..85e1999
--- /dev/null
+++ b/src/main/java/cc/mrbird/febs/common/utils/ZzSmsSend.java
@@ -0,0 +1,68 @@
+package cc.mrbird.febs.common.utils;
+
+import cn.hutool.core.date.DatePattern;
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.core.util.XmlUtil;
+import cn.hutool.crypto.SecureUtil;
+import cn.hutool.http.HttpUtil;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author wzy
+ * @date 2021-03-25
+ **/
+@Slf4j
+public class ZzSmsSend {
+
+    private static final String URL = "http://zzsms365.com/v2sms.aspx?action=send";
+
+    private static final String USER_ID = "1654";
+    private static final String ACCOUNT = "1369815429";
+    private static final String PWD = "1369815429";
+
+    public static boolean sendVerifyCode(String telphone, String code, int time) {
+        String content = "【HiBit】您的验证码是{},请在{}分钟内输入,请勿泄露给他人,如非本人操作,请及时修改密码。";
+        return send(telphone, StrUtil.format(content, code, time));
+    }
+
+    private static boolean send(String telphone, String content) {
+        Map<String, Object> data = new HashMap<>();
+        Long time = System.currentTimeMillis();
+        data.put("userid", USER_ID);
+        data.put("timestamp", time);
+        data.put("mobile", telphone);
+        String signStr = ACCOUNT + PWD + time;
+        String sign = SecureUtil.md5(signStr);
+        data.put("sign", sign);
+        data.put("content", content);
+
+        String response = HttpUtil.post(URL, data);
+        log.info("短信发送:{}", response);
+        if ("Success".equals(XmlUtil.xmlToMap(response).get("returnstatus"))) {
+            return true;
+        } else {
+//            throw new GlobalException((String) XmlUtil.xmlToMap(response).get("message"));
+            return false;
+        }
+    }
+
+    public static void main(String[] args) {
+        Map<String, Object> data = new HashMap<>();
+        Long time = System.currentTimeMillis();
+        data.put("userid", USER_ID);
+        data.put("timestamp", time);
+        data.put("mobile", "15773002834");
+        String signStr = ACCOUNT + PWD + time;
+        String sign = SecureUtil.md5(signStr);
+        data.put("sign", sign);
+        data.put("content", "【HiBit】尊敬的用户,恭喜您于2010-03-25有一笔充值已成功到账,充值数量为10。");
+
+        String post = HttpUtil.post(URL, data);
+        System.out.println(post);
+    }
+}
diff --git a/src/main/java/cc/mrbird/febs/mall/controller/CommonController.java b/src/main/java/cc/mrbird/febs/mall/controller/CommonController.java
new file mode 100644
index 0000000..0c1db62
--- /dev/null
+++ b/src/main/java/cc/mrbird/febs/mall/controller/CommonController.java
@@ -0,0 +1,71 @@
+package cc.mrbird.febs.mall.controller;
+
+import cc.mrbird.febs.common.entity.FebsResponse;
+import cc.mrbird.febs.common.exception.FebsException;
+import cc.mrbird.febs.common.utils.AppContants;
+import cc.mrbird.febs.common.utils.RedisUtils;
+import cc.mrbird.febs.common.utils.SubMailSend;
+import cc.mrbird.febs.common.utils.ZzSmsSend;
+import cn.hutool.core.util.StrUtil;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author wzy
+ * @date 2021-09-16
+ **/
+@Slf4j
+@RestController
+@RequestMapping(value = "/api/common")
+@RequiredArgsConstructor
+@Api(value = "CommonController", tags = "公共请求类")
+public class CommonController {
+
+    private final RedisUtils redisUtils;
+
+    @ApiOperation(value = "获取验证码接口", notes = "获取验证码通用接口")
+    @GetMapping(value = "/verifyCode")
+    public FebsResponse verifyCode(@ApiParam(name = "account", value = "手机号", required = true) @RequestParam(value = "account") String account,
+                                   @ApiParam(name = "type", value = "类型1-手机号", required = true) @RequestParam("type") String type) {
+        log.info("#账号:{}, 类型:{}#", account, type);
+
+        Integer code = (int) ((Math.random() * 9 + 1) * 100000);
+        if (StrUtil.isNotBlank(redisUtils.getString(AppContants.VERIFY_CODE_PREFIX + account))) {
+            throw new FebsException("验证码已发送");
+        }
+
+        // 发送手机验证码
+        if (AppContants.ACCOUNT_TYPE_MOBILE.equals(type)) {
+            boolean result = ZzSmsSend.sendVerifyCode(account, code.toString(), 2);
+            if (result) {
+                Map<String, Object> map = new HashMap<>();
+                boolean flag = redisUtils.set(AppContants.VERIFY_CODE_PREFIX + account, code, 120);
+                map.put("code", flag);
+                return new FebsResponse().success().message("验证码发送成功");
+            }
+            // 发送邮件验证码
+        } else if (AppContants.ACCOUNT_TYPE_EMAIL.equals(type)) {
+            boolean flag = SubMailSend.sendMail(account, code.toString());
+            if (flag) {
+                redisUtils.set(AppContants.VERIFY_CODE_PREFIX + account, code, 120);
+                return new FebsResponse().success().message("验证码发送成功");
+            } else {
+                return new FebsResponse().fail().message("验证码发送失败");
+            }
+        } else {
+            log.info("未定义账号类型");
+            throw new FebsException("未定义账号类型");
+        }
+        return new FebsResponse().fail().message("验证码发送失败");
+    }
+}
diff --git a/src/main/java/cc/mrbird/febs/mall/service/CommonService.java b/src/main/java/cc/mrbird/febs/mall/service/CommonService.java
new file mode 100644
index 0000000..06e129d
--- /dev/null
+++ b/src/main/java/cc/mrbird/febs/mall/service/CommonService.java
@@ -0,0 +1,32 @@
+package cc.mrbird.febs.mall.service;
+
+import cc.mrbird.febs.common.utils.AppContants;
+import cc.mrbird.febs.common.utils.RedisUtils;
+import cn.hutool.core.util.StrUtil;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author wzy
+ * @date 2021-09-16
+ **/
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class CommonService {
+    private final RedisUtils redisUtils;
+
+    public boolean verifyCode(String account, String code) {
+        String cacheCode = redisUtils.getString(AppContants.VERIFY_CODE_PREFIX + account);
+        if (StrUtil.isBlank(cacheCode)) {
+            return false;
+        }
+        if (code.equals(cacheCode)) {
+            redisUtils.del(AppContants.VERIFY_CODE_PREFIX + account);
+            return true;
+        } else {
+            return false;
+        }
+    }
+}

--
Gitblit v1.9.1