文章目录
1 用户端定位
2.1 需求分析
本项目在用户端和服务端都有定位的需求,本节分析用户端即小程序端的定位需求。
2.1.1 用户端首页定位
用户端在小程序认证通过后会自动进行定位,也可以在首页手动定位,定位成功后用户在查询家政服务项目时会根据定位的城市查询该城市有哪些服务项目。
手动定位过程如下图:
2.1.2 高德地图配置
小程序端的定位是通过手机的定位模块进行定位,定位成功获取经纬度坐标,平台根据经纬度坐标请求地图服务获取经纬度坐标对应的具体位置。
小程序首先通过微信提供的方法拿到经纬度坐标,然后请求后端获取具体的位置,后端会请求高德地图根据经纬度获取具体的城市信息。
要测试用户端定位的流程首先需要在高德地图开通地图定位服务。
参考“高德地图web服务配置文档” 获取访问接口的key。
进入nacos配置jzo2o-publics.yml中高德地图key。
2.1.3 测试定位
启动成功,点击“快速登录”,先同意服务条款,再允许获取位置信息,点击“允许”观察Network,请求定位接口/publics/map/regeo?location=,如下图:
到此说明定位测试通过。
下边介绍一种虚拟定位的设置,这个在开发中经常使用,虚拟定位即不是按手机位置进行定位,比如:你在北京,想测试定位到郑州某个位置该如何操作呢?
在微信开发环境可以指定小程序虚拟定位。
进入https://lbs.amap.com/tools/picker,选择一个北京的地方
116.394,39.905
首先打开虚拟定位,指定经纬度,如下图:
重新定位发起请求
code: 200
data: {cityCode: "010", province: "北京市", city: null, district: "西城区",…}
city: null
cityCode: "010"
district: "西城区"
fullAddress: "北京市西城区西长安街街道人民大会堂全国人大常委会"
province: "北京市"
msg: "OK"
2.2 阅读代码
2.2.1 用户端定位交互流程
2.2.2 阅读代码
定位过程中小程序请求publics服务的接口查询经纬度对应的位置信息,调用/publics/map/regeo接口,下边阅读/publics/map/regeo接口。
在com.jzo2o.publics.controller.outer.MapController中
@GetMapping("/regeo")
@ApiOperation("根据经纬度查询地址信息")
@ApiImplicitParams({
@ApiImplicitParam(name = "location", value = "经纬度", required = true, dataTypeClass = String.class)
})
public MapLocationResDTO getCityCodeByLocation(@NotNull(message = "坐标不能为空") @RequestParam("location") String location) {
MapLocationDTO mapLocationDTO = mapService.getCityCodeByLocation(location);
return BeanUtil.toBean(mapLocationDTO, MapLocationResDTO.class);
}
接口文档:
http://localhost:11503/publics/doc.html#/default/%E5%9C%B0%E5%9B%BE%E6%9C%8D%E5%8A%A1%E7%9B%B8%E5%85%B3%E6%8E%A5%E5%8F%A3/getCityCodeByLocationUsingGET
通过mapService的getCityCodeByLocation方法调用高德地图的查询地理编码接口
public MapLocationDTO getCityCodeByLocation(String location) {
Map<String, Object> params = new HashMap();
params.put("location", location);
params.put("key", this.amapProperties.getKey());
String jsonStr = HttpRequest.get("https://restapi.amap.com/v3/geocode/regeo?").form(params).execute().body();
JSONObject jsonObject = JSONUtil.parseObj(jsonStr);
String cityCode = (String)jsonObject.getJSONObject("regeocode").getJSONObject("addressComponent").get("citycode");
Object province = jsonObject.getJSONObject("regeocode").getJSONObject("addressComponent").get("province");
Object city = jsonObject.getJSONObject("regeocode").getJSONObject("addressComponent").get("city");
Object district = jsonObject.getJSONObject("regeocode").getJSONObject("addressComponent").get("district");
Object fullAddress = jsonObject.getJSONObject("regeocode").get("formatted_address");
return MapLocationDTO.builder().province(ObjectUtil.isEmpty(province) ? null : province.toString()).city(ObjectUtil.isEmpty(city) ? null : city.toString()).district(ObjectUtil.isEmpty(district) ? null : district.toString()).fullAddress(ObjectUtil.isEmpty(fullAddress) ? null : fullAddress.toString()).cityCode(cityCode).build();
}
高德地图的查询地理编码接口定位如下图:
接口文档地址:https://lbs.amap.com/api/webservice/guide/api/georegeo,大家可自行阅读。
最终接口返回省、市、县、详细位置等信息,如下:
为什么要调用高德地图的查询地理编码接口呢?
在foundations数据库的region区域表中有一列是city_code,高德地图返回的详细信息中city_code与region区域表中city_code是一致的,如下图:
小程序拿到的是经纬度坐标,通过调用此接口就可以根据经纬度得到city_code从而关联到平台具体的区域。
region表的city_code从哪来?
先从从高德地图下载拿到全国的区域信息,包括了city_code(https://lbs.amap.com/api/webservice/download)
将下载得到AMap_adcode_citycode.xlsx文件处理为json文件由前端进行保存。
前端在添加区域时从该json文件中选择区域,如下图:
区域信息中包括了从高德地图拿到的city_code,添加一个区域将city_code保存到了region表中。
2 我的地址簿-实战
注意:我的地址簿在用户下单时需要使用,优先完成。
2.1 需求分析
2.1.1 新增地址簿
用户下单时需要选择服务地址(相当于收货地址),在“我的”–》“我的地址”界面维护地址簿信息。
点击“我的地址”进入地址簿管理界面
点击“新增地址”:
点击“定位”打开定位窗口
默认通过手机定位模块定位到当前所在位置,也可以输入地址进行搜索,点击具体的地址,确认无误点击“确定”完成定位。
完成定位后在城市和详细地址栏中自动填入定位的地址,如下图:
注意:前端提交的经纬度是一个字符串,格式为:“经度,纬度”,前端请求publics服务进行定位,在调试时需要启动publics服务。
填写联系人和电话,点击“确定”
点击“确定”将地址簿信息保存成功。
注意:默认地址一个人只有一个,如果设置新地址为默认地址需要取消旧的默认地址。
2.1.2 地址簿查询
地址簿新增成功在我的地址中查询,如下图:
2.1.3 地址簿编辑
进入我的地址页面,点击“编辑”对地址簿进行修改
2.1.4 地址簿删除
进入我的地址页面,点击“删除”对地址簿进行删除
2.1.5 批量删除
点击“管理”对地址簿批量删除
2.2 数据表设计
到jzo2o-customer数据库的address_book表中。
CREATE TABLE `address_book` (
`id` bigint NOT NULL COMMENT '主键',
`user_id` bigint NOT NULL COMMENT '用户id',
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '名称',
`phone` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '电话',
`province` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '省份',
`city` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '市级',
`county` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '区/县',
`address` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '详细地址',
`lon` double(10,5) DEFAULT NULL COMMENT '经度',
`lat` double(10,5) DEFAULT NULL COMMENT '纬度',
`is_default` int NOT NULL DEFAULT '0' COMMENT '是否为默认地址,0:否,1:是',
`is_deleted` int NOT NULL DEFAULT '0' COMMENT '是否已删除,0:未删除,1:已删除',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`create_by` bigint DEFAULT NULL COMMENT '创建者',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`update_by` bigint DEFAULT NULL COMMENT '更新者',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC COMMENT='地址薄'
2.3 接口设计
2.3.1 新增地址簿
2.3.1.1 新增地址簿接口
接口名称:新增地址簿
接口路径:POST/customer/consumer/address-book
请求数据类型 application/json
2.3.1.2 controller
创建com.jzo2o.customer.controller.consumer.AddressBookController
@RestController("addressBookController")
@RequestMapping("/consumer/address-book")
@Api(tags = "用户端 - 地址薄相关接口")
public class AddressBookController {
@Resource
private IAddressBookService addressBookService;
/**
* 新增地址薄
* @param addressBookUpsertReqDTO
*/
@PostMapping
@ApiOperation("新增地址薄")
public void add(@RequestBody AddressBookUpsertReqDTO addressBookUpsertReqDTO) {
addressBookService.add(addressBookUpsertReqDTO);
}
}
2.3.1.3 service
接口:
void add(AddressBookUpsertReqDTO addressBookUpsertReqDTO);
实现
过程:1、先从threadlocal中获取当前用户id。2、查询是否有默认地址。3、最后组装数据,完成插入
@Override
@Transactional
public void add(AddressBookUpsertReqDTO addressBookUpsertReqDTO) {
AddressBook addressBook= BeanUtil.toBean(addressBookUpsertReqDTO, AddressBook.class);
//1.获取当前用户id
Long userId = UserContext.currentUserId();
addressBook.setUserId(userId);
//2.默认地址处理
if(addressBook.getIsDefault().equals(1)) {
//2.1.查询当前用户的默认地址
AddressBook defaultAddress = lambdaQuery()
.eq(AddressBook::getUserId, userId)
.eq(AddressBook::getIsDefault, "1")
.one();
//2.2.如果有默认地址,将其改为非默认
if(defaultAddress != null) {
defaultAddress.setIsDefault(0);
updateById(defaultAddress);
}
}
//3.新增地址
save(addressBook);
}
2.3.1.4 测试
因为还没写page,所以前端暂时没法看,查看数据库
已经成功插入
2.3.1.5 高德地图地理编码
发现个小问题,忘了插入经纬度了。那就用高德正向地址编码一下获取经纬度。
@Resource
private MapApi mapApi;
@Override
public void add(AddressBookUpsertReqDTO addressBookUpsertReqDTO) {
//0.设置经纬度
LocationResDTO locationByAddress = mapApi.getLocationByAddress(addressBookUpsertReqDTO.getAddress());
String location=locationByAddress.getLocation();
Double lon= Double.valueOf(location.split(",")[0]);
Double lat= Double.valueOf(location.split(",")[1]);
AddressBook addressBook= BeanUtil.toBean(addressBookUpsertReqDTO, AddressBook.class);
addressBook.setLon(lon);
addressBook.setLat(lat);
//1.获取当前用户id
Long userId = UserContext.currentUserId();
addressBook.setUserId(userId);
2.3.2 地址簿查询
2.3.2.1 地址薄分页查询接口
接口名称:地址薄分页查询
接口路径:GET/customer/consumer/address-book/page
请求数据类型 application/x-www-form-urlencoded
2.3.2.2 响应实体类-AddressResDto
创建com.jzo2o.customer.model.dto.response.AddressResDto
@Data
@ApiModel("地址响应体")
public class AddressResDto {
/**
* 详细地址
*/
@ApiModelProperty("详细地址")
private String address;
/**
* 市
*/
@ApiModelProperty("市")
private String city;
/**
* 区
*/
@ApiModelProperty("区")
private String county;
/**
* 经度
*/
@ApiModelProperty("经度")
private Double lon;
/**
* 纬度
*/
@ApiModelProperty("纬度")
private Double lat;
/**
* 更新时间
*/
@ApiModelProperty("更新时间")
private String updateTime;
/**
* 用户id
*/
@ApiModelProperty("用户id")
private Long userId;
/**
* 是否为默认地址
*/
@ApiModelProperty("是否为默认地址")
private Integer isDefault;
/**
* 省份
*/
@ApiModelProperty("省份")
private String province;
/**
* 创建时间
*/
@ApiModelProperty("创建时间")
private String createTime;
/**
* 手机号
*/
@ApiModelProperty("手机号")
private String phone;
/**
* 名称
*/
@ApiModelProperty("名称")
private String name;
/**
* 地址id
*/
@ApiModelProperty("地址id")
private Long id;
}
2.3.2.3 controller
@GetMapping("/page")
@ApiOperation("分页查询地址薄")
public PageResult<AddressResDto> page(AddressBookPageQueryReqDTO addressBookPageQueryReqDTO) {
return addressBookService.pageAddress(addressBookPageQueryReqDTO);
}
2.3.2.4 service
接口
PageResult<AddressResDto> pageAddress(AddressBookPageQueryReqDTO addressBookPageQueryReqDTO);
实现
@Override
public PageResult<AddressResDto> pageAddress(AddressBookPageQueryReqDTO addressBookPageQueryReqDTO) {
return PageHelperUtils.selectPage(addressBookPageQueryReqDTO,
() -> baseMapper.queryAddressListByUserId(UserContext.currentUserId()));
}
2.3.2.5 mapper
接口
List<AddressResDto> queryAddressListByUserId(@Param("userId") Long userId);
xml
<select id="queryAddressListByUserId" resultType="com.jzo2o.customer.model.dto.response.AddressResDto"
parameterType="java.lang.Long">
SELECT * FROM address_book WHERE user_id=#{userId} AND is_deleted=0
</select>
2.3.1.5 测试
2.3.3 地址薄编辑
2.3.3.1 地址薄详情接口
接口名称:地址薄详情
接口功能:修改地址簿前调用此接口先查询在页面显示
接口路径:GET/customer/consumer/address-book/{id}
2.3.3.2 controller
@GetMapping("/{id}")
@ApiOperation("根据id查询地址薄")
public AddressBook getById(@PathVariable("id") Long id) {
return addressBookService.getById(id);
}
2.3.3.3 地址薄修改接口
接口名称:地址薄修改
接口功能:地址薄修改提交
接口路径:PUT/customer/consumer/address-book/{id}
请求数据类型 application/json
2.3.3.4 controller
@PutMapping("/{id}")
@ApiOperation("修改地址薄")
public AddressBook update(@PathVariable("id") Long id, @RequestBody AddressBookUpsertReqDTO addressBookUpsertReqDTO) {
return addressBookService.update(id, addressBookUpsertReqDTO);
}
2.3.3.5 service
接口
AddressBook update(Long id, AddressBookUpsertReqDTO addressBookUpsertReqDTO);
实现
@Override
@Transactional
public AddressBook update(Long id, AddressBookUpsertReqDTO addressBookUpsertReqDTO) {
//1.设置经纬度
LocationResDTO locationByAddress = mapApi.getLocationByAddress(addressBookUpsertReqDTO.getAddress());
String location=locationByAddress.getLocation();
Double lon= Double.valueOf(location.split(",")[0]);
Double lat= Double.valueOf(location.split(",")[1]);
AddressBook addressBook= BeanUtil.toBean(addressBookUpsertReqDTO, AddressBook.class);
addressBook.setLon(lon);
addressBook.setLat(lat);
//2.判断是否修改默认地址
if(addressBook.getIsDefault().equals(1)) {
//2.1.查询当前用户的默认地址
AddressBook defaultAddress = lambdaQuery()
.eq(AddressBook::getUserId, UserContext.currentUserId())
.eq(AddressBook::getIsDefault, "1")
.ne(AddressBook::getId, id)
.one();
//2.2.如果有默认地址,将其改为非默认
if(defaultAddress != null) {
defaultAddress.setIsDefault(0);
updateById(defaultAddress);
}
}
//3.更新地址
addressBook.setId(id);
updateById(addressBook);
return addressBook;
}
2.3.3.6 测试
把手机号修改成17777777777
修改定位到故宫
2.3.3.7 设置默认地址
请求路径:/customer/consumer/address-book/default?id=xxx&flag=xxx
2.3.3.8 controller
@PutMapping("/default")
@ApiOperation("设置默认地址")
public AddressBook setDefault(@RequestParam("id") Long id, @RequestParam("flag") Integer flag){
return addressBookService.setDefault(id, flag);
}
2.3.3.9 service
接口
AddressBook setDefault(Long id, Integer flag);
实现
@Override
@Transactional
public AddressBook setDefault(Long id, Integer flag) {
//1.查询当前用户的默认地址
AddressBook defaultAddress = lambdaQuery()
.eq(AddressBook::getUserId, UserContext.currentUserId())
.eq(AddressBook::getIsDefault, "1")
.ne(AddressBook::getId, id)
.one();
//2.如果有默认地址,将其改为非默认
if(defaultAddress != null) {
defaultAddress.setIsDefault(0);
updateById(defaultAddress);
}
//3.将当前地址改为默认
AddressBook addressBook = getById(id);
addressBook.setIsDefault(flag);
updateById(addressBook);
return addressBook;
}
2.3.4 地址簿删除
进入我的地址页面,点击“删除”对地址簿进行删除
为了删除方便,创建了四个地址,码农烧烤、探花、骑车、宝哥
2.3.4.1 地址簿批量删除接口
接口名称:地址簿批量删除
接口路径:DELETE/customer/consumer/address-book/batch
2.3.4.2 controller
@DeleteMapping("/batch")
@ApiOperation("批量删除地址薄")
public void deleteBatch(@RequestBody List<Long> ids) {
addressBookService.removeByIds(ids);
}
2.3.4.3 测试
删除探花和骑车
查看数据库,已经将is_deleted字段置为1
2.3.4.4 逻辑删除
查看控制台
发现deleteBatchIds执行的Update逻辑删除,这是哪里配置的呢?
查看mysql的nacos配置,发现配置了logic-delete-field字段,指向isDeleted,实现逻辑删除,也可以在实体类的属性上加上 @TableLogic(value="0",delval="1")
字段实现逻辑删除
2.3.5 获取默认地址接口
接口名称:获取默认地址
接口功能:在下单界面先获取当前用户的默认地址,如果有默认地址则直接显示在页面中
接口路径:GET/customer/consumer/address-book/defaultAddress
请求数据类型 application/x-www-form-urlencoded
请求参数:无
2.3.5.1 controller
@GetMapping("/defaultAddress")
@ApiOperation("获取默认地址")
public AddressBookResDTO getDefaultAddress(){
return addressBookService.getDefaultAddress();
}
2.3.5.2 service
接口
AddressBookResDTO getDefaultAddress();
实现
@Override
public AddressBookResDTO getDefaultAddress() {
AddressBook addressBook = lambdaQuery()
.eq(AddressBook::getUserId, UserContext.currentUserId())
.eq(AddressBook::getIsDefault, "1")
.one();
if(addressBook == null) {
return null;
}
return BeanUtil.toBean(addressBook, AddressBookResDTO.class);
}