Prechádzať zdrojové kódy

Merge branch 'worker_0609'

dongxiaoqin 10 mesiacov pred
rodič
commit
bf16dea898

+ 13 - 1
app/adminapi/controller/ConfigController.php

@@ -56,6 +56,18 @@ class ConfigController extends BaseAdminController
         return $this->data($data);
     }
 
-
+    /**
+     * @notes 根据字段类型获取字典配置数据
+     * @return \think\response\Json
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function dictConfig()
+    {
+        $value = $this->request->get('value', '');
+        $data = ConfigLogic::getDictConfig($value);
+        return $this->data($data);
+    }
 
 }

+ 34 - 0
app/adminapi/controller/master_worker/MasterWorkerController.php

@@ -20,8 +20,10 @@ use app\adminapi\controller\BaseAdminController;
 use app\adminapi\lists\master_worker\MasterWorkerLists;
 use app\adminapi\lists\master_worker\MoneyAnalysis;
 use app\adminapi\lists\master_worker\MasterWorkerOnlineLists;
+use app\adminapi\lists\master_worker\MasterWorkerStopLists;
 use app\adminapi\logic\master_worker\MasterWorkerLogic;
 use app\adminapi\validate\master_worker\MasterWorkerValidate;
+use app\adminapi\lists\master_worker\MasterWorkerScheduleLists;
 
 
 /**
@@ -142,4 +144,36 @@ class MasterWorkerController extends BaseAdminController
         $params = (new MasterWorkerValidate())->get();
         return $this->data(MasterWorkerLogic::getAllWorkers($params));
     }
+
+    /**
+     * @notes 长期合作工程师停单
+     * @return \think\response\Json
+     */
+    public function stop()
+    {
+        $params = (new MasterWorkerValidate())->post()->goCheck('stop');
+        $result = MasterWorkerLogic::stop($params,$this->adminInfo);
+        if (true === $result) {
+            return $this->success('停单成功', [], 1, 1);
+        }
+        return $this->fail(MasterWorkerLogic::getError());
+    }
+
+    /**
+     * @notes 长期合作工程师停单记录列表
+     * @return \think\response\Json
+     */
+    public function stopLists()
+    {
+        return $this->dataLists(new MasterWorkerStopLists());
+    }
+
+    /**
+     * @notes 长期合作工程师排班列表
+     * @return \think\response\Json
+     */
+    public function scheduleLists()
+    {
+        return $this->dataLists(new MasterWorkerScheduleLists());
+    }
 }

+ 1 - 1
app/adminapi/controller/works/ServiceWorkController.php

@@ -327,6 +327,7 @@ class ServiceWorkController extends BaseAdminController
         }
         return $this->success('操作成功', [], 1, 1);
     }
