ToB企服应用市场:ToB评测及商务社交产业平台

标题: Java-仿华为商城项目-SpringBoot+MyBatis+MySQL [打印本页]

作者: tsx81429    时间: 2022-8-30 17:27
标题: Java-仿华为商城项目-SpringBoot+MyBatis+MySQL
Store网上商城项目

用户注册

1 创建数据表

2 创建用户的实体类

3 注册-持久层

3.1 规划需要执行

1.用户的注册功能。相当于在做数据的插入操作
  1. insert into t_user (username,password) values (值列表)
复制代码
2.在用户注册时要首先去查询当前的用户名是否存在,如果存在则不能进行注册。相当于是一条查询语句
  1. select * from t_user where username=?
复制代码
3.2 设计接口和抽象方法

定义Mapper接口。在项目的目录下创建mapper包,在这个包下根据不同的功能模块来创建mapper接口。创建一个UserMapper的接口。要在接口中定义这两个抽象方法。
  1. public interface UserMapper {
  2.     /**
  3.      * 插入用户的数据
  4.      * @param user 用户的数据
  5.      * @return 受影响的行数(增,删,改,都用受影响行数作为返回值)
  6.      */
  7.     Integer insert(User user);
  8.     /**
  9.      * 根据用户名来查询用户的数据
  10.      * @param username 用户名
  11.      * @return 如果找到对应的用户则返回这个用户的数据,如果没有则返回null
  12.      */
  13.     User findByUsername(String username);
  14. }
复制代码
3.3 编写映射

1.定义xml映射文件,与对应的接口进行关联。所有的映射文件需要放在resources目录下,在这个目录下创建一个mapper文件夹,然后在这个文件夹下存放Mapper映射文件
2.创建接口对应的映射文件,遵循和接口的名称保存一致即可。创建一个UserMapper.xml文件。
3.配置接口中的方法对应上SQL语句上。需要借助标签来完成,insert\update\delete\select,对应的是SQL语句的增删改查操作。
4.resultMap 定义匹配不同命名规则的属性,并把resultMap的id传入到标签的resultMap属性里面
  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE mapper
  3.         PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  4.         "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  5. <mapper namespace="com.cy.store.mapper.UserMapper">
  6.     <resultMap id="UserEntityMap" type="com.cy.store.entity.User">
  7.         <id column="uid" property="uid"></id>
  8.         <result column="is_delete" property="isDelete"></result>
  9.         <result column="created_user" property="createdUser"></result>
  10.         <result column="created_time" property="createdTime"></result>
  11.         <result column="modified_user" property="modifiedUser"></result>
  12.         <result column="modified_time" property="modifiedTime"></result>
  13.     </resultMap>
  14.     <insert id="insert" useGeneratedKeys="true" keyProperty="uid">
  15.         INSERT INTO t_user(username, password, salt, phone, email, gender, avatar, isDelete,createdUser, createdTime, modifiedUser, modifiedTime)
  16.         VALUES (#{username}, #{password}, #{salt}, #{phone}, #{email}, #{gender}, #{avatar}, #{isDelete},#{createdUser}, #{createdTime}, #{modifiedUser}, #{modifiedTime})
  17.     </insert>
  18.     <select id="findByUsername" resultMap="UserEntityMap">
  19.         SELECT *
  20.         FROM t_user
  21.         WHERE username = #{username}
  22.     </select>
  23. </mapper>
复制代码
3.4 单元测试

每个独立的层编写完毕后都需要写单元测试方法,来测试当前功能。在test包结构下创建一个mapper包,在这个包下再创建持久层的功能测试。
  1. @SpringBootTest
  2. @RunWith(SpringRunner.class)
  3. public class UserMapperTests {
  4.     @Autowired
  5.     private UserMapper userMapper;
  6.     @Test
  7.     public void insert() {
  8.         User user = new User();
  9.         user.setUsername("tim");
  10.         user.setPassword("123");
  11.         Integer rows = userMapper.insert(user);
  12.         System.out.println(rows);
  13.     }
  14.     @Test
  15.     public void findByUsername(){
  16.         User user = userMapper.findByUsername("tim");
  17.         System.out.println(user);
  18.     }
  19. }
复制代码
4 注册-业务层

4.1 规划异常

1.RuntimeException异常:作为这类异常的子类,然后再去定义具体的异常类型来继承这个异常。业务层异常的基类,ServiceException异常。这个异常继承RuntimeException异常。异常机制的建立。
  1. public class ServiceException extends RuntimeException{
  2.     public ServiceException() {
  3.         super();
  4.     }
  5.     public ServiceException(String message) {
  6.         super(message);
  7.     }
  8.     public ServiceException(String message, Throwable cause) {
  9.         super(message, cause);
  10.     }
  11.     public ServiceException(Throwable cause) {
  12.         super(cause);
  13.     }
  14.     protected ServiceException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
  15.         super(message, cause, enableSuppression, writableStackTrace);
  16.     }
  17. }
