校验收货地址是否超出配送范围
注册账号:https://passport.baidu.com/v2/?reg&tt=1671699340600&overseas=&gid=CF954C2-A3D2-417F-9FE6-B0F249ED7E33&tpl=pp&u=https%3A%2F%2Flbsyun.baidu.com%2Findex.php%3Ftitle%3D%E9%A6%96%E9%A1%B5
登录百度地图开放平台:https://lbsyun.baidu.com/
进入控制台,创建应用,获取AK:
相关接口:
https://lbsyun.baidu.com/index.php?title=webapi/guide/webservice-geocoding
https://lbsyun.baidu.com/index.php?title=webapi/directionlite-v1
配置外卖商家店铺地址和百度地图的AK:
sky:
shop:
address: 北京市海淀区上地十街10号
baidu:
ak: ************************
改造OrderServiceImpl,注入上面的配置项:
@Value("${sky.shop.address}")
private String shopAddress;
@Value("${sky.baidu.ak}")
private String ak;
在OrderServiceImpl中提供校验方法:
/**
* 校验配送地址是否超出范围
*
* @param cityName
* @param districtName
* @param detail
*/
@SneakyThrows
private void checkOutOfRange(String cityName, String districtName, String detail) {
//获取收货地址和店铺地址坐标
String userAddress = cityName + districtName + detail;
JSONObject userLocation = getLocaton(userAddress);
JSONObject shopLocation = getLocaton(shopAddress);
//计算距离
Integer distance = countDistance(userLocation, shopLocation);
if (distance > 5000) {
throw new OrderBusinessException("超出配送范围");
}
}
/**
* 获取距离差
*
* @param userLocation
* @param shopLocation
* @return
* @throws IOException
*/
private Integer countDistance(JSONObject userLocation, JSONObject shopLocation) throws IOException {
final String direction = "https://api.map.baidu.com/directionlite/v1/riding";
Float userLat = userLocation.getFloat("lat");
Float userLng = userLocation.getFloat("lng");
String userLoc = userLat + "," + userLng;
Float shopLat = shopLocation.getFloat("lat");
Float shopLng = shopLocation.getFloat("lng");
String shopLoc = shopLat + "," + shopLng;
//生成url
StringBuilder stringBuilder = new StringBuilder(direction);
stringBuilder.append("?")
.append("ak=")
.append(ak)
.append("&")
.append("origin=")
.append(userLoc)
.append("&")
.append("destination=")
.append(shopLoc)
.append("&")
.append("steps_info=")
.append("0");
String url = stringBuilder.toString();
//创建HttpClient对象
CloseableHttpClient httpClient = HttpClients.createDefault();
//创建请求对象
HttpGet httpGet = new HttpGet(url);
CloseableHttpResponse response = httpClient.execute(httpGet);
//获取响应状态码
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode != 200) {
throw new OrderBusinessException("地址解析失败");
}
try {
String bodyAsString = EntityUtils.toString(response.getEntity());
//解析返回结果
JSONObject jsonObject = JSON.parseObject(bodyAsString);
System.out.println(jsonObject);
JSONObject result = jsonObject.getJSONObject("result");
JSONArray routes = result.getJSONArray("routes");
JSONObject object = (JSONObject) routes.get(0);
Integer distance = object.getInteger("distance");
return distance;
} finally {
httpClient.close();
response.close();
}
}
/**
* 获取地址坐标
*
* @param address
* @return
* @throws IOException
*/
private JSONObject getLocaton(String address) throws IOException {
final String encoder = "https://api.map.baidu.com/geocoding/v3/";
//生成url
StringBuilder encoderBuilder = new StringBuilder(encoder);
encoderBuilder.append("?")
.append("address=")
.append(address)
.append("&")
.append("output=")
.append("json")
.append("&")
.append("ak=")
.append(ak);
String encoderUrl = encoderBuilder.toString();
//创建HttpClient对象
CloseableHttpClient httpClient = HttpClients.createDefault();
//创建请求对象
HttpGet httpGet = new HttpGet(encoderUrl);
CloseableHttpResponse response = httpClient.execute(httpGet);
//获取响应状态码
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode != 200) {
throw new OrderBusinessException("配送路线规划失败");
}
try {
String bodyAsString = EntityUtils.toString(response.getEntity());
//解析返回结果
JSONObject jsonObject = JSON.parseObject(bodyAsString);
System.out.println(jsonObject);
JSONObject location = jsonObject.getJSONObject("result").getJSONObject("location");
return location;
} finally {
httpClient.close();
response.close();
}
}
在OrderServiceImpl的submitOrder方法中调用上面的校验方法:
/**
* 用户下单
*
* @param ordersSubmitDTO
* @return
*/
@Override
@Transactional
public OrderSubmitVO ordersubmit(OrdersSubmitDTO ordersSubmitDTO) {
//处理业务异常(地址簿为空,购物车为空),前端和后端都应作此校验,以保证代码的健壮性
Long addressBookId = ordersSubmitDTO.getAddressBookId();
AddressBook addressBook = addressBookMapper.getById(addressBookId);
//判断地址簿是否为空
if (addressBook == null) {
throw new OrderBusinessException(MessageConstant.ADDRESS_BOOK_IS_NULL);
}
Long userId = BaseContext.getCurrentId();
ShoppingCart shoppingCart = ShoppingCart.builder().userId(userId).build();
List<ShoppingCart> shoppingCartList = shoppingCartMapper.list(shoppingCart);
//判断购物车是否为空
if (shoppingCartList == null || shoppingCartList.size() == 0) {
throw new OrderBusinessException(MessageConstant.SHOPPING_CART_IS_NULL);
}
//判断是否超出配送范围
checkOutOfRange(addressBook.getCityName(), addressBook.getDistrictName(), addressBook.getDetail());
//向订单表中插入一条数据
Orders orders = new Orders();
BeanUtils.copyProperties(ordersSubmitDTO, orders);
orders.setNumber(String.valueOf(System.currentTimeMillis()));
orders.setStatus(Orders.PENDING_PAYMENT);
orders.setUserId(userId);
orders.setOrderTime(LocalDateTime.now());
orders.setPayStatus(Orders.UN_PAID);
orders.setPhone(addressBook.getPhone());
orders.setAddress(addressBook.getDetail());
orders.setConsignee(addressBook.getConsignee());
orderMapper.insert(orders);
ArrayList<OrderDetail> orderDetails = new ArrayList<>();
//向订单详情表中插入n条数据
for (ShoppingCart cart : shoppingCartList) {
OrderDetail orderDetail = new OrderDetail();
BeanUtils.copyProperties(cart, orderDetail);
orderDetail.setOrderId(orders.getId());
orderDetails.add(orderDetail);
}
orderDetailMapper.insertBatch(orderDetails);
//清空购物车数据
shoppingCartMapper.deleteByUserId(userId);
//封装OrderSubmitVO数据
OrderSubmitVO orderSubmitVO = OrderSubmitVO.builder()
.id(orders.getId())
.orderTime(orders.getOrderTime())
.orderAmount(orders.getAmount())
.orderNumber(orders.getNumber())
.build();
return orderSubmitVO;
}
sql执行顺序优先级由高到低依次是:from关键字后面的语句、where关键字后面的语句、“group by”后面的语句、having后面的语句、select后面的语句、“order by”后面的语句、limit后面的语句。
if(表达式, tvalue, fvalue) :当表达式为true时,取值tvalue;当表达式为false时,取值fvalue
eg:
-- if(条件表达式, true取值 , false取值)
select if(gender=1,'男性员工','女性员工') AS 性别, count(*) AS 人数
from tb_emp
group by gender;
case 表达式 when 值1 then 结果1 [when 值2 then 结果2 …] [else result] end
eg:
-- case 表达式 when 值1 then 结果1 when 值2 then 结果2 ... else result end
select (case job
when 1 then '班主任'
when 2 then '讲师'
when 3 then '学工主管'
when 4 then '教研主管'
else '未分配职位'
end) AS 职位 ,
count(*) AS 人数
from tb_emp
group by job;
unsigned属性就是将数字类型无符号化,例如INT的类型范围是-2 147 483 648 ~ 2 147 483 647, INT UNSIGNED的范围类型就是0 ~ 4 294 967 295。可以增加字段长度。
eg:
gender tinyint unsigned not null comment '性别, 说明: 1 男, 2 女'
@RequestParam是SpringMVC的一个常用注解,常用于Controller层的方法参数上,
value属性可用于映射前端参数和方法形参名,
defaultValue属性用于设置方法形参默认值,当前端未发送参数或发送参数为""时,使用默认值,
required属性用于设置方法形参是否必须传递
@Param是Mybatis中的一个常用注解,用于dao层中的接口方法参数上,其作用是给方法参数命名,命名后保证使用参数占位符注入sql语句中时,与sql中的参数名一致
区分不同方法的标示符。由方法名+形参列表构成,方法名和形参数据类型列表可以唯一的确定一个方法,与方法的返回值没有关系,是判断重载的重要依据
<resultMap id="setmealAndDishMap" type="com.sky.vo.SetmealVO" autoMapping="true">
<result column="id" property="id"/>
<collection property="setmealDishes" ofType="SetmealDish">
<result column="sd_id" property="id"/>
<result column="setmeal_id" property="setmealId"/>
<result column="dish_id" property="dishId"/>
<result column="sd_name" property="name"/>
<result column="sd_price" property="price"/>
<result column="copies" property="copies"/>
collection>
resultMap>
<select id="getByIdWithDish" parameterType="long" resultMap="setmealAndDishMap">
select a.*,
b.id sd_id,
b.setmeal_id,
b.dish_id,
b.name sd_name,
b.price sd_price,
b.copies
from setmeal a
left join
setmeal_dish b
on
a.id = b.setmeal_id
where a.id = #{id}
select>
1.定义:将一个复杂对象的创建与表示分离,使得同样的构建过程可以获得不同的表示
2.作用:用户可以在不知道构建过程和细节的情况下就创建复杂的对象,只需指定对象的类型和内容,由构建者模式去实现创建对象;方便用户创建复杂的对象,提高了代码的复用性和封装性
3.模式原理:由指挥者与客户进行沟通,明确需求后,指挥者将需求拆分为多个部件,并指挥各个具体的构建者去构建对象不同的部件,最终形成产品
4.优点:
- 易于解耦:将产品本身与产品的创建过程解耦,使用同样的构建过程即可获得不同的产品对象
- 易于拓展:增加新的具体构建者不需要修改原有类库中的代码,符合"开闭原则"
- 易于精确控制复杂对象的创建:将复杂对象的创建分解在不同的方法中,使得创建过程更加清晰
5.缺点:
- 使用构建者模式创建的对象之间具有很多共性,组成部分相似,故差异性很大的产品不则适合使用构建者模式,其使用范围受到了一定的限制
- 如果产品内部变化比较复杂,可能需要定义大量的具体构建者来实现产品的创建,使得系统变得庞大
6.应用场景:
需要创建的对象拥有复杂的内部结构,且这些产品具有共性
隔离复杂对象的创建和使用,并使得相同的构建过程可以获得不同的对象
1.观察项目模块间关系,哪些是主模块,哪些是子模块
2.主模块的结构是什么样的,不同模块是怎样联系到一起的
3.查看配置文件内容,了解项目所使用的依赖及框架
序列化:java对象–>json字符串 java对象–>二进制数据
反序列化:json字符串–>java对象 二进制数据–>java对象
共同点:都用于完成序列化与反序列化
Jackson使用的是ObjectMapper类实现序列化与反序列化
而fastjson使用JSON类实现
SpringMVC默认使用Jackson来实现序列化与反序列化,eg:@RequestBody反序列化,@ResponseBody序列化
package将项目打包成jar文件,install除了将项目打包成jar文件之外,还将之放到本地仓库
在REST风格的URL中,通过四种请求方式,来操作数据的增删改查
URL定位资源,HTTP动词描述操作
路径加s表示资源类而不是单个资源
在Controller方法执行之后需要及时清理缓存,防止内存泄漏
在postHandle方法中加入清理缓存的逻辑
根据数据库类型的不同选择不同的sql方言添加分页关键词,实现分页功能