网站电脑商城项目笔记

目录

注册

3.1规划需要执行的SQL语句

3.2设计接口和抽象方法

3.3编写映射

4.注册-业务层

4.1规划异常

4.2设计接口和抽象方法

5.注册控制层

5.1创建响应

5.2设计请求

5.3处理请求

5.4控制层优化设计

6.注册-前端页面


注册

持久层:通过Mybatis来操作数据库,在做mybatis开发流程

3.1规划需要执行的SQL语句

用户注册的功能,相当于在做数据的插入操作

insert into t_user (username,password) values(值列表)

用户注册时首先要去查询当前的用户名是否存在,如果存在则不能执行注册。相当于是一条查询语句

select * from t_user where username=?

3.2设计接口和抽象方法

1.编写Mapper接口,在项目的目录下首先创建一个mapper文件,在这个包下再根据不同功能模块来创建mapper接口,创建一个usermapper接口,要在接口定义这两个SQL语句抽象方法

/*用户模块的接口*/
//@Mapper
public interface UserMapper {
    /*
    *
    *插入用户的数据
    * 用户的数据
    * 返回受影响的行数(增删改查)根据返回值返回是否执行成功*/
    Integer insert(User user);

    /*
    * 根据用户名来查询用户数据
    * 用户名
    * 如果对应的用户返回这个用户的数据如果没有找到则返回null值*/
    User FindByname(String username);
}

2.在启动类配置Mapper接口文件上

@MapperScan(“com.liyang.store.mapper”)

3.3编写映射

1.定义XML文件,与对应的接口定义进行关联,所有的映射文件需要放置resource目录下,在这个目录下创建一个mapper文件夹,然后在这个文件夹存放Mapper的映射文件

2.创建接口对应的映射文件,遵循和接口的名称保持一致即可,创建一个UserMapper.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.liyang.store.mapper.UserMapper" >

</mapper>

3.配置接口中的方法对应上SQL文件语句上,需要借助标签来完成,insert\updata\delete\select对应的SQL语句操作

<!--namespace,用于指定当前的映射文件和那个接口进行映射,需要指定接口文件和路径,需要标注报的完整路径接口-->
<mapper namespace="com.liyang.store.mapper.UserMapper" >
    <!--自定义映射规则resultMap标签来完成映射规则文件的定义-->
    <!--id属性:标签给这个映射负责分配一个唯一的id值,对应的就是resultMap=“id属性的值”,属性的取值
         type属性:取值是一个类,表示的是数据库中的查询结果与java中的那个实体类进行结果的映射-->
    <resultMap id="UserEntityMap" type="com.liyang.store.entity.User">
        <!--将表的资源和类的属性不一致的字段进行匹配指定,名称一致的字段可以省略不写-->
        
        <!--配合完成名称不一致的映射:
            column属性:表示表中的资源名称
            property属性:表示类中的属性名称-->
        <!--在定义映射规则时主键是不可以省略的-->
        <id column="uid" property="uid"/>
        <result column="is_delete" property="isDelete"/>
        <result column="created_user" property="createdUser"/>
        <result column="created_time" property="createdTime"/>
        <result column="modified_user" property="modifiedUser"/>
        <result column="modified_time" property="modifiedTime"/>
    </resultMap>
    <!--id属性:表示映射的接口中的方法的名称,直接在标签的内容来编写SQL语句 解耦-->
    <!--useGeneratedKeys表示开启某个字段的值递增(主键)
         KeyProperty属性:标签将表的那个字段作为主键的递增-->
    <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语句在执行的时候,查询的结果是一个对象,多个对象-->
    <!--ResultType:表示查询的结果集类型,只需要指定对应映射的类型并且包含完整的包接口
        ResultMap:标签当表的资源和类的对象属性的字段名称不一致时,来自定义查询结果集的映射规则 -->
    <select id="findByUsername" resultType="UserEntityMap">
        SELECT * from t_user WHERE username=#{username}
    </select>
    
</mapper>

3.将mapper文件的位置注册到properties对应的配置文件中

mybatis.mapper-locations=classpath:mapper/*.xml

3.单元测试,每个独立的层编写完成后需要编写单元测试方法,来测试当前的功能。在test包下结构下


@SpringBootTest//表示标注当前的类是一个测试类,不会随同项目一块打包
@RunWith(SpringRunner.class)//表示启动这个单元测试类,需要传递一个参数必须是SpringRunner的实例类型
public class UserMapperTests {
    //idea有检测的功能,接口是不能狗直接创建bean的(动态代理技术来解决)
    @Autowired
    private UserMapper userMapper;
    /*单元测试方法:就可以单独独立运行,不用启动整个项目,可以做单元测试,提升了测试效率
    *1.必须被Test注解修饰,
    * 2.返回类型必须是void
    * 3.方法的参数列表不指定任何类型
    * 4.方法返回修饰符必须是public
    * */
    @Test
    public void insert(){
        User user = new User();
        user.setUsername("tim");
        user.setPassword("1234");
        Integer insert = userMapper.insert(user);
        System.out.println(insert);
    }

    @Test
    public void findByUsername(){
        User tim = userMapper.findByUsername("tim");
        System.out.println(tim);
    }
}

4.注册-业务层

4.1规划异常

1RuntimeException异常。作为异常的子类,然后再去执行定义具体的异常类型来继承这个异常 ,业务层异常基类,ServiceException,这个异常继承RuntimeException。

/*业务层异常的基类:throws new ServiceException("业务层抛出异常")*/
public 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);
    }
}

根据业务层不同的功能来详细定义具体的异常类型,统一的去继承ServiceException异常类

2.用户在进行注册时候可能会产生用户名被占用的错误,抛出一个异常:UsernamDepuiltedException异常

package com.liyang.store.service.ex;

public class UsernameDuplicatedException extends ServiceException {
    public UsernameDuplicatedException() {
        super();
    }

