外卖小程序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
     */
    @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优先级

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 job
             when 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中的参数名一致

方法签名

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

一对多属性字段映射

	
    <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.优点:

  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方言添加分页关键词,实现分页功能

你可能感兴趣的:(小程序,java,开发语言)