123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682 |
- <?php
- // +----------------------------------------------------------------------
- // | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
- // +----------------------------------------------------------------------
- // | Copyright (c) 2016~2020 https://www.crmeb.com All rights reserved.
- // +----------------------------------------------------------------------
- // | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
- // +----------------------------------------------------------------------
- // | Author: CRMEB Team <admin@crmeb.com>
- // +----------------------------------------------------------------------
- namespace app\services\order;
- use app\dao\order\StoreDeliveryOrderDao;
- use app\jobs\order\OrderTakeJob;
- use app\jobs\store\StoreFinanceJob;
- use app\services\BaseServices;
- use app\services\other\CityAreaServices;
- use app\services\store\SystemStoreServices;
- use app\services\supplier\SystemSupplierServices;
- use crmeb\services\DeliverySevices;
- use crmeb\services\FormBuilder as Form;
- use crmeb\services\SystemConfigService;
- use crmeb\traits\OptionTrait;
- use think\exception\ValidateException;
- use think\facade\Log;
- use think\facade\Route as Url;
- /**
- * 发货单
- * Class StoreDeliveryOrderServices
- * @package app\services\order
- * @mixin StoreDeliveryOrderDao
- */
- class StoreDeliveryOrderServices extends BaseServices
- {
- use OptionTrait;
- protected $statusData = [
- 2 => '待取货',
- 3 => '配送中',
- 4 => '已完成',
- -1 => '已取消',
- 9 => '物品返回中',
- 10 => '物品返回完成',
- 100 => '骑士到店',
- ];
- /**
- * 平台达达门店
- * @var string
- */
- public $platCityShopId = 'plat_delivery_city_shop_001';
- /**
- * 构造方法
- * StoreDeliveryOrderServices constructor.
- * @param StoreDeliveryOrderDao $dao
- */
- public function __construct(StoreDeliveryOrderDao $dao)
- {
- $this->dao = $dao;
- }
- /**
- * 配送信息
- * @return string[]
- */
- public function getStatusMsg()
- {
- return $this->statusData;
- }
- /**
- * @param array $where
- * @return array
- * @throws \think\db\exception\DataNotFoundException
- * @throws \think\db\exception\DbException
- * @throws \think\db\exception\ModelNotFoundException
- */
- public function systemPage(array $where)
- {
- [$page, $limit] = $this->getPageValue();
- $list = $this->dao->getList($where, '*', $page, $limit, ['orderInfo', 'storeInfo']);
- $count = $this->dao->count($where);
- if ($list) {
- foreach ($list as &$item) {
- $item['add_time'] = $item['add_time'] ? date('Y-m-d H:i:s') : '';
- $item['distance'] = $item['distance'] ? bcdiv($item['distance'], '1000', 3) : 0;
- }
- }
- return compact('count', 'list');
- }
- /**
- * 生成订单号
- * @return string
- * @throws \Exception
- */
- public function getOrderSn(string $key = '')
- {
- [$msec, $sec] = explode(' ', microtime());
- $msectime = number_format((floatval($msec) + floatval($sec)) * 1000, 0, '', '');
- $orderId = $key . $msectime . random_int(10000, max(intval($msec * 10000) + 10000, 98369));
- return $orderId;
- }
- /**
- * 地址转经纬度
- * @param $region
- * @param $address
- * @return mixed|null
- */
- public function lbs_address($region, $address)
- {
- $key = sys_config('tengxun_map_key', '');
- if (!$key) {
- throw new ValidateException('请先配置地图KEY');
- }
- $locationOption = new \Joypack\Tencent\Map\Bundle\AddressOption($key);
- $locationOption->setAddress($address);
- $locationOption->setRegion($region);
- $location = new \Joypack\Tencent\Map\Bundle\Address($locationOption);
- $res = $location->request();
- if ($res->error) {
- throw new ValidateException($res->error);
- }
- if ($res->status) {
- throw new ValidateException($res->message);
- }
- if (!$res->result) {
- throw new ValidateException('获取失败');
- }
- return $res->result;
- }
- /**
- * 处理订单数据
- * @param array $station
- * @param array $order
- * @param int $type
- * @return array
- */
- public function getPriceParams(array $station, array $order, int $type)
- {
- $data = [];
- $type = (int)$type;
- switch ($type) {
- case 1:
- $city = DeliverySevices::init(DeliverySevices::DELIVERY_TYPE_DADA)->getCity([]);
- $res = [];
- foreach ($city as $item) {
- $res[$item['label']] = $item['key'];
- }
- //达达城市数据:西安
- $city_name = str_replace(['市','自治州','地区','区划','县'], '',$station['city_name'] ?? '');
- $data = [
- 'shop_no' => $station['origin_shop_id'],
- 'city_code' => $res[$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://uu城市数据:西安市
- $business = DeliverySevices::init(DeliverySevices::DELIVERY_TYPE_UU)->getBusiness();
- $business = array_combine(array_column($business, 'key'), $business);
- $data = [
- 'from_address' => $station['station_address'],
- 'to_address' => $order['user_address'],
- 'city_name' => $station['city_name'] ?? '西安',
- 'goods_type' => $business[$station['business'] ?? 1]['label'],
- 'send_type' =>'0',
- 'to_lat' => $order['latitude'],
- 'to_lng' => $order['longitude'],
- 'from_lat' => $station['lat'],
- 'from_lng' => $station['lng'],
- ];
- break;
- }
- return $data;
- }
- /**
- * 创建配送单
- * @param int $id
- * @param array $data
- * @param int $type
- * @param $order
- * @return bool
- * @throws \think\db\exception\DataNotFoundException
- * @throws \think\db\exception\DbException
- * @throws \think\db\exception\ModelNotFoundException
- */
- public function create(int $id, array $data, int $type = 1, $order = [])
- {
- if (!$order) {
- /** @var StoreOrderServices $orderServices */
- $orderServices = app()->make(StoreOrderServices::class);
- $order = $orderServices->get($id);
- }
- if (!$order) {
- throw new ValidateException('订单信息获取失败');
- }
- if (isset($order['user_address']) && !$order['user_address']) {
- throw new ValidateException('自提订单不支持同城配送');
- }
- $order = is_object($order) ? $order->toArray() : $order;
- //处理地址定位
- if (isset($order['user_location']) && $order['user_location']) {
- [$longitude, $latitude] = explode(' ', $order['user_location']);
- $order['longitude'] = $longitude;
- $order['latitude'] = $latitude;
- if (!$order['longitude'] || !$order['latitude']) {
- $addressArr = $this->addressHandle($order['user_address']);
- $city_name = $addressArr['city'] ?? '';
- try {
- $addres = $this->lbs_address($city_name, $order['user_address']);
- $order['latitude'] = $addres['location']['lat'] ?? '';
- $order['longitude'] = $addres['location']['lng'] ?? '';
- } catch (\Exception $e) {
- throw new ValidateException('获取经纬度失败');
- }
- }
- }
- if ($order['store_id']) {//门店
- $resType = 1;
- $relationId = (int)$order['store_id'];
- } elseif ($order['supplier_id']) {//供应商
- $resType = 2;
- $relationId = (int)$order['supplier_id'];
- } else {//平台信息
- $resType = 0;
- $relationId = 0;
- }
- //获取发货信息
- $station = $this->syncCityShop($relationId, $resType, $type);
- if (!$station || !isset($station['lat']) || !$station['lat'] || !isset($station['lng']) || !$station['lng']) {
- throw new ValidateException('获取发货信息失败');
- }
- $getPriceParams = $this->getPriceParams($station, $order, $type);
- $orderSn = $this->getOrderSn($type == 1 ? 'dd' : 'uu');
- $getPriceParams['origin_id'] = $orderSn;
- $getPriceParams['cargo_weight'] = $data['cargo_weight'] ?? '';
- $service = DeliverySevices::init($type);
- try {
- //计算价格
- $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['delivery_remark'];
- $priceData['push_type'] = 2;
- $priceData['special_type'] = $data['special_type'] ?? 0;
- }
- $res = $service->addOrder($priceData);
- $ret = [
- 'type' => $resType,
- 'relation_id' => $relationId,
- 'oid' => $id,
- 'order_id' => $orderSn,
- 'delivery_no' => $type == 2 ? $res['ordercode'] : $priceData['deliveryNo'] ?? '',
- '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'],
- 'info' => $data['delivery_remark'],
- 'status' => $res['status'] ?? 0,
- 'station_type' => $type,
- 'to_lat' => $order['latitude'],
- 'to_lng' => $order['longitude'],
- 'from_lat' => $station['lat'] ?? '',
- 'from_lng' => $station['lng'] ?? '',
- 'distance' => $priceData['distance'],
- 'fee' => $priceData['fee'] ?? $priceData['need_paymoney'] ?? 0,
- 'mark' => $data['delivery_remark'],
- 'uid' => $order['uid'],
- 'add_time' => time()
- ];
- //入库操作
- $this->dao->save($ret);
- //记录门店流水
- if ($resType == 1) {
- StoreFinanceJob::dispatch([$ret, 6, $ret['fee']]);
- }
- return true;
- } catch (\Throwable $e) {
- if (isset($res['status']) && $res['status'] == 'success'){
- $error['origin_id'] = $orderSn;
- $error['reason'] = $type == 1 ? 36 : '信息错误';
- $error['delivery_no'] = $type == 2 ? $res['ordercode'] : $priceData['deliveryNo'];
- $service->cancelOrder($error);
- }
- throw new ValidateException($e->getMessage());
- }
- }
- /**
- * 配送订单详情
- * @param int $id
- * @return array|\think\Model
- * @throws \think\db\exception\DataNotFoundException
- * @throws \think\db\exception\DbException
- * @throws \think\db\exception\ModelNotFoundException
- */
- public function detail(int $id)
- {
- $res = $this->dao->get($id, ['orderInfo']);
- if (!$res) {
- throw new ValidateException('配送订单不存在,或已取消');
- }
- $res = $res->toArray();
- $order = DeliverySevices::init((int)$res['station_type'])->getOrderDetail($res);
- if (!$res) throw new ValidateException('订单不存在');
- $res['data'] = [
- 'order_id' => $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',
- ];
- return $res;
- }
- /**
- * 删除
- * @param int $id
- * @return mixed
- */
- public function delete(int $id)
- {
- $res = $this->dao->get($id);
- if (!$res) throw new ValidateException('订单不存在');
- return $this->dao->delete($id);
- }
- /**
- * @param $id
- * @return mixed
- * @throws \think\db\exception\DataNotFoundException
- * @throws \think\db\exception\DbException
- * @throws \think\db\exception\ModelNotFoundException
- */
- public function cancelForm($id)
- {
- $order = $this->dao->get($id);
- if (!$order) throw new ValidateException('订单不存在');
- if ($order['status'] == -1) throw new ValidateException('订单已取消,无法操作');
- $field = [];
- if ($order['station_type'] == 1) {
- $options = DeliverySevices::init(1)->reasons();
- $field[] = Form::select('reason', '取消原因')->setOptions(Form::setOptions($options))->filterable(1)->col(12);
- $field[] = Form::input('cancel_reason', '其他原因说明')->required('请输入原因');
- } else {
- $field[] = Form::input('reason', '取消原因')->required('请输入原因');
- }
- return create_form('取消同城配送订单', $field, Url::buildUrl('/order/delivery_order/cancel/'. $id), 'POST');
- }
- /**
- * 取消订单
- * @param int $id
- * @param array $reason
- * @return mixed
- * @throws \think\db\exception\DataNotFoundException
- * @throws \think\db\exception\DbException
- * @throws \think\db\exception\ModelNotFoundException
- */
- public function cancel(int $id, array $reason)
- {
- $order = $this->dao->get($id);
- if (!$order) throw new ValidateException('配送订单不存在');
- if ($order['status'] == -1) throw new ValidateException('请勿重复操作');
- $data = [
- 'origin_id' => $order['order_id'],
- 'order_code'=> $order['delivery_no'],
- 'reason' => $reason['reason'],
- 'cancel_reason' => $reason['cancel_reason'],
- ];
- $this->transaction(function () use($id, $order, $data){
- $mark = $data['reason'];
- $delivery = DeliverySevices::init((int)$order['station_type']);
- if ($order['station_type'] == DeliverySevices::DELIVERY_TYPE_DADA) {
- $options = $delivery->reasons();
- if ($options) $options = array_combine(array_column($options, 'value'), $options);
- $mark = $options[$data['reason']]['label'] ?? '';
- }
- if ($data['cancel_reason']) $mark .= ','.$data['cancel_reason'];
- $res = $delivery->cancelOrder($data);
- $deduct_fee = $res['deduct_fee'] ?? 0;
- $this->doCancel($id, $order, $deduct_fee, $mark);
- });
- return true;
- }
- /**
- * 执行取消订单
- * @param int $id
- * @param $deliveryOrder
- * @param $deduct_fee
- * @param $mark
- * @return bool
- */
- public function doCancel(int $id , $deliveryOrder, $deduct_fee, $mark)
- {
- //修改配送单
- $this->dao->update($id, ['status' => 1, 'mark' => $mark, 'deduct_fee' => $deduct_fee]);
- //修改愿订单
- /** @var StoreOrderServices $storeOrderServices */
- $storeOrderServices = app()->make(StoreOrderServices::class);
- $storeOrderServices->update($deliveryOrder['oid'], ['status' => 0, 'delivery_type' => '', 'delivery_name' => '', 'delivery_id' => '', 'delivery_uid' => '']);
- /** @var StoreOrderStatusServices $statusServices */
- $statusServices = app()->make(StoreOrderStatusServices::class);
- $statusServices->save([
- 'oid' => $deliveryOrder['oid'],
- 'change_type' => 'city_delivery_cancel',
- 'change_message' => '同城配送取消',
- 'change_time' => time()
- ]);
- //记录门店流水
- if ($deliveryOrder['type'] == 1) {
- StoreFinanceJob::dispatch([$deliveryOrder, 7, $deliveryOrder['fee']]);
- }
- return true;
- }
- /**
- * 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_id = $data['order_id'];
- if ($data['order_status'] == 1) {
- $orderData = $this->dao->getOne(['order_id' => $data['order_id']]);
- 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->getOne(['delivery_no' => $data['order_code']]);
- $order_id = $data['origin_id'] ?: $deliveryOrder['order_id'] ;
- //当前状态 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_id) && isset($status)){
- $deliveryOrder = $this->dao->getOne(['order_id' => $order_id]);
- if ($deliveryOrder) {
- $this->notifyAfter($status, $reason, $deliveryOrder, $delivery, $deductFee);
- }else {
- Log::info('同城配送回调,未查询到订单:'.$order_id);
- }
- }
- }
- /**
- * @param $status
- * @param $reason
- * @param $res
- * @param $data
- * @param $deductFee
- * @return bool
- */
- public function notifyAfter($status, $reason, $deliveryOrder, $data, $deductFee)
- {
- if (!isset($this->statusData[$status])) return true;
- /** @var StoreOrderServices $orderServices */
- $orderServices = app()->make(StoreOrderServices::class);
- $orderData = $orderServices->get($deliveryOrder['oid']);
- /** @var StoreOrderStatusServices $statusServices */
- $statusServices = app()->make(StoreOrderStatusServices::class);
- $change_type = 'city_delivery_' . $status;
- $oid = (int)$deliveryOrder['oid'];
- if ($orderData['status'] != $status && !$statusServices->count(['oid' => $oid, 'change_type' => $change_type])) {
- $order = $orderServices->get($oid);
- //修改配送单
- $this->dao->update($deliveryOrder['id'], ['status' => $status, 'reason' => $reason]);
- //增加
- $message = '订单已配送【'. $this->statusData[$status].'】';
- $statusServices->save([
- 'oid' => $oid,
- 'change_type' => $change_type,
- 'change_message' => $message,
- 'change_time' => time()
- ]);
- switch ($status) {
- case 2:
- if (!empty($data)) $orderServices->update($oid, $data);
- break;
- case 4:
- //订单收货
- if ($order['status'] != 2) {
- OrderTakeJob::dispatch([$order]);
- }
- break;
- case -1:
- $this->doCancel((int)$deliveryOrder['id'], $deliveryOrder, $deductFee , $reason);
- break;
- }
- }
- return true;
- }
- /**
- * 同步达达门店信息
- * @param int $id
- * @param bool $is_new
- * @param int $type
- * @param int $station_type
- * @return array
- * @throws \think\db\exception\DataNotFoundException
- * @throws \think\db\exception\DbException
- * @throws \think\db\exception\ModelNotFoundException
- */
- public function syncCityShop(int $id, int $type = 1, int $station_type = 1)
- {
- $data = [];
- if ($station_type == 1) {
- $status = sys_config('dada_delivery_status');
- } else {
- $status = sys_config('uu_delivery_status');
- }
- if (!$status) {//未开启
- return $data;
- }
- if ($type == 1) {//门店
- /** @var SystemStoreServices $storeServices */
- $storeServices = app()->make(SystemStoreServices::class);
- $station = $storeServices->getStoreInfo($id);
- /** @var CityAreaServices $cityArea */
- $cityArea = app()->make(CityAreaServices::class);
- $station['city_name'] = $cityArea->value(['id' => $station['city']], 'name');
- $station['address'] = $station['detailed_address'];
- if (!$station['city_shop_id']) {
- $station['city_shop_id'] = 'deliver_store_' . $station['id'] . '_'. $this->getOrderSn();
- $storeServices->update($id, ['city_shop_id' => $station['city_shop_id']]);
- }
- } elseif ($type == 2){//供应商
- /** @var SystemSupplierServices $services */
- $services = app()->make(SystemSupplierServices::class);
- $station = $services->getSupplierInfo($id);
- if ($station) {
- $station['name'] = $station['supplier_name'] ?? '';
- }
- } else {//平台
- $station = SystemConfigService::more(['refund_address', 'refund_name', 'refund_phone']);
- $address = $station['refund_address'];
- if ($address) {
- $station['address'] = $station['refund_address'];
- $station['name'] = $station['refund_name'];
- $station['phone'] = $station['refund_phone'];
- $station['city_shop_id'] = $this->platCityShopId;
- }
- }
- if (!isset($station['latitude'])) {//地址转经纬度
- $addressArr = $this->addressHandle($station['address']);
- if (!$addressArr['province'] || !$addressArr['city']) {
- throw new ValidateException($type == 0 ? '请检查(设置->商城设置->交易设置->退货收货人地址)完整性' : '请检查该订单关联供应商地址信息是否正确');
- }
- $station['city_name'] = $addressArr['city'] ?? '';
- try {
- $addres = $this->lbs_address($station['city_name'], $station['address']);
- $station['latitude'] = $addres['location']['lat'] ?? '';
- $station['longitude'] = $addres['location']['lng'] ?? '';
- } catch (\Exception $e) {
- throw new ValidateException('获取经纬度失败');
- }
- }
- $data = [
- 'lng' => (float)($station['longitude'] ?? 0),
- 'lat' => (float)($station['latitude'] ?? 0),
- 'phone' => $station['phone'] ?? '',
- 'business' => (int)($station['business'] ?? 5),
- 'contact_name' => $station['name'] ?? '',
- 'station_name' => $station['name'],
- 'station_address' => $station['address'] ?? '',
- 'status' => 1,
- 'origin_shop_id' => $station['city_shop_id'],
- ];
- if ($data) {
- $serve = DeliverySevices::init($station_type);
- try {
- $shop = $serve->getShopDetail($data['origin_shop_id']);
- } catch (\Throwable $e) {
- $shop = [];
- }
- try {
- if (!$shop) {
- $serve->addShop($data);
- } else {
- $serve->updateShop($data);
- }
- } catch (\Throwable $e) {
- throw new ValidateException('创建达达门店失败,原因:' . $e->getMessage());
- }
- }
- $data['city_name'] = $station['city_name'];
- return $data;
- }
- }
|