    public UsernameDuplicatedException(String message) {
        super(message);
    }

    public UsernameDuplicatedException(String message, Throwable cause) {
        super(message, cause);
    }

    public UsernameDuplicatedException(Throwable cause) {
        super(cause);
    }

    protected UsernameDuplicatedException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }
}

3.正在执行数据插入操作的时候,服务器,数据库宕机执行插入的过程中所产生的异常insertException

package com.liyang.store.service.ex;


/*数据在插入过程中产生的异常*/
public class InsertException extends ServiceException {

    public InsertException() {
        super();
    }

    public InsertException(String message) {
        super(message);
    }

    public InsertException(String message, Throwable cause) {
        super(message, cause);
    }

    public InsertException(Throwable cause) {
        super(cause);
    }

    protected InsertException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }
}

4.2设计接口和抽象方法

在service包下创建一个IUserService接口

package com.liyang.store.service;

import com.liyang.store.entity.User;

/*用户模块用户接口*/
public interface IUserService {
    /*
    * 用户注册方法
    * */
    void reg(User user);
}

2.创建一个实现类UserServiceImpl类需要实现这个接口,并且实现抽象的方法

package com.liyang.store.service.impl;

import com.liyang.store.entity.User;
import com.liyang.store.mapper.UserMapper;
import com.liyang.store.service.IUserService;
import com.liyang.store.service.ex.InsertException;
import com.liyang.store.service.ex.UsernameDuplicatedException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;

import javax.xml.crypto.Data;
import java.util.Date;
import java.util.UUID;

/*用户模块业务层的实现类*/
@Service //@Service注解:将当前类的对象交给Spring来管理,自动创建对象以及对象的维护
public class UserServiceImpl implements IUserService {
    @Autowired
    private UserMapper userMapper;

    @Override
    public void reg(User user) {
        //通过user参数来获取传递过来的参数
        String username = user.getUsername();
        //调用findByuserName(username) 判断用户是否被注册过
        User result = userMapper.findByUsername(username);
        //判断结果集是否为null,抛出用户名被占用的异常
        if(result!=null){
            throw new UsernameDuplicatedException("用户名被占用");
        }
        //密码加密处理的实现:md5
        String oldpassword = user.getPassword();
        //获取颜值 (随机生成一个颜值)
        String salt = UUID.randomUUID().toString().toUpperCase();

        user.setSalt(salt);//补全颜值的数据
        //密码和颜值作为一个整体进行加密处理 忽略了原有密码的强度提升了数据的安全性
        String md5Password = getMD5Password(oldpassword, salt);
        //将加密之后的密码重新补全到user对象中
        user.setPassword(md5Password);

        //补全数据:is_delete设置为零
        user.setCreatedUser(user.getUsername());
        user.setModifiedUser(user.getUsername());
        Date date=new Date();
        user.setCreatedTime(date);
        user.setModifiedTime(date);

        //执行注册业务功能的实现
        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++) {
            //md5加密算法方法的调用
            password=DigestUtils.md5DigestAsHex((salt+password+salt).getBytes()).toUpperCase();
        }
        //返回加密之后的密码
        return password;
    }
}

3.在单元测试包创建一个UserServiceTests类,在这个类添加单元测试类的功能

package com.liyang.store.service;


import com.liyang.store.entity.User;
import com.liyang.store.mapper.UserMapper;
import com.liyang.store.service.ex.ServiceException;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import javax.sql.rowset.serial.SerialException;

@SpringBootTest//表示标注当前的类是一个测试类,不会随同项目一块打包
@RunWith(SpringRunner.class)//表示启动这个单元测试类,需要传递一个参数必须是SpringRunner的实例类型
public class UserServiceTests {
    //idea有检测的功能,接口是不能狗直接创建bean的(动态代理技术来解决)
    @Autowired
    private IUserService userService;
    /*单元测试方法:就可以单独独立运行,不用启动整个项目,可以做单元测试,提升了测试效率
    *1.必须被Test注解修饰,
    * 2.返回类型必须是void
    * 3.方法的参数列表不指定任何类型
    * 4.方法返回修饰符必须是public
    * */
    @Test
    public void reg(){
        try {
            User user = new User();
            user.setUsername("liyang02");
            user.setPassword("1234");
            userService.reg(user);
            System.out.println("ok");
        } catch (ServiceException e) {
            //获取类的对象,获取类的名称
            System.out.println(e.getClass().getSimpleName());
            //获得异常的具体描述信息
            System.out.println(e.getMessage());
        }
    }


}

5.注册控制层

5.1创建响应

状态码,状态描述信息、数据。这部分功能封装一个类中,将这类作为方法返回值

package com.liyang.store.util;

import java.io.Serializable;

/*Json格式的数据进行响应。
* */
public class JsonResult<E> implements Serializable {
    private Integer state;//状态码
    private String message;//描述信息
    private E data;

    public JsonResult() {
    }

    public JsonResult(Integer state) {
        this.state = state;
    }

    public JsonResult(String message) {
        this.message = message;
    }

    public JsonResult(Integer state, E data) {
        this.state = state;
        this.data = data;
    }

    public Integer getState() {
        return state;
    }

    public void setState(Integer state) {
        this.state = state;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public E getData() {
        return data;
    }

    public void setData(E data) {
        this.data = data;
    }
}

5.2设计请求

依照当前的业务功能模块进行请求的设计

请求路径:/user/reg

请求参数:User user

请求类型:Post

响应结果:JsonResult<void>

5.3处理请求

1.创建一个控制层对应的类UserController类,依赖于业务层的接口

package com.liyang.store.controller;


import com.liyang.store.entity.User;
import com.liyang.store.service.IUserService;
import com.liyang.store.service.ex.InsertException;
import com.liyang.store.service.ex.UsernameDuplicatedException;
import com.liyang.store.util.JsonResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController //@Controller + @ResponseBody
@RequestMapping("users")
public class UserController {
    @Autowired
    private IUserService userService;
    
