前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SpringBoot的旅游项目——day01(学习记录附赠源码)

SpringBoot的旅游项目——day01(学习记录附赠源码)

作者头像
上分如喝水
发布2021-08-16 17:05:49
8670
发布2021-08-16 17:05:49
举报
文章被收录于专栏:XiaoLin笔记

文章目录

前言

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

第一天

一、技术选型

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

    后端选用的技术为:

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

二、搭建项目

image-20210409192943185
image-20210409192943185

创建文件夹

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

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

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

创建travel-core

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

创建travel-website

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

创建travel-website-api

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

创建travel-mgrsite

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

三、引入依赖

3.1、父目录

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

代码语言:javascript
复制
  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>cn.linstudy.travelgroupId>
        <artifactId>travel-coreartifactId>
        <version>1.0version>
      dependency>
    dependencies>
  dependencyManagement>

    完整的pom文件:

代码语言:javascript
复制
<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.0modelVersion>
  <parent>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-parentartifactId>
    <version>2.4.3version>
    <relativePath/>
  parent>

  <groupId>cn.linstudy.travelgroupId>
  <artifactId>travel-parentartifactId>
  <packaging>pompackaging>
  <version>1.0version>
  <description>父类,用于导入各种需要的依赖,不写代码description>
  <modules>
    <module>travel-coremodule>
    <module>travel-mgrsitemodule>
    <module>travel-website-apimodule>
  modules>

  <properties>
    <maven.compiler.source>11maven.compiler.source>
    <maven.compiler.target>11maven.compiler.target>
  properties>

  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>cn.linstudy.travelgroupId>
        <artifactId>travel-coreartifactId>
        <version>1.0version>
      dependency>
    dependencies>
  dependencyManagement>

project>

3.2、travel-core

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

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

代码语言:javascript
复制
<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-parentartifactId>
    <groupId>cn.linstudy.travelgroupId>
    <version>1.0version>
    <relativePath>../pom.xmlrelativePath>
  parent>
  <modelVersion>4.0.0modelVersion>

  <artifactId>travel-coreartifactId>
  <description>用于抽取重复的代码:mapper、service、domaindescription>

  <properties>
    <maven.compiler.source>11maven.compiler.source>
    <maven.compiler.target>11maven.compiler.target>
  properties>

  <dependencies>
    <dependency>
      <groupId>org.projectlombokgroupId>
      <artifactId>lombokartifactId>
      <optional>trueoptional>
    dependency>

    <dependency>
      <groupId>com.baomidougroupId>
      <artifactId>mybatis-plus-boot-starterartifactId>
      <version>3.4.0version>
    dependency>

    <dependency>
      <groupId>com.alibabagroupId>
      <artifactId>druid-spring-boot-starterartifactId>
      <version>1.1.24version>
    dependency>

    <dependency>
      <groupId>mysqlgroupId>
      <artifactId>mysql-connector-javaartifactId>
    dependency>

    <dependency>
      <groupId>com.alibabagroupId>
      <artifactId>fastjsonartifactId>
      <version>1.2.74version>
    dependency>


    <dependency>
      <groupId>org.springframework.bootgroupId>
      <artifactId>spring-boot-starter-webartifactId>
      <scope>providedscope>
    dependency>
    <dependency>
      <groupId>io.springfoxgroupId>
      <artifactId>springfox-swagger2artifactId>
      <version>2.9.2version>
    dependency>
    <dependency>
      <groupId>io.springfoxgroupId>
      <artifactId>springfox-swagger-uiartifactId>
      <version>2.9.2version>
    dependency>

    <dependency>
      <groupId>org.springframework.bootgroupId>
      <artifactId>spring-boot-starter-data-redisartifactId>
    dependency>

    <dependency>
      <groupId>com.aliyun.ossgroupId>
      <artifactId>aliyun-sdk-ossartifactId>
      <version>3.5.0version>
    dependency>
    <dependency>
      <groupId>commons-iogroupId>
      <artifactId>commons-ioartifactId>
      <version>2.6version>
    dependency>

    <dependency>
      <groupId>commons-beanutilsgroupId>
      <artifactId>commons-beanutilsartifactId>
      <version>1.9.3version>
    dependency>
    <dependency>
      <groupId>org.springframework.bootgroupId>
      <artifactId>spring-boot-starter-data-elasticsearchartifactId>
    dependency>

  dependencies>

project>

3.3、travel-website-api

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