复制代码
根据业务层不同的功能来详细定义具体的异常的类型,统一的去继承ServiceException异常类。
2.业务异常:用户在进行注册时候可能会产生用户名被占用的错误,抛出一个异常:UsernameDepulitedException异常
  1. public class UsernameDuplicatedException extends ServiceException{
  2.     public UsernameDuplicatedException() {
  3.         super();
  4.     }
  5.     public UsernameDuplicatedException(String message) {
  6.         super(message);
  7.     }
  8.     public UsernameDuplicatedException(String message, Throwable cause) {
  9.         super(message, cause);
  10.     }
  11.     public UsernameDuplicatedException(Throwable cause) {
  12.         super(cause);
  13.     }
  14.     protected UsernameDuplicatedException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
  15.         super(message, cause, enableSuppression, writableStackTrace);
  16.     }
  17. }
复制代码
3.其他异常:正在执行数据插入操作的时候,服务器宕机。处于正在执行插入的过程中所产生的异常InsertException
4.2 设计接口和抽象方法

1.在service包下创建一个IUserService接口。
  1. public interface IUserService {
  2.     /**
  3.      * 用户注册方法
  4.      * @param user 用户的数据对象
  5.      */
  6.     void reg(User user);
  7. }
复制代码
2.创建一个实现类UserServiceImpl类,需要实现这个接口,并且实现抽象方法
  1. @Service
  2. public class UserServiceImpl implements IUserService {
  3.     @Autowired
  4.     private UserMapper userMapper;
  5.     @Override
  6.     public void reg(User user) {
  7.         String username = user.getUsername();
  8.         User result = userMapper.findByUsername(username);
  9.         if (result != null){
  10.             throw new UsernameDuplicatedException("用户名被占用");
  11.         }else {
  12.             String oldPassword =user.getPassword();
  13.             String salt = UUID.randomUUID().toString().toUpperCase();
  14.             String md5Password = getMD5Password(oldPassword, salt);
  15.             user.setPassword(md5Password);
  16.             user.setSalt(salt);
  17.             user.setIsDelete(0);
  18.             user.setCreatedUser(user.getUsername());
  19.             user.setModifiedUser(user.getUsername());
  20.             Date date = new Date();
  21.             user.setCreatedTime(date);
  22.             user.setModifiedTime(date);
  23.             Integer rows = userMapper.insert(user);
  24.             if(rows != 1){
  25.                 throw new InsertException("在用户注册过程中产生了未知异常");
  26.             }
  27.         }
  28.     }
  29.     private String getMD5Password(String password,String salt){
  30.         for (int i = 0; i < 3; i++) {
  31.             password=DigestUtils.md5DigestAsHex((salt+password+salt).getBytes(StandardCharsets.UTF_8)).toUpperCase();
  32.         }
  33.         return password;
  34.     }
  35. }
复制代码
3.在单元测试包下创建一个UserServiceTests类,在这个类中添加单元测试的功能。
  1. @SpringBootTest
  2. @RunWith(SpringRunner.class)
  3. public class UserServiceTests {
  4.     @Autowired
  5.     private IUserService userService;
  6.     @Test
  7.     public void reg(){
  8.         try {
  9.             User user=new User();
  10.             user.setUsername("yuanxin03");
  11.             user.setPassword("123");
  12.             userService.reg(user);
  13.             System.out.println("ok");
  14.         }catch (ServiceException e){
  15.             System.out.println(e.getClass().getSimpleName());
  16.             System.out.println(e.getMessage());
  17.         }
  18.     }
  19. }
