1.新增收货地址
t_address
sql
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;
注意name是关键字,所以需要用``
2.创建收货地址的实体类
在entity包下创建实体类Address继承BaseEntity类
java
/**
* 收货地址的实体类
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Address extends BaseEntity {
private Integer aid;//收货地址id
private Integer uid;//归属用户id
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;//是否默认 0-不默认,1-默认
}
3.持久层[Mapper]
1 各功能的开发顺序
当前收货地址功能模块:
- 第一个页面:列表的展示,修改,删除,设置默认
- 第二个页面:新增收货地址
开发顺序:新增收货地址->列表的展示->设置默认收货地址->删除收货地址->修改收货地址
2 需要规划要执行的sql语句
1.新增收货地址对应的是插入语句:
java
insert into t_address (aid以外的所有字段) values (字段值)
- 大部分平台都会规定一个用户的收货地址数量,这里规定最多20个.那么在插入用户新的地址之前就要先做查询操作.如果查询到的是刚好20,这并不是一个java语法的异常,可以认为是业务控制的异常,这个异常随后在service抛,在controller捕获
java
select count(*) from t_address where uid=?
2.设置接口和抽象方法
创建接口AddressMapper,在这个接口中定义上面两个SQL语句抽象方法定义
java
//收货地址持久层接口
public interface AddressMapper {
/**
* 插入用户的收货地址数据
*
* @param address 收货地址
* @return 受影响的行数
*/
Integer insertAddress(Address address);
/**
* 获取收货地址的数量(不能超过20),根据用户的uid
*
* @param uid 用户uid
* @return 收货地址数量
*/
Integer countAddress(Integer uid);
}
3.编写映射
1.在mapper标签中配置Address类属性与数据库中表的字段映射
XML
<resultMap id="AddressEntityMap" type="com.example.mycomputerstore.entity.Address">
<id column="aid" property="aid"/>
<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="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>
判断该映射是否配置成功:按着ctrl并点击type="com.cy.store.entity.Address"中的Address,如果能跳转到Address类说明映射成功
2.在AddressMapper.xml中配置以上两个抽象方法的映射
java
<insert id = "insertAddress" 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="countAddress" resultType="java.lang.Integer">
SELECT count(*) FROM t_address WHERE uid = #{uid}
</select>
4.单元测试
java
@SpringBootTest
@RunWith(SpringRunner.class)
public class AddressMapperTests {
@Autowired
private AddressMapper addressMapper;
@Test
public void insert() {
Address address = new Address();
address.setUid(11);
address.setPhone("133336");
address.setName("女朋友");
addressMapper.insert(address);
}
@Test
public void countByUid() {
Integer count = addressMapper.countByUid(11);
System.out.println(count);
}
}
4.业务层[service]
1. 规划异常
- 插入数据时用户不存在(被管理员误删等等),抛UsernameNotFoundException异常(已经有了,不需要重复创建)
- 当用户插入的地址是第一条时,需要将当前地址作为默认收货地址
- 实现办法:如果查询到统计总数为0则将当前地址的is_default值设置为1
- 如果查询的结果>=20,这时需要抛出业务控制的异常AddressCountLimitException
java
/**收货地址总数超出限制的异常(20条)*/
public class AddressCountLimitException extends ServiceException {
/**重写ServiceException的所有构造方法*/
}
- 插入数据时产生未知的异常InsertException(已经有了,不需要重复创建)
2 设计接口和抽象方法及实现
1.创建一个IAddressService接口,在接口中定义业务的抽象方法
因为mapper层接口该功能模块定义了两个抽象方法,所以就要在service层接口该功能模块也定义两个抽象方法?不是这样的,要看mapper层的这两个方法是依赖关系还是独立关系,如果某一个抽象方法依赖于另一个抽象方法,那就需要在业务层将这两个方法整合到一个方法中.一句话来说就是:一个功能模块可能需要多条sql语句
java
//收货地址业务层接口
public interface IAddressService {
/**
*这三个参数的由来:
* 1.首先肯定要有address
* 2.业务层需要根据uid查询该用户收货地址总数及新建地址时给字段uid赋值
* 但新建收货地址的表单中并没有哪个控件让输入用户uid,所以需要控制层将uid传给业务层
* 3.业务层在创建/修改收货地址时需要同时修改数据库中创建人/修改人的字段
* 但新建收货地址的表单中并没有哪个控件让输入用户username,所以需要控制层将username传给业务层
* 注意:> 可以用HttpSession session代替Integer uid, String username,但
* 这样写的话就需要把BaseController类下获取uid,username的方法重新封装到一个
* 类中并让IAddressServiceImp实现类继承该类,这样就需要微调一下代码逻辑,太麻
* 烦,并且,最好每一层只处理该层需要做的事情,session对象是控制层传递的,所以就
* 把session对象定义封装在控制层中,不需要在业务层中额外处理以降低耦合
*/
void addAddress(Integer uid, String username, Address address);
}
方法addNewAddress中三个参数的由来:
- 首先肯定要有address
- 业务层需要根据uid查询该用户收货地址总数及新建地址时给字段uid赋值
- 但新建收货地址的表单中并没有哪个控件让输入用户uid,所以需要控制层将uid传给业务层并在业务层封装到address对象中
- 业务层在创建/修改收货地址时需要同时修改数据库中创建人/修改人的字段
- 但新建收货地址的表单中并没有哪个控件让输入用户username,所以需要控制层将username传给业务层并在业务层封装到address对象中
可以用HttpSession session代替Integer uid, String username,但这样写的话就需要把BaseController类下获取uid,username的方法重新封装到一个类中并让AddressServiceImpl实现类继承该类,这样就需要微调一下代码逻辑,太麻烦,并且,最好每一层只处理该层需要做的事情,session对象是控制层传递的,所以就把session对象定义封装在控制层中,不需要在业务层中额外处理,这样可以降低耦合
2.创建一个AddressServiceImpl类实现接口中抽象方法
java
@Service
public class IAddressServiceImpl implements IAddressService {
@Autowired
private AddressMapper addressMapper;
//在添加用户的收货地址的业务层依赖于DistrictService的业务层接口
@Autowired
private IDistrictService districtService;
/**
* 为了方便日后修改最大收货地址数量,可以在配置文件
* application.properties中定义user.address.max-count=20
*/
@Value("${user.address.max-count}")
private Integer MaxAddress ;
@Override
public void addAddress(Integer uid, String username, Address address) {
//查询收货地址信息是否大于20
Integer count = addressMapper.countAddress(uid);
if(count >= MaxAddress) {
throw new AddressCountLimitException("收货地址不能超过20条");
}
//设置信息
address.setUid(uid);
Integer isDelete = count == 0 ? 1 : 0;//1表示默认收货地址,0反之
address.setIsDefault(isDelete);
address.setCreatedTime(new Date());
address.setCreatedUser(username);
address.setModifiedTime(new Date());
address.setModifiedUser(username);
//对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);
//插入收货地址
Integer row = addressMapper.insertAddress(address);
if(row != 1) {
throw new InsertException("未知错误 在 新建收货地址");
}
}
}
别忘了在配置文件application.properties中定义user.address.max-count=20
3 单元测试
在test下的service文件夹下创建AddressServiceTests测试类
java
@SpringBootTest
@RunWith(SpringRunner.class)
public class AddressServiceTests {
@Autowired
private IAddressService addressService;
@Test
public void addNewAddress() {
Address address = new Address();
address.setPhone("175726");
address.setName("男朋友");
addressService.addNewAddress(11,"mxy",address);
}
}
5.控制层[Controller]
1 处理异常
义务层抛出了收货地址总数超出上限的异常,在BaseController中进行捕获处理
java
else if (e instanceof AddressCountLimitException) {
result.setState(4003);
result.setMessage("用户的收货地址超出上限的异常");
}
2 设计请求
- /addresses/add_new_address
- post
- Address address,HttpSession session
- JsonResult<Void>
3. 处理请求
在controller包下创建AddressController并继承BaseController,该类用来处理用户收货地址的请求和响应
java
/**
* 用户收货地址
*/
@RestController
@RequestMapping("/address")
public class AddressController extends BaseController {
@Autowired
private IAddressService addressService;
/**
* 新增用户收货地址
*
* @param address
* @param session:这里使用session 是为了获取用户的uid和username
* @return
*/
@PostMapping("/add_new_address")
public JsonResult<Void> addAddress(Address address, HttpSession session) {
//先获取用户uid和username
Integer uid = getuidFromSession(session);
String username = getUsernameFromSession(session);
addressService.addAddress(uid, username, address);
return new JsonResult<>(OK);
}
}
6.前端页面
javascript
$("#btn-add-new-address").click(function (){
$.ajax({
url:"/address/add_new_address",
type:"POST",
data:$("#form-add-new-address").serialize(),
dataType:"JSON",
success(e){
if(e.state==200){
alert("新增收货地址成功")
}else{
alert("新增收货地址失败")
}
},
error(xhr){
alert("新增收货地址产生未知的异常"+xhr.status)
}
})
})