1、数据库表创建

在store中创建t_address:

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

//收货地址实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Address extends BaseEntity implements Serializable {
    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.各功能开发顺序

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

开发顺序:新增收货地址——列表展示——设置默认收货地址——删除收货地址——修改收货地址

一.新增收货地址开发

1.持久层开发

1.插入新增收货地址:

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

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

select count(*)  from t_address where uid=?
接口和抽象方法

AddressMapper:

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

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

AddressMapper.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">
<mapper namespace="com.cy.store.mapper.AddressMapper">

<!--    插入新收货地址-->
    <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(*) from t_address where uid=#{uid}
    </select>
</mapper>
4.单元测试

AddressMapperTests

@SpringBootTest
public class AddressMapperTests {

    @Autowired
    private AddressMapper addressMapper;

    @Test
    public void insert(){
        Address address = new Address();
        address.setUid(1);
        address.setPhone("123456");
        address.setName("tom");
        addressMapper.insert(address);
    }

    @Test
    public void countByUid(){
        Integer count = addressMapper.countByUid(1);
        System.out.println(count);
    }
}
2.业务层开发
1.规划异常

当用户是第一次插入的收货地址,将该收货地址设为默认地址,如果查询到统计总数为0则将当前地址的is_default值设为1,。查询统计结果为0不等于异常。

查询结果大于20,这时候需要抛出业务控制的异常AddressCountLimitException异常。

//收货地址总数超过限制条的异常
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);
    }
}
2.接口和抽象方法

创建IAddressService接口

//收货地址业务层接口
public interface IAddressService {

    /**
     * 新增收货地址
     * @param uid:用户id  从session获取
     * @param username:修改人
     * @param address:新增地址的数据
     */
    void addNewAddress(Integer uid,String username,Address address);
}
3.实现抽象方法

创建AddressServiceImpl

//新增收货地址的实现类
@Service
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("用户收货地址超出上限");
        }

        //补全数据
        address.setUid(uid);
        int 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("插入时产生未知异常");
        }
    }
}

1.在配置文件application.properties中定义最大收货数量

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

在AddressServiceTests进行测试

@SpringBootTest
public class AddressServiceTests {

    @Autowired
    private IAddressService addressService;


    @Test
    public void addNewAddress(){
        Address address=new Address();
        address.setPhone("745612");
        address.setName("a");
        addressService.addNewAddress(11,"tom",address);
    }
}
3.控制层开发
1.处理异常

在BaseController中添加:

else if (e instanceof AddressCountLimitException){
    result.setState(4004);
    result.setMessage("用户的收货地址超出限制");
}
2.设计请求
/addresses/add_new_address
POST
Address address,HttpSession session
JsonResult<void>
3.处理请求

创建AddressController ,用于处理用户收货地址的控制

@RestController
@RequestMapping("/addresses")
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);
    }
}
4.前端页面

1.在addAddress.html页面中配置新增收货地址表单的属性。给form表单添加id="form-add-new-address"属性、"请输入收货人姓名"添加name="name"属性、"请输入邮政编码"添加name="zip"属性、"输入详细的收货地址,小区名称、门牌号等"添加name="address"属性、"请输入手机号码"添加name="phone"属性、"请输入固定电话号码"添加name="tel"属性、"请输入地址类型,如:家、公司或者学校"添加name="tag"属性、"保存"按钮添加id="btn-add-new-address"属性。以上属性如果已经添加无需重复添加。

addAddress.html

<script>
   $("#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("新增收货地址成功")
            } else {
               alert("新增收货地址失败")
            }
         },
         error: function (xhr) {
            alert("新增收货地址产生未知异常!"+xhr.message);
         }
      });
   });
</script>

三.获取省市区的列表

1.省/市/区-数据库

导入数据库数据

2.创建实体类

在entity下创建一个District实体类,用于表示省市区的数据实体类

//省市区的实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class District extends BaseEntity implements Serializable {
    private Integer id;
    private String parent;
    private String code;
    private String name;
}
2.持久层开发

根据父代号进行查询

select * from t_dict_district where parent=? order by code ASC
1.接口和抽象方法

