外卖平台对接笔记(美团外卖,饿了么外卖)

背景:

针对中台订单中心需要将线上线下门店,订单,商品,以及线上金额对账等进行打通,对饿了么外卖,美团外卖进行了订单对接,当前以落单,重要的订单状态流转,以及对外卖平台直接调用为实现,记录以下实现方案,后期对接外卖平台以该文档持续补充。

一:饿了么外卖对接

    一:饿了么开发平台

1:URL

饿了么商家开放平台

二:应用及配置介绍

            1:个人应用

新建应用,目前新建的是个人应用;如果需要发布正式环境,需要进行上线审核,待审核过后,才可以进行正式环境的使用。

2:沙箱环境

进入沙箱环境,会有对应的测试店铺路径和账号密码,然后配置对应的推送URL

该推送URL为饿了么的推送订单以及订单状态变更的URL,需要一个Get请求的路径和Post请求的路径,Get请求路径是饿了么会校验该路径是否合法,必须要使用外网链接,如果没有,可以通过花生壳进行内网穿透

key和Secret是该应用生成token时所需要的固定参数,通过此token可以获取到该应用下所有的门店信息,其绑定是在饿了么商家中心进行绑定(测试门店未发布正式环境无法绑定)

3:饿了么商家中心

①:URL:配置正式门店与应用关系

饿了么商家版

通过门店下店铺管理页面,对门店进行绑定(注意:门店必须已经正式上线才可以绑定,测试门店无法绑定)

     新增商品时的SKU编码在饿了么落单时会通过订单明细里面的商品对应的扩展字段来下发,可以通过它来定义第三方系统的Sku编码,使饿了么的Sku编码和第三方系统的Sku编码产生映射

4:注意

通过对饿了么线上网店信息的修改,也可以将线上网店里面的扩展编码存入第三方系统的线下门店编码,饿了么线上网店的扩展编码在落单时会随着订单落下,通过它可以使饿了么的网店编码和第三方系统的线下门店编码产生映射。

三:落单代码逻辑

1:Get请求接口

(饿了么测试推送URL时使用)

	/**
     * API层:饿了么对接POST接口
     */
    @ApiOperation(value = "饿了么对接GET接口", tags = {"tradeCenterApi"}, nickname = "doGet")
    @ApiResponses(value = {@ApiResponse(code = 200, message = "000000:成功,否则失败")})
    String elementGet();
	
	/**
     * Controller层
     */
 	@Override
    @GetMapping(value = "/element", produces = {"application/json"})
    public String elementGet(){
        log.info("测试是否接收到饿了么消息");
        Map map = new HashMap();
        map.put("message", "OK");
        return JSONObject.toJSONString(map);
    }

2:Post请求接口

(饿了么真正使用的落单URL,主要通过type,消息类型字段判断此次订单落单的场景)

 	/**
     * API层:饿了么对接POST接口
     */
    @ApiOperation(value = "饿了么对接POST接口", tags = {"tradeCenterApi"}, nickname = "doPost")
    @ApiResponses(value = {@ApiResponse(code = 200, message = "000000:成功,否则失败")})
    String elementPost(ElemReqDto elemReqDto);

	/**
     * Controller层
     */
	@Override
    @PostMapping(value = "/element", produces = {"application/json"})
    public String elementPost(@ApiParam(value = "饿了么订单信息", required = true)
                              @Valid @RequestBody ElemReqDto elemReqDto){
        log.info("测试是否接收到饿了么消息:{}",JSONObject.toJSONString(elemReqDto));
        return tradeCenterService.elementPost(elemReqDto);
    }

   /**
     * Service层:饿了么接单以及状态流转
     */
    String elementPost(ElemReqDto elemReqDto);

/**
  * Service实现层:饿了么接单以及状态流转
  */	
@lombok.extern.slf4j.Slf4j
@Service("tradeCenterServiceImpl")
@Transactional(rollbackFor = Exception.class)
public class TradeCenterServiceImpl implements TradeCenterService {
	@Override
    public String elementPost(ElemReqDto elemReqDto) {
        Optional.ofNullable(elemReqDto).orElseThrow(() -> new AppException("饿了么单据不存在"));
        String message = elemReqDto.getMessage();
        log.info("Type类型:{};message消息体:{}", elemReqDto.getType(), message);
        try {
            String type = String.valueOf(elemReqDto.getType());
            if (type.equals("14") || type.equals("17") || type.equals("15")) {
                // TODO: 2021/6/22 取消场景:   
                //14==1:接单前用户取消;2:商户拒绝接单;3:5分支未接单自动取消 
                //** 17==订单取消(门店接单后取消) ** 
                //15==门店接单后用户取消
                type = TradeConstants.ElemEvent.ELEM_CANCEL;
            }
            //去工厂获取其对应的类型
            ElemDomainService orderEvent = ElemFactory.getOrderEvent(type);
            //根据类型找到对应的实现逻辑
            orderEvent.orderEvent(message, type);

            Map map = new HashMap();
            map.put("message", "OK");
            return JSONObject.toJSONString(map);
        } catch (Exception e) {
            return e.getMessage();
        }
    }	
}

3:针对饿了么单据不同场景以及类型采用策略+工厂模式实现

①:工厂类:注入到容器中,通过容器将参数遍历并且存放到map中  

@Service
public class ElemFactory {

    private static Map<String, ElemDomainService> map = new ConcurrentHashMap<String, ElemDomainService>();

    public static ElemDomainService getOrderEvent(String type) {
        return map.get(type);
    }

    @Autowired
    public void register(ElemDomainService[] instances) {
        if (instances != null && instances.length > 0) {
            for (ElemDomainService oth : instances) {
                map.put(oth.getEvent(), oth);
            }
        }
    }

}	

②:接口类:定义该策略的接口,一般俩个,一个为获取其对应的Type类型,一个为针对该类型找到对应的实现逻辑

public interface ElemDomainService {

    /**
     * 获取事件类型
     * @return
     */
    String getEvent();

    /**
     * 执行对应逻辑
     */
    void orderEvent(String message,String type);

}

③:接口实现类:以落单实现类为案例

@Service
@Slf4j
public class ElemCreateServiceImpl implements ElemDomainService {
    
	@Autowired
    private WaiMaiReqDtoConvertor waiMaiReqDtoConvertor;

    @Autowired
    TradeCenterDomainService tradeCenterDomainService;
	
    @Override
    public String getEvent() {
        //10代表订单生效,正常需要采用枚举类
        return "10";
    }
    
