2019_07_04store 收货地址 第六天

本文介绍了一个电商应用中收货地址管理模块的实现过程,包括实体类设计、持久层、业务层和控制层的开发,以及省市区数据的查询功能。

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)
@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);
	}
	
}

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

(a) 规划异常

插入数据可能产生InsertException

如果需要限制用户创建的收货地址数据,则达到上限仍尝试创建,就会产生cn.tedu.store.service.ex.AddressCountLimitException

控制基类加入以下异常:

else if (e instanceof AddressCountLimitException) {
jr.setState(4003);
}

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

具体实现代码为:
private static final Integer MAX_COUNT = 3;

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

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 {

	@Autowired
	private IAddressService addressService;

}

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

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

完成后,打开浏览器,先登录,通过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, 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<District> 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<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)
@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<List<District>>
是否拦截:否,需要添加白名单

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

}

然后,添加处理请求的方法:
@RestController
@RequestMapping(“districts”)
public class DistrictController extends BaseController {

@Autowired
private IDistrictService districtService;

@GetMapping("/")
public JsonResult<List<District>> getByParent(String parent) {
	// 调用业务层对象执行查询
	List<District> data
		= districtService.getByParent(parent);
	// 返回成功和查询结果
	return new JsonResult<>(SUCCESS, data);
}

然后,在拦截器的配置中,将/districts/**添加为白名单。
config配置文件中:
patterns.add("/districts/**");

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员西柚柚

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值