复制代码
5 注册-控制层

5.1 创建响应

状态码,状态描述信息,数据。这部分功能封装在一个类中,将这类作为方法返回值,返回给前端浏览器。
  1. public class JsonResult<E> implements Serializable {
  2.     // 状态码
  3.     private Integer state;
  4.     // 描述信息
  5.     private String message;
  6.     // 数据
  7.     private E data;
  8. }
复制代码
5.2 设计请求

根据当前的业务功能模块,进行请求的设计
  1. 请求路径:/users/reg
  2. 请求参数:User user
  3. 请求类型:Post
  4. 请求结果:JsonResult<void>
复制代码
5.3 处理请求

1.创建控制层对应的类UserController类,依赖于业务层的接口。
2.异常捕获
5.4 控制层优化设计

在控制层抽离父类,在这个父类中统一的去处理关于异常的相关操作。编写一个BaseController类,统一处理异常。
6 注册-前端页面

1.在register页面中编写发送请求的方法,点击事件来完成。选中对应的按钮(\(("选择器")),再去添加点击的事件,\).ajax()函数发送异步请求。
2.JQuery封装了一个函数,称之为$ajax()函数,通过对象调用ajax()函数,可以异步加载相关的请求。依靠的是JavaScript提供的一个对象XHR(XmlHttpResponse),封装了这个对象。
3.ajax()的使用方式。需要传递一个方法体作为方法的参数来使用:
  1. $.ajax({
  2.     url:"",
  3.     type:"",
  4.     data:"",
  5.     dataType:"",
  6.     success:function(){
  7.       
  8.     },
  9.     error:function(){
  10.         
  11.     }
  12. });
复制代码
4.ajax()函数参数的含义:
5.js代码可以独立在一个js的文件里或声明在一个script标签中
用户登录

1 登录-持久层

1.1 规划需要执行的SQL语句

依据用户提交的用户名和密码做select查询。
  1. select * from t_user where username=?
复制代码
1.2 接口设计和方法

2 登录-业务层

2.1 规划异常

1.用户名对应的密码错误,密码匹配失败的异常:PasswordNotMatchException,运行时异常,业务异常。
2.用户名没有找到:UsernameNotFoundException,运行时异常,业务异常。
3.异常的编写
2.2 设计业务层接口和抽象方法

1.直接在IUserService接口中编写抽象方法,login(String username,String password)。将当前登录成功的用户数据以当前用户对象的形式返回。状态管理:可以将数据保存在cookie或者session中,可以避免重复度很高的数据多次频繁操作数据进行获取(用户id-在session中,用户的头像-cookie中)。
2.在实现类中实现父接口的抽象方法
3.在测试类中测试业务层登录的方法是否可以执行通过。
4.如果一个类没有手动创建直接将这个类复制到项目,idea找不到这个类。解决:重新构建
2.3 抽象方法实现

3 登录-控制层

3.1 处理异常

业务层抛出的异常是什么,需要在统一异常处理类中进行统一的捕获和处理,如果业务层抛出的异常类型在已经在统一异常处理类中曾经处理过,则不需要重复处理
3.2 设计请求
  1. 请求路径:/users/login
  2. 请求参数:String username, String password
  3. 请求类型:Post
  4. 请求结果:JsonResult<User>
复制代码
3.3 处理请求

在UserController类中编写处理请求的方法。
  1. @RequestMapping("/login")
  2. public JsonResult<User> login(String username,String password){
  3.     User login = userService.login(username, password);
  4.     return new JsonResult<>(OK,login);
  5. }
复制代码
4 登录-前端页面

1.在login.html页面中依据前面所设置的请求来发送ajax请求。
2.访问页面进行用户的登录操作。
用户会话session

session对象主要存在服务器端,可以用于保存服务器的临时数据的对象,所保存的数据可以在整个项目中都可以通过访问来获取,把session的数据看作一个共享的数据源。首次登陆的时候所获取的用户数据,转移到session对象即可。
session.getAttrbute("key")可以将获取session中的数据的这种行为进行封装,封装在BaseController类中。
拦截器