    @Override
    public void orderEvent(String message, String type) {
        //将消息体的内容转换为对象
        ElemOrderReqDto elemOrderReqDtos = JSONObject.parseObject(message, ElemOrderReqDto.class);
        //将饿了么的消息体对象与第三方系统进行字段转换
        OrderReqDto orderReqDto = waiMaiReqDtoConvertor.ElemDtoToOrderDto(elemOrderReqDtos);
        //进行入库操作
        tradeCenterDomainService.save(orderReqDto);
    }
}

4:以上逻辑所需要的类

①:饿了么信息

/**
 * 饿了么信息
 *
 */
@Validated
@AllArgsConstructor
@NoArgsConstructor
@Data
@ApiModel
public class ElemReqDto implements Serializable {
    private static final long serialVersionUID = 1L;

    /**
     * 应用id,应用创建时系统分配的唯一id varchar
     */
    @ApiModelProperty(value = "应用id,应用创建时系统分配的唯一id")
    @JsonProperty(index = 10)
    private Long appId;

    /**
     * 消息的唯一id,用于唯一标记每个消息 varchar
     */
    @ApiModelProperty(value = "消息的唯一id,用于唯一标记每个消息")
    @JsonProperty(index = 10)
    private String requestId;

    /**
     * 消息类型,参加下方【消息类型】 varchar
     */
    @ApiModelProperty(value = "消息类型,参加下方【消息类型】")
    @JsonProperty(index = 10)
    private Integer type;

    /**
     * JSON格式字符串 varchar
     */
    @ApiModelProperty(value = "JSON格式字符串")
    @JsonProperty(index = 10)
    private String message;

    /**
     * 商户的店铺id varchar
     */
    @ApiModelProperty(value = "商户的店铺id")
    @JsonProperty(index = 10)
    private Long shopId;

    /**
     * 网店对应的门店编码 varchar
     */
    @ApiModelProperty(value = "网店对应的门店编码")
    @JsonProperty(index = 10)
    private String storeCode;

    /**
     * 消息发送的时间戳,每次推送时生成,单位毫秒 varchar
     */
    @ApiModelProperty(value = "消息发送的时间戳,每次推送时生成,单位毫秒")
    @JsonProperty(index = 10)
    private Long timestamp;

    /**
     * 消息的唯一id,用于唯一标记每个消息 varchar
     */
    @ApiModelProperty(value = "消息的唯一id,用于唯一标记每个消息")
    @JsonProperty(index = 10)
    private Long userId;

    /**
     * 消息的唯一id,用于唯一标记每个消息 varchar
     */
    @ApiModelProperty(value = "消息的唯一id,用于唯一标记每个消息")
    @JsonProperty(index = 10)
    private String signature;

}

②:饿了么订单信息

/**
 * 饿了么订单信息
 */
@Validated
@AllArgsConstructor
@NoArgsConstructor
@Data
@ApiModel
public class ElemOrderReqDto implements Serializable{
    private static final long serialVersionUID = 1L;

    /**
     * 外部流水单号 varchar
     */
    @ApiModelProperty(value = "外部流水单号")
    @JsonProperty(index = 10)
    private Long orderId;

    /**
     * 顾客送餐地址 varchar
     */
    @ApiModelProperty(value = "顾客送餐地址")
    @JsonProperty(index = 10)
    private String address;

    /**
     * 下单时间 varchar
     */
    @ApiModelProperty(value = "下单时间")
    @JsonProperty(index = 10)
    private LocalDateTime createdAt;

    /**
     * 用户实际支付配送费  varchar
     */
    @ApiModelProperty(value = "用户实际支付配送费")
    @JsonProperty(index = 10)
    private BigDecimal deliverFee;

    /**
     * 订单备注  varchar
     */
    @ApiModelProperty(value = "订单备注")
    @JsonProperty(index = 10)
    private String description;

    /**
     * 店铺Id varchar
     */
    @ApiModelProperty(value = "店铺Id")
    @JsonProperty(index = 10)
    private Long shopId;

    /**
     * 网店对应的门店编码 varchar
     */
    @ApiModelProperty(value = "网店对应的门店编码")
    @JsonProperty(index = 10)
    private String storeCode;

    /**
     * 订单状态 varchar
     */
    @ApiModelProperty(value = "订单状态")
    @JsonProperty(index = 10)
    private String status;

    /**
     * 订单总价 varchar
     */
    @ApiModelProperty(value = "订单总价")
    @JsonProperty(index = 10)
    private BigDecimal totalPrice;

    /**
     * 订单原价 varchar
     */
    @ApiModelProperty(value = "订单原价")
    @JsonProperty(index = 10)
    private BigDecimal originalPrice;

    /**
     * 订单收货人姓名 varchar
     */
    @ApiModelProperty(value = "订单收货人姓名")
    @JsonProperty(index = 10)
    private String consignee;

    /**
     * 订单业务类型 (0外卖单,1到店自取订单,2企业到店买单) varchar
     */
    @ApiModelProperty(value = "订单业务类型 (0外卖单,1到店自取订单,2企业到店买单)")
    @JsonProperty(index = 10)
    private String orderBusinessType;

    /**
     * 订单明细  varchar
     */
    @ApiModelProperty(value = "订单明细")
    @JsonProperty(index = 10)
    private List<ElemOrderLineReqDto> groups;

    /**
     * 订单参加活动信息  varchar
     */
    @ApiModelProperty(value = "订单参加活动信息")
    @JsonProperty(index = 10)
    private List<ElemOrderLineReqDto> orderActivities;

    /**
     * 顾客联系电话  varchar
     */
    @ApiModelProperty(value = "顾客联系电话")
    @JsonProperty(index = 10)
    private List<String> phoneList;

}

③:饿了么订单明细信息

/**
 * 饿了么订单明细信息
 */
@Validated
@AllArgsConstructor
@NoArgsConstructor
@Data
@ApiModel
public class ElemOrderLineReqDto implements Serializable{
    private static final long serialVersionUID = 1L;

    /**
     * 分组名称 varchar
     */
    @ApiModelProperty(value = "分组名称")
    @JsonProperty(index = 10)
    private String name;

    /**
     * 类别:normal:普通商品;discount:赠品 varchar
     */
    @ApiModelProperty(value = "类别:normal:普通商品;discount:赠品")
    @JsonProperty(index = 10)
    private String type;

    /**
     * 商品明细 varchar
     */
    @ApiModelProperty(value = "商品明细")
    @JsonProperty(index = 10)
    private List<ElemItemReqDto> items;
}

④:饿了么订单明细商品信息

/**
 * 饿了么订单明细商品信息
 */
@Validated
@AllArgsConstructor
@NoArgsConstructor
@Data
@ApiModel
public class ElemItemReqDto implements Serializable {

    private static final long serialVersionUID = 1L;

    /**
     * 商品名称 varchar
     */
    @ApiModelProperty(value = "商品名称")
    @JsonProperty(index = 10)
    private String name;

