SpringBoot構建電商基礎秒殺項目(一)


SpringBoot其實不是新框架,而是默認配置了很多框架的使用方式。就像maven整合了所有jar包,Springboot整合了所有框架,並通過一行簡單的main方法啟動應用

繼承了spring的框架們:

 

電商秒殺應用簡介:

商品列表頁獲取秒殺商品列表

進入商品詳情頁獲取秒殺商品詳情

秒殺開始后進入下單確認頁下單並支付成功

項目實戰:

使用IDEA+Maven搭建SpringBoot開發環境

集成Mybatis操作數據庫

實現秒殺項目

 

 第2章·基礎項目搭建的基本流程:

構建Maven項目—>引入SpringBoot依賴—>接入Mybatis

2.1 使用IDEA創建maven項目

new->project->maven項目->選擇maven-archetype-quickstart

1.導入springboot相關配置:

https://blog.csdn.net/m0_37657841/article/details/90524410

https://blog.csdn.net/midnight_time/article/details/90717676

跟着做完后輸入8080出現了一下頁面說明springboot工程在不需要任何外力下內嵌了tomcat容器

2.SpringMVC+RESTful的使用:

???有點失敗,網頁沒有出來,依舊是上面那樣。沒有報錯也不知為啥。

***************************
APPLICATION FAILED TO START
***************************

Description:

Web server failed to start. Port 8080 was already in use.

----------------------------------------------------------------

看到報錯了。。。8080已經被用了??之前tomcat不是都關了嗎。。

注:8080 是默認端口,如果要使用其他端口,可以在res下面寫 application.properties 修改,如:server.port=8090

然后我把idea停止運行再試就成功了。。。。所以是自己占了自己??暈

好了,還是挺神奇的,都沒有在index.jsp上面寫對應到方法的requestmapping?(不過簡單的顯示確實不需要頁面。。。

3.接入Mybatis(看安利視頻的博主圖文並茂的博客鏈接)

mybatis竟然有自動生成mapping.xml文件的插件。。。

創建數據庫,填寫mybatis-generator.xml,這個xml里面指定mybatis插件連接數據庫和讓插件生成什么文件生到哪里。

運行Mybatis插件,自動生成相關文件

就還挺神奇的,實體類(對應表),DAO接口(寫方法)和mapping.xml文件(sql語句),這3個都不需要手動編寫了

只需要把包寫好就行,比如實體類老師先寫好了包dataobject,然后改生成文件中的包的路徑com.miaoshaproject.dataobject

然后寫生成對應表及類名

 運行run:mybatis_generator

然后application.properties中配置SpringBoot項目數據源

------------------------------------------------------------------------------------------------------------------

編寫測試的時候@MapperScan注解報紅??

找半天后發現pom文件里mybatis的包引錯了。。。

要用mybatis-springboot

<!--5、引入的第五個:Mybatis相關的jar包-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.1</version>
</dependency>

 ------------------------------------------------------------------------------------------------------

然后要運行app這個,換過來。千萬運行之前那個generator的maven

網友總結了所有代碼,但沒有詳細注解說明:

https://www.cnblogs.com/victorbu/p/10538615.html

輸入8090端口可以看到了:

 然后手動網數據庫插入一條數據:

顯示數據庫中找到的用戶的名字

以上就是第二章基礎搭建,springboot接入mybatis,編寫generator.xml讓插件自動生成實體類,dao接口和映射文件。

 

 第三章 用戶模塊開發

上一章,我們學會了:

構建Maven項目
引入SpringBoot
引入Mybatis
簡單使用SpringMVC

https://blog.csdn.net/midnight_time/article/details/91048543

https://blog.csdn.net/m0_37657841/article/details/90545984

接下來就要詳細的學習使用SpringMVC了,具體知識點有:

1.MVC分層架構

之前學的都是數據庫查詢到的一行實體對象數據直接返回到前端,但實際開發中不可以直接返回數據庫的數據,需要service層有一個model掩蓋一下。

password屬性也加到model里面。 model層才是真正處理業務核心層,而dataobject實體類僅僅只是對數據庫的映射

但是model也不需要全都返回給前端,所以在cotroller層再增加一個viewobject層

由cotroller層的@requestbody和@requestparm在頁面展示了json數據

    1. DAO層 --- dataobject,與數據庫一一映射

    2. Service層 --- model,整合因業務需求而分離的dataobject

    3. Controller層 --- viewobject,屏蔽如密碼等敏感信息返回給前端

2.通用返回類型

Controller中的方法返回值類型會出現不同,比如返回viewobject,返回null,返回異常Exception信息等等。如果每個方法都有獨自的返回值類型的話,那樣的設計不太好,也不容易以統一的形式(比如統一的JSON格式)呈現給前端。好的解決方法就是獨立出一層:response,創建一種通用的返回值類型CommonReturnType,它有兩個屬性status和data。

1.增加一個response包。創建CommonReturnType類給他統一返回類型。

public class CommonReturnType {
    //表明對應請求的返回結果 success fail
    private String status;
    //若status=success,則data內返回前端需要的json數據
    //若status=fail,則data內使用通用的錯誤碼格式
    private Object data;

//    public CommonReturnType(String status, Object data) {//這才是構造方法
//        this.status = status;
//        this.data = data;
//    }
//定義一個通用的創建方法 是自己寫的方法 不是構造方法 那為什么不用構造方法呢?因為他這個可以返回值!
public static CommonReturnType create(Object result){//傳入對象或基本數據類型都可以
    return create(result, "success");//調用下面這個重載的方法,傳入的都依然返回 但是多設置了一個status
}

    public static CommonReturnType create(Object result, String status){

        CommonReturnType type = new CommonReturnType();
        type.setStatus(status);
        type.setData(result);

        return type;
    }



    public String getStatus() {
        return status;
    }

    public void setStatus(String status) {
        this.status = status;
    }

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }
}
View Code

