项目层级

 

 零、register.html / BaseController(v1.1)/ getotp.html(v1.1)

  1 <html>
  2 <head>
  3     <meta charset="UTF-8">
  4     <link rel="stylesheet" href="assets/global/plugins/bootstrap/css/bootstrap.min.css" type="text/css"/>
  5     <link rel="stylesheet" href="assets/global/css/components.css" type="text/css"/>
  6     <link rel="stylesheet" href="assets/admin/pages/css/login.css" type="text/css"/>
  7     <script rel="stylesheet" src="assets/global/plugins/jquery-1.11.0.min.js" type="text/javascript"></script>
  8     <link rel="stylesheet" href="selectMeiHua.css">
  9 </head>
 10 <body class="login" background="edge_bj_1.jpg">
 11 
 12 <div class="content">
 13     <h3 class="form-title">用户注册</h3>
 14     <div class="form-group">
 15         <label class="control-label">手机号</label>
 16         <div>
 17             <input type="text" class="form-control" placeholder="手机号" name="telphone" id="telphone">
 18         </div>
 19     </div>
 20     <div class="form-group">
 21         <label class="control-label">验证码</label>
 22         <div>
 23             <input type="text" class="form-control" placeholder="验证码" name="otpCode" id="otpCode">
 24         </div>
 25     </div>
 26     <div class="form-group">
 27         <label class="control-label">用户昵称</label>
 28         <div>
 29             <input type="text" class="form-control" placeholder="用户昵称" name="name" id="name">
 30         </div>
 31     </div>
 32     <div class="form-group">
 33         <label class="control-label">性别</label>
 34         <div class="select-box">
 35             <input type="text" class="select-input" value="" id="gender" name="gender" placeholder="性别">
 36             <ul class="options-box hide">
 37                 <li></li>
 38                 <li></li>
 39             </ul>
 40         </div>
 41     </div>
 42     <div class="form-group">
 43         <label class="control-label">年龄</label>
 44         <div>
 45             <input type="text" class="form-control" placeholder="年龄" name="age" id="age">
 46         </div>
 47     </div>
 48     <div class="form-group">
 49         <label class="control-label">密码</label>
 50         <div>
 51             <input type="password" class="form-control" placeholder="密码" name="password" id="password">
 52         </div>
 53     </div>
 54     <div>
 55         <button class="btn blue" id="register" type="submit">
 56             注册
 57         </button>
 58     </div>
 59 </div>
 60 
 61 </body>
 62 <script>
 63 
 64     jQuery(document).ready(function () {
 65 
 66         //绑定otp的click事件用于向后端发送手机验证码的请求
 67         $("#register").on("click", function () {
 68 
 69             var tel = $("#telphone").val();
 70             var name = $("#name").val();
 71             var age = $("#age").val();
 72             var gender = $("#gender").val();
 73             var otpCode = $("#otpCode").val();
 74             var password = $("#password").val();
 75             if (tel == null || tel == '') {
 76                 alert("用户手机号不能为空");
 77                 return false;
 78             }
 79             if (name == null || name == '') {
 80                 alert("昵称不能为空");
 81                 return false;
 82             }
 83             if (age == null || age == '') {
 84                 alert("用户年龄不能为空");
 85                 return false;
 86             }
 87             if (gender == null || gender == '') {
 88                 alert("用户性别不能为空");
 89                 return false;
 90             }
 91             if (password == null || password == '') {
 92                 alert("用户密码不能为空");
 93                 return false;
 94             }
 95             if (otpCode == null || otpCode == '') {
 96                 alert("验证码不能为空");
 97                 return false;
 98             }
 99             $.ajax({
100                 type: "POST",
101                 contentType: "application/x-www-form-urlencoded",
102                 url: "http://localhost:8080/user/register",
103                 data: {
104                     "telphone": $("#telphone").val(),
105                     "name": $("#name").val(),
106                     "age": $("#age").val(),
107                     "gender": $("#gender").val(),
108                     "otpCode": $("#otpCode").val(),
109                     "encrptPassword": $("#password").val(),
110                 },
111                 dataType: "json",
112                 xhrFields: {
113                     withCredentials: true
114                 },
115                 crossDomain: true,
116                 success: function (result) {
117 
118                     if (result.status == "success") {
119                         alert(result.data)
120                     } else {
121                         alert("请求失败 原因为:" + result.data.errMsg)
122                     }
123                 },
124                 error: function (data) {
125                     alert("请求失败 原因为:" + data.responseText)
126                 }
127             });
128         })
129     })
130 
131     window.onload = function () {
132 
133         // 判断是否有某个class
134         function hasClass(ele, cls) {
135             return ele.className.match(new RegExp("(\\s|^)" + cls + "(\\s|$)"));
136         }
137 
138         // //为指定的dom元素添加样式
139         function addClass(ele, cls) {
140             if (!hasClass(ele, cls)) ele.className += " " + cls;
141         }
142 
143         // //删除指定dom元素的样式
144         function removeClass(ele, cls) {
145             if (hasClass(ele, cls)) {
146                 var reg = new RegExp("(\\s|^)" + cls + "(\\s|$)");
147                 ele.className = ele.className.replace(reg, " ");
148             }
149         }
150 
151         // //如果存在(不存在),就删除(添加)一个样式
152         // function toggleClass(ele, cls) {
153         //   if (hasClass(ele, cls)) {
154         //     removeClass(ele, cls);
155         //   } else {
156         //     addClass(ele, cls);
157         //   }
158         // }
159 
160         document.getElementsByClassName('select-input')[0].onclick = function () {
161             var optionsBox = document.getElementsByClassName('options-box')[0];
162             var selectInput = document.getElementsByClassName('select-input')[0];
163             // 这里最好用children,不要用childNode, 否则会有多余的text节点
164             var lis = optionsBox.children;
165             if (hasClass(optionsBox, 'hide')) { // 如果当前不是正在打开选项状态
166                 removeClass(optionsBox, 'hide')
167                 addClass(selectInput, 'isActive')
168                 for (var i = 0; i < lis.length; i++) {
169                     if (lis[i].innerHTML == selectInput.value) { // 如果之前已经选择过,将之前的选项激活状态
170                         addClass(lis[i], 'active')
171                     } else {
172                         removeClass(lis[i], 'active')
173                     }
174                 }
175             } else {
176                 addClass(optionsBox, 'hide');
177                 removeClass(selectInput, 'isActive');
178             }
179         }
180 
181         document.getElementsByClassName('options-box')[0].onclick = function (e) {
182             var optionsBox = document.getElementsByClassName('options-box')[0];
183             var selectInput = document.getElementsByClassName('select-input')[0];
184             //这一行及下一行是为兼容IE8及以下版本
185             e = e || window.event;
186             var target = e.target || e.srcElement;
187             if (target.tagName.toLowerCase() === "li") {
188                 // 将选中的值赋值给展示框文本
189                 selectInput.value = target.innerHTML;
190                 // 关闭选择列表
191                 addClass(optionsBox, 'hide');
192                 // 取消展示框的激活状态
193                 removeClass(selectInput, 'isActive');
194             }
195         }
196 
197         // 列表中选项滑过效果
198         document.getElementsByClassName('options-box')[0].onmouseover = function (e) {
199             // 事件代理
200             var optionsBox = document.getElementsByClassName('options-box')[0];
201             var selectInput = document.getElementsByClassName('select-input')[0];
202             e = e || window.event;
203             var target = e.target || e.srcElement;
204             if (target.tagName.toLowerCase() === "li") {
205                 if (target.innerHTML != selectInput.value) { //如果滑过的不是已经选中的,给予暂时的滑过效果
206                     addClass(target, 'active');
207                 }
208             }
209         }
210 
211         document.getElementsByClassName('options-box')[0].onmouseout = function (e) {
212             var optionsBox = document.getElementsByClassName('options-box')[0];
213             var selectInput = document.getElementsByClassName('select-input')[0];
214             //这一行及下一行是为兼容IE8及以下版本
215             e = e || window.event;
216             var target = e.target || e.srcElement;
217             if (target.tagName.toLowerCase() === "li") {
218                 if (target.innerHTML != selectInput.value) { // 如果滑出的不是已经选中的,将滑过的效果取消
219                     removeClass(target, 'active');
220                 }
221             }
222         }
223     }
224 
225 </script>
226 </html>
 1 package com.miaoshaProject.controller;
 2 
 3 import com.miaoshaProject.error.BusinessException;
 4 import com.miaoshaProject.error.EnumBusinessError;
 5 import com.miaoshaProject.response.CommonReturnType;
 6 import org.springframework.http.HttpStatus;
 7 import org.springframework.web.bind.annotation.ExceptionHandler;
 8 import org.springframework.web.bind.annotation.ResponseBody;
 9 import org.springframework.web.bind.annotation.ResponseStatus;
