zx/app/common/repositories/delivery/DeliveryOrderRepository.php

507 lines
20 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?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', '取消原因:')->placeholder('请选择取消原因')->options($options);
$rule[] = Elm::text('cancel_reason', '其他原因说明:')->placeholder('请输入其它原因说明');
}
if ($formData['station_type'] == DeliverySevices::DELIVERY_TYPE_UU) {
$rule[] = Elm::input('reason', '取消原因:')->placeholder('请输入取消原因')->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'] ?? 0;
$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;
}
}