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.
 
 
 
 
 
crmeb_php/app/common/repositories/delivery/DeliveryOrderRepository.php

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;
}
}