(七)搭建springboot商城--新增收货地址

1.新增收货-数据表创建

CREATE TABLE t_address (
	aid INT AUTO_INCREMENT COMMENT '收货地址id',
	uid INT COMMENT '归属的用户id',
	name VARCHAR(20) COMMENT '收货人姓名',
	province_name VARCHAR(15) COMMENT '省-名称',
	province_code CHAR(6) COMMENT '省-行政代号',
	city_name VARCHAR(15) COMMENT '市-名称',
	city_code CHAR(6) COMMENT '市-行政代号',
	area_name VARCHAR(15) COMMENT '区-名称',
	area_code CHAR(6) COMMENT '区-行政代号',
	zip CHAR(6) COMMENT '邮政编码',
	address VARCHAR(50) COMMENT '详细地址',
	phone VARCHAR(20) COMMENT '手机',
	tel VARCHAR(20) COMMENT '固话',
	tag VARCHAR(6) COMMENT '标签',
	is_default INT COMMENT '是否默认:0-不默认,1-默认',
	created_user VARCHAR(20) COMMENT '创建人',
	created_time DATETIME COMMENT '创建时间',
	modified_user VARCHAR(20) COMMENT '修改人',
	modified_time DATETIME COMMENT '修改时间',
	PRIMARY KEY (aid)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

2.新增收货体制-创建实体类

 创建一个Address类,在类中定义表的相关字段,采用驼峰命名方式,最后再去继承BaseEntity类。

/** 收货地址数据的实体类**/
public class Address extends BaseEntity {

    private Integer aid;
    private Integer uid;
    private String name;
    private String provinceName;
    private String provinceCode;
    private String cityName;
    private String cityCode;
    private String areaName;
    private String areaCode;
    private String zip;
    private String address;
    private String phone;
    private String tel;
    private String tag;
    private Integer isDefault;

3.新增收货地址-持久层

3.1各功能的开发顺序

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

3.2 规划需要执行的SQL语句

1.对应是插入语句:

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

2.一个用户的收货地址规定最多只能有20条数据对应。插入用户数据之前先做查询操作。收货逻辑控制方面的异常。

select count(*) from t_adress where uid=?

3.3 接口与抽象方法 

1.创建一个接口Address接口,在这个接口中来定义上面两个SQL语句抽象方法定义。

package com.cy.store.mapper;

import com.cy.store.entity.Address;

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

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

3.4 配置SQL映射 

创建一个AddressMapper.xml映射文件,在这个文件中添加抽象方法的映射,映射到SQL语句上。





    
        
        
        
        
        
        
        
        
        
        
        
        
    


    
        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}
    


    

在test下的mapper文件夹下创建AddressMapperTests的测试类。

package com.cy.store.mapper;

import com.cy.store.entity.Address;
import com.cy.store.entity.User;
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 java.util.Date;

//@SpringBootTest:表示标注当前的类i是一个测试类,不会随打包发送
@SpringBootTest
//@RunWith:表示启动这个单元测试类(是不能够运行的),需要传递一个参数,必须时SpringRunner的实例类型
@RunWith(SpringRunner.class)
public class AddressMapperTests {
    @Autowired
    private AddressMapper addressMapper;

    @Test
    public void insert(){
        Address address=new Address();
        address.setUid(9);
        address.setPhone("123123123");
        address.setName("刘慧慧");
        addressMapper.insert(address);
    }
    @Test
    public void countByUid(){
        System.out.println(addressMapper.countByUid(9));
    }


}

4.新增收货地址-业务层

4.1 规划异常

如果用户是第一次插入用户的收货地址,规则:当用户插入的地址是第一条时,需要将当前的地址作为默认的收货地址。如果查询的统计总数为0,则将当前地址的is_default的值设置为1。查询统计的结果为0不代表异常。

查询的结果大于20了,这时候需要抛出业务控制的异常AddressCountLimitExcepton异常,自行创建这个异常。

package com.cy.store.service.ex;
/** 收货地址总数超出限制的异常(20条)**/
public class AddressCountLimitException extends ServiceException{
   
}

插入数据时产生未知的异常 InsertException,不需要再重复的创建。

4.2 接口与抽象方法

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

package com.cy.store.service;

import com.cy.store.entity.Address;

/*** 收货地址业务层接口*/
public interface IAddressService {
    void addNewAddress(Integer uid,String username,Address address);

}

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

在配置文件中来定义数据。

#Spring读取配置文件中的数据:@Value("${user.address.max-count}")
user.address.max-count=20

在实现类中实现业务控制。 

package com.cy.store.service.impl;

import com.cy.store.entity.Address;
import com.cy.store.mapper.AddressMapper;
import com.cy.store.service.IAddressService;
import com.cy.store.service.ex.AddressCountLimitException;
import com.cy.store.service.ex.InsertException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;

import java.util.Date;

/**新增收货地址的实现类**/
public class AddressServiceImpl 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、isDelete
        address.setUid(uid);
        Integer isDefault=count==0? 1:0; //1表示默认,0表示不默认
        address.setIsDefault(isDefault);
        //补全四项日志
        address.setCreatedUser(username);
        address.setCreatedTime(new Date());
        address.setModifiedUser(username);
        address.setModifiedTime(new Date());

        //插入收货地址的方法
        Integer rows = addressMapper.insert(address);
        if(rows !=1){
            throw new InsertException("插入用户的收货地址产生未知异常");
        }

    }
}

