网上图书商城项目学习笔记-004注册页面前后台的验证、注册功能实现、激活功能实现

一、功能分析

1.注册页面前后台的验证

2.注册功能实现

3.激活功能实现

二、代码实现

1.前端验证

  1 $(function() {
  2     /*
  3      * 1. 得到所有的错误信息,循环遍历之。调用一个方法来确定是否显示错误信息!
  4      */
  5     $(".labelError").each(function() {
  6         showError($(this));//遍历每个元素,使用每个元素来调用showError方法
  7     });
  8     
  9     /*
 10      * 2. 切换注册按钮的图片
 11      */
 12     $("#submitBtn").hover(
 13         function(){$(this).attr("src", "/goods/images/regist2.jpg")},
 14         function(){$(this).attr("src", "/goods/images/regist1.jpg")}
 15     );
 16     
 17     /*
 18      * 3. 输入框得到焦点隐藏错误信息
 19      */
 20     $(".input").focus(function() {
 21         var labelId = $(this).attr("id") + "Error";//通过输入框找到对应的label的id
 22         var domEl = $("#" + labelId);
 23         domEl.text("");//把label的内容清空!
 24         showError(domEl);//隐藏没有信息的label
 25     });
 26     
 27     /*
 28      * 4. 输入框失去焦点进行校验
 29      */
 30     $(".input").blur(function() {
 31         var id = $(this).attr("id");//获取当前输入框的id
 32         var funName = "validate" + id.substring(0,1).toUpperCase() + id.substring(1) + "()";//得到对应的校验函数名
 33         eval(funName);//执行函数调用
 34     });
 35     
 36     /*
 37      * 5. 表单提交时进行校验
 38      */
 39     $("#registForm").submit(function() {
 40         var result = true;
 41         if(!validateLoginname()) result = false;
 42         if(!validateLoginpass()) result = false;
 43         if(!validateReloginpass()) result = false;
 44         if(!validateEmail()) result = false;
 45         if(!validateVerifyCode()) result = false;
 46         return result;
 47     });
 48 })
 49 
 50 /*
 51  * 登录名校验方法
 52  */
 53 function validateLoginname() {
 54     var $id = "#loginname";
 55     var value = $($id).val();//获取输入框内容
 56     var $errorId = $id + "Error";
 57     /*
 58      *  1. 非空校验
 59      */
 60     if(!value) {
 61         /*
 62          * 获取对应的label
 63          * 添加错误信息
 64          * 显示label
 65          */
 66         processError($errorId, "用户名不能为空!");
 67         return false;
 68     }
 69     /*
 70      * 2. 长度校验
 71      */
 72     if(value.length < 3 || value.length > 20) {
 73         /*
 74          * 获取对应的label
 75          * 添加错误信息
 76          * 显示label
 77          */
 78         processError($errorId, "用户名长度必须在3 ~ 20之间!");
 79         return false;
 80     }
 81     /*
 82      * 3. 是否注册校验
 83      */
 84     $.ajax({
 85         url:"/goods/UserServlet",
 86         data:{method : "validateLoginname", loginname:value},
 87         type:"POST",
 88         dataType:"json",
 89         asycn:false,//是否异步请求,如果是异步,那么不会等服务器返回,我们这个函数就向下运行了。
 90         cache:false,
 91         success:function(result) {
 92             if(!result) {
 93                 processError($errorId, "用户名已被注册!");
 94                 return false;
 95             }
 96         }
 97     });
 98     return true;
 99 }
