27. 收货地址-分析

关于“收货地址”数据的管理,涉及的功能有:增加,修改,删除,设为默认,显示列表。

以上功能的开发顺序应该是:增加 > 显示列表 > 设为默认 > 删除 > 修改。

28. 收货地址-创建数据表

创建收货地址数据表:

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;

29. 收货地址-创建实体类

创建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;}

30. 收货地址-增加-持久层

(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"><!-- 增加收货地址数据 --><!-- Integer addnew(Address address) --><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><!-- 统计某用户的收货地址的数量 --><!-- Integer countByUid(Integer uid) --><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)@SpringBootTestpublic class AddressMapperTests {@AutowiredAddressMapper mapper;@Testpublic void addnew() {Address address = new Address();address.setUid(1);address.setName("小刘同学");Integer rows = mapper.addnew(address);System.err.println("rows=" + rows);}@Testpublic void countByUid() {Integer uid = 100;Integer count = mapper.countByUid(uid);System.err.println("count=" + count);}}

31. 收货地址-增加-业务层

(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 {@Autowiredprivate 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项日志// 执行增加
}

具体实现代码为:

 @Overridepublic void create(Integer uid, String username, Address address)throws AddressCountLimitException, InsertException {// 基于参数uid查询该用户的收货地址数量Integer count = countByUid(uid);// 判断数量是否达到上限值if (count >= MAX_COUNT) {// 是:AddressCountLimitExceptionthrow new AddressCountLimitException("增加收货地址失败!当前收货地址的数量(" + count + ")已经达到上限(" + MAX_COUNT + ")!");}// 补全参数address中的数据:uidaddress.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)@SpringBootTestpublic class AddressServiceTests {@AutowiredIAddressService service;@Testpublic 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());}}}

32. 收货地址-增加-控制器层

(a) 统一处理异常

需要处理AddressCountLimitException

(b) 设计请求

设计“增加收货地址”的请求方式:

请求路径:/addresses/create
请求参数:Address address, HttpSession session
请求方式:POST
响应数据:JsonResult<Void>

© 处理请求

创建cn.tedu.store.controller.AddressController,继承自BaseController,在类之前添加@RestController@RequestMapping("addresses")注解,在类中添加@Autowired private IAddressService addressService;业务层对象:

@RestController
@RequestMapping("addresses")
public class AddressController extends BaseController {@Autowiredprivate IAddressService addressService;}

然后,添加处理请求的方法:

@RequestMapping("create")
public JsonResult<Void> create(Address address, HttpSession session) {// 从Session中获取uid和username// 调用业务层对象执行新增收货地址// 返回成功
}

完成后,打开浏览器,先登录,通过http://localhost:8080/addresses/create?name=Kitty进行测试。

检查完成后,可以将接口中的MAX_COUNT设置为更大一些的值。

33. 收货地址-增加-前端界面

34. 收货地址-导入省市区数据

进入MySQL控制台,执行指令以导入省市区数据

source 脚本文件路径

35. 服务器端提供省/市/区的数据查询功能

首先,应该创建与数据表对应的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<District> findByParent(String parent);

然后,复制得到DistrictMapper.xml文件,并配置以上抽象方法的映射:

 <select id="findByParent" resultType="cn.tedu.store.entity.District">SELECT id, parent,code, nameFROM t_dict_district WHERE parent=#{parent}ORDER BYcode ASC</select>

src/test/java下创建cn.tedu.store.mapper.DistrictMapperTests测试类,编写并执行单元测试:

 @RunWith(SpringRunner.class)@SpringBootTestpublic class DistrictMapperTests {@AutowiredDistrictMapper mapper;@Testpublic 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<District> getByParent(String parent);

创建cn.tedu.store.service.impl.DistrictServiceImpl类,实现IDistrictService接口,在类之前添加@Service注解,在类中添加@Autowired private DistrictMapper districtMapper;持久层对象:

@Service
public class DistrictServiceImpl implements IDistrictService {@Autowiredprivate DistrictMapper districtMapper;
}

然后,复制持久层接口中的抽象方法,粘贴到该实现类中,然后,私有化实现这2个方法:

/*** 获取全国所有省/某省所有市/某市所有区的列表* @param parent 父级单位的代号,如果获取全国所有省,则使用86作为父级代号* @return 匹配的省或市或区的列表*/
private List<District> findByParent(String parent) {return districtMapper.findByParent(parent);
}

重写接口中的抽象方法:

public List<District> getByParent(String parent) {return findByParent(parent);
}

完成后,在src/test/java下创建cn.tedu.store.service.DistrictServiceTests测试类,编写并执行单元测试:

 @RunWith(SpringRunner.class)@SpringBootTestpublic class DistrictServiceTests {@AutowiredIDistrictService service;@Testpublic 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<List<District>>
是否拦截:否,需要添加白名单

创建cn.tedu.store.controller.DistrictController,继承自BaseController,在类之前添加@RestController@RequestMapping("districts")注解,在类中添加@Autowired private IDistrictService districtService;业务层对象:

@RestController
@RequestMapping("districts")
public class DistrictController extends BaseController {@Autowiredprivate IDistrictService districtService;}

然后,添加处理请求的方法:

@GetMapping("/")
public JsonResult<List<District>> getByParent(String parent) {// 调用业务层对象执行查询// 返回成功和查询结果
}

然后,在拦截器的配置中,将/districts/**添加为白名单。

完成后,打开浏览器,先登录,通过http://localhost:8080/districts/?parent=86进行测试。

36. 收货地址-增加-业务层-补

首先,需要在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数据。

37. 收货地址-显示列表-持久层

(a) 规划SQL语句

查询所需要显示的列表的SQL语句大致是:

select *
from t_address
where uid=?

(b) 接口与抽象方法

AddressMapper中添加抽象方法:

/*** 获取某用户的收货地址数据的列表* @param uid 用户的id* @return 用户的收货地址数据的列表*/
List<Address> findByUid(Integer uid);

© 配置映射

如果查询列表中的字段列表使用*号,则表示查询所有字段,由于存在查询结果的列名与封装的对象的属性名不匹配的问题,所以,先配置一个<resultMap>(可参考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>

然后配置以上接口中的抽象方法:

 <!-- 获取某用户的收货地址数据的列表 --><!-- List<Address> findByUid(Integer uid) --><select id="findByUid"resultMap="AddressEntityMap">SELECT *FROM t_address WHERE uid=#{uid}ORDER BYis_default DESC,modified_time DESC</select>

AddressMapperTests中测试:

@Test
public void findByUid() {Integer uid = 7;List<Address> list = mapper.findByUid(uid);System.err.println("count=" + list.size());for (Address item : list) {System.err.println(item);}
}

38. 收货地址-显示列表-业务层

(a) 规划异常

(b) 接口与抽象方法

将持久层中的抽象方法复制到业务层接口,然后把方法名的find改成get

/*** 获取某用户的收货地址数据的列表* @param uid 用户的id* @return 用户的收货地址数据的列表*/
List<Address> 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;}然后添加接口中定义的抽象方法:@Overridepublic List<Address> getByUid(Integer uid) {return findByUid(uid);}完成后,进行测试:@Testpublic 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);}}

39. 收货地址-显示列表-控制器层

(a) 统一处理异常

(b) 设计请求

设计“收货地址-显示列表”的请求方式:

请求路径:/addresses/
请求参数:HttpSession session
请求方式:GET
响应数据:JsonResult<List<Address>>

© 处理请求

@GetMapping
public JsonResult<List<Address>> getByUid(HttpSession session) {}

完成后,打开浏览器,可通过http://localhost:8080/addresses/进行测试访问。

40. 收货地址-显示列表-前端界面

41. 收货地址-设为默认-持久层

(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);

© 配置映射

映射:

 <!-- 将指定的收货地址设置为默认 --><!-- Integer updateDefault(@Param("aid") Integer aid, @Param("username") String username, @Param("modifiedTime") Date modifiedTime) --><update id="updateDefault">UPDATEt_addressSETis_default=1,modified_user=#{username},modified_time=#{modifiedTime}WHEREaid=#{aid}</update><!-- 将指定用户的收货地址全部设置为非默认 --><!-- Integer updateNonDefault(Integer uid) --><update id="updateNonDefault">UPDATEt_addressSETis_default=0WHEREuid=#{uid}</update><!-- 根据收货地址id查询收货地址详情 --><!-- Address findByAid(Integer aid) --><select id="findByAid"resultMap="AddressEntityMap">SELECT *FROM t_address WHERE aid=#{aid}</select>

单元测试:

 @Testpublic void updateDefault() {Integer aid = 30;String username = "哈哈";Date modifiedTime = new Date();Integer rows = mapper.updateDefault(aid, username, modifiedTime);System.err.println("rows=" + rows);}@Testpublic void updateNonDefault() {Integer uid = 7;Integer rows = mapper.updateNonDefault(uid);System.err.println("rows=" + rows);}@Testpublic void findByAid() {Integer aid = 30;Address result = mapper.findByAid(aid);System.err.println(result);}

42. 收货地址-设为默认-业务层

(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@Transactionalpublic void setDefault(Integer aid, Integer uid, String username)throws AddressNotFoundException, AccessDeniedException, UpdateException {// 根据参数aid查询数据Address result = findByAid(aid);// 判断查询结果是否为nullif (result == null) {// 是:AddressNotFoundExceptionthrow new AddressNotFoundException("设置默认收货地址失败!尝试访问的数据不存在!");}// 判断查询结果中的uid与参数uid是否不同if (!result.getUid().equals(uid)) {// 是:AccessDeniedExceptionthrow new AccessDeniedException("设置默认收货地址失败!不允许访问他人的数据!");}// 将该用户的所有收货地址设置为非默认updateNonDefault(uid);// 将指定的收货地址设置为默认updateDefault(aid, username, new Date());}完成后,单元测试:@Testpublic 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());}}

43. 收货地址-设为默认-控制器层

(a) 统一处理异常

需要处理2个新的异常!

(b) 设计请求

设计“收货地址-设为默认”的请求方式:

请求路径:/addresses/{aid}/set_default
请求参数:@PathVariable("aid") Integer aid, HttpSession session
请求方式:POST
响应数据:JsonResult<Void>http://localhost:8080/addresses/18/set_default

© 处理请求

@RequestMapping("{aid}/set_default")
public JsonResult<Void> setDefault(@PathVariable("aid") Integer aid, HttpSession session) {// 从Session中获取uid和usernameInteger uid = getUidFromSession(session);String username = getUsernameFromSession(session);// 调用业务层对象执行设置默认addressService.setDefault(aid, uid, username);// 返回return new JsonResult<>(SUCCESS);
}

44. 收货地址-设为默认-前端界面

45. 收货地址-删除-持久层

(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);

© 配置映射

映射:

 <!-- 根据收货地址数据的id删除收货地址 --><!-- Integer deleteByAid(Integer aid) --><delete id="deleteByAid">DELETE FROMt_addressWHEREaid=#{aid}</delete><!-- 查询某用户最后修改的收货地址数据 --><!-- Address findLastModified(Integer uid) --><select id="findLastModified"resultMap="AddressEntityMap">SELECT*FROMt_addressWHEREuid=#{uid}ORDER BYmodified_time DESCLIMIT0,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);
}

46. 收货地址-删除-业务层

(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);}重写业务接口中的抽象方法:@Transactionalpublic 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@Transactionalpublic void delete(Integer aid, Integer uid, String username)throws AddressNotFoundException, AccessDeniedException, DeleteException, UpdateException {// 根据参数aid查询数据Address result = findByAid(aid);// 判断查询结果是否为nullif (result == null) {// 是:AddressNotFoundExceptionthrow new AddressNotFoundException("删除收货地址失败!尝试访问的数据不存在!");}// 判断查询结果中的uid与参数uid是否不同if (!result.getUid().equals(uid)) {// 是:AccessDeniedExceptionthrow new AccessDeniedException("删除收货地址失败!不允许访问他人的数据!");}// 执行删除deleteByAid(aid);// 判断此前的查询结果中的isDefault是否为0:returnif (result.getIsDefault() == 0) {return;}// 根据参数uid统计收货地址数量Integer count = countByUid(uid);// 判断数量为0:returnif (count == 0) {return;}// 根据参数uid查询最后修改的收货地址Address lastModifiedAddress = findLastModified(uid);// 根据查询到的最后修改的收货地址中的aid执行设置默认updateDefault(lastModifiedAddress.getAid(), username, new Date());}测试:@Testpublic 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());}}

47. 收货地址-删除-控制器层

(a) 统一处理异常

处理:DeleteException

(b) 设计请求

设计“收货地址-删除”的请求方式:

/resources/id/command请求路径:/addresses/{aid}/delete
请求参数:@PathVariable("aid") Integer aid, HttpSession session
请求方式:POST
响应数据:JsonResult<Void>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. 收货地址-删除-前端界面

电商项目实战--收货地址相关相关推荐

  1. 电商项目之收货地址理解

    1.收货地址增加(请求参数:HttpSession session,Shipping shipping) a.获取session中当前登陆用户 b.将用户id和地址进行关联,防止横向越权,没有用户id ...

  2. 软件测试电商项目实战(写进简历没问题)

    前言 说实话,在找项目的过程中,我下载过(甚至付费下载过)N多个项目.联系过很多项目的作者,但是绝大部分项目,在我看来,并不适合你拿来练习,它们或多或少都存在着"问题",比如: 1 ...

  3. 电商项目实战之分布式事务解决方案

    电商项目实战之分布式事务解决方案 本地事务 事务隔离级别 事务传播机制 分布式事务 CAP理论 选举与同步理论 BASE理论 解决方案 2PC模式(XA事务) 柔性事务-TCC事务补偿型方案 柔性事务 ...

  4. java spu sku_SpringBoot电商项目实战 — 商品的SPU/SKU实现

    最近事情有点多,所以系列文章已停止好多天了.今天我们继续Springboot电商项目实战系列文章.到目前为止,整个项目的架构和基础服务已经全部实现,分布式锁也已经讲过了.那么,现在应该到数据库设计及代 ...

  5. 前端电商项目实战,如何从 0 开始创造一个【考拉海购官网】?( 共6节教程 )

    文章目录 声明 一,关于页面还原度效果比较 二,第一组演示图是 考拉海购官网的 三,第二组演示图是 本次教程从0开发的 四,教程目录(共6节) 五,全部代码下载地址 新手提示 (1)如何从github ...

  6. 电商项目实战-项目模板-毕业设计

    下载地址:电商项目实战项目模板.毕业设计-Web服务器文档类资源-CSDN下载 ├── 基于vue电商管理系统.zip └── 电商项目实战     ├── 10.vuex     │   ├── c ...

  7. 电商项目实战第三节: CSS3+HTML5+JS 设计案例【考拉海购网站】之【分类导航栏】

    上一节:电商项目实战第二节: CSS3+HTML5+JS 设计案例[考拉海购网站]之[搜索框那一栏] 文章目录 [考拉海购网站]之[分类导航栏] 第一步,分析页面布局 第二步,写需要的html标签 i ...

  8. 电商项目实战第六节: CSS3+HTML5+JS 设计案例【考拉海购网站】之【页底信息,网站备案信息】

    上一节:电商项目实战第五节: CSS3+HTML5+JS 设计案例[考拉海购网站]之[商品栏及右侧垂直导航] 文章目录 电商项目实战第六节: CSS3+HTML5+JS 设计案例[考拉海购网站]之[页 ...

  9. 电商项目实战第四节: CSS3+HTML5+JS 设计案例【考拉海购网站】之【轮播图特效】

    上一节:电商项目实战第三节: CSS3+HTML5+JS 设计案例[考拉海购网站]之[分类导航栏] 文章目录 [考拉海购网站]之[轮播图特效] 第一步,根据页面布局写相应的html标签 index.h ...

  10. 电商项目实战之缓存与Redis分布式锁

    电商项目实战之缓存与Redis分布式锁 缓存失效 缓存穿透 缓存雪崩 缓存击穿 分布式缓存 分布式锁 SpringBoot整合Redisson实现分布式锁 实现过程 缓存和数据库一致性 场景分析 解决 ...

最新文章

  1. 他山之石:Github的使用
  2. mysql删除数据表show_简单的连接、删除数据库和show命令
  3. acl 服务器编程框架特点介绍
  4. 金融数据公司发展趋势小探
  5. cas单点登陆系统-casServer搭建
  6. Caused by: java.net.ConnectException: Connection refused: no further information
  7. 两个条件一个为false就运行_【上古十大神马,其中一个以虎为食,一个诛杀相柳,两个龙王之子】缅怀金庸—射雕英雄传200...
  8. python book pdf_The Django Book 第2版 中文PDF版[1.87MB]
  9. MCGrating光栅设计软件
  10. 【编程规范】 后端API接口设计编写与文档编写参考
  11. 论文-融合机器学习与知识推理的可解释性框架-李迪媛1, 康达周2
  12. no transaction is in progresss
  13. Angular 的 ngOnInit 和 Constructor 的区别
  14. Windows7旗舰版SP1_32位2018.10(装机版)
  15. c语言程序课程设计题目
  16. 计算几何入门 1.4:凸包的构造——Jarvis March算法
  17. xml图片太大_XML的大图片还是Goo的大球图?
  18. java开发报错怎么处理_Java开发中常见报错及解决办法
  19. 系统运维架构 DevOps
  20. 计算机技术论文搜索引擎,搜索引擎-毕设论文.doc

热门文章

  1. 扫描仪怎样装无线网络服务器,怎么设置打印机IP地址和安装扫描仪。
  2. WAFW00F、WebCrack
  3. 微信电影影视小程序系统源码
  4. 宝德Pr2500y服务器装系统,GP2500-LG41-24V
  5. 【Unity】【Odin Inspector】关于AOT Generation
  6. docker+fastfds搭建文件服务器
  7. 无人驾驶入门1:无人驾驶概览
  8. B2C项目系统软件测试计划
  9. [Python] 开发一个局域网联机小游戏
  10. java 循环读取文件_JAVA读写文件中的循环问题