拦截器:首先将所有的请求统一拦截到拦截器中,开发者可以在拦截器中定义过滤的规则,如果不满足系统的设置的过滤规则,统一的处理是重新去打开login.html页面(重定向和转发),推荐使用重定向。
在SpringBoot项目中实现拦截器的定义和使用。SpringBoot是依靠SpringMVC来完成的。SpringMVC提供了一个接口HandlerInterceptor接口,用于表示定义一个拦截器。首先,自定义一个类,再用这个类去实现这个接口。
1.自定义一个类,用这个类实现HandlerInterceptoer接口。
  1. /**
  2. * 定义一个拦截器
  3. */
  4. public class LoginInterceptor implements HandlerInterceptor {
  5.     /**
  6.      * 检测全局session对象中是否有uid数据,如果有则放行,如果没有则重定向到登录页面
  7.      * @param request 请求对象
  8.      * @param response 响应对象
  9.      * @param handler 处理器
  10.      * @return 如果返回值为true表示放行当前的请求,如果返回值为false则表示拦截当前的请求
  11.      * @throws Exception
  12.      */
  13.     @Override
  14.     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
  15.         Object uid = request.getSession().getAttribute("uid");
  16.         if (uid==null){
  17.             response.sendRedirect("/web/login.html");
  18.             return false;
  19.         }
  20.         return true;
  21.     }
  22. }
复制代码
2.注册过滤器:添加白名单(哪些资源可以在不登录的情况下访问:login.html\register.html\login\reg\index.html\product.html)、添加黑名单(登录时才可以访问的页面资源)
3.注册过滤技术:借助WebMvcConfigure接口,可以将用户定义的拦截器进行注册,才可以保证拦截器能够生效和使用。定义一个类,然后让这个类实现WebMvcConfigure接口。配置信息,建议存放在项目的config包结构下。
  1. public class LoginInterceptorConfigurer implements WebMvcConfigurer {
  2.     @Override
  3.     public void addInterceptors(InterceptorRegistry registry) {
  4.         WebMvcConfigurer.super.addInterceptors(registry);
  5.     }
  6. }
复制代码
修改密码

需要用户提交原始密码和新密码,再根据当前登录的用户进行信息的修改操作。
1 修改密码-持久层

1.1 规划需要执行的SQL语句

根据用户的uid修改用户的password值。
  1. update t_user set password=?,modified_user?=modified_time=?where uid=?
复制代码
根据uid查询用户的数据。在修改密码之前,首先要保证当前这用户的数据存在(is_delete),检测是否被标记为删除、检测输入的原始密码是否正确。
  1. select * from t_user where uid=?
复制代码
1.2 UserMapper接口定义方法

1.3 UserMapper.xml完善SQL语句

1.4 单元测试

2 修改密码-业务层

2.1 规划可能产生的异常

1 用户输入的原密码错误 PasswordNotMatch异常
2 用户已删除
3 uid找不到用户
4 update更新时产生的未知的异常,UpadateException
2.2 设计接口和抽象方法

执行用户修改密码的核心方法
2.3 编写单元测试

3 修改密码-控制层

3.1 处理异常

UpdateException需要配置在统一的异常处理方法中。
3.2 设计请求
  1. /users/changPassword
  2. POST
  3. String oldPassword,String newPassword
  4. JsonResult<void>
复制代码
3.3 处理请求
  1. @RequestMapping("/change_password")
  2.     public JsonResult<Void> changePassword(String oldPassword,String newPassword,HttpSession session){
  3.         Integer uid = getUidFromSession(session);
  4.         String username = getUsernameFromSession(session);
  5.         userService.changePassword(uid,username,oldPassword,newPassword);
  6.         return new JsonResult<>(OK);
  7.     }
复制代码
4 修改密码-前端页面

个人资料

1.个人资料-持久层

1.1 需要规划的SQL语句

1.更新用户信息的SQL语句
  1. update t_user set phone=?,email=?,gender=?,modified_user=?,modified_time=? where uid=?
复制代码
2.根据用户名来查询用户的数据
  1. select * from t_user where uid=?
复制代码
1.2 接口与抽象方法

更新用户的信息方法的定义。
  1.         /**
  2.      * 更新用户的数据信息
  3.      * @param user 用户的数据
  4.      * @return 返回值为受影响的行数
  5.      */
  6.     Integer updateInfoByUid(User user);
