# 1. 流程图
# 2. 详细设计
## 2.1 用户表结构设计
CREATE TABLE users (
id varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
mobile varchar(11) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '手机号',
nickname varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '昵称',
real_name varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '真实姓名',
show_which_name int NOT NULL DEFAULT '2' COMMENT '对外展示名,1:真实姓名,2:昵称',
sex int NOT NULL COMMENT '性别,1:男 0:女 2:保密',
face varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '用户头像',
email varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '邮箱',
birthday date DEFAULT NULL COMMENT '生日',
country varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '国家',
province varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '省份',
city varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '城市',
district varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '区县',
description varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '介绍',
start_work_date date DEFAULT NULL COMMENT '我参加工作的时间',
position varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '我当前职位/职务',
role int NOT NULL COMMENT '身份角色,1: 求职者,2: HR。切换为HR时也可以登录求职者',
hr_in_which_company_id varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '成为HR后,认证的(绑定的)公司主键id',
hr_signature varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '我的一句话签名',
hr_tags varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '我的个性化标签',
created_time datetime NOT NULL COMMENT '创建时间',
updated_time datetime NOT NULL COMMENT '更新时间',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE KEY mobile (`mobile`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户表';
## 2.2 注册云短信服务
这里使用到了腾讯云短信,为什么用腾讯云不用阿里云呢?因为腾讯云免费100条...够测试使用了
## 2.3 配置依赖
在common包的pom中加入依赖
<dependency>
<groupId>com.tencentcloudapi</groupId>
<artifactId>tencentcloud-sdk-java</artifactId>
<!-- 可以在这里查最新的 https://central.sonatype.com/artifact/com.tencentcloudapi/tencentcloud-sdk-java -->
<version>3.1.1129</version>
</dependency>
## 2.4 全局统一返回 R
package resp;
import common.HttpStatusEnum;
import lombok.Getter;
@Getter
public class R<T> {
/**
*标识返回状态
*/
private Integer code;
/**
* 标识返回消息
*/
private String message;
/**
* 标识返回内容
*/
private T data;
public R() {
}
public R(Integer code, T data, String message) {
this.code = code;
this.data = data;
this.message = message;
}
/**
* 成功返回
*/
public static <T> R<T> ok(T data){
return new R<>(HttpStatusEnum.SUCCESS.getCode(),data,HttpStatusEnum.SUCCESS.getMessage());
}
/**
* 成功返回
*/
public static <T> R<T> ok(T data,String message){
return new R<>(HttpStatusEnum.SUCCESS.getCode(), data, message);
}
/**
* 失败返回
*/
public static <T> R<T> failed(HttpStatusEnum httpStatusEnum){
return new R<>(httpStatusEnum.getCode(),null, httpStatusEnum.getMessage());
}
/**
* 失败返回
*/
public static <T> R<T> failed(String message){
return new R<>(HttpStatusEnum.FAIL.getCode(), null, message);
}
}
## 2.5 添加自定义异常
package exception;
import lombok.Getter;
import lombok.Setter;
/**
* 自定义异常
* @author xiaoqiu
*/
@Setter
@Getter
public class XiaoQiuException extends RuntimeException{
private String code;
public XiaoQiuException(String code, String message) {
super(message);
this.code = code;
}
}
## 2.6 全局异常处理
package exception;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import resp.R;
/**
* 全局异常处理器
* @author xiaoqiu
*/
@Slf4j
@ControllerAdvice
@ResponseBody
public class GlobalExceptionHandler {
/**
* 处理自定义的业务异常
*/
@ExceptionHandler(value = XiaoQiuException.class)
public R<String> bizExceptionHandler(XiaoQiuException e) {
log.error("发生业务异常! msg: -> ", e);
return R.failed(e.getMessage());
}
/**
* 处理空指针的异常
*/
@ExceptionHandler(value = NullPointerException.class)
public R<String> exceptionHandler(NullPointerException e) {
log.error("发生空指针异常! msg: -> ", e);
return R.failed("发生空指针异常!");
}
/**
* 服务器异常
*/
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public R<String> exception(Exception e) {
log.error("服务器异常! msg: -> ", e);
return R.failed("服务器异常!");
}
}
## 2.7 在common包配置发送短信工具类
package utils;
import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.common.exception.TencentCloudSDKException;
import com.tencentcloudapi.common.profile.ClientProfile;
import com.tencentcloudapi.common.profile.HttpProfile;
import com.tencentcloudapi.sms.v20210111.SmsClient;
import com.tencentcloudapi.sms.v20210111.models.SendSmsRequest;
import com.tencentcloudapi.sms.v20210111.models.SendSmsResponse;
import exception.XiaoQiuException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import static exception.ResultCode.SMS_SEND_EXCEPTION;
@Component
@Slf4j
public class SMSUtils {
@Value("${sms.secretId:test}")
private String secretId;
@Value("${sms.secretKey:test}")
private String secretKey;
@Value("${sms.templateId:test}")
private String templateId;
@Value("${sms.sign.name:小秋}")
private String signName;
@Value("${sms.sdkAppId: 10000}")
private String sdkAppId;
@Value("${sms.test.switch:true}")
private boolean smsTestSwitch;
public void sendSMS(String phone, String code) {
try {
// 测试避免频发发送短信,关闭认证,直接成功
if (smsTestSwitch) {
return;
}
if (StringUtils.isBlank(phone) || StringUtils.isBlank(code)) {
throw new XiaoQiuException(SMS_SEND_EXCEPTION, "手机号或验证码为空!");
}
// 必要步骤:CAM密匙查询获取: https://console.cloud.tencent.com/cam/capi
SmsClient client = getSmsClient();
// 实例化一个请求对象,每个接口都会对应一个request对象
SendSmsRequest req = getSendSmsRequest(phone, code);
// 返回的resp是一个SendSmsResponse的实例,与请求对象对应
SendSmsResponse resp = client.SendSms(req);
// 输出json格式的字符串回包
log.info("手机号:{}, code: {}, 短信发送结果:{}", phone, code, SendSmsResponse.toJsonString(resp));
// System.out.println(SendSmsResponse.toJsonString(resp));
} catch (TencentCloudSDKException e) {
log.error("发送短信失败!", e);
}
}
private SmsClient getSmsClient() {
Credential cred = new Credential(secretId, secretKey);
// 实例化一个http选项,可选的,没有特殊需求可以跳过
HttpProfile httpProfile = new HttpProfile();
// httpProfile.setReqMethod("POST"); // 默认使用POST
/* SDK会自动指定域名。通常是不需要特地指定域名的,但是如果你访问的是金融区的服务
则必须手动指定域名,例如sms的上海金融区域名: sms.ap-shanghai-fsi.tencentcloudapi.com /
httpProfile.setEndpoint("sms.tencentcloudapi.com");
// 实例化一个client选项
ClientProfile clientProfile = new ClientProfile();
clientProfile.setHttpProfile(httpProfile);
// 实例化要请求产品的client对象,clientProfile是可选的
return new SmsClient(cred, "ap-nanjing", clientProfile);
}
private SendSmsRequest getSendSmsRequest(String phone, String code) {
SendSmsRequest req = new SendSmsRequest();
String[] phoneNumberSet = {"+86" + phone};//电话号码
req.setPhoneNumberSet(phoneNumberSet);
// 短信应用ID: 短信SdkAppId在 [短信控制台] 添加应用后生成的实际SdkAppId
req.setSmsSdkAppId(sdkAppId);
// 签名
req.setSignName(signName);
// 模板id:必须填写已审核通过的模板 ID。模板ID可登录 [短信控制台] 查看
req.setTemplateId(templateId);
/* 模板参数(自定义占位变量): 若无模板参数,则设置为空 */
String[] templateParamSet1 = {code};
req.setTemplateParamSet(templateParamSet1);
return req;
}
// 可以启动一个main函数测试
// public static void main(String[] args) {
// try {
// new SMSUtils().sendSMS("18812348888", "8888");
// } catch (Exception e) {
// e.printStackTrace();
// }
// }
}