小程序即时配送功能开发,引发的猜想。首先即时配送功能是微信小程序刚推出的新功能,接口有很多坑爹的,比如经纬度那些,就要精确到保留到小数点后面10位,文档都没说;如果是小程序第三方开发平台开发的话,调用“替商户开通即时配送权限接口”和”绑定快递功能接口“还要输入第三方的的账号密码,恶心到爆炸,建议有这方面开发需求的开发小伙伴,跳过这2个接口,确实很多坑,我开发遇到过了;比如啥openid未命中,商户不存在,XX的,要开发就一定要使用真实正式数据,不然很头疼的,再说一遍,微信小程序官方文档一定要自己看数据的返回,文档是一堆坑的。
经纬度保留至小数点后10位
'lat'=>sprintf("%1\$.10f",$orderInfo->latitude)
在对接的过程中,发现了一个对接接口的好方式,以后照葫芦画瓢,美滋滋的。数据的处理都写着一个独立的model类中,像这些第三方的接口数据返回对应的中文统一定义成变量,是一个不错的选择。
封装参数,请求接口。
//封装参数:多长都不怕!
$send_address=[
'name'=>$send['name'],
'city'=>$send['address'], //todo 截取市
'address'=>$send['address'],
'address_detail'=>$send['address'],
'phone'=>$send['phone'],
'lat'=>sprintf("%1\$.10f",$send['lat']),
'lng'=>sprintf("%1\$.10f",$send['lng']),
];
//收货人地址
$receiver=[
'address'=>$orderInfo->address,
'address_detail'=>$orderInfo->address,
'city'=>json_decode($orderInfo->address_data,true)['city'],
'coordinate_type'=>0,
'lat'=>sprintf("%1\$.10f",$orderInfo->latitude), //精确到小数点后6位;剩余0补齐到10位
'lng'=>sprintf("%1\$.10f",$orderInfo->longitude),
'name'=>$orderInfo->name,
'phone'=>$orderInfo->mobile];
//订单信息
$order_info=[
'delivery_service_code'=>$config['service_code'],
'expected_delivery_time'=>time(), //期待时间
'is_direct_delivery'=> 0,
'is_finish_code_needed'=> 1,
'is_insured'=> 0,
'is_pickup_code_needed'=> 1,
'note'=> 'test_note',
'order_time'=> $orderInfo->addtime,
'order_type'=> 0,
'poi_seq'=> $poi_seq,
'tips'=> 0
];
$shop=[
'goods_count'=>$good_nums,
'goods_name'=> '默认商品',
'img_url'=>$pic?:'',
'wxa_path'=>$page?:'/page/index/index'
];
$cargo=[
'cargo_first_class'=> '医药健康',
'cargo_second_class'=> '送药',
'goods_value'=>$orderInfo->total_price?:0,
'goods_weight'=> $this->weight,
];
$params = [
'access_token'=>$token,
//'shopid'=>$config['appkey'],
'shopid'=>"test_shop_id",
'shop_order_id' => $orderInfo->order_no,
//'shop_no' => $shop_model->encode, //门店编码
'shop_no' => '11047059', //门店编码
// 'delivery_sign' => SHA1($config['appkey'].$orderInfo->order_no.$config['secret']),
'delivery_sign' => SHA1("test_shop_id".$orderInfo->order_no."test_app_secrect"),
//'delivery_id' => $delivery_arr[$config['name']],
'delivery_id' =>"TEST",
'openid' => $open_id->wechat_open_id,
'sender' => $send_address,
'receiver'=>$receiver,
'cargo'=>$cargo,
'order_info'=>$order_info,
'shop'=>$shop,
];
//即时配送
$js=new DcJsForm();
$res=$js->actionAddDeliveryOrder($params);
请求接口中间过程,这个过程是主要不想数据入库直接弹出显示,看个人需求使用
/**
* 第三方代商户发起绑定配送公司帐号的下配送单接口请求地址
*/
public function actionAddDeliveryOrder($params)
{
$code = 0;
$msg = '';
$return_data=[];
$data = $this->addDelivery($params);
if ($data['resultcode'] == 0 && $data['resultmsg'] == 'ok') {
$return_data=$data;
$errmsg = '下单成功';
$msg .= '【' . $this->store->name . '】' . $errmsg . '<br/>';
}
if (isset($data['resultcode']) && $data['resultcode'] == '1010') {
$code = 1;
$errmsg = '收件人信息不正确';
$msg .= '【' . $this->store->name . '】' . $errmsg . '<br/>';
}
if (isset($data['errcode']) && $data['errcode'] != 0 && in_array($data['errcode'],array_keys($this->errArr))) {
$code = 1;
$errmsg = $this->errArr[$data['errcode']];
$msg .= '【' . $this->store->name . '】' . $errmsg . '<br/>';
}
if (isset($data['errcode']) && $data['errcode'] != 0 && !in_array($data['errcode'],array_keys($this->errArr))) {
$code = 1;
$errmsg =$data['resultmsg'];
$msg .= '【' . $this->store->name . '】' . $errmsg . '<br/>';
}
return [
'code' => $code,
'data' => $msg ?: '下单成功',
'return_data'=>$return_data?:[]
];
}
正式请求:
/**
* 第三方代商户发起下即时配送单
* @param int $type
* @return mixed
* @throws \Exception
*/
public function addDelivery($params)
{
$url = self::WX_OPENDELIVERY_URL.'/order/add?access_token='.$params['access_token'];
$data = $this->curlPost($url, $params);
return $data;
}
//封装
private function curlPost($url, $data = null, $isJson = true)
{
$curl = $this->getCurl();
$data = $isJson ? json_encode($data, JSON_UNESCAPED_UNICODE) : $data;
$curl->post($url, $data);
if ($curl->error_code) {
throw new \Exception($curl->error_message);
}
return json_decode($curl->response, true);
}
private function getCurl()
{
$curl = new Curl();
$curl->setOpt(CURLOPT_SSL_VERIFYPEER, false);
$curl->setOpt(CURLOPT_SSL_VERIFYHOST, false);
return $curl;
}
因为这个即时配送会涉及到微信那边的事件推送,因此推送处理!模板消息发送等等一系列的处理,你懂的。
//处理微信公众号平台转发的事件通知 (这个是配置的推送接收地址!)
public function actionSyncEvent()
{
$param = \Yii::$app->request->post();
$check = checkSign();
if (!$check) {
return new ApiResponse(1, "非法请求", "");
}
$data = json_decode(base64_decode($param['data']),true);
$data_post = base64_decode($param['data_post']);
if (!$data['ToUserName']) {
return new ApiResponse(1, "数据格式错误", "");
}
$event = new WechatEvents();
$event->public_id = $data['ToUserName'];
$event->event = $data['Event'];
$event->data = json_encode($data);
$event->data_post = $data_post;
$event->status = 0;
$time = time();
$event->cTime = $time;
$event->cTime_format = date ( 'Y-m-d H:i:s',$time);
$event->save();
return new ApiResponse(0, "转发成功", "");
}
想想,数据是推送过来了,但是我们怎么触发处理呢?定时任务啥啥的看个人需求罗。
<?php
namespace app\modules\cli\controllers;
use app\controllers\Controller;
use app\models\MsOrder;
use app\models\MsOrderRefund;
use app\models\Order;
use app\models\OrderRefund;
use app\models\PtOrder;
use app\models\PtOrderRefund;
use app\models\Store;
use app\models\WechatApp;
use app\models\WechatTplMsgSender;
use app\modules\cli\models\MessageForm;
use app\models\User;
use app\models\Member;
use app\models\UserMember;
use app\models\Register;
use app\models\WechatEvents;
use app\modules\mch\models\DcEnum;
use app\modules\mch\models\MemberListForm;
use luweiss\wechat\Wechat;
class MessageController extends Controller{
/**
* 公众号事件:接收 + 处理
*/
public function actionIndex(){
$query = WechatEvents::find()
->where(['status' => 0])
->andWhere(['in', 'event', ['update_waybill_status','user_get_card', 'submit_membercard_user_info', 'update_member_card']])
->orderBy('id ASC')
->limit(10);
$messages = $query->all();
if(empty($messages)){
return false;
}
foreach($messages as $message) {
$data = json_decode($message->data, true);
switch ($message->event) {
case 'update_waybill_status': //即时配送订单回调
$res = $this->getOrderStatus($data, $message->cTime);
if ($res) {
$message->status = 1;
}else{
$message->status = 2;
}
$message->save();
usleep(50000);
break;
case 'user_get_card': // 会员卡领取
$res = $this->getUserMember($data, $message->cTime);
if ($res) {
$message->status = 1;
}else{
$message->status = 2;
}
$message->save();
usleep(50000);
break;
case 'submit_membercard_user_info': // 会员卡激活:同步积分、拉取会员信息
$res = $this->actUserMember($data, $message->cTime);
if ($res) {
$message->status = 1;
}else{
$message->status = 2;
}
$message->save();
break;
case 'update_member_card': // 会员信息更新:积分更新
$message->status = 1;
$message->save();
break;
}
}
exit;
}
/**
* 领卡事件处理:
*
* 1、查卡券
* 2、是否同步积分
* ①已激活:更新usermember,积分同步(user->integral)
* ②未激活:新增usermember,积分不同步(激活事件处理)
* 3、设置信息
*
*/
public function getUserMember($list = [], $time)
{
$member = Member::findOne(['card_id' => $list['CardId']]);
if (!$member) {
echo "Card:{$list['CardId']} not found\n";
return false;
}
$form = new MemberListForm();
$wechat_card = $form->getWx($member->store_id);
if (!$wechat_card) {
return false;
}
$user = User::findOne(['wechat_union_id' => $list['UnionId'],'store_id' => $member->store_id]);
$user_member = UserMember::findOne(['card_id' => $list['CardId'], 'user_card_code' => $list['UserCardCode']]);
if ($user) {
if (!empty($user_member->phone) && empty($user_member->user_id)) { // 已激活,首次领取:新增usermember,积分同步
$user->integral += $member->reward_num;
$user->total_integral += $member->reward_num;
$user->binding = $user_member->phone;
$user->save();
$register = new Register();
$register->store_id = $member->store_id;
$register->user_id = $user->id;
$register->register_time = '..';
$register->addtime = time();
$register->continuation = 0;
$register->type = 14;
$register->integral = $member->reward_num;
$register->order_id = -1;
$register->save();
}
UserMember::setList([
'store_id' => $user->store_id,
'user_id' => $user->id,
'wechat_open_id' => $user->wechat_open_id,
'wp_open_id' => $list['FromUserName'],
'wechat_union_id' => $user->wechat_union_id,
'card_id' => $list['CardId'],
'user_card_code' => $list['UserCardCode'],
'addtime' => $time,
], $user->store_id, $list['CardId'], $list['UserCardCode']);
} else {
// 公众号领取,没用过小程序
UserMember::setList([
'store_id' => $member->store_id,
'wechat_union_id' => $list['UnionId'],
'wp_open_id' => $list['FromUserName'],
'card_id' => $list['CardId'],
'user_card_code' => $list['UserCardCode'],
'addtime' => $time,
], $member->store_id, $list['CardId'], $list['UserCardCode']);
}
return true;
}
/**
* 激活事件处理:
* 1、查卡券
* 2、是否同步积分
* ①未领取:新增usermember,积分不同步(领取事件处理)
* ②已领取:更新usermember,积分同步(user->integral)
* 3、设置信息:拉取会员信息:更新usermember个人信息
*
* 注:积分更新通知微信,由Register类afterSave()处理
*/
public function actUserMember($list = [], $time)
{
$member = Member::findOne(['card_id' => $list['CardId']]);
if (!$member) {
echo "Card:{$list['CardId']} not found\n";
return false;
}
$form = new MemberListForm();
$wechat_card = $form->getWx($member->store_id);
if (!$wechat_card) {
echo $msg = "Store {$member->store_id}: get Wechat info failed\n";
\Yii::error($msg);
return false;
}
$card_get = $form->getCardUser($wechat_card->getAccessToken(),$list['CardId'],$list['UserCardCode']);
if (!$card_get) {
echo $msg = "CardCode: {$list['UserCardCode']} not found\n";
\Yii::error($msg);
return false;
} else {
// 处理用户信息
$info = $card_get['user_info']['common_field_list'];
foreach($info as $k => $v){
if ($v['name'] == 'USER_FORM_INFO_FLAG_MOBILE') {
$card_get['phone'] = $v['value'];
}
if ($v['name'] == 'USER_FORM_INFO_FLAG_NAME') {
$card_get['user_name'] = $v['value'];
}
if ($v['name'] == 'USER_FORM_INFO_FLAG_BIRTHDAY') {
$card_get['birthday'] = $v['value'];
}
}
$user_member = UserMember::findOne(['card_id' => $list['CardId'], 'user_card_code' => $list['UserCardCode']]);
if (!empty($user_member->wechat_union_id)) { // 已领取:更新usermember,积分同步
$user = User::findOne(['wechat_union_id' => $user_member->wechat_union_id, 'store_id' => $member->store_id]);
if ($user) { // 用户已注册:同步user->integral
$user->integral += $member->reward_num;
$user->total_integral += $member->reward_num;
$user->binding = $card_get['phone'];
$user->save();
$register = new Register();
$register->store_id = $member->store_id;
$register->user_id = $user->id;
$register->register_time = '..';
$register->addtime = time();
$register->continuation = 0;
$register->type = 14;
$register->integral = $member->reward_num;
$register->order_id = -1;
$register->save();
}
}else{
echo $msg = "User_member - CardCode: {$list['UserCardCode']} not found\n";
\Yii::error($msg);
return false;
}
$res = UserMember::setList([
'store_id' => $member->store_id,
'card_id' => $list['CardId'],
'user_card_code' => $list['UserCardCode'],
'nickname' => preg_replace('/[\xf0-\xf7].{3}/', '', $card_get['nickname']),
'membership_number' => $card_get['membership_number'],
'sex' => $card_get['sex'],
'user_name' => preg_replace('/[\xf0-\xf7].{3}/', '', $card_get['user_name']),
'phone' => $card_get['phone'],
'birthday' => $card_get['birthday'],
'user_card_status' => $card_get['user_card_status'],
'has_active' => ($card_get['has_active'] == true ? 1 : 0),
'addtime' => $time,
], $member->store_id, $list['CardId'], $list['UserCardCode']);
}
return true;
}
/**
* 即时配送订单回调处理
* @return string
*/
public function getOrderStatus($data=[], $time){
if (empty($data)) {
return false;
}
$type = substr($data['shop_order_id'], 0, 1);
$order_type = 0;
if ($type == 'M') {//秒杀
$info = MsOrder::find()->where(['order_no' => $data['shop_order_id']])->one();
$orderRefund = MsOrderRefund::findOne($info->id);
$order_type = 1;
}elseif ($type == 'P'){//拼团
$info = PtOrder::find()->where(['order_no' => $data['shop_order_id']])->one();
$orderRefund = PtOrderRefund::findOne($info->id);
$order_type = 2;
}else{//普通与砍价
$info = Order::find()->where(['order_no' => $data['shop_order_id']])->one();
$orderRefund = OrderRefund::findOne($info->id);
$order_type = 0;
}
if (empty($info)) {
return false;
}
$statusArr = DcEnum::JS_PS_ORDER_CODE[$data['order_status']]; //配送单状态
$status = $statusArr['status'];
//$this->addLog($info, $data, $status);
$shipment_status = $info->shipment_status;
$info->shipment_status = $status;
//已接单、已取货
if ($status == 1 || $status == 2) {
$info->courier_name = isset($data['agent']['name'])?:''; //沙盒无返回
$info->courier_phone = isset($data['agent']['phone'])?:'';
}
//已取消
if ($status == 4) {
//不在售后,取消配送订单后为未发货
if (!$orderRefund) {
$info->is_send = 0;
}
//$info->cancel_reason_id = $data['cancel_reason_id'];
//$info->cancel_reason = DcEnum::MT_SYSTEM_CANCEL_CODE[$data['cancel_reason_id']] ? $data['cancel_reason']. '(系统取消)' : $data['cancel_reason'];
}
//已送达(已收货)
if ($status == 3) {
$info->is_confirm = 1;
$info->confirm_time = time();
if ($info->pay_type == 2) {
$info->is_pay = 1;
$info->pay_time = time();
}
}
if ($status != $shipment_status) {
$content = json_decode($info->cancel_content, true);
$content[count($content)] = [
'code' => 0,
'msg' => $statusArr['msg'],
'time' => date('Y-m-d H:i:s')
];
$info->cancel_content = json_encode($content, JSON_UNESCAPED_UNICODE);
}
$result = $info->save();
if ($result) {
$this->sendStatusTpl($info->store_id, $info->user_id, $statusArr['msg'], $info->id, $order_type);
return true;
}
}
//发送状态模版消息
private function sendStatusTpl($store_id, $user_id, $statusMsg, $order_id, $order_type = 1)
{
$store = Store::findOne($store_id);
$wechat_app = WechatApp::findOne(['id' => $store->wechat_app_id]);
$wechat = new Wechat([
'subMchId' => $wechat_app->mch_id,
'appId' => $wechat_app->app_id,
'appSecret' => $wechat_app->app_secret,
'appBrandUserName' => $wechat_app->app_brand_user_name
]);
//发送处方审核通知
$wechat_tpl_meg_sender = new WechatTplMsgSender($store_id, '', $wechat, $user_id);
$wechat_tpl_meg_sender->DcStatusMsg($statusMsg, $order_type, $order_id);
}
}