前言

学完SpringBoot的项目,Github地址,欢迎start,一起学习!

第一天

一、技术选型

基于SpringBoot+VUE的前后端分离的仿照马蜂窝的项目。

后端选用的技术为:

  1. SpringBoot
  2. MySQL
  3. MyBatis-Plus
  4. Redis
  5. MongDB
  6. Elasticsearch

二、搭建项目

创建文件夹

本项目使用的搭建方式是多模块的搭建方式。我们首先需要在Idea的工作空间中新建一个文件夹,用于存放父目录。

在文件夹中创建一个父目录

这个是一个父目录,不写代码,主要的工作是用于引入一些所有的子目录都需要引入的依赖。

创建travel-core

由于我们需要写的domain、service、mapper是很多的子目录都需要的,为了防止代码冗余,我们将这些代码抽取成一个公共的模块,取名叫做:travel-core。

创建travel-website

本次系统采用的1是前后端分离的项目,我们将静态资源抽取出来成濑一个专门放纯静态页面的模块,不做任何1的业务逻辑的实现,仅仅实现前端数据的展示和js。

创建travel-website-api

既然有了前端的页面,如果想成为一个完整的项目,就必须需要接口,我们将和前端交互的接口抽取成一个模块。

创建travel-mgrsite

有了前台还不够,我们需要后台来进行管理,我们将后台的接口抽取成一个专门的模块。

三、引入依赖

3.1、父目录

我们需要将travel-core的核心代码管理起来,方便后面的模块调用。

  <dependencyManagement>
<dependencies>
<dependency>
<groupId>cn.linstudy.travel</groupId>
<artifactId>travel-core</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
</dependencyManagement>

完整的pom文件:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.3</version>
<relativePath/>
</parent> <groupId>cn.linstudy.travel</groupId>
<artifactId>travel-parent</artifactId>
<packaging>pom</packaging>
<version>1.0</version>
<description>父类,用于导入各种需要的依赖,不写代码</description>
<modules>
<module>travel-core</module>
<module>travel-mgrsite</module>
<module>travel-website-api</module>
</modules> <properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties> <dependencyManagement>
<dependencies>
<dependency>
<groupId>cn.linstudy.travel</groupId>
<artifactId>travel-core</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
</dependencyManagement> </project>

3.2、travel-core

接下来我们需要处理travel-core核心模块的依赖了,首先要做的是先引入父目录的依赖同时指定父目录的pom.xml文件的位置。

因为这个模块需要处理事情主要是一些通用的domain、service、mapper,所以需要引入常用的依赖。完整的pom文件为:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<!--需要先引入父目录-->
<parent>
<artifactId>travel-parent</artifactId>
<groupId>cn.linstudy.travel</groupId>
<version>1.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion> <artifactId>travel-core</artifactId>
<description>用于抽取重复的代码:mapper、service、domain</description> <properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties> <dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency> <dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.0</version>
</dependency> <dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.24</version>
</dependency> <dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency> <dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.74</version>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency> <dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>3.5.0</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency> <dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.9.3</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency> </dependencies> </project>

3.3、travel-website-api

我们需要在travel-website-api中引入travel-core,这样才可以获取到travel-core中抽取的代码,同时需要引入一些额外的依赖。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>travel-parent</artifactId>
<groupId>cn.linstudy.travel</groupId>
<version>1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion> <artifactId>travel-website-api</artifactId>
<description>用于处理前端请求的接口</description>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties> <dependencies>
<dependency>
<groupId>cn.linstudy.travel</groupId>
<artifactId>travel-core</artifactId>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies> </project>

3.4、创建配置类

在以前传统的开发,我们需要在启动类头上贴MapperScan注解,表示需要扫描mapper接口并且创建代理对象的位置。

    由于我们这次是分模块开发,无法在启动类上扫描Mapper接口,所以我们创建一个配置类来进行配置。

/**
* @Description 配置类
* @Author XiaoLin
* @Date 2021/4/9 14:51
*/
@Configuration
@MapperScan(basePackages = "cn.linstudy.travel.mapper")
public class CoreConfig { }