创建新的接口DistrictMapper

public interface DistrictMapper {
    /**
     * 根据父代号查询区域信息
     * @param parent:父代号
     * @return :返回某区域列表
     */
    List<District> findByParent(Integer parent);
}
2.编写映射

DistrictMapper.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.DistrictMapper">

    <select id="findByParent" resultType="com.cy.store.entity.District">
        select * from t_dict_district where parent=#{parent}
                 order by code ASC
    </select>
</mapper>
3.单元测试

test下mapper包中创建一个DistrictMapperTests:

@SpringBootTest
public class DistrictMapperTests {

    @Autowired
    private DistrictMapper districtMapper;

    @Test
    public void findByParent(){
        List<District> list = districtMapper.findByParent(350200);
        for (District d : list) {
            System.out.println(d);
        }
    }
}
3.业务层开发
1.接口和抽象方法

IDistrictService

//省市区的业务层接口
public interface IDistrictService {
    /**
     * 根据父代号查询区信息
     * @param parent:父代号
     * @return:多个区域的信息
     */
    List<District> getByParent(String parent);
}
3.实现抽象方法

创建DistrictServiceImpl

//省市区业务层实现类
@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;
    }
}
4.单元测试
@SpringBootTest
public class DistrictServiceTests {

    @Autowired
    private IDistrictService districtService;

    @Test
    public void getByParent(){
        List<District> list = districtService.getByParent("86");
        for (District district : list) {
            System.out.println(district);
        }
    }
}
4.控制层开发
1.设计请求
/districts/
GET
String parent
JsonResult<List<District>>
2.处理请求

创建DistrictController

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

    @Autowired
    private IDistrictService districtService;

    //districts开头的请求都被拦截到getByparent()方法  @RequestMapping({"/",""})
     @RequestMapping({"/",""})
    public JsonResult<List<District>> getByParent(String parent){
        List<District> data = districtService.getByParent(parent);
        return new JsonResult<>(OK,data);
    }
}

四.获取省市区名称

1.持久层开发

根据当前的code来获取省市区名称

select * from t_dist_district where code=?
1.接口和抽象方法

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.单元测试

在DistrictMapperTests中进行测试

@Test
public void findNameByCode(){
    String name = districtMapper.findNameByCode("350200");
    System.out.println(name);
}
2.业务层开发
1.定义抽象方法
/**
 * 根据代号查省市区名称
 * @param code
 * @return 返回省市区名称
 */
String getNameByCode(String code);
2.实现抽象方法
@Override
public String getNameByCode(String code) {
    return districtMapper.findNameByCode(code);
}
3.优化业务层

把当前方法(getNameByCode)添加到AddressServiceImpl类中addNewAddress方法中

//新增收货地址的实现类
@Service
public class AddressServiceImpl implements IAddressService {

    @Autowired
    private AddressMapper addressMapper;

    /*在添加用户的收货地址的业务层依赖于IDistrictService接口
     *对address对象中的数据进行补全:省市区的名字看前端代码发现前端传递过来的省市区的name分别为:
     * provinceCode,cityCode,areaCode,所以这里可以用address对象的get方法获取这三个的数据
     */
    @Autowired
    private IDistrictService districtService;

    @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("用户收货地址超出上限");
        }

        //对address对象中的省市区数据进行补全
        String ProvinceName=districtService.getNameByCode(address.getProvinceCode());
        String CityName=districtService.getNameByCode(address.getCityCode());
        String AreaName=districtService.getNameByCode(address.getAreaCode());
        address.setProvinceName(ProvinceName);
        address.setCityName(CityName);
        address.setAreaName(AreaName);

        //补全数据
        address.setUid(uid);
        int 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("插入时产生未知异常");
        }
    }
}
4.前端页面

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

2.编写相关事件代码