10 
11 import javax.servlet.http.HttpServletRequest;
12 import java.util.HashMap;
13 
14 /**
15  * @Author wangshuo
16  * @Date 2022/4/14, 11:15
17  * 返回通用错误信息
18  */
19 public class BaseController {
20 
21     public final static String CONTENT_TYPE_FORMED="application/x-www-form-urlencoded";
22 
23     //定义通用的exceptionHandler解决未被Controller吸收的exception
24     @ExceptionHandler(Exception.class)
25     @ResponseStatus(HttpStatus.OK)
26     @ResponseBody
27     public Object handlerException(HttpServletRequest httpServletRequest, Exception e) {
28 
29         HashMap<Object, Object> hashMap = new HashMap<>();
30         if (e instanceof BusinessException) {
31             BusinessException businessException = (BusinessException) e;
32             hashMap.put("errCode", businessException.getErrorCode());
33             hashMap.put("errMsg", businessException.getErrorMsg());
34         } else {
35             hashMap.put("errCode", EnumBusinessError.UNKNOWN_ERROR.getErrorCode());
36             hashMap.put("errMsg", EnumBusinessError.UNKNOWN_ERROR.getErrorMsg());
37         }
38         return CommonReturnType.create(hashMap, "fail");
39     }
40 }
<html>
<head>
    <meta charset="UTF-8">
    <link rel="stylesheet" href="assets/global/plugins/bootstrap/css/bootstrap.min.css" type="text/css"/>
    <link rel="stylesheet" href="assets/global/css/components.css" type="text/css"/>
    <link rel="stylesheet" href="assets/admin/pages/css/login.css" type="text/css"/>
    <script rel="stylesheet" src="assets/global/plugins/jquery-1.11.0.min.js" type="text/javascript"></script>