    public JsonResult<Void> reg(User user){
        //创建响应结果对象
        JsonResult<Void> result = new JsonResult<>();
        try {
            userService.reg(user);
            result.setState(200);
            result.setMessage("用户注册成功");
        } catch (UsernameDuplicatedException e) {
          result.setState(4000);
          result.setMessage("用户名被占用");
        }catch (InsertException e) {
            result.setState(5000);
            result.setMessage("注册时产生未知的异常");
        }
        return result;
    }
    
    
}

5.4控制层优化设计

在控制层抽离出来一个父类,在这个父类中统一的去处理关于异常的相关操作。编写BaseController类,统一处理异常@ExceptionHandler注解实现了对服务层抛出的异常进行统一处理,提高了代码的健壮性和可维护性。

package com.liyang.store.controller;

import com.liyang.store.service.ex.InsertException;
import com.liyang.store.service.ex.ServiceException;
import com.liyang.store.service.ex.UsernameDuplicatedException;
import com.liyang.store.util.JsonResult;
import org.springframework.web.bind.annotation.ExceptionHandler;

/*控制层的基类*/
public class BaseController {

    public static final int OK=200;//操作成功状态码

    //请求处理方法,这个方法的返回值就是需要传递给前端的数据
    //自动将异常对象传递给此方法的参数列表上
    //当前项目中产生了异常,被统一拦截到此方法中,这个 方法此时就充当的是请求处理的方法,方法的返回值直接给到前端
    @ExceptionHandler(ServiceException.class) //用于统一处理抛出的异常
    public JsonResult<Void> handleException(Throwable e){
        JsonResult<Void> result=new JsonResult<>(e.getMessage());
        if(e instanceof UsernameDuplicatedException){
            result.setState(4000);
            result.setMessage("用户名已被占用");
        }else if(e instanceof InsertException){
            result.setState(5000);
            result.setMessage("注册时产生未知的异常");
        }
        return result;
    }
}

localhost:8080/users/reg?username=liyangggee&password=123456测试端口地址

 重新构造了reg()方法

  @RequestMapping("reg")
    public JsonResult<Void> reg(User user){
        //创建响应结果对象
       userService.reg(user);
       return new JsonResult<>(OK);
    }

6.注册-前端页面

1.在register页面中编写发送请求方法,点击事件完成。先选中对应的按钮($(“选择器”)),再去添加点击的事件,$ajax()发送异步请求

2.JQuery封装了一个函数,称之为$.ajax()函数,通过对象调用ajax()函数,可以异步加载相关请求,依靠的是javascript提供的一个XHR(XMLhttpResonse)

3.ajax()使用方式,需要传递一个方法作为的参数来使用,一对大括号称之为方法体。ajax接收多个参数语法结构,参数和参数之间要求使用 ,分割  每一组参数之间使用:进行语法分割 参数的组成部分是参数的名称(不能随便定义),是参数的值,参数的值要求是用字符串来标识。参数的声明顺序没有要求

语法结构:

$.ajax({

         url:" ",

          type:"",

          data:"",

          dataType:" ",

          success:function(){   

           } ,

          error:function(){ 

           }

     })

4.ajax()函数参数含义

参数功能描述
url表示请求的地址:url:"local:8080/user/reg?"
type请求类型(get和post请求类型)列如:type:"post"
data向指定的请求url地址提交的数据,data:“username=tom&pwd=123”
dataType提交的数据的类型,数据的类型一般指定为json类型。dataType:"json"
success当服务器正常响应客户端时,会自动调用success参数的方法,并将服务器返回的数据以参数的形式传递给这个参数上
error当服务器正常响应客户端时,会自动调用error参数的方法,并将服务器返回的数据以参数的形式传递给这个参数上

5.js代码可以独立存放在一个js的文件里或者声明在一个script标签中。

6.js代码无法正常被服务器解析执行,体现在点击页面的中的按钮没有任何的响应。解决方案:

  在项目的Maven下clear清理项目--install中

  在项目的fille选项-cash清理缓存

重新去构建项目:build选项下rebuild选项

重启idea

用户登录

当用户输入用户名和密码将数据提交给后台数据库进行查询,如果存在对应的用户名和密码则表示登录成功,登录成功之后跳转到主页就是index.html跳转在前端使用jquery来完成

1.登录-持久层(查询功能)

1.1规划需要执行的sql语句

一句用户提交的用户名和密码做select查询。密码比较在业务层

select * from where username=? and password=?

说明:如果在分析过程中发现某个功能模块已经被开发完成,所以就可以省略当前的开发步骤

分析的过程不能省略。

1.2接口设计和方法

