手撕商城项目-注册

在这里插入图片描述
连接数据库store
在这里插入图片描述
在这里插入图片描述

创建完成后,先测试一下环境以及数据库连接

测试连接:

  • 启动springboot主类,是否有对应的spring图形

在这里插入图片描述

  • 在单元测试中测试数据库的连接是否正常加载
    在这里插入图片描述

现在ok
下一步:访问项目的静态资源是否可以正常的加载。所有的静态项目(pages目录下的所有项目)复制到static目录下
在这里插入图片描述

注意:idea对于JS的代码的兼容性较差,编写了js代码但是有时候不能正常去加载
解决:1、idea缓存清理
2、clear-instal
3、rebuild重新构建
4、重启idea和操作系统

在这里插入图片描述

用户注册功能

在这里插入图片描述

1、创建t_user表

CREATE TABLE t_user (
	uid INT AUTO_INCREMENT COMMENT '用户id',
	username VARCHAR(20) NOT NULL UNIQUE COMMENT '用户名',
	password CHAR(32) NOT NULL COMMENT '密码',
	salt CHAR(36) COMMENT '盐值',
	phone VARCHAR(20) COMMENT '电话号码',
	email VARCHAR(30) COMMENT '电子邮箱',
	gender INT COMMENT '性别:0-女,1-男',
	avatar VARCHAR(50) COMMENT '头像',
	is_delete INT COMMENT '是否删除:0-未删除,1-已删除',
	created_user VARCHAR(20) COMMENT '日志-创建人',
	created_time DATETIME COMMENT '日志-创建时间',
	modified_user VARCHAR(20) COMMENT '日志-最后修改执行人',
	modified_time DATETIME COMMENT '日志-最后修改时间',
	PRIMARY KEY (uid)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

2、创建用户实体类

  • 通过表的结构提取出表的公共字段,放在一个实体类的基类中,起名BaseEntity基类中
    在这里使用lombok
package com.cy.store.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;


import java.io.Serializable;
import java.util.Date;

//实体类的基类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class BaseEntity implements Serializable {
    private String createdUser;
    private Date createdTime;
    private String modifiedUser;
    private Date modifiedTime;
    
}

  • 创建用户实体类。需要继承基类。
package com.cy.store.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;
//用户实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User extends BaseEntity implements Serializable {
    //使用包装类
    private Integer uid;
    private String username;
    private String password;
    private String salt;
    private String phone;
    private String email;
    private Integer gender;//0或1
    private String avatar;
    private Integer isDelete;
    
    /*任何实体类都要实现三个方法
    * 1、get和set
    * 2、equals和hashcode()
    * 3、toString()
    * */
}

注册-持久层

通过Mybatis来操作数据库。在做mybatis开发流程

1、规划需要执行的sql语句

1.1 用户的注册功能,相当于在做数据的插入操作
INSERT INTO t_user (username,password) VALUES (值列表)
1.2 在用户注册时首先要去查询当前的用户名是否存在,如果存在则不能注册,相当于一条查询语句
SELECT *FROM t_user WHERE username=?

2、设计接口和抽象方法

2.1 定义mapper接口。在项目的目录结构下首先创建一个mapper包,在这个包下再根据不同的功能模块来创建mapper接口。创建一个Usermapper的接口。要在接口中定义这两个sql语句抽象方法
package com.cy.store.mapper;

import com.cy.store.entity.User;

//用户模块的持久层接口
public interface Usermapper {
    /**
     * 插入用户数据
     * @param user  用户的数据
     * @return      受影响的行数(增删改都受影响的行数作为返回值,可以根据返回值来判断是否执行成功)
     */
    Integer insert(User user);

    /**
     * 根据用户名来查询用户的数据
     * @param username   用户名
     * @return           如果找到对应的用户则返回这个用户的数据,如果没有找到则返回null
     */
    User findUsername(String username);
}

2.2 在启动类配置mapper接口文件的位置

在这里插入图片描述

3、编写映射

3.1 定义xml文件,与对应的接口进行关联。所有的映射文件属于资源文件,需要放在resources下,为了管理方便,在这个目录下创建一个mapper文件夹,在这个文件夹下存放Mapper的映射文件
3.2 创建接口对应的映射文件,遵循和接口名称一样即可,创建一个UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<!--namespace属性:用于指定当前的映射文件和哪个接口进行映射,需要指定接口的文件路径,需要标注包的完整路径接口欧-->
<mapper namespace="com.cy.store.mapper.Usermapper">
    