修改cotroller的返回值為這個,則可以看到瀏覽器返回多了個statusdata 統一的返回類型

(學MVC時那個前端控制器和視圖控制器可以自動對應success.jsp,fail.jsp也很好用。。這里springboot好像沒有web.xml配置這些了??

 定義通用的返回對象——返回錯誤信息

    1.創建error包

    2.創建commonError接口

public interface CommonError {
    public int getErrCode();
    public String getErrMsg();
    public CommonError setErrMsg(String errMsg);

    3.創建接口的實現類枚舉類(就可以直接寫定義),因為異常有ErrorCode和ErrorMsg兩個屬性,因此我們將異常封裝一下抽象到一個枚舉類EmBusinessError

//實現類是一個枚舉類 這個類可以傳給cotroller里面捕獲異常相關的繼承exception的新建一個BusinessException類
public enum EmBusinessError implements CommonError {

    //通用錯誤類型00001
    PARAMETER_VALIDATION_ERROR(00001, "參數不合法"),

    //10000開頭為用戶信息相關錯誤定義
    USER_NOT_EXIST(10001, "用戶不存在")
    //下面還可以繼續方便增加枚舉的錯誤信息 代碼修改可用性高
    ;

    private int errCode;
    private String errMsg;

    EmBusinessError(int errCode, String errMsg) {
        this.errCode = errCode;
        this.errMsg = errMsg;
    }

    @Override
    public int getErrCode() {
        return this.errCode;
    }

    @Override
    public String getErrMsg() {
        return this.errMsg;
    }

    @Override
    public CommonError setErrMsg(String errMsg) {
        this.errMsg = errMsg;
        return this;
    }
}
View Code

使用這個類就是,這個類可以傳給cotroller里面捕獲異常相關的繼承exception的新建一個BusinessException類

//包裝器業務異常實現 和em類一樣實現接口,且都可以通過set方法覆蓋掉原來的接口對象
public class BusinessException extends Exception implements CommonError {
    private CommonError commonError;

    //直接接收EmBusinessError的傳參用於構造業務異常(?怎么傳啊?這個參數就是來自em實現類?還是里面的set方法返回值?
    // 應該是后者em的set方法返回值傳進這里面初始化)
    public BusinessException(CommonError commonError) {
        super();//有一些Exception自身初始化的機制在里面
        this.commonError = commonError;
    }
    //接收自定義errMsg的方式構造業務異常
    public BusinessException(CommonError commonError, String errMsg) {
        super();
        this.commonError = commonError;
        this.commonError.setErrMsg(errMsg);
    }

    @Override
    public int getErrCode() {
        return this.commonError.getErrCode();
    }

    @Override
    public String getErrMsg() {
        return this.commonError.getErrMsg();
    }

    @Override
    public CommonError setErrMsg(String errMsg) {
        this.commonError.setErrMsg(errMsg);
        return this;
    }
}
View Code

然后就可以在controller里面拋出了,用法是這樣的,參數直接傳寫好的枚舉就可以!!省事兒!

        //若獲取的對應用戶信息不存在
        if (userModel == null) {
            throw new BusinessException(EmBusinessError.USER_NOT_EXIST);
        }

但不管是直接throw的業務異常或者JVM虛擬機拋出的運行時異常,僅僅是拋到了Tomcat的容器層,我們在Controller層並不能進行處理。所以接着來

     4.定義@exceptionHandler解決未被controller層吸收的exception

像處理異常這種工作,不只是UserController要用到,以后處理其他模塊也有可能有異常,因此將代碼提取到新建一個BaseController中,讓UserController繼承BaseController,這樣以后擴展其他模塊時,代碼就可以復用了

@Controller("user")
@RequestMapping("/user")
public class UserController extends BaseController{

    @Autowired
    private UserService userService;

    @RequestMapping("/get")
    @ResponseBody
    public CommonReturnType getUser(@RequestParam(name = "id") Integer id) throws BusinessException {
        //調用service服務獲取對應id的用戶對象並返回給前端
        UserModel userModel = userService.getUserById(id);

        //若獲取的對應用戶信息不存在
        if (userModel == null) {
//            userModel.setEncrptPassword("123");
           throw new BusinessException(EmBusinessError.USER_NOT_EXIST);
        }

        //將核心領域模型用戶對象轉化為可供UI使用的viewobject
        UserVO userVO = convertFromModel(userModel);
        //返回通用對象
        return CommonReturnType.create(userVO);
    }

    //把model轉成VO 主要是為了調用service方法生成的是model,但我們需要VO類
    private UserVO convertFromModel(UserModel userModel){
        if(userModel == null){
            return null;
        }
        UserVO userVO = new UserVO();
        BeanUtils.copyProperties(userModel,userVO);

        return userVO;
    }

}
View Code

以上統一了返回類型和錯誤信息

完成了基礎能力建設,接下來進入模型能力管理。功能方面了

用戶信息管理:

     otp短信獲取

  otp注冊用戶

  用戶手機登錄

3.otp(一次性密碼)驗證碼生成

這里是后台生成隨機數發短信給用戶,跟之前學的網頁上生成隨機數讓用戶填寫不太一樣

 //用戶獲取!!otp短信方法
    @RequestMapping("/getotp")
    @ResponseBody
    public CommonReturnType getOtp(@RequestParam(name="telphone")String telphone){
        //需要按照一定的規則生成OTP驗證碼 就是單純的Random隨機數
        Random random = new Random();
        int randomInt = random.nextInt(99999);//此時隨機數取值[0,99999)
        randomInt+=10000;//此時取值[10000,109999)
        String otpCode = String.valueOf(randomInt);

        //將OTP驗證碼同對應用戶的手機號關聯 企業級是使用Redis來實現的,(鍵值對形式的數據庫,反復點擊可以反復覆蓋)
        // 但是目前這個項目屬於入門級別沒講到分布式,暫時用httpServletRequest對象獲取session方式來綁定手機號與驗證碼。
        request.getSession().setAttribute(telphone,otpCode);//就這樣做關聯了

        //將OTP驗證碼通過短信通道發送給用戶,省略(有興趣可買第三方服務的短信通道)
        System.out.println("telphone = "+telphone+"&otpCode ="+otpCode);

        return CommonReturnType.create(null);
    }

控制台打印了手機號和綁定的驗證碼:

3.6 用戶模型管理——Metronic模板簡介

采用前后端分離的思想,建立一個html文件夾,引入static文件夾(下載資料里)

前端文件保存在本地的哪個盤下都可以,因為是通過ajax來異步獲取接口

getotp.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>GetOtp</title>
    <script src = "static/assets/global/plugins/jquery-1.11.0.min.js" type="text/javascript"></script>
    <link href="static/assets/global/plugins/bootstrap/css/bootstrap.min.css" rel="stylesheet" type="text/css"/>
    <link href="static/assets/global/plugins/css/component.css" rel="stylesheet" type="text/css"/>
    <link href="static/assets/admin/pages/css/login.css" rel="stylesheet" type="text/css"/>

</head>
<body class="login">
<div class="content">
    <h3 class="form-title">獲取otp信息</h3>
    <div class="form-group">
        <label class="control-label">手機號</label>
        <div>
            <input class="form-control" type="text" placeholder="手機號" name="telphone" id="telphone"/>
        </div>
    </div>
    <div class="form-actions">
        <button class="btn blue" id="getotp" type="submit">
            獲取otp短信
        </button>
    </div>
</div>

</body>

<script>
    jQuery(document).ready(function () {

        //綁定otp的click事件用於向后端發送獲取手機驗證碼的請求
        $("#getotp").on("click",function () {

            var telphone=$("#telphone").val();
            if (telphone==null || telphone=="") {
                alert("手機號不能為空");
                return false;
            }

            //映射到后端@RequestMapping(value = "/getotp", method = {RequestMethod.POST}, consumes = {CONTENT_TYPE_FORMED})
            $.ajax({
                type:"POST",
                contentType:"application/x-www-form-urlencoded",
                url:"http://localhost:8090/user/getotp",
                data:{
                    "telphone":$("#telphone").val(),
                },
                success:function (data) {
                    if (data.status=="success") {
                        alert("otp已經發送到了您的手機,請注意查收");
                    }else {
                        alert("otp發送失敗,原因為" + data.data.errMsg);
                    }
                },
                error:function (data) {
                    alert("otp發送失敗,原因為"+data.responseText);
                }
            });
        });
    });

</script>
</html>
View Code

這里的$#是取地址去id取元素 好像不是jsp的el表達式

但是有跨域請求出錯,因為html是本地文件的端口,服務器是主機端口。跨域請求錯誤,只需要在UserController類上加一個注解@CrossOrigin即可

 控制台打印成功

4.登錄、注冊功能

3.9 用戶模型管理——用戶注冊功能實現

1.實現方法:conttoller中用戶注冊方法,也是通用返回類型給前端,就是數據加狀態,不返回就create方法傳進null。

//    用戶注冊方法
    @RequestMapping(value = "/register", method = {RequestMethod.POST}, consumes = {CONTENT_TYPE_FORMED})
    @ResponseBody //要從請求體拿的參數就都要寫出
    public CommonReturnType register(@RequestParam(name = "telphone") String telphone,
                                     @RequestParam(name = "otpCode") String otpCode,
                                     @RequestParam(name = "name") String name,
                                     @RequestParam(name = "gender") String gender,
                                     @RequestParam(name = "age") String age,
                                     @RequestParam(name = "password") String password) throws BusinessException, UnsupportedEncodingException, NoSuchAlgorithmException {

        //驗證手機號和對應的otpCode相符合
        String inSessionOtpCode = (String) this.request.getSession().getAttribute(telphone);//找到session里的otp(用戶提交的手機號必須一致才能找到) if (!com.alibaba.druid.util.StringUtils.equals(otpCode, inSessionOtpCode)) {throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR, "短信驗證碼不符合");
        }//符合的話往下走 給數據庫傳入用戶前端填寫的數據 所以要去業務層寫方法

        //用戶的注冊流程
        UserModel userModel = new UserModel();
        userModel.setName(name);
        userModel.setAge(Integer.valueOf(age));
        userModel.setGender(Byte.valueOf(gender));
        userModel.setTelphone(telphone);
        userModel.setRegisterMode("byphone");

        //密碼加密才能傳入數據庫(?。。
        userModel.setEncrptPassword(this.EncodeByMd5(password));

        userService.register(userModel);
        return CommonReturnType.create(null);

   }

    //密碼加密
    public String EncodeByMd5(String str) throws NoSuchAlgorithmException, UnsupportedEncodingException {
        //確定計算方法
        MessageDigest md5 = MessageDigest.getInstance("MD5");
        BASE64Encoder base64en = new BASE64Encoder();
        //加密字符串
        String newstr = base64en.encode(md5.digest(str.getBytes("utf-8")));
        return newstr;
    }

要做校驗在傳入數據庫之前判斷各個屬性的數據是否為空,在serviceimpl里面加注冊register方法。

 @Override
    @Transactional public void register(UserModel userModel) throws BusinessException {
        if (userModel == null) {
            throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR);
        }
        if (StringUtils.isEmpty(userModel.getName()) //因為字符串類型不能用==null判斷所以用StringUtils.isEmpty方法(依賴的?)
                || userModel.getGender() == null
                || userModel.getAge() == null
                || StringUtils.isEmpty(userModel.getTelphone())) {
            throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR);
        }


        //實現傳入的model->dataobject方法 下面寫一個轉換方法 這些框架以后也有用
        UserDO userDO = convertFromModel(userModel);
        userDOMapper.insertSelective(userDO);//這個好像之前也學過我還測試來着(學框架的時候在哪里???找不着了。。。)
        // 就是如果傳入了null是不會覆蓋之前有數據的值。如果是insert就會覆蓋了。
        UserPasswordDO userPasswordDO = convertPasswordFromModel(userModel);
        userPasswordDOMapper.insertSelective(userPasswordDO);

        return;
    }

    //model->dataobject的轉換方法
    private UserDO convertFromModel(UserModel userModel) {
        if (userModel == null) {
            return null;
        }
        UserDO userDO = new UserDO();
        BeanUtils.copyProperties(userModel, userDO);
        return userDO;
    }