   不用重复开发,单元测试也是无需单独执行了

2.登录-业务层

2.1异常规划

用户名对应的密码错误,密码匹配失败的异常:PasswordNotMatchException运行时异常。

2.用户名没有被找到,抛出异常:UsernameNotException

3.异常编写:

业务层异常需要继承ServiceException异常类

在具体的异常类中定义构造方法(可以使用快捷键生成,有五个构造方法)。

2.2设计业务接口层接口和抽象方法

1.直接在IUservice接口中编写抽象方法 login(String username,String Password)。

将当前登录成功的用户数据以当前用户对象的形式进行返回,状态管理:可以将数据保存在Session中和Cookie中,可以避免重复度很高的数据多次频繁操作数据进行获取(用户名、用户id-存放在session中,用户头像存在cookie当中)

2.需要实现中实现父接口的抽象方法。

3.在测试类中测试业务层类中实现父接口中的抽象方法

4.如果一个类没有手动创建直接将这个类赋值到项目,idea找不到这个类。之前缓存导致不能够正常找到这类的符号。重新构建项目,

2.3抽象方法的实现

3.登录-控制层

3.1处理异常

业务层抛出的异常是什么,需要在统一异常处理类中进行统一的捕获和处理,如果也曾抛出的异常类型已经在异常处理过,则不需要重复添加

else if(e instanceof UserNotFoundException){
    result.setState(5001);
    result.setMessage("用户数据不存在的异常");
}else if(e instanceof PasswordNotMatchException){
    result.setState(5002);
    result.setMessage("用户名的密码错误的异常");
}

3.2设计请求

请求路径:/user/login

请求方式:POST

请求数据:String username,Sring Password ,HttpSession session

响应结果:JsonResolut<User>

3.3处理请求

在UserController类中编写处理请求的方法

@RequestMapping("login")
public JsonResult<User> login(String username,String password){
    User data = userService.login(username, password);
    return new JsonResult<User>(OK,data);
}

4.登录-前端页面

1.在login.html页面中依据前面所设置的请求来发送ajax请求。

<script type="text/javascript">
    $("#btn-login").click(function () {
        $.ajax({
            url:"/users/login",
            type:"POST",
            data:$("#form-login").serialize(),
            dataType:"JSON",
            success:function (json) {
                if(json.state==200){
                    alert("登录成功");
                    //跳转到主页对象
                    //相对路径来确定跳转页面
                    location.href="index.html";

                }else{
                    alert("登录失败")
                }
            },
            error:function (xhr) {
                alert("登录产生了未知的异常"+xhr.message)
            }
        });
    })

</script>

用户会话session

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

1.封装session对象中数据的获取(封装到父类中)数据的设置(当用户登录成功进行数据的设置,设置到全局的session)

2.在父类中封装两个数据:获取uid和获取username对应的两个方法。用户头像暂时不考虑,将来封装cookie中

   protected final Integer getuidFromSession(HttpSession session){
        return  Integer.valueOf(session.getAttribute("uid").toString());
    }
    
    /*
    * 获取当前登录用户的username
    * Session session
    * 在实现类中重写父类中的toString,不是句柄信息的输出
    * */
    protected final String  getUsernameFromSession(HttpSession session){
        return session.getAttribute("username").toString();
    }
}

3.在登录的方法中将数据封装在session对象中,服务本身自动创建有session对象,已经是一个全局的session对象。springboot直接使用session对象,直接将Http类型的对象作为请求处理方法,会自动将全局的session对象注入到请求处理方法的session形参上

拦截器

拦截器:首先将所有的请求统一拦截到拦截器中,可以在拦截器中来定义过滤的规则,如果不满足系统的设置的过滤规则,统一的处理是重新去打开login.html页面(重定向和转发)推荐使用重定向。在SpringBoot项目中拦截器的定义和使用。springboot是依靠springMVC来完成的。SpringMVC提供了一个接口,HandlerIntercepter接口,用于表示定义一个拦截器,受限自定义一个拦截器,受限自定义各类,在这个类实现这个接口

1.首先自定义一个类,在这个类实现这个HandlerIntercepter接口

public interface HandlerInterceptor {
    //在调用所有处理请求的方法之前被自动调用执行的方法
    default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        return true;
    }
    //请求之后login在ModelAndView对象返回之后调用的方法

    default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
    }

//在整个请求所有关联的资源被执行完毕最后所执行的方法
    default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
    }
}

2.注册过滤器:添加白名单(那些资源可以在不登录的情况下访问:login.html\register.html\login.html\index.html\product.html),添加黑名单(在用户登录的情况下才可以登录页面资源)。

3.注册过滤器的技术:借助WebMvcConfigure接口,可以将用户定义的拦截器进行注册,才可以保证拦截器能够生效和使用。定义一个类,然后让这个类实现WebMvcConfigure接口。配置信息,建议存放在项的config包架构下。

/*
* 处理器拦截器的注册 实现白名单和黑名单
* */
@Configuration //加载拦截器
public class LoginInterceptorConfigurer implements WebMvcConfigurer {
    /*
    * 配置拦截器
    * */


    @Override
    public void addInterceptors(InterceptorRegistry registry) {

        //创建自定义的拦截器对象
        HandlerInterceptor interceptor=new LoginInterceptor();
        // 白名单

        List<String> i=new ArrayList<>();
        i.add("/bootStrap3/**");
        i.add("/css/**");
        i.add("images/**");
        i.add("/js/**");
        i.add("/web/register.html");
        i.add("/web/login.html");
        i.add("/web/index.html");
        i.add("/web/product.html");
        i.add("/users/reg");
        i.add("/users/login.html");
        //完成拦截器的注册
        registry.addInterceptor(interceptor)
                .addPathPatterns("/**")
        .excludePathPatterns(i);//表示要拦截的url是什么内容

    }
}

修改密码

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

1.修改面-持久层Mapper

1.1规划需要执行的执行的SQL语句 根据用户uid修改用户的password

updata t_user set password=?,modified=?,modified_time where uid=?

根据uid查询用户的数据,在修改密码之前,首先要保证当前用户的数据存在,检测标记为已经删除,检测输入的原始密码是否正确

select * from t_user where uid=?

1.2设计接口和抽象方法

UseMapper接口,将以上的两个方法抽象出来,将定义的映射到sql语句上。

1.3SQL映射 ,配置到映射文件中,配置到UserMapper.xml文件中。

    <update id="updataPasswordByUid">
        update t_user set password=#{password},
        modified_user=#{modifiedUser},
        modified_Time=#{modifiedTime} where uid=${uid}
    </update>

    <select id="findByUid" resultMap="UserEntityMap">
        select * from t_user where uid=#{uid}
    </select>

做单元测试

   @Test
    public void updataPasswordByUid(){
                userMapper.updataPasswordByUid(78,"1234","管理员",new Date());
    }
    @Test
    public void findByUid(){
        System.out.println(userMapper.findByUid(78));
    }

2.修改面-业务层

2.需要规划异常

2.1.用户的原密码异常、is_delete=1、uid找不到在用户发现的异常

2.2.updata在更新的时候、有可能未知的异常,UpdateException。