    /**
     * SkuId varchar
     */
    @ApiModelProperty(value = "SkuId")
    @JsonProperty(index = 10)
    private Long skuId;

    /**
     * 商品分类Id varchar
     */
    @ApiModelProperty(value = "商品分类Id")
    @JsonProperty(index = 10)
    private String categoryId;

    /**
     * 商品单价 varchar
     */
    @ApiModelProperty(value = "商品单价")
    @JsonProperty(index = 10)
    private BigDecimal price;

    /**
     * 总价 varchar
     */
    @ApiModelProperty(value = "总价")
    @JsonProperty(index = 10)
    private BigDecimal total;

    /**
     * 商品数量 varchar
     */
    @ApiModelProperty(value = "商品数量")
    @JsonProperty(index = 10)
    private Integer quantity;

    /**
     * SkuCode
     */
    @ApiModelProperty(value = "SkuCode")
    @JsonProperty(index = 10)
    private String extendCode;

}

⑤:订单转换类(针对自己字段进行修改)

	/**
     * 饿了么订单落单时转换为中心订单(正向)
     *
     * @param elemOrderReqDto
     * @return
     */
    public static OrderReqDto ElemDtoToOrderDto(ElemOrderReqDto elemOrderReqDto) {
        OrderReqDto orderReqDto = OrderReqDto.builder()
                .orderIdOut(String.valueOf(elemOrderReqDto.getOrderId()))
                .saleTime(elemOrderReqDto.getCreatedAt())
                .tradeType(TradeConstants.TradeType.ELEM)
                .orderType(TradeConstants.OrderType.ELEMENT)
                .storeType(TradeConstants.storeType.STORE)
            	//此处的openId代表我们的第三方门店编码,可以直接使用,也可以去数据库查询
                .storeCode(elemOrderReqDto.getOpenId())
                .phoneNumber(String.valueOf(elemOrderReqDto.getPhoneList()))
                .payableAmount(elemOrderReqDto.getTotalPrice())
                .actualAmount(elemOrderReqDto.getTotalPrice())
                .orderAmount(elemOrderReqDto.getOriginalPrice())
                .freightAmount(elemOrderReqDto.getDeliverFee())
                .orderSplit(TradeConstants.orderSplit.NO_SPLIT)
                .orderSupportReverse(TradeConstants.orderSupportReverse.SUPPORT_REVERSE)
                .orderFlatFlag(TradeConstants.FlatFlag.NO_FLAT_FLAG_NOMAL).build();
        //收货地址订单
        OrderAddressReqDto addressReqDto = OrderAddressReqDto.builder()
                .detailAddress(elemOrderReqDto.getAddress())
                .receiver(elemOrderReqDto.getConsignee()).build();
        orderReqDto.setAddress(addressReqDto);

        //扩展信息
        OrderExtReqDto orderExtReqDto = OrderExtReqDto.builder()
                .logisticsStatus(TradeConstants.LogisticsStatus.Logistics)
                .sendTime(LocalDateTime.now()).build();
        orderReqDto.setOrderExt(orderExtReqDto);

        //订货信息
        OrderReservationReqDto orderReservationResDto = OrderReservationReqDto.builder()
				.takeSendTime(LocalDateTime.now())
                .reservationUserName(elemOrderReqDto.getConsignee())
                .phoneNumber(String.valueOf(elemOrderReqDto.getPhoneList()))
                .productionStoreCode(elemOrderReqDto.getOpenId())
                .takeSendStoreCode(elemOrderReqDto.getOpenId())
            	.build();
        orderReqDto.setReservationResDto(orderReservationResDto);

        //订单明细信息
        List<ElemOrderLineReqDto> elemOrderLineReqDtoList = elemOrderReqDto.getGroups().stream()
                .filter(v -> v.getType().equals("normal"))
                .collect(Collectors.toList());
        List<OrderLineReqDto> orderLineReqDtoList = new ArrayList<>();
        elemOrderLineReqDtoList.get(0).getItems().forEach(v -> {
            OrderLineReqDto orderLineReqDto = OrderLineReqDto.builder()
                    .actualAmount(v.getTotal())
                    .originPrice(v.getPrice())
                    .actualPrice(v.getPrice())
                	//ExtendCode为我们第三方的商品编码,也可以直接使用美团
                    .itemCode(v.getExtendCode())
                    .skuCode(v.getExtendCode())
                    .itemName(v.getName())
                    .skuQuantity(BigDecimal.valueOf(v.getQuantity()))
                    .storeCode(String.valueOf(elemOrderReqDto.getShopId()))
                    .build();
            orderLineReqDtoList.add(orderLineReqDto);
        });
        orderReqDto.setDetailList(orderLineReqDtoList);
        return orderReqDto;
    }

四:调用饿了么Api(以确认接单,拒绝接单为例)

1:后面采用如上的策略+工厂,不再描述

    /**
     * API层:
     */
    @ApiOperation(value = "确认/拒绝接单", tags = {"tradeCenterApi"})
    @ApiImplicitParams({
            @ApiImplicitParam(name = "orderNo", value = "订单号(逗号拼接,支持多个)", paramType = "query"),
            @ApiImplicitParam(name = "Type", value = "接单:(10:有赞;20:饿了么;30:美团);拒单(-10:有赞;-20:饿了么;-30:美团)", paramType = "query")
    })
    @ApiResponses(value = {@ApiResponse(code = 200, message = "操作是否成功,000000:成功,否则失败", response = MultiOrderResDto.class)})
    ResultDTO<Void> conFirmOrder(@ApiIgnore @RequestParam Map<String, Object> params);

  	/**
     * Controller层:
     */
    @Override
    @GetMapping(value = "/conFirmOrder", produces = {"application/json"})
    public ResultDTO<Void> conFirmOrder(@ApiIgnore @RequestParam Map<String, Object> params) {
        tradeCenterService.conFirmOrder(params);
        return ResultDTO.ok();
    }

2:确认接单逻辑

/**
 * @Description
 * @Author: dingjunxin
 * @Email: dingjunxin.djx@bitsun-inc.com
 */
@Service
@Slf4j
public class ElemOrderConfirmServiceImpl implements OrderConfirmDomainService {

    @Autowired
    TradeCenterDomainService tradeCenterDomainService;

    @Override
    public String getEvent() {
        return TradeConstants.OrderConfirmType.ELEMENT;
    }

    @Override
    public void orderEvent(OrderResDto orderResDto, String type) {
        try {
            //此处主要使用饿了么网店编码进行确认接单
            OrderService orderService = WaiMaiUtil.getOrderService(data);
			//此处需要传入饿了么订单号进行确认接单            
            orderService.confirmOrderLite(orderResDto.getOrderIdOut());
        } catch (Exception e) {
            throw new AppException(e.getMessage());
        }
    }
}

