Browse Source

阿里云短信

chao 4 years ago
parent
commit
258ff7bd70

+ 48 - 14
pom.xml

@@ -42,25 +42,59 @@
             <groupId>org.springframework.cloud</groupId>
             <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
         </dependency>
-		<dependency>
-			<groupId>org.springframework.boot</groupId>
-			<artifactId>spring-boot-starter-data-r2dbc</artifactId>
-		</dependency>
-		<dependency>
-			<groupId>dev.miku</groupId>
-			<artifactId>r2dbc-mysql</artifactId>
-			<scope>runtime</scope>
-		</dependency>
-		<dependency>
-			<groupId>mysql</groupId>
-			<artifactId>mysql-connector-java</artifactId>
-			<scope>runtime</scope>
-		</dependency>
+        <!-- mysql -->
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+            <scope>runtime</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.mybatis.spring.boot</groupId>
+            <artifactId>mybatis-spring-boot-starter</artifactId>
+            <version>2.1.4</version>
+        </dependency>
+        <dependency>
+            <groupId>com.github.pagehelper</groupId>
+            <artifactId>pagehelper-spring-boot-starter</artifactId>
+            <version>1.3.0</version>
+            <exclusions>
+                <exclusion>
+                    <artifactId>mybatis-spring-boot-starter</artifactId>
+                    <groupId>org.mybatis.spring.boot</groupId>
+                </exclusion>
+            </exclusions>
+        </dependency>
 		<dependency>
 			<groupId>org.projectlombok</groupId>
 			<artifactId>lombok</artifactId>
 			<optional>true</optional>
 		</dependency>
+        <!-- redis依赖包 -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-redis</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>io.lettuce</groupId>
+                    <artifactId>lettuce-core</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>redis.clients</groupId>
+            <artifactId>jedis</artifactId>
+        </dependency>
+        <!-- 阿里云短信sdk -->
+        <dependency>
+            <groupId>com.aliyun</groupId>
+            <artifactId>aliyun-java-sdk-core</artifactId>
+            <version>4.5.18</version>
+        </dependency>
+        <dependency>
+            <groupId>com.aliyun</groupId>
+            <artifactId>aliyun-java-sdk-dysmsapi</artifactId>
+            <version>2.1.0</version>
+        </dependency>
 
 
 		<dependency>

+ 18 - 0
src/main/java/com/caimei365/user/controller/ClubApi.java

@@ -0,0 +1,18 @@
+package com.caimei365.user.controller;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * 机构用户API
+ *
+ * @author : Charles
+ * @date : 2021/3/4
+ */
+@RestController
+@RequiredArgsConstructor
+@RequestMapping("/user/club")
+public class ClubApi {
+
+}

+ 0 - 47
src/main/java/com/caimei365/user/controller/TestApi.java

@@ -1,47 +0,0 @@
-package com.caimei365.user.controller;
-
-import com.caimei365.user.entity.BaseUser;
-import com.caimei365.user.service.BaseUserService;
-import lombok.RequiredArgsConstructor;
-import org.springframework.web.bind.annotation.*;
-import reactor.core.publisher.Mono;
-
-/**
- * Description
- *
- * @author : Charles
- * @date : 2021/02/20
- */
-
-@RestController
-@RequiredArgsConstructor
-@RequestMapping("/user")
-public class TestApi {
-
-    private final BaseUserService baseUserService;
-
-    @GetMapping("/test")
-    public String getTestString() {
-        return "test user";
-    }
-
-    @GetMapping("/get")
-    public Mono<BaseUser> findById(Integer id) {
-        return baseUserService.findById(id);
-    }
-
-    @PostMapping("/update")
-    public Mono<Integer> updateById(BaseUser user) {
-        return baseUserService.updateById(user);
-    }
-
-    @PostMapping("/insert")
-    public Mono<Void> insertUser(BaseUser user) {
-        return baseUserService.insertUser(user);
-    }
-
-    @PostMapping("/delete")
-    public Mono<Void> deleteById(Integer id) {
-        return baseUserService.deleteById(id);
-    }
-}

+ 51 - 0
src/main/java/com/caimei365/user/controller/UserApi.java

@@ -0,0 +1,51 @@
+package com.caimei365.user.controller;
+
+import com.caimei365.user.model.JsonModel;
+import com.caimei365.user.service.UserService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.Map;
+
+/**
+ * 用户公共API
+ *
+ * @author : Charles
+ * @date : 2021/3/4
+ */
+@RestController
+@RequiredArgsConstructor
+@RequestMapping("/user")
+public class UserApi {
+
+    private final UserService userService;
+
+    /**
+     * 获取图片验证码
+     * spi旧接口:/user/getImgVerifyCode
+     * @param platformType 0:www,1:crm/h5,2:小程序
+     */
+    @GetMapping("/captcha")
+    public JsonModel<Map<String, Object>> getCaptchaImage(Integer platformType) {
+        return userService.getCaptchaImage(platformType);
+    }
+
+
+    /**
+     * 短信验证码
+     * spi旧接口:/user/activateCodeByReg
+     * @param mobile
+     * @param activateCodeType 1:找回密码,2:手机号注册机构,3:供应商注册
+     * @param imgCode
+     * @param token
+     * @return
+     */
+    @GetMapping("/sms/code")
+    public JsonModel getSmsCode(String mobile, String activateCodeType, String imgCode, String token, Integer platformType) {
+        return null;//loginService.activateCodeByReg(mobile, activateCodeType, imgCode, token, platformType);
+    }
+
+
+}

