| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645 |
- <?php
- // +----------------------------------------------------------------------
- // | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
- // +----------------------------------------------------------------------
- // | Copyright (c) 2016~2024 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;
- }
- /**
- * 商户的同城配送订单列表
- * @param array $where
- * @param int $page
- * @param int $limit
- * @return array
- * @author Qinii
- */
- 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');
- }
- /**
- * 平台同城配送订单列表
- * @param array $where
- * @param int $page
- * @param int $limit
- * @return array
- * @author Qinii
- */
- 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');
- }
- /**
- * 需要配置的订单详情
- * @param int $id
- * @param int|null $merId
- * @return \app\common\model\BaseModel|array|mixed|\think\Model
- * @throws \think\db\exception\DataNotFoundException
- * @throws \think\db\exception\DbException
- * @throws \think\db\exception\ModelNotFoundException
- * @author wuhaotian
- * @email 442384644@qq.com
- * @date 2024/7/5
- */
- 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();
- // 先检查结果是否存在
- if (!$res) throw new ValidateException('订单不存在');
-
- // 然后再处理订单详情
- try {
- $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',
- ];
- } catch (\Exception $e) {
- // 记录异常并返回友好提示
- Log::error('获取配送订单详情失败:' . $e->getMessage());
- throw new ValidateException('获取订单详情失败,请稍后再试');
- }
- return $res;
- }
- /**
- * 取消配送订单
- * @param $id
- * @return \FormBuilder\Form
- * @author Qinii
- */
- 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);
- }
- /**
- * 取消表单
- * @param $id
- * @param $merId
- * @param $reason
- * @return mixed
- * @author Qinii
- */
- 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);
- });
- }
- /**
- * 订单取消后操作
- * @param $deliveryOrder
- * @param $deductFee
- * @param $mark
- * @return void
- * @author Qinii
- */
- 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();
- }
- /**
- * 同城配送订单信息回调
- * @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);
- }
- }
- }
- /**
- * 同城配送回调
- * @param $status
- * @param $reason
- * @param $res
- * @param $data
- * @param $deductFee
- * @throws \think\db\exception\DataNotFoundException
- * @throws \think\db\exception\DbException
- * @throws \think\db\exception\ModelNotFoundException
- * @author wuhaotian
- * @email 442384644@qq.com
- * @date 2024/7/5
- */
- 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);
- }
- }
- /**
- * 创建配送订单
- * @param $id
- * @param $merId
- * @param $data
- * @param $order
- * @return bool
- * @author wuhaotian
- * @email 442384644@qq.com
- * @date 2024/7/5
- */
- 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());
- }
- }
- /**
- * 获取订单价格参数
- * @param DeliveryStation $deliveryStation
- * @param StoreOrder $order
- * @param array $addres
- * @param int $type
- * @return array
- * @author wuhaotian
- * @email 442384644@qq.com
- * @date 2024/7/5
- */
- 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' => '商户当前余额'
- ],
- ];
- }
- /**
- * 销毁订单。
- * 本函数用于根据给定的订单ID和商家ID删除订单。首先,它会验证订单是否存在,如果不存在,则抛出一个异常;
- * 如果存在,则执行删除操作。
- *
- * @param int $id 订单ID。这是主键,用于唯一标识一个订单。
- * @param int $merId 商家ID。用于确定订单所属的商家。
- * @return bool 返回删除操作的结果。如果删除成功,返回true;否则,返回false。
- * @throws ValidateException 如果订单不存在,则抛出此异常。
- */
- public function destory($id, $merId)
- {
- // 根据订单ID和商家ID构建查询条件
- $where = [
- $this->dao->getPk() => $id, // 使用主键作为查询条件之一
- 'mer_id' => $merId, // 商家ID作为查询条件之一
- ];
- // 根据查询条件查找订单
- $res = $this->dao->getSearch($where)->find();
- // 如果查询结果为空,即订单不存在,则抛出异常
- if (!$res) throw new ValidateException('订单不存在');
- // 删除订单,并返回删除结果
- return $this->dao->delete($id);
- }
- /**
- * 生成订单编号
- *
- * 本函数用于生成唯一的订单编号。编号由时间戳和随机数组成,确保了编号的唯一性和可追踪性。
- * 时间戳部分精确到毫秒,增加了编号的唯一性,随机数部分进一步确保了即使在毫秒级别内有多个订单生成,也能得到不同的编号。
- *
- * @return string 返回生成的订单编号
- */
- public function getOrderSn()
- {
- // 获取当前时间的微秒和秒部分
- list($msec, $sec) = explode(' ', microtime());
- // 将微秒和秒转换为毫秒,并格式化为整数,用于订单编号的时间戳部分
- $msectime = number_format((floatval($msec) + floatval($sec)) * 1000, 0, '', '');
- // 生成订单编号:前缀 + 毫秒时间戳 + 随机数
- // 使用'dc'作为前缀,表示订单编号的来源或类型
- // 随机数生成范围确保大于毫秒时间戳转换的整数,避免重复,并且预设了一个最小值98369,以保持编号的一定长度和特征
- $orderId = 'dc' . $msectime . random_int(10000, max(intval($msec * 10000) + 10000, 98369));
- return $orderId;
- }
- /**
- * 根据订单ID和用户ID查询订单信息
- *
- * 本函数用于通过给定的订单ID和用户ID,从数据库中检索相应的订单信息。
- * 如果找不到匹配的订单,将抛出一个验证异常,指出订单不存在。
- * 这是对前端接口的封装,确保了只有合法的订单才能被查询和访问。
- *
- * @param int $id 订单ID
- * @param int $uid 用户ID
- * @return array 查询到的订单信息
- * @throws ValidateException 如果订单不存在则抛出异常
- */
- 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;
- }
- }
|