四、测试

搭建好之后就是需要测试了,我们需要在travel-core中按照惯例写一些抽取的代码。

4.1、travel-core写代码

/**
* @Description: 用于抽取所有的实体类id
* @author XiaoLin
* @date 2021/4/9
*/
public abstract class BaseDomain implements Serializable {
// MyBatis-Plus表示这个是id自增
@ApiModelProperty(value = "主键id")
@TableId(type = IdType.AUTO)
protected Long id;
}
/**
* @Description 用户信息实体类
* @Author XiaoLin
* @Date 2021/4/9 14:18
*/
@Setter
@Getter
@TableName("userinfo")
@ApiModel(value = "cn.linstudy.travel.domain",description = "用户信息实体类")
public class UserInfo extends BaseDomain{ public static final int GENDER_SECRET = 0; //保密
public static final int GENDER_MALE = 1; //男
public static final int GENDER_FEMALE = 2; //女
public static final int STATE_NORMAL = 0; //正常
public static final int STATE_DISABLE = 1; //冻结 @ApiModelProperty(value = "昵称")
private String nickname; @ApiModelProperty(value = "手机")
private String phone; @ApiModelProperty(value = "手机")
private String email; //邮箱 @JsonIgnore
@ApiModelProperty(value = "密码")
private String password; @ApiModelProperty(value = "性别")
private Integer gender = GENDER_SECRET; @ApiModelProperty(value = "用户级别")
private Integer level = 0; @ApiModelProperty(value = "所在城市")
private String city; @ApiModelProperty(value = "头像")
private String headImgUrl; @ApiModelProperty(value = "个性签名")
private String info; @ApiModelProperty(value = "状态")
private Integer state = STATE_NORMAL; }
/**
* @Description 用户Mapper
* @Author XiaoLin
* @Date 2021/4/9 14:20
*/
public interface UserInfoMapper extends BaseMapper<UserInfo> { // 继承MyBatis-Plus的通用Mapper,泛型是实体类
}
/**
* @Description 用户业务层接口
* @Author XiaoLin
* @Date 2021/4/9 14:21
*/
public interface UserInfoService extends IService<UserInfo> { // 继承MyBatis-Plus的通用Service接口,泛型是实体类 }
/**
* @Description 用户业务层接口实现类
* @Author XiaoLin
* @Date 2021/4/9 14:23
*/
@Service
@Transactional
// 实现MyBatis-Plus的通用Service实现类,泛型参数一是mapper接口,第二个是用户实体类
public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper,UserInfo> implements UserInfoService { }

4.2、travel-website-api写接口

到了测试环节,我们需要在travel-website-api中写接口来进行测试。

/**
* @Description
* @Author XiaoLin
* @Date 2021/4/9 14:46
*/
@RestController
public class UserInfoController { @Autowired
UserInfoService userInfoService; @GetMapping("detail")
// @ApiImplicitParam(name = "id", value = "用户id")
public Object getUser( String id){
return userInfoService.getById(Long.valueOf(id));
}
}

4.3、浏览器访问

启动后我们需要在浏览器中输入:http://localhost:8080/detail?id=1进行测试。

4.4、整合Swagger2

每次测试我们都要在浏览器中进行输入稍显麻烦,我们可以整合Swagger2来进行接口测试。所以我们可以尝试整合Swagger2。

4.4.1、编写配置文件

我们需要在travel-core中创建一个Swagger的配置类。

/**
* @Description Swagger配置类
* @Author XiaoLin
* @Date 2021/4/9 17:22
*/
@Configuration//表示这是一个配置类 public class SwaggerConfig { @Bean
public Docket reeateDocket(){
List<Parameter> parameterList=new ArrayList<>();
ParameterBuilder parameterBuilder=new ParameterBuilder();
parameterBuilder.name("token")
.description("swagger调试用,模拟传入用户凭证")
.modelRef(new ModelRef("String"))
.parameterType("header").required(false);
parameterList.add(parameterBuilder.build());
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())//创建该Api的基本信息(这些基本信息会展现在文档页面中)
.select()//函数返回一个ApiSelectorBuilder实例用来控制哪些接口暴露给Swagger ui来展现
.apis(RequestHandlerSelectors.basePackage("cn.linstudy.travel.controller"))//指定需要扫描的包路路径
.paths(PathSelectors.any())
.build()
.globalOperationParameters(parameterList)
;
}
//配置swagger的信息
private ApiInfo apiInfo(){
return new ApiInfoBuilder()
.title("SpringBoot-Travel项目实战")
.description("接口")
.termsOfServiceUrl("")
.version("1.0")
.build();
}
}

