You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
508 lines
20 KiB
508 lines
20 KiB
<?php
|
|
// +----------------------------------------------------------------------
|
|
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
|
// +----------------------------------------------------------------------
|
|
// | Copyright (c) 2016~2022 https://www.crmeb.com All rights reserved.
|
|
// +----------------------------------------------------------------------
|
|
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
|
// +----------------------------------------------------------------------
|
|
// | Author: CRMEB Team <admin@crmeb.com>
|
|
// +----------------------------------------------------------------------
|
|
namespace app\common\repositories\delivery;
|
|
|
|
use app\common\dao\delivery\DeliveryOrderDao;
|
|
use app\common\model\delivery\DeliveryStation;
|
|
use app\common\model\store\order\StoreOrder;
|
|
use app\common\repositories\BaseRepository;
|
|
use app\common\repositories\store\order\StoreOrderRepository;
|
|
use app\common\repositories\store\order\StoreOrderStatusRepository;
|
|
use app\common\repositories\system\merchant\MerchantRepository;
|
|
use app\common\repositories\system\serve\ServeOrderRepository;
|
|
use app\common\repositories\user\UserRepository;
|
|
use crmeb\services\DeliverySevices;
|
|
use FormBuilder\Factory\Elm;
|
|
use think\exception\ValidateException;
|
|
use think\facade\Db;
|
|
use think\facade\Log;
|
|
use think\facade\Route;
|
|
|
|
class DeliveryOrderRepository extends BaseRepository
|
|
{
|
|
|
|
protected $statusData = [
|
|
2 => '待取货',
|
|
3 => '配送中',
|
|
4 => '已完成',
|
|
-1 => '已取消',
|
|
9 => '物品返回中',
|
|
10 => '物品返回完成',
|
|
100 => '骑士到店',
|
|
];
|
|
|
|
protected $message = [
|
|
2 => StoreOrderStatusRepository::ORDER_DELIVERY_CITY_WAITING,
|
|
3 => StoreOrderStatusRepository::ORDER_DELIVERY_CITY_ING,
|
|
4 => StoreOrderStatusRepository::ORDER_DELIVERY_CITY_OVER,
|
|
-1 => StoreOrderStatusRepository::ORDER_DELIVERY_CITY_CANCEL,
|
|
9 => StoreOrderStatusRepository::ORDER_DELIVERY_CITY_REFUNDING,
|
|
10 => StoreOrderStatusRepository::ORDER_DELIVERY_CITY_REFUND,
|
|
100 => StoreOrderStatusRepository::ORDER_DELIVERY_CITY_ARRIVE,
|
|
];
|
|
|
|
public function __construct(DeliveryOrderDao $dao)
|
|
{
|
|
$this->dao = $dao;
|
|
}
|
|
|
|
public function merList(array $where, int $page, int $limit)
|
|
{
|
|
$query = $this->dao->getSearch($where)->with(['station','storeOrder'])->order('create_time DESC');
|
|
$count = $query->count();
|
|
$list = $query->page($page, $limit)->select();
|
|
|
|
return compact('count', 'list');
|
|
}
|
|
|
|
public function sysList(array $where, int $page, int $limit)
|
|
{
|
|
$query = $this->dao->getSearch($where)->with([
|
|
'merchant' => function($query) {
|
|
$query->field('mer_id,mer_name');
|
|
},
|
|
'station',
|
|
'storeOrder'=> function($query) {
|
|
$query->field('order_id,order_sn');
|
|
},
|
|
])->order('create_time DESC');
|
|
$count = $query->count();
|
|
$list = $query->page($page, $limit)->select();
|
|
|
|
return compact('count', 'list');
|
|
}
|
|
|
|
public function detail(int $id, ?int $merId)
|
|
{
|
|
$where[$this->dao->getPk()] = $id;
|
|
if ($merId) $where['mer_id'] = $merId;
|
|
$res = $this->dao->getSearch($where)->with([
|
|
'merchant' => function($query) {
|
|
$query->field('mer_id,mer_name');
|
|
},
|
|
'station',
|
|
])->find();
|
|
$order = DeliverySevices::create($res['station_type'])->getOrderDetail($res);
|
|
$res['data'] = [
|
|
'order_code' => $order['order_code'],
|
|
'to_address' => $order['to_address'],
|
|
'from_address' => $order['from_address'],
|
|
'state' => $order['state'],
|
|
'note' => $order['note'],
|
|
'order_price' => $order['order_price'],
|
|
'distance' => round(($order['distance'] / 1000),2) . ' km',
|
|
];
|
|
if (!$res) throw new ValidateException('订单不存在');
|
|
return $res;
|
|
}
|
|
|
|
public function cancelForm($id)
|
|
{
|
|
$formData = $this->dao->get($id);
|
|
if (!$formData) throw new ValidateException('订单不存在');
|
|
if ($formData['status'] == -1) throw new ValidateException('订单已取消,无法操作');
|
|
|
|
$form = Elm::createForm(Route::buildUrl('merchantStoreDeliveryOrderCancel',['id' => $id])->build());
|
|
$rule = [];
|
|
if ($formData['station_type'] == DeliverySevices::DELIVERY_TYPE_DADA){
|
|
$options = DeliverySevices::create(DeliverySevices::DELIVERY_TYPE_DADA)->reasons();
|
|
$rule[] = Elm::select('reason', '取消原因')->options($options);
|
|
$rule[] = Elm::text('cancel_reason', '其他原因说明');
|
|
}
|
|
if ($formData['station_type'] == DeliverySevices::DELIVERY_TYPE_UU){
|
|
$rule[] = Elm::input('reason', '取消原因')->required(1);
|
|
}
|
|
$form->setRule($rule);
|
|
return $form->setTitle('取消同城配送订单',$formData);
|
|
}
|
|
|
|
public function cancel($id, $merId, $reason)
|
|
{
|
|
$order = $this->dao->getWhere([$this->dao->getPk() => $id, 'mer_id' => $merId]);
|
|
if (!$order) throw new ValidateException('配送订单不存在');
|
|
if ($order['status'] == -1) throw new ValidateException('请勿重复操作');
|
|
$data = [
|
|
'origin_id' => $order['order_sn'],
|
|
'order_code'=> $order['order_code'],
|
|
'reason' => $reason['reason'],
|
|
'cancel_reason' => $reason['cancel_reason'],
|
|
];
|
|
return Db::transaction(function () use($order, $data){
|
|
$mark = $data['reason'];
|
|
if ($order['station_type'] == DeliverySevices::DELIVERY_TYPE_DADA) {
|
|
$options = DeliverySevices::create(DeliverySevices::DELIVERY_TYPE_DADA)->reasons();
|
|
$mark = $options[$data['reason']];
|
|
}
|
|
if ($data['cancel_reason']) $mark .= ','.$data['cancel_reason'];
|
|
$res = DeliverySevices::create($order['station_type'])->cancelOrder($data);
|
|
$deduct_fee = $res['deduct_fee'] ?? 0;
|
|
$this->cancelAfter($order, $deduct_fee, $mark);
|
|
//订单记录
|
|
$statusRepository = app()->make(StoreOrderStatusRepository::class);
|
|
$orderStatus = [
|
|
'order_id' => $order->order_id,
|
|
'order_sn' => $order->order_sn,
|
|
'type' => $statusRepository::TYPE_ORDER,
|
|
'change_message' => '同城配送订单已取消',
|
|
'change_type' => $statusRepository::ORDER_DELIVERY_CITY_CANCEL,
|
|
];
|
|
$statusRepository->createAdminLog($orderStatus);
|
|
});
|
|
}
|
|
|
|
public function cancelAfter($deliveryOrder, $deductFee, $mark)
|
|
{
|
|
//修改配送订单
|
|
$deliveryOrder->status = -1;
|
|
$deliveryOrder->reason = $mark;
|
|
$deliveryOrder->deduct_fee = $deductFee;
|
|
$deliveryOrder->save();
|
|
|
|
//修改商城订单
|
|
$res = app()->make(StoreOrderRepository::class)->get($deliveryOrder['order_id']);
|
|
$res->status = 0;
|
|
$res->delivery_type = 0;
|
|
$res->delivery_name = '';
|
|
$res->delivery_id = '';
|
|
$res->save();
|
|
|
|
//修改商户
|
|
$merchant = app()->make(MerchantRepository::class)->get($deliveryOrder['mer_id']);
|
|
$balance = bcadd(bcsub($deliveryOrder['fee'], $deductFee, 2), $merchant->delivery_balance, 2);
|
|
$merchant->delivery_balance = $balance;
|
|
$merchant->save();
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
* TODO 回调
|
|
* @param $data
|
|
* @author Qinii
|
|
* @day 2/17/22
|
|
*/
|
|
public function notify($data)
|
|
{
|
|
//达达
|
|
/**
|
|
* 订单状态(待接单=1,待取货=2,配送中=3,已完成=4,已取消=5, 指派单=8,妥投异常之物品返回中=9, 妥投异常之物品返回完成=10, 骑士到店=100,创建达达运单失败=1000 可参考文末的状态说明)
|
|
*/
|
|
Log::info('同城回调参数:'.var_export(['=======',$data,'======='],1));
|
|
if (isset($data['data'])) {
|
|
$data = json_decode($data['data'], 1);
|
|
}
|
|
|
|
$reason = '';
|
|
$deductFee = 0;
|
|
$delivery = [];
|
|
if (isset($data['order_status'])){
|
|
$order_sn = $data['order_id'];
|
|
if ($data['order_status'] == 1) {
|
|
$orderData = $this->dao->getSearch(['sn' => $data['order_id']])->find();
|
|
if (!$orderData['finish_code']) {
|
|
$orderData->finish_code = $data['finish_code'];
|
|
$orderData->save();
|
|
}
|
|
return ;
|
|
} else if (in_array( $data['order_status'],[2,3,4,5,9,10,100])){
|
|
$status = $data['order_status'];
|
|
if ($data['order_status'] == 5){
|
|
$msg = [
|
|
'取消:',
|
|
'达达配送员取消:',
|
|
'商家主动取消:',
|
|
'系统或客服取消:',
|
|
];
|
|
//1:达达配送员取消;2:商家主动取消;3:系统或客服取消;0:默认值
|
|
$status = -1;
|
|
$reason = $msg[$data['cancel_from']].$data['cancel_reason'];
|
|
}
|
|
$deductFee = $data['deductFee'] ?? 0;
|
|
if (isset($data['dm_name']) && $data['dm_name']) {
|
|
$delivery = [
|
|
'delivery_name' => $data['dm_name'],
|
|
'delivery_id' => $data['dm_mobile'],
|
|
];
|
|
}
|
|
|
|
}
|
|
} else if (isset($data['state'])){ //uu
|
|
if (!$data['origin_id']) $deliveryOrder = $this->dao->getWhere(['order_code' => $data['order_code']]);
|
|
$order_sn = $data['origin_id'] ?: $deliveryOrder['order_sn'] ;
|
|
//当前状态 1下单成功 3跑男抢单 4已到达 5已取件 6到达目的地 10收件人已收货 -1订单取消
|
|
switch ($data['state']) {
|
|
case 3:
|
|
$status = 2;
|
|
break;
|
|
case 4:
|
|
$status = 100;
|
|
break;
|
|
case 5:
|
|
$status = 3;
|
|
break;
|
|
case 10:
|
|
$status = 4;
|
|
break;
|
|
case -1:
|
|
$status = -1;
|
|
$reason = $data['state_text'];
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if (isset($data['driver_name']) && $data['driver_name']) {
|
|
$delivery = [
|
|
'delivery_name' => $data['driver_name'],
|
|
'delivery_id' => $data['driver_mobile'],
|
|
];
|
|
}
|
|
}
|
|
|
|
if (isset($order_sn) && isset($status)){
|
|
$res = $this->dao->getWhere(['order_sn' => $order_sn]);
|
|
if ($res) {
|
|
$this->notifyAfter($status, $reason, $res, $delivery, $deductFee);
|
|
}else {
|
|
Log::info('同城配送回调,未查询到订单:'.$order_sn);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
public function notifyAfter($status, $reason, $res, $data, $deductFee)
|
|
{
|
|
if (!isset($this->statusData[$status])) return ;
|
|
|
|
$make = app()->make(StoreOrderRepository::class);
|
|
$orderData = $make->get($res['order_id']);
|
|
|
|
if ($orderData['status'] != $status ) {
|
|
$res->status = $status;
|
|
$res->reason = $reason;
|
|
$res->save();
|
|
//订单记录
|
|
$statusRepository = app()->make(StoreOrderStatusRepository::class);
|
|
$message = '订单同城配送【'. $this->statusData[$status].'】';
|
|
$orderStatus = [
|
|
'order_id' => $orderData['order_id'],
|
|
'order_sn' => $orderData['order_sn'],
|
|
'type' => $statusRepository::TYPE_ORDER,
|
|
'change_message' => $message,
|
|
'change_type' => $this->message[$status],
|
|
];
|
|
$statusRepository->createSysLog($orderStatus);
|
|
if ($status == 2 && !empty($data))
|
|
$make->update($res['order_id'],$data);
|
|
if ($status == 4){
|
|
$order = $make->get($res['order_id']);
|
|
$user = app()->make(UserRepository::class)->get($order['uid']);
|
|
$make->update($res['order_id'],['status' => 2]);
|
|
$make->takeAfter($order, $user);
|
|
}
|
|
if ($status == -1)
|
|
$this->cancelAfter($res, $deductFee , $reason);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
public function create($id, $merId, $data, $order)
|
|
{
|
|
$type = systemConfig('delivery_type');
|
|
$callback_url = rtrim(systemConfig('site_url'), '/') . '/api/notice/callback';
|
|
$where = ['station_id' => $data['station_id'], 'mer_id' => $merId, 'status' => 1, 'type' => $type];
|
|
$station = app()->make(DeliveryStationRepository::class)->getWhere($where);
|
|
|
|
if (!$station) throw new ValidateException('门店信息不存在');
|
|
if (!$station['city_name']) throw new ValidateException('门店缺少所在城市,请重新编辑门店信息');
|
|
//地址转经纬度
|
|
try{
|
|
$addres = lbs_address($station['city_name'], $order['user_address']);
|
|
if($type == DeliverySevices::DELIVERY_TYPE_UU) {
|
|
[$location['lng'],$location['lat']] = gcj02ToBd09($addres['location']['lng'],$addres['location']['lat']);
|
|
} else {
|
|
$location = $addres['location'];
|
|
}
|
|
}catch (\Exception $e) {
|
|
throw new ValidateException('获取经纬度失败');
|
|
}
|
|
|
|
$getPriceParams = $this->getPriceParams($station, $order,$location,$type);
|
|
$orderSn = $this->getOrderSn();
|
|
$getPriceParams['origin_id'] = $orderSn;
|
|
$getPriceParams['callback_url'] = $callback_url;
|
|
$getPriceParams['cargo_weight'] = $data['cargo_weight'] ?? '';
|
|
|
|
$service = DeliverySevices::create($type);
|
|
//计算价格
|
|
$priceData = $service->getOrderPrice($getPriceParams);
|
|
if ($type == DeliverySevices::DELIVERY_TYPE_UU) { //uu
|
|
$priceData['receiver'] = $order['real_name'];
|
|
$priceData['receiver_phone'] = $order['user_phone'];
|
|
$priceData['note'] = $data['mark'];
|
|
$priceData['callback_url'] = $callback_url;
|
|
$priceData['push_type'] = 2;
|
|
$priceData['special_type'] = $data['special_type'] ?? 0;
|
|
}
|
|
app()->make(MerchantRepository::class)->changeDeliveryBalance($merId, $priceData['fee'] ?? $priceData['need_paymoney']);
|
|
//发布订单
|
|
Db::startTrans();
|
|
try{
|
|
$res = $service->addOrder($priceData);
|
|
$ret = [
|
|
'station_id' => $data['station_id'],
|
|
'order_sn' => $orderSn,
|
|
'city_code' => $station['city_name'],
|
|
'receiver_phone' => $order['user_phone'],
|
|
'user_name' => $order['real_name'],
|
|
'from_address' => $station['station_address'],
|
|
'to_address' => $order['user_address'],
|
|
'order_code' => $type == 2 ? $res['ordercode'] : $priceData['deliveryNo'],
|
|
'order_id' => $id,
|
|
'mer_id' => $merId,
|
|
'info' => $data['mark'],
|
|
'status' => $res['status'] ?? 0,
|
|
'station_type' => $type,
|
|
'to_lat' => $addres['location']['lat'],
|
|
'to_lng' => $addres['location']['lng'],
|
|
'from_lat' => $station['lat'],
|
|
'from_lng' => $station['lng'],
|
|
'distance' => $priceData['distance'],
|
|
'fee' => $priceData['fee'] ?? $priceData['need_paymoney'],
|
|
'mark' => $data['mark'],
|
|
'uid' => $order['uid'],
|
|
];
|
|
//入库操作
|
|
$this->dao->create($ret);
|
|
Db::commit();
|
|
return true;
|
|
}catch (\Exception $exception) {
|
|
if (isset($res['status']) && $res['status'] == 'success'){
|
|
$error['origin_id'] = $orderSn;
|
|
$error['reason'] = $type == 1 ? 36 : '信息错误';
|
|
$error['order_code'] = $type == 2 ? $res['ordercode'] : $priceData['deliveryNo'];
|
|
sleep(1);
|
|
$service->cancelOrder($error);
|
|
}
|
|
Db::rollback();
|
|
throw new ValidateException($exception->getMessage());
|
|
}
|
|
|
|
}
|
|
|
|
public function getPriceParams(DeliveryStation $deliveryStation, StoreOrder $order, array $addres, int $type)
|
|
{
|
|
$data = [];
|
|
$type = (int)$type;
|
|
switch ($type) {
|
|
case 1:
|
|
$city = DeliverySevices::create(DeliverySevices::DELIVERY_TYPE_DADA)->getCity([]);
|
|
$res = [];
|
|
foreach ($city as $item) {
|
|
$res[$item['label']] = $item['key'];
|
|
}
|
|
$data = [
|
|
'shop_no' => $deliveryStation['origin_shop_id'],
|
|
'city_code' => $res[$deliveryStation['city_name']],
|
|
'cargo_price' => $order['pay_price'],
|
|
'is_prepay' => 0,
|
|
'receiver_name' => $order['real_name'],
|
|
'receiver_address' => $order['user_address'],
|
|
'cargo_weight' => 0,
|
|
'receiver_phone' => $order['user_phone'],
|
|
'is_finish_code_needed' => 1,
|
|
];
|
|
break;
|
|
case 2:
|
|
$data = [
|
|
'from_address' => $deliveryStation['station_address'],
|
|
'to_address' => $order['user_address'],
|
|
'city_name' => $deliveryStation['city_name'],
|
|
'goods_type' => $deliveryStation['business']['label'],
|
|
'send_type' =>'0',
|
|
'to_lat' => $addres['lat'],
|
|
'to_lng' => $addres['lng'],
|
|
'from_lat' => $deliveryStation['lat'],
|
|
'from_lng' => $deliveryStation['lng'],
|
|
];
|
|
break;
|
|
}
|
|
return $data;
|
|
}
|
|
|
|
public function getTitle()
|
|
{
|
|
$query = app()->make(MerchantRepository::class)->getSearch(['is_del' => 0]);
|
|
$merchant = $query->count();
|
|
$price = app()->make(ServeOrderRepository::class)
|
|
->getSearch(['type' => 10,'status' => 1])->sum('pay_price');
|
|
$balance = $query->sum('delivery_balance');
|
|
return [
|
|
[
|
|
'className' => 'el-icon-s-order',
|
|
'count' => $merchant,
|
|
'field' => '个',
|
|
'name' => '商户数'
|
|
],
|
|
[
|
|
'className' => 'el-icon-s-order',
|
|
'count' => $price,
|
|
'field' => '元',
|
|
'name' => '商户充值总金额'
|
|
],
|
|
[
|
|
'className' => 'el-icon-s-order',
|
|
'count' => $balance,
|
|
'field' => '元',
|
|
'name' => '商户当前余额'
|
|
],
|
|
];
|
|
}
|
|
|
|
public function destory($id, $merId)
|
|
{
|
|
$where = [
|
|
$this->dao->getPk() => $id,
|
|
'mer_id' => $merId,
|
|
];
|
|
$res = $this->dao->getSearch($where)->find();
|
|
if (!$res) throw new ValidateException('订单不存在');
|
|
|
|
return $this->dao->delete($id);
|
|
}
|
|
|
|
/**
|
|
* TODO 订单SN
|
|
* @return string
|
|
* @author Qinii
|
|
* @day 2/17/22
|
|
*/
|
|
public function getOrderSn()
|
|
{
|
|
list($msec, $sec) = explode(' ', microtime());
|
|
$msectime = number_format((floatval($msec) + floatval($sec)) * 1000, 0, '', '');
|
|
$orderId = 'dc' . $msectime . random_int(10000, max(intval($msec * 10000) + 10000, 98369));
|
|
return $orderId;
|
|
}
|
|
|
|
public function show(int $id, int $uid)
|
|
{
|
|
$where['order_id'] = $id;
|
|
$where['uid'] = $uid;
|
|
$res = $this->dao->getSearch($where)->with(['storeOrderStatus','storeOrder'])->find();
|
|
if (!$res) throw new ValidateException('订单不存在');
|
|
return $res;
|
|
}
|
|
|
|
}
|
|
|