> 文章列表 > 外卖小程序08

外卖小程序08

外卖小程序08

目录

  • 需求
    • 环境准备
    • 代码开发
      • application.yml
      • OrderServiceImpl
  • SQL优先级
  • SQL语句:表达式
  • SQL中的unsigned属性
  • MultipartFile 常见方法
  • @RequestParam和@Param的区别
  • 方法签名
  • 一对多属性字段映射
  • 构建者模式
  • 快速上手新接触项目的步骤
  • 序列化与反序列化
  • Jackson与fastjson的区别
  • MAVEN命令package和install的区别
  • REST风格
  • ThreadLocal内存泄漏问题
  • PageHelper底层所使用的拦截器作用

需求

校验收货地址是否超出配送范围

环境准备

注册账号: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

代码开发

application.yml

配置外卖商家店铺地址和百度地图的AK:

sky:shop:address: 北京市海淀区上地十街10号baidu:ak: ************************

OrderServiceImpl

改造OrderServiceImpl,注入上面的配置项:

    @Value("${sky.shop.address}")private String shopAddress;@Value("${sky.baidu.ak}")private String ak;

在OrderServiceImpl中提供校验方法:

/*** 校验配送地址是否超出范围** @param cityName* @param districtName* @param detail*/@SneakyThrowsprivate 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;//生成urlStringBuilder 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/";//生成urlStringBuilder 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@Transactionalpublic 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优先级

sql执行顺序优先级由高到低依次是:from关键字后面的语句、where关键字后面的语句、“group by”后面的语句、having后面的语句、select后面的语句、“order by”后面的语句、limit后面的语句。

SQL语句:表达式

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 jobwhen 1 then '班主任'when 2 then '讲师'when 3 then '学工主管'when 4 then '教研主管'else '未分配职位'end) AS 职位 ,count(*) AS 人数
from tb_emp
group by job;

SQL中的unsigned属性

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 女'

MultipartFile 常见方法:

  • String getOriginalFilename(); //获取原始文件名
  • void transferTo(File dest); //将接收的文件转存到磁盘文件中
  • long getSize(); //获取文件的大小,单位:字节
  • byte[] getBytes(); //获取文件内容的字节数组
  • InputStream getInputStream(); //获取接收到的文件内容的输入流

@RequestParam和@Param的区别

@RequestParam是SpringMVC的一个常用注解,常用于Controller层的方法参数上,
value属性可用于映射前端参数和方法形参名,
defaultValue属性用于设置方法形参默认值,当前端未发送参数或发送参数为""时,使用默认值,
required属性用于设置方法形参是否必须传递

@Param是Mybatis中的一个常用注解,用于dao层中的接口方法参数上,其作用是给方法参数命名,命名后保证使用参数占位符注入sql语句中时,与sql中的参数名一致

方法签名

区分不同方法的标示符。由方法名+形参列表构成,方法名和形参数据类型列表可以唯一的确定一个方法,与方法的返回值没有关系,是判断重载的重要依据

一对多属性字段映射

	<!--autoMapping 属性默认值为true--><resultMap id="setmealAndDishMap" type="com.sky.vo.SetmealVO" autoMapping="true"><result column="id" property="id"/><!--setmealVO中的集合类型属性内部字段映射property属性指的是集合对应的属性名ofType属性指的是集合中元素的类型--><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.copiesfrom setmeal aleft joinsetmeal_dish bona.id = b.setmeal_idwhere a.id = #{id}</select>

构建者模式

1.定义:将一个复杂对象的创建与表示分离,使得同样的构建过程可以获得不同的表示

2.作用:用户可以在不知道构建过程和细节的情况下就创建复杂的对象,只需指定对象的类型和内容,由构建者模式去实现创建对象;方便用户创建复杂的对象,提高了代码的复用性和封装性

3.模式原理:由指挥者与客户进行沟通,明确需求后,指挥者将需求拆分为多个部件,并指挥各个具体的构建者去构建对象不同的部件,最终形成产品

4.优点:

  1. 易于解耦:将产品本身与产品的创建过程解耦,使用同样的构建过程即可获得不同的产品对象
  2. 易于拓展:增加新的具体构建者不需要修改原有类库中的代码,符合"开闭原则"
  3. 易于精确控制复杂对象的创建:将复杂对象的创建分解在不同的方法中,使得创建过程更加清晰

5.缺点:

  1. 使用构建者模式创建的对象之间具有很多共性,组成部分相似,故差异性很大的产品不则适合使用构建者模式,其使用范围受到了一定的限制
  2. 如果产品内部变化比较复杂,可能需要定义大量的具体构建者来实现产品的创建,使得系统变得庞大

6.应用场景:

  1. 需要创建的对象拥有复杂的内部结构,且这些产品具有共性

  2. 隔离复杂对象的创建和使用,并使得相同的构建过程可以获得不同的对象

快速上手新接触项目的步骤

1.观察项目模块间关系,哪些是主模块,哪些是子模块

2.主模块的结构是什么样的,不同模块是怎样联系到一起的

3.查看配置文件内容,了解项目所使用的依赖及框架

序列化与反序列化

序列化:java对象–>json字符串 java对象–>二进制数据

反序列化:json字符串–>java对象 二进制数据–>java对象

Jackson与fastjson的区别

共同点:都用于完成序列化与反序列化

Jackson使用的是ObjectMapper类实现序列化与反序列化

而fastjson使用JSON类实现

SpringMVC默认使用Jackson来实现序列化与反序列化,eg:@RequestBody反序列化,@ResponseBody序列化

MAVEN命令package和install的区别

package将项目打包成jar文件,install除了将项目打包成jar文件之外,还将之放到本地仓库

REST风格

在REST风格的URL中,通过四种请求方式,来操作数据的增删改查

URL定位资源,HTTP动词描述操作

路径加s表示资源类而不是单个资源

  • GET : 查询
  • POST :新增
  • PUT :修改
  • DELETE :删除

ThreadLocal内存泄漏问题

在Controller方法执行之后需要及时清理缓存,防止内存泄漏

在postHandle方法中加入清理缓存的逻辑

PageHelper底层所使用的拦截器作用

根据数据库类型的不同选择不同的sql方言添加分页关键词,实现分页功能