3:配置层

    /**
     * 获取OrderService
     */
    public static OrderService getOrderService() {
        //todo 以上正常需要传入网店信息,去找其对应的应用秘钥,当前注释,不再描述
        // 当前应用appKey
        String appKey = "fqRp1D9y1v";
        // 当前应用secret
        String appSecret = "d5c2caf79c4708539f81ff0701160510b3982837";
        // 实例化一个配置类
        Config config = getConfig(false,appKey,appSecret);
        Token token = getToken(config);
        System.out.println(token);
        OrderService orderService = new OrderService(config, token);
        return orderService;
    }

	 /**
     * 获取配置类
     * @param isSandbox 是否沙箱
     * @return
     */
    public static Config getConfig(Boolean isSandbox,String appKey,String appSecret){
        Config config=null;
        if(isSandbox){
            config=new Config(isSandbox, appKey,appSecret);
        }else{
            // TODO 填充正式环境数据
            config=new Config(isSandbox, appKey,appSecret);
        }
        return config;
    }

	  /**
     * 获取对应Token
     */
    public static Token getToken(Config config){
        // 使用config对象,实例化一个授权类
        OAuthClient client = new OAuthClient(config);
        // 使用授权类获取token
        Token token = client.getTokenInClientCredentials();
        return token;
    }

五:饿了么金额计算(结账用,暂无)


二:美团外卖对接

一:美团外卖开发平台

URL

美团开放平台-为美好智慧生活连接更多可能

二:应用及配置介绍

1:应用管理

目前美团可以通过品牌商和服务商的身份入住,当前以品牌商身份进行对接,美团只有一个开发环境,并且当测试门店绑定手机号需要联系美团人员进行绑定,否则找不到对应的测试门店,无法测试下单。

   

 2:美团商家中心

①:URL:配置美团的商家绑定对应门店,

当前无法确定门店网店,商品映射关系的参数

        ②:appId和AppSecret用于生成调用美团API的sig

③:美团网店与第三方门店绑定

描述:通过该授权页面,可以将美团对应的网店和我们的第三方门店进行绑定,但是有一个问题和饿了么不同,美团推送订单时,所携带的APP_POI_CODE,是可以直接下发我们的门店编码,如果我们没有进行配置,则默认下发美团的网店ID,而如果我们将美团的修改为第三方门店的编码,则直接下发我们的门店编码,此字段只有一个,可以通过页面进行配置,但是由于扩展性,建议落库。

三:落单代码逻辑

(正向,逆向,状态流转)

1:API层

        

    /**
     * 美团正向单
     */
    @ApiOperation(value = "美团正向单", tags = {"mTPost"}, nickname = "doPost")
    @ApiResponses(value = {@ApiResponse(code = 200, message = "000000:成功,否则失败")})
    Object mTPost(MtReqDto request);

    /**
     * 美团逆向单
     */
    @ApiOperation(value = "美团逆向单", tags = {"mTReversePost"}, nickname = "doPost")
    @ApiResponses(value = {@ApiResponse(code = 200, message = "000000:成功,否则失败")})
    Object mTReversePost(@RequestParam Map<String,Object> params);

    /**
     * 美团订单状态流转POST接口
     */
    @ApiOperation(value = "美团订单状态流转POST接口", tags = {"TriggerMtOrder"}, nickname = "doPost")
    @ApiResponses(value = {@ApiResponse(code = 200, message = "000000:成功,否则失败")})
    Object TriggerMtOrder(@RequestParam Map<String, Object> params);

  2:Controller层

	@Override
    @PostMapping(value = "/mTPost", produces = {"text/html"})
    public Object mTPost(MtReqDto mtReqDto){
        log.info("测试是否接收到美团正向消息:{}",JSONObject.toJSONString(mtReqDto));
        return tradeCenterService.MtTrigger(mtReqDto);
    }

    @Override
    @GetMapping(value = "/mTReversePost", produces = {"text/html"})
    public Object mTReversePost(Map<String,Object> params){
        MtReverseReqDto mtReverseReqDto = MtofBeanUtils.mapToObject(params, MtReverseReqDto.class);
        log.info("测试是否接收到美团逆向消息:{}",JSONObject.toJSONString(mtReverseReqDto));
        return tradeCenterService.MtReverseTrigger(mtReverseReqDto);
    }

    @Override
    @GetMapping(value = "/TriggerMtOrder")
    public Object TriggerMtOrder(Map<String,Object> params){
        log.info("测试是否接收到美团取消消息:{}",JSONObject.toJSONString(params));
        //9:取消(针对某种场景的取消)
        MtReqDto mtReqDto= MtReqDto.builder()
                .order_id(Long.valueOf(params.get("order_id").toString()))
                .status("9")
                .build();
        return tradeCenterService.MtTrigger(mtReqDto);
    }	

3:service层

    /**
     * 美团正向接单以及状态流转
     */
    Object MtTrigger(MtReqDto mtReqDto);

    /**
     * 美团逆向接单以及状态流转
     */
    Object MtReverseTrigger(MtReverseReqDto mtReqDto);

4:Service实现类(采用策略+工厂,如上,不详细描述)

@Override
    public Object MtTrigger(MtReqDto reqDto) {
        Optional.ofNullable(reqDto).orElseThrow(() -> new AppException("美团单据不存在"));

        reqDto.setDetail(URLUtil.decode(reqDto.getDetail()));
        reqDto.setPoi_receive_detail(URLUtil.decode(reqDto.getPoi_receive_detail()));

        String message = URLUtil.decode(JSONObject.toJSONString(reqDto));
        MtReqDto mtReqDto = JSONObject.parseObject(message, MtReqDto.class);

        try {
            
            String type = "MT" + mtReqDto.getStatus();
            
            //策略+工厂
            MtDomainService orderEvent = MtFactory.getOrderEvent(type);
            orderEvent.orderEvent(mtReqDto, type);

            Map map = new HashMap();
            return map.put("data", "OK");

        } catch (Exception e) {
            return e.getMessage();
        }
    }

    @Override
    public Object MtReverseTrigger(MtReverseReqDto reqDto) {
        Optional.ofNullable(reqDto).orElseThrow(() -> new AppException("美团单据不存在"));

        String message = URLUtil.decode(JSONObject.toJSONString(reqDto));
        MtReverseReqDto mtReverseReqDto = JSONObject.parseObject(message, MtReverseReqDto.class);

        try {
            String type = "MT_REVERSE_" + mtReverseReqDto.getRes_type();
            
            //1;3;7;8:取消
            if (mtReverseReqDto.getRes_type() == 1 || mtReverseReqDto.getRes_type() == 3
                    || mtReverseReqDto.getRes_type() == 7 || mtReverseReqDto.getRes_type() == 8) {
                type = "MT_REVERSE_CANCEL";
            } else if (mtReverseReqDto.getRes_type() == 2 || mtReverseReqDto.getRes_type() == 4
                    || mtReverseReqDto.getRes_type() == 5 || mtReverseReqDto.getRes_type() == 6) {
                //2;4;5;6:完成
                type = "MT_REVERSE_FINISH";
            }else if (mtReverseReqDto.getRes_type()==0&& CollectionUtils.isNotEmpty(mtReverseReqDto.getFood())){
                type="MT_REVERSE_PART";
            }
            
            MtDomainService orderEvent = MtFactory.getOrderEvent(type);
            orderEvent.orderReverseEvent(mtReverseReqDto, type);

            Map map = new HashMap();
            return map.put("data", "OK");

        } catch (Exception e) {
            return e.getMessage();
        }
    }