//model的password->dataobject的password轉換方法
private UserPasswordDO convertPasswordFromModel(UserModel userModel) {
    if (userModel == null) {
        return null;
    }
    UserPasswordDO userPasswordDO = new UserPasswordDO();
    userPasswordDO.setEncrptPassword(userModel.getEncrptPassword());
    userPasswordDO.setUserId(userModel.getId());

    return userPasswordDO;
}

引入做輸入校驗的依賴(apache.lang包) StringUtils用的這個包

<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
<dependency>
  <groupId>org.apache.commons</groupId>
  <artifactId>commons-lang3</artifactId>
  <version>3.7</version>
</dependency>

提交到數據庫insert要記得加事務控制。@Transactional

首先在getotp界面添加注冊成功的跳轉界面,然后寫前端注冊界面:也用h5 復制getotp寫一個register.html

 七、用戶登錄流程

還需要解決跨域的問題,因為

//跨域請求中,不能做到session共享,要設置

@CrossOrigin(allowCredentials = "true",allowedHeaders = "*")

然后前端也要加ajax受信 

//允許跨域請求
xhrFields:{withCredentials:true},

--------------------------------------------------------------------------------------------------

我加了register.html忘記加getotp了 debug半天也不太會,最后感覺sout比debug好使。。。

-------------------------------------------------------------------------------------------------