+ 0 - 53
src/main/java/com/caimei365/user/dao/BaseUserDao.java

@@ -1,53 +0,0 @@
-package com.caimei365.user.dao;
-
-import com.caimei365.user.entity.BaseUser;
-import org.springframework.data.r2dbc.repository.Modifying;
-import org.springframework.data.r2dbc.repository.Query;
-import org.springframework.data.repository.reactive.ReactiveCrudRepository;
-import org.springframework.stereotype.Repository;
-import reactor.core.publisher.Mono;
-
-/**
- * Description
- *
- * @author : Charles
- * @date : 2021/3/2
- */
-@Repository
-public interface BaseUserDao extends ReactiveCrudRepository<BaseUser, Integer> {
-    /**
-     * 根据用户id查询用户
-     * @param id userId
-     * @return BaseUser
-     */
-    @Override
-    @Query("select userId as id, userMobile as mobile, userName as name from base_user where userId= :id")
-    Mono<BaseUser> findById(Integer id);
-
-    /**
-     * 更新用户名
-     * @param id userId
-     * @param name userName
-     */
-    @Modifying
-    @Query("update base_user set userName= :name where userId= :id")
-    Mono<Integer> updateNameById(Integer id, String name);
-
-    /**
-     * 新增用户
-     * @param name userName
-     * @param mobile userMobile
-     */
-    @Modifying
-    @Query("insert into base_user(userName, userMobile) values (:name, :mobile)")
-    Mono<Void> insertUser(String name, String mobile);
-
-    /**
-     * 根据用户id删除用户
-     * @param id userId
-     */
-    @Override
-    @Modifying
-    @Query("delete from base_user where userId= :id")
-    Mono<Void> deleteById(Integer id);
-}

+ 1 - 1
src/main/java/com/caimei365/user/entity/BaseUser.java → src/main/java/com/caimei365/user/model/BaseUser.java

@@ -1,4 +1,4 @@
-package com.caimei365.user.entity;
+package com.caimei365.user.model;
 
 import lombok.Data;
 

+ 81 - 0
src/main/java/com/caimei365/user/model/JsonModel.java

@@ -0,0 +1,81 @@
+package com.caimei365.user.model;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 全局API返回值
+ *
+ * @author : Charles
+ * @date : 2021/3/4
+ */
+@Data
+public class JsonModel<T> implements Serializable {
+    /** 状态码 */
+    private int code;
+    /** 提示信息 */
+    private String msg;
+    /** 返回的数据 */
+    private T data;
+
+    private JsonModel() {}
+
+    private JsonModel(int code, String msg) {
+        this.code = code;
+        this.msg = msg;
+    }
+
+    private JsonModel(int code, String msg, T data) {
+        this.code = code;
+        this.msg = msg;
+        this.data = data;
+    }
+
+    public static JsonModel success() {
+        return new JsonModel<>(0, "操作成功");
+    }
+
+    public static<T> JsonModel<T> success(T data) {
+        return new JsonModel<>(0, "操作成功", data);
+    }
+
+    public static<T> JsonModel<T> success(String msg, T data) {
+        return new JsonModel<>(0, msg, data);
+    }
+
+    public static<T> JsonModel<T> success(int code, String msg, T data) {
+        return new JsonModel<>(code, msg, data);
+    }
+
+    public static JsonModel error() {
+        return new JsonModel<>(-1, "操作失败");
+    }
+
+    public static JsonModel error(String msg) {
+        return new JsonModel<>(-1, msg);
+    }
+
+    public static JsonModel error(int code, String msg) {
+        return new JsonModel<>(code, msg);
+    }
+
+    public static<T> JsonModel<T> error(T data) {
+        return new JsonModel<>(-1, "操作失败", data);
+    }
+
+    public static<T> JsonModel<T> error(String msg, T data) {
+        return new JsonModel<>(-1, msg, data);
+    }
+
+    public static<T> JsonModel<T> error(int code, String msg, T data) {
+        return new JsonModel<>(code, msg, data);
+    }
+
+    @Override
+    public String toString() {
+        return "JsonModel{" + "code=" + code + ", msg='" + msg + '\'' + ", data=" + data + '}';
+    }
+
+    private static final long serialVersionUID = 1L;
+}

+ 0 - 39
src/main/java/com/caimei365/user/service/BaseUserService.java

