allocatr/application/services/OrderDispatchService.php
2025-06-08 11:54:14 +08:00

492 lines
17 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
namespace app\services;
use app\admin\model\Order;
use app\admin\model\OrderDispatch;
use app\api\library\ApiException;
use think\Db;
use think\Hook;
class OrderDispatchService extends BaseService
{
public function dispatchList(int $workerId, int $pageSize)
{
return $this->getOrderDispatchModel()
->with(['orderInfo' => function ($query) {
$query->with(
[
'area' => function ($query) {
$query->field('id,area_code,merge_name');
},
'coupon' => function ($query) {
$query->field('id,threshold,discount_value');
}
]
)->field('id,order_no,item_id,item_title,receive_type,address,lng,lat,plan_time,online_amount,discount_amount,area_id,status,coupon_id');
}])
->where('status', OrderDispatch::STATUS_TOGET)
->where('type', '<>', 1)
->where('worker_id', $workerId)
->field(['id', 'order_id', 'status', 'remark', 'create_time'])
->order('id desc')
->paginate($pageSize);
}
/**
* 工作台列表
* @param int $workerId 师傅id
* @param string $type 类型pending=待沟通,need_visit=待上门,ongoing=服务中,finished=已完成
* @param string $needVisitType 待上门类型today=今日,tomorrow=明日,all=全部
* @param int $pageSize
*/
public function workbenchOrderList(int $workerId, string $type, int $pageSize, string $needVisitType)
{
$model = $this->getWorkbenchOrderModel($workerId, $type, $needVisitType);
$result = $model
->field(['id', 'order_id', 'status', 'remark', 'create_time', 'plan_time'])
->order('id desc')
->paginate($pageSize);
return $result;
}
/**
* 统计工作台订单
* @param int $workerId
* @return array
*/
public function countWorkbenchOrder(int $workerId)
{
$needVisit = $this->getWorkbenchOrderModel($workerId, 'need_visit')->count();
return [
'pending' => $this->getWorkbenchOrderModel($workerId, 'pending')->count(),
'need_visit' => $needVisit,
'ongoing' => $this->getWorkbenchOrderModel($workerId, 'ongoing')->count(),
'finished' => $this->getWorkbenchOrderModel($workerId, 'finished')->count(),
'today' => $this->getWorkbenchOrderModel($workerId, 'need_visit', 'today')->count(),
'tomorrow' => $this->getWorkbenchOrderModel($workerId, 'need_visit', 'tomorrow')->count(),
'all' => $needVisit,
];
}
/**
* 获取工作台订单模型
* @param int $workerId
* @param string $type
* @param string $needVisitType
*/
private function getWorkbenchOrderModel(int $workerId, string $type, string $needVisitType = '')
{
$model = $this->getOrderDispatchModel()
->with(['orderInfo' => function ($query) {
$query->with([
'area' => function ($query) {
$query->field('id,area_code,merge_name');
},
'coupon' => function ($query) {
$query->field('id,threshold,discount_value');
}
])->field('id,order_no,item_id,item_title,receive_type,address,lng,lat,plan_time,online_amount,discount_amount,area_id,customer,tel,status,coupon_id');
}])
->where('type', '<>', 1)
->where('worker_id', $workerId);
switch ($type) {
case 'pending':
//待沟通
$model->where('status', OrderDispatch::STATUS_GOTIT);
break;
case 'need_visit':
//待上门
$model->where('status', OrderDispatch::STATUS_PLANIT);
switch ($needVisitType) {
case 'today':
$model->where('plan_time', '>=', date('Y-m-d 00:00:00'));
$model->where('plan_time', '<=', date('Y-m-d 23:59:59'));
break;
case 'tomorrow':
$model->where('plan_time', '>=', date('Y-m-d 00:00:00', strtotime('+1 day')));
$model->where('plan_time', '<=', date('Y-m-d 23:59:59', strtotime('+1 day')));
break;
}
break;
case 'ongoing':
//服务中
$model->where('status', OrderDispatch::STATUS_CLOCK);
break;
case 'finished':
//已完成
$model->where('status', OrderDispatch::STATUS_FINISH);
break;
}
return $model;
}
/**
* 师傅接单/拒接
* @param int $workerId 师傅id
* @param array $params 请求参数
* @return true
*/
public function orderConfirm(int $workerId, array $params)
{
$orderDispatchId = $params['order_dispatch_id'];
$type = $params['type'];
$orderDispatch = $this->getOrderDispatchModel()
->where('worker_id', $workerId)
->where('id', $orderDispatchId)
->find();
if (!$orderDispatch) {
$this->apiError('订单不存在');
}
if ($orderDispatch->status !== OrderDispatch::STATUS_TOGET) {
$this->apiError('该订单已被接单');
}
//接单或拒接
$orderDispatchStatus = $type == 'accept' ? OrderDispatch::STATUS_GOTIT : OrderDispatch::STATUS_REFUSED;
Db::startTrans();
try {
//接单
$orderDispatch->status = $orderDispatchStatus;
$orderDispatch->follow = 1;
$orderDispatch->got_time = date('Y-m-d H:i:s', time());
//拒接原因
if ($type == 'reject') {
if (empty($params['reject_reason'])) {
$this->apiError('请输入拒接原因');
}
$orderDispatch->reject_reason = $params['reject_reason'];
$orderDispatch->remark = $params['reject_reason'];
}
$orderDispatch->save();
$orderDispatch->admin_user = '师傅:'. $orderDispatch->worker_name;
$orderDispatchChangeParams = [
'dispatch' => $orderDispatch,
'remark' => $type == 'accept' ? '师傅接单' : '师傅拒接',
];
Hook::listen('order_dispatch_change', $orderDispatchChangeParams);
//拒接,更新订单状态
if ($type == 'reject') {
$order = $this->getOrderModel()->find($orderDispatch->order_id);
$order->status = Order::STATUS_DISPATCHING;
$order->save();
$orderChangeParams['order'] = $order;
$orderChangeParams['role'] = 2;
$orderChangeParams['auth'] = $this->getWorkerModel()->find($orderDispatch->worker_id);
$orderChangeParams['remark'] = "任务被师傅拒接[OrderDispatchId{$orderDispatch->id},原因为:{$params['reject_reason']},订单状态回退";
Hook::listen('order_change', $orderChangeParams);
}
Db::commit();
} catch (ApiException $e) {
Db::rollback();
$this->apiError($e->getMessage());
} catch (\Exception $e) {
Db::rollback();
$this->apiError('操作失败', $e, [
'msg' => '师傅接单或拒接操作失败',
'workerId' => $workerId,
'orderDispatchId' => $orderDispatchId,
'type' => $type,
]);
}
return true;
}
/**
* 派单详情
* @param int $workerId 师傅id
* @param int $orderDispatchId 派单id
*/
public function dispatchInfo(int $workerId, int $orderDispatchId)
{
$orderFields = [
'id',
'order_no',
'item_id',
'item_title',
'receive_type',
'address',
'lng',
'lat',
'plan_time',
'online_amount',
'discount_amount',
'area_id',
'customer',
'tel',
'remark',
'detail',
'images',
'status',
'coupon_id',
'is_material_used',
'material_cost',
'material_images',
];
$orderDispatchFields = [
'id',
'order_id',
'status',
'remark',
'worker_remark',
'create_time',
'total',
'online_total',
'is_receipt',
'plan_time',
'reject_reason',
'arrive_images',
'arrive_time',
'images',
'image',
'finish_time',
'offline_total_type',
'is_finish_today',
'estimated_finish_time',
'work_progress',
];
$res = $this->getOrderDispatchModel()
->with(['orderInfo' => function ($query) use ($orderFields) {
$query->with([
'area' => function ($query) {
$query->field('id,area_code,merge_name');
},
'coupon' => function ($query) {
$query->field('id,threshold,discount_value');
}
])->field($orderFields);
}])
->where('id', $orderDispatchId)
->where('worker_id', $workerId)
->field($orderDispatchFields)
->find();
if (!$res) {
$this->apiError('订单不存在');
}
return $res;
}
/**
* 提交预约上门时间
* @param int $workerId 师傅id
* @param int $orderDispatchId 派单id
* @param string $planTime 预约时间
* @return true
*/
public function appointmentTime(int $workerId, int $orderDispatchId, string $planTime)
{
$orderDispatch = $this->getOrderDispatchInfo($workerId, $orderDispatchId);
$orderDispatch->status = OrderDispatch::STATUS_PLANIT;
$orderDispatch->plan_time = $planTime;
$orderDispatch->follow = 1;
$orderDispatch->save();
$orderDispatch->admin_user = '师傅:'. $orderDispatch->worker_name;
$orderDispatchChangeParams = [
'dispatch' => $orderDispatch,
'remark' => '师傅已和客户预约,预约时间:' . $planTime,
];
Hook::listen('order_dispatch_change', $orderDispatchChangeParams);
return true;
}
public function updatePlanTime(int $workerId, int $orderDispatchId, string $planTime)
{
$orderDispatch = $this->getOrderDispatchInfo($workerId, $orderDispatchId);
if ($orderDispatch->status !== OrderDispatch::STATUS_PLANIT) {
$this->apiError('该单不支持修改上门时间');
}
$orderDispatch->plan_time = $planTime;
$orderDispatch->save();
$orderDispatch->admin_user = '师傅:'. $orderDispatch->worker_name;
$orderDispatchChangeParams = [
'dispatch' => $orderDispatch,
'remark' => "师傅修改上门时间,旧的时间:{$orderDispatch->plan_time},新的时间:$planTime"
];
Hook::listen('order_dispatch_change', $orderDispatchChangeParams);
return true;
}
/**
* 完成上门
* @param int $workerId 师傅id
* @param int $orderDispatchId 派单id
* @param string $images 上门图片
* @return true
*/
public function arrivedOnSite(int $workerId, int $orderDispatchId, string $images)
{
$time = datetime(time());
$orderDispatch = $this->getOrderDispatchInfo($workerId, $orderDispatchId);
$orderDispatch->status = OrderDispatch::STATUS_CLOCK;
$orderDispatch->arrive_images = $this->removeStrCdnUrl($images);
$orderDispatch->arrive_time = $time;
$orderDispatch->follow = 1;
$orderDispatch->save();
$orderDispatch->admin_user = '师傅:'. $orderDispatch->worker_name;
//派单状态变更
$orderDispatchChangeParams = [
'dispatch' => $orderDispatch,
'remark' => '师傅已上门,上门时间:' . $time,
];
Hook::listen('order_dispatch_change', $orderDispatchChangeParams);
return true;
}
/**
* 移除字符串中的 cdnUrl
* @param string $str
* @return string
*/
private function removeStrCdnUrl(string $str): string
{
$cdnUrl = cdnurl('', true);
return str_replace($cdnUrl, '', $str);
}
/**
* 获取订单信息
* @param int $workerId 师傅id
* @param int $orderDispatchId 派单id
*/
private function getOrderDispatchInfo(int $workerId, int $orderDispatchId) {
$res = $this->getOrderDispatchModel()
->where('id', $orderDispatchId)
->where('worker_id', $workerId)
->find();
if (!$res) {
$this->apiError('工单不存在');
}
return $res;
}
/**
* @param int $workerId 师傅id
* @param array $params 请求参数
* @return bool
*/
public function completeService(int $workerId, array $params)
{
Db::startTrans();
try {
$time = datetime(time());
$orderDispatch = $this->getOrderDispatchInfo($workerId, $params['order_dispatch_id']);
$orderDispatch->status = OrderDispatch::STATUS_FINISH;
$orderDispatch->images = $this->removeStrCdnUrl($params['complete_images']);
$orderDispatch->follow = 2;
$orderDispatch->finish_time = $time;
// 材料相关
$order = $this->getOrderModel()->where('id', $orderDispatch->order_id)->find();
// 是否使用材料
$order->is_material_used = $params['is_material_used'];
if ($order->is_material_used == 1) {
// 材料成本
$order->material_cost = $params['material_cost'];
// 材料凭证图片组
$order->material_images = $this->removeStrCdnUrl($params['material_images']);
$order->save();
}
if ($params['receive_type'] == 1) {
$orderDispatch->image = $this->removeStrCdnUrl($params['payment_image']);
$orderDispatch->offline_total_type = $params['offline_total_type'];
//线下尾款
if ($params['final_payment_method'] == 1) {
$orderDispatch->total = $params['amount'];
}
//线上尾款
if ($params['final_payment_method'] == 2) {
$orderDispatch->online_total = $params['amount'];
}
}
$orderDispatch->save();
$orderDispatch->admin_user = '师傅:'. $orderDispatch->worker_name;
//派单状态变更
$orderDispatchChangeParams = [
'dispatch' => $orderDispatch,
'remark' => '师傅已完成服务,完成时间:' . $time,
];
Hook::listen('order_dispatch_change', $orderDispatchChangeParams);
//修改订单状态
$orderDispatchInfo = OrderDispatch::get($params['order_dispatch_id']);
$roleInfo = ['role' => 2, 'auth' => $this->getWorkerModel()->find($workerId), 'remark' => '师傅完成服务'];
$this->getOrderLogic()->dispachFinishAfter($orderDispatchInfo, $roleInfo);
Db::commit();
} catch (\Exception $e) {
Db::rollback();
$this->apiError('操作失败', $e);
}
return true;
}
/**
* 保存师傅备注
* @param int $workerId 师傅id
* @param int $orderDispatchId 订单派单id
* @param string $workerRemark 备注信息
* @return true
*/
public function saveWorkerRemark(int $workerId, int $orderDispatchId, string $workerRemark)
{
$orderDispatch = $this->getOrderDispatchInfo($workerId, $orderDispatchId);
$orderDispatch->worker_remark = $workerRemark;
$orderDispatch->save();
return true;
}
public function updateProgress(int $workerId, array $params)
{
$orderDispatch = $this->getOrderDispatchInfo($workerId, $params['order_dispatch_id']);
$orderDispatch->is_finish_today = $params['is_finish_today'];
$orderDispatch->estimated_finish_time = $params['estimated_finish_time'];
$orderDispatch->follow = 1;
if ($params['is_finish_today'] == 0) {
$orderDispatch->work_progress = $params['work_progress'];
}
$orderDispatch->save();
return true;
}
}