5:使用工具类

①:美团订单表头

/**
 * 美团订单表头
 */
@Validated
@AllArgsConstructor
@NoArgsConstructor
@Data
@ApiModel
@Builder
public class MtReqDto implements Serializable{
    private static final long serialVersionUID = 1L;

    /**
     * 订单ID(数据库中请用bigint(20)存储此字段)
     */
    private Long order_id;
    /**
     * 订单展示ID
     */
    private Long wm_order_id_view;
    /**
     * APP方商家ID
     */
    private String app_poi_code;
    /**
     * 美团商家名称
     */
    private String wm_poi_name;
    /**
     * 美团商家地址
     */
    private String wm_poi_address;
    /**
     * 美团商家电话
     */
    private String wm_poi_phone;
    /**
     * 收件人地址(此字段为用户填写的收货地址,可在开发者中心订阅是否根据经纬度反查地址,若订阅则会在此字段后追加反查结果,并用“@#”符号分隔,如:用户填写地址@#反查结果)
     */
    private String recipient_address;
    /**
     * 收件人电话(请兼容13812345678和13812345678_123456两种号码格式,以便对接隐私号订单,最多不超过20位)
     */
    private String recipient_phone;
    /**
     * 备用隐私号 ["13812345678_1236","13812345678_3456"]
     */
    private List<String> backup_recipient_phone;
    /**
     * 收件人姓名(若用户没有填写姓名,此字段默认为空。可在开发者中心订阅是否用“美团客人”填充此字段)
     */
    private String recipient_name;
    /**
     * 门店配送费
     */
    private BigDecimal shipping_fee;
    /**
     * 总价
     */
    private BigDecimal total;
    /**
     * 原价
     */
    private BigDecimal original_price;
    /**
     * 忌口或备注
     */
    private String caution;
    /**
     * 送餐员电话
     */
    private String shipper_phone;
    /**
     * 订单状态 2:新订单-用户支付完成,待商家接单 4:商家已接单 8:订单已完成 9:订单已取消
     */
    private String status;
    /**
     * 城市ID(目前暂时用不到此信息)
     */
    private Long city_id;
    /**
     * 是否开发票
     */
    private Integer has_invoiced;
    /**
     * 发票抬头
     */
    private String invoice_title;
    /**
     * 纳税人识别号,该信息默认不推送,如有需求可在开发者中心订阅
     */
    private String taxpayer_id;
    /**
     * 创建时间 (注:订单创建时间)
     */
    private Long ctime;
    /**
     * 更新时间
     */
    private Long utime;
    /**
     * 用户预计送达时间,“立即送达”时为0,非0 代表非即时单(包含到店自取,时间为用户到店取餐时间),预订单代表用户下单时填写的预计送达时间,单位是秒,10位时间戳
     */
    private Long delivery_time;
    /**
     * 	是否是第三方配送平台配送,0表否,1表是)
     */
    private Integer is_third_shipping;
    /**
     * 支付类型,1表货到付款,2表在线支付(非支付渠道)
     */
    private Integer pay_type;
    /**
     * 取餐类型(0:普通取餐;1:到店取餐),该信息默认不推送,如有需求可在开发者中心订阅
     */
    private Integer pick_type;
    /**
     * 实际送餐地址纬度
     */
    private Double latitude;
    /**
     * 实际送餐地址经度
     */
    private Double longitude;
    /**
     * 门店当天的推单流水号,该信息默认不推送,如有需求可在开发者中心订阅
     */
    private Integer day_seq;
    /**
     * 用户是否收藏此门店(true, false),该信息默认不推送,如有需求可在开发者中心订阅
     */
    private Boolean is_favorites;
    /**
     * 用户是否第一次在此门店点餐(true, false),该信息默认不推送,如有需求可在开发者中心订阅
     */
    private Boolean is_poi_first_order;
    /**
     * 用餐人数(0:用户没有选择用餐人数;1-10:用户选择的用餐人数;-10:10人以上用餐;88:用户需要餐具;99:用户不需要餐具),该信息默认不推送,如有需求可在开发者中心订阅
     */
    private String dinners_number;
    /**
     * 订单配送方式,该信息默认不推送,如有需求可在开发者中心订阅
     */
    private String logistics_code;
    /**
     * 商家对账信息的json数据,该信息默认不推送,如有需求可在开发者中心订阅
     */
    private String poi_receive_detail;

    /**
     * 第一种:优惠信息,其值为由list<object>序列化得到的json字符串
     */
    private String extras;
    /**
     * 门店平均配送时长,单位为秒
     */
    private String avg_send_time;
    /**
     * 订单来源属性标识,该信息默认不推送,如有需求可在开发者中心订阅
     */
    private String channel;
    /**
     * 开发票方式,0,非自动开票订单,1美团发票合作商家,在线自动开具电子发票
     */
    private Integer invMakeType;
    /**
     * 订单数据状态标记。当订单中部分字段的数据因内部交互异常或网络等原因延迟生成(超时),导致开发者当前获取的订单数据不完整,此时平台对订单数据缺失情况进行标记。如不完整,建议尝试重新查询。注意,平台仅对部分模块的数据完整性进行监察标记(参考incmp_modules字段)。参考值: -1:有数据降级 0:无数据降级
     */
    private Integer incmp_code;
    /**
     *有降级的数据模块的集合,参考值: 0:订单商品详情 1:订单优惠信息 2:商品优惠详情 3:订单用户会员信息 4:订单维度的商家对账信息 5:订单维度的商家对账信息(元) 6:订单收货人地址 7:订单配送方式 8:开放平台用户id 9:部分退款商品信息 10:退货退款物流信息 11:部分订单基本信息(包括订单优惠信息、订单商品详情、门店信息等) 12:sku信息 13:spu信息 14:商品信息(可能是sku或spu等商品相关信息获取时降级) 15:替换折扣价为原价
     */
    private Set<Object> incmp_modules;
    /**
     * 其他费用,需要在mcc增加对应需要推送的app_id才推送,List<Object>的json string
     */
    private String extendsAmount;