2.2设计接口和抽象方法

执行用户修改密码的核心方法

void changePassword(Integer uid,String username,String oldPassword,String newPassword);

在实现类中实现当前的抽象方法。

@Override
    public void changePassword(Integer uid, String username, String oldPassword, String newPassword) {
        User result=userMapper.findByUid(uid);
        if(result==null || result.getIsDelete()==1){
            throw new UserNotFoundException("用户数据不存在");
        }
        //原始密码和数据库中密码进行比较
        String oldMd5Password=getMD5Password(oldPassword,result.getSalt());
        
        if(!result.getPassword().equals(oldMd5Password)){
            throw new PasswordNotMatchException("密码错误");
        }
        //将新的密码设置到数据库中,将新的密码进行加密再去更新
        String newMd5Password=getMD5Password(newPassword,result.getSalt());
        Integer rows = userMapper.updataPasswordByUid(uid, newPassword, username, new Date());
        
        if(rows!=1){
            throw new UpdateException("更新数据产生了未知的异常")
        }
    }

在单元测试测试类中编写测试方法

3.修改面-控制层

3.1处理异常

UpdateException需要配置到统一的处理方法中。

else if(e instanceof UpdateException){
    result.setState(5001);
    result.setMessage("更新数据时产生了未知的异常");
}

3.2设计请求

/users/change_password

post

String oldPassword,String newPassword//需要和表单中的name 属性保持一致

JsonResult<void>  

3.3处理请求

@RequestMapping("change_password")
    public JsonResult<Void> changePassword(String oldPassword,
                                           String newPassword,
                                           HttpSession session) {
        Integer uid = getuidFromSession(session);
        String username = getUsernameFromSession(session);
        userService.changePassword(uid, username, oldPassword, newPassword);
        return new JsonResult<Void>(OK);
    }

4.修改密码-前端密码

password.html中添加ajax请求的处理,不在手动去编写ajax结构,直接

个人资料

1.个人资料 持久层

1.1需要规划sql语句

1.根据用户信息的SQL语句

updata t_user set  phone=?,email=?,gender=?,modified_user=?,modifed_time=?  where uid=?

2.根据用户名查询用户的数据

select * from t_user where uid=?

查询用户的数据不需要再重复开发。

1.2接口与抽象方法

更新用户信息的方法

1.3抽象方法的映射

<update id="updateInfoByUid">
        update t_user set 
        
        <if test="phone!=null">phone=#{phone}, </if>
        <if test="email!=null">email=#{email},</if>
        <if test="gender!=null">gender=#{gender},</if>
         modified_user=#{modifiedUser},
        modified_Time=#{modifiedTime}
        where uid=${uid}
    </update>

在测试类中测试功能

   @Test
    public void updateInfoVyUid(){
        User user = new User();
        user.setUid(21);
        user.setPhone("155111000");
        user.setEmail("zigongliyang@gmail.com");
        user.setGender(1);
        userMapper.updateInfoByUid(user);
    }

2.个人资料-业务层

2.1异常规划

1.设计两个功能

  • 当打开页面时获取页面信息并且填充到对应的文本框中,
  • 检测用户是否修改按钮,如果检测到则执行修改用户信息的操作

2.打开页面的时候可能找不到用户的数据,点击删除按钮之前需要再次的去检测用户数据是否存在

2.2接口和抽象的方法

主要有两个功能的模块,对应的是两个抽象的方法设计

/*根据用户id查询
* */
User getByUid(Integer uid);

void  changInfo(Integer uid,String username,User user);

在UserSerivceImpl类中添加两个抽象方法的具体实现,

3.个人资料-控制层

3.1处理异常

暂无

3.2一点击就发送当前用户数据的查询

/users/get_by_uid

GET

HttpSession session

JsonResult<User>

2.点击修改按钮发送用户的数据修改操作请求的设计

/users/chang_info

post

User user, httpsession

JsonResult<void>

3.3处理请求

@RequestMapping("get_by_uid")
    public JsonResult<User> getByUid(HttpSession session) {
        // 从HttpSession对象中获取uid
        Integer uid = getuidFromSession(session);
        // 调用业务对象执行获取数据
        User data = userService.getByUid(uid);
        // 响应成功和数据
        return new JsonResult<User>(OK, data);
    }

    @RequestMapping("change_info")
    public JsonResult<Void> changInfo(User user,HttpSession session){
        //user 对象有四个部分的数据 username phone email gender
        //uid 需要再次封装到user对象中
        Integer uid = getuidFromSession(session);
        String username = getUsernameFromSession(session);

        userService.changInfo(uid,username,user);
        return new JsonResult<>(OK);
    }

4.个人资料 前端页面

1.在打开userdata.html页面自动发送ajax请求(get_by_uid),查询到的数据填充到这个页面。

2.检测到用户点击了修改 按钮之后发送一个ajax请求

上传图片

1.上传图片-持久层

1.1SQL语句的规划,将对象的文件保存在操作系统上,然后把这个文件路径给纪录在,因为在纪录路径的是非常便捷和方便,将如果要打开这个文件可以依据这个路径找到这个文件。在数据库中只需要保存这个文件路径即可,将左右的静态资源放到某台电脑上,再把这台电脑作为服务器使用,对应的是更新avatar字段的sql语句

updata t_user set avatar=?,modified_user=?,modified_time=? where uid=?

1.2设计接口和抽象方法来定义抽象方法用于修改用户的头像

 /*修改用户头像
    * @Param(SQL映射文件中#{}占位符变量名,解决的问题,当SQL语句的占位符和
    * 需要将某个参数强制注入到某个占位符变量时,可以使用@Param这个注解来标注映射的*/

    Integer updateAvatarByUid(@Param("uid") Integer uid,
                              @Param("avatar") String avatar,
                              @Param("modifiedUser") String modifiedUser,
                              @Param("modifiedTime") Date modifiedTime);

 1.3接口映射