還是出錯,剛剛是手機號和驗證碼不匹配的錯誤,現在直接未知錯誤。。。。

debug半天發現是serviceimpl的這里有問題 那么就是mapper的問題??這不是自動生成的嘛。。。。我暈 

 還有我都給model設置id了它還是等於null???可能idea已經被我這頻繁啟動暫停折磨瘋掉了...

 

 這都什么人間疾苦。。

包導錯了改過來了(StringUtils這個包用剛剛添加的依賴apache公司的),還是不成功.................................

花費一下午也沒找出問題,放棄了。總之這個包是得注釋掉,但是注釋掉這個包,我的數據庫操作還是可能有問題。不再用這個了。

ps:github下載的要重新設置maven的倉庫地址,setting地址和證書-Dmaven.wagon.http.ssl.insecure=true -Dmaven.wagon.http.ssl.allowall=true

然后clean-install-reimport操作。但首先要改成本地都有的版本比較好。

------------------------------------------------------------------------------------------------------------

 <insert id="insertSelective" parameterType="com.miaoshaproject.dataobject.UserDO" keyProperty="id" useGeneratedKeys="true">

 添加id自增

------------0529經同學找錯發現錯在數據庫中表的設計,非空必須要有默認值--------------

varchar類型默認空的就是,寫兩個單引號 ‘’