100 
101 /*
102  * 登录密码校验方法
103  */
104 function validateLoginpass() {
105     var $id = "#loginpass";
106     var $errorId = $id + "Error";
107     var value = $($id).val();
108     if(!value) {
109         processError($errorId, "密码不能为空!");
110         return false;
111     }
112     if(value.length < 3 || value.length > 20) {
113         processError($errorId, "密码长度必须在3 ~ 20之间!");
114         return false;
115     }
116     return true;
117 }
118 
119 /*
120  * 确认密码校验方法
121  */
122 function validateReloginpass() {
123     var $id = "#reloginpass";
124     var $errorId = $id + "Error";
125     var value = $($id).val();
126     if(!value) {
127         processError($errorId, "确认密码不能为空!");
128         return false;
129     }
130     if(value != $("#loginpass").val()){
131         processError($errorId, "两次输入不一致!");
132         return false;
133     }
134     return true;
135 }
136 
137 /*
138  * 判断当前元素是否存在内容,如果存在显示,不页面不显示!
139  */
140 function showError(ele) {
141     var text = ele.text();//获取元素的内容
142     if(!text) {//如果没有内容
143         ele.css("display", "none");//隐藏元素
144     } else {//如果有内容
145         ele.css("display", "");//显示元素
146     }
147 }
148 
149 /**
150  * Email校验方法
151  */
152 function validateEmail() {
153     var $id = "#email";
154     var $errorId = $id + "Error";
155     var value = $($id).val();
156     if(!value){
157         processError($errorId, "Email不能为空!");
158         return false;
159     }
160     //Email格式校验
161     if(!/^([a-zA-Z0-9_-])+@([a-zA-Z-9_-])+((.[a-zA-Z0-9_-]{2,3}){1,2})$/.test(value)) {
162         processError($errorId, "错误的Email格式!");
163         return false;
164     }
165     $.ajax({
166         url     : "/goods/UserServlet",
167         data    : {method : "validateEmail", email : value},
168         type    : "post",
169         dataType: "json",
170         async   : false,
171         cache   : false,
172         success : function(result) {
173             if(!result) {
174                 processError($errorId, "Email已被注册!");
175                 return false;
176             }
177         }
178     });
179     return true;
180 }
181 
182 /*
183  * 验证码校验方法
184  */
185 function validateVerifyCode() {
186     var $id = "#verifyCode";
187     var $errorId = $id + "Error";
188     var value = $($id).val();
189     if(!value) {
190         processError($errorId, "验证码不能为空!");
191         return false;
192     }
193     
194     //长度验证
195     if(value.length != 4) {
196         processError($errorId, "错误的验证码!");
197         return false;
198     }
199     
200     //是否正确
201     $.ajax({
202         url : "/goods/UserServlet",
203         data : {method : "validateVerifyCode", verifyCode : value},
204         type : "POST",
205         dataType : "json",
206         asyc : false,
207         cache : false,
208         success : function(result) {
209             if(!result) {
210                 processError($errorId, "验证码错误!");
211                 return false;
212             }
213         }
214     });
215     return true;
216 }
217 
218 /*
219  * 换一张验证码
220  */
221 function _changeVerifyCode() {
222     /*
223      * 1. 获取<img>元素
224      * 2. 重新设置它的src
225      * 3. 使用毫秒来添加参数
226      */
227     $("#vCode").attr("src", "/goods/VerifyCodeServlet?'/>" + new Date().getTime());
228 }
229 
230 /*
231  * 校验完后调用此方法进行错误信息处理
232  */
233 function processError($errorId, msg) {
234     var errorEl = $($errorId);
235     errorEl.text(msg);
236     showError(errorEl);
237     return;
238 }

2.后台验证