    /**
     * 订单商品详情,其值为由list<object>序列化得到的json字符串
     */
    private String detail;

}

②:美团订单明细信息

/**
 * 美团订单明细信息
 */
@Validated
@AllArgsConstructor
@NoArgsConstructor
@Data
@ApiModel
public class MtOrderLineReqDto implements Serializable{
    private static final long serialVersionUID = 1L;

    /**
     * 分组名称 varchar
     */
    @ApiModelProperty(value = "分组名称")
    @JsonProperty(index = 10)
    private String app_food_code;

    /**
     * sku编码 varchar
     */
    @ApiModelProperty(value = "sku编码")
    @JsonProperty(index = 10)
    private String sku_id;

    /**
     * 商品数量 varchar
     */
    @ApiModelProperty(value = "商品数量")
    @JsonProperty(index = 10)
    private BigDecimal quantity;

    /**
     * 商品单价,不包含餐盒费,此字段默认为活动折扣后价格,可在开发者中心订阅是否替换为原价 varchar
     */
    @ApiModelProperty(value = "商品单价,不包含餐盒费,此字段默认为活动折扣后价格,可在开发者中心订阅是否替换为原价")
    @JsonProperty(index = 10)
    private BigDecimal price;

    /**
     * 餐盒数量,在计算餐盒数量和餐盒费用时,请先按照商品规格维度将餐盒数量向上取整后,再乘以相应的餐盒费单价,计算得出餐盒费用。 varchar
     */
    @ApiModelProperty(value = "餐盒数量,在计算餐盒数量和餐盒费用时,请先按照商品规格维度将餐盒数量向上取整后,再乘以相应的餐盒费单价,计算得出餐盒费用。")
    @JsonProperty(index = 10)
    private String box_num;

    /**
     * 餐盒价格 varchar
     */
    @ApiModelProperty(value = "餐盒价格")
    @JsonProperty(index = 10)
    private BigDecimal box_price;

    /**
     * 单位 varchar
     */
    @ApiModelProperty(value = "单位")
    @JsonProperty(index = 10)
    private String unit;

    /**
     * 商品折扣,默认为1,仅美团商家可设置 varchar
     */
    @ApiModelProperty(value = "商品折扣,默认为1,仅美团商家可设置")
    @JsonProperty(index = 10)
    private String food_discount;

    /**
     * 菜品名称 varchar
     */
    @ApiModelProperty(value = "菜品名称")
    @JsonProperty(index = 10)
    private String food_name;
}

③:美团逆向订单表头

/**
 * 美团逆向订单表头
 */
@Validated
@AllArgsConstructor
@NoArgsConstructor
@Data
@ApiModel
@Builder
public class MtReverseReqDto implements Serializable{
    private static final long serialVersionUID = 1L;

    /**
     * 逆向ID
     */
    private String refund_id;

    /**
     * 订单ID(数据库中请用bigint(20)存储此字段)
     */
    private Long order_id;
    /**
     * 通知类型:apply:发起退款;agree:确认退款;reject:驳回退款;cancelRefund:用户取消退款申请
     */
    private String notify_type;
    /**
     * 退款原因
     */
    private String reason;
    /**
     * 0:未处理;1:商家驳回退款请求;2、商家同意退款;3、客服驳回退款请求;4、客服帮商家同意退款;5、超过3小时自动同意;6、系统自动确认;7:用户取消退款申请;8:用户取消退款申诉
     */
    private Integer res_type;
    /**
     * 是否申诉退款:0-否;1-是
     */
    private Integer is_appeal;
    /**
     * 退款金额
     * 消息类型msg_type:
     * 1:全部退款申请
     * 2:全部退款申请处理(同意or拒绝)
     * 3:全部退款退款成
     * 4:全部退款失败
     * 11:部分退款申请
     * 12:部分退款申请处理(同意or拒绝)
     * 13:部分退款退款成功
     * 14:部分款退款失败
     *  23:重复支付退款)
     * 当 msg_type > 10时,有金额money退款字段
     */
    private String money;

    /**
     * 退款商品信息
     */
    private List<ItemLineReqDto> food;
}

④:美团逆向订单明细

/**
 * 美团逆向订单明细
 */
@Validated
@AllArgsConstructor
@NoArgsConstructor
@Data
@ApiModel
@Builder
public class ItemLineReqDto implements Serializable {

    private static final long serialVersionUID = 1L;

    /**
     * APP方菜品id,最大长度128,不同门店可以重复,同一门店内不能重复 varchar
     */
    @ApiModelProperty(value = "APP方菜品id,最大长度128,不同门店可以重复,同一门店内不能重复")
    @JsonProperty(index = 10)
    private String app_food_code;

    /**
     * 退款菜品名称
     */
    @ApiModelProperty(value = "退款菜品名称")
    @JsonProperty(index = 10)
    private String food_name;

    /**
     * sku码
     */
    @ApiModelProperty(value = "sku码")
    @JsonProperty(index = 10)
    private String sku_id;

    /**
     * 单位
     */
    @ApiModelProperty(value = "单位")
    @JsonProperty(index = 10)
    private String spec;

    /**
     * 商品价格
     */
    @ApiModelProperty(value = "商品价格")
    @JsonProperty(index = 10)
    private BigDecimal food_price;

    /**
     * 商品数量
     */
    @ApiModelProperty(value = "商品数量")
    @JsonProperty(index = 10)
    private Integer count;

    /**
     * 打包盒数量
     */
    @ApiModelProperty(value = "打包盒数量")
    @JsonProperty(index = 10)
    private BigDecimal box_num;

    /**
     * 打包盒价格
     */
    @ApiModelProperty(value = "打包盒价格")
    @JsonProperty(index = 10)
    private BigDecimal box_price;

    /**
     * 菜品原价,单位元
     */
    @ApiModelProperty(value = "菜品原价,单位元")
    @JsonProperty(index = 10)
    private BigDecimal origin_food_price;

    /**
     * 退款价格,单位元
     */
    @ApiModelProperty(value = "退款价格,单位元")
    @JsonProperty(index = 10)
    private BigDecimal refund_price;

}