int類型默認0就是

id有自增所以不必默認

改過來真的就注冊成功了.....難怪必須用下載的數據庫,人家的建表語句里定義了默認值...

還有就是,返回未知錯誤沒有返回什么錯誤,找錯誤應該想到所有異常最后都匯聚的BaseController里面

增加打印錯誤

----------------------------------------------------------------------------------------------------------------

上面並沒有做手機號的唯一性驗證

首先,在數據庫中添加索引:

索引名稱為:telphone_unique_index,索引字段選擇telphone,索引類型為UNIQUE,索引方法為BTREE

然后修改以下代碼:

 try {
            userMapper.insertSelective(userDO);
        } catch (DuplicateKeyException ex) {
            throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR, "手機號已注冊");//二重奏構造之二自定義的業務錯誤
        }

八、用戶登錄流程

用戶注冊流程,后端Controller層需要獲取用戶輸入,然后封裝成Model,傳遞給Service層,Service層再拆分成dataobject,調用DAO層,將DO插入數據庫。

登錄和注冊流程相似,只不過在調用DAO層時,不是進行插入操作,而是查詢操作。

1.controller層寫登錄方法/login

    //用戶登錄接口
    @RequestMapping(value = "/login", method = {RequestMethod.POST}, consumes = {CONTENT_TYPE_FORMED})
    @ResponseBody
    public CommonReturnType login(@RequestParam(name = "telphone") String telphone,
                                  @RequestParam(name = "password") String password) throws BusinessException, UnsupportedEncodingException, NoSuchAlgorithmException {
        //入參校驗
        if (StringUtils.isEmpty(telphone) || StringUtils.isEmpty(password)) {
            throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR);
        }

        //用戶登錄服務,用來校驗用戶登錄是否合法
        //用戶加密后的密碼
        UserModel userModel = userService.validateLogin(telphone, this.EncodeByMd5(password));

        //將登陸憑證加入到用戶登錄成功的session內
        this.httpServletRequest.getSession().setAttribute("IS_LOGIN", true);
        this.httpServletRequest.getSession().setAttribute("LOGIN_USER", userModel);

        return CommonReturnType.create(null);

    }
login

2.service層