UserMapper.xml文件中编写映射的SQL语句

<update id="updateAvatarByUid">
        update t_user set avatar=#{avatar},
         modified_user=#{modifiedUser},
        modified_Time=#{modifiedTime}
        where uid=#{uid}
    </update>

1.4在测试类中测试方法

@Test
    public void updateAvatarByUid(){
        userMapper.updateAvatarByUid(6,
                "/update/avatar.png",
                "管理员",new Date());
    }

2.上传图片-业务层

2.1规划异常

1.用户数据不存在,找不到对应的用户数据。

2.更新的时候,未知异常产生。

(无需开发)

2.2设计接口和抽象方法

/**
     * 修改用户的头像
     * @param uid 用户id
     * @param avatar 用户头像id
     * @param username 用户的名称
     */
    void changeAvatar(Integer uid,String avatar,String username);

2.3实现抽象方法

编写业务层的更新用户头像的方法

@Override
    public void changeAvatar(Integer uid, String avatar, String username) {
        //查询当前用户数据是否存在
        User result = userMapper.findByUid(uid);
        if(result==null || result.getIsDelete().equals(1)){
            throw new UserNotFoundException("用户不存在");
        }
       Integer rows=userMapper.updateAvatarByUid(uid,avatar,username,new Date());
        if(rows!=1){
            throw new UpdateException("更新用户头像产生未知的异常");
        }
    }

 测试业务层的方法执行。

 @Test
    public void changeAvatar(){
        userService.changeAvatar(21,"/upload/test.png","xiaoming");
    }

3.上传图片-控制层

3.1规划异常

FileUploadException 泛指文件上传的异常 文件上传的异类 继承RunTimeException

FileEmtyException文件为空的异常

FileSizeException 文件大小超出限制

FileTypeException文件类型异常

FileUploadException文件读写异常

五个构造方法显示的声明出来,再去继承相关的父类

else if (e instanceof FileEmptyException) {
            result.setState(6000);
        } else if (e instanceof FileSizeException) {
            result.setState(6001);
        } else if (e instanceof FileTypeException) {
            result.setState(6002);
        } else if (e instanceof FileStateException) {
            result.setState(6003);
        } else if (e instanceof FileUploadIOException) {
            result.setState(6004);
        }

在异常统一处理方法的参数列表上增加新的异常处理作为他的参数

@ExceptionHandler({ServiceException.class,FileUploadException.class})

 3.3设计请求

/users/change_avatar

POST 2kb

HttpSession session,MutipartFile file .

JsonResoult<String>

实现请求

public JsonResoult<String>  changeAvatar(){}

@RequestMapping("chang_avatar")
    public JsonResult<String> changeAvatar(HttpSession session,
                                           @RequestParam("file") MultipartFile file){
        //判断文件是否为空
        if(file.isEmpty()){
            throw new FileEmptyException("文件为空");
        }
        if(file.getSize()>AVATAR_MAX_SIZE){
            throw new FileSizeException("文件超出限制");
        }
        //判断文件的类型是否是我们规定的和后缀类型
        String contentType=file.getContentType();
        //如果集合包含某个元素则返回值true
        if(!AVATAR_TYPE.contains(contentType)){
            throw new FileTypeException("文件不支持");
        }
        //上传的文件 //upload/文件.png
       String parent= session.getServletContext().getRealPath("upload");
        //File对象指向这个路径 File是否存在
        File dir=new File(parent);

        if (!dir.exists()){
            dir.mkdirs();//创建目录
        }
        //获取到这个文件名称,UUID工具生成一个新的字符串作为文件名
        String originalFilename=file.getOriginalFilename();
        System.out.println(originalFilename);
        int index = originalFilename.lastIndexOf(".");
        String suffix=originalFilename.substring(index);

        String filename=UUID.randomUUID().toString().toUpperCase()+suffix;
        
        File dest=new File(dir,filename);// 空文件
        //参数file中数据写入到这个空文件中
        try {
            file.transferTo(dest);//将file文件中的数据写入到dest文件中
        } catch (FileStateException e){
            throw new FileStateException("文件状态异常");
        }
        catch (IOException e) {
            throw new FileUploadIOException("文件读写异常");
        }
        Integer uid=getuidFromSession(session);
        String username=getUsernameFromSession(session);
        //返回头像的路径/upload/test.png
        String avatar="/upload"+filename;
        userService.changeAvatar(uid,avatar,username);
        //返回用户头像路径给前端页面,将来用于头像使用
        return new JsonResult<>(OK,avatar);
    }

4.上传图像-前端页面

在upload页面编写上传头像的代码

说明:如果直接使用表单进行文件上传,需要给表单显示的添加一个属性enctype=“multipart/form-data”声明出来,不会将目标的数据结构做修改在上传

5.解决BUG

5.1更改默认的大小限制

SpringMVC默认为1mb文件可以进行上传,手动的去修改SpringMVC默认上传文件的大小。

方式1:直接可以在配置文件中进行配置

spring.servlet.multipart.max-request-size=15MB
spring.servlet.multipart.max-file-size=10MB

方式2:需要采用Java代码形式来设置文件的上传大小的限制。主类进行配置,可以定义一个方法,必须用@Bean修饰。在类的前面添加一个@Configration注解进行修改类 mutipartConfigElement

@Bean
public MultipartConfigElement getMultipartConfigElement() {
    MultipartConfigFactory factory=new MultipartConfigFactory();
    //设置需要创建的对象的相关信息
    factory.setMaxFileSize(DataSize.of(10, DataUnit.MEGABYTES));
    factory.setMaxRequestSize(DataSize.of(15,DataUnit.MEGABYTES));
    //通过工厂类创建对象 MultipartConfigElement 对象
    return factory.createMultipartConfig();
}

5.2显示头像

在页面中通过ajax请求来提交文件,提交完成后返回json串,解析出data中数据,设置到img头像

