新增收货地址
(一)新建数据库
在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;
(二)收货地址实体类
在entity包下创建实体类Address继承baseEntity类
/**收货地址额实体类*/ public class Address extends BaseEntity { 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; /** * get,set * equals和hashCode * toString */ }
(三)新增收货地址(持久层)
1.规划sql
1.新增收货地址对应的是插入语句:
insert into t_address (aid以外的所有字段) values (字段值)
2.大部分平台都会规定一个用户的收货地址数量,这里规定最多20个.那么在插入用户新的地址之前就要先做查询操作.如果查询到的是刚好20,这并不是一个java语法的异常,可以认为是业务控制的异常,这个异常随后在service抛,在controller捕获
select count(*) from t_address where uid=?
2.设计接口和抽象方法
创建接口AddressMapper,在这个接口中定义上面两个SQL语句抽象方法定义
public interface AddressMapper { //新增地址 Integer insert(Address address); //统计总地址 Integer countByUid(Integer uid); }
创建一个AddressMapper的映射文件,在这个文件中添加抽象方法映射到sql上
<resultMap id="AddressEntityMap" type="com.cy.store.entity.Address"> <id column="aid" property="aid"/> <result column="province_name" property="provinceName"/> <result column="province_code" property="provinceCode"/> <result column="city_name" property="cityName"/> <result column="city_code" property="cityCode"/> <result column="area_name" property="areaName"/> <result column="area_code" property="areaCode"/> <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> <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>
(四)新增收货地址(业务层)
1.规划异常
插入数据时用户不存在(被管理员误删等等),抛UsernameNotFoundException异常(已经有了,不需要重复创建)
插入数据时产生未知的异常InsertException(已经有了,不需要重复创建)
当用户插入的地址是第一条时,需要将当前地址作为默认收货地址
实现办法:如果查询到统计总数为0则将当前地址的is_default值设置为1
如果查询的结果>=20,这时需要抛出业务控制的异常AddressCountLimitException
/**收货地址总数超出限制的异常(20条)*/ public class AddressCountLimitException extends ServiceException { /**重写ServiceException的所有构造方法*/ }
2.设计接口和抽象方法及实现
1.创建一个IAddressService接口,在接口中定义业务的抽象方法
public interface IAddressService { //新增收货地址 void addNewAddress(Integer uid,String username,Address address); }
2.创建一个AddressServiceImpl类实现接口中抽象方法
//实现方法 @Service public class AddressServiceImpl implements IAddressService { @Resource private AddressMapper addressMapper; @Resource private UserMapper userMapper; //在配置文件application.properties中定义user.address.max-count=20 @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("收货地址大于20条"); } User user = userMapper.findUid(uid); if (user==null || user.getIsDelete()==1){ throw new UsernameNotFoundException("用户未登录"); } address.setUid(uid); Integer isDefault=count==0?1:0; address.setIsDefault(isDefault); address.setCreatedTime(new Date()); address.setModifiedUser(username); address.setModifiedTime(new Date()); address.setCreatedUser(username); Integer insert = addressMapper.insert(address); if (insert!=1){ throw new InsertException("添加地址有错误"); } } }
(五)新增收货地址(控制层)
1.处理异常
业务层抛出了收货地址总数超出上限的异常,在BaseController中进行捕获处理
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,该类用来处理用户收货地址的请求和响应。
@PostMapping("add_new_address") public JsonResult<Void> add(Address address, HttpSession session){ Integer getuidfromsession = getuidfromsession(session); String getusernamesession = getusernamesession(session); addressService.addNewAddress(getuidfromsession,getusernamesession,address); return new JsonResult<>(ok); }
(六)新增收货地址(前端页面)
<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.创建t_dict_district表
CREATE TABLE t_dict_district ( id INT(11) NOT NULL AUTO_INCREMENT, parent VARCHAR(6) DEFAULT NULL, `code` VARCHAR(6) DEFAULT NULL, `name` VARCHAR(16) DEFAULT NULL, PRIMARY KEY (id) ) ENGINE=INNODB DEFAULT CHARSET=utf8;
parent代表父区域的代码号
code代表自身的代码号
省的父代码号是+86,代表中国
向该表中插入省市区数据
LOCK TABLES t_dict_district WRITE; INSERT INTO t_dict_district VALUES (1,'110100','110101','东城区'),(2,'110100','110102','西城区')等等等等; UNLOCK TABLES;
二)创建省市区的实体类
在包entity下创建实体类District(不需要继承BaseEntity,但因为没有继承BaseEntity所以需要实现接口Serializable序列化)
/**省市区的数据实体类*/ public class District implements Serializable { private Integer id; private String parent; private String code; private String name; /** * get,set * equals和hashCode * toString */ }
(三)获取省市区列表(持久层)
1.规划sql
select * from t_dict_district where parent=? order by ASC
2.设计接口和抽象方法
在mapper层下创建接口DistrictMapper
在这里插入代码片
(四)获取省市区列表(业务层)
1.设计接口和抽象方法及实现
1.创建一个接口IDistrictService,并定义抽象方法
public interface IDistrictService { /** * 根据父代码号来查询区域信息(省或市或区) * @param parent 父代码号 * @return 多个区域的信息 */ List<District> getByParent(String parent); }
2.创建DistrictServiceImpl实现类来实现抽象方法
@Service public class DistrictServiceImpl implements IDistrictService { @Resource private DistrictMapper districtMapper; @Override public List<District> getByParent(String parent) { List<District> districtList = districtMapper.findByParent(parent); for (District d: districtList) { d.setParent(null); d.setId(null); } return districtList; } }
(五)获取省市区列表(控制层)
1.设计请求
请求路径:/districts/
请求方式:GET
请求参数:String parent
响应结果:JsonResult<List< District>
2.处理请求
@RestController @RequestMapping("districts") public class DistrictController extends BaseController{ @Autowired private IDistrictService districtService; @GetMapping({"/",""}) public JsonResult<List<District>> getByParent(String parent){ List<District> list = districtService.getByParent(parent); return new JsonResult<>(ok,list); } }
**为了能不登录也可以访问该数据,需要将districts请求添加到白名单中:
在LoginInterceptorConfigure类的addInterceptors方法中添加代码:patterns.add(“/districts/ * *”); **
(六)获取省市区列表(前端页面)
1.原始的下拉列表展示是将数据放在js,再动态获取js中的数据,而目前为止我们已经将数据放在了数据库,所以不能让它再使用这种办法了,所以需要注释掉addAddress.html页面的这两行js代码:
<script type="text/javascript" src="../js/distpicker.data.js"></script> <script type="text/javascript" src="../js/distpicker.js"></script>
获取省市区名称
(一)获取省市区名称(持久层)
1.规划sql
根据当前code来获取当前省市区的名称,对应就是一条查询语句
select * from t_dict_district where code=?
2.设计接口和抽象方法
在DistrictMapper接口定义findNameByCode方法
//根据当前code来获取当前省市区的名称,对应就是一条查询语句 List<District> findNameByCode(String code);
在DistrictMapper.xml文件中添加findNameByCode方法的映射
<select id="findNameByCode" resultType="java.lang.String"> select * from t_dict_district where code=#{code} </select>
(二)获取省市区名称(业务层)
1.规划异常
没有异常需要处理
2.设计接口和抽象方法及实现
1.在IDistrictService接口定义对应的业务层接口中的抽象方法
//根据code查询 String getNameByCode(String code);
2.在DistrictServiceImpl实现此方法
@Override public String getNameByCode(String code) { String name = districtMapper.findNameByCode(code); return name; }
(三)获取省市区名称(控制层)
实际开发中在获取省市区名称时并不需要前端传控制层,然后传业务层,再传持久层,而是在新增收货地址的业务层需要获取省市区名称,也就是说获取省市区名称的模块不需要控制层,只是需要被新增收货地址的业务层所依赖.
(四)获取省市区名称(业务层优化)
1.添加地址层依赖于IDistrictService层
@Autowired private IDistrictService districtService;
2.在AddressServiceImpl的方法中将DistrictService接口中获取到的省市区数据封装到address对象,(此时address就包含了所有用户收货地址的数据)
/** * 对address对象中的数据进行补全:省市区的名字看前端代码发现前端传递过来的省市区的name分别为: * provinceCode,cityCode,areaCode,所以这里可以用address对象的get方法获取这三个的数据 */ 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);
(五)获取省市区名称(前端页面)
在addAddress.html页面中来编写对应的省市区展示及根据用户的不同选择来限制对应的标签中的内容
/**因为清空后下拉列表的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("县的信息加载失败") } } }); });
后记
👉👉💕💕美好的一天,到此结束,下次继续努力!欲知后续,请看下回分解,写作不易,感谢大家的支持!! 🌹🌹🌹