接口

    /*
    telphone:用戶注冊手機
    encrptPassowrd:用戶加密后的密碼
     */
    UserModel validateLogin(String telphone, String encrptPassword) throws BusinessException;

實現類

    @Override
    public UserModel validateLogin(String telphone, String encrptPassword) throws BusinessException {
        //通過用戶手機獲取用戶信息
        UserDO userDO = userDOMapper.selectByTelphone(telphone);
        if (userDO == null) {
            throw new BusinessException(EmBusinessError.USER_LOGIN_FAIL);
        }
        userPasswordDO userPasswordDO = userPasswordDOMapper.selectByUserId(userDO.getId());
        UserModel userModel = convertFromDataObject(userDO, userPasswordDO); //比對用戶信息內加密的密碼是否和傳輸進來的密碼相匹配
        if (StringUtils.equals(encrptPassword, userModel.getEncrptPassword())) {
            throw new BusinessException(EmBusinessError.USER_LOGIN_FAIL);
        }

        return userModel;
    }

返回業務異常時用到的是在枚舉類里直接拿的

所以要在枚舉類里添加該異常

USER_LOGIN_FAIL(20002,"用戶手機號或密碼不正確"),

3.dao

需要添加四個文件(Mybatis自動生成的只有selectByPrimaryKey)

UserDOMapper 添加 selectByTelphone