</head>
<body class="login" background="edge_bj_1.jpg">

<div class="content">
    <h3 class="form-title">用户登录</h3>
    <div>
        <label class="control-label">手机号</label>
        <div class="form-group">
            <input type="text" class="form-control" placeholder="手机号" name="telphone" id="telphone">
        </div>
    </div>
    <div>
        <button class="btn blue" id="getotp" type="submit">
            获取验证码
        </button>
    </div>
</div>

</body>
<script>

    jQuery(document).ready(function () {

        //绑定otp的click事件用于向后端发送手机验证码的请求
        $("#getotp").on("click", function () {

            var tel = $("#telphone").val();
            if (tel == null || tel == '') {
                alert("用户手机号不能为空");
                return false;
            }
            $.ajax({
                type: "POST",
                contentType: "application/x-www-form-urlencoded",
                url: "http://localhost:8080/user/getotp",
                data: {
                    "telphone": $("#telphone").val(),
                },
                dataType: "json",
                xhrFields: {
                    withCredentials: true
                },
                crossDomain: true,
                success: function (data) {

                    if (data.status == "success") {
                        alert("验证码已发送至您的手机,请注意查收")
                        alert(data.data)
                        window.location.href = "D:/FirefoxDownLoad/static/register.html";
                    } else {
                        alert("请求失败  原因为:" + data.data.errorMsg)
                    }
                },
                error: function (data) {
                    alert("请求失败   原因为:" + data.responseText)
                }
            });
            return false;
        })
    })

</script>
</html>

 

 

一、pom依赖引入(redis / apache.commons-long3)

 1 <!-- https://mvnrepository.com/artifact/org.springframework.session/spring-session-data-redis -->
 2         <dependency>
 3             <groupId>org.springframework.session</groupId>
 4             <artifactId>spring-session-data-redis</artifactId>
 5             <version>2.6.1</version>
 6         </dependency>
 7 
 8         <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-redis -->
 9         <dependency>
10             <groupId>org.springframework.boot</groupId>
11             <artifactId>spring-boot-starter-data-redis</artifactId>
12             <version>2.6.6</version>
13         </dependency>
14 
15         <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
16         <dependency>
17             <groupId>org.apache.commons</groupId>
18             <artifactId>commons-lang3</artifactId>
19             <version>3.12.0</version>
20         </dependency>

application.properties

