思路汇总

①注册功能:

首先在主页index,点击注册:<a class="nav-link" th:href="@{/register}">注册</a>

通过控制器方法getRegisterPage(),跳转到register.html界面。

输入账号密码邮箱后,点击立即注册。form表单跳转到th:action="@{/register}"。由于此次发送的是POST请求,执行register()控制器方法。该方法调用了userService.register(user),将user进行注册。

userService.register(user)会进行注册参数的判断:包括参数非空、账号邮箱已存在,并将错误信息存储到Map中。如果都没问题,则对user的其他属性进行复制,例如设置类型、状态、默认头像、自动生成激活码等,从而得到一个完整的user。然后发送激活邮件,context包含了用户邮件地址、激活路径(包含用户ID、激活码):

激活路径格式:http://localhost:8080/community/activation/101/code

并将html文件与context封装到content中,之后调用mailClient.sendMail(邮箱、主题、content),发送html邮件:

String content = templateEngine.process("/mail/activation",context);

到此为止,用户已经注册成功,控制器方法register()还没执行完。继续对userService.register(user)返回的Map进行判断,跳转到设定的界面,并展示相应的信息。

②激活功能:

用户邮箱中会展现activation.html中的内容,里面有个点击此链接激活账号的功能。这个链接就是上一段中【包含用户ID、激活码的激活路径】,我们点击后,会通过控制器方法@RequestMapping(path = "/activation/{userId}/{code}", method = RequestMethod.GET)

activation(){...} 来判断链接中的激活码和user设定的激活码是否相同,并进行各种判断,封装信息、下一步的访问路径。最后返回"/site/operate-result"页面,这是个中转页面,上面会显示上一步判断的信息,并在8秒后跳回上一步设定的路径。

1. 注册页面

首先搞个注册页面:

<!doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"><link rel="icon" href="https://static.nowcoder.com/images/logo_87_87.png"/><link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" crossorigin="anonymous"><link rel="stylesheet" th:href="@{/css/global.css}" /><link rel="stylesheet" th:href="@{/css/login.css}" /><title>牛客网-注册</title>
</head>
<body><div class="nk-container"><!-- 头部 --><!--用index中的header替换掉此内容--><header class="bg-dark sticky-top" th:replace="index::header"><div class="container"><!-- 导航 --><nav class="navbar navbar-expand-lg navbar-dark"><!-- logo --><a class="navbar-brand" href="#"></a><button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button><!-- 功能 --><div class="collapse navbar-collapse" id="navbarSupportedContent"><ul class="navbar-nav mr-auto"><li class="nav-item ml-3 btn-group-vertical"><a class="nav-link" th:href="@{/index}">首页</a></li><li class="nav-item ml-3 btn-group-vertical"><a class="nav-link position-relative" href="letter.html">消息<span class="badge badge-danger">12</span></a></li><li class="nav-item ml-3 btn-group-vertical"><a class="nav-link" href="@{/register}">注册</a></li><li class="nav-item ml-3 btn-group-vertical"><a class="nav-link" href="login.html">登录</a></li><li class="nav-item ml-3 btn-group-vertical dropdown"><a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><img src="http://images.nowcoder.com/head/1t.png" class="rounded-circle" style="width:30px;"/></a><div class="dropdown-menu" aria-labelledby="navbarDropdown"><a class="dropdown-item text-center" href="profile.html">个人主页</a><a class="dropdown-item text-center" href="setting.html">账号设置</a><a class="dropdown-item text-center" href="login.html">退出登录</a><div class="dropdown-divider"></div><span class="dropdown-item text-center text-secondary">nowcoder</span></div></li></ul><!-- 搜索 --><form class="form-inline my-2 my-lg-0" action="search.html"><input class="form-control mr-sm-2" type="search" aria-label="Search" /><button class="btn btn-outline-light my-2 my-sm-0" type="submit">搜索</button></form></div></nav></div></header><!-- 内容 --><div class="main"><div class="container pl-5 pr-5 pt-3 pb-3 mt-3 mb-3"><h3 class="text-center text-info border-bottom pb-3">注&nbsp;&nbsp;册</h3><form class="mt-5" method="post" th:action="@{/register}"><div class="form-group row"><label for="username" class="col-sm-2 col-form-label text-right">账号:</label><div class="col-sm-10"><!--如果usernameMsg不为空,就在input标签中添加 th:class='is-invalid' --><!--设定默认值,在报错返回来以后,model仍存有刚才的user对象,这样用户就不要再输一遍了--><!--标签的name属性,需要与user的属性名相同--><input type="text"th:class="|form-control ${usernameMsg!=null?'is-invalid':''}|"th:value="${user!=null?user.username:''}"id="username" name="username" placeholder="请输入您的账号!" required><!--账号的错误就取出map中存的账号错误信息--><div class="invalid-feedback" th:text="${usernameMsg}">该账号已存在!</div></div></div><div class="form-group row mt-4"><label for="password" class="col-sm-2 col-form-label text-right">密码:</label><div class="col-sm-10"><input type="password"th:class="|form-control ${passwordMsg!=null?'is-invalid':''}|"th:value="${user!=null?user.password:''}"id="password" name="password" placeholder="请输入您的密码!" required><div class="invalid-feedback" th:text="${passwordMsg}">密码长度不能小于8位!</div>                            </div></div><div class="form-group row mt-4"><label for="confirm-password" class="col-sm-2 col-form-label text-right">确认密码:</label><div class="col-sm-10"><input type="password" class="form-control"th:value="${user!=null?user.password:''}"id="confirm-password" placeholder="请再次输入密码!" required><div class="invalid-feedback">两次输入的密码不一致!</div></div></div><div class="form-group row"><label for="email" class="col-sm-2 col-form-label text-right">邮箱:</label><div class="col-sm-10"><input type="email"th:class="|form-control ${emailMsg!=null?'is-invalid':''}|"th:value="${user!=null?user.email:''}"id="email" name="email" placeholder="请输入您的邮箱!" required>
<!--                         <div class="invalid-feedback" th:text="${emailMsg}">-->
<!--                             该邮箱已注册!-->
<!--                         </div>--></div></div><div class="form-group row mt-4"><div class="col-sm-2"></div><div class="col-sm-10 text-center"><button type="submit" class="btn btn-info text-white form-control">立即注册</button></div></div></form>              </div></div><!-- 尾部 --><!-- 代码太长,我先删掉 --></div><script src="https://code.jquery.com/jquery-3.3.1.min.js" crossorigin="anonymous"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" crossorigin="anonymous"></script><script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" crossorigin="anonymous"></script><script th:src="@{/js/global.js}"></script><script th:src="@{/js/register.js}"></script>
</body>
</html>