UserPasswordDOMapper 添加 selectByUserId (這個之前測試返回類型的時候寫過了!!

DOMapper.java中的方法與DOMapper.xml 中數據庫CURD語句一一映射。

4.前端login.html

<body class="login">
    <div class="content">
        <h3 class="form-title">用戶登錄</h3>
        <div class="form-group">
            <label class="control-label">手機號</label>
            <div>
                <input class="form-control" type="text" placeholder="手機號" name="telphone" id="telphone"/>
            </div>
        </div>
        <div class="form-group">
            <label class="control-label">密碼</label>
            <div>
                <input class="form-control" type="password" placeholder="密碼" name="password" id="password"/>
            </div>
        </div>
        <div class="form-actions">
            <button class="btn blue" id="login" type="submit">
                登錄
            </button>
            <button class="btn green" id="register" type="submit">
                注冊
            </button>
        </div>
    </div>

</body>

<script>
    jQuery(document).ready(function () {

        //綁定注冊按鈕的click事件用於跳轉到注冊頁面
        $("#register").on("click",function () {
            window.location.href = "getotp.html";
        });

        //綁定登錄按鈕的click事件用於登錄
        $("#login").on("click",function () {

            var telphone=$("#telphone").val();
            var password=$("#password").val();
            if (telphone==null || telphone=="") {
                alert("手機號不能為空");
                return false;
            }
            if (password==null || password=="") {
                alert("密碼不能為空");
                return false;
            }

            //映射到后端@RequestMapping(value = "/login", method = {RequestMethod.POST}, consumes = {CONTENT_TYPE_FORMED})
            $.ajax({
                type:"POST",
                contentType:"application/x-www-form-urlencoded",
                url:"http://localhost:8090/user/login",
                data:{
                    "telphone":telphone,
                    "password":password
                },
                //允許跨域請求
                xhrFields:{withCredentials:true},
                success:function (data) {
                    if (data.status=="success") {
                        alert("登錄成功");
                    }else {
                        alert("登錄失敗,原因為" + data.data.errMsg);
                    }
                },
                error:function (data) {
                    alert("登錄失敗,原因為"+data.responseText);
                }
            });
            return false;
        });
    });

有點好奇客戶端非webview展示html的,那種原生的app怎么傳數據給服務器。尤其我的課題不能用表單傳,應該也是ajax這樣data里面就可以

點擊登錄:

點擊注冊回到getotp.html

3.10 優化校驗規則

去倉庫可以直接搜有用的輪子 ,老師直接搜了validator然后點擊版本號找依賴就是。

<!--校驗-->
<dependency>
  <groupId>org.hibernate</groupId>
  <artifactId>hibernate-validator</artifactId>
  <version>5.2.4.Final</version>
</dependency>

好用❤❤❤❤❤

https://mvnrepository.com/

好用❤❤❤❤

https://www.mvnjar.com/

好用❤❤❤❤

http://maven.outofmemory.cn/hot/

2.對validator進行一個簡單的封裝

新建validator的目錄

新建一個ValidationResult的類

校驗后的結果類,有一個布爾類型的hasError屬性,一個Map類型的errorMsgMap,和他們的set,get方法。

還有一個getErrMsg的方法從Map拿value。

public class ValidationResult {
    //校驗結果是否有錯
    private boolean hasErrors = false;
    //存放錯誤信息的map
    private Map<String, String> errorMsgMap = new HashMap<>();

    public boolean isHasErrors() {
        return hasErrors;
    }

    public void setHasErrors(boolean hasErrors) {
        this.hasErrors = hasErrors;
    }

    public Map<String, String> getErrorMsgMap() {
        return errorMsgMap;
    }

    public void setErrorMsgMap(Map<String, String> errorMsgMap) {
        this.errorMsgMap = errorMsgMap;
    }

    //實現通用的通過格式化字符串信息獲取錯誤結果的msg方法
    public String getErrMsg() {
        return StringUtils.join(errorMsgMap.values().toArray(), ",");
                //join方法是在每個元素之間打后面的參數即逗號,萬一又是年齡不為空,手機號不為空等多個錯誤 } }

新建一個ValidatiorImpl的類

主要是實現implements InitializingBean接口的實現類,配合實體類上的屬性們寫注解用

@Component
public class ValidatorImpl implements InitializingBean {

    private Validator validator;//校驗包里的類

    //實現校驗方法並返回校驗結果
    public ValidationResult validate(Object bean) {//主要是這個方法做校驗!!!
        final ValidationResult result = new ValidationResult();
        Set<ConstraintViolation<Object>> constraintViolationSet = validator.validate(bean); if (constraintViolationSet.size() > 0) {
            //有錯誤
            result.setHasErrors(true);//此時hasError屬性為true了
            constraintViolationSet.forEach(constraintViolation ->{
                String errMsg = constraintViolation.getMessage();//在實體類屬性的注解上 String propertyName = constraintViolation.getPropertyPath().toString(); result.getErrorMsgMap().put(propertyName, errMsg);//校驗結果類存入錯誤信息
            });
        }
        return result;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        //將hibernate validator通過工廠的初始化方式使其實例化
        this.validator = Validation.buildDefaultValidatorFactory().getValidator();
    }
}

在UserServiceImpl中使用validator做校驗,之后要給別的實體類做校驗也可以用

 
        
 //校驗入參
        ValidationResult result = validator.validate(userModel);
        if (result.isHasErrors()) {
            throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR, result.getErrMsg());
                                            //后面是錯誤的信息,實體類注解上的,會覆蓋 }

使用這個校驗在業務層impl,以后做校驗時只需要在model的屬性上做注解即可。

給屬性們加注解作為限制條件@NotNull @NotBlank

要認准這個包!!!

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
public class UserModel {

    private Integer id; @NotBlank(message = "姓名不能為空") private String name; @NotNull(message = "姓名不能為空") private Byte gender; @NotNull(message = "年齡不能為空") @Min(value = 0,message = "年齡不能小於0")//還能加這種判斷條件 @Max(value = 150,message = "年齡不能大於150") private Integer age; @NotBlank(message = "手機號不能為空") private String telphone; private String registerMode; private String thirdPartyId; @NotBlank(message = "密碼不能為空") private String encrptPassword;

 我們的報錯系統和校驗系統就很健壯了。

----------------------------------------------------------------------------------------------------------------------------------------------

關於后端所有帶異常的方法的返回值

我們這套代碼是前后端分離,且前后端都有進行校驗。有些校驗只有后端才能完成,

比如說:

手機號是否重復

驗證碼是否獲取成功

是否登錄成功

是否注冊成功

參數是否合法

用戶登錄手機號和密碼是否匹配等等。

這些校驗之后,如果異常則會拋出對應的異常, Service層throw Exception會拋到調用服務的Controller層,Controller中throw Exception最終都會在BaseController中進行return給前端。(走的還是通用返回類型)

列一下后端所有帶異常的方法的返回值,有助於邏輯的理解。前面學的時候沒太明白。

正確信息通用返回CommonReturnTyppe使用二重奏方法只傳入data或者null就是方法一默認的狀態是success方法二傳入data和自定義string類型的狀態

異常信息最終也是在basecontroller里用通用返回類型方法二,responsedata是一個map集合接收businessexception里的異常信息作為data,后面是自定義string的狀態"fail"

具體說這個responedata是commonerror接口的實現類bussinessexception繼承exception獲得的,他的構造方法是二重奏方法第一個構造方法可以直接傳入也是commonerror接口實現類的embusinesserror枚舉設置好的錯誤類型,第二個構造方法是傳入coomonerror還傳入自定義的errmsg覆蓋掉commonerror里面的errmsg.

{
    "status":"fail",
    "data":{
        "errorCode":10001,
        "errorMsg":"未知錯誤"//來自枚舉類的UNKNOWN_ERROR(10002, "未知錯誤"),但是是由baseController里面的定義系統錯誤時賦上的名字
 } }

 

   
  
  層
   
   
    

類名 方法名 返回正確信息 返回異常信息
Controller層 BaseController handlerException

 return CommonReturnType.create

(responseData,“fail”);//所以最終還是通用返回類型

Controller層  UserController getUser

return CommonReturnType

.create(userVO);

 無,但會throw new BusinessException

(EmBusinessError.USER_NOT_EXIST);

通過BaseController進行return

Controller層 UserController getOtp

return CommonReturnType

.create

(otpCodeObj, “successGetOtpCode”);

  無,但會throw new BusinessException

(EmBusinessError.PARAMETER_VALIDATION_ERROR,

手機號已重復注冊”);

通過BaseController進行return

Controller層 UserController register

 return CommonReturnType.

create(null);

無,但會 throw new BusinessException

(EmBusinessError.PARAMETER_VALIDATION_ERROR,

短信驗證碼錯誤”);

通過BaseController進行return

Controller層 UserController login

return CommonReturnType.

create(null);

無,但會throw new BusinessException

(EmBusinessError.PARAMETER_VALIDATION_ERROR);

通過BaseController進行return

Service層 UserServiceImpl register

throw new BusinessException

(EmBusinessError.PARAMETER_VALIDATION_ERROR);

通過BaseController進行return throw new BusinessException

(EmBusinessError.PARAMETER_VALIDATION_ERROR, result.getErrMsg());

通過BaseController進行return throw new BusinessException

(EmBusinessError.PARAMETER_VALIDATION_ERROR,

手機號已重復注冊”);

通過BaseController進行return

Service層  UserServiceImpl  validateLogin return userModel;

 throw new BusinessException

(EmBusinessError.USER_LOGIN_FAIL);

通過BaseController進行return throw new BusinessException

(EmBusinessError.PARAMETER_VALIDATION_ERROR);

通過BaseController進行return

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

----------------------------------------------------------------------------------------------

總結:

第二章  應用SpringBoot完成基礎項目搭建

1)  7步構建maven框架

2)5步引入springboot依賴,選擇Building a RESTful Web Service

3)9步完成Mybatis接入SpringBoot項目。(其中創建數據庫一定要注意除了自增的id以外,其他欄若設置了非空,就一定要寫默認值!!!)

步數總結看該博主:

https://blog.csdn.net/midnight_time/article/details/90717676

代碼復制看該博主:

https://blog.csdn.net/m0_37657841/article/details/90524410

第三章 用戶模塊開發

1)MVC分層架構,dao已經自動生成,要加上service和controller