(1)servlet层

  a).UserServlet.java

  1 package com.tony.goods.user.web.servlet;
  2 
  3 import java.io.IOException;
  4 import java.util.HashMap;
  5 import java.util.Map;
  6 
  7 import javax.servlet.http.HttpServletRequest;
  8 import javax.servlet.http.HttpServletResponse;
  9 import javax.servlet.http.HttpSession;
 10 
 11 import cn.itcast.commons.CommonUtils;
 12 import cn.itcast.servlet.BaseServlet;
 13 
 14 import com.tony.goods.user.domain.User;
 15 import com.tony.goods.user.service.UserService;
 16 import com.tony.goods.user.service.exception.UserException;
 17 
 18 /**
 19  * 用户模块WEB层
 20  * @author T
 21  *
 22  */
 23 public class UserServlet extends BaseServlet {
 24 
 25     private UserService userService = new UserService();
 26 
 27     /**
 28      * 用户名是否注册校验
 29      * @param request
 30      * @param response
 31      * @return
 32      * @throws IOException
 33      */
 34     public String validateLoginname(HttpServletRequest request, HttpServletResponse response) throws IOException {
 35         String loginname = request.getParameter("loginname");
 36         boolean result = userService.validateLoginname(loginname);
 37         response.getWriter().print(result);
 38         return null;
 39     }
 40     
 41     /**
 42      * Email是否注册校验
 43      * @param request
 44      * @param response
 45      * @return
 46      * @throws IOException
 47      */
 48     public String validateEmail(HttpServletRequest request, HttpServletResponse response) throws IOException {
 49         String email = request.getParameter("email");
 50         boolean result = userService.validateEmail(email);
 51         response.getWriter().print(result);
 52         return null;
 53     }
 54     
 55     /**
 56      * Email是否注册校验
 57      * @param request
 58      * @param response
 59      * @return
 60      * @throws IOException
 61      */
 62     public String validateVerifyCode(HttpServletRequest request, HttpServletResponse response) throws IOException {
 63         //1. 获取输入框中的验证码
 64         String verifyCode = request.getParameter("verifyCode");
 65         //2. 获取图片上真实的校验码
 66         String vCode = (String) request.getSession().getAttribute("vCode");
 67         //3. 进行忽略大小写比较,得到结果
 68         boolean result = verifyCode.equalsIgnoreCase(vCode);
 69         response.getWriter().print(result);
 70         return null;
 71     }
 72     
 73     public String regist(HttpServletRequest request, HttpServletResponse response) {
 74         //1. 封装表单数据到User对象
 75         User formUser = CommonUtils.toBean(request.getParameterMap(), User.class);
 76         
 77         //2. 校验之, 如果校验失败,保存错误信息,返回到regist.jsp显示
 78         Map<String, String> errors = validateRegist(formUser, request.getSession());
 79         if(errors.size() > 0) {
 80             request.setAttribute("form", formUser);
 81             request.setAttribute("errors", errors);
 82             return "f:/jsps/user/regist.jsp";
 83         }
 84         //3. 使用service完成业务,会 发激活链接到邮箱
 85         userService.regist(formUser);
 86         
 87         //4. 保存成功信息,转发到msg.jsp显示!
 88         request.setAttribute("code", "success");
 89         request.setAttribute("msg", "注册成功,请马上到邮箱激活!");
 90         return "f:/jsps/msg.jsp";
 91     }
 92 
 93     /**
 94      * 注册校验
 95      * 对表单的字段进行逐个校验,如果有错误,使用当前字段名称为key,错误信息为value,保存到map中
 96      * 返回map
 97      */
 98     private Map<String, String> validateRegist(User formUser, HttpSession session) {
 99         Map<String, String> errors = new HashMap<String, String>();
100         // 1. 校验登录名
101         String loginname = formUser.getLoginname();
102         if(loginname == null || loginname.trim().isEmpty()) {
103             errors.put("loginname", "用户名不能为空!");
104         } else if(loginname.length() < 3 || loginname.length() > 20) {
105             errors.put("loginname", "用户名长度必须在3~20之间!");
106         } else if(!userService.validateLoginname(loginname)) {
107             errors.put("loginname", "用户名已被注册!");
108         }
109         
110         // 2. 校验登录密码
111         String loginpass = formUser.getLoginpass();
112         if(loginpass == null || loginpass.trim().isEmpty()) 
113             errors.put("loginpass", "密码不能为空!");
114         else if(loginpass.length() < 3 || loginpass.length() > 20)
115             errors.put("loginpass", "密码长度必须在3~20之间!");
116         
117         // 3. 确认密码校验
118         String reloginpass = formUser.getReloginpass();
119         if(reloginpass == null || reloginpass.trim().isEmpty()) {
120             errors.put("reloginpass", "确认密码不能为空!");
121         } else if(!reloginpass.equals(loginpass)) {
122             errors.put("reloginpass", "两次输入不一致!");
123         }
124         
125         // 4. 校验email
126         String email = formUser.getEmail();
127         if(email == null || email.trim().isEmpty()) {
128             errors.put("email", "Email不能为空!");
129         } else if(!email.matches("^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+((\.[a-zA-Z0-9_-]{2,3}){1,2})$")) {
130             errors.put("email", "Email格式错误!");
131         } else if(!userService.validateEmail(email)) {
132             errors.put("email", "Email已被注册!");
133         }
134         
135         // 5. 验证码校验
136         String verifyCode = formUser.getVerifyCode();
137         String vCode = (String) session.getAttribute("vCode");
138         if(verifyCode == null || verifyCode.trim().isEmpty()) {
139             errors.put("verifyCode", "验证码不能为空!");
140         } else if(!verifyCode.equalsIgnoreCase(vCode)) {
141             errors.put("verifyCode", "验证码错误!");
142         }
143         
144         return errors;
145     }
146     
147     /**
148      * 激活功能
149      * @param request
150      * @param response
151      * @return
152      */
153     public String activation(HttpServletRequest request, HttpServletResponse response) {
154         /*
155          * 1. 获取参数激活码
156          * 2. 用激活码调用service方法完成激活
157          *   > service方法有可能抛出异常, 把异常信息拿来,保存到request中,转发到msg.jsp显示
158          * 3. 保存成功信息到request,转发到msg.jsp显示。
159          */
160         String code = request.getParameter("activationCode");
161         try {
162             userService.activate(code);
163             request.setAttribute("code", "success");//通知msg.jsp显示对号
164             request.setAttribute("msg", "恭喜,激活成功,请马上登录!");
165         } catch (UserException e) {
166             // 说明service抛出了异常
167             request.setAttribute("msg", e.getMessage());
168             request.setAttribute("code", "error");//通知msg.jsp显示X
169         }
170         return "f:/jsps/msg.jsp";
171     }
172 }

  b).BaseServlet.java

 1 package cn.itcast.servlet;
 2 
 3 import java.io.IOException;
 4 import java.lang.reflect.Method;
 5 
 6 import javax.servlet.ServletException;
 7 import javax.servlet.http.HttpServlet;
 8 import javax.servlet.http.HttpServletRequest;
 9 import javax.servlet.http.HttpServletResponse;