复制代码
1.3 抽象方法的映射

UserMapper.xml文件
  1. <update id="updateInfoByUid">
  2.     UPDATE t_user
  3.     SET <if test="phone!=null">phone=#{phone},</if>
  4.     <if test="email!=null">email=#{email},</if>
  5.     <if test="gender!=null">gender=#{gender},</if>
  6.     modified_user=#{modifiedUser},
  7.     modified_time=#{modifiedTime}
  8.     WHERE uid=#{uid}
  9. </update>
复制代码
1.4 测试
  1. @Test
  2. public void updateInfoByUid(){
  3.     User user=new User();
  4.     user.setUid(11);
  5.     user.setPhone("1232");
  6.     user.setEmail("98670@qq.com");
  7.     user.setGender(1);
  8.     Integer integer = userMapper.updateInfoByUid(user);
  9.     System.out.println(integer);
  10. }
复制代码
2.个人资料-业务层

2.1 异常规划

2.2 接口和抽象方法

主要有两个功能的模块,对应的是两个抽象的方法的设计。
  1. /**
  2. * 根据用户的uid查询用户数据
  3. * @param uid
  4. * @return 用户的数据
  5. */
  6. User getByUid(Integer uid);
  7. /**
  8. * 修改用户信息
  9. * @param session
  10. * @param user
  11. */
  12. void changeInfo(Session session,User user);
复制代码
2.3 实现抽象方法

在UserServiceImpl类中添加两个抽象方法的具体实现。
  1.         @Override
  2.     public User getByUid(Integer uid) {
  3.         User result = userMapper.findByUid(uid);
  4.         if (result==null||result.getIsDelete()==1){
  5.             throw new UsernameNotFoundException("用户数据不存在");
  6.         }else {
  7.             User user=new User();
  8.             user.setEmail(result.getEmail());
  9.             user.setPhone(result.getPhone());
  10.             user.setGender(result.getGender());
  11.             return user;
  12.         }
  13.     }
  14.     @Override
  15.     public void changeInfo(Integer uid,String username, User user) {
  16.         User result = userMapper.findByUid(uid);
  17.         if (result==null||result.getIsDelete()==1){
  18.             throw new UsernameNotFoundException("用户数据不存在");
  19.         }else {
  20.             user.setUid(uid);
  21.             user.setModifiedUser(username);
  22.             user.setModifiedTime(new Date());
  23.             Integer integer = userMapper.updateInfoByUid(user);
  24.             if (integer!=1){
  25.                 throw new UpdateException("更新数据时产生未知异常");
  26.             }
  27.         }
  28.     }
复制代码
2.4 接口测试
  1.         @Test
  2.         public void getByUid(){
  3.         System.out.println(userService.getByUid(11));
  4.     }
  5.     @Test
  6.     public void changeInfo(){
  7.         User user = new User();
  8.         user.setPhone("1234");
  9.         user.setEmail("56565@qq.com");
  10.         user.setGender(0);
  11.         userService.changeInfo(11,"denlu",user);
  12.     }
复制代码
3.个人资料-控制层

3.1 处理异常

3.2 设计请求

1.设置-打开页面就发送当前用户的查询。
  1. /users/get_by_uid
  2. GET
  3. HttpSession session
  4. JsonResult<User>
复制代码
2.点击修改按钮发送用户的数据修改操作请求的设计。
  1. /users/change_info
  2. POST
  3. User user,HttpSession session
  4. JsonResult<Void>
复制代码
3.3 处理请求

4. 个人资料-前端页面

1.在打开userdata.html页面自动发送ajax请求,查询到的数据填充到这个页面。
2.在检测到用户点击了修改按钮之后发送了一个ajax请求(change_info)。
上传头像

1.上传头像-持久层

1.1 SQL语句的规划

将对象文件保存在操作系统上,然后再把这个文件路径给记录下来,因为在记录路径的时候是非常的便捷和方便的。将来如果要打开这个文件,可以根据路径去找到这个文件。所以数据库里只要保存文件存储路径即可。将保存的静态资源(图片,文件,其他资源文件)放在某台电脑上,再把这台电脑作为一台单独的服务器使用。
对应是一个用户avatar的sql语句
  1. update t_user set avatar=?,modified_user=?,modified_time=? where uid=?