2)通用返回類型

  之前springMVC的controller里面的方法是String為返回類型執行后return一個jsp頁面,也有viod的了,這里viod也要改為返回通用類型,數據為null即可,也有返回ModelAndView,后來@responsebody以后也可以直接把返回值為User對象直接返回給前端...(springMVCday2的response課,各種返回類型以及異常處理都有學,但是學完就忘好難過)。

  這里是統一返回數據和狀態,數據的正確信息返回viewobject或null或者像驗證碼這樣的自定義返回類型(博主用了我們沒用)異常信息(異常信息包含errorCode和errorMsg)或空。有了這樣結構化的返回值,再加上@ResponseBody注解,就會被序列化為前端容易理解的JSON字符串。(而且不需要jkson解析json和Javabean的jar包??

3)otp驗證碼生成

  controller就能完成,不需要數據庫。使用httpServletRequest.getSession().setAttribute(telphone, otpCode);在session域里面綁定手機號與驗證碼。(企業應該用redis)

4)登錄、注冊功能

  需要與數據庫交互了。

  發送驗證碼之后跳轉到注冊,注冊成功后跳轉到登錄。那么一開始應該是注冊登錄界面,不選登錄點擊注冊應該要跳轉到發送驗證碼。。

關於密碼表中用戶id要對應用戶表中的id的操作:

  1.要在useDOMapper.xml中給insertSelective方法設置自增(所以userDO就有了id可以給Model?)

  2.在UserModel轉換為密碼實體類 userPasswordDO的時候要設置密碼表中的userId : userPasswordDO.setUserId(userModel.getId());

  3.userModel中的id也不可能一開始就有,畢竟用戶不會傳id,所以需要在userDO執行insertSelective方法之后,通過UserDO,從數據庫中查詢到當前id,然后賦值給userModel。讓userModel帶着它去構建userPasswordDO。 : userModel.setId(userDO.getId());

5)項目中的校驗邏輯

  首先自定義一個校驗結果類,要是出錯了可以存出錯信息給通用返回類型給前端返回信息,然后一個實現 InitializingBean接口的實現類,就可以搭配注解使用了

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM