(一)项目分析
1.项目分析
📝📝项目功能:登录,注册,热销商品,用户管理(密码,个人信息,头像,收货地址),购物车(展示,增加,删除),订单模块
📝📝开发顺序:注册,登录,用户管理,购物车,商品,订单模块
2.开发顺序
模块的开发顺序:
持久层开发:依据前端页面的设置规划相关的SQL语句,以及进行配置
业务层开发:核心功能控制,业务操作以及异常的处理
控制层开发:接收请求,处理响应
前端开发:JS,Query,AJAX这些技术来连接后台
3.前端资源测试
创建一个springboot项目导入前端静态资源 ,检测是否能够正常显示。如果这个过程访问失败,原因是idea对于JS代码的兼容性较差,编写了js代码但是有的时候不能正常去加载,解决办法有以下四种
1.clear-install:依次点击MavenProject->store->Lifecycle->clean,等待清理项目完毕后点击同目录下的install重新部署
2.idea缓存清理:点击File下的Invalidate Caches/Restart…然后在弹出的窗口中选择Invalidate and Restart,此时就会自动清除缓存并重新启动idea
3.rebuild重新构建:点击工具栏的Build下的Rebuild Project
4.重启电脑
(二)用户注册
1.创建数据库
创建数据库t_user
CREATE TABLE t_user ( uid INT AUTO_INCREMENT COMMENT '用户id', username VARCHAR(20) NOT NULL UNIQUE COMMENT '用户名', password CHAR(32) NOT NULL COMMENT '密码', salt CHAR(36) COMMENT '盐值', phone VARCHAR(20) COMMENT '电话号码', email VARCHAR(30) COMMENT '电子邮箱', gender INT COMMENT '性别:0-女,1-男', avatar VARCHAR(50) COMMENT '头像', is_delete INT COMMENT '是否删除:0-未删除,1-已删除', created_user VARCHAR(20) COMMENT '日志-创建人', created_time DATETIME COMMENT '日志-创建时间', modified_user VARCHAR(20) COMMENT '日志-最后修改执行人', modified_time DATETIME COMMENT '日志-最后修改时间', PRIMARY KEY (uid) ) ENGINE=INNODB DEFAULT CHARSET=utf8;
2.实体类编写
创建用户表实体类(因为在用户表的设计中有4个公共字段,所以将这些字段字段提取出来放在一个实体基类baseEntity中)
//基类 @Data @AllArgsConstructor @NoArgsConstructor public class baseEntity implements Serializable { private String createdUser; private Date createdTime; private String modifiedUser; private Date modifiedTime;
实体类User继承baseEntity(实体类User因为要在网络中以流的形式传输,所以需要serialize序列化)
//实体类 @Data public class User extends baseEntity implements Serializable { private Integer uid; private String username; private String password; private String salt; private String phone; private String email; private Integer gender; private String avatar; private Integer isDelete;
3.注册(持久层)
mapper接口
//用户模块持久层 //@Mapper public interface UserMapper { //插入用户数据 Integer insert(User user); //根据用户名查找用户数据 User findByUsername(String username); }
mapper的xml映射文件
<?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"> <!--namespace用于指定当前的映射文件和哪个接口进行映射,需要指定接口的文件路径,路径需要是包的完整路径结构--> <mapper namespace="com.cy.store.mapper.UserMapper"> <resultMap id="UserEntityMap" type="com.cy.store.entity.User"> <id column="uid" property="uid"></id> <result column="is_delete" property="isDelete"></result> <result column="created_user" property="createdUser"></result> <result column="created_time" property="createdTime"></result> <result column="modified_user" property="modifiedUser"></result> <result column="modified_time" property="modifiedTime"></result> </resultMap> <insert id="insert" useGeneratedKeys="true" keyProperty="uid"> insert into t_user( username,password,salt,phone,email,gender,avatar,is_delete, created_user,created_time,modified_user,modified_time) values ( #{username},#{password},#{salt},#{phone},#{email},#{gender},#{avatar},#{isDelete},#{createdUser},#{createdTime},#{modifiedUser},#{modifiedTime} ) </insert> <select id="findByUsername" resultMap="UserEntityMap"> select * from t_user where username=#{username} </select> </map
4.注册(业务层)
service层
接受前端从控制器流转过来的数据
结合真实的注册业务来完成功能业务逻辑的调转和流程
异常规划:
比如,用户在进行注册时可能会产生用户名被占用的错误,这时需要抛出一个异常
1.异常不能用RuntimeException,太笼统了,开发者没办法第一时间定位到具体的错误类型上,我们可以定义具体的异常类型来继承这个异常。
2.正常的开发中异常又要分等级,可能是在业务层产生异常,可能是在控制层产生异常,所以可以创建一个业务层异常的基类,起名ServiceException异常,并使其继承RuntimeException异常。
3.后期开发业务层时具体的异常可以再继承业务层的异常ServiceException。
在service包下创建一个ex包再在下创建ServiceException类作为业务层异常的基类
publipublic class ServiceException extends RuntimeException{ public ServiceException() { super(); } public ServiceException(String message) { super(message); } public ServiceException(String message, Throwable cause) { super(message, cause); } public ServiceException(Throwable cause) { super(cause); } protected ServiceException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); } }
分别创建查询异常UsernameDuplicatedException 和插入异常的InsertException 类
public class UsernameDuplicatedException extends ServiceException{ /**重写ServiceException的所有构造方法*/ }
//数据插入过程中产生异常 public class InsertException extends ServiceException{ /**重写ServiceException的所有构造方法*/ }
创建IUserService接口(接口命名的默认规则:I+业务名字+层的名字)
//用户模块业务层接口 public interface IUserService { void reg(User user); }
service实现serviceimpl类
@Service public class UserServiceImpl implements IUserService { @Resource private UserMapper userMapper; @Override public void reg(User user) { String username=user.getUsername(); //调用findbyusername判断用户是否被注册 User result= userMapper.findByUsername(username); if (result!=null){ throw new UsernameDuplicatedException("用户已经存在"); } //密码加密 md5加密算法 //串(盐值)+password+串 ---md5进行加密 连续加载三次 //盐值就是随机字符串 String oldPassword=user.getPassword(); String salt= UUID.randomUUID().toString().toUpperCase(); //记录盐值 登录进行密码比较 user.setSalt(salt); //将密码和盐值作为一个整体 加密 String md5password=getMd5Password(oldPassword,salt); //重新加入 user.setPassword(md5password); //补全数据 user.setIsDelete(0); Date date=new Date(); user.setCreatedTime(date); user.setModifiedTime(date); user.setCreatedUser(user.getUsername()); user.setModifiedUser(user.getModifiedUser()); Integer rows=userMapper.insert(user); if (rows!=1){ throw new InsertException("注册异常"); } } //定义md5算法加密 private String getMd5Password(String password,String salt){ for (int i=0 ;i<3;i++){ password=DigestUtils.md5DigestAsHex((salt+password+salt).getBytes()).toUpperCase(); } return password; } }
5.注册(控制层)
Controller层
请求路径:/users/reg
请求参数:User user
请求类型:POST
响应结果:JsonResult< void >
在controller包下创建BaseController 类作为控制层下类的基类,用来做统一的异常捕获:
public class BaseController { public static final int ok = 200; //请求处理方法,这个方法的返回值就是需要传递给前端数据 //自动将异常对象传递给此方法的参数列表上 //当前项目中产生了异常,被统一拦截到此方法中,这个方法此时充当的是请求处理方法,方法的返回值直接给前端 @ExceptionHandler(ServiceException.class)//用于统一处理抛出异常 public JsonResult<Void> handleException(Throwable e) { JsonResult<Void> result = new JsonResult<>(e); if (e instanceof UsernameDuplicatedException) { result.setState(400); result.setMessage("用户名存在"); } else if (e instanceof InsertException){ result.setState(500); result.setMessage("注册出现异常"); } return result; } }
创建UserController类,依赖于业务层的接口,继承BaseController并重构UserController下的reg方法。
@RestController @RequestMapping("/users") public class UserController extends BaseController{ @Autowired private IUserService userService; @PostMapping("/reg") public JsonResult<Void> reg(@RequestBody User user){ userService.reg(user); return new JsonResult<>(ok); } }
6.注册前端页面
ajax函数:
这是jQuery封装的一个函数,称为$.ajax()函数,通过对象调用ajax()函数用来异步加载相关的请求.依靠的是JavaScript提供的一个对象:XHR(全称XmlHttpResponse)
ajax()函数的语法结构:
使用ajax()时需要传递一个方法体作为方法的参数来使用(一对大括号就是一个方法体)
ajax接受多个参数时,参数与参数之间使用",“分割
每一组参数之间使用”:"进行分割
参数的组成部分一个是参数的名称(不能随便定义),另一个是参数的值(必须用字符串来表示)
参数的声明顺序没有要求
语法结构:
$.ajax({ url: "", type: "", data: "", dataType: "", success: function() { }, error: function() { } });
前端js代码
<script> //1.监听注册按钮是否被点击,如果被点击可以执行一个方法(这里不能像ajax函数那样删去function()只留下{},这是官方规定的!) $("#btn-reg").click(function () { //let username = $("#username").val(); //let pwd = $("#password").val(); //上面这两行是动态获取表单中控件的数据,但是如果这样获取的话ajax函数中 //就是data: "username="+username + "&password="+pwd,但太麻烦了,如 // 果这个表单提交的是用户的兴趣爱好,那数据就很多了,一个表单20个数据都很正 // 常,如果此时还用这种方式就太麻烦了,所以不建议用这种方式 //2.发送ajax()的异步请求来完成用户的注册功能 $.ajax({ url: "/users/reg", type: "POST", //serialize这个API会自动检测该表单有什么控件,每个控件检测后还会获取每个控 // 件的值,拿到这个值后并自动拼接成形如username=Tom&password=123的结构 data: $("#form-reg").serialize(), dataType: "JSON", success: function (json) { //1.js是弱数据类型,这个地方不用声明json的数据类型 //2.如果服务器成功响应就会将返回的数据传给形参,比如{state: 4000,message: "用户名 // 已经被占用",data: null} if (json.state == 200) { alert("注册成功") } else { alert("注册失败") } }, error: function (xhr) { //如果问题不在可控范围内,服务器就不会返回自己定 //义的json字符串:{state: 4000,message: "用户名已经被占用",data: null} //而是返回一个XHR类型的对象,该对象也有一个状态码名字是status alert("注册时产生未知的错误!"+xhr.status); } }); }); </script>
浏览器上输入http://localhost:8080/web/register.html 测试注册功能是否可以注册。此时可能会出现点击注册提交表单时没有任何响应,原因是idea对于JS代码的兼容性较差,编写了js代码但是有的时候不能正常去加载,解决办法有四种,同前面的:项目环境搭建->项目测试->测试静态资源能否正常加载
后记
👉👉💕💕美好的一天,到此结束,下次继续努力!,写作不易,感谢大家的支持!! 🌹🌹🌹