server.port=8080
#mybatis mapping??
mybatis.mapperLocations=classpath:mapping/*.xml
#MySQL
spring.datasource.name=miaosha
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/miaosha
spring.datasource.username=root
spring.datasource.password=678678
#druid??
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#session????
server.servlet.session.timeout=1
#redis
spring.redis.host=localhost
spring.redis.port=6379
spring.redis.password=678678

二、login_config拦截器加跨域请求处理

1.CorsFilter

 1 package com.miaoshaProject.login_config;
 2 
 3 import org.springframework.core.Ordered;
 4 import org.springframework.core.annotation.Order;
 5 import org.springframework.stereotype.Component;
 6 
 7 import javax.servlet.*;
 8 import javax.servlet.http.HttpServletRequest;
 9 import javax.servlet.http.HttpServletResponse;
10 import java.io.IOException;
11 
12 /**
13  * @Description 跨域请求拦截器
14  * @Author rongtao
15  * @Data 2019/4/24 13:32
16  * https://www.codeleading.com/article/4517856247/
17  */
18 @Component
19 @Order(Ordered.HIGHEST_PRECEDENCE)
20 public class CorsFilter implements Filter {
21 
22     public CorsFilter(){
23         System.out.println("CorsFilter Constructor...");
24     }
25 
26     @Override
27     public void init(FilterConfig filterConfig) throws ServletException {
28         System.out.println("CorsFilter init...");
29     }
30 
31     @Override
32     public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
33         System.out.println("CorsFilter doFilter...");
34         HttpServletRequest req = (HttpServletRequest) request;
35         HttpServletResponse res = (HttpServletResponse) response;
36         //允许请求携带认证信息(cookie)
37         res.setHeader("Access-Control-Allow-Credentials", "true");
38         //指定允许其他域名访问
39         res.setHeader("Access-Control-Allow-Origin", req.getHeader("Origin"));
40         //允许请求的类型
41         res.setHeader("Access-Control-Allow-Methods", "GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH");
42         //允许的请求头字段
43         res.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
44         //设置预检请求的有效期
45         //浏览器同源策略:出于安全考虑,浏览器限制跨域的http请求。怎样限制呢?通过发送两次请求:预检请求、用户请求。
46         //1、预检请求作用:获知服务器是否允许该跨域请求:如果允许,才发起第二次真实的请求;如果不允许,则拦截第二次请求
47         //2、单位:s,在此期间不用发送预检请求。
48         //3、若为0:表示每次请求都发送预检请求,每个ajax请求之前都会先发送预检请求。
49         res.setHeader("Access-Control-Max-Age", "3600");
50         //OPTIONS Method表示浏览器发送的预检请求。
51         if ("OPTIONS".equalsIgnoreCase(req.getMethod())) {
52             res.setStatus(HttpServletResponse.SC_OK);
53         } else {
54             chain.doFilter(req, res);
55         }
56     }
57 
58     @Override
59     public void destroy() {
60         System.out.println("CorsFilter destroy...");
61     }
62 }

2.InterceptorConfig

 1 package com.miaoshaProject.login_config;
 2 
 3 import com.miaoshaProject.login_config.SessionInterceptor;
 4 import org.springframework.context.annotation.Configuration;
 5 import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
 6 import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
 7 
 8 /**
 9  * @Description WebMvcConfigurerAdapter:扩展mvc
10  * @Author rongtao
11  * @Data 2019/4/24 13:41
12  * https://www.codeleading.com/article/4517856247/
13  */
14 @Configuration
15 public class InterceptorConfig extends WebMvcConfigurerAdapter {
16     @Override
17     public void addInterceptors(InterceptorRegistry registry) {
18         //注册登录超时拦截器,并排除拦截登录请求
19         registry.addInterceptor(new SessionInterceptor()).excludePathPatterns("/user/*");//update
20         super.addInterceptors(registry);
21     }
22 }

3.SessionInterceptor(待更新)

 1 package com.miaoshaProject.login_config;
 2 
 3 import com.miaoshaProject.dataobject.UserDO;
 4 import org.springframework.stereotype.Component;
 5 import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
 6 
 7 import javax.servlet.http.HttpServletRequest;
 8 import javax.servlet.http.HttpServletResponse;
 9 import java.io.PrintWriter;