</mapper>
3.3 配置接口 中的方法对应上的sql语句上。需要借助标签来完成,insert、update、select、delete,对应的sql语句的增删改查操作
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<!--namespace属性:用于指定当前的映射文件和哪个接口进行映射,需要指定接口的文件路径,需要标注包的完整路径接口欧-->
<mapper namespace="com.cy.store.mapper.Usermapper">
    
<!--自定义映射规则-->
<!--id:表示这个映射负责分配一个唯一的id值,对应的就是resultMap="id属性的值"属性的取值
  type:取值是一个类,表示的是数据库中的查询结果与java中哪个实体类进行结果集映射-->
<resultMap id="UserEntityMap" type="com.cy.store.entity.User">
<!--将表的字段和类的属性不一致的字段进行匹配指定,名称一致的字段可以省略不写-->
<!--column:表中的资源名称
    property:表示类中的属性名称-->
<!--在定义映射规则时,主键不能省略-->
    <result column="uid" property="uid"></result>
    <result column="is_delete" property="isDelete"></result>
    <result column="created_user" property="createdUser"></result>
    <result column="created_time" property="createdTime"></result>
    <result column="modified_user" property="modifiedUser"></result>
    <result column="modified_time" property="modifiedTime"></result>
</resultMap>

<!--id的值表示映射的接口中方法的名称,直接在标签内部来编写sql语句-->
    
<!--useGeneratedKeys="true":表示开启某个字段值自增
    keyProperty="uid":表示将表中的哪个字段作为主键自增-->
    <insert id="insert" useGeneratedKeys="true" keyProperty="uid">
        insert into store.t_user(username, password,salt,phone,email,gender,avatar,
                                 is_delete,created_user,created_time,modified_user,
                                 modified_time)     
            values (#{username}, #{password},#{salt},#{phone},#{email},#{gender},#{avatar},
                #{isDelete},#{createdUser},#{createdTime},#{modifiedUser},#{modifiedTime})
    </insert>
    
    <select id="findUsername" resultMap="UserEntityMap">
               <!-- select在查询时候,查询的结果是一个对象,多个对象。
               resultType:表示查询的结果集类型,只需要指定对应映射类的类型,并且包含完整包接口
               resultMap:当表的字段和类的对象属性的字段名称不一致时,来自定义查询结果集的映射规则-->
        select *from store.t_user where username = #{username}

</select>
</mapper>
3.4 由于这是项目中第一次使用SQL映射,所以需要在application.properties中添加mybatis.mapperlocations属性的配置,以指定XML文件的位置。
mybatis.mapper-locations=classpath:mapper/*.xml

4、单元测试

每个独立的层编写完毕后需要编写单元测试方法,测试功能是否正常
在test包结构下创建一个mapper包,在这个包下再创建测试类

package com.cy.store.mapper;

import com.cy.store.entity.User;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.stereotype.Repository;
import org.springframework.test.context.junit4.SpringRunner;

@SpringBootTest//表示当前类是一个测试类,不会随项目一块打包
//@RunWith(SpringRunner.class)//表示启动这个单元测试类,需要传递一个参数,必须是SpringRunner的实例类型

public class UserMapperTests {
    //idea有检测功能,接口是不能够直接创建Bean的
    @Autowired
    private  Usermapper usermapper;
    @Test
    /*
    * 单元测试方法:
    * 1、必须被@Test注解修饰
      2、返回值类型必须void
      3、方法的参数列表不指定任何类型
      4、方法的访问修饰符必须是public
    * */
    public void insert(){
        User user = new User();
        user.setUsername("tim");
        user.setPassword("1234");
        Integer rows = usermapper.insert(user);
        System.out.println(rows);
    }
}

注册-业务层

1、规划异常

1.1 .为了便于统一管理自定义异常,应先创建com.cy.store.service.ex.ServiceException自定义异常的基类异常,继承自RuntimeException类,并从父类生成子类的五个构造方法。
package com.cy.store.service.ex;
//业务层异常基类
public class ServiceException extends RuntimeException{
    public ServiceException() {
        super();
    }

    public ServiceException(String message) {
        super(message);
    }

    public ServiceException(String message, Throwable cause) {
        super(message, cause);
    }

    public ServiceException(Throwable cause) {
        super(cause);
    }

    protected ServiceException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }
}

1.2 用户在注册时可能会产生用户名被占用的错误,抛出异常:UsernameDuplicateException
package com.cy.store.service.ex;

public class UsernameDuplicateException extends ServiceException{
    public UsernameDuplicateException() {
        super();
    }