注:这里的<header class="bg-dark sticky-top" th:replace="index::header">

利用了主页面index中的header替换掉此内容,为复用。需要在index的header上添加:

<header class="bg-dark sticky-top" th:fragment="header">

2. 创建工具类

mycommunity/util下新建工具类:CommunityUtil

目前主要包含两个方法:

- 为密码随机生成一个字符串后缀,用来加强密码;

- 将密码进行MD5加密。

public class CommunityUtil {//生成随机字符串public static String generateUUID(){return UUID.randomUUID().toString().replace("-","");}//MD5加密//hello + 3e4r8 --> abc13huoad34public static String md5(String key){if(StringUtils.isBlank(key)){return null;}return DigestUtils.md5DigestAsHex(key.getBytes());}
}

3. 提交注册数据

由于注册是针对用户表的数据,因此注册逻辑写在UserService中。

自动注入了MailClient、TemplateEngine、domain、contextPath(后两个为配置文件中的数据)。

package com.nowcoder.mycommunity.service;import com.nowcoder.mycommunity.dao.UserMapper;
import com.nowcoder.mycommunity.entity.User;
import com.nowcoder.mycommunity.util.CommunityConstant;
import com.nowcoder.mycommunity.util.CommunityUtil;
import com.nowcoder.mycommunity.util.MailClient;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;@Service
public class UserService implements CommunityConstant {@Autowiredprivate UserMapper userMapper;@Autowiredprivate MailClient mailClient;@Autowiredprivate TemplateEngine templateEngine;//以下值从配置文件中读取@Value("${mycommunity.path.domin}")private String domain;@Value("${server.servlet.context-path}")private String contextPath;public User findUserById(int id){return userMapper.selectById(id);}// 由于注册是针对用户表的数据,因此注册逻辑写在UserService中public Map<String,Object> register(User user){Map<String,Object> map = new HashMap<>();//空值处理if (user == null){throw new IllegalArgumentException("参数不能为空!");}if (StringUtils.isBlank(user.getUsername())){map.put("usernameMsg","账号不能为空!");return map;}if (StringUtils.isBlank(user.getPassword())){map.put("passwordMsg","密码不能为空!");return map;}if (StringUtils.isBlank(user.getEmail())){map.put("emailMsg","邮箱不能为空!");return map;}//验证账号:看是否已存在User u = userMapper.selectByName(user.getUsername());if(u != null){map.put("usernameMsg", "该账号已存在!");return map;}//验证邮箱//u = userMapper.selectByEmail(user.getEmail());//if(u != null){//    map.put("emailMsg", "该邮箱已被注册!");//    return map;//}//注册//生成随机字符串,添加到密码上user.setSalt(CommunityUtil.generateUUID().substring(0,5));user.setPassword(CommunityUtil.md5(user.getPassword() + user.getSalt()));user.setType(0);user.setStatus(0);user.setActivationCode(CommunityUtil.generateUUID());//设置头像user.setHeaderUrl(String.format("http://images.nowcoder.com/head/%dt.png",new Random().nextInt(1000)));user.setCreateTime(new Date());userMapper.insertUser(user);//发送激活邮件Context context = new Context();context.setVariable("email", user.getEmail());//激活路径格式:http://localhost:8080/community/activation/101/code//注:用户ID为主键自动生成String url = domain + contextPath + "/activation/" + user.getId() + "/" + user.getActivationCode();context.setVariable("url",url);String content = templateEngine.process("/mail/activation",context);mailClient.sendMail(user.getEmail(), "激活账号", content);//最后返回的map为空,则说明没有问题return map;}//激活//需要传进用户ID,得到该用户的激活码,与传进来的激活码进行对比public int activation(int userId, String code){User user = userMapper.selectById(userId);//如果可以查询到该用户ID,说明已经有了,重复注册if(user.getStatus() == 1){return ACTIVATION_REPEAT;} else if(user.getActivationCode().equals(code)){//激活码正确,则将用户的状态设为1userMapper.updateStatus(userId,1);return ACTIVATION_SUCCESS;} else {return ACTIVATION_FAILURE;}}
}