3.测试业务层功能是否正常。AddressServiceTests测试来测试业务功能。

package com.cy.store.service;

import com.cy.store.entity.Address;
import com.cy.store.entity.User;
import com.cy.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;

@SpringBootTest
@RunWith(SpringRunner.class)
public class AddressServiceTests {
    @Autowired
    private IAddressService addressService;
    @Test
    public void iAddressService(){
        Address address=new Address();
        address.setUid(9);
        address.setPhone("123123123");
        address.setName("童晓凡");
        addressService.addNewAddress(9,"管理员工",address);
    }

}

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

5.1 处理异常

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

else if (e instanceof AddressCountLimitException) {
            result.setState(4003);
            result.setMessage("用户名的收货地址超出限制的异常");
        }

5.2 设计请求

/addresses/add_new_address

POST

Address address,HttpSession session

JsonResult

5.3 处理请求

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

@RequestMapping("addresses")
@RestController
public class AddressController extends BaseController{
    @Autowired
    private IAddressService addressService;
    @RequestMapping("add_new_address")
    public JsonResult addNewAddress(Address address, HttpSession session){
        Integer uid=getUidFromSession(session);
        String username=getUsernameFromSession(session);
        addressService.addNewAddress(uid,username,address);
        return new JsonResult<>(OK);
    }

}

先登陆再测试。

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

获取省市区表

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