    public UsernameDuplicateException(String message) {
        super(message);
    }

    public UsernameDuplicateException(String message, Throwable cause) {
        super(message, cause);
    }

    public UsernameDuplicateException(Throwable cause) {
        super(cause);
    }

    protected UsernameDuplicateException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }
}

1.3 .在用户进行注册时,会执行数据库的INSERT操作,该操作也是有可能失败的。则创建

InsertException异常类,继承自ServiceException类,并从父类生成子类的5个构造方法。

package com.cy.store.service.ex;

public class InsertException extends ServiceException{
    public InsertException() {
        super();
    }

    public InsertException(String message) {
        super(message);
    }

    public InsertException(String message, Throwable cause) {
        super(message, cause);
    }

    public InsertException(Throwable cause) {
        super(cause);
    }

    protected InsertException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }
}

2、设计接口和抽象方法

2.1 先创建com.cy.store.service.IUserService业务层接口,并在接口中添加抽象方法。
package com.cy.store.service;

import com.cy.store.entity.User;

//用户模块业务层接口
public interface IUserService {
    /**
     *
     * 用户注册方法
     * @param user 用户的数据对象
     */
    void reg(User user);

}

2.2 创建com.cy.store.service.impl.UserServiceImpl业务层实现类,并实现IUserService接口。在类之前添加@Service注解,并在类中添加持久层UserMapper对象。
package com.cy.store.service.impl;

import com.cy.store.entity.User;
import com.cy.store.mapper.UserMapper;
import com.cy.store.service.IUserService;
import com.cy.store.service.ex.InsertException;
import com.cy.store.service.ex.UsernameDuplicateException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;

import java.time.LocalDateTime;
import java.util.Date;
import java.util.UUID;

@Service
//用户模块业务层实现类
public class UserServiceImpl implements IUserService {
    @Autowired
    private UserMapper usermapper;
    @Override
    public void reg(User user) {
        //通过user参数来获取传递过来的username
        String username = user.getUsername();
        //调用findUsername判断用户是否被注册
        User result = usermapper.findUsername(username);
        //判断结果集是否为null
        if (result !=null){
            //抛出异常
            throw new UsernameDuplicateException("用户名被占用");
        }

          //密码加密处理MD5
        //串  +   password   +串--------->MD5算法加密,连续加载三次
        //盐值+   password   +盐值--------盐值本身就是一个随机的字符串
        String oldPassword = user.getPassword();
        //随机生成一个盐值
        String salt = UUID.randomUUID().toString().toUpperCase();//全部大写的盐值
        //补全数据:盐值
        user.setSalt(salt);
        //加密处理
        String md5Password = getMD5Password(oldPassword, salt);
        //将加密后的密码重新补全设置到user对象中
        user.setPassword(md5Password);


        //补全数据:is_delete设置为0
        user.setIsDelete(0);
        //补全数据:4个日志字段信息
        user.setCreatedUser(user.getUsername());
        user.setModifiedUser(user.getUsername());
        Date date = new Date();
        user.setCreatedTime(date);
        user.setModifiedTime(date);


        //执行注册业务功能的实现(rows==1)
        Integer rows = usermapper.insert(user);
        if (rows!=1){
            throw new InsertException("在用户注册过程中产生了位置的错误");
        }

    }


    //定义一个MD5算法加密方法
    private String getMD5Password(String password,String salt){
        for (int i=1;i<3;i++){

            //MD5算法调用
            password = DigestUtils.md5DigestAsHex((salt + password + salt).getBytes()).toUpperCase();
        }
        //返回加密后的密码
        return password;
    }
}

2.3 单元测试
package com.cy.store.service;

import com.cy.store.entity.User;
import com.cy.store.mapper.UserMapper;
import com.cy.store.service.ex.ServiceException;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest//表示当前类是一个测试类,不会随项目一块打包

public class UserServiceTests {

    @Autowired
    private IUserService userService;

    @Test

    public void reg(){
        try {
            User user = new User();
            user.setUsername("林峰2");
            user.setPassword("123344");
            userService.reg(user);
            System.out.println("ok");
        } catch (ServiceException e) {
            //获取类的对象,再获取名称
            System.out.println(e.getClass().getSimpleName());
            //获取异常的具体描述信息
            System.out.println(e.getMessage());
        }
    }
}

注册-控制层

1、创建响应结果类

状态码、状态描述信息、数据。这部分功能封装在一个类中,将这类作为方法的返回值,返回给前端浏览器
创建com.cy.store.util.JsonResult响应结果类型。