+
     public function appointmentLists()
     {
         return $this->dataLists(new ServiceWorkAppointmentLists());
@@ -336,7 +337,6 @@ class ServiceWorkController extends BaseAdminController
     {
         $params = (new ServiceWorkValidate())->post()->goCheck('detail');
         $result = ServiceWorkLogic::appointmentAudit($params);
-
         if (false === $result) {
             return $this->fail(ServiceWorkLogic::getError());
         }

+ 2 - 1
app/adminapi/lists/master_worker/MasterWorkerLists.php

@@ -49,7 +49,7 @@ class MasterWorkerLists extends BaseAdminDataLists implements ListsSearchInterfa
     {
         // 派单搜索条件 - 工程师接单状态
         return [
-            '=' => ['mw.sn', 'mw.real_name', 'mw.city', 'mw.account', 'mw.password', 'mw.mobile', 'mw.sex', 'mw.channel', 'mw.is_disable', 'mw.is_new_user', 'mw.create_time', 'mw.update_time', 'mw.accept_order_status', 'mw.cooperation','mw.audit_state', 'mw.work_status'],
+            '=' => ['mw.sn', 'mw.real_name', 'mw.city', 'mw.account', 'mw.password', 'mw.mobile', 'mw.sex', 'mw.channel', 'mw.is_disable', 'mw.is_new_user', 'mw.create_time', 'mw.update_time', 'mw.accept_order_status', 'mw.cooperation','mw.audit_state', 'mw.work_status',"mw.type"],
             //'in' => ['mw.time_period']
         ];
     }
@@ -179,6 +179,7 @@ class MasterWorkerLists extends BaseAdminDataLists implements ListsSearchInterfa
             ->whereRaw($distanceWhereSql)
             ->field($fields)
             ->limit($this->limitOffset, $this->limitLength)
+            ->order("mw.type","desc")
             ->orderRaw('(mws.comprehensive_score + mws.weight_score) desc')
             ->order($orders)
             ->select()->toArray();

+ 3 - 2
app/adminapi/lists/master_worker/MasterWorkerOnlineLists.php

@@ -42,7 +42,7 @@ class MasterWorkerOnlineLists extends BaseAdminDataLists implements ListsSearchI
     {
         // 派单搜索条件 - 工程师接单状态
         return [
-            '=' => ['mw.real_name', 'mw.mobile', 'mw.accept_order_status'],
+            '=' => ['mw.real_name', 'mw.mobile', 'mw.accept_order_status','mw.type'],
             '%like%' => ['sa.service_name'],
         ];
     }
@@ -82,7 +82,7 @@ class MasterWorkerOnlineLists extends BaseAdminDataLists implements ListsSearchI
      */
     public function lists(): array
     {
-        $fields = ['mw.id,mw.avatar,mw.real_name,mw.mobile,mw.work_total,mw.distance,mw.accept_order_status,mw.address,mw.service_area_id,mws.comprehensive_score, mws.weight_score,sa.service_name,mwr.credential_images'];
+        $fields = ['mw.id,mw.avatar,mw.real_name,mw.mobile,mw.work_total,mw.distance,mw.accept_order_status,mw.address,mw.service_area_id,mw.type,mws.comprehensive_score, mws.weight_score,sa.service_name,mwr.credential_images'];
         $orders = ['mw.id' => 'desc'];
         $queryWhere = $this->queryWhere();
         // 根据位置排序
@@ -105,6 +105,7 @@ class MasterWorkerOnlineLists extends BaseAdminDataLists implements ListsSearchI
             ->whereRaw($distanceWhereSql)
             ->field($fields)
             ->limit($this->limitOffset, $this->limitLength)
+            ->order("mw.type","desc")
             ->order($orders)
             ->select()->toArray();
         foreach($list as &$item){

+ 88 - 0
app/adminapi/lists/master_worker/MasterWorkerScheduleLists.php

@@ -0,0 +1,88 @@
+<?php
+// +----------------------------------------------------------------------
+// | likeadmin快速开发前后端分离管理后台(PHP版)
+// +----------------------------------------------------------------------
+// | 欢迎阅读学习系统程序代码,建议反馈是我们前进的动力
+// | 开源版本可自由商用,可去除界面版权logo
+// | gitee下载:https://gitee.com/likeshop_gitee/likeadmin
+// | github下载:https://github.com/likeshop-github/likeadmin
+// | 访问官网:https://www.likeadmin.cn
+// | likeadmin团队 版权所有 拥有最终解释权
+// +----------------------------------------------------------------------
+// | author: likeadminTeam
+// +----------------------------------------------------------------------
+
+namespace app\adminapi\lists\master_worker;
+
+
+use app\adminapi\lists\BaseAdminDataLists;
+use app\common\model\master_worker\MasterWorker;
+use app\common\lists\ListsSearchInterface;
+/**
+ * MasterWorkerSchedule列表
+ * Class MasterWorkerScheduleLists
+ * @package app\adminapi\listsmaster_worker
+ */
+class MasterWorkerScheduleLists extends BaseAdminDataLists implements ListsSearchInterface
+{
+    public $count = 0;
+    /**
+     * @notes 设置搜索条件
+     * @return \string[][]
+     * @author likeadmin
+     * @date 2024/07/10 18:17
+     */
+    public function setSearch(): array
+    {
+        return [
+            '=' => ['mobile'],
+            '%like%' => ['real_name']
+        ];
+    }
+
+    public function queryWhere(){
+        $where[] = ['type','=',2];
+        return $where;
+    }
+    /**
+     * @notes 获取列表
+     * @return array
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function lists(): array
+    {
+        $list = MasterWorker::with(['stopList'])->where($this->searchWhere)
+            ->where($this->queryWhere())
+            ->field(['id','real_name','mobile','avatar'])
+            ->limit($this->limitOffset, $this->limitLength)
+            ->order("id","desc")
+            ->select()
+            ->toArray();
+        foreach ($list as &$item) {
+            $stopList = [];
+            foreach( $item['stopList'] as $v ) {
+                $min = (int)date("d",strtotime($v['start_time']));
+                $max = (int)date("d",strtotime($v['end_time']));
+                $stopList = array_merge($stopList,range($min,$max));
+            }
+            $item['stopList'] = array_values(array_unique($stopList));
+        }
+        return $list;
+    }
+
+
+    /**
+     * @notes 获取数量
+     * @return int
+     * @author likeadmin
+     * @date 2024/07/10 18:17
+     */
+    public function count(): int
+    {
+        return MasterWorker::where($this->searchWhere)
+            ->where($this->queryWhere())
+            ->count();
+    }
+}

+ 75 - 0
app/adminapi/lists/master_worker/MasterWorkerStopLists.php

@@ -0,0 +1,75 @@
+<?php
+// +----------------------------------------------------------------------
+// | likeadmin快速开发前后端分离管理后台(PHP版)
+// +----------------------------------------------------------------------
+// | 欢迎阅读学习系统程序代码,建议反馈是我们前进的动力
+// | 开源版本可自由商用,可去除界面版权logo
+// | gitee下载:https://gitee.com/likeshop_gitee/likeadmin
+// | github下载:https://github.com/likeshop-github/likeadmin
+// | 访问官网:https://www.likeadmin.cn
+// | likeadmin团队 版权所有 拥有最终解释权
+// +----------------------------------------------------------------------
+// | author: likeadminTeam
+// +----------------------------------------------------------------------
+
+namespace app\adminapi\lists\master_worker;
+
+
+use app\adminapi\lists\BaseAdminDataLists;
+use app\common\lists\ListsSearchInterface;
+use app\common\model\master_worker\MasterWorkerStop;
+
+/**
+ * MasterWorkerStop列表
+ * Class MasterWorkerStopLists
+ * @package app\adminapi\listsmaster_worker
+ */
+class MasterWorkerStopLists extends BaseAdminDataLists implements ListsSearchInterface
+{
+    public $count = 0;
+    /**
+     * @notes 设置搜索条件
+     * @return \string[]
+     */
+    public function setSearch(): array
+    {
+        return [
+            '=' => ['mw.mobile'],
+            '%like%' => ['mw.real_name'],
+        ];
+    }
+   
+    /**
+     * @notes 获取列表
+     * @return array
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function lists(): array
+    {
+        $list = MasterWorkerStop::alias('mws')
+            ->join('master_worker mw', 'mws.worker_id = mw.id')
+            ->join('admin a', 'mws.admin_id = a.id')
+            ->where($this->searchWhere)
+            ->field("mw.real_name,mw.mobile,mw.avatar,a.name as admin_name,mws.*")
+            ->limit($this->limitOffset, $this->limitLength)
+            ->order("mws.id","desc")
+            ->select()->toArray();
+        
+        return $list;
+    }
+
+    /**
+     * @notes 获取数量
+     * @return int
+     */
+    public function count(): int
+    {
+        return MasterWorkerStop::alias('mws')
+            ->join('master_worker mw', 'mws.worker_id = mw.id')
+            ->join('admin a', 'mws.admin_id = a.id')
+            ->where($this->searchWhere)
+            ->count();
+    }
+}

+ 30 - 14
app/adminapi/logic/ConfigLogic.php

@@ -14,21 +14,9 @@
 
 namespace app\adminapi\logic;
 
-use app\adminapi\logic\article\ArticleCateLogic;
-use app\adminapi\logic\auth\MenuLogic;
-use app\adminapi\logic\auth\RoleLogic;
-use app\adminapi\logic\dept\DeptLogic;
-use app\adminapi\logic\dept\JobsLogic;
-use app\adminapi\logic\setting\dict\DictTypeLogic;
-use app\common\enum\YesNoEnum;
 use app\common\logic\TableDataLogic;
-use app\common\model\article\ArticleCate;
-use app\common\model\auth\SystemMenu;
-use app\common\model\auth\SystemRole;
-use app\common\model\dept\Dept;
-use app\common\model\dept\Jobs;
 use app\common\model\dict\DictData;
-use app\common\model\dict\DictType;
+use app\common\model\dict\DictConfig;
 use app\common\service\{FileService, ConfigService};
 
 /**
@@ -108,7 +96,35 @@ class ConfigLogic
         }
         return $result;
     }
+    
+    /**
+     * @notes 根据类型获取字典类型
+     * @param $type
+     * @return array
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public static function getDictConfig($value)
+    {
+        if (!is_string($value)) {
+            return [];
+        }
+        
+        $value = explode(',', $value);
+        $lists = DictConfig::whereIn('value', $value)->select()->toArray();
+        
+        if (empty($lists)) {
+            return [];
+        }
 
-
+        $result = [];
+        foreach ($lists as $item) {
+            if ($item['content']) {
+                $result[$item['value']] = json_decode($item['content'], true);
+            }
+        }
+        return $result;
+    }
 
 }

+ 70 - 3
app/adminapi/logic/master_worker/MasterWorkerLogic.php

@@ -21,6 +21,7 @@ use app\common\model\master_worker\MasterWorkerScore;
 use app\common\model\master_worker\MasterWorkerRetentionMoneyLog;
 use app\common\model\master_worker_credential\MasterWorkerCredentialImages;
 use app\common\model\service_area\ServiceArea;
+use app\common\model\master_worker\MasterWorkerStop;
 use think\db\Query;
 use think\facade\Config;
 use think\facade\Db;
@@ -51,7 +52,7 @@ class MasterWorkerLogic extends BaseLogic
                 self::setError('您所选的位置已超出服务区域!');
                 return false;
             }
-            $masterWorker = MasterWorker::create([
+            $data = [
                 'sn' => $params['sn'],
                 'avatar' => $params['avatar'],
                 'real_avatar' => $params['real_avatar'],
@@ -85,7 +86,13 @@ class MasterWorkerLogic extends BaseLogic
                 'labels' => (isset($params['labels']) && $params['labels'])?implode(',',$params['labels']):'',
                 'remark' => $params['remark']??'',
                 'is_wecall' => $params['is_wecall']??0,
-            ]);
+            ];
+            if (isset($params['type']) && $params['type'] == 2) {
+                $data['type'] = 2;
+                $data['promotion_level'] = 6;   //A1级工程师
+                $data['title_promotion'] = 1;   //普通工程师
+            }
+            $masterWorker = MasterWorker::create($data);
             
             //添加工程师汇总评分数据
             MasterWorkerScore::create([
@@ -124,7 +131,7 @@ class MasterWorkerLogic extends BaseLogic
         }*/
         Db::startTrans();
         try {
-            $detail = MasterWorker::where('id', $params['id'])->field('id,lon,lat,address,service_area_id')->findOrEmpty()->toArray();
+            $detail = MasterWorker::where('id', $params['id'])->field('id,lon,lat,address,service_area_id,type')->findOrEmpty()->toArray();
             $address = $params['address']??'';
             if ($detail['address'] != $address && !empty($address)) {
                 $lon_lat = get_address_lat_lng($address);
@@ -184,7 +191,20 @@ class MasterWorkerLogic extends BaseLogic
                 'labels' => (isset($params['labels']) && $params['labels'])?implode(',',$params['labels']):'',
                 'remark' => $params['remark']??'',
                 'is_wecall' => $params['is_wecall']??0,
+                'type'  => $params['type']??1,
+                'title_promotion'  => $params['title_promotion']??0,
             ];
+            
+            if (isset($params['type']) && $detail['type'] != $params['type']) {
+                if ($params['type'] == 2) {
+                    $update['promotion_level'] = 6;   //A1级工程师
+                    $update['title_promotion'] = 1;   //普通工程师
+                } else {
+                    $update['promotion_level'] = 0;   
+                    $update['title_promotion'] = 0;   
+                }
+            }
+
             //'tenant_id' => $params['tenant_id']??0,
             MasterWorker::where('id', $params['id'])->update($update);
             
@@ -284,4 +304,51 @@ class MasterWorkerLogic extends BaseLogic
         }
 
     }
+
+    /**
+     * 长期合作工程师停单
+     * @param $params
+     * @return bool
+     */
+    public static function stop($params, $userInfo){
+        try{
+            Db::startTrans();
+            $worker = MasterWorker::where(['id' => $params['id']])->findOrEmpty();
+            if($worker->isEmpty()){
+                self::setError('工程师不存在');
+                return false;
+            }
+            $start_time = strtotime($params['start_time']. " 00:00:00");
+            $end_time = strtotime($params['end_time']. " 23:59:59");
+            //校验停单日期是否重复
+            $exists = MasterWorkerStop::where(['worker_id' => $params['id'] ])
+                        ->where(function($query) use ($start_time, $end_time) {
+                            $query->where('start_time', 'between', [$start_time, $end_time])
+                            ->whereOr('end_time', 'between', [$start_time, $end_time]);
+                        })
+                        ->count('id');
+            if($exists > 0){
+                self::setError('该日期已存在停单记录');
+                return false;
+            }
+            if ($params['start_time'] == date('Y-m-d')) {
+                $worker->accept_order_status = 0;
+            }
+            $worker->save();
+
+            MasterWorkerStop::create([
+                'worker_id' => $params['id'],
+                'start_time' => $start_time,
+                'end_time' => $end_time,
+                'admin_id' => $userInfo['admin_id'],
+            ]);
+            Db::commit();
+            return true;
+
+        } catch (\Exception $e){
+            Db::rollback();
+            self::setError($e->getMessage());
+            return false;
+        }
+    }
 }

+ 18 - 3
app/adminapi/logic/works/ServiceWorkLogic.php

@@ -528,6 +528,13 @@ class ServiceWorkLogic extends BaseLogic
             $work->master_worker_id = $params['master_worker_id'];
             $work->work_status = 1;
             $work->dispatch_time = time();
+
+            //长期合作工程师,派单即领单
+            if ($worker->type == 2){
+                $work->work_status = 2;
+                $work->receive_time = time();
+            }
+
             MasterWorker::setWorktotal('inc',$params['master_worker_id']);
             $work->save();
             $work_log = [
@@ -574,7 +581,7 @@ class ServiceWorkLogic extends BaseLogic
         $work_where = !empty($params['work_sn'])?['work_sn'=>$params['work_sn']]:['id'=>$params['id']];
         $result = ServiceWork::with([
             'worker'=> function(Query $query) {
-                $query->field('id,worker_number,real_name,mobile');
+                $query->field('id,worker_number,real_name,mobile,type');
             },
             'allocateWorkerLog' =>function(Query $query){
                 $query->field('id,work_id,opera_log,create_time');
@@ -712,8 +719,12 @@ class ServiceWorkLogic extends BaseLogic
                                         ->toArray();
 
         //查收工程师提成金额
-        $change_amount = MasterWorkerAccountLog::where(['work_sn'=>$result['work_sn'],'action'=>1])->value('change_amount');
-        $result['change_amount'] = !empty($change_amount)?$change_amount:0;
+        if (!empty($params['user_id']) && $result['worker']['type'] == 2) {
+            $result['change_amount'] = "-";
+        } else {
+            $change_amount = MasterWorkerAccountLog::where(['work_sn'=>$result['work_sn'],'action'=>1])->value('change_amount');
+            $result['change_amount'] = !empty($change_amount)?$change_amount:0;
+        }
 
         //质保金相关金额
         $result['retention'] = MasterWorkerRetentionMoneyLog::where('work_id',$result['id'])->visible(['action','amount'])->select()
@@ -1082,6 +1093,10 @@ class ServiceWorkLogic extends BaseLogic
             if($worker->isEmpty()){
                 throw new \Exception('工程师不存在');
             }
+            if ($worker->type == 2) {          
+                throw new \Exception('您当前无权限取消分配');  
+            }
+
             if ($work->tenant_id > 0) {
                 //团队订单取消时,派单数量减1
                 $updateData = date("H",strtotime($work->appointment_time)) < 12 ? ['am_order' => Db::raw('am_order - 1')] : ['pm_order' => Db::raw('pm_order - 1')];

+ 9 - 0
app/adminapi/validate/master_worker/MasterWorkerValidate.php

@@ -47,6 +47,8 @@ class MasterWorkerValidate extends BaseValidate
         'login_time' => 'require',
         'is_new_user' => 'require',
         'exp' => 'require',
+        'start_time' =>'require',
+        'end_time' =>'require',
 
     ];
 
@@ -72,6 +74,8 @@ class MasterWorkerValidate extends BaseValidate
         'login_time' => '最后登录时间',
         'is_new_user' => '是否是新注册用户: [1-是, 0-否]',
         'exp' => '经验值',
+        'start_time' => '停单开始时间',
+        'end_time' => '停单结束时间',
 
     ];
 
@@ -127,4 +131,9 @@ class MasterWorkerValidate extends BaseValidate
     {
         return $this->only(['password']);
     }
+
+    public function sceneStop()
+    {
+        return $this->only(['id','start_time','end_time']);
+    }
 }

+ 3 - 1
app/common.php

@@ -17,13 +17,15 @@ function generateCaptcha() {
     $font_size = 20;    // 字体大小
 
     // 允许的字符(去除易混淆的字符如0、O、1、l等)
-    $allowed_chars = '23456789abcdefghjkmnpqrstuvwxyzABCDEFGHJKMNPQRSTUVWXYZ';
+    //$allowed_chars = '23456789abcdefghjkmnpqrstuvwxyzABCDEFGHJKMNPQRSTUVWXYZ';
+    $allowed_chars = '0123456789';
 
     // 生成随机验证码
     $code = '';
     for ($i = 0; $i < $characters; $i++) {
         $code .= $allowed_chars[rand(0, strlen($allowed_chars) - 1)];
     }
+    //$code = strtolower($code); // 转换为小写字母
 
     // 创建图像资源
     $image = imagecreatetruecolor($width, $height);

+ 4 - 2
app/common/cache/MasterWokerTokenCache.php

@@ -90,7 +90,8 @@ class MasterWokerTokenCache extends BaseCache
                 'team_role' => $user->team_role,
                 'terminal' => $userSession->terminal,
                 'expire_time' => $userSession->expire_time,
-                'type' => 1,
+                'is_temporary' => 0,
+                'type' => $user->type,
             ];
         } else {
             $user = MasterWorkerTemporary::where('id', '=', $userSession->user_id)
@@ -104,7 +105,8 @@ class MasterWokerTokenCache extends BaseCache
                 'mobile' => $user->mobile,
                 'terminal' => $userSession->terminal,
                 'expire_time' => $userSession->expire_time,
-                'type' => 2,
+                'is_temporary' => 1,//是否是临时工程师:1是;0否
+                'type' => $user->type,
             ];
         }
 

+ 167 - 2
app/common/command/AutomaticDispatch.php

@@ -15,6 +15,8 @@ use app\common\model\master_worker\MasterWorkerTeam;
 use app\common\model\works\ServiceWorkAllocateWorkerLog;
 use app\workerapi\logic\ServiceWorkerAllocateWorkerLogic;
 use app\common\model\master_worker\MasterWorkerServiceTime;
+use app\common\model\master_worker\MasterWorkerStop;
+use think\facade\Cache;
 
 class AutomaticDispatch extends Command
 {
@@ -60,6 +62,43 @@ class AutomaticDispatch extends Command
 
         //异常工单:上门时间超过两小时工单未确认完成
         $this->unFinishedWorkAnomalous();
+
+        if (!Cache::get("automatic_dispatch_acceptOrderStatus_".date("Y-m-d"))) {
+            //长期合作工程师:停单/开启接单
+            $this->acceptOrderStatus();
+
+            Cache::set("automatic_dispatch_acceptOrderStatus_".date("Y-m-d"), 1, 24*60*60);//秒数
+        }        
+    }
+
+    /**
+     * 长期合作工程师停单/启单
+     */
+    public function acceptOrderStatus()
+    {
+        $startTime = strtotime(date('Y-m-d 00:00:00'));
+        $endTime = strtotime(date('Y-m-d 23:59:59', strtotime("-1 days")));
+        
+        $list = MasterWorkerStop::where(function ($query) use ($startTime, $endTime) {
+                        $query->where('start_time', $startTime)
+                              ->whereOr('end_time', $endTime);
+                    })
+                    ->field("id,worker_id,start_time,end_time")
+                    ->order("start_time", "ASC")
+                    ->select()
+                    ->toArray();
+                    
+        if (!$list) {
+            return ;
+        }
+        foreach($list as $item) {
+            try {
+                $accept_order_status = strtotime($item['start_time']) == $startTime ? 0 : 1;
+                MasterWorker::where('id', $item['worker_id'])->update(['accept_order_status' => $accept_order_status]);
+            } catch (\Exception $e) {
+                Log::write('长期合作工程师停单/启单异常:'.$e->getMessage());
+            } 
+        }
     }
 
     /**
@@ -178,8 +217,13 @@ class AutomaticDispatch extends Command
 
         foreach($list as $item) {     
             try {
-                //优先平台工程师派单
+                
+                //优先平台工程师派单(长期合作)
                 $res = $this->platformWorker($item);
+                if ($res === false) {
+                    //优先平台工程师派单(短期合作)
+                    $res = $this->platformTemporaryWorker($item);
+                }
                 if ($res === false) {
                     //门店负责人派单
                     $res = $this->teamWorker($item);
@@ -217,7 +261,7 @@ class AutomaticDispatch extends Command
     }
 
     /**
-     * 派单给平台工程师
+     * 派单给平台工程师(长期合作)
      */
     protected function platformWorker($item) {
 
@@ -237,6 +281,127 @@ class AutomaticDispatch extends Command
         $worker = MasterWorker::alias('a')
             ->leftJoin('master_worker_score b', 'a.id = b.worker_id')
             ->where([
+                ['type', '=', 2],
+                ['is_disable', '=', 0],
+                ['work_status', '=', 0],
+                ['accept_order_status', '=', 1],
+                ['city', '=', $item['city']],
+                ['service_area_id', '=', $item['service_area_id']],
+                ['tenant_id', '=', 0]
+            ])
+            ->distinct('a.id')
+            ->whereRaw('FIND_IN_SET(' . $item['goods_category_id'] . ', a.category_ids)')
+            //->whereRaw("{$distanceCalculation} <= a.distance")
+            ->field([
+                'a.id',
+                'a.tenant_id',
+                'a.distance',
+                'a.lon',
+                'a.lat',
+                'a.worker_number',
+                'a.real_name',
+                'a.mobile',
+                'a.is_wecall',
+                'b.comprehensive_score',
+                'b.weight_score',
+                $real_distance
+            ])
+            ->orderRaw('(b.comprehensive_score + b.weight_score) desc')
+            ->limit(100)
+            ->select()
+            ->toArray();
+            //echo MasterWorker::getLastSql();die;
+        $queue = [];
+        foreach($worker as $key => $value) {  
+            //过滤已接过此单的师傅
+            $exists = ServiceWorkAllocateWorkerLog::where('work_id', $item['id'])->where('master_worker_id',$value['id'])->count();
+            if ($exists) {
+                continue;
+            }
+        
+            //计算地理效率得分
+            $realDistance = bcdiv($value['real_distance'],1000,2);
+            $travelTime = $realDistance * 2;//预计每公里行驶2分钟
+            
+            $distanceScore = 100 - ($travelTime * 1.5) - ($realDistance * 5);
+            $distanceScore = bcadd($distanceScore, 0, 2);
+            $tmpDistanceRate = bcmul($distanceScore, $this->distanceRate, 2);
+            $tmpRate = 0;
+            $value['travelTime'] = $travelTime;
+            $value['comprehensive_score'] = isset($value['comprehensive_score']) ? $value['comprehensive_score'] : 0;
+            $value['weight_score'] = isset($value['weight_score']) ? $value['weight_score'] : 0;
+            $tmpRate = bcmul($value['comprehensive_score'], $this->comprehensiveRate, 2) + bcmul($value['weight_score'], $this->weightRate, 2);
+            $tmpRate = bcadd($tmpRate, $tmpDistanceRate,2);
+            $tmpKey = isset($queue[$tmpRate]) ? bcadd($tmpRate, $key / 100,2) : $tmpRate;//防止键名重复
+            $queue[$tmpKey] = $value;
+           
+        }
+        //按照工程师的总分值倒序排序
+        krsort($queue);
+        foreach($queue as $worker) {
+            $serviceTime = MasterWorkerServiceTime::where('master_worker_id',$worker['id'])->where('goods_category_id',$item['goods_category_id'])->value('service_time');
+            if (empty($serviceTime)) {
+                $serviceTime = GoodsTime::whereRaw('FIND_IN_SET('.$item['goods_category_id'].', goods_category_ids)')->value('service_time');
+                $serviceTime = $serviceTime ?? $this->defaultServiceTime;//默认服务时长
+            }
+
+            //预约开始时间和结束时间
+            $appointment_time = is_numeric($item['appointment_time']) ? $item['appointment_time'] : strtotime($item['appointment_time']);
+            $estimated_finish_time = $appointment_time + $serviceTime * 60 + $worker['travelTime'] * 60;
+            //校验客户的预约时间是否在工程师的空挡期内
+            $count = ServiceWork::where([
+                ['master_worker_id','=',$worker['id']],
+                ['work_status','>=',1],
+                ['work_status','<=',5],
+                ['service_status','<',4]
+            ])
+            ->where(function ($query) use ($appointment_time,$estimated_finish_time) {
+                $query->where('appointment_time', 'between',[$appointment_time, $estimated_finish_time])
+                    ->whereOr('estimated_finish_time', 'between', [$appointment_time, $estimated_finish_time]);
+            })
+            ->count();
+            if ($count == 0) {
+                $operaLog = '系统自动派单于'.date('Y-m-d H:i:s',time()).'分配了工程师'.'编号['.$worker['worker_number'].']'.$worker['real_name'];
+                $res = $this->allocateWorker($item,$worker['id'],$worker['tenant_id'],$operaLog,$estimated_finish_time);
+                if ($res === true && $worker['is_wecall'] == 1) {
+                    $this->customerList[] = [
+                        'phone' => $worker['mobile'],
+                        'properties' => [
+                            '订单号' => substr($item['work_sn'], -4),
+                            // '详细地址'=>$item['address'],
+                            // '服务类型'=> isset($this->categoryType[$item['category_type']]) ? $this->categoryType[$item['category_type']] : '',
+                            // '客户手机号'=>$item['mobile']
+                        ]
+                    ];
+                }
+                return $res;
+            }
+        } 
+        return false;
+    }
+
+    /**
+     * 派单给平台工程师(短期合作)
+     */
+    protected function platformTemporaryWorker($item) {
+
+        // 定义地球半径(单位:米)
+        $earthRadius = 6371000;
+
+        // 定义 Haversine 公式计算距离的 SQL 片段
+        $distanceCalculation = "{$earthRadius} * 2 * ASIN(SQRT(
+            POWER(SIN((RADIANS({$item['lat']}) - RADIANS(a.lat)) / 2), 2) +
+            COS(RADIANS({$item['lat']})) * COS(RADIANS(a.lat)) *
+            POWER(SIN((RADIANS({$item['lon']}) - RADIANS(a.lon)) / 2), 2)
+        ))";
+        // 计算距离的字段定义
+        $real_distance = Db::raw("{$distanceCalculation} AS real_distance");
+
+        // 获取符合条件的工程师
+        $worker = MasterWorker::alias('a')
+            ->leftJoin('master_worker_score b', 'a.id = b.worker_id')
+            ->where([
+                ['type', '=', 1],
                 ['is_disable', '=', 0],
                 ['work_status', '=', 0],
                 ['accept_order_status', '=', 1],

+ 88 - 11
app/common/command/UpdateWorkerScore.php

@@ -18,7 +18,9 @@ use app\common\model\master_worker\MasterWorkerTeam;
 use app\common\model\master_worker\MasterWorkerScore;
 use app\common\model\works\ServiceWorkAllocateWorkerLog;
 use app\common\model\master_worker\MasterWorkerServiceTime;
+use app\common\model\works\ServiceWorkLog;
 use app\common\model\service_area\ServiceArea;
+use app\common\model\dict\DictConfig;
 
 class UpdateWorkerScore extends Command
 {
@@ -46,7 +48,7 @@ class UpdateWorkerScore extends Command
         }
 
     }
-
+   
     /**
      * 初始化工程师服务区域ID
      */
@@ -103,7 +105,9 @@ class UpdateWorkerScore extends Command
     protected function changeWorderScore()
     {
         $startTime = date('Y-m-d 00:00:00', strtotime('-7 days'));
+        $startTime = strtotime($startTime);
         $endTime = date('Y-m-d 23:59:59', strtotime('-1 days'));
+        $endTime = strtotime($endTime);
         $page = 0;
         $size = 50;
         while(true) {
@@ -111,7 +115,7 @@ class UpdateWorkerScore extends Command
             $offset = ($page - 1) * $size;
             $list = MasterWorker::alias("a")
                     ->leftJoin("master_worker_score b","a.id = b.worker_id")
-                    ->field('a.id,a.category_ids,b.comprehensive_score_history')
+                    ->field('a.id,a.category_ids,a.type,b.comprehensive_score_history')
                     ->limit($offset, $size)
                     ->select()
                     ->toArray();
@@ -120,7 +124,11 @@ class UpdateWorkerScore extends Command
             }
             foreach($list as $item) {
                 $workId = $item['id'];
-                $this->updateComprehensiveScore($startTime,$endTime,$workId,$item['comprehensive_score_history']);
+                if ($item['type'] == 2) {
+                    $this->updateComprehensiveScore($startTime,$endTime,$workId,$item['comprehensive_score_history']);
+                } else {
+                    $this->updateTemporaryComprehensiveScore($startTime,$endTime,$workId,$item['comprehensive_score_history']);
+                }
 
                 //更新工程师平均服务时长
                 $this->updateServiceTime($startTime,$endTime,$item);
@@ -162,21 +170,91 @@ class UpdateWorkerScore extends Command
 
     }
 
+    /*
+    * 长期合作师傅综合评分 100分 (每一项默认20%)
+    评分周期:每周
+    1、用户评分 
+    2、完单率 = 完结工单/总工单
+    3、客户粘性率 = 工程师第一次联系客户时间-领单时间 和定义的时间进行比较 
+    4、加单率 = 加单个数/派单数 
+    5、上门率 = 已上门/总单数
+    */
+    protected function updateComprehensiveScore($startTime,$endTime,$workId,$historyScore) {
+        try {
+            //查询本周平均评分值
+            $goodsReviewsAvg = GoodsReviews::alias('a')->leftJoin("service_work b","a.work_id = b.id")->whereBetweenTime('a.create_time', $startTime, $endTime)->avg('rating');
+            $commentScore = $goodsReviewsAvg > 0 ? bcdiv($goodsReviewsAvg, 5, 2) : 0;
+            
+            //总工单:统计派单日志记录
+            $allOrder = ServiceWorkAllocateWorkerLog::where('master_worker_id',$workId)->whereBetweenTime('create_time', $startTime, $endTime)->group('work_id')->count();
+            
+            //完结工单:统计工单表已完成的工单
+            $completeOrder = ServiceWork::where('master_worker_id',$workId)->where('service_status',3)->where('work_pay_status','in',[1,2])->whereBetweenTime('create_time', $startTime, $endTime)->count();
+            
+            if ($allOrder == 0) {
+                $completionRate = 0;
+            } else {
+                //完单率
+                $completionRate = bcdiv($completeOrder, $allOrder ,2);
+            }
+
+            if ($completeOrder == 0) {
+                $addRate = 0;
+            } else {
+                //加单率
+                $addWord = ServiceWork::where('master_worker_id',$workId)->where('work_type',2)->whereBetweenTime('create_time', $startTime, $endTime)->count();
+                $addRate = bcdiv($addWord, $completeOrder ,2);
+                $addRate = $addRate > 1 ? 1 : $addRate;
+            }
+            
+            //客户粘性率(10分钟内)
+            $viscosityCount = ServiceWork::where('master_worker_id',$workId)->where('work_status','>',1)->where('first_contact_time','>',0)->whereRaw('first_contact_time - receive_time < 600')->whereBetweenTime('create_time', $startTime, $endTime)->count();
+            $viscosityRate = $allOrder > 0 ? bcdiv($viscosityCount, $allOrder,2) : 0;
+
+            //上门率
+            $doorCount = ServiceWorkLog::where('master_worker_id',$workId)->where('opera_log','like','%已上门%')->whereBetweenTime('create_time', $startTime, $endTime)->count();
+            
+            $doorRate = $allOrder > 0 ? bcdiv($doorCount, $allOrder,2) : 0;
+            //echo $commentScore ."--". $completionRate ."--". $viscosityRate ."--". $addRate ."--". $doorRate;die;
+            //工程师汇总评分 
+            $comprehensiveScore = $commentScore + $completionRate + $viscosityRate + $addRate + $doorRate;
+            $comprehensiveScore = bcdiv($comprehensiveScore, 5, 2) * 100;
+            
+            //更新工程师等级晋升
+            $score = bcdiv($comprehensiveScore, 20, 1);
+
+            $content = DictConfig::where('value', 'promotion_level')->value('content');
+            $content = $content ? json_decode($content, true) : [];
+            foreach($content as $item) {
+                $value = explode("-",$item['value']);
+                if ($score >= $value[0] || (isset($value[1]) && $score >= $value[0] && $score <= $value[1])) {
+                    $promotion_level = $item['key'];
+                    MasterWorker::where('id',$workId)->update(['promotion_level' => $promotion_level]);
+                    break;
+                }
+            }
+            $this->doComprehenSivescore($workId,$comprehensiveScore,$historyScore,$doorCount);
+        } catch (\Exception $e) {
+            Log::write('更新长期合作工程师综合评分异常:'.$e->getMessage());
+            return false;
+        }
+    }
+
     /**
-     * 更新工程师综合评分
+     * 更新工程师综合评分 (短期合作工程师)
      */
-    protected function updateComprehensiveScore($startTime,$endTime,$workId,$historyScore) {
+    protected function updateTemporaryComprehensiveScore($startTime,$endTime,$workId,$historyScore) {
         try {
             //查询本周平均评分值
             $goodsReviewsAvg = GoodsReviews::alias('a')->leftJoin("service_work b","a.work_id = b.id")->whereBetweenTime('a.create_time', $startTime, $endTime)->avg('rating');
             $commentScore = $goodsReviewsAvg > 0 ? bcdiv($goodsReviewsAvg, 5, 2) : 0;
             
             //总工单:统计派单日志记录
-            $allOrder = ServiceWorkAllocateWorkerLog::where('master_worker_id',$workId)->whereBetweenTime('create_time', $startTime, $endTime)->count();
+            $allOrder = ServiceWorkAllocateWorkerLog::where('master_worker_id',$workId)->whereBetweenTime('create_time', $startTime, $endTime)->group('work_id')->count();
             //完结工单:统计工单表已完成的工单
             $completeOrder = ServiceWork::where('master_worker_id',$workId)->where('service_status',3)->where('work_pay_status','in',[1,2])->whereBetweenTime('create_time', $startTime, $endTime)->count();
             //接单量:统计工程师接单的数量
-            $acceptOrder = ServiceWorkReceiveLog::where('master_worker_id',$workId)->whereBetweenTime('create_time', $startTime, $endTime)->count();
+            $acceptOrder = ServiceWorkReceiveLog::where('master_worker_id',$workId)->whereBetweenTime('create_time', $startTime, $endTime)->group('work_id')->count();
 
             if ($allOrder == 0) {
                 $completionRate = 0;
@@ -208,9 +286,8 @@ class UpdateWorkerScore extends Command
             }
          
             //客户粘性率(10分钟内)
-            $avgTime = ServiceWork::where('master_worker_id',$workId)->where('work_status','>',1)->where('first_contact_time','>',0)->whereBetweenTime('create_time', $startTime, $endTime)->field('AVG(first_contact_time - receive_time) as avg_time')->find();
-            $avgTime = $avgTime['avg_time'] ? $avgTime['avg_time'] / 60 : 0;
-            $viscosityRate = $avgTime <= 10  && $avgTime > 0 ? 1 : 0;
+            $viscosityCount = ServiceWork::where('master_worker_id',$workId)->where('work_status','>',1)->where('first_contact_time','>',0)->whereRaw('first_contact_time - receive_time < 600')->whereBetweenTime('create_time', $startTime, $endTime)->count();
+            $viscosityRate = $acceptOrder > 0 ? bcdiv($viscosityCount, $acceptOrder,2) : 0;
 
             $partOrder = ServiceWork::where('master_worker_id',$workId)->where('work_status','>',1)->where('order_effective_id',0)->whereBetweenTime('create_time', $startTime, $endTime)->count();
             if ($partOrder == 0) {
@@ -228,7 +305,7 @@ class UpdateWorkerScore extends Command
             $this->doComprehenSivescore($workId,$comprehensiveScore,$historyScore,$acceptOrder);
         } catch (\Exception $e) {
             
-            Log::write('更新工程师综合评分异常:'.$e->getMessage());
+            Log::write('更新短期合作工程师综合评分异常:'.$e->getMessage());
             return false;
         }
     }

+ 7 - 0
app/common/model/master_worker/MasterWorker.php

@@ -118,4 +118,11 @@ class MasterWorker extends BaseModel
     {
         return $this->hasMany(ServiceWork::class,'master_worker_id','id')->field('id,master_worker_id,title,address,appointment_time,dispatch_time,estimated_finish_time,finished_time,goods_category_id,mobile,real_name,city,work_sn,work_status','in',[0,1])->order('appointment_time','asc');
     }
+    //当月停单记录
+    public function stopList()
+    {
+        return $this->hasMany(MasterWorkerStop::class,'worker_id','id')
+                ->where('start_time', 'between',[strtotime(date('Y-m-01 00:00:00')),strtotime(date('Y-m-t 23:59:59'))])
+                ->order('start_time','asc');
+    }
 }

+ 23 - 0
app/common/model/master_worker/MasterWorkerStop.php

@@ -0,0 +1,23 @@
+<?php
+
+namespace app\common\model\master_worker;
+
+use app\common\model\BaseModel;
+
+/**
+ * 工程师停单记录表
+ * Class MasterWorkerStop
+ * @package app\common\model
+ */
+class MasterWorkerStop extends BaseModel
+{
+    protected $name = 'master_worker_stop';
+    public function getStartTimeAttr($value,$data)
+    {
+        return !empty($data['start_time'])?date('Y-m-d',$data['start_time']):'';
+    }
+    public function getEndTimeAttr($value,$data)
+    {
+        return !empty($data['end_time'])?date('Y-m-d',$data['end_time']):'';
+    }
+}

+ 5 - 1
app/workerapi/http/middleware/LoginMiddleware.php

@@ -52,6 +52,10 @@ class LoginMiddleware
 
         //token临近过期,自动续期
         if ($userInfo) {
+            if (!isset($userInfo['is_temporary']) ) {
+                $userInfo = (new MasterWokerTokenCache())->setUserInfo($token);
+            }
+
             //获取临近过期自动续期时长
             $beExpireDuration = Config::get('project.user_token.be_expire_duration');
             //token续期
@@ -65,7 +69,7 @@ class LoginMiddleware
 
             //临时工程师校验一下权限
             $controller = $request->controller();
-            if (isset($userInfo['type']) && $userInfo['type'] == 2 && !in_array(strtolower($controller), ['groupworks','sms','login','upload','masterworkeragree'])) {
+            if (isset($userInfo['is_temporary']) && $userInfo['is_temporary'] == 1 && !in_array(strtolower($controller), ['groupworks','sms','login','upload','masterworkeragree'])) {
                 return JsonService::fail('暂无权限', [], 401);
             }
         }

+ 6 - 2
app/workerapi/logic/AccountLogic.php

@@ -27,14 +27,18 @@ class AccountLogic extends BaseLogic
             $ytime = strtotime("$yDay 00:00:00");//昨天开始时间戳
             $yetime = strtotime("$yDay 23:59:59");//昨天开始时间戳
             $data= ['user_money' =>0,'account_yesterday' => 0,'case_out_money' => 0];
-            $data['user_money'] = MasterWorker::where('id',$params['worker_id'])->value('user_money') ?? 0;
+            $masterWorker = MasterWorker::where('id',$params['worker_id'])->field('user_money,type')->findOrEmpty();
+            if ($masterWorker->type == 2) {
+                return ['user_money' =>"-",'account_yesterday' => "-",'case_out_money' => "-"];
+            }
+            $data['user_money'] = $masterWorker && $masterWorker->user_money ?? 0;
             $where = [];
             $where[] = ['worker_id','=',$params['worker_id']] ;
             $where[] = ['change_type', '=', WorkerAccountLogEnum::UM_INC_ADMIN];
             $where[] = ['action', '=',WorkerAccountLogEnum::INC];
             $where[] = ['create_time','between',[$ytime,$yetime]];
             $data['account_yesterday']= MasterWorkerAccountLog::where($where)
-                ->sum('change_amount');
+            ->sum('change_amount');
             $data['case_out_money']= MasterWorkerCaseOutLog::where([
                 'worker_id'=>$params['worker_id'],
                 'review_status' => 1,

+ 12 - 2
app/workerapi/logic/MasterWorkerLogic.php

@@ -136,7 +136,7 @@ class MasterWorkerLogic extends  BaseLogic
 
      public static function detail($userId): array
     {
-        $worker = MasterWorker::field('id,team_id,team_role,sn,avatar,real_avatar,real_name,nickname,account,mobile,sex,estimate_money,user_money,earnest_money,exp,worker_number,work_status,accept_order_status,identity_source')
+        $worker = MasterWorker::field('id,team_id,team_role,sn,avatar,real_avatar,real_name,nickname,account,mobile,sex,estimate_money,user_money,earnest_money,exp,worker_number,work_status,accept_order_status,identity_source,type')
             ->findOrEmpty($userId)
             ->toArray();
 
@@ -156,7 +156,13 @@ class MasterWorkerLogic extends  BaseLogic
         //今日退款
         $worker['refund_account_today'] = MasterWorkerAccountLog::where(['worker_id'=> $worker['id'],'action'=>2,'change_type'=>WorkerAccountLogEnum::UM_DEC_ADMIN])->whereTime('create_time', 'today')->sum('change_amount');
          //今日收益
-        $worker['account_today'] = MasterWorkerAccountLog::where(['worker_id'=> $worker['id'],'action'=>1,'change_type'=>WorkerAccountLogEnum::UM_INC_ADMIN])->whereTime('create_time', 'today')->sum('change_amount')-$worker['refund_account_today'];
+        if ($worker['type'] == 2) {
+            //长期合作工程师不返回
+            $worker['user_money'] = "-";
+            $worker['account_today'] = "-";
+        } else {
+            $worker['account_today'] = MasterWorkerAccountLog::where(['worker_id'=> $worker['id'],'action'=>1,'change_type'=>WorkerAccountLogEnum::UM_INC_ADMIN])->whereTime('create_time', 'today')->sum('change_amount')-$worker['refund_account_today'];
+        }
         //本月成功订单
         $worker['success_work'] = ServiceWork::where(['master_worker_id'=>$worker['id'],'service_status'=>3])->whereTime('create_time', 'month')->count();
         //本月失败单
@@ -181,6 +187,10 @@ class MasterWorkerLogic extends  BaseLogic
             ];
             if($params['field'] == 'accept_order_status'){
                 $masterWorker = MasterWorker::where(['id'=>$userId])->findOrEmpty();
+                //长期合作工程师不允许修改接单状态
+                if ($masterWorker['type'] == 2) {
+                    throw new Exception('您当前无权限操作接单状态');
+                }
                 if($masterWorker['work_status'] != 0 || $masterWorker['is_disable'] != 0){
                     throw new Exception('该账号已禁用或已长期停单');
                 }