CREATE TABLE t_dict_district (
                                 id int(11) NOT NULL AUTO_INCREMENT,
                                 parent varchar(6) DEFAULT NULL,
                                 code varchar(6) DEFAULT NULL,
                                 name varchar(16) DEFAULT NULL,
                                 PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

parent属性表示的是父区域代码号,省的父代码号+86。

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

创建个District实体类

package com.cy.store.entity;
/**  省市区的数据实体类**/
public class District extends BaseEntity{
    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;
        if (!super.equals(o)) return false;

        District district = (District) o;

        if (getId() != null ? !getId().equals(district.getId()) : district.getId() != null) return false;
        if (getParent() != null ? !getParent().equals(district.getParent()) : district.getParent() != null)
            return false;
        if (getCode() != null ? !getCode().equals(district.getCode()) : district.getCode() != null) return false;
        return getName() != null ? getName().equals(district.getName()) : district.getName() == null;
    }

    @Override
    public int hashCode() {
        int result = super.hashCode();
        result = 31 * result + (getId() != null ? getId().hashCode() : 0);
        result = 31 * result + (getParent() != null ? getParent().hashCode() : 0);
        result = 31 * result + (getCode() != null ? getCode().hashCode() : 0);
        result = 31 * result + (getName() != null ? getName().hashCode() : 0);
        return result;
    }

    @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

抽象方法定义 。DistrictMapper接口。

package com.cy.store.mapper;

import com.cy.store.entity.District;

import java.util.List;

public interface DistrictMapper {
    /**
     * 根据父代号查询区域信息
     * @param parent 父代号
     * @return 某个父区域下所有的区域列表
     */
    List findByParent(Integer parent);
}

配置SQL映射





    

在test下的mapper文件夹下创建DistrictMapperTests的测试类。 

package com.cy.store.mapper;

import com.cy.store.entity.District;
import com.cy.store.entity.User;
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 java.util.Date;
import java.util.List;

//@SpringBootTest:表示标注当前的类i是一个测试类,不会随打包发送
@SpringBootTest
//@RunWith:表示启动这个单元测试类(是不能够运行的),需要传递一个参数,必须时SpringRunner的实例类型
@RunWith(SpringRunner.class)
public class DistrictMapperTests {
    @Autowired
    private DistrictMapper districtMapper;
    @Test
    public void findByParent(){
        List list = districtMapper.findByParent("110100");
        for (District district : list) {
            System.out.println(district);
        }
    }
}

 4  获取省市区列表-业务层

1.创建接口IDistrictService,并定义抽象方法。

package com.cy.store.service;

import com.cy.store.entity.District;

import java.util.List;

public interface IDistrictService {
    /**
     * 根据父代号来查询区域信息(省市区)
     * @param parent 父代码
     * @return 多个区域的信息
     */
    List getByParent(String parent);
}

2.创建 DistrictServiceImpl实现类,实现抽象方法。

package com.cy.store.service.impl;

import com.cy.store.entity.District;
import com.cy.store.mapper.DistrictMapper;
import com.cy.store.service.IDistrictService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
@Service
public class DistrictServiceImpl implements IDistrictService {
    @Autowired
    private DistrictMapper districtMapper;
    @Override
    public List getByParent(String parent) {
        List list = districtMapper.findByParent(parent);
        /**
         * 进行网络数据传输时,为了尽量避免无效数据的传递,可以将无效数据设置为null
         * 可以节省流量,另一方面提升了效率
         * **/
        for (District district : list) {
            district.setId(null);
            district.setParent(null);
        }
        return list;
    }
}

3.单元测试

package com.cy.store.service;

import com.cy.store.entity.District;
import com.cy.store.entity.User;
import com.cy.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 java.util.List;

@SpringBootTest
@RunWith(SpringRunner.class)
public class DistrictServiceTests {
 @Autowired
    private IDistrictService districtService;
 @Test
    public void getByParent(){
     //86表示中国,所有省的父代号都是86
     List list = districtService.getByParent("86");
     for (District district : list) {
         System.out.println(district);
     }
 }

}

 5 获取省市区列表-控制层

5.1 设置请求

/districts/

GET

String parent

JsonResult>

5.2  请求方法

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

@RequestMapping("districts")
@RestController
public class DistrictController extends BaseController{
    @Autowired
    private IDistrictService districtService;
    //districts开头的请求都被拦截到getByParent()方法
    @RequestMapping({"/",""})
    public JsonResult> getByParent(String parent){
        List data= districtService.getByParent(parent);
        return new JsonResult<>(OK,data);

    }

添加到白名单中

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

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

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

 1.注释掉通过js来完成省市区列表加载的代码。


2.检查前端页面在提交省市区数据时是否有相关name属性和id属性。 

3.运行前端看是否还可以正常保存数据(除了省市区外)。

获取省市区的名称

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

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

select * from t_dist_district where code=?

2.在DistrictMapper接口定义出来。

String findNameByCode(String code);

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

4.单元测试方法编写。

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

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

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

2.定义对应的业务层接口中的抽象方法。

String getNameByCode(String code);

3.在子类中 进行实现。

@Override
public String getNameByCode(String code) {
    
    return districtMapper.findNameByCode(code);
}

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

3 获取省市区的名称-业务层优化

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

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

2.在addNewAddress方法中将districtService接口中获取到的省市区数据转移到address对象,这个对象中就包含了所有的用户收货地址的数据。 

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

4 获取省市区的名称-前端页面

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

2.编写相关的事件代码 


                    
                    

你可能感兴趣的:(spring,boot,java,后端)