4. 创建控制器

在LoginController中编写注册相应的控制器方法。

package com.nowcoder.mycommunity.controller;import com.nowcoder.mycommunity.dao.UserMapper;
import com.nowcoder.mycommunity.entity.User;
import com.nowcoder.mycommunity.service.UserService;
import com.nowcoder.mycommunity.util.CommunityConstant;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;import java.util.Map;@Controller
public class LoginController implements CommunityConstant {@Autowiredprivate UserService userService;//登录页面@RequestMapping(path = "/login", method = RequestMethod.GET)public String getLoginPage(){System.out.println("lalala");return "/site/login";}//注册页面@RequestMapping(path = "/register", method = RequestMethod.GET)public String getRegisterPage(){return "/site/register";}@RequestMapping(path="/register",method = RequestMethod.POST)public String register(Model model, User user){Map<String, Object> map = userService.register(user);//map为空,说明注册成功了if(map == null || map.isEmpty()){model.addAttribute("msg","注册成功,我们已经向您的邮箱发送了一封激活邮件,请尽快激活!");model.addAttribute("target","/index");//这里并未通过控制器,而是直接访问了静态资源return "/site/operate-result";} else{model.addAttribute("usernameMsg",map.get("usernameMsg"));model.addAttribute("passwordMsg",map.get("passwordMsg"));//model.addAttribute("emailMsg",map.get("emailMsg"));return "/site/register";}}@RequestMapping(path = "/activation/{userId}/{code}", method = RequestMethod.GET)public String activation(Model model,@PathVariable("userId") int userId,@PathVariable("code") String code){System.out.println("我进来了!");int result = userService.activation(userId, code);if(result == ACTIVATION_SUCCESS){model.addAttribute("msg","激活成功!您的账号可以正常使用了!");model.addAttribute("target","/login");} else if (result == ACTIVATION_REPEAT){model.addAttribute("msg","无效操作,该账号已经激活过了!");model.addAttribute("target","/index");} else {model.addAttribute("msg","激活失败,请提供正确的激活码!");model.addAttribute("target","/index");}return "/site/operate-result";}
}

这里还创建了一个常量接口,用于存放常量:

util目录下,新建接口:CommunityConstant

//常量接口
public interface CommunityConstant {//激活成功int ACTIVATION_SUCCESS = 0;//重复激活int ACTIVATION_REPEAT = 1;//激活失败int ACTIVATION_FAILURE = 2;
}

5. 其他html

activation.html:

<!doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="utf-8"><link rel="icon" href="https://static.nowcoder.com/images/logo_87_87.png"/><title>牛客网-激活账号</title>
</head>
<body><div><p><b th:text="${email}">xxx@xxx.com</b>, 您好!</p><p>您正在注册牛客网, 这是一封激活邮件, 请点击 <a th:href="${url}">此链接</a>,激活您的牛客账号!</p></div>
</body>
</html>

operate-result.html

