up:: 分层思想及其设计

说明: 本节介绍下用户注册流程

Controller层接收数据

 
//用户注册接口
@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")Integer gender,
                                 @RequestParam(name="age")Integer age,
                                 @RequestParam(name="password")String password) throws BusinessException, UnsupportedEncodingException, NoSuchAlgorithmException {
    //验证手机号和对应的otpcode相符合
    String inSessionOtpCode = (String) this.httpServletRequest.getSession().getAttribute(telphone);
    if(!com.alibaba.druid.util.StringUtils.equals(otpCode,inSessionOtpCode)){
        throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR,"短信验证码不符合");
    }
    //用户的注册流程
    UserModel userModel = new UserModel();
    userModel.setName(name);
    userModel.setGender(new Byte(String.valueOf(gender.intValue())));
    userModel.setAge(age);
    userModel.setTelphone(telphone);
    userModel.setRegisterMode("byphone");
    userModel.setEncrptPassword(this.EncodeByMd5(password));
    userService.register(userModel);
    return CommonReturnType.create(null);
}

说明:

这里的统一错误抛出待写。。。

注册页面

<html>
<head>
<meta charset="UTF-8">
<link href="static/assets/global/plugins/bootstrap/css/bootstrap.min.css" rel="stylesheet" type="text/css"/>
<link href="static/assets/global/css/components.css" rel="stylesheet" type="text/css"/>
<link href="static/assets/admin/pages/css/login.css" rel="stylesheet" type="text/css"/>
<script src="static/assets/global/plugins/jquery-1.11.0.min.js" type="text/javascript"></script>
 
</head>
 
<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="text" placeholder="验证码" name="otpCode" id="otpCode"/>
         </div>
      </div>
      <div class="form-group">
         <label class="control-label">用户昵称</label>
         <div>
            <input  class="form-control" type="text" placeholder="用户昵称" name="name" id="name"/>
         </div>
      </div>
      <div class="form-group">
         <label class="control-label">性别</label>
         <div>
            <input  class="form-control" type="text" placeholder="性别" name="gender" id="gender"/>
         </div>
      </div>
      <div class="form-group">
         <label class="control-label">年龄</label>
         <div>
            <input  class="form-control" type="text" placeholder="年龄" name="age" id="age"/>
         </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="register" type="submit">
            提交注册
         </button>
      </div>
   </div>
 
 
</body>
 
 
<script>
 
   jQuery(document).ready(function(){
 
      //绑定otp的click时间用于向后端发送获取手机验证码的请求
      $("#register").on("click",function(){
         var telphone = $("#telphone").val();
         var password = $("#password").val();
         var age = $("#age").val();
         var gender = $("#gender").val();
         var name = $("#name").val();
         var otpCode = $("#otpCode").val();
         if(telphone == null || telphone == ""){
            alert("手机号不能为空");
            return false;
         }
         if(password == null || password == ""){
            alert("密码不能为空");
            return false;
         }
         if(age == null || age == ""){
            alert("年龄不能为空");
            return false;
         }
         if(gender == null || gender == ""){
            alert("性别不能为空");
            return false;
         }
         if(otpCode == null || otpCode == ""){
            alert("密码不能为空");
            return false;
         }
 
         $.ajax({
            type:"POST",
            contentType:"application/x-www-form-urlencoded",
            url:"http://localhost:8090/user/register",
            data:{
               "telphone":$("#telphone").val(),
               "password":password,
               "age":age,
               "gender":gender,
               "otpCode":otpCode,
               "name":name
            },
            xhrFields:{withCredentials:true},
            success:function(data){
               if(data.status == "success"){
                  alert("注册成功");
                  window.location.href="./login.html";
               }else{
                  alert("注册失败,原因为"+data.data.errMsg);
               }
            },
            error:function(data){
               alert("注册失败,原因为"+data.responseText);
            }
         });
         return false;
      });
 
 
   });
 
 
</script>
 
 
 
 
</html>

这里还是使用ajax请求来进行,class标签名使用Metronic模板的,就能生成样式直接使用。

Service层开发,进行参数检验