img头像标签的src属性上就可以了,

serialize(),可以将表单数据自动拼接成key=Vlaue的结构进行提交给服务器,一般提交是普通的控件类型的数据等等

FormData类:将表单的数据保持原有的结构进行数据的条件。newFormData(),

ajax默认处理数据时按照字符串

5.3登录后显示头像

可以更新头像成功后,将服务器返回的头像路径保存在客户端cookies对象中,然后每次检测到用户打开上传头像页面 ,在这个页面中通过ready方法来自动检测去读取cookies中头像并设置

新增收货地址

新增收货地址-持久层

2.新建收货地址

3.1各功能的开发顺序

当前收货地址功能模块:列表展示,修改删除、新增收货地址。开发顺序:新增收货地址-列表展示-设置默认收货地址-删除收货地址-修改收货地址

3.2需要执行的sql语句

1.对应是插入

insert into t_address (除了aid外字段列表) values (字段值列表)

2.一个用户的收货地址规定最多只能有20条数据对应,在插入用户数据之前先做查询操作,大于20条抛出异常

select count(*)  t_address where uid=?

3.3接口与抽象方法

public interface AddressMapper {
    /**
     * 插入用户的收货地址
     * @param address 收货地址数据
     * @return  受影响的行数
     */
    Integer insert(Address address);

    /**
     * 根据用户id统计收货地址数量
     * @param uid
     * @return
     */
    Integer countByUid(Integer uid);
}

3.4完成SQL映射

<?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.liyang.store.mapper.AddressMapper" >
    <resultMap id="AddressEntityMap" type="com.liyang.store.entity.Address">
        <id column="aid" property="aid"/>
        <result column="province_code" property="provinceCode"/>
        <result column="province_name" property="provinceName"/>
        <result column="city_code" property="cityCode"/>
        <result column="city_name" property="cityName"/>
        <result column="area_code" property="areaCode"/>
        <result column="area_name" property="areaName"/>
        <result column="is_default" property="isDefault"/>
        <result column="created_user" property="createdUser"/>
        <result column="created_time" property="createdTime"/>
        <result column="modified_user" property="modifiedUser"/>
        <result column="modified_time" property="modifiedTime"/>
    </resultMap>

    <!-- 插入收货地址数据:Integer insert(Address address) -->
    <insert id="insert" useGeneratedKeys="true" keyProperty="aid">
        INSERT INTO t_address (
        uid, name, province_name, province_code, city_name, city_code, area_name, area_code, zip,
        address, phone, tel,tag, is_default, created_user, created_time, modified_user, modified_time
        ) VALUES (
        #{uid}, #{name}, #{provinceName}, #{provinceCode}, #{cityName}, #{cityCode}, #{areaName},
        #{areaCode}, #{zip}, #{address}, #{phone}, #{tel}, #{tag}, #{isDefault}, #{createdUser},
        #{createdTime}, #{modifiedUser}, #{modifiedTime}
        )
    </insert>
    
    <select id="countByUid" resultType="java.lang.Integer">
        select count(*) t_address where uid=#{uid}
    </select>

</mapper>

4增收货地址-业务层

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

package com.liyang.store.service.ex;


/*收货地址总数大于20条异常*/
public class AddressCountLimitException extends ServiceException{

    public AddressCountLimitException() {
        super();
    }

    public AddressCountLimitException(String message) {
        super(message);
    }

    public AddressCountLimitException(String message, Throwable cause) {
        super(message, cause);
    }

    public AddressCountLimitException(Throwable cause) {
        super(cause);
    }

    protected AddressCountLimitException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }
}

4.2接口与抽象方法

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

public interface IAddressService {
   void addNewAddress(Integer uid, String username, Address address);
}

2.创建一个AddressServiceImpl实现类,去实现抽象方法

#Spring读取配置文件中的数据:@Values("${user.address.max-count}")
user.address.max-count=20
/*新增收货地址的实现类*/
public class AddressSericeImpl implements IAddressService {

    @Autowired
    private AddressMapper addressMapper;

    @Value("${user.address.max-count}")
    private Integer maxCount;

    @Override
    public void addNewAddress(Integer uid, String username, Address address) {
        //调用收货地址统计的方法
       Integer count=addressMapper.countByUid(uid);
       if(count>=maxCount){
           throw new AddressCountLimitException("用户收货地址超出上限");
       }
       //uid is_delete、
        address.setUid(uid);
        Integer isDefault= count==0?1:0; //1表示 默认,0表示不是默认
        address.setIsDefault(isDefault);
        //补全4项日志
        address.setCreatedUser(username);
        address.setModifiedUser(username);
        address.setCreatedTime(new Date());
        address.setModifiedTime(new Date());
        
        //插入收货地址的方法
        Integer rows=addressMapper.insert(address);
        if(rows!=1){
            throw new InsertException("插入用户的收货地址异常");
        }
        

    }
}

3.测试业务层的功能,AddressServiceTests测试业务功能

@SpringBootTest//表示标注当前的类是一个测试类,不会随同项目一块打包
@RunWith(SpringRunner.class)//表示启动这个单元测试类,需要传递一个参数必须是SpringRunner的实例类型
public class AddressServiceTests {

    @Autowired
    private IAddressService addressService;


    
    @Test
    public void addNewAddress(){
        Address address=new Address();
        address.setPhone("178423232");
        address.setName("男朋友");
        addressService.addNewAddress(23,"管理员",address);
    }

}

5.新增收货地址-控制器

5.1处理异常

业务层抛出了收货地址总数超标异常,在BaseController中进行处理。

5.2设计请求

/address/add_new_address

post

Address address,HttpSession session 

JsonResult<Void>

5.3处理请求

在控制层创建AddressController来处理用户收货地址的请求

@RequestMapping("addresses")
@RestController
public class AddressController extends BaseController{

    @Autowired
    private IAddressService addressService;