<!doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"><link rel="icon" href="https://static.nowcoder.com/images/logo_87_87.png"/><link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" crossorigin="anonymous"><link rel="stylesheet" th:href="@{/css/global.css}" /><title>牛客网-操作结果</title>
</head>
<body class="bg-white"><div class="nk-container"><!-- 头部 --><header class="bg-dark sticky-top" th:replace="index::header"><div class="container"><!-- 导航 --><nav class="navbar navbar-expand-lg navbar-dark"><!-- logo --><a class="navbar-brand" href="#"></a><button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button><!-- 功能 --><div class="collapse navbar-collapse" id="navbarSupportedContent"><ul class="navbar-nav mr-auto"><li class="nav-item ml-3 btn-group-vertical"><a class="nav-link" href="../index.html">首页</a></li><li class="nav-item ml-3 btn-group-vertical"><a class="nav-link position-relative" href="letter.html">消息<span class="badge badge-danger">12</span></a></li><li class="nav-item ml-3 btn-group-vertical"><a class="nav-link" href="register.html">注册</a></li><li class="nav-item ml-3 btn-group-vertical"><a class="nav-link" href="login.html">登录</a></li><li class="nav-item ml-3 btn-group-vertical dropdown"><a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><img src="http://images.nowcoder.com/head/1t.png" class="rounded-circle" style="width:30px;"/></a><div class="dropdown-menu" aria-labelledby="navbarDropdown"><a class="dropdown-item text-center" href="profile.html">个人主页</a><a class="dropdown-item text-center" href="setting.html">账号设置</a><a class="dropdown-item text-center" href="login.html">退出登录</a><div class="dropdown-divider"></div><span class="dropdown-item text-center text-secondary">nowcoder</span></div></li></ul><!-- 搜索 --><form class="form-inline my-2 my-lg-0" action="search.html"><input class="form-control mr-sm-2" type="search" aria-label="Search" /><button class="btn btn-outline-light my-2 my-sm-0" type="submit">搜索</button></form></div></nav></div></header><!-- 内容 --><div class="main"><div class="container mt-5"><div class="jumbotron"><p class="lead" th:text="${msg}">您的账号已经激活成功,可以正常使用了!</p><hr class="my-4"><p>系统会在 <span id="seconds" class="text-danger">8</span> 秒后自动跳转,您也可以点此 <a id="target" th:href="@{${target}}" class="text-primary">链接</a>, 手动跳转!</p></div></div></div><!-- 尾部 --><footer class="bg-dark"><div class="container"><div class="row"><!-- 二维码 --><div class="col-4 qrcode"><img src="https://uploadfiles.nowcoder.com/app/app_download.png" class="img-thumbnail" style="width:136px;" /></div><!-- 公司信息 --><div class="col-8 detail-info"><div class="row"><div class="col"><ul class="nav"><li class="nav-item"><a class="nav-link text-light" href="#">关于我们</a></li><li class="nav-item"><a class="nav-link text-light" href="#">加入我们</a></li><li class="nav-item"><a class="nav-link text-light" href="#">意见反馈</a></li><li class="nav-item"><a class="nav-link text-light" href="#">企业服务</a></li><li class="nav-item"><a class="nav-link text-light" href="#">联系我们</a></li><li class="nav-item"><a class="nav-link text-light" href="#">免责声明</a></li><li class="nav-item"><a class="nav-link text-light" href="#">友情链接</a></li></ul></div></div><div class="row"><div class="col"><ul class="nav btn-group-vertical company-info"><li class="nav-item text-white-50">公司地址:北京市朝阳区大屯路东金泉时代3-2708北京牛客科技有限公司</li><li class="nav-item text-white-50">联系方式:010-60728802(电话)&nbsp;&nbsp;&nbsp;&nbsp;admin@nowcoder.com</li><li class="nav-item text-white-50">牛客科技©2018 All rights reserved</li><li class="nav-item text-white-50">京ICP备14055008号-4 &nbsp;&nbsp;&nbsp;&nbsp;<img src="http://static.nowcoder.com/company/images/res/ghs.png" style="width:18px;" />京公网安备 11010502036488号</li></ul></div></div></div></div></div></footer></div><script src="https://code.jquery.com/jquery-3.3.1.min.js" crossorigin="anonymous"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" crossorigin="anonymous"></script><script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" crossorigin="anonymous"></script><script>$(function(){setInterval(function(){var seconds = $("#seconds").text();$("#seconds").text(--seconds);if(seconds == 0) {location.href = $("#target").attr("href");}}, 1000);});</script>
</body>
</html>