启动服务器,并且输入网址:http://localhost:8080/swagger-ui.html

4.4.2、控制器中添加注解,用以扫描

/**
* @Description
* @Author XiaoLin
* @Date 2021/4/9 14:46
*/
@RestController
@Api(tags = "用户相关接口")
public class UserInfoController { @Autowired
UserInfoService userInfoService; @ApiOperation(value = "根据id查询用户")
@GetMapping("detail")
@ApiImplicitParam(name = "id", value = "用户id")
public Object getUser( String id){
return userInfoService.getById(Long.valueOf(id));
}
}

五、注册

5.1、校验手机号码合法性

注册首先需要做的是校验手机的合法性,确保用户输入合法的手机号用于下一步发短信验证码。

$(function () {
$('#_js_loginBtn').click(function () {
var val = $('#inputPassword').val(); //js 正则表达语法:
// / /g : 正则表达式对象 // ^1 以1开头
// \d 数字 0-9 数字中一个
// {10} 重复个数 \d{10} 表示10个数字
// $ 以xx结束
// [3456789] 代码 3 4 5 6 7 8 9 中一个数
// 正则表达式校验
if (/^(13[0-9]|14[5|7]|15[0|1|2|3|4|5|6|7|8|9]|18[0|1|2|3|5|6|7|8|9])\d{8}$/g.test(val)) { // 如果匹配的话就发请求到后台校验手机号是否注册过
$.get(domainUrl + "/users/checkPhone", {phone:val}, function (data) {
if(!data){
$('#inputPassword').next().text('').hide()
$('.login-box').hide()
$('.signup-box').show()
$("#phone").val(val);
}else{ $('#inputPassword').next().text('手机号码已注册.').show()
}
})
} else {
// 匹配不通过表示手机号格式不正确
$('#inputPassword').next().text('手机号码格式不正确').show()
}
});

5.2、编写校验手机是否注册接口

5.2.1、技术难点分析

5.2.2、配置跨域

由于我们的项目是前后端分离的,会涉及到跨域的问题,所以我们首先需要解决的问题是跨域。我们在travel-core中的config包新建一个配置类,专门用于处理跨域请求。

/**
* @Description 解决跨域配置类
* @Author XiaoLin
* @Date 2021/4/9 20:31
*/
@Configuration
public class WebConfigurer implements WebMvcConfigurer { @Bean
public WebConfigurer corsConfigurer() {
return new WebConfigurer() {
@Override
//重写父类提供的跨域请求处理的接口
public void addCorsMappings(CorsRegistry registry) {
//添加映射路径
registry.addMapping("/**")
//放行哪些原始域
.allowedOriginPatterns("*")
//是否发送Cookie信息
.allowCredentials(true)
//放行哪些原始域(请求方式)
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
//放行哪些原始域(头部信息)
.allowedHeaders("*")
//暴露哪些头部信息(因为跨域访问默认不能获取全部头部信息)
.exposedHeaders("Header1", "Header2");
} }; }
}

5.3、编写发短信工具类(调用阿里云接口)(踩了巨坑)

我们需要调用阿里云的接口来进行发短信,我在网上找了一个工具类(放在travel-core中),想着不能把阿里云短信的配置信息直接打在代码里面,造成硬编码的问题,所以我想着把他抽取出来,放在配置文件中。

aliyun:
accessKeyId: "你的阿里云accessKeyId"
secret: "你的阿里云密钥"

但是我在赋值的是时候傻眼了,因为这里的值都是静态的变量,用@Value注解没办法进行赋值。下面贴出初始的工具类。

public class SendMessageUtils {

    // 产品名称:云通信短信API产品,开发者无需替换
static final String product = "Dysmsapi";
// 产品域名,开发者无需替换
static final String domain = "dysmsapi.aliyuncs.com"; // TODO 此处需要替换成开发者自己的AK(在阿里云访问控制台寻找)
static final String accessKeyId = "youaccessKeyId"; // TODO 改这里
static final String accessKeySecret = "youaccessKeySecret"; // TODO 改这里 public static SendSmsResponse sendSms(String telephone, String code) throws ClientException { // 可自助调整超时时间
System.setProperty("sun.net.client.defaultConnectTimeout", "10000");
System.setProperty("sun.net.client.defaultReadTimeout", "10000"); // 初始化acsClient,暂不支持region化
IClientProfile profile = DefaultProfile.getProfile("cn-hangzhou", accessKeyId, accessKeySecret);
DefaultProfile.addEndpoint("cn-hangzhou", "cn-hangzhou", product, domain);
IAcsClient acsClient = new DefaultAcsClient(profile); // 组装请求对象-具体描述见控制台-文档部分内容
SendSmsRequest request = new SendSmsRequest();
// 必填:待发送手机号
request.setPhoneNumbers(telephone);
// 必填:短信签名-可在短信控制台中找到
request.setSignName("你的短信签名"); // TODO 改这里
// 必填:短信模板-可在短信控制台中找到
request.setTemplateCode("你的短信模板"); // TODO 改这里
// 可选:模板中的变量替换JSON串,如模板内容为"亲爱的用户,您的验证码为${code}"时,此处的值为
request.setTemplateParam("{\"code\":\"" + code + "\"}"); // 选填-上行短信扩展码(无特殊需求用户请忽略此字段)
// request.setSmsUpExtendCode("90997"); // 可选:outId为提供给业务方扩展字段,最终在短信回执消息中将此值带回给调用者
request.setOutId("yourOutId"); // hint 此处可能会抛出异常,注意catch
SendSmsResponse sendSmsResponse = acsClient.getAcsResponse(request);
if(sendSmsResponse.getCode()!= null && sendSmsResponse.getCode().equals("OK")){
System.out.println("短信发送成功!");
}else {
System.out.println("短信发送失败!");
}
return sendSmsResponse;
} }

我试了好几次都无法1获取到值,为了应对@Value注解1赋值给静态变量的问题,需要加上seter方法进行赋值,而且记住要删除默认生成的setter方法的static修饰符,否则还是无法获取。将从yml中获取的值赋值给set方法的参数,随后赋值给成员变量,但是要记住一定要删除默认生成的setter方法的static修饰符

@PropertySource(value = "classpath:application-core.yml")
@Component
public class SendMessageUtils { private static String accessKeyId; // TODO 修改成自己的
private static String accessKeySecret; // TODO 修改成自己的 @Value("${aliyun.accessKeyId}")
public void setAccessKeyId(String accessKeyId) {
SendMessageUtils.accessKeyId = accessKeyId;
} @Value("${aliyun.secret}")
public void setAccessKeySecret(String secret) {
SendMessageUtils.accessKeySecret = secret;
} //产品名称:云通信短信API产品,开发者无需替换
static final String product = "Dysmsapi";
//产品域名,开发者无需替换
static final String domain = "dysmsapi.aliyuncs.com"; public static SendSmsResponse sendSms(String telephone, String code) throws ClientException {
//可自助调整超时时间
System.setProperty("sun.net.client.defaultConnectTimeout", "10000");
System.setProperty("sun.net.client.defaultReadTimeout", "10000");
System.out.println(1/0);
//初始化acsClient,暂不支持region化
IClientProfile profile = DefaultProfile.getProfile("cn-hangzhou", accessKeyId, accessKeySecret);
DefaultProfile.addEndpoint("cn-hangzhou", "cn-hangzhou", product, domain);
IAcsClient acsClient = new DefaultAcsClient(profile);
//组装请求对象-具体描述见控制台-文档部分内容
SendSmsRequest request = new SendSmsRequest(); //必填:待发送手机号
request.setPhoneNumbers(telephone);
//必填:短信签名-可在短信控制台中找到
request.setSignName("XiaoLin"); // TODO 修改成自己的
//必填:短信模板-可在短信控制台中找到
request.setTemplateCode("SMS_213078152"); // TODO 修改成自己的
//可选:模板中的变量替换JSON串,如模板内容为"亲爱的${name},您的验证码为${code}"时,此处的值为
// request.setTemplateParam("{\"name\":\"Tom\", \"code\":\"123\"}");
request.setTemplateParam("{\"code\":\"" + code + "\"}");
//选填-上行短信扩展码(无特殊需求用户请忽略此字段)
//request.setSmsUpExtendCode("90997");
//可选:outId为提供给业务方扩展字段,最终在短信回执消息中将此值带回给调用者
// request.setOutId("yourOutId");
//hint 此处可能会抛出异常,注意catch
SendSmsResponse sendSmsResponse = acsClient.getAcsResponse(request);
if (sendSmsResponse.getCode() != null && sendSmsResponse.getCode().equals("OK")) {
System.out.println("短信发送成功!");
} else {
System.out.println("短信发送失败!");
}
return sendSmsResponse;
} }

5.4、调用工具类发送短信

/**
* @Description: 发送验证码实现类
* @author XiaoLin
* @date 2021/4/10
* @Param: [phone]
* @return cn.linstudy.travel.qo.response.JsonResult
*/
@Override
public JsonResult sendVerifyCode(String phone) {
try {
String code = VerifyCodeUtils.generateVerifyCode(4);
System.out.println("发送了短信");
SendMessageUtils.sendSms(phone,code);
userInfoRedisService.setVerifyCode(phone,code);
return JsonResult.success();
} catch (Exception e) {
return JsonResult.error(SystemConstant.CODE_SEND_PHONE_MESSAGE,e.getMessage()); }
}

5.5、将验证码放入Redis

5.5.1、使用枚举重写Redis的key

枚举类的特点:

  1. 枚举类构造器是私有的
  2. 枚举类定义完成之后,枚举类的个数是固定的。

因为防止有些人不按照我们的规定进行拼key,所以我们利用枚举来进行重写key

package cn.linstudy.travel.redis;

/**
* @Description
* @Author XiaoLin
* @Date 2021/4/10 20:03
*/
@Getter
public enum RedisKeyEnum { // 用户注册验证码 key 实例对象
ENUM_VERYFY_CODE("veryfy_code",SystemConstant.VERIFY_CODE_VAI_TIME*60L); // 前缀
private String prefix;
// 有效时长
private Long time; RedisKeyEnum(String prefix, long time) {
this.prefix = prefix;
this.time = time;
} // 拼接key
public String join(String... values){
StringBuilder sb = new StringBuilder();
sb.append(this.prefix);
for (String value : values) {
sb.append(":").append(value);
}
return sb.toString();
}
}
package cn.linstudy.travel.redis.service;

/**
* @Description 用户缓存的业务类
* @Author XiaoLin
* @Date 2021/4/10 9:13
*/
public interface UserInfoRedisService { /**
* 将验证码设置到redis中
* @param phone
* @param code
*/
void setVerifyCode(String phone, String code); }
package cn.linstudy.travel.redis.service.impl;

/**
* @Description
* @Author XiaoLin
* @Date 2021/4/10 9:18
*/
@Service
public class UserInfoRedisServiceImpl implements UserInfoRedisService { @Autowired
private StringRedisTemplate template;
@Override
public void setVerifyCode(String phone, String code) {
String key = RedisKeyEnum.ENUM_VERYFY_CODE.join(phone);
String value = code;
// 将验证码放入Redis,并且设置时效
template.opsForValue().set(key,value, RedisKeyEnum.ENUM_VERYFY_CODE.getTime(), TimeUnit.SECONDS);
} }

5.6、参数校验

虽然在前台进行了参数的校验,但是在后台也是需要进行参数的非空校验的,不排除有些人通过接口测试的方式进入方法。

5.6.1、自定义异常

package cn.linstudy.travel.exception;

/**
* @Description 自定义异常
* @Author XiaoLin
* @Date 2021/4/10 10:34
*/
public class LogicException extends RuntimeException{ // 标记非系统异常
public LogicException(String message) {
super(message);
}
}

5.6.2、统一异常处理

所有的异常都会被捕获,然后来到这里。

package cn.linstudy.travel.advice;
/**
通用异常处理类
* ControllerAdvice controller类功能增强注解, 动态代理controller类实现一些额外功能
* 请求进入controller映射方法之前做功能增强: 经典用法:日期格式化
* 请求进入controller映射方法之后做功能增强: 经典用法:统一异常处理
* @Author XiaoLin
* @Date 2021/4/10 19:17
*/
public class CommonExceptionHandler { //这个方法定义的跟映射方法操作一样
@ExceptionHandler(LogicException.class)
@ResponseBody
public Object LogicException(Exception e, HttpServletResponse resp) {
e.printStackTrace();
resp.setContentType("application/json;charset=utf-8");
return JsonResult.error(SystemConstant.CODE_ERROR_PARAM, e.getMessage(), null);
} @ExceptionHandler(RuntimeException.class)
@ResponseBody
public Object RuntimeException(Exception e, HttpServletResponse resp) {
e.printStackTrace();
resp.setContentType("application/json;charset=utf-8");
return JsonResult.defaultError();
}
}

5.6.1、断言

SpringBoot有一种断言方式,我们需要重写断言来进行判断来简化if-else操作,但是原生的断言不适合我们,我们需要重写。

public class AssertsUtils {

  private AssertsUtils() {
} /**
* @return void
* @Description: 判断指定的参数是否为null,或者空串,如果为空抛出异常
* @author XiaoLin
* @date 2021/4/10
* @Param: [text, message]
*/
public static void hasText(String text, String message) {
if (!StringUtils.hasText(text)) {
throw new LogicException(message);
}
} /**
* @Description: 判断传入的参数是否相等
* @author XiaoLin
* @date 2021/4/10
* @Param: [param1, param2, message]
* @return void
*/
public static void isEquals(String param1, String param2, String message) {
if (param1 == null || param2 == null) {
throw new LogicException("传入的参数为空");
}
if (!param1.equals(param2)) {
throw new LogicException(message);
}
} }

5.7、封装VO

我们将前台传进来的数据封装成一个注册的VO

package cn.linstudy.travel.vo;

/**
* @Description 用户注册提交表单的VO
* @Author XiaoLin
* @Date 2021/4/10 9:48
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("userinfo")
public class UserInfoRegisterVO extends BaseDomain { @ApiModelProperty(value = "昵称")
private String nickname; @ApiModelProperty(value = "密码")
private String password; // 这个字段不会映射到数据库中
@TableField(exist = false)
@ApiModelProperty(value = "确认密码")
private String repeatPassword; @ApiModelProperty(value = "手机")
private String phone;
}

5.8、重写mapper的insert方法

由于MyBatis-Plus的Mapper的insert方法不适用于我们封装的VO对象进行增加,所以我们需要重写Mapper的insert方法。

package cn.linstudy.travel.mapper;

/**
* @Description 用户Mapper
* @Author XiaoLin
* @Date 2021/4/9 14:20
*/
public interface UserInfoMapper extends BaseMapper<UserInfo> {// 继承MyBatis-Plus的通用Mapper,泛型是实体类 @Insert("insert into userinfo( nickname, phone, password) values (#{nickname},#{phone},#{password})")
void insert(UserInfoRegisterVO userInfoRegisterVO);
}

5.9、完整ServiceImpl代码

package cn.linstudy.travel.service.impl;

import cn.linstudy.travel.constant.SystemConstant;
import cn.linstudy.travel.domain.UserInfo;
import cn.linstudy.travel.exception.LogicException;
import cn.linstudy.travel.mapper.UserInfoMapper;
import cn.linstudy.travel.qo.response.JsonResult;
import cn.linstudy.travel.redis.service.UserInfoRedisService;
import cn.linstudy.travel.service.UserInfoService;
import cn.linstudy.travel.utils.AssertsUtils;
import cn.linstudy.travel.utils.SendMessageUtils;
import cn.linstudy.travel.utils.VerifyCodeUtils;
import cn.linstudy.travel.vo.UserInfoRegisterVO;
import com.aliyuncs.exceptions.ClientException;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import javax.security.auth.login.LoginException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; /**
* @Description 用户业务层接口实现类
* @Author XiaoLin
* @Date 2021/4/9 14:23
*/
@Service
@Transactional
// 实现MyBatis-Plus的通用Service实现类,泛型参数一是mapper接口,第二个是用户实体类
public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper,UserInfo> implements UserInfoService { @Autowired
UserInfoMapper userInfoMapper; /**
* @Description: 发送验证码实现类
* @author XiaoLin
* @date 2021/4/10
* @Param: [phone]
* @return cn.linstudy.travel.qo.response.JsonResult
*/
@Override
public JsonResult sendVerifyCode(String phone) {
try {
String code = VerifyCodeUtils.generateVerifyCode(4);
SendMessageUtils.sendSms(phone,code);
userInfoRedisService.setVerifyCode(phone,code);
return JsonResult.success();
} catch (Exception e) {
return JsonResult.error(SystemConstant.CODE_SEND_PHONE_MESSAGE,e.getMessage());
}
} /**
* @Description: 用户注册实现类
* @author XiaoLin
* @date 2021/4/10
* @Param: [userInfoRegisterVO]
* @return cn.linstudy.travel.qo.response.JsonResult
*/
@Override
public JsonResult register(UserInfoRegisterVO userInfoRegisterVO) {
AssertsUtils.hasText(userInfoRegisterVO.getNickname(),"昵称不能为空");
AssertsUtils.hasText(userInfoRegisterVO.getPassword(),"密码不能为空");
AssertsUtils.hasText(userInfoRegisterVO.getRepeatPassword(),"再次密码不能为空");
AssertsUtils.hasText(userInfoRegisterVO.getPhone(),"手机不能为空");
AssertsUtils.isEquals(userInfoRegisterVO.getPassword(),userInfoRegisterVO.getRepeatPassword(),"两次的密码不一样");
try {
// 注册
userInfoMapper.insert(userInfoRegisterVO);
}catch (Exception e){
e.printStackTrace();
}
return new JsonResult(SystemConstant.CODE_SUCCESS,SystemConstant.MSG_SUCCESS);
} }

5.4、测试

直接使用swagger进行测试即可。

SpringBoot的旅游项目——day01(学习记录附赠源码)的更多相关文章

  1. JUC.Lock(锁机制)学习笔记[附详细源码解析]

    锁机制学习笔记 目录: CAS的意义 锁的一些基本原理 ReentrantLock的相关代码结构 两个重要的状态 I.AQS的state(int类型,32位) II.Node的waitStatus 获 ...

  2. JUC.Condition学习笔记[附详细源码解析]

    目录 Condition的概念 大体实现流程 I.初始化状态 II.await()操作 III.signal()操作 3个主要方法 Condition的数据结构 线程何时阻塞和释放 await()方法 ...

  3. SpringBoot学习入门之Hello项目的构建、单元测试和热部署等(配图文,配置信息详解,附案例源码)

    前言: 本文章主要是个人在学习SpringBoot框架时做的一些准备,参考老师讲解进行完善对SpringBoot构建简单项目的学习汇集成本篇文章,作为自己对SpringBoot框架的总结与笔记. 你将 ...

  4. winserver的consul部署实践与.net core客户端使用(附demo源码)

    winserver的consul部署实践与.net core客户端使用(附demo源码)   前言 随着微服务兴起,服务的管理显得极其重要.都知道微服务就是”拆“,把臃肿的单块应用,拆分成多个轻量级的 ...

  5. Spring Boot整合ElasticSearch和Mysql 附案例源码

    导读 前二天,写了一篇ElasticSearch7.8.1从入门到精通的(点我直达),但是还没有整合到SpringBoot中,下面演示将ElasticSearch和mysql整合到Spring Boo ...

  6. OAuth2学习及DotNetOpenAuth部分源码研究

    OAuth2学习及DotNetOpenAuth部分源码研究 在上篇文章中我研究了OpenId及DotNetOpenAuth的相关应用,这一篇继续研究OAuth2. 一.什么是OAuth2 OAuth是 ...

  7. memcached学习笔记——存储命令源码分析上篇

    原创文章,转载请标明,谢谢. 上一篇分析过memcached的连接模型,了解memcached是如何高效处理客户端连接,这一篇分析memcached源码中的process_update_command ...

  8. Sping学习笔记(一)----Spring源码阅读环境的搭建

    idea搭建spring源码阅读环境 安装gradle Github下载Spring源码 新建学习spring源码的项目 idea搭建spring源码阅读环境 安装gradle 在官网中下载gradl ...

  9. mybatis源码学习(一) 原生mybatis源码学习

    最近这一周,主要在学习mybatis相关的源码,所以记录一下吧,算是一点学习心得 个人觉得,mybatis的源码,大致可以分为两部分,一是原生的mybatis,二是和spring整合之后的mybati ...

随机推荐

  1. Java基础语法:abstract修饰符

    一.简介 描述: 'abstract'修饰符可以用来修饰方法,也可以修饰类. 如果修饰方法,那么该方法就是抽象方法:如果修饰类,那么该类就是抽象类. 抽象类和抽象方法起到一个框架作用,方便后期扩展的重 ...

  2. Go的数组

    目录 数组 一.数组的定义 1.声明数组 2.初始化设值 3.指定位置设值 4.不指定长度初始化(了解) 二.数组的使用 三.数组的类型 四.数组的长度 五.迭代数组 1.初始化迭代 2.使用rang ...

  3. ISC BIND9 - 最详细、最认真的从零开始的BIND 9 服务讲解

    DNS and BIND 服务的搭建说明 目录 目录 DNS and BIND 服务的搭建说明 1. 背景 1.1 DNS 1.2 FQDN 1.3 BIND 1.4 本文中搭建模拟DNS服务网络虚拟 ...

  4. PVE更新WEB管理地址

    PVE也是一台Linux系统,如果PVE更换了网络环境,比如从家里拿到了办公室,那么就需要对其更新网络,才能让其它机器访问到它的8006管理地址. 具体做法是通过修改配置文件来更改IP. 更新网卡配置 ...

  5. SQL驱动限制,导致插入失败

    insert into TB_IF_ORDERS (DC_CD,JOB_DT,SEQ_NO,ORDER_KEY,ORDER_ID,ORDER_LINE_NUM,COMPANY_CD,CUST_CD,S ...

  6. NewSQL分布式数据库,例如TIDB用K/V的底层逻辑

    内容参考 对分布式对定义参考这篇文章: 微服务都想用,先把分布式和微服务之间的关系说清楚 对分布式架构中心或无中心对比参考这篇文章: 分布式存储单主.多主和无中心架构的特征与趋势 对HDFS对内部机制 ...

  7. ICPC题目选讲

    Traveling in the grid world 题目描述 有一个 \(n\times m\) 的格点图,两点之间走他们的连线,但是这条连线不能恰好覆盖其他整点.还要求相邻两步之间的连线不能斜率 ...

  8. [POJ2828] Buy Tickets(待续)

    [POJ2828] Buy Tickets(待续) 题目大意:多组测试,每组给出\(n\)条信息\((a,b)\),表示\(b\)前面有\(a\)个人,顺序靠后的信息优先级高 Solution.1 由 ...

  9. Linux普通用户安装配置mysql(非root权限)

    Linux普通用户安装配置mysql(非root权限) 说明:在实际工作中,公司内网的机器我们一般没有root权限,也没有连网,最近参考网上的资料使用一般的账户成功安装mysql,记录如下 Linux ...

  10. 2019 GDUT Rating Contest III : Problem E. Family Tree

    题面: E. Family Tree Input file: standard input Output file: standard output Time limit: 1 second Memory ...