10 
11 /**
12  * @Description 登录超时拦截器
13  * @Author rongtao
14  * @Data 2019/4/24 13:37
15  * https://www.codeleading.com/article/4517856247/
16  */
17 @Component
18 public class SessionInterceptor extends HandlerInterceptorAdapter {
19     //拦截action
20     @Override
21     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
22             throws Exception {
23         UserDO user = (UserDO) request.getSession().getAttribute("user");
24         System.out.println(user);
25         //session中User过期
26         if(user == null){
27             String uri = request.getRequestURI();
28             System.out.println(uri);
29             //ajax请求响应头会有,x-requested-with
30             if (request.getHeader("x-requested-with") != null && request.getHeader("x-requested-with")
31                     .equalsIgnoreCase("XMLHttpRequest")) {
32                 //在响应头设置session状态
33                 response.setHeader("sessionstatus", "timeout");
34                 response.setHeader("url", uri.substring(0, uri.indexOf("/", 1)));
35             } else {
36                 PrintWriter out = response.getWriter();
37                 StringBuilder sb = new StringBuilder();
38                 sb.append("<script type=\"text/javascript\" charset=\"UTF-8\">");
39                 sb.append("alert(\"登录超时,请重新登录\");");
40                 sb.append("window.top.location.href=\"");
41                 sb.append("/login.jsp");
42                 sb.append("\";</script>");
43                 out.print(sb.toString());
44                 out.close();
45             }
46             //返回false不再调用其他的拦截器和处理器
47             return false;
48         }
49         return true;
50     }
51 }

三、EnumBusinessError

 1 package com.miaoshaProject.error;
 2 
 3 /**
 4  * @Author wangshuo
 5  * @Date 2022/4/14, 8:43
 6  * 自定义error
 7  */
 8 public enum EnumBusinessError implements CommonError{
 9     //10001 参数不合法
10     PARAMETER_VALIDATION_ERROR(10001,"参数不合法"),
11     //20000未知错误
12     UNKNOWN_ERROR(20000,"未知错误"),
13     //以30000开头的错误码代表用户信息错误
14     USER_NOT_EXISTS(30001,"用户不存在"),
15     REGISTER_OTP_ERROR(30002,"验证码错误"),
16     REGISTER_REPEAT(30003,"该手机号已注册账户,请勿重复注册")
17     ;
18 
19     private EnumBusinessError(Integer code,String msg){
20 
21         this.errorCode = code;
22         this.errorMsg = msg;
23     }
24 
25     private int errorCode;
26     private String errorMsg;
27 
28     @Override
29     public int getErrorCode() {
30         return this.errorCode;
31     }
32 
33     @Override
34     public String getErrorMsg() {
35         return this.errorMsg;
36     }
37 
38     //定制化的方法改动错误信息
39     @Override
40     public CommonError setErrorMsg(String msg) {
41         this.errorMsg = msg;
42         return this;
43     }
44 }

四、UserController

 1 //用户注册接口
 2     @RequestMapping(value = "/register", method = {RequestMethod.POST}, consumes = {CONTENT_TYPE_FORMED})
 3     @ResponseBody
 4     public CommonReturnType userRegister(@RequestParam(name = "telphone") String telphone,
 5                                          @RequestParam(name = "otpCode") String otpCode,
 6                                          @RequestParam(name = "name") String name,
 7                                          @RequestParam(name = "gender") String gender,
 8                                          @RequestParam(name = "age") Integer age,
 9                                          @RequestParam(name = "encrptPassword") String encrptPassword) throws BusinessException, NoSuchAlgorithmException {
10 
11         //验证手机号和对应的optCode是否符合
12         /*String obj = (String)this.httpServletRequest.getSession().getAttribute(telphone);*/
13         String s = codeMap.get(telphone);
14 
15         //用druid类库的equals方法,这个有判空处理
16         /*if (!StringUtils.equals(obj, otpCode))
17             throw new BusinessException(EnumBusinessError.REGISTER_OTP_ERROR);*/
18         if (!StringUtils.equals(s, otpCode))
19             throw new BusinessException(EnumBusinessError.REGISTER_OTP_ERROR);
20         //用户的注册流程 不加构造方法了先,看的清楚点
21         UserModel userModel = new UserModel();
22         userModel.setAge(age);
23         if (StringUtils.equals(gender, "男")) {
24             userModel.setGender(1);
25         } else {
26             userModel.setGender(2);
27         }
28         userModel.setName(name);
29         userModel.setRegisterMode("byphone");
30         userModel.setTelpphone(telphone);
31         //md5加密密码
32         userModel.setEncrptPassword(this.encodeByMD5(encrptPassword));
33         uesrService.register(userModel);
34         return CommonReturnType.create("注册成功,请重新登录");
35     }
36 
37     //用户获取otp短信接口
38     @RequestMapping(value = "/getotp", method = {RequestMethod.POST}, consumes = {CONTENT_TYPE_FORMED})
39     @ResponseBody
40     public CommonReturnType getOtp(@RequestParam(name = "telphone") String telphone) {
41 
42         //需要按照一定的规则生成otp验证码
43         Random random = new Random();
44         int randomInt = random.nextInt(89999);
45         randomInt += 10000;
46         String otpCode = String.valueOf(randomInt);
47         //将opt验证码同用户的手机号码关联  使用httpSession的方式绑定他的手机号与optCode
48         /*this.httpServletRequest.getSession().setAttribute(telphone, otpCode);*/
49         //试试存map 不行就存redis
50         codeMap.put(telphone, otpCode);
51         System.out.println(telphone + " / " + otpCode);
52         //通过短信通道将验证码发送给用户(省略)
53         return CommonReturnType.create(otpCode);
54     }