@@ -1,39 +0,0 @@
-package com.caimei365.user.service;
-
-import com.caimei365.user.dao.BaseUserDao;
-import com.caimei365.user.entity.BaseUser;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.stereotype.Service;
-import reactor.core.publisher.Mono;
-
-/**
- * Description
- *
- * @author : Charles
- * @date : 2021/3/2
- */
-@Slf4j
-@Service
-@RequiredArgsConstructor
-public class BaseUserService {
-
-    private final BaseUserDao baseUserDao;
-
-    public Mono<BaseUser> findById (Integer id) {
-        return baseUserDao.findById(id);
-    }
-
-    public Mono<Integer> updateById(BaseUser user){
-        return baseUserDao.updateNameById(user.getId(), user.getName());
-    }
-
-    public Mono<Void> insertUser(BaseUser user){
-        return baseUserDao.insertUser(user.getName(), user.getMobile());
-    }
-
-    public Mono<Void> deleteById(Integer id) {
-        return baseUserDao.deleteById(id);
-    }
-
-}

+ 278 - 0
src/main/java/com/caimei365/user/service/RedisService.java

@@ -0,0 +1,278 @@
+package com.caimei365.user.service;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.core.ValueOperations;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.io.Serializable;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Description
+ *
+ * @author : Charles
+ * @date : 2021/3/4
+ */
+@Slf4j
+@Service
+public class RedisService {
+
+	@Resource
+	private RedisTemplate<Serializable, Object> redisTemplate;
+
+    /**
+     * 批量删除
+     * @param keys
+     */
+	public void remove(String... keys) {
+		for(String key :keys){
+			remove(key);
+		}
+	}
+
+    /**
+     * 批量删除正则匹配到的
+     * @param pattern
+     */
+	public void removePattern(String pattern) {
+		Set<Serializable> keys = redisTemplate.keys(pattern);
+        assert keys != null;
+        if (keys.size() > 0){
+			redisTemplate.delete(keys);
+		}
+	}
+
+    /**
+     * 删除
+     * @param key
+     */
+	public void remove(String key) {
+		if (exists(key)) {
+			redisTemplate.delete(key);
+		}
+	}
+
+    /**
+     * 判断缓存中是否存在
+     * @param key
+     * @return boolean
+     */
+	public boolean exists(String key) {
+		return key.isEmpty() ? false : redisTemplate.hasKey(key);
+	}
+
+    /**
+     * 读取缓存
+     * @param key
+     * @return
+     */
+	public Object get(String key) {
+		Object result = null;
+		ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
+		result = operations.get(key);
+		return result;
+	}
+
+    /**
+     * 写入缓存
+     * @param key
+     * @param value
+     * @return
+     */
+	public boolean set(String key, Object value) {
+		boolean result = false;
+		try {
+			ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
+			operations.set(key, value);
+			result = true;
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+		return result;
+	}
+
+    /**
+     * 写入缓存并加上过期时间(秒)
+     * @param key
+     * @param value
+     * @param expireTimeSeconds
+     * @return
+     */
+	public boolean set(String key, Object value, Long expireTimeSeconds) {
+		boolean result = false;
+		try {
+			ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
+			operations.set(key, value);
+			redisTemplate.expire(key, expireTimeSeconds, TimeUnit.SECONDS);
+			result = true;
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+		return result;
+	}
+
+    /* **************************** 针对list操作的方法 **************************** */
+    /**
+     * 在key对应list的尾部添加
+     * @param key
+     * @param value
+     * @return
+     */
+	public long rightPushForList(String key, Object value) {
+		return redisTemplate.opsForList().rightPush(key, value);
+	}
+
+    /**
+     * 在key对应list的头部添加
+     * @param key
+     * @param value
+     * @return
+     */
+	public long leftPushForList(String key, Object value) {
+		return redisTemplate.opsForList().leftPush(key, value);
+	}
+
+    /**
+     * key对应list的长度
+     * @param key
+     * @return
+     */
+	public long listSize(String key) {
+		return redisTemplate.opsForList().size(key);
+	}
+
+    /**
+     * 获取list集合
+     * @param Key
+     * @param begin
+     * @param end
+     * @return
+     */
+	public List<?> getList(String Key, int begin, int end) {
+		return redisTemplate.opsForList().range(Key, begin, end);
+	}
+
+    /**
+     * 在key对应list的尾部移除
+     * @param key
+     * @return
+     */
+	public Object rightPopForList(String key) {
+		return redisTemplate.opsForList().rightPop(key);
+	}
+
+    /**
+     * 在key对应list的头部移除
+     * @param key
+     * @return
+     */
+	public Object leftPopForList(String key) {
+		return redisTemplate.opsForList().leftPop(key);
+	}
+
+    /**
+     * 移除list的index位置上的值
+     * @param Key
+     * @param index
+     * @param value
+     */
+	public void removeList(String Key, long index, Object value) {
+		redisTemplate.opsForList().remove(Key, index, value);
+	}
+
+    /**
+     * 重置list的index位置上的值
+     * @param Key
+     * @param index
+     * @param value
+     */
+	public void setList(String Key, long index, Object value) {
+		redisTemplate.opsForList().set(Key, index, value);
+	}
+
+
+    /**
+     * 写入list
+     * @param key
+     * @param list
+     */
+	public void setList(String key, List list) {
+		if(list!=null&&list.size()>0){
+			for (Object object : list) {
+				rightPushForList(key,object);
+			}
+		}
+	}
+
+    /**
+     * 写入map
+     * @param key
+     * @param map
+     */
+	public void setMap(String key, Map<String, Object> map) {
+		redisTemplate.opsForHash().putAll(key, map);
+	}
+
+    /**
+     * 获取map
+     * @param key
+     * @param mapKey
+     * @return
+     */
+	public Object get(String key, String mapKey) {
+		return redisTemplate.opsForHash().get(key, mapKey);
+	}
+
+    /**
+     * 写入map
+     * @param key
+     * @param hashKey
+     * @param hashValue
+     */
+	public void setMapByKV(String key, Object hashKey, Object hashValue) {
+		redisTemplate.opsForHash().put(key, hashKey,hashValue);
+	}
+
+    /**
+     * 删除map中的某个key-value
+     * @param key
+     * @param hashKey
+     */
+	public void removeHash(String key, String hashKey) {
+		redisTemplate.opsForHash().delete(key, hashKey);
+	}
+
+    /**
+     *
+     * @param key
+     * @return
+     */
+	public Map<Object, Object> getEntries(String key) {
+		return redisTemplate.opsForHash().entries(key);
+	}
+
+    /**
+     *
+     * @param key
+     * @param step
+     * @return
+     */
+	public long increase(String key, long step) {
+		return redisTemplate.opsForValue().increment(key, step);
+	}
+
+    /**
+     * 获取失效时间
+     * @param key
+     * @return
+     */
+	public long getExpireTime(String key) {
+		return redisTemplate.getExpire(key, TimeUnit.SECONDS);
+	}
+
+}

+ 20 - 0
src/main/java/com/caimei365/user/service/UserService.java

@@ -0,0 +1,20 @@
+package com.caimei365.user.service;
+
+import com.caimei365.user.model.JsonModel;
+
+import java.util.Map;
+
+/**
+ * Description
+ *
+ * @author : Charles
+ * @date : 2021/3/4
+ */
+public interface UserService {
+    /**
+     * 获取图片验证码
+     *
+     * @param platformType 0:www,1:crm/h5,2:小程序
+     */
+    JsonModel<Map<String, Object>> getCaptchaImage(Integer platformType);
+}

+ 57 - 0
src/main/java/com/caimei365/user/service/impl/UserServiceImpl.java

@@ -0,0 +1,57 @@
+package com.caimei365.user.service.impl;
+
+import com.caimei365.user.model.JsonModel;
+import com.caimei365.user.service.RedisService;
+import com.caimei365.user.service.UserService;
+import com.caimei365.user.utils.ImageCaptchaUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.util.HashMap;
+import java.util.Map;
+
+
+/**
+ * Description
+ *
+ * @author : Charles
+ * @date : 2021/3/4
+ */
+@Slf4j
+@Service
+public class UserServiceImpl implements UserService {
+
+    @Resource
+    private RedisService redisService;
+
+    /**
+     * 获取图片验证码
+     *
+     * @param platformType 0:www,1:crm/h5,2:小程序
+     */
+    @Override
+    public JsonModel<Map<String, Object>> getCaptchaImage(Integer platformType) {
+        Map<String, Object> result = new HashMap<>(2);
+        try {
+            ImageCaptchaUtil.Captcha captcha = null;
+            String captchaKey = "";
+            while (captchaKey.isEmpty()) {
+                captcha = ImageCaptchaUtil.getCaptcha(200, 80, 4);
+                captchaKey = ImageCaptchaUtil.getCaptchaKey(captcha.getMd5Code(), platformType);
+                boolean exists = redisService.exists(captchaKey);
+                // 为了防止重复的图片验证码, 存在就循环重新生成
+                if (!exists) {
+                    // 保存5分钟
+                    redisService.set(captchaKey, captcha.getCode(), 5 * 60L);
+                    break;
+                }
+            }
+            result.put("baseImage", captcha.getBase64Image());
+            result.put("token", captcha.getMd5Code());
+            return JsonModel.success(result);
+        } catch (Exception e) {
+            return JsonModel.error("获取图片验证码异常", null);
+        }
+    }
+}

+ 118 - 0
src/main/java/com/caimei365/user/utils/AliyunSmsUtil.java

@@ -0,0 +1,118 @@
+package com.caimei365.user.utils;
+
+import com.aliyuncs.DefaultAcsClient;
+import com.aliyuncs.IAcsClient;
+import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest;
+import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
+import com.aliyuncs.exceptions.ClientException;
+import com.aliyuncs.http.MethodType;
+import com.aliyuncs.profile.DefaultProfile;
+import com.aliyuncs.profile.IClientProfile;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * 阿里云短信
+ *
+ * @author : Charles
+ * @date : 2021/3/5
+ */
+@Slf4j
+public class AliyunSmsUtil {
+    /** 短信API产品名称(短信产品名固定,无需修改) */
+    private static final String PRODUCT = "Dysmsapi";
+    /** 短信API产品域名(接口地址固定,无需修改) */
+    private static final String DOMAIN = "dysmsapi.aliyuncs.com";
+    /** 开发者自己的AK(在阿里云访问控制台寻找) */
+    private static final String ACCESS_KEY_ID = "LTAI4GBL3o4YkWnbKYgf2Xia";
+    private static final String ACCESS_KEY_SECRET = "dBjAXqbYiEPP6Ukuk2ZsXQeET7FVkK";
+    /**
+    * 可登录阿里云查看和定义短信模板:https://dysms.console.aliyun.com/dysms.htm#/domestic/text/template
+    */
+    public static boolean sendSms(String mobile, Integer type, String templateParam) {
+        String templateCode = null;
+        if (null == type || type<0) {
+            return false;
+        } else if (type == 0) {
+            // 模版内容: 验证码${code},您正在尝试变更重要信息,请妥善保管账户信息。
+            templateCode = "SMS_205240539";
+        } else if (type == 1) {
+            // 模版内容: 验证码${code},您正在尝试修改登录密码,请妥善保管账户信息。
+            templateCode = "SMS_205240540";
+        } else if (type == 2) {
+            // 模版内容: 验证码${code},您正在注册成为新用户,感谢您的支持!
+            templateCode = "SMS_205240541";
+        } else if (type == 3) {
+            // 模版内容: 验证码${code},您正尝试异地登录,若非本人操作,请勿泄露。
+            templateCode = "SMS_205240542";
+        } else if (type == 4) {
+            // 模版内容: 验证码${code},您正在登录,若非本人操作,请勿泄露。
+            templateCode = "SMS_205240543";
+        } else if (type == 5) {
+            // 模版内容: 验证码${code},您正在进行身份验证,打死不要告诉别人哦!
+            templateCode = "SMS_205240544";
+        } else if (type == 6) {
+            // 模版内容: 欢迎成为采美会员机构用户,您的登录账号为:${name},初始密码为:${password}。
+            templateCode = "SMS_205430877";
+        } else if (type == 7) {
+            // 模版内容: 欢迎成为采美普通机构用户,您的登录账号为:${name},初始密码为:${password},您可使用该账号密码登录采美365网和“采美采购商城”小程序。
+            templateCode = "SMS_205440777";
+        } else if (type == 8) {
+            // 模版内容: 欢迎注册采美365网,您的短信验证码为:${code},该验证码 5 分钟内有效,请勿泄漏于他人。
+            templateCode = "SMS_205435882";
+        } else if (type == 9) {
+            // 模版内容: 欢迎注册采美365网供应商账号,您的短信验证码为:${code},该验证码 5 分钟内有效,请勿泄漏于他人。
+            templateCode = "SMS_205445750";
+        } else if (type == 10) {
+            // 模版内容: 注册成功!您可以通过手机号${name}登录。
+            templateCode = "SMS_205445760";
+        } else if (type == 11) {
+            // 模版内容: 欢迎成为${name}的运营人员,您的邀请码为${code}。您可使用以下两种方式激活您的身份:1. 您可在微信搜索“采美采购商城”小程序,使用邀请码登录并绑定微信;2. 进入“采美采购商城”小程序后,使用邀请码登录并绑定微信。绑定微信后,您可通过微信授权直接登录“采美采购商城”小程序或微信扫码直接登录采美365网站。
+            templateCode = "SMS_205445789";
+        } else if (type == 12) {
+            // 模版内容: 欢迎成为机构运营人员,您的验证码为${code}。运营人员可通过微信授权直接登录采美365网。
+            templateCode = "SMS_205435899";
+        } else if (type == 13) {
+            // 模版内容: 您正在更换联系人手机号,您的验证码为:${code}。
+            templateCode = "SMS_205435893";
+        }
+        try {
+            //可自助调整超时时间
+            System.setProperty("sun.net.client.defaultConnectTimeout", "10000");
+            System.setProperty("sun.net.client.defaultReadTimeout", "10000");
+
+            //初始化acsClient,暂不支持region化
+            IClientProfile profile = DefaultProfile.getProfile("cn-hangzhou", ACCESS_KEY_ID, ACCESS_KEY_SECRET);
+            DefaultProfile.addEndpoint("cn-hangzhou", "cn-hangzhou", PRODUCT, DOMAIN);
+            IAcsClient acsClient = new DefaultAcsClient(profile);
+
+            //组装请求对象
+            SendSmsRequest request = new SendSmsRequest();
+            //使用post提交
+            request.setMethod(MethodType.POST);
+            //必填:待发送手机号。支持以逗号分隔的形式进行批量调用,批量上限为1000个手机号码,批量调用相对于单条调用及时性稍有延迟,验证码类型的短信推荐使用单条调用的方式;发送国际/港澳台消息时,接收号码格式为国际区号+号码,如“85200000000”
+            request.setPhoneNumbers(mobile);
+            //必填:短信签名-可在短信控制台中找到
+            request.setSignName("采美");
+            //必填:短信模板-可在短信控制台中找到,发送国际/港澳台消息时,请使用国际/港澳台短信模版
+            request.setTemplateCode(templateCode);
+            //可选:模板中的变量替换JSON串,如模板内容为"亲爱的${name},您的验证码为${code}"时,此处的值为{\"name\":\"Tom\", \"code\":\"123\"}";
+            request.setTemplateParam(templateParam);
+            //请求失败这里会抛ClientException异常
+            SendSmsResponse sendSmsResponse = acsClient.getAcsResponse(request);
+            if(sendSmsResponse.getCode() != null && "OK".equals(sendSmsResponse.getCode())) {
+                //请求成功
+                System.out.println("----------------阿里云短信接口返回的数据----------------");
+                System.out.println("Code=" + sendSmsResponse.getCode());
+                System.out.println("Message=" + sendSmsResponse.getMessage());
+                System.out.println("RequestId=" + sendSmsResponse.getRequestId());
+                System.out.println("BizId=" + sendSmsResponse.getBizId());
+                return true;
+            } else {
+                return false;
+            }
+        } catch (ClientException e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+}

+ 264 - 0
src/main/java/com/caimei365/user/utils/ImageCaptchaUtil.java

@@ -0,0 +1,264 @@
+package com.caimei365.user.utils;
+
+import org.apache.commons.codec.binary.Base64;
+
+import javax.imageio.ImageIO;
+import java.awt.*;
+import java.awt.geom.AffineTransform;
+import java.awt.image.BufferedImage;
+import java.io.*;
+import java.util.Arrays;
+import java.util.Random;
+
+/**
+ * Description
+ *
+ * @author : Charles
+ * @date : 2021/3/3
+ */
+public class ImageCaptchaUtil {
+    private static final String VERIFY_CODES = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ";
+    private static final String WWW_PREFIX = "www:vc:";
+    private static final String CRM_PREFIX = "miniProgram:vc:";
+    private static final String APPLETS_PREFIX = "crm:vc:";
+
+    private static Random random = new Random();
+
+    /**
+     * 定义一个验证码类
+     */
+    public static class Captcha {
+        /**
+         * 生成的字符串验证码
+         */
+        private String code;
+        /**
+         * 验证码字符串的加密
+         */
+        private String md5Code;
+        /**
+         * 验证码生成的图片(base64格式)
+         */
+        private String base64Image;
+
+        Captcha(String code, String base64Image) {
+            this.code = code;
+            this.base64Image = base64Image;
+        }
+
+        public String getCode() {
+            return code;
+        }
+        public void setCode(String code) {
+            this.code = code;
+        }
+        public String getMd5Code() throws Exception {
+            if (this.code != null) {
+                md5Code = code;
+                for (int i = 0; i < 3; i++) {
+                    md5Code = Md5Util.md5(md5Code);
+                }
+            }
+            return md5Code;
+        }
+        /*public void setMd5Code(String md5Code) {
+            this.md5Code = md5Code;
+        }*/
+        public String getBase64Image() {
+            return base64Image;
+        }
+        public void setBase64Image(String base64Image) {
+            this.base64Image = base64Image;
+        }
+    }
+
+    /**
+     * 获取验证码缓存key
+     *
+     * @param md5Code
+     * @param flag 0:www,1:crm/h5,2:小程序
+     * @return
+     */
+    public static String getCaptchaKey(String md5Code, Integer flag) {
+        if (0 == flag) {
+            return "www:vc:" + md5Code;
+        } else if (1 == flag) {
+            return "crm:vc:" + md5Code;
+        } else if (2 == flag) {
+            return "miniProgram:vc:" + md5Code;
+        } else{
+            return md5Code;
+        }
+    }
+
+    /**
+     * 图片验证码
+     *
+     * @param w 验证码图片宽度
+     * @param h 验证码图片高度
+     * @param size 验证码图片里面字符串位数
+     */
+    public static Captcha getCaptcha(int w, int h, int size) throws IOException {
+        // 使用系统默认字符源生成验证码
+        String code = generateCaptcha(size);
+        ByteArrayOutputStream outPutStream = new ByteArrayOutputStream();
+        drawCaptchaImage(w, h, outPutStream, code);
+        InputStream in = new ByteArrayInputStream(outPutStream.toByteArray());
+        byte[] data = null;
+        // 读取图片字节数组
+        try {
+            data = new byte[in.available()];
+            in.read(data);
+            in.close();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        // 对字节数组Base64编码
+        String base64 = Base64.encodeBase64String(data);
+        return new Captcha(code, "data:image/jpg;base64," + base64);
+    }
+
+    /**
+     * 生成验证码
+     * @param size 验证码长度
+     *
+     */
+    private static String generateCaptcha(int size) {
+        int charLength = VERIFY_CODES.length();
+        Random rand = new Random(System.currentTimeMillis());
+        StringBuilder verifyCode = new StringBuilder(size);
+        for (int i = 0; i < size; i++) {
+            verifyCode.append(VERIFY_CODES.charAt(rand.nextInt(charLength - 1)));
+        }
+        return verifyCode.toString();
+    }
+
+    /**
+     * 输出指定验证码图片流
+     */
+    private static void drawCaptchaImage(int w, int h, OutputStream os, String code) throws IOException {
+        int size = code.length();
+        BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
+        Random rand = new Random();
+        Graphics2D g2 = image.createGraphics();
+        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+        Color[] colors = new Color[5];
+        Color[] colorSpaces = new Color[]{Color.WHITE, Color.CYAN, Color.GRAY, Color.LIGHT_GRAY, Color.MAGENTA,
+                Color.ORANGE, Color.PINK, Color.YELLOW};
+        float[] fractions = new float[colors.length];
+        for (int i = 0; i < colors.length; i++) {
+            colors[i] = colorSpaces[rand.nextInt(colorSpaces.length)];
+            fractions[i] = rand.nextFloat();
+        }
+        Arrays.sort(fractions);
+        // 设置边框色
+        g2.setColor(Color.GRAY);
+        g2.fillRect(0, 0, w, h);
+        Color c = getRandColor(200, 250);
+        // 设置背景色
+        g2.setColor(c);
+        g2.fillRect(0, 2, w, h - 4);
+        // 绘制干扰线
+        Random random = new Random();
+        // 设置线条的颜色
+        g2.setColor(getRandColor(160, 200));
+        for (int i = 0; i < 20; i++) {
+            int x = random.nextInt(w - 1);
+            int y = random.nextInt(h - 1);
+            int xl = random.nextInt(6) + 1;
+            int yl = random.nextInt(12) + 1;
+            g2.drawLine(x, y, x + xl + 40, y + yl + 20);
+        }
+        // 添加噪点
+        float noiseRate = 0.05f;
+        int area = (int) (noiseRate * w * h);
+        for (int i = 0; i < area; i++) {
+            int x = random.nextInt(w);
+            int y = random.nextInt(h);
+            int rgb = getRandomIntColor();
+            image.setRGB(x, y, rgb);
+        }
+        // 使图片扭曲
+        distortion(g2, w, h, c);
+        g2.setColor(getRandColor(100, 160));
+        int fontSize = h - 4;
+        Font font = new Font("Algerian", Font.ITALIC, fontSize);
+        g2.setFont(font);
+        char[] chars = code.toCharArray();
+        for (int i = 0; i < size; i++) {
+            AffineTransform affine = new AffineTransform();
+            affine.setToRotation(Math.PI / 4 * rand.nextDouble() * (rand.nextBoolean() ? 1 : -1),
+                    (w / size) * i + fontSize / 2, h / 2);
+            g2.setTransform(affine);
+            g2.drawChars(chars, i, 1, ((w - 10) / size) * i + 5, h / 2 + fontSize / 2 - 10);
+        }
+        g2.dispose();
+        ImageIO.write(image, "jpg", os);
+    }
+
+    private static Color getRandColor(int fc, int bc) {
+        if (fc > 255) {
+            fc = 255;
+        }
+        if (bc > 255) {
+            bc = 255;
+        }
+        int r = fc + random.nextInt(bc - fc);
+        int g = fc + random.nextInt(bc - fc);
+        int b = fc + random.nextInt(bc - fc);
+        return new Color(r, g, b);
+    }
+
+    private static int getRandomIntColor() {
+        int[] rgb = getRandomRgb();
+        int color = 0;
+        for (int c : rgb) {
+            color = color << 8;
+            color = color | c;
+        }
+        return color;
+    }
+
+    private static int[] getRandomRgb() {
+        int[] rgb = new int[3];
+        for (int i = 0; i < 3; i++) {
+            rgb[i] = random.nextInt(255);
+        }
+        return rgb;
+    }
+
+    private static void distortion(Graphics g, int w1, int h1, Color color) {
+        distortionX(g, w1, h1, color);
+        distortionY(g, w1, h1, color);
+    }
+
+    private static void distortionX(Graphics g, int w1, int h1, Color color) {
+        int period = random.nextInt(2);
+        int frames = 1;
+        int phase = random.nextInt(2);
+        for (int i = 0; i < h1; i++) {
+            double d = (double) (period >> 1)
+                    * Math.sin((double) i / (double) period + (6.2831853071795862D * (double) phase) / (double) frames);
+            g.copyArea(0, i, w1, 1, (int) d, 0);
+            g.setColor(color);
+            g.drawLine((int) d, i, 0, i);
+            g.drawLine((int) d + w1, i, w1, i);
+        }
+    }
+
+    private static void distortionY(Graphics g, int w1, int h1, Color color) {
+        int period = random.nextInt(40) + 10;
+        int frames = 20;
+        int phase = 7;
+        for (int i = 0; i < w1; i++) {
+            double d = (double) (period >> 1)
+                    * Math.sin((double) i / (double) period + (6.2831853071795862D * (double) phase) / (double) frames);
+            g.copyArea(i, 0, 1, h1, 0, (int) d);
+            g.setColor(color);
+            g.drawLine(i, (int) d, i, 0);
+            g.drawLine(i, (int) d + h1, i, h1);
+
+        }
+
+    }
+}

+ 62 - 0
src/main/java/com/caimei365/user/utils/Md5Util.java

@@ -0,0 +1,62 @@
+package com.caimei365.user.utils;
+
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.util.UUID;
+
+/**
+ * md5加密工具类
+ *
+ * @author : Charles
+ * @date : 2021/3/3
+ */
+public class Md5Util {
+
+    private static final String HEX_DIGITS = "0123456789ABCDEF";
+
+    /**
+	 * md5加密
+	 * @param s:待加密字符串
+	 * @return 加密后16进制字符串
+	 */
+	public static String md5(String s) {
+	    try {
+	    	//实例化MessageDigest的MD5算法对象
+	        MessageDigest md = MessageDigest.getInstance("MD5");
+	        //通过digest方法返回哈希计算后的字节数组
+	        byte[] bytes = md.digest(s.getBytes(StandardCharsets.UTF_8));
+	        //将字节数组转换为16进制字符串并返回
+	        return toHex(bytes);
+	    }
+	    catch (Exception e) {
+	        throw new RuntimeException(e);
+	    }
+	}
+
+	/**
+	 * 获取随即盐
+	 * @return salt
+	 */
+	public static String salt(){
+		//利用UUID生成随机盐
+		UUID uuid = UUID.randomUUID();
+		//返回a2c64597-232f-4782-ab2d-9dfeb9d76932
+		String[] arr = uuid.toString().split("-");
+		return arr[0];
+	}
+
+	/**
+	 * 字节数组转换为16进制字符串
+	 * @param bytes byte数组
+	 * @return 16进制字符串
+	 */
+	private static String toHex(byte[] bytes) {
+	    final char[] hexDigits = HEX_DIGITS.toCharArray();
+	    StringBuilder ret = new StringBuilder(bytes.length * 2);
+        for (byte aByte : bytes) {
+            ret.append(hexDigits[(aByte >> 4) & 0x0f]);
+            ret.append(hexDigits[aByte & 0x0f]);
+        }
+	    return ret.toString();
+	}
+}

+ 21 - 3
src/main/resources/application.yml

@@ -5,11 +5,30 @@ server:
 spring:
   application:
     name: @artifactId@
-  r2dbc:
-    url: r2dbcs:mysql://192.168.2.100:3306/caimei?characterEncoding=UTF8&serverTimezone=Asia/Shanghai
+
+  datasource:
+    url: jdbc:mysql://192.168.2.100:3306/caimei?characterEncoding=UTF8&serverTimezone=Asia/Shanghai
     username: developer
     password: 05bZ/OxTB:X+yd%1
 
+  redis:
+    host: 119.29.0.46
+    port: 6380
+    password: 6#xsI%b4o@5c3RoE
+    #Redis数据库索引(默认为0)
+    database: 0
+    jedis:
+      pool:
+        max-active: 50
+        #连接池最大阻塞等待时间(使用负值表示没有限制)
+        max-wait: -1
+        #连接池中的最大空闲连接
+        max-idle: 20
+        #连接池中的最小空闲连接
+        min-idle: 2
+    #连接超时时间(毫秒)
+    timeout: 5000
+
 # 指定服务注册中心的地址
 eureka:
   instance:
@@ -24,4 +43,3 @@ eureka:
 logging:
   level:
     root: info
-    dev.miku.r2dbc.mysql.client.ReactorNettyClient: debug

+ 25 - 0
src/test/java/com/caimei365/user/AliSmsUtilTest.java

@@ -0,0 +1,25 @@
+package com.caimei365.user;
+
+import com.caimei365.user.utils.AliyunSmsUtil;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+
+/**
+ * Description
+ *
+ * @author : Charles
+ * @date : 2021/3/5
+ */
+@SpringBootTest
+public class AliSmsUtilTest {
+
+    @Test
+    void test1() {
+        //发短信
+        AliyunSmsUtil.sendSms("15814011616", 0, "{\"code\":\"123\"}");
+        AliyunSmsUtil.sendSms("15814011616", 0, "{code:456}");
+    }
+
+
+
+}

+ 30 - 0
src/test/java/com/caimei365/user/ReactiveRedisTest.java

@@ -0,0 +1,30 @@
+package com.caimei365.user;
+
+import com.caimei365.user.service.RedisService;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+
+import javax.annotation.Resource;
+
+/**
+ * Description
+ *
+ * @author : Charles
+ * @date : 2021/3/4
+ */
+@SpringBootTest
+class ReactiveRedisTest {
+
+    @Resource
+    private RedisService redisService;
+
+    @Test
+    void test1() {
+        // 保存5分钟
+        redisService.set("test1", "test1_value", 5 * 60L);
+        Object test1 = redisService.get("test1");
+        System.out.println(test1);
+    }
+
+}
+