    @RequestMapping("/add_new_address")
    public JsonResult<Void> addNewAddress(Address address, HttpSession session){
        Integer uid=getuidFromSession(session);
        String username=getUsernameFromSession(session);
        addressService.addNewAddress(uid,username,address);
        return new JsonResult<>(OK);
    }
}

先登录用户,然后在进行接口测试

6.新增收货地址-前端页面

<!--页脚结束-->
<script type="text/javascript">
    $("#btn-add-new-address").click(function () {
        $.ajax({
            url: "/addresses/add_new_address",
            type: "POST",
            data: $("#form-add-new-address").serialize(),
            dataType: "JSON",
            success: function (json) {
                if (json.state == 200) {
                    alert("新增收货地址成功!");
                    location.href = "address.html";
                } else {
                    alert("新增收货地址失败!" + json.message);
                }
            },
            error: function (xhr) {
                alert("您的登录信息已经过期,请重新登录!HTTP响应码:" + xhr.status);
                location.href = "login.html";
            }
        });
    });
</script>

获取省市区列表

1.获取省市区列表-数据库

2.获取省市区列表-实体类

package com.liyang.store.entity;

import java.io.Serializable;
import java.util.Objects;

/**
 * 省/市/区数据的实体类
 */
public class District implements Serializable {
    private Integer id;
    private String parent;
    private String code;
    private String name;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getParent() {
        return parent;
    }

    public void setParent(String parent) {
        this.parent = parent;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof District)) return false;
        District district = (District) o;
        return Objects.equals(getId(), district.getId()) && Objects.equals(getParent(), district.getParent()) && Objects.equals(getCode(), district.getCode()) && Objects.equals(getName(), district.getName());
    }

    @Override
    public int hashCode() {
        return Objects.hash(getId(), getParent(), getCode(), getName());
    }

    @Override
    public String toString() {
        return "District{" +
                "id=" + id +
                ", parent='" + parent + '\'' +
                ", code='" + code + '\'' +
                ", name='" + name + '\'' +
                '}';
    }
}

3获取省市区列表-持久层

select * from t_dict_district where parent=?   order by code asc

抽象方法的定义DstrictMapper

查询语句,根据父代号进行查询

获取省市区列表-业务层

public interface IDistrictService {
    /**
     * 根据父代号来查询区域地址信息
     * @param parent
     * @return
     */
   List<District> getByParent(String parent);
}

2.创建DistrictService实现类,实现抽象的方法

@Service
public class DistrictServiceImpl implements IDistrictService {

    @Autowired
    private DistrictMapper districtMapper;

    @Override
    public List<District> getByParent(String parent) {
       List<District>list= districtMapper.findByParent(parent);
       //在进行网络数据传输时为了尽量避免无效数据的传输,可以将无效数据设置为 null
        //可以节省流量
        for(District d:list){
            d.setId(null);
            d.setParent(null);
        }
        return list;
    }
}

3.单元测试

@SpringBootTest//表示标注当前的类是一个测试类,不会随同项目一块打包
@RunWith(SpringRunner.class)//表示启动这个单元测试类,需要传递一个参数必须是SpringRunner的实例类型
public class DistrictServiceTests {

    @Autowired
    private IDistrictService districtService;
    
    @Test
    public void getByParent(){
        //86中国
     List<District> list= districtService.getByParent("86");
     for(District d:list){
         System.out.println(d);
     }
    }

}

4.获取省市区列表-控制层

4.1设计请求

/districts/

GET

String parent

JsonResolt<List <District>>

4.2请求处理

创建一个DistrictController,在类中来编写请求的方法

@RequestMapping("districts")
@RestController
public class DistrictController extends BaseController{

    @Autowired
    private IDistrictService districtService;

    public JsonResult<List<District>> getByParent(String parent){
       List<District> data=districtService.getByParent(parent);
       return new JsonResult<>(OK,data);
    }

}

districts请求添加白名单中

patterns.add("/districts/**");

直接请求服务器,来访问localhost:8080/districts?parent=86访问进行测试

5.获取省市区列表-前端页面

  • 1检查前端页面在提交省市区数据时是否有相关name属性和id属性。
  • 2.运行前端看是否还可以正常保存数据(除了省市区外)

获取省市区的名称-持久层

1.规划根据当前code来获取当前省市区的名称,对应就是一条查询语句

select * from t_dist_district where code=?

2.在DistrictMapper接口定义出来。

String findNameBycode(String code);

3.在DistrictMapper.xml文件中添加抽象方法的映射

<select id="findNameByCode" resultType="java.lang.String">
        select name from t_dict_district where code=#{code}
    </select>

4.单元测试

 @Test
    public void fidNameByCode(){
      String name= districtMapper.findNameBycode("610000");
        System.out.println(name);
    }

5.获取省市区的名称-业务层

1.在业务层有没有异常需要处理的

2.定义对应的业务接口的

String getNameByCode(String code);

3.在子类中进行实现

    @Override
    public String getNameByCode(String code) {

     return districtMapper.findNameBycode(code);
    }
}

4.测试可以省略不写。(超过8行以上代码都要进行独立的测试)

获取省市区的名称-控制层

1.添加地址层依赖于IDistrictService层

//在添加用户的收货地址的业务层依赖于IDistrictService的业务层接口
@Autowired
private IDistrictService districtService;

2.在addNewAddress方法中将districtServcie接口中获取到的省市区数据专业到address对象,这个对象中就包含了所有的收货地址

  //对address对象中的数据进行补全 省市区
        String provinceName= districtService.getNameByCode(address.getProvinceCode());
        String cityName= districtService.getNameByCode(address.getCityCode());
        String areaName= districtService.getNameByCode(address.getAreaCode());
        address.setAddress(provinceName);
        address.setAddress(cityName);
        address.setAddress(areaName);

获取省市区-前端页面

1.AddAddress.html页面中来编写对应的省市区展示及根据用户的不同选择来限制对应的标签中的内容

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值