10 
11 /**
12  * BaseServlet用来作为其它Servlet的父类
13  * 
14  * @author qdmmy6
15  * 
16  *         一个类多个请求处理方法,每个请求处理方法的原型与service相同! 原型 = 返回值类型 + 方法名称 + 参数列表
17  */
18 @SuppressWarnings("serial")
19 public class BaseServlet extends HttpServlet {
20     @Override
21     public void service(HttpServletRequest request, HttpServletResponse response)
22             throws ServletException, IOException {
23         response.setContentType("text/html;charset=UTF-8");//处理响应编码
24         
25         /**
26          * 1. 获取method参数,它是用户想调用的方法 2. 把方法名称变成Method类的实例对象 3. 通过invoke()来调用这个方法
27          */
28         String methodName = request.getParameter("method");
29         Method method = null;
30         /**
31          * 2. 通过方法名称获取Method对象
32          */
33         try {
34             method = this.getClass().getMethod(methodName,
35                     HttpServletRequest.class, HttpServletResponse.class);
36         } catch (Exception e) {
37             throw new RuntimeException("您要调用的方法:" + methodName + "它不存在!", e);
38         }
39         
40         /**
41          * 3. 通过method对象来调用它
42          */
43         try {
44             String result = (String)method.invoke(this, request, response);
45             if(result != null && !result.trim().isEmpty()) {//如果请求处理方法返回不为空
46                 int index = result.indexOf(":");//获取第一个冒号的位置
47                 if(index == -1) {//如果没有冒号,使用转发
48                     request.getRequestDispatcher(result).forward(request, response);
49                 } else {//如果存在冒号
50                     String start = result.substring(0, index);//分割出前缀
51                     String path = result.substring(index + 1);//分割出路径
52                     if(start.equals("f")) {//前缀为f表示转发
53                         request.getRequestDispatcher(path).forward(request, response);
54                     } else if(start.equals("r")) {//前缀为r表示重定向
55                         response.sendRedirect(request.getContextPath() + path);
56                     }
57                 }
58             }
59         } catch (Exception e) {
60             throw new RuntimeException(e);
61         }
62     }
63 }