⑤:转换类

    /**
     * 美团订单落单时转换为中心订单(正向)
     *
     * @param
     * @return
     */
    public static OrderReqDto MtDtoToOrderDto(MtReqDto mtReqDto) {
        OrderReqDto orderReqDto = OrderReqDto.builder()
                .orderIdOut(String.valueOf(mtReqDto.getOrder_id()))
                .saleTime(LocalDateTime.now())
                .tradeType(TradeConstants.TradeType.MT)
                .orderType(TradeConstants.OrderType.MT)
                .storeType(TradeConstants.storeType.STORE)
            	//该字段代表的可以是美团自己生成的ID,也可以使用我们第三方的门店编码,通过门店授权进行绑定
                .storeCode(meiTuanReqDto.getApp_poi_code())
                .payableAmount(mtReqDto.getTotal())
                .actualAmount(mtReqDto.getTotal())
                .orderAmount(mtReqDto.getOriginal_price())
                .freightAmount(mtReqDto.getShipping_fee())
                .orderSplit(TradeConstants.orderSplit.NO_SPLIT)
                .orderSupportReverse(TradeConstants.orderSupportReverse.SUPPORT_REVERSE)
                .orderFlatFlag(TradeConstants.FlatFlag.NO_FLAT_FLAG_NOMAL).build();

        //地址信息
        OrderAddressReqDto addressReqDto = OrderAddressReqDto.builder()
                .detailAddress(mtReqDto.getRecipient_address())
                .receiver(mtReqDto.getRecipient_name()).build();
        orderReqDto.setAddress(addressReqDto);

        //扩展信息
        OrderExtReqDto orderExtReqDto = OrderExtReqDto.builder()
                .logisticsStatus(TradeConstants.LogisticsStatus.Logistics).build();
        orderReqDto.setOrderExt(orderExtReqDto);

        //订货信息
        OrderReservationReqDto orderReservationResDto = OrderReservationReqDto.builder()
                .takeSendTime(LocalDateTime.now())
                .reservationUserName(mtReqDto.getRecipient_name())
                .phoneNumber(mtReqDto.getRecipient_phone())
                .productionStoreCode(meiTuanReqDto.getApp_poi_code())
                .takeSendStoreCode(meiTuanReqDto.getApp_poi_code()).build();
        orderReqDto.setReservationResDto(orderReservationResDto);

        //明细信息
        List<MtOrderLineReqDto> mtOrderLineReqDtoList = JSONArray.parseArray(mtReqDto.getDetail(), MtOrderLineReqDto.class);
        List<OrderLineReqDto> orderLineReqDtoList = new ArrayList<>();
        mtOrderLineReqDtoList.forEach(v -> {
            OrderLineReqDto orderLineReqDto = OrderLineReqDto.builder()
                    .actualAmount(v.getPrice())
                    .originPrice(v.getPrice())
                    .actualPrice(v.getPrice())
                	//该字段为我们自定义的商品编码,也可使用美团自己生成的,但字段名非这个
                    .itemCode(v.getSku_id())
                    .skuCode(v.getSku_id())
                    .itemName(v.getFood_name())
                    .skuQuantity(v.getQuantity())
                    .storeCode(meiTuanReqDto.getApp_poi_code())
                    .build();
            orderLineReqDtoList.add(orderLineReqDto);
        });
        orderReqDto.setDetailList(orderLineReqDtoList);
        return orderReqDto;
    }

    /**
     * 美团订单落单时转换为中心订单(逆向全额)
     */
    public static ReverseOrderReqDto MtReverseToOrderDto(MtReverseReqDto mtReverseReqDto, OrderResDto orderResDto) {
        BigDecimal money = BigDecimal.ZERO;
        if (mtReverseReqDto.getMoney() != null) {
            money = new BigDecimal(mtReverseReqDto.getMoney());
        }
        ReverseOrderReqDto reverseOrderDto = ReverseOrderReqDto.builder()
                .orderIdOut(mtReverseReqDto.getRefund_id())
                .orderNo(orderResDto.getOrderNo())
                .tradeType(TradeConstants.TradeType.MT_REVERSE)
                .saleTime(LocalDateTime.now())
                .storeType(orderResDto.getStoreType())
                .storeCode(orderResDto.getStoreCode())
                .relateOrderType(TradeConstants.relateOrderType.GENERAL_RELATE_ORDER)
                .actualAmount(mtReverseReqDto.getMoney() != null ? money : null)
                .parentOrderNo(orderResDto.getOrderNo())
                .relateOriginNo(orderResDto.getOrderNo())
                .reverseType(TradeConstants.relateOrderType.GENERAL_RELATE_ORDER)
                .remark(mtReverseReqDto.getReason()).build();
        List<ReverseOrderLineReqDto> orderLineReqDtoList = new ArrayList<>();

        orderResDto.getDetailList().forEach(v -> {
            ReverseOrderLineReqDto reverseOrderLineReqDto = ReverseOrderLineReqDto.builder()
                    .orderNo(orderResDto.getOrderNo())
                    .storeCode(orderResDto.getStoreCode())
                    .skuCode(v.getSkuCode())
                    .itemCode(v.getItemCode())
                    .itemName(v.getItemName())
                    .payableAmount(v.getActualAmount())
                    .actualAmount(v.getActualAmount())
                    .skuQuantity(v.getSkuQuantity())
                    .refundType(TradeConstants.relateOrderType.GENERAL_RELATE_ORDER).build();
            orderLineReqDtoList.add(reverseOrderLineReqDto);
        });
        reverseOrderDto.setDetailList(orderLineReqDtoList);
        return reverseOrderDto;
    }

四:调用美团API接口(以确认接单为案例)

前面如上,采用策略+工厂模式进行扩展,不再描述

①:Service实现类

@Service
@Slf4j
public class MtOrderConfirmServiceImpl implements OrderConfirmDomainService {

    @Autowired
    private WaiMaiReqDtoConvertor waiMaiReqDtoConvertor;

    @Autowired
    TradeCenterDomainService tradeCenterDomainService;

    @Autowired
    private RestTemplateUtils restTemplateUtils;

    @Override
    public String getEvent() {
        return TradeConstants.OrderConfirmType.MT;
    }

