关于“收货地址”数据的管理,涉及的功能有:增加,修改,删除,设为默认,显示列表。
以上功能的开发顺序应该是:增加 > 显示列表 > 设为默认 > 删除 > 修改。
创建收货地址数据表:
CREATE TABLE t_address (
aid INT AUTO_INCREMENT COMMENT '收货地址id',
uid INT COMMENT '用户id',
name VARCHAR(50) COMMENT '收货人姓名',
province_code CHAR(6) COMMENT '省代号',
province_name VARCHAR(50) COMMENT '省名称',
city_code CHAR(6) COMMENT '市代号',
city_name VARCHAR(50) COMMENT '市名称',
area_code CHAR(6) COMMENT '区代号',
area_name VARCHAR(50) COMMENT '区名称',
zip CHAR(6) COMMENT '邮编',
address VARCHAR(100) COMMENT '详细地址',
phone VARCHAR(20) COMMENT '手机',
tel VARCHAR(20) COMMENT '固话',
tag VARCHAR(30) COMMENT '地址类型',
is_default INT COMMENT '是否默认,0-非默认,1-默认',
created_user VARCHAR(50) COMMENT '创建人',
created_time DATETIME COMMENT '创建时间',
modified_user VARCHAR(50) COMMENT '最后修改人',
modified_time DATETIME COMMENT '最后修改时间',
PRIMARY KEY (aid)
) DEFAULT CHARSET=UTF8;
创建cn.tedu.store.entity.Address
,继承自BaseEntity
:
/**
* 收货地址数据的实体类
*/
public class Address extends BaseEntity {
private static final long serialVersionUID = 6946915401608396201L;
private Integer aid;
private Integer uid;
private String name;
private String provinceCode;
private String provinceName;
private String cityCode;
private String cityName;
private String areaCode;
private String areaName;
private String zip;
private String address;
private String phone;
private String tel;
private String tag;
private Integer isDefault;
}
(a) 规划SQL语句
增加收货地址需要执行的SQL语句大致是:
insert into t_address (除了aid以外的所有字段) values (匹配的值列表)
后续,在执行插入数据时,需要确定插入的收货地址数据是不是默认收货地址,每个用户都应该有且仅有1条默认收货地址,当新创建收货地址时,第1条是默认的,其它的都不是默认的!则需要判断出“即将创建的收货地址是不是第1条”,可以通过“查询某用户的收货地址数据的数量”来判断,即数量为0时,即将创建的就是第1条,数量不为0时,即将创建就不是第1条:
select count(*) from t_address where uid=?
另外,还可以限制每个用户最多允许创建多少条收货地址,该功能也可以通过以上查询来完成!
(b) 接口与抽象方法
创建cn.tedu.store.mapper.AddressMapper
接口,并在接口中添加抽象方法:
Integer addnew(Address address);
Integer countByUid(Integer uid);
© 配置映射
将原有的UserMapper.xml
复制,并粘贴为AddressMapper.xml
,删除其中各子级节点的配置,将根节点对应的接口修改为AddressMapper
接口,然后,在该文件中配置以上2个抽象方法的映射:
<mapper namespace="cn.tedu.store.mapper.AddressMapper">
<insert id="addnew"
useGeneratedKeys="true"
keyProperty="aid">
INSERT INTO t_address (
uid, name,
province_code, province_name,
city_code, city_name,
area_code, area_name,
zip, address,
phone, tel,
tag, is_default,
created_user, created_time,
modified_user, modified_time
) VALUES (
#{uid}, #{name},
#{provinceCode}, #{provinceName},
#{cityCode}, #{cityName},
#{areaCode}, #{areaName},
#{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>
在src/test/java下创建cn.tedu.store.mapper.AddressMapperTests
测试类,编写并执行单元测试:
@RunWith(SpringRunner.class)
@SpringBootTest
public class AddressMapperTests {
@Autowired
AddressMapper mapper;
@Test
public void addnew() {
Address address = new Address();
address.setUid(1);
address.setName("小刘同学");
Integer rows = mapper.addnew(address);
System.err.println("rows=" + rows);
}
@Test
public void countByUid() {
Integer uid = 100;
Integer count = mapper.countByUid(uid);
System.err.println("count=" + count);
}
}
(a) 规划异常
插入数据可能产生InsertException
。
如果需要限制用户创建的收货地址数据,则达到上限仍尝试创建,就会产生cn.tedu.store.service.ex.AddressCountLimitException
。
(b) 接口与抽象方法
创建cn.tedu.store.service.IAddressService
接口,然后,在接口中添加抽象方法:
void create(Integer uid, String username, Address address) throws AddressCountLimitException, InsertException;
© 实现抽象方法
创建cn.tedu.store.service.impl.AddressServiceImpl
类,实现IAddressService
接口,在类之前添加@Service
注解,在类中添加@Autowired private AddressMapper addressMapper;
持久层对象:
@Service
public class AddressServiceImpl implements IAddressService {
@Autowired
private AddressMapper addressMapper;
}
然后,复制持久层接口中的2个抽象方法,粘贴到该实现类中,然后,私有化实现这2个方法:
/**
* 增加收货地址数据
* @param address 收货地址数据
* @throws InsertException
*/
private void addnew(Address address) {
Integer rows = addressMapper.addnew(address);
if (rows != 1) {
throw new InsertException("...");
}
}
/**
* 统计某用户的收货地址的数量
* @param uid 用户的id
* @return 用户的收货地址的数量
*/
private Integer countByUid(Integer uid) {
if (uid == null || uid < 1) {
throw new IllegalArgumentException();
}
return addressMapper.countByUid(uid);
}
重写接口中的抽象方法:
public void create(Integer uid, String username, Address address) throws AddressCountLimitException, InsertException {
// 基于参数uid查询该用户的收货地址数量
// 判断数量是否达到上限值
// 是:AddressCountLimitException
// 补全参数address中的数据:uid
// TODO 补全参数address中的数据:省市区的名称
// 补全参数address中的数据:isDefault,根据收货地址数量确定该属性的值
// 创建当前时间对象
// 补全参数address中的数据:4项日志
// 执行增加
}
具体实现代码为:
@Override
public void create(Integer uid, String username, Address address)
throws AddressCountLimitException, InsertException {
// 基于参数uid查询该用户的收货地址数量
Integer count = countByUid(uid);
// 判断数量是否达到上限值
if (count >= MAX_COUNT) {
// 是:AddressCountLimitException
throw new AddressCountLimitException(
"增加收货地址失败!当前收货地址的数量(" + count + ")已经达到上限(" + MAX_COUNT + ")!");
}
// 补全参数address中的数据:uid
address.setUid(uid);
// TODO 补全参数address中的数据:省市区的名称
// 补全参数address中的数据:isDefault,根据收货地址数量确定该属性的值
Integer isDefault = count == 0 ? 1 : 0;
address.setIsDefault(isDefault);
// 创建当前时间对象
Date now = new Date();
// 补全参数address中的数据:4项日志
address.setCreatedUser(username);
address.setCreatedTime(now);
address.setModifiedUser(username);
address.setModifiedTime(now);
// 执行增加
addnew(address);
}
完成后,在src/test/java下创建cn.tedu.store.service.AddressServiceTests
测试类,编写并执行单元测试:
@RunWith(SpringRunner.class)
@SpringBootTest
public class AddressServiceTests {
@Autowired
IAddressService service;
@Test
public void create() {
try {
Integer uid = 6;
String username = "超级管理员";
Address address = new Address();
address.setName("小赵同学");
address.setAddress("开心小区1号2单元303室");
service.create(uid, username, address);
System.err.println("OK.");
} catch (ServiceException e) {
System.err.println(e.getClass().getName());
System.err.println(e.getMessage());
}
}
}
(a) 统一处理异常
需要处理AddressCountLimitException
。
(b) 设计请求
设计“增加收货地址”的请求方式:
请求路径:/addresses/create
请求参数:Address address, HttpSession session
请求方式:POST
响应数据:JsonResult
© 处理请求
创建cn.tedu.store.controller.AddressController
,继承自BaseController
,在类之前添加@RestController
和@RequestMapping("addresses")
注解,在类中添加@Autowired private IAddressService addressService;
业务层对象:
@RestController
@RequestMapping("addresses")
public class AddressController extends BaseController {
@Autowired
private IAddressService addressService;
}
然后,添加处理请求的方法:
@RequestMapping("create")
public JsonResult create(Address address, HttpSession session) {
// 从Session中获取uid和username
// 调用业务层对象执行新增收货地址
// 返回成功
}
完成后,打开浏览器,先登录,通过http://localhost:8080/addresses/create?name=Kitty
进行测试。
检查完成后,可以将接口中的MAX_COUNT
设置为更大一些的值。
进入MySQL控制台,执行指令以导入省市区数据
source 脚本文件路径
首先,应该创建与数据表对应的cn.tedu.store.entity.District
实体类:
public class District implements Serializable {
private Integer id;
private String parent;
private String code;
private String name;
}
服务器端需要提供“获取全国所有的省的列表”、“获取某省所有的市的列表”、“某市所有的区的列表”功能,这些功能的查询操作都是:
select * from t_dict_district where parent=?
所以,在处理持久层时,先创建cn.tedu.store.mapper.DistrictMapper
持久层接口,并添加抽象方法:
List findByParent(String parent);
然后,复制得到DistrictMapper.xml
文件,并配置以上抽象方法的映射:
<select id="findByParent"
resultType="cn.tedu.store.entity.District">
SELECT
id, parent,
code, name
FROM
t_dict_district
WHERE
parent=#{parent}
ORDER BY
code ASC
select>
在src/test/java下创建cn.tedu.store.mapper.DistrictMapperTests
测试类,编写并执行单元测试:
@RunWith(SpringRunner.class)
@SpringBootTest
public class DistrictMapperTests {
@Autowired
DistrictMapper mapper;
@Test
public void findByParent() {
String parent = "86";
List<District> list = mapper.findByParent(parent);
System.err.println("count=" + list.size());
for (District item : list) {
System.err.println(item);
}
}
}
创建cn.tedu.store.service.IDistrictService
接口,然后,在接口中添加抽象方法:
List getByParent(String parent);
创建cn.tedu.store.service.impl.DistrictServiceImpl
类,实现IDistrictService
接口,在类之前添加@Service
注解,在类中添加@Autowired private DistrictMapper districtMapper;
持久层对象:
@Service
public class DistrictServiceImpl implements IDistrictService {
@Autowired
private DistrictMapper districtMapper;
}
然后,复制持久层接口中的抽象方法,粘贴到该实现类中,然后,私有化实现这2个方法:
/**
* 获取全国所有省/某省所有市/某市所有区的列表
* @param parent 父级单位的代号,如果获取全国所有省,则使用86作为父级代号
* @return 匹配的省或市或区的列表
*/
private List findByParent(String parent) {
return districtMapper.findByParent(parent);
}
重写接口中的抽象方法:
public List getByParent(String parent) {
return findByParent(parent);
}
完成后,在src/test/java下创建cn.tedu.store.service.DistrictServiceTests
测试类,编写并执行单元测试:
@RunWith(SpringRunner.class)
@SpringBootTest
public class DistrictServiceTests {
@Autowired
IDistrictService service;
@Test
public void getByParent() {
String parent = "86";
List<District> list = service.getByParent(parent);
System.err.println("count=" + list.size());
for (District item : list) {
System.err.println(item);
}
}
}
后续,客户提交的请求应该是:
请求路径:/districts/
请求参数:String parent
请求类型:GET
响应数据:JsonResult>
是否拦截:否,需要添加白名单
创建cn.tedu.store.controller.DistrictController
,继承自BaseController
,在类之前添加@RestController
和@RequestMapping("districts")
注解,在类中添加@Autowired private IDistrictService districtService;
业务层对象:
@RestController
@RequestMapping("districts")
public class DistrictController extends BaseController {
@Autowired
private IDistrictService districtService;
}
然后,添加处理请求的方法:
@GetMapping("/")
public JsonResult> getByParent(String parent) {
// 调用业务层对象执行查询
// 返回成功和查询结果
}
然后,在拦截器的配置中,将/districts/**
添加为白名单。
完成后,打开浏览器,先登录,通过http://localhost:8080/districts/?parent=86
进行测试。
首先,需要在DistrictMapper.java
接口中定义抽象方法:
District findByCode(String code);
然后,在DistrictMapper.xml
中配置映射,SQL语句大致是:
select name from t_ditc_district where code=?
完成后,在DistrictMapperTests
中编写并执行单元测试:
由于不允许在处理Address
的业务层中直接访问以上DistrictMapper
接口中的内容,所以,还需要以上功能开发至业务层!使得处理Address
的业务层时,可以调用处理District
数据的业务层功能!
所以,在IDistrictService
中添加抽象方法:
District getByCode(String code);
然后,在业务层中先私有化实现持久层的新方法:
private District findByCode(String code) {
return districtMapper.findByCode(code);
}
再实现接口中的抽象方法:
public District getByCode(String code) {
return findByCode(code);
}
完成后,在DistrictServiceTests
中编写并执行单元测试:
接下来,就可以在AddressServiceImpl
中声明@Autowired IDistrictService districtService;
对象,并添加用于“根据省/市/区”代号获取名称的私有方法:
/**
* 根据省/市/区的代号获取名称
* @param code 省/市/区的代号
* @return 省/市/区的代号匹配的名称,如果没有匹配的数据,则返回空字符串
*/
private String getDistrictNameByCode(String code) {
District result = districtService.getByCode("");
return result == null ? "" : result.getName();
}
最后,在“增加”功能的业务层中,调用3次以上方法,根据代号获取对应的名称,并装到参数address
中执行增加,则最终添加的数量中就是包含省市区数据的!
完全完成后,包括测试也通过后,应该删除现在的全部数据,然后,增加不少于10数据。
(a) 规划SQL语句
查询所需要显示的列表的SQL语句大致是:
select
*
from
t_address
where
uid=?
(b) 接口与抽象方法
在AddressMapper
中添加抽象方法:
/**
* 获取某用户的收货地址数据的列表
* @param uid 用户的id
* @return 用户的收货地址数据的列表
*/
List findByUid(Integer uid);
© 配置映射
如果查询列表中的字段列表使用*
号,则表示查询所有字段,由于存在查询结果的列名与封装的对象的属性名不匹配的问题,所以,先配置一个
(可参考UserMapper.xml中的配置):
<resultMap id="AddressEntityMap"
type="cn.tedu.store.entity.Address">
<id column="aid" property="aid"/>
<result column="uid" property="uid"/>
<result column="name" property="name"/>
<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="zip" property="zip"/>
<result column="tel" property="tel"/>
<result column="phone" property="phone"/>
<result column="tag" property="tag"/>
<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>
然后配置以上接口中的抽象方法:
<select id="findByUid"
resultMap="AddressEntityMap">
SELECT
*
FROM
t_address
WHERE
uid=#{uid}
ORDER BY
is_default DESC,
modified_time DESC
select>
在AddressMapperTests
中测试:
@Test
public void findByUid() {
Integer uid = 7;
List list = mapper.findByUid(uid);
System.err.println("count=" + list.size());
for (Address item : list) {
System.err.println(item);
}
}
(a) 规划异常
无
(b) 接口与抽象方法
将持久层中的抽象方法复制到业务层接口,然后把方法名的find
改成get
:
/**
* 获取某用户的收货地址数据的列表
* @param uid 用户的id
* @return 用户的收货地址数据的列表
*/
List getByUid(Integer uid);
© 实现抽象方法
将持久层中的抽象方法复制到业务层实现类,然后私有化实现:
/**
* 获取某用户的收货地址数据的列表
* @param uid 用户的id
* @return 用户的收货地址数据的列表
*/
private List<Address> findByUid(Integer uid) {
List<Address> addresses = addressMapper.findByUid(uid);
for (Address address : addresses) {
address.setTel(null);
address.setZip(null);
address.setCreatedUser(null);
address.setCreatedTime(null);
address.setModifiedUser(null);
address.setModifiedTime(null);
}
return addresses;
}
然后添加接口中定义的抽象方法:
@Override
public List<Address> getByUid(Integer uid) {
return findByUid(uid);
}
完成后,进行测试:
@Test
public void getByUid() {
Integer uid = 7;
List<Address> list = service.getByUid(uid);
System.err.println("count=" + list.size());
for (Address item : list) {
System.err.println(item);
}
}
(a) 统一处理异常
无
(b) 设计请求
设计“收货地址-显示列表”的请求方式:
请求路径:/addresses/
请求参数:HttpSession session
请求方式:GET
响应数据:JsonResult>
© 处理请求
@GetMapping
public JsonResult> getByUid(HttpSession session) {
}
完成后,打开浏览器,可通过http://localhost:8080/addresses/
进行测试访问。
(a) 规划SQL语句
如果需要把某条收货地址设置为默认,需要执行的SQL语句大致是:
update t_address set is_default=1,modified_user=?,modified_time=? where aid=?
除此以外,还需要将原有的默认地址设置为非默认,由于原默认收货地址数据的id可能是未知的,可以“将该用户的所有收货地址设置为非默认,然后再把指定的那条设置为默认”即可,所以,“将该用户的所有收货地址设置为非默认”的SQL语句大致是:
update t_address set is_default=0 where uid=?
在操作数据之前,还是应该对数据进行检查,例如:检查收货地址数据是否存在,对应的SQL查询是:
select aid from t_address where aid=?
以上查询时,查询的字段并不重要,最终只需要判断查询结果是否为null即可,即:用于判断将要被设置为默认的收货地址数据是否存在。
除此以外,由于参数aid是客户端提交的,应该视为不可靠数据,该aid对应的数据可能是不存在的,另外,也可能是他人的数据,所以,在查询时,还应该将uid也查询出来,用于和Session中的uid对比,以判断即将需要操作的数据的归属是否正常:
select uid from t_address where aid=?
(b) 接口与抽象方法
在AddressMapper.java
接口中,声明3个抽象方法:
Integer updateDefault(
@Param("aid") Integer aid,
@Param("username") String username,
@Param("modifiedTime") Date modifiedTime);
Integer updateNonDefault(Integer uid);
Address findByAid(Integer aid);
© 配置映射
映射:
<update id="updateDefault">
UPDATE
t_address
SET
is_default=1,
modified_user=#{username},
modified_time=#{modifiedTime}
WHERE
aid=#{aid}
update>
<update id="updateNonDefault">
UPDATE
t_address
SET
is_default=0
WHERE
uid=#{uid}
update>
<select id="findByAid"
resultMap="AddressEntityMap">
SELECT
*
FROM
t_address
WHERE
aid=#{aid}
select>
单元测试:
@Test
public void updateDefault() {
Integer aid = 30;
String username = "哈哈";
Date modifiedTime = new Date();
Integer rows = mapper.updateDefault(aid, username, modifiedTime);
System.err.println("rows=" + rows);
}
@Test
public void updateNonDefault() {
Integer uid = 7;
Integer rows = mapper.updateNonDefault(uid);
System.err.println("rows=" + rows);
}
@Test
public void findByAid() {
Integer aid = 30;
Address result = mapper.findByAid(aid);
System.err.println(result);
}
(a) 规划异常
此次设为默认的主要操作是Update
操作,则可能抛出UpdateException
;
在操作之前,应该检查被设置为默认的数据是否存在,所以,可能抛出AddressNotFoundException
;
在检查时,还应该检查数据归属是否正确,即用户操作的是不是自己的数据,如果不是,则抛出AccessDeniedException
。
(b) 接口与抽象方法
在IAddressService
接口中添加:
void setDefault(Integer aid, Integer uid, String username) throws AddressNotFoundException, AccessDeniedException, UpdateException;
© 实现抽象方法
先从AddressMapper.java
接口中复制新的抽象方法并粘贴到业务层实现类AddressServiceImpl
中,并私有化实现这些方法:
然后,重写IAddressService
接口中的抽象方法:
public void setDefault(Integer aid, Integer uid, String username) throws AddressNotFoundException, AccessDeniedException, UpdateException {
// 根据参数aid查询数据
// 判断查询结果是否为null
// 是:AddressNotFoundException
// 判断查询结果中的uid与参数uid是否不同
// 是:AccessDeniedException
// 将该用户的所有收货地址设置为非默认
// 将指定的收货地址设置为默认
}
具体实现为:
@Override
@Transactional
public void setDefault(Integer aid, Integer uid, String username)
throws AddressNotFoundException, AccessDeniedException, UpdateException {
// 根据参数aid查询数据
Address result = findByAid(aid);
// 判断查询结果是否为null
if (result == null) {
// 是:AddressNotFoundException
throw new AddressNotFoundException(
"设置默认收货地址失败!尝试访问的数据不存在!");
}
// 判断查询结果中的uid与参数uid是否不同
if (!result.getUid().equals(uid)) {
// 是:AccessDeniedException
throw new AccessDeniedException(
"设置默认收货地址失败!不允许访问他人的数据!");
}
// 将该用户的所有收货地址设置为非默认
updateNonDefault(uid);
// 将指定的收货地址设置为默认
updateDefault(aid, username, new Date());
}
完成后,单元测试:
@Test
public void setDefault() {
try {
Integer aid = 250;
Integer uid = 7;
String username = "呵呵";
service.setDefault(aid, uid, username);
System.err.println("OK.");
} catch (ServiceException e) {
System.err.println(e.getClass().getName());
System.err.println(e.getMessage());
}
}
(a) 统一处理异常
需要处理2个新的异常!
(b) 设计请求
设计“收货地址-设为默认”的请求方式:
请求路径:/addresses/{aid}/set_default
请求参数:@PathVariable("aid") Integer aid, HttpSession session
请求方式:POST
响应数据:JsonResult
http://localhost:8080/addresses/18/set_default
© 处理请求
@RequestMapping("{aid}/set_default")
public JsonResult setDefault(
@PathVariable("aid") Integer aid,
HttpSession session) {
// 从Session中获取uid和username
Integer uid = getUidFromSession(session);
String username = getUsernameFromSession(session);
// 调用业务层对象执行设置默认
addressService.setDefault(aid, uid, username);
// 返回
return new JsonResult<>(SUCCESS);
}
(a) 规划SQL语句
当执行删除时,需要执行的SQL语句大致是:
delete from t_address where aid=?
在执行删除之前,还应该检查数据是否存在和数据归属是否正确,这项检查功能已经存在,无需再开发或调整。
如果删除的是“默认”的收货地址,如果这已经是最后一条收货地址,则无需进行后续的任何处理,可以在删除之后,再次通过此前的countByUid()
进行查询,从而得知刚才删除的是不是最后一条收货地址。
如果删除的是“默认”的收货地址,且还有更多的收货地址,则应该把剩余的收货地址中的某一条设置为默认,可以制定规则“将最近修改的那条设置为默认”,设置为默认可以使用此前的updateDefault()
直接完成,而“找出最近修改的收货地址”需要执行的SQL语句大致是:
select * from t_address where uid=? order by modified_time desc limit 0,1
(b) 接口与抽象方法
则需要在持久层接口中添加2个抽象方法:
Integer deleteByAid(Integer aid);
Address findLastModified(Integer uid);
© 配置映射
映射:
<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>
测试:
@Test
public void deleteByAid() {
Integer aid = 20;
Integer rows = mapper.deleteByAid(aid);
System.err.println("rows=" + rows);
}
@Test
public void findLastModified() {
Integer uid = 7;
Address result = mapper.findLastModified(uid);
System.err.println(result);
}
(a) 规划异常
删除时:DeleteException;
删除前:AddressNotFoundException,AccessDeniedException;
删除后:UpdateException。
(b) 接口与抽象方法
void delete(Integer aid, Integer uid, String username) throws AddressNotFoundException, AccessDeniedException, DeleteException, UpdateException;
© 实现抽象方法
私有化实现持久层中新增的方法:
/**
* 根据收货地址数据的id删除收货地址
* @param aid 收货地址数据的id
* @throws DeleteException 删除数据异常
*/
private void deleteByAid(Integer aid) {
Integer rows = addressMapper.deleteByAid(aid);
if (rows != 1) {
throw new DeleteException(
"删除收货地址失败!出现未知错误!");
}
}
/**
* 查询某用户最后修改的收货地址数据
* @param uid 用户的id
* @return 该用户最后修改的收货地址数据
*/
private Address findLastModified(Integer uid) {
return addressMapper.findLastModified(uid);
}
重写业务接口中的抽象方法:
@Transactional
public void delete(Integer aid, Integer uid, String username) throws AddressNotFoundException, AccessDeniedException, DeleteException, UpdateException {
// 根据参数aid查询数据
// 判断查询结果是否为null:AddressNotFoundException
// 判断查询结果中的uid与参数uid是否不同:AccessDeniedException
// 执行删除
// 判断此前的查询结果中的isDefault是否为0:return
// 根据参数uid统计收货地址数量
// 判断数量为0:return
// 根据参数uid查询最后修改的收货地址
// 根据查询到的最后修改的收货地址中的aid执行设置默认
}
实现:
@Override
@Transactional
public void delete(Integer aid, Integer uid, String username)
throws AddressNotFoundException, AccessDeniedException, DeleteException, UpdateException {
// 根据参数aid查询数据
Address result = findByAid(aid);
// 判断查询结果是否为null
if (result == null) {
// 是:AddressNotFoundException
throw new AddressNotFoundException(
"删除收货地址失败!尝试访问的数据不存在!");
}
// 判断查询结果中的uid与参数uid是否不同
if (!result.getUid().equals(uid)) {
// 是:AccessDeniedException
throw new AccessDeniedException(
"删除收货地址失败!不允许访问他人的数据!");
}
// 执行删除
deleteByAid(aid);
// 判断此前的查询结果中的isDefault是否为0:return
if (result.getIsDefault() == 0) {
return;
}
// 根据参数uid统计收货地址数量
Integer count = countByUid(uid);
// 判断数量为0:return
if (count == 0) {
return;
}
// 根据参数uid查询最后修改的收货地址
Address lastModifiedAddress = findLastModified(uid);
// 根据查询到的最后修改的收货地址中的aid执行设置默认
updateDefault(lastModifiedAddress.getAid(), username, new Date());
}
测试:
@Test
public void delete() {
try {
Integer aid = 26;
Integer uid = 7;
String username = "呵呵";
service.delete(aid, uid, username);
System.err.println("OK.");
} catch (ServiceException e) {
System.err.println(e.getClass().getName());
System.err.println(e.getMessage());
}
}
(a) 统一处理异常
处理:DeleteException
(b) 设计请求
设计“收货地址-删除”的请求方式:
/resources/id/command
请求路径:/addresses/{aid}/delete
请求参数:@PathVariable("aid") Integer aid, HttpSession session
请求方式:POST
响应数据:JsonResult
http://localhost:8080/addresses/18/delete
© 处理请求
```java
@RequestMapping("{aid}/delete")
public JsonResult delete(
@PathVariable(“aid”) Integer aid,
HttpSession session) {
// 从Session中获取uid和username
Integer uid = getUidFromSession(session);
String username = getUsernameFromSession(session);
// 调用业务层对象执行删除
addressService.delete(aid, uid, username);
// 返回
return new JsonResult<>(SUCCESS);
}
### 48. 收货地址-删除-前端界面