注册页面主要实现的功能
① 验证vip: 非vip用户不得注册
② 用户头像上传并回显,默认显示默认头像
③ 验证用户的注册是否是第二次注册
④ 用户注册成功,添加登录日志
一. 验证vip: 用户在输入学号和姓名的时候,姓名输入框失去焦点触发axios请求,在数据库中的vip表查询是否存在该用户,如果存在返回true,否则返回false,返回false,弹出提示框提示信息,并在用户点击确定之前,清空刚才输入的信息,并重新将焦点定位到学号一栏
-
页面显示
-
前端代码一, 验证是否存在该vip
checkVip: function () { this.$http .post("http://localhost:8888/user/checkVip.do", this.registerForm) .then((res) => { if (res.data != true) { // 弹出提示框 this.dialogVisible = true; } }); },
其中在弹出框关闭前触发清空信息并重新定位光标的函数
<!-- 没有该vip,弹出框提示信息 --> <el-dialog title="提示" :visible.sync="dialogVisible" width="30%" :before-close="clearInfo"> <span>没有该vip用户,请重新输入</span> <span slot="footer" class="dialog-footer"> <el-button type="primary" @click="clearInfo">确 定</el-button> </span> </el-dialog>
clearInfo: function() { // 关闭弹出框 this.dialogVisible = false; // 清空输入框 this.registerForm = {}; // 将光标重新定位到第一个input,该input需要指定一个ref,值为clickPosition this.$refs.clickPosition.focus(); },
-
后端代码一,controller层
// 检查是否存在该vip用户 @RequestMapping(value = "/checkVip.do", method = RequestMethod.POST) @ResponseBody @CrossOrigin public boolean checkVipService(@RequestBody CheckUser checkUser) { User user = userService.checkVip(Integer.valueOf(checkUser.getId()), checkUser.getName()); if (user == null) { return false; } return true; }
-
后端代码二,业务逻辑层
package com.zhl.service; /** * @author ZhangHailong * @date 2022/2/23 - 13:45 * @project_name */ public interface UserService { // 检查是否有该vip User checkVip(Integer id, String name); }
package com.zhl.service.impl; import com.zhl.dao.UserDao; import com.zhl.service.UserService; /** * @author ZhangHailong * @date 2022/2/23 - 13:48 * @project_name */ @Service public class UserServiceImpl implements UserService { @Autowired private UserDao userDao; @Override public User checkVip(Integer id, String name) { return userDao.checkVip(id, name); } }
-
后端代码三,持久层
package com.zhl.service.impl; import com.zhl.dao.UserDao; import com.zhl.service.UserService; /** * @author ZhangHailong * @date 2022/2/23 - 13:48 * @project_name */ @Service public class UserServiceImpl implements UserService { @Autowired private UserDao userDao; @Override public User checkVip(Integer id, String name) { return userDao.checkVip(id, name); } }
二. 用户头像上传并回显: 用户在上传头像以后,后台将此头像的绝对地址返回给前端,前端进行回显,在用户提交其他的注册信息到后端后,与头像的绝对地址一起,封装成新用户添加到数据库中
-
参考博客: SpringMVC实现图片的上传
-
配置
<!--文件上传资源解析bean--> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <!-- 设置默认编码 --> <property name="defaultEncoding" value="utf-8"></property> <!-- 上传图片最大大小1M--> <property name="maxUploadSize" value="1048576"></property> </bean>
-
前端代码
<!-- 上传头像 --> <el-form-item label="上传头像"> <el-upload class="upload-demo" action="http://localhost:8888/user/upload.do" :headers="headers" :on-success="handleUpload" > <el-button size="small" type="primary">点击上传</el-button> <div slot="tip" class="el-upload__tip">只能上传jpg/png文件,且不超过500kb</div> </el-upload> </el-form-item>
// 上传头像成功以后,获取后台返回的头像的绝对路径,回显 handleUpload: function(res) { // console.log(res); this.default_portrait = res; // default_portrait就是页面中头像部分绑定的值 },
-
后端代码,因为还是在本地开发阶段,没有购买服务器,所以上传的头像保存到本地的D:\portraits文件夹下,以protrait开头命名
// 注册页面上传头像并回显 @RequestMapping(value = "/upload.do", method = RequestMethod.POST) @ResponseBody @CrossOrigin public String uploadService(MultipartFile file) { String filePath = ""; if (file != null){ // 原始文件名 String originalFileName = file.getOriginalFilename(); // 获取图片后缀 String suffix = originalFileName.substring(originalFileName.lastIndexOf(".")); // 生成图片存储的名称,UUID 避免相同图片名冲突,并加上图片后缀 String fileName = UUID.randomUUID().toString() + suffix; // 图片存储路径 // 此时的"D:\\portraits\\portrait"就相当于服务器下的portrait文件夹,图片的名字是: [email protected] filePath = "D:/portraits/portrait" + fileName; File saveFile = new File(filePath); try { // 将上传的文件保存到服务器文件系统 file.transferTo(saveFile); } catch (IOException e) { e.printStackTrace(); } // 将头像绝对路径放入数据库 this.portrait = filePath; } // 将绝对路径返回,用于回显头像 return filePath; }
遇到的问题: 我一开始指定的filePath是D:/portraits,以为图片就保存到portraits文件夹下了,结果是保存到了D盘下,而且是以portraits为开头的,看了别人的博客,发现是MultipartFile中transferTo(File file)的路径问题, 参考博客: MultipartFile中transferTo(File file)的路径问题, 于是对路径进行了修改,最终效果如下
在购买服务器以后,将文件上传改为上传到OSS-
添加pom依赖
<dependency> <groupId>com.aliyun.oss</groupId> <artifactId>aliyun-sdk-oss</artifactId> <version>3.10.2</version> </dependency> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>1.3.2</version> </dependency> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.2.1</version> </dependency>
-
修改Controller
// 注册页面上传头像并回显 @RequestMapping(value = "/upload.do", method = RequestMethod.POST) @ResponseBody @CrossOrigin public String uploadService(MultipartFile file) { try { String endpoint = "oss-cn-beijing.aliyuncs.com"; String accessKeyId = "****"; String accessKeySecret = "***"; // 创建OSSClient实例。 OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret); // 填写本地文件的完整路径。如果未指定本地路径,则默认从示例程序所属项目对应本地路径中上传文件流。 InputStream inputStream = file.getInputStream(); //获取上传的文件名 String filename = file.getOriginalFilename(); filename = "skaters/"+ currentId +"/portrait/portrait" + new Date().getTime() + filename; // 依次填写Bucket名称(例如examplebucket)和Object完整路径(例如exampledir/exampleobject.txt)。Object完整路径中不能包含Bucket名称。 ossClient.putObject("skaterdata", filename, inputStream); // 关闭OSSClient。 ossClient.shutdown(); // https://qy145.oss-cn-hangzhou.aliyuncs.com/16388489433231.jpg portrait = "https://skaterdata."+endpoint+"/"+filename; } catch (IOException e) { e.printStackTrace(); } return portrait; }
-
三. 用户注册: 用户在提交信息以后,首先后台会去数据库中查找,是否已经存在了该用户,如果存在了,那么返回String对象"已经注册过了", 首次注册的话, 返回"注册成功", 前端根据返回信息进行下一步操作, 注册成功,sessionStorage将用户的id存储,并跳转到主页,已经注册了,询问用户是否跳转到登录页面,如果跳转,携带用户的id,自动填充到id框,用户进行登录操作,如果不跳转,那么清空用户之前填写的所有信息,并将光标重新定位到id一栏
-
前端代码
handleRegister: function () { this.$http .post("http://localhost:8888/user/registerUser.do", this.registerForm) .then((res) => { // console.log(res); if (res.data == "已经注册过了") { // 已经注册过了,显示弹出框询问用户是否跳转到登录页面 this.dialogVisible2 = true; } else if (res.data == "注册成功") { // 注册成功,跳转到主页 sessionStorage.setItem('userId', this.registerForm.id); this.$router.push({ path: "/Home", }); } }); },
<!-- 已经注册过了,询问用户是否跳转到登录页面 --> <el-dialog title="提示" :visible.sync="dialogVisible2" width="30%" :before-close="clearInfo2"> <span>已经注册过了,是否跳转到登录页面?</span> <span slot="footer" class="dialog-footer"> <el-button @click="clearInfo2">取 消</el-button> <el-button type="primary" @click="goToLoginPage">确 定</el-button> </span> </el-dialog>
-
后端代码一, controller层
// 注册用户 @RequestMapping(value = "/registerUser.do", method = RequestMethod.POST) @ResponseBody @CrossOrigin public String registerUserService(@RequestBody User user, HttpServletRequest request) { // 在注册之前,验证该用户是否已注册 User ifRegistered = userService.chkIfRegistered(user); if (ifRegistered != null) { // 已经注册过了 return "已经注册过了"; } user.setPortrait(this.portrait); String ipAddress = request.getRemoteAddr(); userService.registerUser(user, ipAddress); return "注册成功"; }
-
后端代码二, 业务逻辑层代码
package com.zhl.service; import com.zhl.entity.User; /** * @author ZhangHailong * @date 2022/2/23 - 13:45 * @project_name */ public interface UserService { // 添加新用户前检查该用户是否是二次注册 User chkIfRegistered(User user); // 注册用户 User registerUser(User user, String ipAddress);
package com.zhl.service.impl; import com.zhl.dao.UserDao; import com.zhl.entity.User; import com.zhl.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** * @author ZhangHailong * @date 2022/2/23 - 13:48 * @project_name */ @Service public class UserServiceImpl implements UserService { @Autowired private UserDao userDao; @Override public User chkIfRegistered(User user) { return userDao.chkIfRegistered(user); } @Override public User registerUser(User user, String ipAddress) { // 因为持久层的插入操作返回的是作用的行数,但是在切面类中我还要用到这个用户, // 所以在这里做一个判断,如果插入成功,将该注册用户返回 if (userDao.insertUser == -1) return null; return user; } }
-
后端代码三,持久层
package com.zhl.dao; import com.zhl.entity.User; import org.apache.ibatis.annotations.Param; /** * @author ZhangHailong * @date 2022/2/23 - 13:37 * @project_name */ public interface UserDao { // 注册前验证是否已存在该用户 User chkIfRegistered(User user); // 持久层添加用户 int insertUser(User user); }
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.zhl.dao.UserDao"> <!--select|insert|update|delete语句--> <!--检查是否是二次注册--> <select id="chkIfRegistered" resultType="com.zhl.entity.User"> select * from user where id = #{id} and name = #{name} </select> <!--添加用户--> <insert id="insertUser"> insert into user(id,name,nickName,password,lv,portrait,stance) values(#{id},#{name},#{nickName},#{password},#{lv},#{portrait},#{stance}) </insert> </mapper>
四. 用户注册成功后,跳转到主页,这时通过AOP切面类添加登录日志,方式与登录的登录日志相同,这里不多赘述
-
后端代码
package com.zhl.aspect; import com.zhl.dao.LoginLogDao; import com.zhl.entity.LoginLog; import com.zhl.entity.User; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.Aspect; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.util.Date; /** * @author ZhangHailong * @date 2022/3/11 - 19:38 * @project_name */ @Aspect @Component public class LoginLogAspect { @Autowired private LoginLogDao loginLogDao; // 注册成功以后,添加登录日志 @AfterReturning(value = "execution(* *..UserServiceImpl.registerUser(..))", returning = "result") public void addRegisterLog(JoinPoint jp,User result) { if (result != null) { LoginLog loginLog = new LoginLog(); // 注册的id loginLog.setUserId(result.getId()); // 注册时间 loginLog.setLoginTime(new Date()); // 获取registerUser方法的第二个参数,也就是注册的用户的ip,择取需要的填充到LoginLog对象中 loginLog.setIpAddress(jp.getArgs()[1].toString()); loginLogDao.insertLogDao(loginLog); } }