
1.用户下单1.1 介绍1在外卖系统中用户通过下单的方式通知给商家提醒商家根据用户下单数据进行备货与此同时用户下单后会产生订单订单要呈现图片所展示的数据。比如订单总金额用户信息商品数量信息地址信息等。2对于这张样式图在进行分析又可以知道在一个订单里面除了订单号、地址等订单基本信息还记录了订单所关联的一个菜品或多个菜品的详细信息。所以在进行订单管理时需要ordersorders_detail两张表来进行管理。1.2流程分析1) 对于下单所需要的数据分析完成后还需要明确用户点单的操作流程用户将纳入购物车的数据去结算后跳转的便是用户下单页面当用户点击去支付的按钮后用户就完成下单操作与此同时订单数据上传到数据库购物车数据清除。2根据上述分析可知用户下单需要操作orders表orders_detail表两张表所以订单数据上传到数据库时订单表关联的订单明细表也要上传一条或多条数据。1.3 实现用户下单因为是用户端的操作所以先在User包下创建OrderController类提供对应方法并按照分层解耦的方式去实现用户下单功能OrderController层RestController(userOrderController)RequestMapping(/user/order)Slf4jApi(tagsC端-订单接口)publicclassOrderController{AutowiredprivateOrderServiceorderService;/** * 用户下单 * * param ordersSubmitDTO * return */PostMapping(/submit)ApiOperation(用户下单)publicResultOrderSubmitVOsubmit(RequestBodyOrdersSubmitDTOordersSubmitDTO){log.info(用户下单{},ordersSubmitDTO);OrderSubmitVOorderSubmitVOorderService.submitOrder(ordersSubmitDTO);returnResult.success(orderSubmitVO);}}OrderService层publicinterfaceOrderService{/** * 用户下单 * param ordersSubmitDTO * return */OrderSubmitVOsubmitOrder(OrdersSubmitDTOordersSubmitDTO);}OrderServiceImpl层ServiceSlf4jpublicclassOrderServiceImplimplementsOrderService{AutowiredprivateOrderMapperorderMapper;AutowiredprivateOrderDetailMapperorderDetailMapper;AutowiredprivateShoppingCartMappershoppingCartMapper;AutowiredprivateAddressBookMapperaddressBookMapper;/** * 用户下单 * param ordersSubmitDTO * return */OverrideTransactionalpublicOrderSubmitVOsubmitOrder(OrdersSubmitDTOordersSubmitDTO){//校验异常情况地址为空购物车数据为空//地址为空AddressBookaddressBookaddressBookMapper.getById(ordersSubmitDTO.getAddressBookId());if(addressBooknull){thrownewAddressBookBusinessException(MessageConstant.ADDRESS_BOOK_IS_NULL);}//校验地址是否超出配送范围checkOutOfRange(addressBook.getProvinceName()addressBook.getCityName()addressBook.getDistrictName()addressBook.getDetail());//购物车数据为空ShoppingCartcartnewShoppingCart();cart.setUserId(BaseContext.getCurrentId());ListShoppingCartcartListshoppingCartMapper.selectCar(cart);if(cartListnull||cartList.isEmpty()){thrownewShoppingCartBusinessException(MessageConstant.SHOPPING_CART_IS_NULL);}//向订单表插入一条数据OrdersordersOrders.builder().orderTime(LocalDateTime.now()).payStatus(Orders.UN_PAID)//支付状态.status(Orders.PENDING_PAYMENT)//订单状态.number(String.valueOf(System.currentTimeMillis()))//订单号.phone(addressBook.getPhone()).consignee(addressBook.getConsignee()).userId(BaseContext.getCurrentId()).address(addressBook.getProvinceName()addressBook.getCityName()addressBook.getDistrictName()addressBook.getDetail()).userName(addressBook.getConsignee()).build();BeanUtils.copyProperties(ordersSubmitDTO,orders);orderMapper.insert(orders);//向订单明细表插入n条数据//通过购物车数据提取订单详情数据ListOrderDetailorderDetailListnewArrayList();for(ShoppingCartshopcart:cartList){OrderDetailorderDetailnewOrderDetail();BeanUtils.copyProperties(shopcart,orderDetail);orderDetail.setOrderId(orders.getId());orderDetail.setId(null);orderDetailList.add(orderDetail);}orderDetailMapper.insertBatch(orderDetailList);//提交订单后清空购物车数据shoppingCartMapper.deleteByUserId(cart);//封装OrderSubmitVO对象并返回OrderSubmitVOorderSubmitVOOrderSubmitVO.builder().orderNumber(orders.getNumber()).orderAmount(orders.getAmount()).orderTime(orders.getOrderTime()).build();returnorderSubmitVO;}}这里用动态sql语句插入所以创建mapper层以及对应的xml映射文件实现数据的插入Mapper层OrderMapper层MapperpublicinterfaceOrderMapper{/** * 插入订单数据 * param orders */voidinsert(Ordersorders);}OrderMapper.xml?xml version1.0 encodingUTF-8 ?!DOCTYPEmapperPUBLIC-//mybatis.org//DTD Mapper 3.0//ENhttp://mybatis.org/dtd/mybatis-3-mapper.dtdmappernamespacecom.sky.mapper.OrderMapperinsertidinsertparameterTypeOrdersuseGeneratedKeystruekeyPropertyidinsert into orders (number, status, user_id, address_book_id, order_time, checkout_time, pay_method, pay_status, amount, remark, phone, address, consignee, estimated_delivery_time, delivery_status, pack_amount, tableware_number, tableware_status) values (#{number}, #{status}, #{userId}, #{addressBookId}, #{orderTime}, #{checkoutTime}, #{payMethod}, #{payStatus}, #{amount}, #{remark}, #{phone}, #{address}, #{consignee}, #{estimatedDeliveryTime}, #{deliveryStatus}, #{packAmount}, #{tablewareNumber}, #{tablewareStatus})/insert/mapperOrderDetailMapper层MapperpublicinterfaceOrderDetailMapper{/** * 批量插入订单明细数据 * param orderDetailList */voidinsertBatch(ListOrderDetailorderDetailList);}OrderDetailMapper.xml?xml version1.0 encodingUTF-8 ?!DOCTYPEmapperPUBLIC-//mybatis.org//DTD Mapper 3.0//ENhttp://mybatis.org/dtd/mybatis-3-mapper.dtdmappernamespacecom.sky.mapper.OrderDetailMapperinsertidinsertBatchparameterTypelistinsert into order_detail (name, order_id, dish_id, setmeal_id, dish_flavor, number, amount, image) valuesforeachcollectionorderDetailsitemodseparator,(#{od.name},#{od.orderId},#{od.dishId},#{od.setmealId},#{od.dishFlavor}, #{od.number},#{od.amount},#{od.image})/foreach/insert/mapper2.微信支付2.1介绍在用户下单后就需要去实现支付付款支付功能在如今生活十分常见经常用于购买物品后使用支付功能来实现付款常见的为支付宝付款和微信付款本项目采用的就是微信支付。由于微信官方规定个人用户并不具备微信支付的权限。所以主要介绍并了解微信支付的流程图以及对应的接口文档即可同时还会实现跳过微信支付确保该项目的正常运行。2.2流程分析实现微信支付功能在微信小程序官方文档开发指引_小程序支付|微信支付商户文档中心 (qq.com)中也有相关指导以下是提炼出的主要步骤操作步骤1JSAPI/小程序下单商户通过调用JSAPI/小程序下单接口生成预支付订单并获取预支付交易会话标识(prepay_id)即上述时序图中的步骤4、5、6。2小程序调起支付商户小程序获取到支付的必需参数prepay_id后通过调用小程序提供的requestPayment方法来拉起微信收银台并检验商户支付权限校验通过后才会给用户返回支付页面然后用户可以输入密码确认支付即上述时序图中的步骤9-14。2.3跳过微信支付第一步打开微信开发者工具按如下步骤修改修改前修改后将红色框中的部分打上注释蓝色框中的部分取消注释然后重新编译文件即可第二步导入微信支付功能代码时最后一个PayNotifyController的代码不导入第三步按图所示修改OrderServiceImpl中的payment方法第四步实现支付成功后修改订单状态功能3.地址校验优化3.1 介绍基于现实外卖平台都有校验商家地址和用户地址的功能以免出现用户给距离自己很远的商家下单然后商家无法配送的情况发生。而该项目最初并没有设计该功能就无论多远订单都能正常配送这是不合理的。所以后续通过实现百度地图的接口去完善了该缺陷优化了地址校验功能。3.2 流程分析1前置工作注册百度账号申请获取百度地图密钥AK2环境配置在idea中的application.yml文件中配置个人注册的密钥以及配置商家的地址信息3访问官方文档地理编码 | 百度地图API SDK (baidu.com) 基于该示例代码去进行修改实现该地图接口3.3 功能实现1在OrderServiceImpl层注入下列配置项2在OrderServiceImpl层实现地址的校验/** * 检查客户的收货地址是否超出配送范围 * param address */privatevoidcheckOutOfRange(Stringaddress){MapmapnewHashMap();map.put(address,shopAddress);map.put(output,json);map.put(ak,ak);//获取店铺的经纬度坐标StringshopCoordinateHttpClientUtil.doGet(https://api.map.baidu.com/geocoding/v3,map);JSONObjectjsonObjectJSON.parseObject(shopCoordinate);log.info(店铺状态{},jsonObject.getString(status));if(!jsonObject.getString(status).equals(0)){thrownewOrderBusinessException(店铺地址解析失败);}//数据解析JSONObjectlocationjsonObject.getJSONObject(result).getJSONObject(location);Stringlatlocation.getString(lat);Stringlnglocation.getString(lng);//店铺经纬度坐标StringshopLngLatlat,lng;map.put(address,address);//获取用户收货地址的经纬度坐标StringuserCoordinateHttpClientUtil.doGet(https://api.map.baidu.com/geocoding/v3,map);jsonObjectJSON.parseObject(userCoordinate);log.info(用户地址状态{},jsonObject.getString(status));if(!jsonObject.getString(status).equals(0)){thrownewOrderBusinessException(收货地址解析失败);}//数据解析locationjsonObject.getJSONObject(result).getJSONObject(location);latlocation.getString(lat);lnglocation.getString(lng);//用户收货地址经纬度坐标StringuserLngLatlat,lng;map.put(origin,shopLngLat);map.put(destination,userLngLat);map.put(steps_info,0);//路线规划StringjsonHttpClientUtil.doGet(https://api.map.baidu.com/directionlite/v1/driving,map);jsonObjectJSON.parseObject(json);if(!jsonObject.getString(status).equals(0)){thrownewOrderBusinessException(配送路线规划失败);}//数据解析JSONObjectresultjsonObject.getJSONObject(result);JSONArrayjsonArrayresult.getJSONArray(routes);Integerdistance(Integer)((JSONObject)jsonArray.get(0)).get(distance);if(distance5000){//配送距离超过5000米thrownewOrderBusinessException(超出配送范围);}}3在OrderServiceImpl层中的submitVo方法调用上述校验地址方法