/**因为清空后下拉列表的select标签没有option标签,所以需要设置一个默认的option标
         * 签并给市,县加上该标签.option标签并不会把内容发送到后端,而是将value值发
         * 送给后端,所以用value表示当前这个区域的code值
         * */
        var defaultOption="<option value='0'>-----请选择-----</option>";
        $(document).ready(function () {
            //加载省的数据罗列时代码量较多,建议定义在外部方法中,然后在这里调用定义的方法
            showProvinceList();

            //将省,市,县的下拉列表内容设为"-----请选择-----"
            /**
             * select标签默认获取第一个option的内容填充到下拉列表中,所以即使加载
             * 页面时省区域的下拉列表中已经有了所有省但仍然会显示-----请选择-----
             * */
            $("#province-list").append(defaultOption);

            $("#city-list").append(defaultOption);
            $("#area-list").append(defaultOption);
        });

        //省的下拉列表数据展示
        function showProvinceList() {
            $.ajax({
                url: "/districts",//发送请求用于获取所有省对象
                type: "POST",
                data: "parent=86",
                dataType: "JSON",
                success: function (json) {
                    if (json.state == 200) {
                        var list = json.data;//获取所有省对象的List集合
                        for (var i = 0; i < list.length; i++) {
                            var opt =
                                "<option value='"+list[i].code+"'>"+list[i].name+"</option>";
                            $("#province-list").append(opt);
                        }
                    } else {
                        <!--这个其实永远不会执行,因为没有编写
                        异常,控制层返回的状态码永远是OK-->
                        alert("省/直辖区的信息加载失败")
                    }
                }
                //这里没有写属性error,不知道为啥不用写,感觉写了更好
            });
        }

        /**
         * change()函数用于监听某个控件是否发生改变,一旦发生改变就
         * 会触发参数形式的函数,所以参数需要是function(){}
         * */
        $("#province-list").change(function () {
            //先获取到省区域父代码号
            var parent = $("#province-list").val();

            /**
             * 如果我选择了河南省洛阳市涧西区,然后又选择了河北省,此时需要
             * 将市,县下拉列表的所有option清除并显示内容-----请选择-----
             * empty()表示某标签的所有子标签(针对此页面来说select的子标
             * 签只有option)
             * */
            $("#city-list").empty();
            $("#area-list").empty();
            //填充默认值:-----请选择-----
            $("#city-list").append(defaultOption);
            $("#area-list").append(defaultOption);

            if (parent == 0) {//如果继续程序,后面的ajax接收的json数据中的data是
                return;//空集合[],进不了for循环,没有任何意义,所以直接在这里终止程序
            }
            $.ajax({
                url: "/districts",
                type: "POST",
                data: "parent="+parent,
                dataType: "JSON",
                success: function (json) {
                    if (json.state == 200) {
                        var list = json.data;
                        for (var i = 0; i < list.length; i++) {
                            var opt =
                                "<option value='"+list[i].code+"'>"+list[i].name+"</option>";
                            $("#city-list").append(opt);
                        }
                    } else {
                        alert("市的信息加载失败")
                    }
                }
            });
        });

        $("#city-list").change(function () {
            var parent = $("#city-list").val();
            $("#area-list").empty();
            $("#area-list").append(defaultOption);

            if (parent == 0) {
                return;
            }
            $.ajax({
                url: "/districts",
                type: "POST",
                data: "parent="+parent,
                dataType: "JSON",
                success: function (json) {
                    if (json.state == 200) {
                        var list = json.data;
                        for (var i = 0; i < list.length; i++) {
                            var opt =
                                "<option value='"+list[i].code+"'>"+list[i].name+"</option>";
                            $("#area-list").append(opt);
                        }
                    } else {
                        alert("县的信息加载失败")
                    }
                }
            });
        });

五.收货地址列表展示

1.持久层开发

查询收货地址列表,用于展示在页面上

select * from t_address where uid=? order by is_default DESC,create_Time DESC
1.设计接口和抽象方法

AddressMapper:

/**
 * 根据用户id查询用户收货地址数据
 * @param uid
 * @return:返回收货地址的数据
 */
List<Address> findByUid(Integer uid);

AddressMapper.xml

<!--    查询用户的收货地址-->
    <select id="findByUid" resultMap="AddressEntityMap">
        select * from t_address where uid=#{uid}
        order by is_default DESC ,created_time DESC
    </select>