(2)service层

  a).UserService.java

  1 package com.tony.goods.user.service;
  2 
  3 import java.io.IOException;
  4 import java.sql.SQLException;
  5 import java.text.MessageFormat;
  6 import java.util.Properties;
  7 
  8 import javax.mail.MessagingException;
  9 import javax.mail.Session;
 10 
 11 import cn.itcast.commons.CommonUtils;
 12 import cn.itcast.mail.Mail;
 13 import cn.itcast.mail.MailUtils;
 14 
 15 import com.tony.goods.user.dao.UserDao;
 16 import com.tony.goods.user.domain.User;
 17 import com.tony.goods.user.service.exception.UserException;
 18 
 19 /**
 20  * 用户模块业务层
 21  * @author T
 22  *
 23  */
 24 public class UserService {
 25     private UserDao userDao = new UserDao();
 26     
 27     /**
 28      * 用户名注册校验
 29      * @param loginname
 30      * @return
 31      */
 32     public boolean validateLoginname(String loginname) {
 33         try {
 34             return userDao.validateLoginname(loginname);
 35         } catch (SQLException e) {
 36             throw new RuntimeException(e);
 37         }
 38     }
 39     
 40     /**
 41      * Email校验
 42      * @param email
 43      * @return
 44      */
 45     public boolean validateEmail(String email) {
 46         try {
 47             return userDao.validateEmail(email);
 48         } catch (SQLException e) {
 49             throw new RuntimeException(e);
 50         }
 51     }
 52     
 53     public void regist(User user) {
 54         //1. 数据的补齐
 55         user.setUid(CommonUtils.uuid());
 56         user.setStatus(false);
 57         user.setActivationCode(CommonUtils.uuid() + CommonUtils.uuid());
 58         
 59         //2. 向数据库插入
 60         try {
 61             userDao.add(user);
 62         } catch (SQLException e) {
 63             throw new RuntimeException(e);
 64         }
 65         
 66         //3. 发邮件
 67         //3.1 把配置文件内容加载到prop中
 68         Properties prop = new Properties();
 69         try {
 70             prop.load(this.getClass().getClassLoader().getResourceAsStream("email_template.properties"));
 71         } catch (IOException e) {
 72             throw new RuntimeException(e);
 73         }
 74         //3.2  登录邮件服务器,得到session
 75         String host = prop.getProperty("host");
 76         String username = prop.getProperty("username");
 77         String password = prop.getProperty("password");
 78         Session session = MailUtils.createSession(host, username, password);
 79         
 80         //3.3 创建Mail对象
 81         String from = prop.getProperty("from");
 82         String to = user.getEmail();
 83         String subject = prop.getProperty("subject");
 84         // MessageForm.format方法会把第一个参数中的{0},使用第二个参数来替换。
 85         // 例如MessageFormat.format("你好{0}, 你{1}!", "张三", "去死吧"); 返回“你好张三,你去死吧!”
 86         String content = MessageFormat.format(prop.getProperty("content"), user.getActivationCode());
 87         Mail mail = new Mail(from, to ,subject, content);
 88         
 89         //3.4 发送邮件
 90         try {
 91             MailUtils.send(session, mail);
 92         } catch (MessagingException e) {
 93             throw new RuntimeException(e);
 94         } catch (IOException e) {
 95             throw new RuntimeException(e);
 96         }
 97     }
 98 
 99     /**
100      * 激活功能
101      * @param code
102      * @throws UserException
103      */
104     public void activate(String code) throws UserException {
105         /*
106          * 1. 通过激活码查询用户
107          * 2. 如果User为null,说明是无效激活码,抛出异常,给出异常信息(无效激活码)
108          * 3. 查看用户状态是否为true,如果为true,抛出异常,给出异常信息(请不要二次激活)
109          * 4. 修改用户状态为true
110          */
111         try {
112             User user = userDao.findByCode(code);
113             if(user == null) throw new UserException("无效的激活码!"); 
114             if(user.isStatus()) throw new UserException("您已经激活过了,不要二次激活!");
115             userDao.updateStatus(user.getUid(), true);//修改状态
116         } catch (SQLException e) {
117             throw new RuntimeException(e);
118         }
119     }
120 }

(3)dao层

  a).UserDao.java

 1 package com.tony.goods.user.dao;
 2 
 3 import java.sql.SQLException;
 4 
 5 import org.apache.commons.dbutils.QueryRunner;
 6 import org.apache.commons.dbutils.handlers.BeanHandler;
 7 import org.apache.commons.dbutils.handlers.ScalarHandler;
 8 
 9 import cn.itcast.jdbc.TxQueryRunner;