package com.cy.store.util;

import java.io.Serializable;

public class JsonResult<E> implements Serializable {
    private Integer state;//状态码
    private String message;//描述信息
    private E data;//数据

    public JsonResult() {
    }

    public JsonResult(Integer state) {
        this.state = state;
    }
/** 出现异常时调用 */
    public JsonResult(Throwable e) {
    // 获取异常对象中的异常信息
        this.message = e.getMessage();
    }
    
    public JsonResult(Integer state, E data) {
        this.state = state;
        this.data = data;
    }

    public Integer getState() {
        return state;
    }

    public void setState(Integer state) {
        this.state = state;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public E getData() {
        return data;
    }

    public void setData(E data) {
        this.data = data;
    }
}

2、设计请求

2.1 依据当前的业务功能模块进行请求的设计

请求路径:/users/reg
请求参数:User user
请求类型:POST
响应结果:JsonResult

3、处理请求

3.1 创建一个控制层对应的类UserController类。依赖于业务层的接口。

创建com.cy.store.controller.UserController控制器类,在类的声明之前添加@RestController和
@RequestMapping(“users”)注解,在类中添加IUserService业务对象并使用@Autowired注解修饰。

package com.cy.store.controller;

import com.cy.store.entity.User;
import com.cy.store.service.IUserService;
import com.cy.store.service.ex.InsertException;
import com.cy.store.service.ex.UsernameDuplicateException;
import com.cy.store.util.JsonResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("users")
public class UserController {
    @Autowired
    private IUserService userService;
    @RequestMapping("/reg")
    public JsonResult<Void> reg(User user){
        //创建响应结果对象
        JsonResult<Void> result = new JsonResult<>();
        try {
            userService.reg(user);
            result.setState(200);
            result.setMessage("用户注册成功");
        } catch (UsernameDuplicateException e) {
            result.setState(4000);
            result.setMessage("用户名被占用");
        }catch (InsertException e){
            result.setState(5000);
            result.setMessage("注册时产生未知的异常");
        }
          return result;
    }
}

项目启动后,打开浏览器访问http://localhost:8080/users/reg?username=user001&password=123456

在这里插入图片描述
3.2 控制层调整优化设计
在控制抽离一个父类,在这个父类中同一处理关于异常的相关操作。编写一个BaseController类,统一处理

package com.cy.store.controller;

import com.cy.store.service.ex.InsertException;
import com.cy.store.service.ex.ServiceException;
import com.cy.store.service.ex.UsernameDuplicateException;
import com.cy.store.util.JsonResult;
import org.springframework.web.bind.annotation.ExceptionHandler;

//控制层类的基类
public class BaseController {
    public static final int ok = 200;//操作成功的状态码

    /*
        请求处理方法,这个方法的返回值就是需要传递给前端的数据
       自动将异常对象传递给此方法的参数列表上
    * 当前项目中产生了异常,被统一拦截到此方法中,这个方法此时就是充当请求处理方法,方法的返回值直接给到前端
    * */

    @ExceptionHandler(ServiceException.class)
    public JsonResult<Void> handleException(Throwable e){
        JsonResult<Void> result = new JsonResult<>(e);
        if (e instanceof UsernameDuplicateException){
            result.setState(4000);
            result.setMessage("用户名被占用");
        }else if(e instanceof InsertException){
            result.setState(5000);
            result.setMessage("注册时产生未知的异常");
        }
        return result;
    }

}


@RestController
@RequestMapping("users")
public class UserController extends BaseController {
    @Autowired
    private IUserService userService;
    @RequestMapping("reg")
    public JsonResult<Void> reg(User user){

            userService.reg(user);

        return new JsonResult<>(ok);
    }
}

注册-前端页面

在register页面来编写发送请求的方法,点击事件来完成。选中对应的按钮($(“选择器”)),再去添加点击的事件

<script type="text/javascript">
		//1、监听注册按钮是否被点击,如果被点击可以执行一个方法
          $("#btn-reg").click(function () {
			  console.log($("#form-reg").serialize());
			  //2、发送ajax()的异步请求来完成用户的注册功能
			  $.ajax({
				  url: "/users/reg",
				  type: "POST",
				  data: $("#form-reg").serialize(),
				  dataType: "JSON",
				  success: function (json) {
                  if (json.state == 200){
                  	alert("注册成功")
				  }else {
                  	alert("注册失败")
				  }
				  },
				  error: function (xhr) {
                  alert("注册时产生未知的错误!"+xhr.status)
				  }
			  })
		  })
	</script>

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值