2.单元测试

AddressMapperTests:

@Test
public void findByUid(){
    List<Address> list = addressMapper.findByUid(11);
    System.out.println(list);
}
2.业务层开发
1.设计接口和抽象方法

IAddressService:

//根据用户uid返回用户的收货地址数据
List<Address> getByUid(Integer uid);
2.实现抽象方法

AddressServiceImpl:

//返回用户收货地址信息
@Override
public List<Address> getByUid(Integer uid) {
    List<Address> list = addressMapper.findByUid(uid);
    //不需要返回的数据设为null,提高效率,或者查询语句中直接不查询
    for (Address address : list) {
        address.setProvinceCode(null);
        address.setCityCode(null);
        address.setAreaCode(null);
        address.setTel(null);
        address.setIsDefault(null);
        address.setCreatedUser(null);
        address.setCreatedTime(null);
        address.setModifiedUser(null);
        address.setModifiedTime(null);
    }
    return list;
}
3.控制层开发
1.设计请求
/addresses
GET
HttpSession session
JsonResult<List<Address>>
2.处理请求
//收货地址展示
@RequestMapping({"","/"})
public JsonResult<List<Address>> getByUid(HttpSession session){

    Integer uid = getuidFromSession(session);
    List<Address> data = addressService.getByUid(uid);
    return new JsonResult<>(OK,data);
}
4.前端页面

在address.html页面中编写用户收货地址数据的展示列表

<script>
    $(document).ready(function () {
    	showAddressList();
});