10 
11 import com.tony.goods.user.domain.User;
12 
13 /**
14  * 用户模块持久层
15  * @author T
16  *
17  */
18 public class UserDao {
19     private QueryRunner qr = new TxQueryRunner();
20     
21     /**
22      * 通过激活码查询用户
23      * @param code
24      * @return
25      * @throws SQLException
26      */
27     public User findByCode(String code) throws SQLException {
28         String sql = "select * from t_user where activationCode=?";
29         return qr.query(sql, new BeanHandler<User>(User.class), code);
30     }
31     
32     /**
33      * 修改用户状态
34      * @param uid
35      * @param b
36      * @throws SQLException 
37      */
38     public void updateStatus(String uid, boolean status) throws SQLException {
39         String sql = "update t_user set status=? where uid=?";
40         qr.update(sql, status, uid);
41     }
42     
43     /**
44      * 校验用户名是否注册
45      * @param loginname
46      * @return
47      * @throws SQLException
48      */
49     public boolean validateLoginname(String loginname) throws SQLException {
50         String sql = "select count(1) from t_user where loginname=?";
51         Number number = (Number) qr.query(sql, new ScalarHandler(), loginname);
52         return number.intValue() == 0;
53     }
54     
55     /**
56      * 校验Email是否注册
57      * @param email
58      * @return
59      * @throws SQLException
60      */
61     public boolean validateEmail(String email) throws SQLException {
62         String sql = "select count(1) from t_user where email=?";
63         Number number = (Number) qr.query(sql, new ScalarHandler(), email);
64         return number.intValue() == 0;
65     }
66     
67     /**
68      * 添加用户
69      * @param user
70      * @throws SQLException
71      */
72     public void add(User user) throws SQLException {
73         String sql = "insert into t_user values(?,?,?,?,?,?)";
74         Object[] params = {user.getUid(), user.getLoginname(), user.getLoginpass(), user.getEmail(), user.isStatus(), user.getActivationCode()};
75         qr.update(sql, params);
76     }
77 
78 
79 }

(4)view层

  a)regist.jsp

3.配置文件

(1)web.xml

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
 3   
 4   <!-- 统一编码处理 -->
 5   <filter>
 6       <filter-name>EncodingFilter</filter-name>
 7       <filter-class>cn.itcast.filter.EncodingFilter</filter-class>
 8   </filter>
 9   <filter-mapping>
10       <filter-name>EncodingFilter</filter-name>
11       <url-pattern>/*</url-pattern>
12   </filter-mapping>
13   
14   <!-- 生成验证码 -->
15   <servlet>
16       <servlet-name>VerifyCodeServlet</servlet-name>
17       <servlet-class>cn.itcast.vcode.servlet.VerifyCodeServlet</servlet-class>
18   </servlet>
19   <servlet-mapping>
20       <servlet-name>VerifyCodeServlet</servlet-name>
21       <url-pattern>/VerifyCodeServlet</url-pattern>
22   </servlet-mapping>
23   
24   <!-- 用户模块的注册校验(eg:用户名是 否已注册) -->
25   <servlet>
26       <servlet-name>UserServlet</servlet-name>
27       <servlet-class>com.tony.goods.user.web.servlet.UserServlet</servlet-class>
28   </servlet>
29   <servlet-mapping>
30       <servlet-name>UserServlet</servlet-name>
31       <url-pattern>/UserServlet</url-pattern>
32   </servlet-mapping>
33 </web-app>

(2)email.properties

1 subject=u6765u81EAIu65B0u6708u4E66u5E97u7684u6FC0u6D3Bu90AEu4EF6
2 content=u606Du559CuFF0Cu60A8u5DF2u6CE8u518Cu6210u529FuFF0Cu8BF7u70B9u51FB<a href="http://localhost:8080/goods/UserServlet?method=activation&activationCode={0}">u8FD9u91CC</a>u5B8Cu6210u6FC0u6D3Bu3002
3 from=aaa@163.com
4 host=smtp.163.com
5 username=bbb
6 password=xxx
原文地址:https://www.cnblogs.com/shamgod/p/5156350.html