复制代码
1.2 设计接口和抽象方法

UserMapper接口中来定义这个给抽象方法用于修改用户头像
1.3 接口的映射

UserMapper.xml文件中编写映射的SQL语句。
2.上传头像-业务层

2.1 规划异常

1.用户数据不存在,找不到对应的用户的数据。
2.更新的时候,各种未知的异常产生。
2.2 设计接口和抽象方法
  1.         /**
  2.      * 根据uid修改头像
  3.      * @param uid 用户的id
  4.      * @param avatar 用户的头像路径
  5.      * @param username 用户名
  6.      */
  7.     void changeAvatar(Integer uid,String avatar,String username);
复制代码
2.3 实现抽象方法
  1. @Override
  2. public void changeAvatar(Integer uid, String avatar, String username) {
  3.     User user = userMapper.findByUid(uid);
  4.     if(user==null||user.getIsDelete().equals(1)){
  5.         throw new UsernameNotFoundException("用户数据不存在");
  6.     }
  7.     Integer integer = userMapper.updateAvatarByUid(uid, avatar, username, new Date());
  8.     if(integer!=1){
  9.         throw new UpdateException("更新用户头像产生未知的异常");
  10.     }
  11. }
复制代码
2.4 测试

3.上传头像-控制层

3.1 规划异常
  1. 父类:FileUploadException 泛指文件上传的异常
  2.     FileEmptyException 文件为空的异常
  3.     FileSizeException 文件大小超出限制
  4.     FileTypeException 文件类型异常
  5.     FileUploadIOException 文件读写的异常
  6.     FileStateException 文件状态异常
复制代码
五个构造方法显式声明出来,再去继承相关的父类
3.2 异常处理

在基类BaseController类中进行编写和统一处理。
  1. if (e instanceof FileEmptyException){
  2.     result.setState(6000);
  3.     result.setMessage("文件空异常");
  4. }else if (e instanceof FileSizeException){
  5.     result.setState(6001);
  6.     result.setMessage("文件大小异常");
  7. }else if (e instanceof FileTypeException){
  8.     result.setState(6002);
  9.     result.setMessage("文件类型异常");
  10. }else if (e instanceof FileStateException){
  11.     result.setState(6003);
  12.     result.setMessage("文件状态异常");
  13. }else if (e instanceof FileUploadException){
  14.     result.setState(6004);
  15.     result.setMessage("文件上传异常");
  16. }
复制代码
3.3 设计请求
  1. /users/change_avatar
  2. POST(GET请求提交数据2KB)
  3. HttpSession session,MutipartFile file
  4. JsonResult<String>
复制代码
3.4 实现请求
  1.     /**
  2.      * MultipartFile接口是SpringMVC提供的一个接口,这个接口为我们包装了获取文件类型的数据(任何类型的file都可以接收),SpringBoot整合了SpringMVC,只需要在处理请求的方法参数列表上声明一个参数类型的MutipartFile的参数,然后SpringBoot自动将传递给服务的文件数据赋值给这个参数
  3.      * @param session
  4.      * @param file
  5.      * @return
  6.      */
  7.     @RequestMapping("change_avatar")
  8.     public JsonResult<String> changeAvatar(HttpSession session, @RequestParam("file") MultipartFile file) {
  9.         if (file.isEmpty()){
  10.             throw new FileEmptyException("文件为空");
  11.         }
  12.         if (file.getSize()>AVATAR_MAX_SIZE){
  13.             throw new FileSizeException("文件超出限制");
  14.         }
  15.         if (!AVATAR_TYPE.contains(file.getContentType())){
  16.             throw new FileTypeException("文件类型不支持");
  17.         }
  18.         String parent=session.getServletContext().getRealPath("upload");
  19.         File dir = new File(parent);
  20.         if (!dir.exists()){
  21.             dir.mkdirs();//创建当前的目录
  22.         }
  23.         String originalFilename = file.getOriginalFilename();
  24.         int index = originalFilename.lastIndexOf(".");
  25.         String suffix=originalFilename.substring(index);//获取后缀(.XXX)
  26.         String filename = UUID.randomUUID().toString().toUpperCase()+suffix;//拼接随机生成的文件名和前面获取到的字符串
  27.         File dest = new File(dir, filename);//在存储路径下写入同名空文件,传回空文件
  28.         try {
  29.             file.transferTo(dest);//头像文件写入空文件中
  30.         } catch (FileStateException e) {
  31.             throw new FileStateException("文件状态异常");
  32.         } catch (IOException e) {
  33.             throw new FileUploadIOException("文件读写异常");
  34.         }
  35.         Integer uidFromSession = getUidFromSession(session);
  36.         String usernameFromSession = getUsernameFromSession(session);
  37.         String avatar="/upload/"+filename;
  38.         userService.changeAvatar(uidFromSession,avatar,usernameFromSession);
  39.         return new JsonResult<>(OK,avatar);
  40.     }