    @Override
    public void orderEvent(OrderResDto orderResDto, String type) {
        try {
            //获取Sig
            long timestamp = WaiMaiUtil.getMtTimestamp();
            String md5Input = DAConfigConst.Url.MT_CONFIRM + "?app_id=" + WaiMaiUtil.MtConfig.appId 
                	+ "&order_id=" + orderResDto.getOrderIdOut()
                	+ "&timestamp=" + timestamp + WaiMaiUtil.MtConfig.appSecret;
			
         	//此Sig为调用美团Api接口必传参数,由美团URL加API上列举系统参数进行MD5加密生成
            //可通过API控制台先行测试
            String sig = WaiMaiUtil.getSig(md5Input);
            //调用美团参数
            Map<String, Object> params = new MapUtils()
                    .put("app_id", WaiMaiUtil.MtConfig.appId)
                    .put("order_id", orderResDto.getOrderIdOut())
                    .put("timestamp", timestamp)
                    .put("sig", sig);
            log.info("确认接单参数:{}", JSONObject.toJSONString(params));
            //此处采用工具类直接调用外部接口,DA为URL路径
            String result = HttpUtil.sendGet(DAConfigConst.Url.MT_CONFIRM, params);
			
            //此处针对返回报错信息抛出对应异常
            JSONObject jsonObject = JSONObject.parseObject(result);
            if (jsonObject.get("error")!=null){
                MtErrorLineVo error = JSONObject.parseObject(jsonObject.get("error").toString(), MtErrorLineVo.class);
                throw new AppException(error.getMsg());
            }
        } catch (Exception e) {
            throw new AppException(e.getMessage());
        }
    }
}

②:配置类

    public static class MtConfig{
        public static final String appId="4746";

        public static final String appSecret="ec3498174474570a57bcb540dbd537cb";
    }

    /**
     * 获取时间戳
     * @return
     */
    public static long getMtTimestamp(){
        LocalDateTime ldt = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
        return ldt.toEpochSecond(ZoneOffset.ofHours(8));
    }

    /**
     * MD5加密Sig
     * @param sig
     * @return
     */
    public static String getSig(String sig){
       return Md5Util.encryptMessage(sig);
    }

③:MD5加密类

/**
 * @Description Md5加密工具类
 */
public class Md5Util {

    /**
     * 加密明文</br>
     * <p>
     * 加密步骤:</br>
     * 1.先进行MD5加密</br>
     * 4.加密后的明文转换为16进制</br>
     *
     * @param message 明文
     * @return
     */
    public static String encryptMessage(String message) {
        /**
         * 进行Md5加密
         */
        byte[] d5Sha1Data = DigestUtils.md5(EncodingUtils.getBytes(
                (message == null ? "" : message),
                "utf-8"));
        /**
         * 加密后的信息转换为16进制
         */
        StringBuilder builder = new StringBuilder();
        for (byte b : d5Sha1Data) {
            builder.append(String.format("%02x", new Integer(b & 0xff)));
        }
        return builder.toString();
    }

 

五:美团金额对账

  • 4
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
### 回答1: 同城外卖系统源码、同城O2O配送外卖小程序、外卖APP和仿美团饿了么骑手导航配送平台都是用于创建和管理外卖订单、配送和导航的软件。 同城外卖系统源码是一个提供外卖订餐服务的平台,使用者可以在平台上浏览餐厅菜单、下单、付款和选择配送方式。该源码可以供开发人员根据自己的需求进行二次开发,定制自己的外卖订餐系统。 同城O2O配送外卖小程序是一种基于微信或其他社交媒体平台的小程序,用户可以通过搜索附近的餐厅、浏览菜单、下单和支付,餐厅可以收到订单并安排配送。该小程序具有便捷、快速和实时的特点,方便用户下单和餐厅配送管理。 外卖APP是一款专门用于订购外卖的手机应用程序,用户可以通过该APP在手机上浏览餐厅菜单、下单、支付和选择配送方式。该APP通常有用户评价、促销优惠等功能,为用户提供更便捷的外卖服务体验。 仿美团饿了么骑手导航配送平台是一个提供骑手导航和配送管理的软件平台。骑手可以通过该平台接收订单、查看订单详情、导航到目的地以及完成配送。该平台可以根据订单的优先级和距离进行最优的分配,提高配送效率和服务质量。 总之,同城外卖系统源码、同城O2O配送外卖小程序、外卖APP和仿美团饿了么骑手导航配送平台都是为了提供便捷的外卖订购和配送服务,满足用户和商家的需求。不同的系统和平台有不同的特点和功能,可以根据个人或商家的需求选择合适的解决方案。 ### 回答2: 同城外卖系统源码是一种提供给餐饮行业的解决方案,能够帮助餐厅与消费者之间实现线上点餐、配送服务的平台。它包括同城o2o配送外卖小程序和外卖app,可以模仿美团饿了么等知名平台的骑手导航配送平台。 同城外卖系统源码由前端和后台两部分组成。前端部分是同城o2o配送外卖小程序和外卖app,它们提供给消费者一个方便快捷的线上点餐平台,消费者可以通过这些平台选择菜品、下单支付并实时查看订单和配送进度。同时,这些平台也为餐厅提供了一个展示自己菜品并接受订单的渠道。 后台部分是同城外卖系统的核心,它连接了餐厅、骑手和消费者,实现了订单管理、配送管理、骑手管理等功能。通过后台管理系统,餐厅可以接受并管理订单、查看菜品销售情况,骑手可以接受配送任务并进行导航定位,消费者则可以实时查看订单状态和配送进度。 同城外卖系统源码的优势在于它提供了一整套完善的解决方案,不仅包括前端的小程序和app开发,还有后台系统的搭建和管理。这样的系统源码可以有效减少开发成本和周期,帮助餐厅快速上线线上外卖服务。同时,系统的界面和功能可以仿照美团饿了么等成功平台,提供给用户一种熟悉、便捷的体验。 综上所述,同城外卖系统源码包括同城o2o配送外卖小程序和外卖app,可以通过仿美团饿了么的骑手导航配送平台实现餐厅与消费者之间的线上点餐和配送服务。这种系统源码具有成本低、周期短、界面友好等优势,是餐饮行业实现线上外卖服务的一个可行解决方案。 ### 回答3: 同城外卖系统源码是为了满足现代社会外卖配送需求而开发的一种技术解决方案。该系统源码适用于开发同城o2o配送外卖小程序或外卖app,功能类似于美团饿了么等外卖平台,可以提供用户下单、商家接单、骑手接单、订单配送等功能。 该系统源码基于互联网和移动端技术开发,具备可扩展性和稳定性。通过前端界面和后端逻辑的配合,用户可以通过小程序或app浏览并选择附近的餐馆、菜品,进行下单和支付。商家可以接收订单、处理订单,并与骑手进行配送沟通。骑手可以接收配送任务,并通过导航系统进行准确的配送。 这个系统源码可以帮助餐馆和外卖平台开发出高效、便捷的配送系统,提升用户体验和订单处理效率。同时,它也可以提供一种创业机会,帮助个人或企业开发自己的同城外卖平台。 总结而言,同城外卖系统源码是一种满足现代社会外卖配送需求的技术解决方案。通过开发同城o2o配送外卖小程序或外卖app,可以构建类似于美团饿了么等外卖平台的功能,并为用户、商家和骑手提供便捷的下单、接单和配送服务。这个系统源码具备可扩展性和稳定性,适合个人创业或企业开发自己的同城外卖平台

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值