获取省市区列表
1. 获取省市区列表-构建数据库+实体类
构建数据库表并向表中添加数据
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属性表示的是夫区域代码号
LOCK TABLES t_dict_district WRITE;
INSERT INTO t_dict_district VALUES (1,'110100','110101','东城区'),(2,'110100','110102','西城区')等等等等;
UNLOCK TABLES;
创建一个Distric实体类
import lombok.Data;/** 行政区域数据 */
@Data
public class District extends BaseEntity{private Integer id;private String parent;private String code;private String name;
}
2. 获取省市区列表-持久层
2.1 编写sql语句进行查询
2.2 定义抽象接口
District接口,定义抽象方法
import com.cy.store.entity.District;
import org.apache.ibatis.annotations.Mapper;import java.util.List;@Mapper
public interface DistrictMapper {/*** 根据父代号查询所有子级区域* @param parent 父代号* @return 所有子级区域*/List<District> findByParent(String parent);}
2.3 xml映射文件编写sql
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace:用于指定当前的映射文件和哪个接口进行映射,需要指定接口的文件路径 -->
<mapper namespace="com.cy.store.mapper.DistrictMapper"><select id="findByParent" resultType="com.cy.store.entity.District">SELECT * FROM t_district WHERE parent=#{parent}ORDER BY code ASC</select>
</mapper>
2.4 测试