复制代码
4.上传头像-前端页面

在upload页面中编写上传头像代码
说明:如果直接使用表单进行文件的上传,需要给表单显示的添加一个属性enctype="multipart/form-data"声明出来,不会将目标文件的数据结构做修改再上传,不同于字符串
5.解决Bug

5.1 更改默认大小的限制

SpringMVC默认为1MB文件可以进行上传,需要手动修改SpringMVC默认上传文件的大小。
方式1:直接在配置文件中进行配置
  1. spring.servlet.multipart.max-file-size=10MB
复制代码
方式2:采用Java代码的形式来设置文件的上传大小的限制。主类中进行配置,可以定义一个方法,必须使用@Bean修,返回值为MutipartConfigElement。在类的前面添加一个@Configuration进行修饰
  1. @Bean
  2. MultipartConfigElement getMultipartConfigElement(){
  3.     MultipartConfigFactory multipartConfigFactory = new MultipartConfigFactory();
  4.     multipartConfigFactory.setMaxFileSize(DataSize.of(10, DataUnit.MEGABYTES));
  5.     return multipartConfigFactory.createMultipartConfig();
  6. }
复制代码
5.2 显示头像

在页面中通过ajax请求来t提交文件,提交完成后,后端返回json串,从中解析出data中的数据,设置到img头像标签的src属性上
  1. serialize()//将表单数据自动拼接成key=value的结构进行提交给服务器,一般提交是普通的控件类型中的数据(text\password\radio\checkbox)等等
  2. FormdData类//将表单中数据保持原有结构进行数据的提交
  3. --- new FormData($("#form")[0]);
  4. ajax默认处理数据时按照字符串的形式进行处理,以及默认会采用字符串的形式进行提交数据。所以要关闭这两个默认的功能。
  5. --- processData:false,
  6. --- contentType:false,
复制代码
5.3 登录后显示头像

更新头像成功后,将服务器返回的头像路径保存在客户端cookies对象中,然后每次检查用户打开上传头像页面,这个页面中通过ready()方法来自动检测去读取cookie中头像并设到src属性上。
1.设置cookie中的值
2.在upload页面中引入cookie.js文件
3.在upload.html页面中通过ready()自动读取cookie中的数据。
新增收货地址