五、UserServiceImpl

 1 @Override
 2     @Transactional//保证事务唯一性
 3     public void register(UserModel userModel) throws BusinessException {
 4 
 5         if (userModel == null)
 6             throw new BusinessException(EnumBusinessError.PARAMETER_VALIDATION_ERROR);
 7         if (StringUtils.isEmpty(userModel.getName()) || userModel.getGender() == null ||
 8                 StringUtils.isEmpty(userModel.getTelpphone()) || userModel.getAge() == null ||
 9                 StringUtils.isEmpty(userModel.getRegisterMode())) {
10 
11             throw new BusinessException(EnumBusinessError.PARAMETER_VALIDATION_ERROR);
12         }
13         UserDO userDO = convertFromModelObject(userModel);
14         //使用selective
15         //处理用户重复注册异常
16         try {
17             userDOMapper.insertSelective(userDO);
18         }catch (DuplicateKeyException e){
19             throw new BusinessException(EnumBusinessError.REGISTER_REPEAT);
20         }
21 
22         //把刚刚新增成功的userId赋值给UserPasswordDO用于密码表的新增
23         userModel.setId(userDO.getId());
24         UserPasswordDO userPasswordDO = convertPasswordFromModelObject(userModel);
25         userPasswordDOMapper.insertSelective(userPasswordDO);
26     }
27 
28     private UserPasswordDO convertPasswordFromModelObject(UserModel userModel) {
29 
30         if (userModel == null)
31             return null;
32         UserPasswordDO userPasswordDO = new UserPasswordDO();
33         userPasswordDO.setUserId(userModel.getId());
34         userPasswordDO.setEncrptPassword(userModel.getEncrptPassword());
35         return userPasswordDO;
36     }
37 
38     private UserDO convertFromModelObject(UserModel userModel) {
39 
40         if (userModel == null) {
41             return null;
42         }
43         UserDO userDO = new UserDO();
44         BeanUtils.copyProperties(userModel, userDO);
45         return userDO;
46     }

六、UserDOMapper(keyProperty / useGenderateKeys)

<insert id="insertSelective" parameterType="com.miaoshaProject.dataobject.UserDO" keyProperty="id" useGeneratedKeys="true">
    <!--
      WARNING - @mbg.generated
      This element is automatically generated by MyBatis Generator, do not modify.
      This element was generated on Thu Apr 14 10:32:50 CST 2022.
    -->
    insert into user_info
    <trim prefix="(" suffix=")" suffixOverrides=",">
      <if test="id != null">
        id,
      </if>
      <if test="name != null">
        name,
      </if>
      <if test="gender != null">
        gender,
      </if>
      <if test="age != null">
        age,
      </if>
      <if test="telpphone != null">
        telpphone,
      </if>
      <if test="registerMode != null">
        register_mode,
      </if>
      <if test="thirdPartyId != null">
        third_party_id,
      </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides=",">
      <if test="id != null">
        #{id,jdbcType=INTEGER},
      </if>
      <if test="name != null">
        #{name,jdbcType=VARCHAR},
      </if>
      <if test="gender != null">
        #{gender,jdbcType=INTEGER},
      </if>
      <if test="age != null">
        #{age,jdbcType=INTEGER},
      </if>
      <if test="telpphone != null">
        #{telpphone,jdbcType=VARCHAR},
      </if>
      <if test="registerMode != null">
        #{registerMode,jdbcType=VARCHAR},
      </if>
      <if test="thirdPartyId != null">
        #{thirdPartyId,jdbcType=VARCHAR},
      </if>
    </trim>
  </insert>

七、sql

ALTER TABLE `miaosha`.`user_info` 
ADD UNIQUE INDEX `telphone_unique_index`(`telpphone`) USING BTREE COMMENT '给手机号添加唯一索引,确保一个手机号只能注册一个用户';