import com.cy.store.entity.District;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;import java.util.List;@SpringBootTest
class DistrictMapperTest {@Autowiredprivate DistrictMapper districtMapper;@Testvoid findByParent() {List<District> list = districtMapper.findByParent("110100");for (District district : list) {System.out.println(district);}}
}
3. 获取省市区列表-业务层
3.1 创建DistrictService接口,定义抽象方法
import com.cy.store.entity.District;import java.util.List;public interface DistrictService {/*** 根据父代码查询所对应的所有子地区* @param parent 父代码* @return 包含子地区数据的列表*/List<District> findByParent(String parent);
}
3.2 创建 DistrictServiceImpl实现类,实现抽象方法
@Service
public class DistrictServiceImpl implements DistrictService {@Autowiredprivate DistrictMapper districtMapper;/*** 根据父代号查询区域信息* @param parent 父代号* @return 区域信息*/@Overridepublic List<District> findByParent(String parent) {List<District> list = districtMapper.findByParent(parent);
// 为了尽量避免无效数据的传递,可以将无效数据设置为null,提升效率for (District district : list) {district.setId( null);district.setParent(null);}return list;}
}
3.3 创建测试类进行测试
@SpringBootTest
class DistrictServiceTest {@Autowiredprivate DistrictService districtService;@Testvoid findByParent() {List<District> list = districtService.findByParent("130000");System.out.println(list);}
}
4. 获取省市区列表-控制层
4.1 设计请求
/districts/
GET
String parent
JsonResult<List<District>>
4.2 处理请求
创建DistrictController类,编写处理请求的方法
@RestController
@RequestMapping("/districts")
public class DistrictController extends BaseController{@Autowiredprivate DistrictService districtService;@RequestMappingpublic JsonResult<List<District>> getByParent(String parent) {List<District> data = districtService.findByParent(parent);return new JsonResult<>(OK, data);}
}
4.3 将districts请求放入拦截白名单中

启动项目,运行测试

5. 获取省市区列表-前端业务代码
- 注释掉原来通过js来完成省市区列表加载的js代码
将省市区数据放在了数据库,不需要通过js文件来获取了,所以addAddress.html页面的这两行js代码需要注掉
<!-- <script type="text/javascript" src="../js/distpicker.data.js"></script>-->
<!-- <script type="text/javascript" src="../js/distpicker.js"></script>-->
- 检查前端页面在提交省市区数据时是否有相关name属性和id属性
- 运行前端看是否还可以正常保存数据(除了省市区之外)

获取省市区的名称
1. 获取省市区的名称-持久层
1.1 规划sql语句
通过当前code来获取省市区的名称
SELECT name FROM t_dict_district WHERE code=#{code}
1.2 District接口中定义抽象方法
/*** 根据区域代号查询区域名称* @param code 区域代号* @return 区域名称*/String findNameByCode(String code);
1.3 在xml文件中编写sql映射
<select id="findNameByCode" resultType="java.lang.String">SELECT name FROM t_dict_district WHERE code=#{code}</select>
1.4 测试
@Testvoid findNameByCode() {String name = districtMapper.findNameByCode("610000");System.out.println(name);}
2. 获取省市区的名称-业务层
业务层没有异常需要处理
2.1 定义DistrictService抽象接口中的抽象方法
/*** 根据代码查询省市区名称* @param code 代码* @return 名称*/String findNameByCode(String code);
2.2 实现类实现抽象方法
/*** 根据区域代号查询区域名称* @param code 区域代号* @return 区域名称*/@Overridepublic String findNameByCode(String code) {return districtMapper.findNameByCode(code);}
3. 获取省市区的名称-业务层优化
当用户进行收货地址新增操作时,有三个字段(省、市、区)是需要手动封装的,依赖于DistrictService业务层,通过code获取省市区的名称,将其封装到address中,这样address对象中就会有用户所有的收货地址的信息

import com.cy.store.entity.Address;
import com.cy.store.mapper.AddressMapper;
import com.cy.store.service.AddressService;
import com.cy.store.service.DistrictService;
import com.cy.store.service.ex.AddressCountLimitException;
import com.cy.store.service.ex.InsertException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;import java.util.Date;@Service
public class AddressServiceImpl implements AddressService {
// 依赖注入@Autowiredprivate AddressMapper addressMapper;@Autowiredprivate DistrictService districtService;// 获取配置文件中设置的默认收货地址数量上限@Value("${user.address.max-count}")private Integer maxCount;/*** 添加用户的收货地址* @param uid 用户id* @param username 用户名* @param address 收货地址数据*/@Overridepublic void addNewAddress(Integer uid, String username, Address address) {Integer count = addressMapper.countByUid(uid);if (count>=maxCount){throw new AddressCountLimitException("用户收货地址超出上限");}
// 对address对象中的数据进行补全:省市区String provinceName = districtService.findNameByCode(address.getProvinceCode());String cityName = districtService.findNameByCode(address.getCityCode());String areaName = districtService.findNameByCode(address.getAreaCode());address.setProvinceName(provinceName);address.setCityName(cityName);address.setAreaName(areaName);// 补全 数据address.setUid(uid);
// 1 表示默认地址 0表示非默认Integer isDefault = count==0?1:0;address.setIsDefault(isDefault);address.setCreatedUser( username);address.setModifiedUser(username);address.setCreatedTime(new Date());address.setModifiedTime(new Date());Integer rows = addressMapper.insert(address);if (rows!=1){throw new InsertException("插入收货地址数据时产生未知的异常");}}
}
4. 获取省市区的名称-前端页面
addAddress.html页面
- 在加载该页面时三个下拉列表的内容都显示为"-----请选择-----"
- 没有选择省份时,市区的默认均为"-----请选择-----"
- 当一开始加载页面时就发送一个请求,将86传到后端,查到所有省份信息,并返回前端进行渲染
- 当选择 省份后,将省份的名称作为请求参数传递到后端,查询市的名称,区同理
点击四川省后发送请求获取其下的市,并且将获取到的市罗列在市区域下拉列表中
省点击"-----请选择-----“则需要把市,县内容填充为”-----请选择-----"终止请求而不是程序继续跑下去
切换省份时,市,县内容更换为"-----请选择-----"

完整代码如下
<script type="text/javascript">let defaultOption="<option value='0'>---- 请选择 ----</option>";$(document).ready(function () {// 展示省列表showProvinceList();
// 市列表和区列表设为默认值$("#city-list").append(defaultOption);$("#area-list").append(defaultOption);});// 监听省份列表的change改变事件$("#province-list").change(function () {// 获取选中的省的值 0/..let parent = $("#province-list").val();// 清空select下拉列表中的所有option元素$("#city-list").empty();$("#area-list").empty();// 默认填充的是默认值”请选择“$("#city-list").append(defaultOption);$("#area-list").append(defaultOption)// 判断获取的省的值是否为0,如果为0,则不请求市列表和区列表if (parent == 0) {return;}$.ajax({url: "/districts",type: "GET",dataType: "json",data:"parent=" + parent, // 请求挂的参数success: function (json) {if (json.state == 200) {// 获取省列表,包括所哟省名称let list = json.data;// 遍历省列表for (let i = 0; i < list.length; i++) {let opt ="<option value='" + list[i].code + "'>" + list[i].name + "</option>";$("#city-list").append(opt);}}else{alert("获取城市列表失败")}}})})// 监听城市列表的change改变事件$("#city-list").change(function () {// 获取选中的省的值 0/..let parent = $("#city-list").val();// 清空select下拉列表中的所有option元素$("#area-list").empty();// 默认填充的是默认值”请选择“$("#area-list").append(defaultOption)// 判断获取的省的值是否为0,如果为0,则不请求市列表和区列表if (parent == 0) {return;}$.ajax({url: "/districts",type: "GET",dataType: "json",data:"parent=" + parent, // 请求挂的参数success: function (json) {if (json.state == 200) {// 获取省列表,包括所哟省名称let list = json.data;// 遍历省列表for (let i = 0; i < list.length; i++) {let opt ="<option value='" + list[i].code + "'>" + list[i].name + "</option>";$("#area-list").append(opt);}}else{alert("获取城市列表失败")}}})})// 获取省列表function showProvinceList() {$.ajax({url: "/districts",type: "POST",dataType: "json",data:"parent=86", // 请求挂的参数success: function (json) {if (json.state == 200) {// 获取省列表,包括所哟省名称let list = json.data;// 遍历省列表for (let i = 0; i < list.length; i++) {let opt ="<option value='" + list[i].code + "'>" + list[i].name + "</option>";$("#province-list").append(opt);}}else{alert("获取省列表失败")}}})}<!-- 监听按钮是否被点击,如果被点击,触发函数 -->$("#btn-add-new-address").click(function () {// ajax发送请求$.ajax({url: "/addresses/addNewAddress",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>