@Override
@Transactional
public void register(UserModel userModel) throws BusinessException {
    if(userModel == null){
        throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR);
    }
    ValidationResult result =  validator.validate(userModel);
    if(result.isHasErrors()){
        throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR,result.getErrMsg());
    }
 
 
 
    //实现model->dataobject方法
    UserDO userDO = convertFromModel(userModel);
    try{
        userDOMapper.insertSelective(userDO);
    }catch(DuplicateKeyException ex){
        throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR,"手机号已重复注册");
    }
 
 
 
    userModel.setId(userDO.getId());
 
    UserPasswordDO userPasswordDO = convertPasswordFromModel(userModel);
    userPasswordDOMapper.insertSelective(userPasswordDO);
 
    return;
}

说明:

关于参数检验,参考前面所学SpringBoot电商项目商品分类模块使用Valid注解校验入参

若任何一步失败transactional标签会将对应的数据库回滚掉

参数检验

看看UserModel层是啥样的:

package com.imooc.miaoshaproject.service.model;
 
import javax.validation.constraints.*;
 
/**
 * Created by hzllb on 2018/11/11. */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;
 
    public String getEncrptPassword() {
        return encrptPassword;
    }
 
    public void setEncrptPassword(String encrptPassword) {
        this.encrptPassword = encrptPassword;
    }
 
    public Integer getId() {
        return id;
    }
 
    public void setId(Integer id) {
        this.id = id;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public Byte getGender() {
        return gender;
    }
 
    public void setGender(Byte gender) {
        this.gender = gender;
    }
 
    public Integer getAge() {
        return age;
    }
 
    public void setAge(Integer age) {
        this.age = age;
    }
 
    public String getTelphone() {
        return telphone;
    }
 
    public void setTelphone(String telphone) {
        this.telphone = telphone;
    }
 
    public String getRegisterMode() {
        return registerMode;
    }
 
    public void setRegisterMode(String registerMode) {
        this.registerMode = registerMode;
    }
 
    public String getThirdPartyId() {
        return thirdPartyId;
    }
 
    public void setThirdPartyId(String thirdPartyId) {
        this.thirdPartyId = thirdPartyId;
    }
}

这几个参数检验注解前面都学过,回去看看吧,这里不过多讲解。

抛出异常

package com.imooc.miaoshaproject.error;
 
/**
 * Created by hzllb on 2018/11/13. *///包装器业务异常类实现
public class BusinessException extends Exception implements CommonError {
 
    private CommonError commonError;
 
    //直接接收EmBusinessError的传参用于构造业务异常
    public BusinessException(CommonError commonError){
        super();
        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;
    }
 
    public CommonError getCommonError() {
        return commonError;
    }
}

关于Model与Do层的关系

可以看出,model层由UserDo和UserPasswordDo层构成,我们前端输入的信息全部在UserModel层接收,我们需要将其解耦为两个Do分别在其所负责的数据库进行查询。。。。

总结:

DO与PO的区别 - 知乎

领域模型中的实体类分为四种类型:VO、DTO、DO、PO_arthur.dy.lee的博客-CSDN博客

关于model层建VO和PO,实体类(VO,DO,DTO)的划分_hhah1122的博客-CSDN博客_vo类和实体类有什么区别

业务分层为:视图层(VIEW+ACTION)服务层(SERVICE)持久层(DAO)

VO(View Object):视图对象,用于展示层,它的作用是把某个指定页面(或组件)的所有数据封装起来。 DTO(Data Transfer Object):数据传输对象,这个概念来源于J2EE的设计模式,原来的目的是为了EJB的分布式应用提供粗粒度的数据实体,以减少分布式调用的次数,从而提高分布式调用的性能和降低网络负载,但在这里,我泛指用于展示层与服务层之间的数据传输对象。 DO(Domain Object):领域对象,就是从现实世界中抽象出来的有形或无形的业务实体。 PO(PersistentObject):持久化对象,它跟持久层(通常是关系型数据库)的数据结构形成一一对应的映射关系,如果持久层是关系型数据库,那么,数据表中的每个字段(或若干个)就对应PO的一个(或若干个)属性。

模型: 下面以一个时序图建立简单模型来描述上述对象在三层架构应用中的位置 用户发出请求(可能是填写表单),表单的数据在展示层被匹配为VO。 展示层把VO转换为服务层对应方法所要求的DTO,传送给服务层。 服务层首先根据DTO的数据构造(或重建)一个DO,调用DO的业务方法完成具体业务。 服务层把DO转换为持久层对应的PO(可以使用ORM工具,也可以不用),调用持久层的持久化方法,把PO传递给它,完成持久化操作。 对于一个逆向操作,如读取数据,也是用类似的方式转换和传递,略。