牛客网项目3:注册、激活功能相关推荐

  1. 云服务器上部署仿牛客网项目

    云服务器上部署仿牛客网项目 安装JRE 安装Maven 安装MySQL 给mysql导入数据 安装Redis 安装kafka 安装ElasticSearch Wkhtmltopdf 安装tomcat ...

  2. 仿牛客网项目第二章:开发社区登录模块(详细步骤和思路)

    目录 1. 发送邮件 1.0 三步走 1.1 邮箱设置 1.2 Spring Email 1.3 模板引擎 1.4 发送邮件的过程 1.5 检验发送邮件的过程 2. 开发注册功能 2.0 注册功能的步 ...

  3. 牛客网项目1:开发社区首页

    总结在先: ①首先根据每张表创建相对应的实体类,该实体类中的属性与表中的字段名相同: ②由于每张表都需要一些增删改查的方法,因此需要创建Mapper接口(每张表各一个),接口中放有对应表的增删改查方法 ...

  4. 仿牛客网项目第五,六章:异步消息系统和分布式搜索引擎(详细步骤和思路)

    目录 1. Kafka:构建TB级异步消息系统 1.0 同步/异步消息的区别 1.1 项目的目的 1. 2 阻塞队列实现异步消息系统 1.4 Kafka入门 1.5 Spring整合Kafka 1.6 ...

  5. ATeam社区(牛客网项目第三章)

    这里写目录标题 1. 过滤敏感词 1.1 定义敏感词 1.2 定义前缀树 1.3 根据敏感词初始化前缀树 1.4 过滤敏感词的方法 1.5 前缀树过滤敏感词简述 2. 发布帖子 2.1 AJA使用示例 ...

  6. 牛客网项目5:登录、退出功能

    1. 访问登录页面 点击顶部区域内的连接,打开登陆页面. index.html中: <li class="nav-item ml-3 btn-group-vertical"& ...

  7. 牛客网项目——项目开发(八):开发社区搜索功能

    文章目录 1. ElasticsearchService 1.1 注入bean 1.2 保存(修改)和删除 1.3 搜索 searchDiscussPost 2. DiscussPostControl ...

  8. 牛客网项目(社区项目)知识整理

    什么是Spring框架? 有很多模块组成,利用这些模块可以方便开发工作.这些模块是:核心容器(spring core)/数据访问和集成(Spring JDBC)/Web(Spring Web/MVC) ...

  9. 牛客网项目--MyBatis

    1. 安装软件: MySQL 官网下载–>在根目录下,新建文件夹my.ini–> 对mysql进行初始化 配置环境变量,将mysql的bin目录配置到path中: 对mysql进行初始化, ...

最新文章

  1. 学习.NET的两个网站:MSDN和Asp .NET
  2. 2020华为推迟发布鸿蒙战略,早新闻:华为5nm麒麟芯片延期,确认新版鸿蒙将发...
  3. java调试案例_Spring-boot的debug调试代码实例
  4. Linux 修改用户名的主目录 家目录
  5. linux diff详解
  6. java开源项目-SpringBoot在线教育平台
  7. iso硬盘安装 凤凰os_在Linux中安装凤凰系统(Phoenix OS)的方法
  8. css背景图片高斯模糊_CSS3 filter(滤镜) 制作图片高斯模糊无需JS
  9. 莫名其妙把电脑机箱左边耳机孔弄出来声音
  10. 怎么把图片转换成PDF文件?
  11. 竞价推广方案怎么写,这些点你get到了吗?
  12. idea中设置jdk
  13. 如何建设前端物料平台?
  14. 【Caffe学习三】基于ROC-RK3399-PC/Ubuntu18.04的Caffe-SSD-CPU 安装编译___BUG
  15. Spring学习——自动装配
  16. 绘制Excel字符画
  17. 阿里云产品专家陶炳哲:Java应用最佳实验
  18. 【基础篇】详解Zookeeper客户端Curator
  19. 设置adobe reader pro的文本框字体属性
  20. win7家庭版升级到旗舰版

热门文章

  1. SDCMS1.31调用指定栏目信息的代码大全及调用方法
  2. ROS全覆盖规划算法 Coverage Path Planning 采坑
  3. arcgis engine10.2版本 ,狭长面分析,可自定义长宽比例
  4. VC 获取屏幕及打印机的像素,DPI,英寸数,毫米数,缇数(twips)
  5. 手把手教你生成你的独家微信聊天年度报告
  6. 这本694页的程序员砖头书让你精通ASP.NET Core MVC
  7. MySQL——深入理解
  8. u盘修复计算机w7,怎么用u盘修复系统开启,win7旗舰版!
  9. 语谱图(二) Spectrogram 的产生
  10. 超融合架构hci之路坦力nutanix之硬件规格及软件配置之一