1 新增收货地址-数据表的创建
  1. CREATE TABLE t_address (
  2.         aid INT AUTO_INCREMENT COMMENT '收货地址id',
  3.         uid INT COMMENT '归属的用户id',
  4.         name VARCHAR(20) COMMENT '收货人姓名',
  5.         province_name VARCHAR(15) COMMENT '省-名称',
  6.         province_code CHAR(6) COMMENT '省-行政代号',
  7.         city_name VARCHAR(15) COMMENT '市-名称',
  8.         city_code CHAR(6) COMMENT '市-行政代号',
  9.         area_name VARCHAR(15) COMMENT '区-名称',
  10.         area_code CHAR(6) COMMENT '区-行政代号',
  11.         zip CHAR(6) COMMENT '邮政编码',
  12.         address VARCHAR(50) COMMENT '详细地址',
  13.         phone VARCHAR(20) COMMENT '手机',
  14.         tel VARCHAR(20) COMMENT '固话',
  15.         tag VARCHAR(6) COMMENT '标签',
  16.         is_default INT COMMENT '是否默认:0-不默认,1-默认',
  17.         created_user VARCHAR(20) COMMENT '创建人',
  18.         created_time DATETIME COMMENT '创建时间',
  19.         modified_user VARCHAR(20) COMMENT '修改人',
  20.         modified_time DATETIME COMMENT '修改时间',
  21.         PRIMARY KEY (aid)
  22. ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
复制代码
2 新增收货地址-创建实体类
  1.         private Integer aid;
  2.     private Integer uid;
  3.     private String name;
  4.     private String provinceName;
  5.     private String provinceCode;
  6.     private String cityName;
  7.     private String cityCode;
  8.     private String areaName;
  9.     private String areaCode;
  10.     private String zip;
  11.     private String address;
  12.     private String phone;
  13.     private String tel;
  14.     private String tag;
  15.     private Integer isDefault;
复制代码
3 新增收货地址-持久层

3.1 各功能的开发顺序

当前收货地址功能模块:列表的展示,修改,删除,设置默认,新增收货地址。
开发顺序:新增-展示-设置默认-删除-修改
3.2 规划需要执行的SQL语句

1.新增-对应的是插入语句
  1. insert into t_address(除了aid外字段列表) values(字段值列表)
复制代码
2.一个用户的收货地址最多只能有20条数据对应。在插入用户数据前,先做查询操作。收货地址逻辑控制方面的一个异常。
  1. select count(*) t_address where uid=?
复制代码
3.3 接口和抽象方法

1.创建一个新的接口Address,在这个接口中来定义上面两个SQL语句抽象方法的定义。
  1.     /**
  2.      * 插入用户的收货地址数据
  3.      * @param address 收货地址数据
  4.      * @return 受影响的行数
  5.      */
  6.     Integer insert(Address address);
  7.     /**
  8.      * 根据用户的uid统计用户记录的收货地址数量
  9.      * @param uid 用户的uid
  10.      * @return 当前用户的收货地址总数
  11.      */
  12.     Integer countByUid(Integer uid);
复制代码
3.4 配置SQL映射

resource下的AddressMapper.xml
  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE mapper
  3.         PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  4.         "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  5. <mapper namespace="com.cy.store.mapper.AddressMapper">
  6.             <resultMap id="AddressEntityMap" type="com.cy.store.entity.Address">
  7.         <id column="aid" property="aid"/>
  8.         <result column="province_code" property="provinceCode"/>
  9.         <result column="province_name" property="provinceName"/>
  10.         <result column="city_code" property="cityCode"/>
  11.         <result column="city_name" property="cityName"/>
  12.         <result column="area_code" property="areaCode"/>
  13.         <result column="area_name" property="areaName"/>
  14.         <result column="is_default" property="isDefault"/>
  15.         <result column="created_user" property="createdUser"></result>
  16.         <result column="created_time" property="createdTime"></result>
  17.         <result column="modified_user" property="modifiedUser"></result>
  18.         <result column="modified_time" property="modifiedTime"></result>
  19.     </resultMap>
  20.     <insert id="insert" useGeneratedKeys="true" keyProperty="aid">
  21.         INSERT INTO t_address(uid, name, province_name, province_code, city_name, city_code,area_name, area_code, zip, address, phone, tel, tag, isDefault,created_user, created_time, modified_user, modified_time)
  22.         VALUES (#{uid},#{name},#{provinceName},#{provinceCode},#{cityName},#{cityCode},#{areaName},#{areaCode},#{zip},#{address},#{phone},#{tel},#{tag},#{isDefault},#{createdUser},#{createdTime},#{modifiedUser},#{modifiedTime})
  23.     </insert>
  24.     <select id="countByUid" resultType="java.lang.Integer">
  25.         SELECT count(*) from t_address WHERE uid=#{uid}
  26.     </select>
  27. </mapper>
复制代码
2.在test下的mapper文件夹下创建AddressMapperTests的测试类
4 新增收货地址-业务层

4.1 规划异常

如果用户是第一插入用户的收货地址,规则:当用户插入的地址是第一条,需要将当前地址作为默认收货地址,如果查询到统计总数为0,则将当前地址的is_default值设置为1。查询统计的结果为0不代表异常。查询结果大于20,这时需要抛出业务控制的异常AddressCountLimitException异常。自行创建这个异常。
4.2 接口和抽象方法

1.创建一个IAddressService接口,在接口中定义业务的抽象方法
4.3 实现抽象方法

5 新增收货地址-控制层


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4