代码语言:javascript
复制
<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-parentartifactId>
    <groupId>cn.linstudy.travelgroupId>
    <version>1.0version>
  parent>
  <modelVersion>4.0.0modelVersion>

  <artifactId>travel-website-apiartifactId>
  <description>用于处理前端请求的接口description>
  <properties>
    <maven.compiler.source>11maven.compiler.source>
    <maven.compiler.target>11maven.compiler.target>
  properties>

  <dependencies>
    <dependency>
      <groupId>cn.linstudy.travelgroupId>
      <artifactId>travel-coreartifactId>
    dependency>

    <dependency>
      <groupId>org.springframework.bootgroupId>
      <artifactId>spring-boot-starter-webartifactId>
    dependency>
  dependencies>

project>

3.4、创建配置类

    在以前传统的开发,我们需要在启动类头上贴MapperScan注解,表示需要扫描mapper接口并且创建代理对象的位置。     由于我们这次是分模块开发,无法在启动类上扫描Mapper接口,所以我们创建一个配置类来进行配置。

代码语言:javascript
复制
/**
 * @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写代码

代码语言:javascript
复制
/**
    * @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;
}
代码语言:javascript
复制
/**
 * @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;

}
代码语言:javascript
复制
/**
 * @Description 用户Mapper
 * @Author XiaoLin
 * @Date 2021/4/9 14:20
 */
public interface UserInfoMapper extends BaseMapper<UserInfo> { // 继承MyBatis-Plus的通用Mapper,泛型是实体类
}
代码语言:javascript
复制
/**
 * @Description 用户业务层接口
 * @Author XiaoLin
 * @Date 2021/4/9 14:21
 */
public interface UserInfoService extends IService<UserInfo> { // 继承MyBatis-Plus的通用Service接口,泛型是实体类

}
代码语言:javascript
复制
/**
 * @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中写接口来进行测试。

代码语言:javascript
复制
/**
 * @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进行测试。

image-20210409192727450
image-20210409192727450

4.4、整合Swagger2

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

4.4.1、编写配置文件

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

代码语言:javascript
复制
/**
 * @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

image-20210409192035942
image-20210409192035942
4.4.2、控制器中添加注解,用以扫描
代码语言:javascript
复制
/**
 * @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));
  }
}
image-20210409192744121
image-20210409192744121
image-20210409192832367
image-20210409192832367

五、注册

用户注册
用户注册
手机注册分析
手机注册分析

5.1、校验手机号码合法性

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

代码语言:javascript
复制
$(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包新建一个配置类,专门用于处理跨域请求。

代码语言:javascript
复制
/**
 * @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中),想着不能把阿里云短信的配置信息直接打在代码里面,造成硬编码的问题,所以我想着把他抽取出来,放在配置文件中。

代码语言:javascript
复制
aliyun:
  accessKeyId: "你的阿里云accessKeyId"
  secret: "你的阿里云密钥"

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

代码语言:javascript
复制
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修饰符

代码语言:javascript
复制
@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、调用工具类发送短信

短信流程
短信流程
代码语言:javascript
复制
/**
      * @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

代码语言:javascript
复制
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();
 }
}
代码语言:javascript
复制
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);

}
代码语言:javascript
复制
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、自定义异常
代码语言:javascript
复制
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、统一异常处理

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

代码语言:javascript
复制
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操作,但是原生的断言不适合我们,我们需要重写。

代码语言:javascript
复制
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

代码语言:javascript
复制
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方法。

代码语言:javascript
复制
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代码

代码语言:javascript
复制
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进行测试即可。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021/04/11 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文章目录
  • 前言
  • 第一天
    • 一、技术选型
      • 二、搭建项目
        • 三、引入依赖
          • 3.1、父目录
          • 3.2、travel-core
          • 3.3、travel-website-api
          • 3.4、创建配置类
        • 四、测试
          • 4.1、travel-core写代码
          • 4.2、travel-website-api写接口
          • 4.3、浏览器访问
          • 4.4、整合Swagger2
        • 五、注册
          • 5.1、校验手机号码合法性
          • 5.2、编写校验手机是否注册接口
          • 5.3、编写发短信工具类(调用阿里云接口)(踩了巨坑)
          • 5.4、调用工具类发送短信
          • 5.5、将验证码放入Redis
          • 5.6、参数校验
          • 5.7、封装VO
          • 5.8、重写mapper的insert方法
          • 5.9、完整ServiceImpl代码
          • 5.4、测试
      相关产品与服务
      云数据库 Redis
      腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档