/**展示用户收货地址数据列表*/
function showAddressList() {
    $("#address-list").empty();
    $.ajax({
        url: "/addresses",
        type: "get",
        //data: "parent=86",//不需要提交数据,所以data可以删去
        dataType: "JSON",
        success: function (json) {
            if (json.state == 200) {
                var list = json.data;
                console.log(list);//调试用
                for (var i = 0; i < list.length; i++) {
                    //先写一个var tr = ''; 然后去上面的地址展示中找一个tr标签复制粘贴到单引号里面,再删去多余的制表符
                    var tr = '<tr>\n' +
                        '<td>#{tag}</td>\n' +
                        '<td>#{name}</td>\n' +
                        '<td>#{address}</td>\n' +
                        '<td>#{phone}</td>\n' +
                        '<td><a class="btn btn-xs btn-info"><span class="fa fa-edit"></span> 修改</a></td>\n' +
                        '<td><a class="btn btn-xs add-del btn-info"><span class="fa fa-trash-o"></span> 删除</a></td>\n' +
                        '<td><a class="btn btn-xs add-def btn-default">设为默认</a></td>\n' +
                        '</tr>';
                    //下面用正则表达式更改字符串,上面的#{tag}#{name}等等只
                    //是占位符,没有任何意义,我也可以把#{tag}写成任何想写的东西
                    //replace的第一个参数/占位符/g可以,"占位符"也可以
                    tr = tr.replace(/#{tag}/g,list[i].tag);
                    tr = tr.replace(/#{name}/g,list[i].name);
                    tr = tr.replace("#{address}",list[i].address);
                    tr = tr.replace("#{phone}",list[i].phone);

                    $("#address-list").append(tr);
                }

                //用hide方法将第一个收货地址的"设为默认"元素隐藏,.add-def:eq(0)表
                //示第一个class为add-def的标签,这样就可以保证隐藏的是第一个收货地址
                $(".add-def:eq(0)").hide();
            } else {
                <!--这个其实永远不会执行,因为没有编写
                异常,控制层返回的状态码永远是OK-->
                alert("用户收货地址数据加载失败")
            }
        }
    });
}
</script>

六.设默认地址

1.持久层开发

1.检测当前用户想设置为默认收货地址的这条数据是否存在

select * from t_address aid=?

2.在修改用户的收货默认地址之前,先将所有的收货地址设置为非默认

update t_address set is_default=0 where uid=?

3.将用户当前选中的这条记录设置为默认收货地址

update t_address set is_default=1 where aid=?
1.设计抽象方法

AddressMapper:

/**
 * 根据aid查询收货地址数据
 * @param aid
 * @return 收货地址数据,没找到则返回null
 */
Address findByAid(Integer aid);

/**
 * 根据用户的uid值,来修改用户的收货地址来设置为非默认
 * @param uid
 * @return 受影响的行数
 */
Integer updateNonDefault(Integer uid);


Integer updateDefaultByAid(@Param("aid") Integer aid,
                           @Param("modifiedUser") String modifiedUser,
                           @Param("modifiedTime") Date modifiedTime);
2.映射
<!--    设置所有地址为非默认状态-->
    <update id="updateNonDefault">
        update t_address set is_default=0 where uid=#{uid}
    </update>
     
<!--    将用户设置的默认地址设为默认地址-->
    <update id="updateDefaultByAid">
        update t_address
        set is_default=1,modified_user=#{modifiedUser}, modified_time=#{modifiedTime}
        where aid=#{aid}
    </update>
    
    <select id="findByAid" resultMap="AddressEntityMap">
        select * from t_address where aid=#{aid}
    </select>
3.单元测试

AddressMapperTests

@Test
public void findByAid(){
    Address address = addressMapper.findByAid(6);
    System.out.println(address);
}

@Test
public void updateNonDefault(){
    Integer rows = addressMapper.updateNonDefault(11);
    System.out.println(rows);
}

@Test
public void updateDefaultByAid(){
    addressMapper.updateDefaultByAid(6,"tom",new Date());
}
2.业务层开发
1.设计抽象方法

IAddressService接口:

/**
 * 修改某个用户的某条收货地址数据为默认地址
 * @param aid:收货地址的id
 * @param uid:用户的id
 * @param username:修改人
 */
void setDefault(Integer aid,Integer uid,String username);
2.实现抽象方法

AddressServiceImpl:

@Override
public void setDefault(Integer aid, Integer uid, String username) {
    Address result= addressMapper.findByAid(aid);
    if (result==null){
        throw new AddressNotFoundException("收货地址不存在");
    }
    //检测当前收货地址数据是否为该uid的收货地址
    if (!result.getUid().equals(uid)){
        throw new AddressCountLimitException("非法访问");
    }
     //应该添加事物
    Integer rows = addressMapper.updateNonDefault(uid);
    if (rows < 1){
        throw new UpdateException("更新发生未知异常");
    }
    rows = addressMapper.updateDefaultByAid(aid, username, new Date());
    if (rows!=1){
        throw new UpdateException("更新发生未知异常");
    }
}
3.单元测试

AddressServiceTests:

@Test
public void setDefault(){
    addressService.setDefault(5,11,"tom");
}
3.控制层开发
1.处理异常
else if (e instanceof AddressNotFoundException){
    result.setState(4005);
    result.setMessage("用户的收货地址不存在");
}else if (e instanceof AccessDeniedException){
    result.setState(4006);
    result.setMessage("收货地址数据非法访问");
}
2.设计请求
/addresses/{aid}/set_default
GET
@PathVariable("aid") Integer aid,HttpSession session
JsonResult<Void>
3.处理请求

在AddressController类中编写方法

//RestFul风格的请求编写
@RequestMapping("{aid}/set_default")
public JsonResult<Void> setDefaule(@PathVariable("aid") Integer aid,HttpSession session){
    addressService.setDefault(aid,getuidFromSession(session),getUsernameFromSession(session));
    return new JsonResult<>(OK);
}
4.前端页面

1.给设置默认收货地址添加一个onlick属性,指定一个方向的调用,在这个方法中来完成ajax

address.html页面设置点击“设置默认”按钮后,发送ajax请求

//设置收货默认地址
			function setDefault(aid){
				$.ajax({
					url: "/addresses/"+aid+"/set_default",
					type: "GET",
					dataType: "JSON",
					success: function (json) {
						if (json.state == 200) {
							//重新加载收货地址列表页面
                            showAddressList();
							//location.href="address.html"
						} else {
							alert("设置默认收货地址失败")
						}
					},
					error: function (xhr) {
						alert("设置时产生未知的异常!"+xhr.message);
					}
				});
			}

七.删除收货地址

1.持久层开发

2.删除收货地址的SQL语句

delete from t_address where aid=?

3.如果用户删除的是默认地址,将剩下的地址某一条设为默认收货地址

select * from t_address where uid=? order by modified_tieme DESC limit 0,1
1.设计抽象方法

AddressMapper

//删除收货地址
Integer deleteByAid(Integer aid);
    /**
     * 查询用户uid查询用户最后一次被修改的收货地址数据,作为默认收货地址
     * @param uid:用户id
     * @return 收货地址数据
     */
    Address findLastModified(Integer uid);
2.映射

AddressMapper.xml

<!--    删除地址-->
    <delete id="deleteByAid">
        delete from t_address where aid=#{aid}
    </delete>

<!--    查询用户最后一次修改的收货地址数据-->
    <select id="findLastModified" resultMap="AddressEntityMap">
        select * from t_address where uid=#{uid}
        order by modified_time DESC limit 0,1
    </select>
4.单元测试

AddressMapperTests:

@Test
public void deleteByAid(){
    addressMapper.deleteByAid(2);
}

@Test
public void findLastModified(){
    Address lastModified = addressMapper.findLastModified(11);
    System.out.println(lastModified);
}
2.业务层开发

1.在执行删除的时候可能产生未知的删除异常导致数据不能被删除,则抛出DeleteException,在service中ex包下创建。

2.用户数据不存在的异常

1.抽象方法设计

IAdressService

/**
 * 删除收货地址
 * @param aid:收货地址的id
 * @param uid:用户id
 * @param username:修改人
 */
void deleteAddress(Integer aid,Integer uid,String username);
2.实现抽象方法

AddressServiceImpl:

//删除收货地址
@Override
public void deleteAddress(Integer aid, Integer uid, String username) {
    Address result = addressMapper.findByAid(aid);
    if (result==null){
        throw new AddressNotFoundException("收货地址不存在");
    }
    if (!result.getUid().equals(uid)){
        throw new AccessDeniedException("非法访问");
    }

    //判断要删除的地址是否为默认地址
    if (result.getIsDefault()!=1){
        Integer rows = addressMapper.deleteByAid(aid);
        if (rows!=1){
            throw new DeleteException("删除地址发生未知异常");
        }
        Integer count = addressMapper.countByUid(uid);
        if (count==0){
            return;
        }
    }else {
        Integer rows = addressMapper.deleteByAid(aid);
        if (rows!=1){
            throw new DeleteException("删除地址发生未知异常");
        }
        Integer count = addressMapper.countByUid(uid);
        if (count==0){
            return;
        }
        //查询一条最近修改的地址,设置为默认地址
        Address address = addressMapper.findLastModified(uid);
        addressMapper.updateDefaultByAid(address.getAid(),username,new Date());
        }
}
3.单元测试

AddressServiceTests:

@Test
public void deleteAddress(){
    addressService.deleteAddress(5,11,"myd");
}
3.控制层开发
1.处理异常

DeleteException放在BaseController下:

else if (e instanceof DeleteException){
    result.setState(5001);
    result.setMessage("删除数据产生未知异常");
}
2.设计请求
/addresses/{aid}/delete
POST
Interger aid,HttpSession session
JsonResult<Void>
3.处理请求

AddressController:

@RequestMapping("{aid}/delete")
public JsonResult<Void> deleteAddress(
    @PathVariable("aid") Integer aid,HttpSession session){
    addressService.deleteAddress(aid,getuidFromSession(session),getUsernameFromSession(session));
    return new JsonResult<>(OK);
}
4.前端页面

在address.html中添加删除按钮事件,用onclick

2.在编写function deleteAddress(aid)来完成方法的实现

function deleteaddress(aid) {
   $.ajax({
      url: "/addresses/"+aid+"/delete",
      type: "POST",
      dataType: "JSON",
      success: function (json) {
         if (json.state == 200) {
            location.reload();
         } else {
            alert("删除收货地址失败")
         }
      },
      error: function (xhr) {
         alert("删除时产生未知的异常!"+xhr.message);
      }
   });
}