Browse Source

判客后台管理和接口

yb 6 months ago
parent
commit
df6b412559

+ 138 - 0
app/admin/controller/consultant/CustomController.php

@@ -9,6 +9,7 @@ use app\admin\validate\consultant\MarketCustomerValidate;
 use app\controller\Curd;
 use app\model\MarketCustomer;
 use support\Request;
+use support\Response;
 
 class CustomController extends Curd
 {
@@ -44,4 +45,141 @@ class CustomController extends Curd
         }
         return CustomService::add($params);
     }
+
+    /**
+     * Notes: 编辑客户
+     * User: yb
+     * Date: 2024/8/6
+     * Time: 16:51
+     */
+    public function updateCustom(Request $request)
+    {
+        $params = $request->post();
+        if ($this->validate && !$this->validateClass->scene('update')->check($params)) {
+            return json_fail($this->validateClass->getError());
+        }
+        return CustomService::update($params);
+    }
+
+    public function select(Request $request): Response
+    {
+        return CustomService::index($request);
+    }
+
+    /**
+     * Notes: 删除客户
+     * User: yb
+     * Date: 2024/8/2
+     * Time: 13:34
+     * @param Request $request
+     * @return Response
+     */
+    public function deleteCustom(Request $request)
+    {
+        $params = $request->post();
+        if (empty($params['ids'])) {
+            return json_fail('请选择要删除的数据');
+        }
+        return CustomService::delete($params['ids']);
+    }
+
+    /**
+     * Notes: 跟进记录
+     * User: yb
+     * Date: 2024/8/7
+     * Time: 8:59
+     * @param Request $request
+     */
+    public function followList(Request $request)
+    {
+        return CustomService::follow($request);
+    }
+
+    /**
+     * Notes: 删除跟进记录
+     * User: yb
+     * Date: 2024/8/7
+     * Time: 11:32
+     * @param Request $request
+     */
+    public function deleteFollow(Request $request)
+    {
+        $params = $request->post();
+        if (empty($params['ids'])) {
+            return json_fail('请选择要删除的数据');
+        }
+        return CustomService::deleteFollow($params['ids']);
+    }
+
+    /**
+     * Notes: 转移客户
+     * User: yb
+     * Date: 2024/8/7
+     * Time: 14:22
+     * @param Request $request
+     */
+    public function moveCustom(Request $request)
+    {
+        $params = $request->post();
+        if (empty($params['consultant_id'])) {
+            return json_fail('请选择目标顾问');
+        }
+        if (empty($params['move_market_customer_id']) && empty($params['move_consultant_id'])) {
+            return json_fail('请选择需要转移的客户或顾问');
+        }
+        return CustomService::moveCustom($params);
+    }
+
+    /**
+     * Notes: 转移记录
+     * User: yb
+     * Date: 2024/8/7
+     * Time: 15:34
+     * @param Request $request
+     * @return Response
+     */
+    public function moveLogs(Request $request)
+    {
+        return CustomService::moveLogs($request);
+    }
+
+    /**
+     * Notes: 报备审核
+     * User: yb
+     * Date: 2024/8/16
+     * Time: 15:55
+     * @param Request $request
+     */
+    public function checkCustom(Request $request)
+    {
+        $params = $request->post();
+        if ($this->validate && !$this->validateClass->scene('check')->check($params)) {
+            return json_fail($this->validateClass->getError());
+        }
+        return CustomService::checkCustom($params);
+    }
+
+    /**
+     * Notes: 统计分析
+     * User: yb
+     * Date: 2024/8/17
+     * Time: 11:50
+     */
+    public function statisticsIndex(Request $request)
+    {
+        return CustomService::statisticsIndex($request);
+    }
+
+    /**
+     * Notes: 导出
+     * User: yb
+     * Date: 2024/8/17
+     * Time: 13:29
+     * @param Request $request
+     * @return Response
+     */
+    public function exportData(Request $request)
+    {
+        return CustomService::exportData($request);
+    }
 }

+ 35 - 0
app/admin/controller/consultant/TeamController.php

@@ -8,8 +8,10 @@ use app\common\Tree;
 use app\controller\Curd;
 use app\model\Consultant;
 use app\model\SysDept;
+use app\model\SysUser;
 use support\Request;
 use support\Response;
+use Tinywan\Jwt\JwtToken;
 
 class TeamController extends Curd
 {
@@ -26,6 +28,14 @@ class TeamController extends Curd
         $order = $request->get('order', 'desc');
         $field = $field ?? 'dept_addtimes';
         $where['dept_category'] = '获客团队';
+        $deptIds = TeamService::getIdsByUser(1);
+        if (false === $deptIds) {
+            //无权限
+            $where['dept_id'] = ['=', 0];
+        } else if (is_array($deptIds)) {
+            //指定团队下的权限
+            $where['dept_id'] = ['in', $deptIds];
+        }
         $query = $this->doSelect($where, $field, $order);
         return $this->doFormat($query, $format, 1000);
     }
@@ -128,4 +138,29 @@ class TeamController extends Curd
     {
         return TeamService::delDept($request);
     }
+
+    /**
+     * Notes: 检查管理员的部门信息
+     * User: yb
+     * Date: 2024/8/16
+     * Time: 17:28
+     * @return Response
+     */
+    public function checkDept()
+    {
+        $deptIds = TeamService::getIdsByUser(1);
+        if (false === $deptIds) {
+            //无权限
+            $authDeptId = -1;
+        } else if (is_array($deptIds)) {
+            $userId = JwtToken::getCurrentId();
+            $userInfo = SysUser::firstWhere(['user_id' => $userId]);
+            $authDeptId = $userInfo->join_user_dept_id;
+        } else {
+            //超管权限
+            $authDeptId = -2;
+        }
+
+        return json_success('请求成功', ['auth_dept_id' =>$authDeptId]);
+    }
 }

+ 5 - 1
app/admin/controller/sys_manage/MenuController.php

@@ -187,7 +187,11 @@ class MenuController extends Curd
     protected function formatTree($items): Response
     {
         $format_items = [];
+        $btnAuth = []; //按钮权限
         foreach ($items as $item) {
+            if ($item->menu_is_menu == 2) {
+                $btnAuth[] = $item->menu_name;
+            }
             $format_items[] = [
                 'id' => $item->menu_id,
                 'pid' => $item->menu_pid,
@@ -215,7 +219,7 @@ class MenuController extends Curd
         $data = [
             'dashboardGrid' => ["welcome", "member", "sysManage", "about"],
             'menu' => $menu,
-            'permissions' => ['list.add']
+            'permissions' => $btnAuth //按钮权限
         ];
 
         return json_success('success', $data);

+ 19 - 3
app/admin/service/consultant/ConsultantService.php

@@ -5,6 +5,7 @@ namespace app\admin\service\consultant;
 
 
 use app\model\Consultant;
+use app\model\MarketCustomer;
 use app\model\SysDept;
 use app\model\SysUser;
 use support\Request;
@@ -22,6 +23,16 @@ class ConsultantService
         $where = [];
         $whereDept = [];
         $whereTopDept = [];
+        $deptIds = TeamService::getIdsByUser(1);
+        if (false === $deptIds) {
+            //无权限
+            $where[] = ['dept_id', '=', 0];
+        } else if (is_array($deptIds)) {
+            //指定团队下的权限
+            $where[] = [function($query) use ($deptIds) {
+                $query->whereIn('dept_id', $deptIds);
+            }];
+        }
         if (!empty($params['name'])) {
             $where[] = ['name', 'like', "%{$params['name']}%"];
         }
@@ -94,7 +105,7 @@ class ConsultantService
             return json_fail('团队不存在');
         }
         if (empty($params['password'])) {
-            $showPassword = substr($params['mobile'], 4);
+            $showPassword = substr($params['mobile'], 5);
             $password = self::handlePassword($showPassword);
         } else {
             $password = self::handlePassword($params['password']);
@@ -206,15 +217,20 @@ class ConsultantService
         if (!is_array($ids)) {
             $ids = [$ids];
         }
-        //查询员工下是否存在客户
         try {
             if (is_array($ids)) {
+                if (MarketCustomer::whereIn('consultant_id', $ids)->exists()) {
+                    throw new \Exception('员工下存在客户');
+                }
                 Consultant::whereIn('id', $ids)->delete();
             } else {
+                if (MarketCustomer::where('consultant_id', $ids)->exists()) {
+                    throw new \Exception('员工下存在客户');
+                }
                 Consultant::where('id', $ids)->delete();
             }
         } catch (\Exception $e) {
-            return json_fail('删除失败');
+            return json_fail($e->getMessage());
         }
 
         return json_success('删除成功');

+ 774 - 86
app/admin/service/consultant/CustomService.php

@@ -4,116 +4,221 @@
 namespace app\admin\service\consultant;
 
 
+use app\admin\service\member\MemberService;
 use app\model\Consultant;
 use app\model\MarketCustomer;
+use app\model\MarketCustomerFollow;
+use app\model\MarketCustomerLogs;
 use app\model\Member;
+use app\model\SysDept;
 use support\Db;
 use support\exception\BusinessException;
+use support\Request;
+use Tinywan\Jwt\JwtToken;
 
 class CustomService
 {
+    const DIFF_TIME = (60 * 60 * 24 * 30);
     /**
-     * Notes: 处理参数
+     * Notes: 返回选项配置信息
      * User: yb
-     * Date: 2024/8/6
-     * Time: 11:43
-     * @param $data
-     * @return mixed
+     * Date: 2024/8/5
+     * Time: 16:05
+     * @return array
      */
-    protected static function handleNumParams($data)
+    public static function config()
     {
-        $setField = ['require_area', 'requirement', 'age_range', 'focus', 'region'];
-        foreach ($data as $k => $v) {
-            if (in_array($k, $setField)) {
-                if (is_numeric($v)) {
-                    $data[$k] = $v + 1;
-                } else {
-                    $data[$k] = null;
-                }
+        return MarketCustomer::config();
+    }
+
+    public static function commonSearch($params)
+    {
+        $where = [];
+        $currentTime = time();
+        $diffNums = self::DIFF_TIME;
+        $deptIds = TeamService::getIdsByUser();
+        if (false === $deptIds) {
+            //无权限
+            $where[] = ['dept_id', '=', 0];
+        } else if (is_array($deptIds)) {
+            //指定团队下的权限
+            if (!empty($params['dept_id'])) {
+                $deptId = end($params['dept_id']);
+                $deptIds = SysDept::where('dept_super_path','like', "%/{$deptId}/%")->where('dept_category', '=', TeamService::DEPT_CATEGORY)->pluck('dept_id')->toArray();
+            }
+            $where[] = [function($query) use ($deptIds) {
+                $query->whereIn('dept_id', $deptIds);
+            }];
+        } else {
+            if (!empty($params['dept_id'])) {
+                $deptId = end($params['dept_id']);
+                $deptIds = SysDept::where('dept_super_path','like', "%/{$deptId}/%")->where('dept_category', '=', TeamService::DEPT_CATEGORY)->pluck('dept_id')->toArray();
+                $where[] = [function($query) use ($deptIds) {
+                    $query->whereIn('dept_id', $deptIds);
+                }];
             }
         }
-        if ($data['type'] == 1) {
-            //来电
-            if (!empty($data['area'])) {
-                $area = $data['area'];
-                $requireArea = 1;
-                if ($area >= 40 && $area < 51) {
-                    $requireArea = 2;
-                } else if ($area >= 51 && $area < 61) {
-                    $requireArea = 3;
-                } else if ($area >= 61 && $area < 81) {
-                    $requireArea = 4;
-                } else if ($area >= 81 && $area < 101) {
-                    $requireArea = 5;
-                } else if ($area >= 101 && $area < 121) {
-                    $requireArea = 6;
-                } else if ($area >= 121 && $area < 151) {
-                    $requireArea = 7;
-                } else if ($area >= 151) {
-                    $requireArea = 8;
-                }
-                $data['require_area'] = $requireArea;
-                $data['age_range'] = null;
+
+        if (!empty($params['name'])) {
+            $keywords = $params['name'];
+            $where[] = [function($query) use ($keywords) {
+                $query->orWhere('name', 'like', "%{$keywords}%")->orWhere('mobile', 'like', "%{$keywords}%");
+            }];
+        }
+        if (!empty($params['report_status'])) {
+            if ($params['report_status'] == -1) {
+                //待审核
+                $where[] = ['check_status', '=', 1];
             } else {
-                $data['area'] = null;
+                $where[] = ['check_status', '=', 2];
             }
-        } else {
-            //来访
-            $data['area'] = null;
+            if ($params['report_status'] == 1) {
+                //已报备
+                $where[] = [function($query) use ($diffNums, $currentTime) {
+                    $query->whereRaw("((visit_time + {$diffNums}) - {$currentTime} > 0)")->where('current_status', '=', 1);
+                }];
+            } else if ($params['report_status'] == 3) {
+                //已锁定
+                $where[] = [function($query) {
+                    $query->whereIn('current_status', [2,3,4]);
+                }];
+            } else if ($params['report_status'] == 2) {
+                //已失效
+                $where[] = [function($query) use ($diffNums, $currentTime){
+                    $query->orWhere('current_status', '=', -1)->orWhereRaw("((visit_time + {$diffNums}) - {$currentTime} <= 0 AND current_status = 1 AND check_status = 2)");
+                }];
+            }
+        }
+        if (!empty($params['consultant_name'])) {
+            $consultantIds = Consultant::where('name', 'like', "%{$params['consultant_name']}%")->pluck('id');
+            $where[] = [function($query) use ($consultantIds) {
+                $query->whereIn('consultant_id', $consultantIds);
+            }];
+        }
+        if (!empty($params['type'])) {
+            $where[] = ['type', '=', $params['type']];
         }
-        if (!empty($data['visit_time'])) {
-            $data['visit_time'] = strtotime($data['visit_time']);
+        if (!empty($params['visit_time'])) {
+            $datetime = $params['visit_time'];
+            $datetime[0] = strtotime($datetime[0]);
+            $datetime[1] = strtotime($datetime[1]);
+            $where[] = [function($query) use ($datetime) {
+                $query->whereBetween('visit_time', $datetime);
+            }];
         }
-        if (empty($data['visit_type'])) {
-            $data['visit_type'] = null;
+        if (!empty($params['visit_date'])) {
+            $visitDate = $params['visit_date'];
+            $visitDate[0] = strtotime($visitDate[0].' 00:00:00');
+            $visitDate[1] = strtotime($visitDate[1].' 23:59:59');
+            $where[] = [function($query) use ($visitDate) {
+                $query->whereBetween('visit_time', $visitDate);
+            }];
         }
-        if (empty($data['level'])) {
-            $data['level'] = null;
+        if (!empty($params['created_date'])) {
+            $createdDate = $params['created_date'];
+            $createdDate[0] = strtotime($createdDate[0].' 00:00:00');
+            $createdDate[1] = strtotime($createdDate[1].' 23:59:59');
+            $where[] = [function($query) use ($createdDate) {
+                $query->whereBetween('created_at', $createdDate);
+            }];
         }
-        if (empty($data['purpose'])) {
-            $data['purpose'] = null;
+
+        if (!empty($params['current_status'])) {
+            $currentStatus = $params['current_status'];
+            $where[] = [function($query) use ($currentStatus){
+                $query->where('current_status', '=', $currentStatus)->where('check_status', '=', 2);
+            }];
+        }
+        if (!empty($params['consultant_id'])) {
+            $where[] = ['consultant_id', '=', $params['consultant_id']];
+        }
+        if (!empty($params['custom'])) {
+            $keywords = $params['custom'];
+            $where[] = [function($query) use ($keywords) {
+                $query->orWhere('name', 'like', "%{$keywords}%")->orWhere('mobile', 'like', "%{$keywords}%");
+            }];
+        }
+        if (!empty($params['check_status'])) {
+            $where[] = ['check_status', '=', $params['check_status']];
+        }
+        if (!empty($params['stat_report_status'])) {
+            //已报备 1
+            $statReportStatus = $params['stat_report_status'];
+            if ($statReportStatus == 1) {
+                $where[] = [function($query) use ($diffNums, $currentTime) {
+                    $query->whereRaw("((visit_time + {$diffNums}) - {$currentTime} > 0)")->where('current_status', '=', 1)->where('check_status', '=', 2);
+                }];
+            }
+            //已锁定 2
+            if ($statReportStatus == 2) {
+                $where[] = [function($query) {
+                    $query->whereIn('current_status', [2,3,4])->where('check_status', '=', 2);
+                }];
+            }
+            //已失效 -1
+            if ($statReportStatus == -1) {
+                $where[] = [function($query) use ($diffNums, $currentTime){
+                    $query->orWhere('current_status', '=', -1)->orWhereRaw("((visit_time + {$diffNums}) - {$currentTime} <= 0 AND current_status = 1 AND check_status = 2)")->orWhere('check_status', '=', -1);
+                }];
+            }
         }
-        return $data;
+        return $where;
     }
+
     /**
-     * Notes: 返回选项配置信息
+     * Notes: 列表
      * User: yb
-     * Date: 2024/8/5
-     * Time: 16:05
-     * @return array
+     * Date: 2024/8/6
+     * Time: 15:07
+     * @param Request $request
      */
-    public static function config()
+    public static function index(Request $request)
     {
-        $config = [
-            'requirement' => ['康养公寓', '颐养公寓', '商业', '其他'],
-            'require_area' => ['40㎡以下', '40㎡-50㎡', '51㎡-60㎡', '61㎡-80㎡', '81㎡-100㎡', '101㎡-120㎡', '121㎡-150㎡', '150㎡以上'],
-            'age_range' => ['40岁以下', '21岁-30岁', '31岁-40岁', '41岁-50岁', '51岁-60岁', '61岁-80岁', '80岁以上'],
-            'visit_type' => [
-                'common' => ['', '路过', '分销', '朋友介绍', '安居客、房天下', '微信朋友圈', '户外', '微信公众号', '工地围挡', '老带新', '圈层营销', '自拓'],
-                'type1' => ['100' => '派单', '101' => '城市展厅', '102' => '活动'],
-                'type2' => ['200' => '内渠', '201' => '自渠']
-            ],
-            'focus' => ['位置', '交通', '配套', '价格', '品牌', '适老化', '物业', '医疗', '运营'],
-            'region' => ['唐冶', '历城', '历下', '高新', '天桥', '槐荫', '市中', '其他'],
-            'purpose' => ['自买自用', '自买他用'],
-            'level' => ['A类', 'B类', 'C类', 'D类'],
-            'current_status' => ['首次到访', '已到访', '无效客户', '已成交'],
+        $format = $request->get('format', 'normal');
+        $limit = (int)$request->get('pageSize', $format === 'tree' ? 1000 : 10);
+        $limit = $limit <= 0 ? 10 : $limit;
+        $params = $request->get();
+        $page = (int)$request->get('page');
+        $page = $page > 0 ? $page : 1;
+        $currentTime = time();
+        $diffNums = self::DIFF_TIME;
+        $where = self::commonSearch($params);
+        $paginator = MarketCustomer::where($where)->orderBy('visit_time', 'desc')->paginate($limit, '*', 'page', $page);
+        $total = $paginator->total();
+        $items = $paginator->items();
+        if (!empty($items)) {
+            $consultantKeys = [];
+            $consultantIds = $paginator->pluck('consultant_id')->toArray();
+            if (!empty($consultantIds)) {
+                //去重
+                $consultantIds = array_unique($consultantIds);
+                //排序
+                $consultantIds = array_values($consultantIds);
+                //查询顾问信息
+                $consultantList = Consultant::whereIn('id', $consultantIds)->select(['id', 'name', 'mobile'])->get();
+                if (!$consultantList->isEmpty()) {
+                    foreach ($consultantList->toArray() as $consultantItem) {
+                        $consultantKeys[$consultantItem['id']] = $consultantItem;
+                    }
+                }
+            }
+            $teamKeys = TeamService::getItemKeys();
+            foreach ($items as &$item) {
+                $visitTime = $item->visit_time;
+                $visitTimeStamp = strtotime($visitTime);
+                $diff = ($visitTimeStamp + $diffNums) - $currentTime;
+                $item->diff_nums =  ($diff > 0) ? $diff : 0;
+                $item->consultant_name = $consultantKeys[$item->consultant_id]['name'] ?? '';
+                $item->consultant_mobile = $consultantKeys[$item->consultant_id]['mobile'] ?? '';
+                $item->team_name = TeamService::getTeamName($teamKeys, $item->dept_id);
+                $item->mask_mobile = self::handlePhone($item->mobile);
+            }
+        }
+        $data = [
+            'total' => $total,
+            'rows' => $items
         ];
-        //处理config中的visit_type
-        $visitType = [];
-        $visitTypeCommon = $config['visit_type']['common'];
-        $visitType1 = $config['visit_type']['type1'];
-        $visitType2 = $config['visit_type']['type2'];
-        foreach ($visitTypeCommon as $key => $item) {
-            if (!empty($item)) {
-                $visitType['type1']["{$key}"] = $item;
-                $visitType['type2']["{$key}"] = $item;
-            }
-        }
-        $visitType['type1'] += $visitType1;
-        $visitType['type2'] += $visitType2;
-        $config['visit_type'] = $visitType;
-        return $config;
+        return json_success('success', $data);
     }
 
     /**
@@ -124,10 +229,10 @@ class CustomService
      */
     public static function add($params)
     {
-        $params = self::handleNumParams($params);
+        $params = MarketCustomer::handleNumParams($params);
         $mobile = $params['mobile'];
         //查询客户手机号是否已经存在
-        if (MarketCustomer::where('mobile', $mobile)->exists()) {
+        if (MarketCustomer::checkCustomExists($mobile)) {
             return json_fail('客户已经登记过了');
         }
         //查询顾问信息
@@ -153,8 +258,11 @@ class CustomService
             'purpose' => $params['purpose'] ?? null,
             'level' => $params['level'] ?? null,
             'type' => $params['type'] ?? null,
+            'check_status' => 1,
             'visit_time' => $params['visit_time'] ?? null,
             'note' => $params['note'] ?? '',
+            'current_status' => $params['current_status'] ?? null,
+            'created_at' => time()
         ];
 
         Db::beginTransaction();
@@ -162,7 +270,27 @@ class CustomService
             //查询会员表中是否存在该客户
 //            $memberId = Member::where('member_mobile', $insertData['mobile'])->value('member_id');
 //            if (empty($memberId)) {
-//                //新增会员信息
+//                $result = MemberService::add([
+//                    'account_name'=> $insertData['name'],
+//                    'member_category' => '售房客户',
+//                    'mobile' => $insertData['mobile'],
+//                    'source' => 'HOUSE']);
+//                $code = $result->getStatusCode();
+//                if ($code == 200) {
+//                    $content = $result->rawBody();
+//                    if (is_json($content)) {
+//                        $content = json_decode($content, 1);
+//                        if ($content['code'] == 200) {
+//                            $memberId = $content['data']['member_id'];
+//                        } else {
+//                            throw new BusinessException('新增会员失败');
+//                        }
+//                    } else {
+//                        throw new BusinessException('新增会员失败');
+//                    }
+//                } else {
+//                    throw new BusinessException('新增会员失败');
+//                }
 //            }
 //            $insertData['member_id'] = $memberId;
             $customId = MarketCustomer::insertGetId($insertData);
@@ -177,4 +305,564 @@ class CustomService
             return json_fail('添加失败');
         }
     }
+
+    /**
+     * Notes: 编辑客户
+     * User: yb
+     * Date: 2024/8/6
+     * Time: 16:53
+     * @param $params
+     */
+    public static function update($params)
+    {
+        $params = MarketCustomer::handleNumParams($params);
+        $mobile = $params['mobile'];
+        //查询客户手机号是否已经存在
+        if (MarketCustomer::checkCustomExists($mobile, $params['id'])) {
+            return json_fail('客户已经登记过了');
+        }
+        $updateData = [
+            'name' => $params['name'],
+            'mobile' => $params['mobile'],
+            'gender' => $params['gender'] ?? null,
+            'visit_type' => $params['visit_type'] ?? null,
+            'require_area' => $params['require_area'] ?? null,
+            'area' => $params['area'] ?? null,
+            'requirement' => $params['requirement'] ?? null,
+            'age_range' => $params['age_range'] ?? null,
+            'focus' => $params['focus'] ?? null,
+            'region' => $params['region'] ?? null,
+            'purpose' => $params['purpose'] ?? null,
+            'level' => $params['level'] ?? null,
+            'type' => $params['type'] ?? null,
+            'note' => $params['note'] ?? '',
+            'current_status' => $params['current_status'] ?? null,
+            'updated_at' => time()
+        ];
+        if (!empty($params['update_visit_time'])) {
+            if ($params['update_visit_time'] == 1) {
+                $updateData['visit_time'] = $params['visit_time'] ?? null;
+            }
+        }
+        $result = MarketCustomer::where('id', $params['id'])->update($updateData);
+        if ($result) {
+            return json_success('编辑成功');
+        } else {
+            return json_fail('编辑失败');
+        }
+    }
+
+    /**
+     * Notes: 删除客户
+     * User: yb
+     * Date: 2024/8/2
+     * Time: 13:33
+     * @param $ids
+     * @return \support\Response
+     */
+    public static function delete($ids)
+    {
+        if (!$ids) {
+            return json_fail("数据错误");
+        }
+        if (!is_array($ids)) {
+            $ids = [$ids];
+        }
+        try {
+            if (is_array($ids)) {
+                MarketCustomer::whereIn('id', $ids)->delete();
+            } else {
+                MarketCustomer::where('id', $ids)->delete();
+            }
+        } catch (\Exception $e) {
+            return json_fail('删除失败');
+        }
+
+        return json_success('删除成功');
+    }
+
+    /**
+     * Notes: 跟进记录
+     * User: yb
+     * Date: 2024/8/7
+     * Time: 9:00
+     * @param Request $request
+     */
+    public static function follow(Request $request)
+    {
+        $format = $request->get('format', 'normal');
+        $limit = (int)$request->get('pageSize', $format === 'tree' ? 1000 : 10);
+        $limit = $limit <= 0 ? 10 : $limit;
+        $params = $request->get();
+        $page = (int)$request->get('page');
+        $page = $page > 0 ? $page : 1;
+        $where = [];
+        $whereCustom = [];
+        $whereConsultant = [];
+        if (!empty($params['market_customer_id'])) {
+            $marketCustomerIds = $params['market_customer_id'];
+            $where[] = [function($query) use ($marketCustomerIds) {
+                $query->whereIn('market_customer_id', $marketCustomerIds);
+            }];
+        }
+        if (!empty($params['consultant_id'])) {
+            $consultantId = $params['consultant_id'];
+            $where[] = [function($query) use ($consultantId) {
+                $query->whereIn('consultant_id', $consultantId);
+            }];
+        }
+        if (!empty($params['follow_way'])) {
+            $where[] = ['follow_way', '=', $params['follow_way']];
+        }
+        if (!empty($params['custom'])) {
+            $custom = $params['custom'];
+            $whereCustom[] = [function($query) use ($custom) {
+                $query->orWhere('name', 'like', "%{$custom}%")->orWhere('mobile', 'like', "%{$custom}%");
+            }];
+        }
+        if (!empty($params['consultant'])) {
+            $consultant = $params['consultant'];
+            $whereConsultant[] = [function($query) use ($consultant) {
+                $query->orWhere('name', 'like', "%{$consultant}%")->orWhere('mobile', 'like', "%{$consultant}%");
+            }];
+        }
+        $paginator = MarketCustomerFollow::with(['custom', 'consultant'])->whereHas('custom', function($query) use ($whereCustom){
+            $query->where($whereCustom);
+        })->whereHas('consultant', function($query) use ($whereConsultant) {
+            $query->where($whereConsultant);
+        })->where($where)->orderBy('follow_time', 'desc')->paginate($limit, '*', 'page', $page);
+        $total = $paginator->total();
+        $items = $paginator->items();
+        foreach ($items as &$item) {
+            if (!empty($item->custom)) {
+                $item->custom->mask_mobile = self::handlePhone($item->custom->mobile);
+            }
+        }
+        $data = [
+            'total' => $total,
+            'rows' => $items
+        ];
+        return json_success('success', $data);
+    }
+
+    /**
+     * Notes: 删除跟进记录
+     * User: yb
+     * Date: 2024/8/7
+     * Time: 11:25
+     * @param $ids
+     */
+    public static function deleteFollow($ids)
+    {
+        if (!$ids) {
+            return json_fail("数据错误");
+        }
+        if (!is_array($ids)) {
+            $ids = [$ids];
+        }
+        try {
+            if (is_array($ids)) {
+                MarketCustomerFollow::whereIn('id', $ids)->delete();
+            } else {
+                MarketCustomerFollow::where('id', $ids)->delete();
+            }
+        } catch (\Exception $e) {
+            return json_fail('删除失败');
+        }
+
+        return json_success('删除成功');
+    }
+
+    /**
+     * Notes: 转移客户
+     * User: yb
+     * Date: 2024/8/7
+     * Time: 14:27
+     * @param $params
+     */
+    public static function moveCustom($params)
+    {
+        $userId = JwtToken::getCurrentId();
+        $currentConsultantId = $params['consultant_id']; //当前顾问
+        $consultantIds = $params['move_consultant_id'] ?? [];
+        $customIds = $params['move_market_customer_id'] ?? [];
+        $note = $params['note'] ?? '';
+        $consultantInfo = Consultant::firstWhere(['id' => $currentConsultantId]);
+        if (empty($consultantInfo)) {
+            return json_fail('顾问不存在');
+        }
+        $currentDeptId = $consultantInfo->dept_id; //当前部门
+        $logsInertData  = [];
+        $now = time();
+        $whereCustom = [];
+        if (!empty($customIds)) {
+            $whereCustom[] = [function($query) use ($customIds) {
+                $query->whereIn('id', $customIds);
+            }];
+        }
+        if (!empty($consultantIds)) {
+            $whereCustom[] = [function($query) use ($consultantIds) {
+                $query->whereIn('consultant_id', $consultantIds);
+            }];
+        }
+        $customList = MarketCustomer::where($whereCustom)->select(['id','consultant_id','dept_id'])->get();
+        if (!$customList->isEmpty()) {
+            foreach ($customList as$item) {
+                $logsInertData[] = [
+                    'market_customer_id' => $item->id,
+                    'consultant_id' => $currentConsultantId,
+                    'dept_id' => $currentDeptId,
+                    'before_consultant_id' => $item->consultant_id,
+                    'before_dept_id' => $item->dept_id,
+                    'note' => $note,
+                    'change_user_id' => $userId,
+                    'created_at' => $now
+                ];
+            }
+        }
+        if ($customList->isEmpty()) {
+            return json_fail('移交的客户信息不存在');
+        }
+        if (empty($logsInertData)) {
+            return json_fail('移交记录不能为空');
+        }
+        Db::beginTransaction();
+        $result = false;
+        try {
+            foreach ($customList as $item) {
+                $item->consultant_id = $currentConsultantId;
+                $item->dept_id = $currentDeptId;
+                $item->save();
+            }
+            $result = MarketCustomerLogs::insert($logsInertData);
+            Db::commit();
+        }catch (BusinessException|\Exception $e) {
+            Db::rollBack();
+            return json_fail($e->getMessage());
+        }
+        if ($result) {
+            return json_success('移交成功');
+        } else {
+            return json_fail('移交失败');
+        }
+
+    }
+
+    /**
+     * Notes: 转移记录
+     * User: yb
+     * Date: 2024/8/7
+     * Time: 15:33
+     * @param Request $request
+     */
+    public static function moveLogs(Request $request)
+    {
+        $format = $request->get('format', 'normal');
+        $limit = (int)$request->get('pageSize', $format === 'tree' ? 1000 : 10);
+        $limit = $limit <= 0 ? 10 : $limit;
+        $params = $request->get();
+        $page = (int)$request->get('page');
+        $page = $page > 0 ? $page : 1;
+        $platform = 0;
+        $where = [];
+        $whereCustom = [];
+        $whereCurrent = [];
+        $whereBefore = [];
+        $whereOpBack = [];
+        $whereOpFront = [];
+        if (!empty($params['market_customer_id'])) {
+            $where[] = ['market_customer_id', '=', $params['market_customer_id']];
+        }
+        if (!empty($params['consultant_id'])) {
+            $consultantIds = $params['consultant_id'];
+            $where[] = [function($query) use ($consultantIds) {
+                $query->orWhereIn('consultant_id', $consultantIds)->orWhereIn('before_consultant_id', $consultantIds);
+            }];
+        }
+        if (!empty($params['custom'])) {
+            //客户信息
+            $custom = $params['custom'];
+            $whereCustom[] = ['name', 'like', "%{$custom}%"];
+        }
+        if (!empty($params['current_consultant'])) {
+            //当前员工信息
+            $current = $params['current_consultant'];
+            $whereCurrent[] = ['name', 'like', "%{$current}%"];
+        }
+        if (!empty($params['before_consultant'])) {
+            //转移前员工信息
+            $before = $params['before_consultant'];
+            $whereBefore[] = ['name', 'like', "%{$before}%"];
+        }
+        if (!empty($params['platform'])) {
+            $platform = $params['platform'];
+            if ($platform == 1) {
+                //后台
+                $where[] = ['change_user_id', '<>', null];
+                $where[] = ['change_user_id', '=', null];
+            } else {
+                //小程序
+                $where[] = ['change_user_id', '=', null];
+                $where[] = ['change_user_id', '<>', null];
+            }
+        }
+        if (!empty($params['op_name'])) {
+            $opName = $params['op_name'];
+            if (!empty($platform)) {
+                if ($platform == 1) {
+                    $whereOpBack[] = ['user_name', 'like', "%{$opName}%"];
+                } else {
+                    $whereOpFront[] = ['name', 'like', "%{$opName}%"];
+                }
+
+            } else {
+                $whereOpBack[] = [function($query) use ($opName) {
+                    $query->orWhere('user_name', 'like', "%{$opName}%");
+                }];
+                $whereOpFront[] = [function($query) use ($opName) {
+                    $query->orWhere('name', 'like', "%{$opName}%");
+                }];
+            }
+        }
+        $paginator = MarketCustomerLogs::with(['custom', 'beforeMan', 'currentMan', 'opBackPerson', 'opFrontPerson'])
+            ->whereHas('custom', function ($query) use ($whereCustom) {
+                $query->where($whereCustom);
+            })
+            ->whereHas('currentMan', function ($query) use ($whereCurrent) {
+                $query->where($whereCurrent);
+            })
+            ->whereHas('beforeMan', function ($query) use ($whereBefore) {
+                $query->where($whereBefore);
+            })
+            ->where($where)
+            ->orderBy('created_at', 'desc')
+            ->paginate($limit, '*', 'page', $page);
+        $total = $paginator->total();
+        $items = $paginator->items();
+        foreach ($items as &$item) {
+            if (!empty($item->custom)) {
+                $item->custom->mask_mobile = self::handlePhone($item->custom->mobile);
+            }
+        }
+        $data = [
+            'total' => $total,
+            'rows' => $items
+        ];
+        return json_success('success', $data);
+    }
+
+    /**
+     * Notes: 审核客户
+     * User: yb
+     * Date: 2024/8/16
+     * Time: 11:21
+     * @param $params
+     */
+    public static function checkCustom($params)
+    {
+        $adminId = JwtToken::getCurrentId();
+        //查询客户是否已经存在
+        $customId = $params['id'];
+        $checkStatus = $params['check_status'];
+        if (!in_array($checkStatus, [-1,2])) {
+            return json_fail('状态值非法');
+        }
+        $info = MarketCustomer::firstWhere(['id' => $customId]);
+        if (empty($info)) {
+            return json_fail('客户不存在');
+        }
+        $status = $info->check_status;
+        if ($status != 1) {
+            return json_fail('客户不是待审核状态');
+        }
+        $mobile = $info->mobile;
+        if ($checkStatus == 2) {
+            if (MarketCustomer::checkCustomExists($mobile)) {
+                return json_fail('客户已经存在');
+            }
+        }
+        $result = false;
+        Db::beginTransaction();
+        try {
+            $info->check_status = $checkStatus;
+            if ($checkStatus == -1) {
+                //拒绝录入拒绝理由
+                $info->check_note = $params['note'] ?? '无拒绝理由';
+            }
+            $info->check_time = time();
+            $info->check_admin_id = $adminId;
+            $result = $info->save();
+            if ($checkStatus == 2) {
+                //将其他待审的相同手机号的改拒绝
+                $where = [
+                    ['mobile' , '=', $mobile],
+                    ['check_status', '=', 1],
+                    ['id', '<>', $customId]
+                ];
+                MarketCustomer::where($where)->update(['check_time' => time(),'check_status' => -1,'check_note' => '客户已经被其他顾问报备']);
+            }
+            Db::commit();
+        }catch (BusinessException|\Exception $e){
+            Db::rollBack();
+            return json_fail($e->getMessage());
+        }
+        if ($result) {
+            return json_success('审核成功');
+        } else {
+            return json_fail('审核失败');
+        }
+
+
+    }
+
+    /**
+     * Notes: 数据统计分析
+     * User: yb
+     * Date: 2024/8/17
+     * Time: 11:27
+     * @param $params
+     */
+    public static function statisticsIndex(Request $request)
+    {
+        $params = $request->get();
+        $where = self::commonSearch($params);
+        //客户总数
+        $total = MarketCustomer::where($where)->count();
+        //待审核
+        $checkTotal = MarketCustomer::where($where)->where('check_status', 1)->count();
+        //已报备
+        $reportTotal = MarketCustomer::where($where)->where(
+            [[function($query) {
+                $currentTime = time();
+                $diffNums = self::DIFF_TIME;
+                $query->whereRaw("((visit_time + {$diffNums}) - {$currentTime} > 0) AND current_status = 1 AND check_status = 2");
+            }]]
+        )->count();
+        //已来电
+        $mobileTotal = MarketCustomer::where($where)->where([[function($query) {
+            $query->where('check_status', '=', 2)->where('current_status', '=', 1);
+        }]])->count();
+        //已到访
+        $visitTotal = MarketCustomer::where($where)->where([[function($query) {
+            $query->where('check_status', '=', 2)->where('current_status', '=', 2);
+        }]])->count();
+        //已缴费
+        $payTotal = MarketCustomer::where($where)->where([[function($query) {
+            $query->where('check_status', '=', 2)->where('current_status', '=', 3);
+        }]])->count();
+        //已成交
+        $dealTotal = MarketCustomer::where($where)->where([[function($query) {
+            $query->where('check_status', '=', 2)->where('current_status', '=', 4);
+        }]])->count();
+        //已到访 + 已缴费 + 已成交 / 客户总数
+        $rant = 0;
+        if ($total > 0) {
+            $rant = $payTotal + $dealTotal / $total;
+        }
+        $rant = sprintf('%.2f', $rant);
+        $data = [
+            'total' => $total,
+            'check_total' => $checkTotal,
+            'report_total' => $reportTotal,
+            'mobile_total' => $mobileTotal,
+            'visit_total' => $visitTotal,
+            'pay_total' => $payTotal,
+            'deal_total' => $dealTotal,
+            'rant' => $rant,
+        ];
+        return json_success('请求成功', $data);
+
+    }
+
+    /**
+     * Notes: 导出数据
+     * User: yb
+     * Date: 2024/8/17
+     * Time: 13:01
+     */
+    public static function exportData(Request $request)
+    {
+        $currentTime = time();
+        $diffNums = self::DIFF_TIME;
+        $params = $request->get();
+        $where = self::commonSearch($params);
+        if (!empty($params['custom_ids'])) {
+            $customIds = $params['custom_ids'];
+            $where[] = [function($query) use ($customIds){
+                $query->whereIn('id', $customIds);
+            }];
+        }
+        $list = MarketCustomer::where($where)->orderBy('visit_time', 'desc')->get();
+        if (!$list->isEmpty()) {
+            $consultantKeys = [];
+            //查询顾问信息
+            $consultantList = Consultant::select(['id', 'name', 'mobile'])->get();
+            if (!$consultantList->isEmpty()) {
+                foreach ($consultantList->toArray() as $consultantItem) {
+                    $consultantKeys[$consultantItem['id']] = $consultantItem;
+                }
+            }
+            $teamKeys = TeamService::getItemKeys();
+            $returnData = [];
+            $genderData = ['', '男', '女'];
+            $typeData = ['', '来电', '来访'];
+            $currentStatusData = [-1 => '无效客户', 1 => '已来电', 2 => '已到访', 3 => '已缴费', 4 => '已成交'];
+            foreach ($list as &$item) {
+                $visitTime = $item->visit_time;
+                $visitTimeStamp = strtotime($visitTime);
+                $diff = ($visitTimeStamp + $diffNums) - $currentTime;
+                $currentStatus = $item->current_status;//当前状态
+                $checkStatus = $item->check_status;//审核状态
+                if ($checkStatus == 1) {
+                    $currentStatusText = '待审核';
+                }
+                if ($checkStatus == -1) {
+                    $currentStatusText = '已失效';
+                }
+                if ($checkStatus == 2) {
+                    //审核通过
+                    if ($currentStatus > 1) {
+                        $currentStatusText = $currentStatusData[$currentStatus] ?? '';
+                    }
+                    if ($currentStatus == 1) {
+                        if ($diff > 0) {
+                            $currentStatusText = $currentStatusData[$currentStatus] ?? '';
+                        } else {
+                            $currentStatusText = '已失效';
+                        }
+                    }
+                    if ($currentStatus == -1) {
+                        $currentStatusText = '无效客户';
+                    }
+                }
+                $returnData[] = [
+                    'name' => $item->name,
+                    'mobile' => $item->mobile,
+                    'mask_mobile' => self::handlePhone($item->mobile),
+                    'gender' => $genderData[$item->gender] ?? '',
+                    'type' => $typeData[$item->type] ?? '',
+                    'consultant_name' => $consultantKeys[$item->consultant_id]['name'] ?? '',
+                    'consultant_mobile' => $consultantKeys[$item->consultant_id]['mobile'] ?? '',
+                    'team_name' => TeamService::getTeamName($teamKeys, $item->dept_id),
+                    'current_status' => $currentStatusText, //当前状态
+                    'visit_time' => $item->visit_time,
+                    'created_at' => date('Y-m-d H:i:s',strtotime($item->created_at))
+                ];
+            }
+            return json_success('请求成功', $returnData);
+        }
+
+    }
+    /**
+     * Notes: 对手机号加密处理
+     * User: yb
+     * Date: 2024/8/8
+     * Time: 15:41
+     * @param $val
+     * @return string|string[]
+     */
+    public static function handlePhone($val)
+    {
+        return substr_replace($val, '****', 3, 4);
+    }
 }

+ 120 - 6
app/admin/service/consultant/TeamService.php

@@ -8,6 +8,7 @@ use app\model\SysUser;
 use support\Db;
 use support\exception\BusinessException;
 use support\Request;
+use Tinywan\Jwt\JwtToken;
 
 class TeamService
 {
@@ -210,7 +211,21 @@ class TeamService
      */
     public static function parentList()
     {
-        $list = SysDept::where(['dept_category' => self::DEPT_CATEGORY, 'dept_super_id' => 0])->select(['dept_id','dept_name'])->get();
+        $deptIds = TeamService::getIdsByUser(1);
+        $where = [
+            ['dept_category', '=', self::DEPT_CATEGORY],
+            ['dept_super_id', '=',  0]
+        ];
+        if (false === $deptIds) {
+            //无权限
+            $where[] = ['dept_id', '=', 0];
+        } else if (is_array($deptIds)) {
+            //指定团队下的权限
+            $where[] = [function($query) use ($deptIds) {
+                $query->whereIn('dept_id', $deptIds);
+            }];
+        }
+        $list = SysDept::where($where)->select(['dept_id','dept_name'])->get();
         $result = $list->toArray();
         $result[] = [
             'dept_id' => 0,
@@ -220,7 +235,7 @@ class TeamService
     }
 
     /**
-     * Notes: 获取团队信息
+     * Notes: 获取团队信息 只有名字
      * User: yb
      * Date: 2024/8/2
      * Time: 10:00
@@ -228,10 +243,7 @@ class TeamService
      */
     public static function getKeys()
     {
-        $where = [
-            ['dept_category', '=', self::DEPT_CATEGORY]
-        ];
-        $data = SysDept::where($where)->select(['dept_id', 'dept_super_id', 'dept_super_path', 'dept_name'])->get();
+        $data = self::getTeams();
         $keys = [];
         if (!empty($data)) {
             $array = $data->toArray();
@@ -241,4 +253,106 @@ class TeamService
         }
         return $keys;
     }
+
+    /**
+     * Notes: 获取团队信息包含整个item
+     * User: yb
+     * Date: 2024/8/9
+     * Time: 20:31
+     */
+    public static function getItemKeys()
+    {
+        $data = self::getTeams();
+        $keys = [];
+        if (!empty($data)) {
+            foreach ($data->toArray() as $item) {
+                $keys[$item['dept_id']] = $item;
+            }
+        }
+        return $keys;
+    }
+
+    /**
+     * Notes: 根据id获取所有的子集
+     * User: yb
+     * Date: 2024/8/12
+     * Time: 11:34
+     * @param $id
+     */
+    public static function getIds($id, $self = 0)
+    {
+        $where = [
+            ['dept_category', '=', self::DEPT_CATEGORY]
+        ];
+        $deptInfo = SysDept::where('dept_id', $id)->where('dept_category', self::DEPT_CATEGORY)->first();
+        $deptSuperPath = $deptInfo->dept_super_path;
+        $where[] = ['dept_super_path', 'like', "{$deptSuperPath}%"];
+        if ($self == 1) {
+            $where[] = ['dept_id', '<>', $id];
+        }
+        return SysDept::where($where)->pluck('dept_id')->toArray();
+    }
+
+    /**
+     * Notes: 通过用户获取所有子集
+     * User: yb
+     * Date: 2024/8/16
+     * Time: 16:48
+     */
+    public static function getIdsByUser($self = 0)
+    {
+        $userId = JwtToken::getCurrentId();
+        $userInfo = SysUser::firstWhere(['user_id' => $userId]);
+        $joinUserDeptId = $userInfo->join_user_dept_id;
+        //获取部门信息
+        $deptInfo = SysDept::firstWhere(['dept_id' => $joinUserDeptId]);
+        if (empty($deptInfo)) {
+            //无部门信息
+            return false;
+        }
+        if ($deptInfo->dept_category != self::DEPT_CATEGORY) {
+            //超管权限
+            return true;
+        }
+        return self::getIds($joinUserDeptId, $self);
+    }
+
+    /**
+     * Notes: 获取团队完整名称包含所有父集
+     * User: yb
+     * Date: 2024/8/9
+     * Time: 20:43
+     * @param $keys
+     * @param $id
+     * @return string
+     */
+    public static function getTeamName($keys, $id)
+    {
+        if (isset($keys[$id])) {
+            $path = $keys[$id]['dept_super_path'];
+            $array = explode('/', $path);
+            array_shift($array);
+            array_pop($array);
+            $namesArr = [];
+            foreach ($array as $item) {
+                if (!empty($item)) {
+                    if (isset($keys[$item]['dept_name'])) {
+                        $namesArr[] = $keys[$item]['dept_name'];
+                    }
+                }
+            }
+            $name = implode(' - ', $namesArr);
+            return $name;
+        }
+        return '';
+    }
+
+    public static function getTeams()
+    {
+        $where = [
+            ['dept_category', '=', self::DEPT_CATEGORY]
+        ];
+        $data = SysDept::where($where)->select(['dept_id', 'dept_super_id', 'dept_super_path', 'dept_name'])->get();
+        return $data;
+    }
 }

+ 4 - 2
app/admin/validate/consultant/MarketCustomerValidate.php

@@ -14,13 +14,15 @@ class MarketCustomerValidate extends Validate
         'name|客户姓名' => 'require',
         'consultant_id|所属顾问' => 'require',
         'visit_time|访问时间' => 'require',
-        'current_status|当前状态' => 'require'
+        'current_status|当前状态' => 'require',
+        'check_status|审核状态' => 'require'
     ];
 
     protected $message = [];
 
     protected $scene = [
         'add' => ['mobile', 'name', 'consultant_id', 'visit_time', 'current_status'],
-        'update' => ['id', 'mobile', 'name', 'visit_time', 'current_status']
+        'update' => ['id', 'mobile', 'name', 'visit_time', 'current_status'],
+        'check' => ['id', 'check_status']
     ];
 }

+ 45 - 0
app/middleware/WechatAuthCheck.php

@@ -0,0 +1,45 @@
+<?php
+
+
+namespace app\middleware;
+
+
+use Tinywan\Jwt\Exception\JwtTokenException;
+use Tinywan\Jwt\JwtToken;
+use Webman\Http\Request;
+use Webman\Http\Response;
+use Webman\MiddlewareInterface;
+
+class WechatAuthCheck implements MiddlewareInterface
+{
+
+    /**
+     * @inheritDoc
+     */
+    public function process(Request $request, callable $handler): Response
+    {
+        try {
+            // 跳过不需要验证的控制器方法
+            $controller = new \ReflectionClass($request->controller);
+            $notNeedLogin = $controller->getDefaultProperties()['notNeedLogin'] ?? [];
+            if (in_array($request->action, $notNeedLogin)) {
+                return $handler($request);
+            }
+
+            $token = $request->header('Authorization');
+            if (!$token) {
+                throw new JwtTokenException('请先登录~', 500);
+            }
+            if (strpos($token, 'Bearer') === 0) {
+                $token = trim(substr($token, 6));
+            }
+            JwtToken::verify(1, $token);
+            $request->user_id = JwtToken::getCurrentId();
+            $request->userInfo = JwtToken::getExtend();
+
+            return $handler($request);
+        } catch (JwtTokenException $e) {
+            return json_fail($e->getMessage());
+        }
+    }
+}

+ 134 - 1
app/model/MarketCustomer.php

@@ -3,10 +3,10 @@
 
 namespace app\model;
 
-
 use DateTimeInterface;
 use Illuminate\Database\Eloquent\SoftDeletes;
 use support\Model;
+use Illuminate\Database\Eloquent\Casts\Attribute;
 
 class MarketCustomer extends Model
 {
@@ -20,4 +20,137 @@ class MarketCustomer extends Model
     {
         return $date->format('Y-m-d H:i:s');
     }
+
+    protected function visitTime():Attribute
+    {
+        return Attribute::get(function ($value) {
+           return date('Y-m-d H:i:s', $value);
+        });
+    }
+
+    public static function config()
+    {
+        $config = [
+            'requirement' => ['康养公寓', '颐养公寓', '商业', '其他'],
+            'require_area' => ['40㎡以下', '40㎡-50㎡', '51㎡-60㎡', '61㎡-80㎡', '81㎡-100㎡', '101㎡-120㎡', '121㎡-150㎡', '150㎡以上'],
+            'age_range' => ['20岁以下', '21岁-30岁', '31岁-40岁', '41岁-50岁', '51岁-60岁', '61岁-80岁', '80岁以上'],
+            'visit_type' => [
+                'common' => ['', '路过', '分销', '朋友介绍', '安居客、房天下', '微信朋友圈', '户外', '微信公众号', '工地围挡', '老带新', '圈层营销', '自拓'],
+                'type1' => ['100' => '派单', '101' => '城市展厅', '102' => '活动'],
+                'type2' => ['200' => '内渠', '201' => '自渠']
+            ],
+            'focus' => ['位置', '交通', '配套', '价格', '品牌', '适老化', '物业', '医疗', '运营'],
+            'region' => ['唐冶', '历城', '历下', '高新', '天桥', '槐荫', '市中', '其他'],
+            'purpose' => ['自买自用', '自买他用'],
+            'level' => ['A类', 'B类', 'C类', 'D类'],
+            'current_status' => ['首次到访', '已到访', '无效客户', '已成交'],
+        ];
+        //处理config中的visit_type
+        $visitType = [];
+        $visitTypeCommon = $config['visit_type']['common'];
+        $visitType1 = $config['visit_type']['type1'];
+        $visitType2 = $config['visit_type']['type2'];
+        foreach ($visitTypeCommon as $key => $item) {
+            if (!empty($item)) {
+                $visitType['type1']["{$key}"] = $item;
+                $visitType['type2']["{$key}"] = $item;
+            }
+        }
+        $visitType['type1'] += $visitType1;
+        $visitType['type2'] += $visitType2;
+        $config['visit_type'] = $visitType;
+        return $config;
+    }
+
+    /**
+     * Notes: 处理参数
+     * User: yb
+     * Date: 2024/8/6
+     * Time: 11:43
+     * @param $data
+     * @return mixed
+     */
+    public static function handleNumParams($data)
+    {
+        $setField = ['require_area', 'requirement', 'age_range', 'focus', 'region'];
+        foreach ($data as $k => $v) {
+            if (in_array($k, $setField)) {
+                if (is_numeric($v)) {
+                    $data[$k] = $v + 1;
+                } else {
+                    $data[$k] = null;
+                }
+            }
+        }
+        if ($data['type'] == 1) {
+            //来电
+            if (!empty($data['area'])) {
+                $area = $data['area'];
+                $requireArea = 1;
+                if ($area >= 40 && $area < 51) {
+                    $requireArea = 2;
+                } else if ($area >= 51 && $area < 61) {
+                    $requireArea = 3;
+                } else if ($area >= 61 && $area < 81) {
+                    $requireArea = 4;
+                } else if ($area >= 81 && $area < 101) {
+                    $requireArea = 5;
+                } else if ($area >= 101 && $area < 121) {
+                    $requireArea = 6;
+                } else if ($area >= 121 && $area < 151) {
+                    $requireArea = 7;
+                } else if ($area >= 151) {
+                    $requireArea = 8;
+                }
+                $data['require_area'] = $requireArea;
+                $data['age_range'] = null;
+            } else {
+                $data['area'] = null;
+            }
+        } else {
+            //来访
+            $data['area'] = null;
+        }
+        if (!empty($data['visit_time'])) {
+            $data['visit_time'] = strtotime($data['visit_time']);
+        }
+        if (empty($data['visit_type'])) {
+            $data['visit_type'] = null;
+        }
+        if (empty($data['level'])) {
+            $data['level'] = null;
+        }
+        if (empty($data['purpose'])) {
+            $data['purpose'] = null;
+        }
+        return $data;
+    }
+
+    /**
+     * Notes: 检验客户是否可录入
+     * User: yb
+     * Date: 2024/8/9
+     * Time: 21:14
+     * @param $mobile
+     * @param int $id
+     * @return bool
+     */
+    public static function checkCustomExists($mobile, $id = 0)
+    {
+        $where = [
+            ['mobile', '=', $mobile],
+            [function($query) {
+            $query->orWhere(function ($query){
+                $diffNums = (60 * 60 * 24 * 30);
+                $currentTime = time();
+                $query->whereRaw("((visit_time + {$diffNums}) - {$currentTime} > 0)")->where('current_status', '=', 1);
+            })->orWhereIn('current_status', [2,3,4]);
+            }],
+            ['check_status', '=', 2]
+        ];
+        if (!empty($id)) {
+            $where[] = ['id', '<>', $id];
+        }
+        return self::where($where)->exists();
+    }
 }

+ 37 - 0
app/model/MarketCustomerFollow.php

@@ -0,0 +1,37 @@
+<?php
+
+
+namespace app\model;
+
+use DateTimeInterface;
+use Illuminate\Database\Eloquent\Casts\Attribute;
+use support\Model;
+
+class MarketCustomerFollow extends Model
+{
+    protected $table = 'market_customer_follow';
+
+    protected $dateFormat = 'U';
+
+    public function serializeDate(DateTimeInterface $date)
+    {
+        return $date->format('Y-m-d H:i:s');
+    }
+
+    protected function followTime():Attribute
+    {
+        return Attribute::get(function ($value) {
+            return date('Y-m-d H:i:s', $value);
+        });
+    }
+
+    public function custom()
+    {
+        return $this->belongsTo(MarketCustomer::class, 'market_customer_id');
+    }
+
+    public function consultant()
+    {
+        return $this->belongsTo(Consultant::class, 'consultant_id');
+    }
+}

+ 45 - 0
app/model/MarketCustomerLogs.php

@@ -0,0 +1,45 @@
+<?php
+
+
+namespace app\model;
+
+use DateTimeInterface;
+use support\Model;
+
+class MarketCustomerLogs extends Model
+{
+    protected $table = 'market_customer_logs';
+
+    protected $dateFormat = 'U';
+
+    public function serializeDate(DateTimeInterface $date)
+    {
+        return $date->format('Y-m-d H:i:s');
+    }
+
+    public function custom()
+    {
+        return $this->belongsTo(MarketCustomer::class, 'market_customer_id');
+    }
+
+    public function beforeMan()
+    {
+        return $this->belongsTo(Consultant::class, 'before_consultant_id');
+    }
+
+    public function currentMan()
+    {
+        return $this->belongsTo(Consultant::class, 'consultant_id');
+    }
+
+    public function opBackPerson()
+    {
+        return $this->belongsTo(SysUser::class, 'change_user_id');
+    }
+
+    public function opFrontPerson()
+    {
+        return $this->belongsTo(Consultant::class, 'change_consultant_id');
+    }
+
+}

+ 10 - 0
app/wechat/IndexController.php

@@ -0,0 +1,10 @@
+<?php
+
+
+namespace app\wechat;
+
+
+class IndexController
+{
+    protected $validateClass = null;
+}

+ 168 - 0
app/wechat/controller/CustomController.php

@@ -0,0 +1,168 @@
+<?php
+
+
+namespace app\wechat\controller;
+
+
+use app\wechat\IndexController;
+use app\wechat\service\CustomService;
+use app\wechat\validate\CustomValidate;
+use support\Request;
+
+class CustomController extends IndexController
+{
+
+    public function __construct()
+    {
+        $this->validateClass = new CustomValidate();
+    }
+
+    /**
+     * Notes: 选项配置
+     * User: yb
+     * Date: 2024/8/8
+     * Time: 15:08
+     * @return \support\Response
+     */
+    public function options()
+    {
+        return json_success('请求成功', CustomService::config());
+    }
+
+    /**
+     * Notes:新增客户
+     * User: yb
+     * Date: 2024/8/8
+     * Time: 15:08
+     */
+    public function addCustom(Request $request)
+    {
+        $params = $request->post();
+        if (!$this->validateClass->scene('add')->check($params)) {
+            return json_fail($this->validateClass->getError());
+        }
+        return CustomService::add($params);
+    }
+
+    /**
+     * Notes: 客户列表
+     * User: yb
+     * Date: 2024/8/12
+     * Time: 10:57
+     */
+    public function customList(Request $request)
+    {
+        $params = $request->post();
+        return CustomService::index($params);
+    }
+
+    /**
+     * Notes: 编辑客户
+     * User: yb
+     * Date: 2024/8/12
+     * Time: 10:36
+     * @param Request $request
+     */
+    public function editCustom(Request $request)
+    {
+        $params = $request->post();
+        if (!$this->validateClass->scene('update')->check($params)) {
+            return json_fail($this->validateClass->getError());
+        }
+        return CustomService::edit($params);
+    }
+
+    /**
+     * Notes: 客户详情
+     * User: yb
+     * Date: 2024/8/8
+     * Time: 15:48
+     */
+    public function customInfo(Request $request)
+    {
+        $params = $request->post();
+        if (empty($params['id'])) {
+            return json_fail('客户id不能为空');
+        }
+        return CustomService::info($params['id']);
+    }
+
+    /**
+     * Notes: 选择客户
+     * User: yb
+     * Date: 2024/8/13
+     * Time: 15:20
+     * @param Request $request
+     */
+    public function selectList(Request $request)
+    {
+        $params = $request->post();
+        return CustomService::myCustomList($params);
+    }
+
+    /**
+     * Notes: 更新客户状态
+     * User: yb
+     * Date: 2024/8/14
+     * Time: 13:21
+     * @param Request $request
+     */
+    public function updateStatus(Request $request)
+    {
+        $params = $request->post();
+        if (!$this->validateClass->scene('status')->check($params)) {
+            return json_fail($this->validateClass->getError());
+        }
+        return CustomService::updateStatus($params);
+    }
+
+    /**
+     * Notes: 移交客户
+     * User: yb
+     * Date: 2024/8/15
+     * Time: 14:36
+     * @param Request $request
+     */
+    public function moveCustom(Request $request)
+    {
+        $params = $request->post();
+        if (empty($params['consultant_id'])) {
+            return json_fail('请选择目标顾问');
+        }
+        if (empty($params['move_market_customer_id']) && empty($params['move_consultant_id'])) {
+            return json_fail('请选择需要转移的客户或顾问');
+        }
+        return CustomService::moveCustom($params);
+    }
+
+    /**
+     * Notes: 移交记录
+     * User: yb
+     * Date: 2024/8/15
+     * Time: 14:36
+     * @param Request $request
+     */
+    public function moveLogs(Request $request)
+    {
+        $params = $request->post();
+        if (empty($params['custom_id'])) {
+            return json_fail('客户id不能为空');
+        }
+        return CustomService::moveLogs($params);
+    }
+
+    /**
+     * Notes: 审核客户
+     * User: yb
+     * Date: 2024/8/16
+     * Time: 11:38
+     */
+    public function checkCustom(Request $request)
+    {
+        $params = $request->post();
+        if (!$this->validateClass->scene('check')->check($params)) {
+            return json_fail($this->validateClass->getError());
+        }
+        return CustomService::checkCustom($params);
+    }
+}

+ 64 - 0
app/wechat/controller/FollowController.php

@@ -0,0 +1,64 @@
+<?php
+
+
+namespace app\wechat\controller;
+
+
+use app\wechat\IndexController;
+use app\wechat\service\FollowService;
+use app\wechat\validate\FollowValidate;
+use support\Request;
+
+class FollowController extends IndexController
+{
+    public function __construct()
+    {
+        $this->validateClass = new FollowValidate();
+    }
+
+    /**
+     * Notes: 添加跟进记录
+     * User: yb
+     * Date: 2024/8/13
+     * Time: 11:25
+     * @param Request $request
+     */
+    public function addFollow(Request $request)
+    {
+        $params = $request->post();
+        if (!$this->validateClass->check($params)) {
+            return json_fail($this->validateClass->getError());
+        }
+        return FollowService::add($params);
+    }
+
+    /**
+     * Notes: 跟进记录列表
+     * User: yb
+     * Date: 2024/8/13
+     * Time: 15:55
+     * @param Request $request
+     */
+    public function followList(Request $request)
+    {
+        $params = $request->post();
+        return FollowService::index($params);
+    }
+
+    /**
+     * Notes: 跟进记录详情
+     * User: yb
+     * Date: 2024/8/13
+     * Time: 17:00
+     * @param Request $request
+     * @return \support\Response
+     */
+    public function followInfo(Request $request)
+    {
+        $params = $request->post();
+        if (empty($params['id'])) {
+            return json_fail('跟进记录id不能为空');
+        }
+        return FollowService::info($params['id']);
+    }
+}

+ 258 - 0
app/wechat/controller/UserController.php

@@ -0,0 +1,258 @@
+<?php
+
+
+namespace app\wechat\controller;
+
+
+use app\common\Tree;
+use app\wechat\IndexController;
+use app\wechat\service\UserService;
+use support\Request;
+use support\Response;
+
+class UserController extends IndexController
+{
+    public $notNeedLogin = ['login'];
+
+    public function index(Request $request)
+    {
+        $params = $request->post();
+        return UserService::getAll($params);
+    }
+
+    /**
+     * Notes:
+     * User: yb
+     * Date: 2024/8/8
+     * Time: 9:19
+     * @param Request $request
+     * @return \support\Response
+     */
+    public function login(Request $request)
+    {
+        $params = $request->post();
+        if (empty($params['mobile'])) {
+            return json_fail('手机号不能为空');
+        }
+        if (empty($params['password'])) {
+            return json_fail('密码不能为空');
+        }
+        return UserService::login($params);
+    }
+
+    /**
+     * Notes: 验证身份
+     * User: yb
+     * Date: 2024/8/8
+     * Time: 10:08
+     * @param Request $request
+     */
+    public function auth()
+    {
+        return UserService::auth();
+    }
+
+    /**
+     * Notes: 统计分析
+     * User: yb
+     * Date: 2024/8/14
+     * Time: 10:22
+     */
+    public function statistics(Request $request)
+    {
+        $params = $request->post();
+        return UserService::statistics($params);
+    }
+
+    /**
+     * Notes: 客户数量走势
+     * User: yb
+     * Date: 2024/8/17
+     * Time: 17:51
+     */
+    public function customTrend(Request $request)
+    {
+        $params = $request->post();
+        return UserService::customTrend($params);
+    }
+
+    /**
+     * Notes: 用户详情
+     * User: yb
+     * Date: 2024/8/14
+     * Time: 13:34
+     */
+    public function info()
+    {
+        return UserService::info();
+    }
+
+    /**
+     * Notes: 成员详情
+     * User: yb
+     * Date: 2024/8/15
+     * Time: 10:22
+     * @param Request $request
+     * @return Response
+     */
+    public function infoById(Request $request)
+    {
+        $params = $request->post();
+        if (empty($params['id'])) {
+            return json_fail('成员id不能为空');
+        }
+        return UserService::getInfoById($params['id']);
+    }
+
+    /**
+     * Notes: 编辑成员
+     * User: yb
+     * Date: 2024/8/15
+     * Time: 10:27
+     * @param Request $request
+     */
+    public function updateUserById(Request $request)
+    {
+        $params = $request->post();
+        if (empty($params['mobile'])) {
+            return json_fail('手机号不能为空');
+        }
+        if (empty($params['name'])) {
+            return json_fail('姓名不能为空');
+        }
+        if (empty($params['gender'])) {
+            return json_fail('性别不能为空');
+        }
+        if (empty($params['dept_id'])) {
+            return  json_fail('所属部门不能为空');
+        }
+        if (empty($params['status'])) {
+            return  json_fail('状态不能为空');
+        }
+        return UserService::updateById($params);
+    }
+
+    /**
+     * Notes: 更新用户
+     * User: yb
+     * Date: 2024/8/14
+     * Time: 14:29
+     */
+    public function updateUser(Request $request)
+    {
+        $params = $request->post();
+        if (empty($params['mobile'])) {
+            return json_fail('手机号不能为空');
+        }
+        if (empty($params['name'])) {
+            return json_fail('姓名不能为空');
+        }
+        if (empty($params['gender'])) {
+            return json_fail('性别不能为空');
+        }
+        return UserService::update($params);
+    }
+
+    /**
+     * Notes: 删除成员
+     * User: yb
+     * Date: 2024/8/15
+     * Time: 11:38
+     * @param Request $request
+     */
+    public function deleteUser(Request $request)
+    {
+        $params = $request->post();
+        if (empty($params['id'])) {
+            return json_fail('成员id不能为空');
+        }
+        return UserService::del($params['id']);
+    }
+
+    /**
+     * Notes: 设置密码
+     * User: yb
+     * Date: 2024/8/14
+     * Time: 14:44
+     */
+    public function setPassword(Request $request)
+    {
+        $params = $request->post();
+        if (empty($params['old_password'])) {
+            return json_fail('旧密码不能为空');
+        }
+        if (empty($params['new_password'])) {
+            return json_fail('新密码不能为空');
+        }
+        return UserService::editPassword($params);
+    }
+
+    /**
+     * Notes: 我的成员
+     * User: yb
+     * Date: 2024/8/14
+     * Time: 15:24
+     */
+    public function userList(Request $request)
+    {
+        $params = $request->post();
+        return UserService::index($params);
+    }
+
+    /**
+     * Notes: 添加员工
+     * User: yb
+     * Date: 2024/8/15
+     * Time: 9:54
+     * @param Request $request
+     * @return Response
+     */
+    public function addUser(Request $request)
+    {
+        $params = $request->post();
+        if (empty($params['mobile'])) {
+            return json_fail('手机号不能为空');
+        }
+        if (empty($params['name'])) {
+            return json_fail('姓名不能为空');
+        }
+        if (empty($params['gender'])) {
+            return json_fail('性别不能为空');
+        }
+        if (empty($params['dept_id'])) {
+            return  json_fail('所属部门不能为空');
+        }
+        if (empty($params['status'])) {
+            return  json_fail('状态不能为空');
+        }
+        return UserService::add($params);
+    }
+
+    /**
+     * Notes: 我的团队
+     * User: yb
+     * Date: 2024/8/14
+     * Time: 16:38
+     * @return \support\Response
+     */
+    public function teamList()
+    {
+        return $this->formatTree(UserService::getTeamList());
+    }
+
+    protected function formatTree($items): Response
+    {
+        $format_items = [];
+        foreach ($items as $item) {
+            $format_items[] = [
+                'label' => $item->dept_name,
+                'value' => (string)$item->dept_id,
+                'id' => $item->id ?? $item->dept_id,
+                'pid' => $item->pid ?? $item->dept_super_id
+            ];
+        }
+        $tree = new Tree($format_items);
+        return json_success('success', $tree->getTree());
+    }
+
+}

+ 609 - 0
app/wechat/service/CustomService.php

@@ -0,0 +1,609 @@
+<?php
+
+
+namespace app\wechat\service;
+
+
+use app\model\Consultant;
+use app\model\MarketCustomer;
+use app\model\MarketCustomerFollow;
+use app\model\MarketCustomerLogs;
+use app\wechat\validate\CustomValidate;
+use support\Db;
+use support\exception\BusinessException;
+use Tinywan\Jwt\JwtToken;
+
+class CustomService
+{
+    const DIFF_TIME = (60 * 60 * 24 * 30);
+    /**
+     * Notes: 选项配置
+     * User: yb
+     * Date: 2024/8/8
+     * Time: 15:18
+     * @return array
+     */
+    public static function config()
+    {
+        $options = MarketCustomer::config();
+        if (isset($options['visit_type'])) {
+            //如果存在拜访方式,直接在后台处理完成后返回前台
+            foreach ($options['visit_type'] as $key => $val) {
+                $data = [];
+                foreach ($val as $index => $item) {
+                    $data[] = ['text' => $item, 'value' => $index];
+                }
+                $options['visit_type'][$key] = $data;
+            }
+        }
+        return $options;
+    }
+
+    /**
+     * Notes: 客户列表
+     * User: yb
+     * Date: 2024/8/12
+     * Time: 11:08
+     * @param $params
+     */
+    public static function index($params)
+    {
+        $page = $params['page'] ?? 1;
+        $size = $params['size'] ?? 10;
+        //查询顾问信息
+        $consultantId = JwtToken::getCurrentId();
+        $consultantInfo = Consultant::firstWhere(['id' => $consultantId]);
+        $deptId = $consultantInfo->dept_id;
+        $type = $consultantInfo->type;
+        $currentTime = time();
+        $diffNums = self::DIFF_TIME;
+        $where = [];
+        $whereFollow = [];
+        if ($type == 1) {
+            //团队下所有的
+            $deptIds = TeamService::getIds($deptId);
+            $where[] = [function($query) use ($deptIds) {
+                $query->whereIn('dept_id', $deptIds);
+            }];
+            if (!empty($params['consultant_id'])) {
+                $where[] = ['consultant_id', '=', $params['consultant_id']];
+                $whereFollow[] = ['consultant_id', '=', $params['consultant_id']];
+            } else {
+                $consultantIds = UserService::getIds(1);
+                $whereFollow[] = [function($query) use ($consultantIds) {
+                    $query->whereIn('consultant_id', $consultantIds);
+                }];
+            }
+
+        } else {
+            //个人的
+            $where[] = ['consultant_id', '=', $consultantId];
+            $whereFollow[] = ['consultant_id', '=', $consultantId];
+        }
+        if (!empty($params['custom'])) {
+            $keywords = $params['custom'];
+            $where[] = [function($query) use ($keywords) {
+                $query->orWhere('name', 'like', "%{$keywords}%")->orWhere('mobile', 'like', "%{$keywords}%");
+            }];
+        }
+        if (!empty($params['report_status'])) {
+            if ($params['report_status'] == 1) {
+                //已报备
+                $where[] = [function($query) use ($diffNums, $currentTime) {
+                    $query->whereRaw("((visit_time + {$diffNums}) - {$currentTime} > 0)")->where('current_status', '=', 1)->where('check_status', '=', 2);
+                }];
+            } else if ($params['report_status'] == 3) {
+                //已锁定
+                $where[] = [function($query) {
+                    $query->whereIn('current_status', [2,3,4])->where('check_status', '=', 2);
+                }];
+            } else if ($params['report_status'] == 2) {
+                //已失效
+                $where[] = [function($query) use ($diffNums, $currentTime){
+                    $query->orWhere('current_status', '=', -1)->orWhereRaw("((visit_time + {$diffNums}) - {$currentTime} <= 0 AND current_status = 1)");
+                }];
+                $where[] = ['check_status', '=', 2];
+            } else if ($params['report_status'] == '-1') {
+                //待审核
+                $where[] = ['check_status', '=', 1];
+            }
+        }
+        if (!empty($params['type'])) {
+            $where[] = ['type', '=', $params['type']];
+        }
+        if (!empty($params['visit_time'])) {
+            $datetime = $params['visit_time'];
+            $startTime = strtotime($datetime['start'].' 00:00:00');
+            $endTime = strtotime($datetime['end'].' 23:59:59');
+            $where[] = [function($query) use ($startTime, $endTime) {
+                $query->whereBetween('visit_time', [$startTime, $endTime]);
+            }];
+        }
+        if (!empty($params['current_status'])) {
+            $where[] = ['current_status', '=', $params['current_status']];
+        }
+        $paginator = MarketCustomer::where($where)->orderBy('visit_time', 'desc')->paginate($size, '*', 'page', $page);
+        $total = $paginator->total();
+        $items = $paginator->items();
+        if (!empty($items)) {
+            $now = time();
+            foreach ($items as &$item) {
+                $item->mask_mobile = self::handlePhone($item->mobile);
+                $visitTimeInt = strtotime($item->visit_time);
+                $endTime = $visitTimeInt + 60 * 60 * 24 * 30;
+                $visitTimeInt = $endTime - $now;
+                if ($visitTimeInt <= 0) {
+                    $visitTimeInt = 0;
+                }
+                $item->visit_time_int = $visitTimeInt;
+            }
+        }
+        //本日新增
+        $date = date('Y-m-d');
+        $start = strtotime($date.' 00:00:00');
+        $end = strtotime($date.' 23:59:59');
+        $newNums = MarketCustomer::where($where)->whereBetween('created_at', [$start, $end])->count();
+        $followNums = MarketCustomerFollow::where($whereFollow)->whereBetween('created_at', [$start, $end])->count();
+        $data = [
+            'new_follow_num' => $followNums,
+            'new_custom_num' => $newNums,
+            'total' => $total,
+            'rows' => $items
+        ];
+        return json_success('success', $data);
+
+
+    }
+
+    /**
+     * Notes: 我的客户
+     * User: yb
+     * Date: 2024/8/13
+     * Time: 15:14
+     * @param $params
+     */
+    public static function myCustomList($params)
+    {
+        $page = $params['page'] ?? 1;
+        $size = $params['size'] ?? 10;
+        //查询有效顾问信息
+        $consultantId = JwtToken::getCurrentId();
+        $consultantInfo = Consultant::firstWhere(['id' => $consultantId]);
+        $deptId = $consultantInfo->dept_id;
+        $type = $consultantInfo->type;
+        $where = [];
+        if ($type == 1) {
+            //团队下所有的
+            $deptIds = TeamService::getIds($deptId);
+            $where[] = [function($query) use ($deptIds) {
+                $query->whereIn('dept_id', $deptIds);
+            }];
+        } else {
+            //个人的
+            $where[] = ['consultant_id', '=', $consultantId];
+        }
+        $where[] = ['check_status', '=', 2];
+        $where[] = [function($query) {
+            $query->orWhere(function ($query){
+                $diffNums = (60 * 60 * 24 * 30);
+                $currentTime = time();
+                $query->whereRaw("((visit_time + {$diffNums}) - {$currentTime} > 0)")->where('current_status', '=', 1);
+            })->orWhereIn('current_status', [2,3,4]);
+        }];
+        if (!empty($params['custom'])) {
+            $keywords = $params['custom'];
+            $where[] = [function($query) use ($keywords) {
+                $query->orWhere('name', 'like', "%{$keywords}%")->orWhere('mobile', 'like', "%{$keywords}%");
+            }];
+        }
+        $paginator = MarketCustomer::where($where)->orderBy('created_at', 'desc')->paginate($size, ['id','name','mobile','gender'], 'page', $page);
+        $total = $paginator->total();
+        $items = $paginator->items();
+        if (!empty($items)) {
+            foreach ($items as &$item) {
+                $item->mask_mobile = self::handlePhone($item->mobile);
+            }
+        }
+        $data = [
+            'total' => $total,
+            'rows' => $items
+        ];
+        return json_success('success', $data);
+    }
+
+    /**
+     * Notes: 添加客户
+     * User: yb
+     * Date: 2024/8/6
+     * Time: 11:20
+     */
+    public static function add($params)
+    {
+        $params = MarketCustomer::handleNumParams($params);
+        $mobile = $params['mobile'];
+        //查询客户手机号是否已经存在
+        if (MarketCustomer::checkCustomExists($mobile)) {
+            return json_fail('客户已经登记过了');
+        }
+        //查询顾问信息
+        $consultantId = JwtToken::getCurrentId();
+        $consultantInfo = Consultant::firstWhere(['id' => $consultantId]);
+        if (empty($consultantInfo)) {
+            return json_fail('顾问信息不存在');
+        }
+        $deptId = $consultantInfo->dept_id;
+        $insertData = [
+            'name' => $params['name'],
+            'mobile' => $mobile,
+            'consultant_id' => $consultantId,
+            'dept_id' => $deptId,
+            'gender' => $params['gender'] ?? null,
+            'visit_type' => $params['visit_type'] ?? null,
+            'require_area' => $params['require_area'] ?? null,
+            'area' => $params['area'] ?? null,
+            'requirement' => $params['requirement'] ?? null,
+            'age_range' => $params['age_range'] ?? null,
+            'focus' => $params['focus'] ?? null,
+            'region' => $params['region'] ?? null,
+            'purpose' => $params['purpose'] ?? null,
+            'level' => $params['level'] ?? null,
+            'type' => $params['type'] ?? null,
+            'visit_time' => time(),
+            'note' => $params['note'] ?? '',
+            'check_status' => 1,
+            'current_status' => $params['current_status'] ?? null,
+            'created_at' => time()
+        ];
+        if ($insertData['type'] == 1) {
+            $insertData['current_status'] = 1;
+        } else if ($insertData['type'] == 2) {
+            $insertData['current_status'] = 2;
+        }
+        Db::beginTransaction();
+        try {
+            $customId = MarketCustomer::insertGetId($insertData);
+            Db::commit();
+        }catch (BusinessException|\Exception $e){
+            Db::rollBack();
+            return json_fail($e->getMessage());
+        }
+        if ($customId) {
+            return json_success('添加成功');
+        } else {
+            return json_fail('添加失败');
+        }
+    }
+
+    /**
+     * Notes: 编辑客户
+     * User: yb
+     * Date: 2024/8/12
+     * Time: 10:23
+     * @param $params
+     * @return \support\Response
+     */
+    public static function edit($params)
+    {
+        $consultantId = JwtToken::getCurrentId();
+        $params = MarketCustomer::handleNumParams($params);
+        if (empty($params['id'])) {
+            return json_fail('客户id不能为空');
+        }
+        $consultantInfo = Consultant::firstWhere(['id' => $consultantId]);
+        if (empty($consultantInfo)) {
+            return json_fail('顾问信息不存在');
+        }
+        $info = MarketCustomer::firstWhere(['id' => $params['id'], 'consultant_id' => $consultantId]);
+        if (empty($info)) {
+            return json_fail('客户信息不存在');
+        }
+        $updateData = [
+            'name' => $params['name'],
+            'gender' => $params['gender'] ?? null,
+            'visit_type' => $params['visit_type'] ?? null,
+            'require_area' => $params['require_area'] ?? null,
+            'area' => $params['area'] ?? null,
+            'requirement' => $params['requirement'] ?? null,
+            'age_range' => $params['age_range'] ?? null,
+            'focus' => $params['focus'] ?? null,
+            'region' => $params['region'] ?? null,
+            'purpose' => $params['purpose'] ?? null,
+            'level' => $params['level'] ?? null,
+            'type' => $params['type'] ?? null,
+            'note' => $params['note'] ?? '',
+            'updated_at' => time()
+        ];
+        if (empty($params['mobile'])) {
+            return  json_fail('手机号不能为空');
+        }
+        $mobile = $params['mobile'];
+        if (false === strpos($mobile,'****')) {
+            //校验手机号格式
+            $validate = new CustomValidate();
+            if (!$validate->scene('phone')->check(['mobile' => $mobile])) {
+                return  json_fail($validate->getError());
+            }
+            //修改手机号操作
+            if (MarketCustomer::checkCustomExists($mobile, $params['id'])) {
+                return json_fail('客户已经登记过了');
+            }
+            $updateData['mobile'] = $mobile;
+        }
+        $currentStatus = $info->current_status;
+        if ($currentStatus == 1) {
+            //已来电改为已到访
+            if ($params['type'] == 2) {
+                $updateData['current_status'] = 2;
+            }
+        }
+        $result = false;
+        Db::beginTransaction();
+        try {
+            $result = MarketCustomer::where('id', $params['id'])->update($updateData);
+            Db::commit();
+        }catch (BusinessException|\Exception $e){
+            Db::rollBack();
+            return json_fail($e->getMessage());
+        }
+        if ($result) {
+            return json_success('编辑成功');
+        } else {
+            return json_fail('编辑失败');
+        }
+    }
+
+    /**
+     * Notes: 客户详情
+     * User: yb
+     * Date: 2024/8/8
+     * Time: 15:36
+     * @param $params
+     */
+    public static function info($id)
+    {
+        $customInfo = MarketCustomer::firstWhere(['id' => $id]);
+        if (empty($customInfo)) {
+            return json_fail('客户不存在');
+        }
+        //手机号加密处理
+        $customInfo->mask_mobile = self::handlePhone($customInfo->mobile);
+        //查询负责顾问
+        $consultantName = Consultant::where('id', $customInfo->consultant_id)->value('name');
+        $customInfo->consultant_name = $consultantName;
+        //查询最新的跟进记录
+        $follow = MarketCustomerFollow::where('market_customer_id', $id)->orderBy('created_at', 'desc')->first();
+        $customInfo->follow = $follow;
+        return json_success('', $customInfo);
+    }
+
+    /**
+     * Notes: 更新客户状态
+     * User: yb
+     * Date: 2024/8/14
+     * Time: 13:23
+     * @param $params
+     */
+    public static function updateStatus($params)
+    {
+        $customId = $params['id'];
+        $currentStatus = $params['current_status'];
+        if (!in_array($currentStatus, [-1, 3, 4])) {
+            return json_fail('状态值非法');
+        }
+        $info = MarketCustomer::firstWhere(['id' => $customId]);
+        if (empty($info)) {
+            return json_fail('客户不存在');
+        }
+        $info->current_status = $currentStatus;
+        $result = $info->save();
+        if ($result) {
+            return json_success('更新成功');
+        } else {
+            return json_fail('更新失败');
+        }
+    }
+
+    /**
+     * Notes: 转移客户
+     * User: yb
+     * Date: 2024/8/7
+     * Time: 14:27
+     * @param $params
+     */
+    public static function moveCustom($params)
+    {
+        $userId = JwtToken::getCurrentId();
+        //获取绑定的管理信息
+        $userInfo = Consultant::firstWhere(['id' => $userId]);
+        if (empty($userInfo)) {
+            return json_fail('管理员信息不存在');
+        }
+        if ($userInfo->type != 1) {
+            return json_fail('操作权限不足');
+        }
+        $relationUserId = $userInfo->relation_user_id;
+
+        $currentConsultantId = $params['consultant_id']; //当前顾问
+        $consultantId = $params['move_consultant_id'] ?? 0;
+        $customId = $params['move_market_customer_id'] ?? 0;
+        $note = $params['note'] ?? '';
+        $consultantInfo = Consultant::firstWhere(['id' => $currentConsultantId]);
+        if (empty($consultantInfo)) {
+            return json_fail('接收成员不存在');
+        }
+        $currentDeptId = $consultantInfo->dept_id; //当前部门
+        $logsInertData  = [];
+        $now = time();
+        $whereCustom = [];
+        if (!empty($customId)) {
+            $whereCustom[] = ['id', '=', $customId];
+        }
+        if (!empty($consultantId)) {
+            $whereCustom[] = ['consultant_id', '=', $consultantId];
+        }
+        $customList = MarketCustomer::where($whereCustom)->select(['id','consultant_id','dept_id'])->get();
+        if (!$customList->isEmpty()) {
+            foreach ($customList as$item) {
+                $logsInertData[] = [
+                    'market_customer_id' => $item->id,
+                    'consultant_id' => $currentConsultantId,
+                    'dept_id' => $currentDeptId,
+                    'before_consultant_id' => $item->consultant_id,
+                    'before_dept_id' => $item->dept_id,
+                    'note' => $note,
+                    'change_user_id' => $relationUserId,
+                    'change_consultant_id' => $userId,
+                    'created_at' => $now
+                ];
+            }
+        }
+        if ($customList->isEmpty()) {
+            return json_fail('成员没有客户记录');
+        }
+        if (empty($logsInertData)) {
+            return json_fail('移交记录不能为空');
+        }
+        Db::beginTransaction();
+        $result = false;
+        try {
+            foreach ($customList as $item) {
+                $item->consultant_id = $currentConsultantId;
+                $item->dept_id = $currentDeptId;
+                $item->save();
+            }
+            $result = MarketCustomerLogs::insert($logsInertData);
+            Db::commit();
+        }catch (BusinessException|\Exception $e) {
+            Db::rollBack();
+            return json_fail($e->getMessage());
+        }
+        if ($result) {
+            return json_success('移交成功');
+        } else {
+            return json_fail('移交失败');
+        }
+
+    }
+
+    /**
+     * Notes: 转移记录
+     * User: yb
+     * Date: 2024/8/7
+     * Time: 15:33
+     * @param $params
+     */
+    public static function moveLogs($params)
+    {
+        $page = $params['page'] ?? 1;
+        $size = $params['size'] ?? 10;
+        $where = [];
+        if (!empty($params['custom_id'])) {
+            $where[] = ['market_customer_id', '=', $params['custom_id']];
+        }
+        $selectFn = function ($query){
+            $query->select('name', 'mobile', 'id');
+        };
+        $paginator = MarketCustomerLogs::with(['custom' => $selectFn, 'beforeMan' => $selectFn, 'currentMan' => $selectFn])
+            ->where($where)
+            ->orderBy('created_at', 'desc')
+            ->paginate($size, '*', 'page', $page);
+        $total = $paginator->total();
+        $items = $paginator->items();
+        $data = [
+            'total' => $total,
+            'rows' => $items
+        ];
+        return json_success('success', $data);
+    }
+
+    /**
+     * Notes: 审核客户
+     * User: yb
+     * Date: 2024/8/16
+     * Time: 11:21
+     * @param $params
+     */
+    public static function checkCustom($params)
+    {
+        $userId = JwtToken::getCurrentId();
+        $adminId = Consultant::where('id', '=', $userId)->value('relation_user_id');
+        //查询客户是否已经存在
+        $customId = $params['id'];
+        $checkStatus = $params['check_status'];
+        if (!in_array($checkStatus, [-1,2])) {
+            return json_fail('状态值非法');
+        }
+        $info = MarketCustomer::firstWhere(['id' => $customId]);
+        if (empty($info)) {
+            return json_fail('客户不存在');
+        }
+        $status = $info->check_status;
+        if ($status != 1) {
+            return json_fail('客户不是待审核状态');
+        }
+        $mobile = $info->mobile;
+        if ($checkStatus == 2) {
+            if (MarketCustomer::checkCustomExists($mobile)) {
+                return json_fail('客户已经存在');
+            }
+        }
+        $result = false;
+        Db::beginTransaction();
+        try {
+            $info->check_status = $checkStatus;
+            if ($checkStatus == -1) {
+                //拒绝录入拒绝理由
+                $info->check_note = $params['note'] ?? '无拒绝理由';
+            }
+            $info->check_time = time();
+            $info->check_admin_id = $adminId;
+            $info->check_consultant_id = $userId;
+            $result = $info->save();
+            if ($checkStatus == 2) {
+                //将其他待审的相同手机号的改拒绝
+                $where = [
+                    ['mobile' , '=', $mobile],
+                    ['check_status', '=', 1],
+                    ['id', '<>', $customId]
+                ];
+                MarketCustomer::where($where)->update(['check_time' => time(),'check_status' => -1,'check_note' => '客户已经被其他顾问报备']);
+            }
+            Db::commit();
+        }catch (BusinessException|\Exception $e){
+            Db::rollBack();
+            return json_fail($e->getMessage());
+        }
+        if ($result) {
+            return json_success('审核成功');
+        } else {
+            return json_fail('审核失败');
+        }
+
+
+    }
+
+    /**
+     * Notes: 对手机号加密处理
+     * User: yb
+     * Date: 2024/8/8
+     * Time: 15:41
+     * @param $val
+     * @return string|string[]
+     */
+    public static function handlePhone($val)
+    {
+        return substr_replace($val, '****', 3, 4);
+    }
+
+    /**
+     * Notes: 处理拜访时间
+     * User: yb
+     * Date: 2024/8/8
+     * Time: 16:06
+     * @param $val
+     * @return float|int
+     */
+    protected static function handleVisitTime($val) {
+        return (strtotime($val) * 1000);
+    }
+}

+ 162 - 0
app/wechat/service/FollowService.php

@@ -0,0 +1,162 @@
+<?php
+
+
+namespace app\wechat\service;
+
+
+use app\model\MarketCustomer;
+use app\model\MarketCustomerFollow;
+use support\Db;
+use support\exception\BusinessException;
+use Tinywan\Jwt\JwtToken;
+
+class FollowService
+{
+    /**
+     * Notes: 添加跟进记录
+     * User: yb
+     * Date: 2024/8/13
+     * Time: 11:26
+     * @param $params
+     */
+    public static function add($params)
+    {
+        $consultantId = JwtToken::getCurrentId();
+        //查询客户是否存在
+        $where = [
+            ['consultant_id', '=', $consultantId],
+            ['id', '=', $params['market_customer_id']]
+        ];
+        $customInfo = MarketCustomer::where($where)->first();
+        if (empty($customInfo)) {
+            return json_fail('客户不存在');
+        }
+        $currentStatus = $customInfo->current_status;
+        if ($currentStatus == -1) {
+            return json_fail('无效客户,无法跟进');
+        }
+        if ($currentStatus == 1) {
+            $visitTime = $customInfo->visit_time;
+            $now = time();
+            $start = strtotime($visitTime);
+            $end = $start + CustomService::DIFF_TIME;
+            if ($end < $now) {
+                return json_fail('已失效,请重新添加客户信息');
+            }
+        }
+        $insertData = [
+            'market_customer_id' => $params['market_customer_id'],
+            'consultant_id' => $consultantId,
+            'visit_type' => $params['visit_type'],
+            'intention_type' => $params['intention_type'],
+            'follow_time' => strtotime($params['follow_time']),
+            'follow_way' => $params['follow_way'],
+            'follow_content' => $params['follow_content'],
+            'created_at' => time()
+        ];
+        $result = false;
+        Db::beginTransaction();
+        try {
+            if ($currentStatus == 1) {
+                if ($insertData['visit_type'] == 2) {
+                    $customInfo->current_status = 2;
+                }
+            }
+            $customInfo->visit_time = $insertData['follow_time'];
+            $customInfo->save();
+            $result = MarketCustomerFollow::insert($insertData);
+            Db::commit();
+        }catch (BusinessException|\Exception $e) {
+            Db::rollBack();
+            return json_fail($e->getMessage());
+        }
+        if ($result) {
+            return json_success('跟进成功');
+        } else {
+            return json_fail('跟进失败');
+        }
+    }
+
+    /**
+     * Notes: 跟进列表
+     * User: yb
+     * Date: 2024/8/13
+     * Time: 15:56
+     * @param $params
+     */
+    public static function index($params)
+    {
+        $page = $params['page'] ?? 1;
+        $size = $params['size'] ?? 10;
+        $ids = UserService::getIds();
+        $where = [
+            [function($query) use ($ids) {
+            $query->whereIn('consultant_id',$ids);
+            }]
+        ];
+        $whereCustom = [];
+        $whereConsultant = [];
+        if (!empty($params['market_customer_id'])) {
+            $where[] = ['market_customer_id', '=', $params['market_customer_id']];
+        }
+        if (!empty($params['consultant_id'])) {
+            $where[] = ['consultant_id', '=', $params['consultant_id']];
+        }
+        if (!empty($params['follow_way'])) {
+            $where[] = ['follow_way', '=', $params['follow_way']];
+        }
+        if (!empty($params['follow_time'])) {
+            $date = $params['follow_time'];
+            $start = strtotime($date['start'].' 00:00:00');
+            $end = strtotime($date['end'].' 23:59:59');
+            $where[] = [function($query) use ($start, $end) {
+                $query->whereBetween('follow_time', [$start, $end]);
+            }];
+        }
+        if (!empty($params['custom'])) {
+            $custom = $params['custom'];
+            $whereCustom[] = [function($query) use ($custom) {
+                $query->orWhere('name', 'like', "%{$custom}%")->orWhere('mobile', 'like', "%{$custom}%");
+            }];
+        }
+        if (!empty($params['consultant'])) {
+            $consultant = $params['consultant'];
+            $whereConsultant[] = [function($query) use ($consultant) {
+                $query->orWhere('name', 'like', "%{$consultant}%")->orWhere('mobile', 'like', "%{$consultant}%");
+            }];
+        }
+        $paginator = MarketCustomerFollow::with(['custom', 'consultant'])->whereHas('custom', function($query) use ($whereCustom){
+            $query->where($whereCustom);
+        })->whereHas('consultant', function($query) use ($whereConsultant) {
+            $query->where($whereConsultant);
+        })->where($where)->orderBy('follow_time', 'desc')->paginate($size, '*', 'page', $page);
+        $total = $paginator->total();
+        $items = $paginator->items();
+        $data = [
+            'total' => $total,
+            'rows' => $items
+        ];
+        return json_success('success', $data);
+
+    }
+
+    /**
+     * Notes: 跟进详情
+     * User: yb
+     * Date: 2024/8/13
+     * Time: 16:50
+     * @param $params
+     */
+    public static function info($id)
+    {
+        $info = MarketCustomerFollow::with(['custom', 'consultant'])->where('id', $id)->first();
+        if (empty($info)) {
+            return json_fail('跟进记录不存在');
+        }
+        if (!empty($info->custom->mobile)) {
+            $mobile = $info->custom->mobile;
+            $info->custom->mask_mobile = CustomService::handlePhone($mobile);
+        }
+        return json_success('请求成功', $info);
+    }
+}

+ 64 - 0
app/wechat/service/TeamService.php

@@ -0,0 +1,64 @@
+<?php
+
+
+namespace app\wechat\service;
+
+
+use app\model\SysDept;
+
+class TeamService
+{
+    const DEPT_CATEGORY = '获客团队';
+
+    /**
+     * Notes: 根据id获取所有的子集
+     * User: yb
+     * Date: 2024/8/12
+     * Time: 11:34
+     * @param $id
+     */
+    public static function getIds($id)
+    {
+        $where = [
+            ['dept_category', '=', self::DEPT_CATEGORY]
+        ];
+        $deptInfo = SysDept::where('dept_id', $id)->where('dept_category', self::DEPT_CATEGORY)->first();
+        $deptSuperPath = $deptInfo->dept_super_path;
+        $where[] = ['dept_super_path', 'like', "{$deptSuperPath}%"];
+        return SysDept::where($where)->pluck('dept_id')->toArray();
+    }
+
+    /**
+     * Notes: 获取团队信息
+     * User: yb
+     * Date: 2024/8/14
+     * Time: 16:30
+     * @return
+     */
+    public static function getTeams()
+    {
+        $where = [
+            ['dept_category', '=', self::DEPT_CATEGORY]
+        ];
+        $data = SysDept::where($where)->select(['dept_id', 'dept_super_id', 'dept_super_path', 'dept_name'])->get();
+        return $data;
+    }
+
+    /**
+     * Notes: 根据部门id获取部门信息
+     * User: yb
+     * Date: 2024/8/14
+     * Time: 16:35
+     * @param $id
+     */
+    public static function getTeamList($id)
+    {
+        $deptSuperPath = SysDept::where('dept_id', $id)->value('dept_super_path');
+        $deptList = SysDept::where('dept_super_path', 'like', "{$deptSuperPath}%")->where('dept_category', self::DEPT_CATEGORY)->get();
+        if ($deptList->isEmpty()) {
+            return [];
+        } else {
+            return $deptList;
+        }
+    }
+}

+ 671 - 0
app/wechat/service/UserService.php

@@ -0,0 +1,671 @@
+<?php
+
+
+namespace app\wechat\service;
+
+
+use app\model\Consultant;
+use app\model\MarketCustomer;
+use app\model\SysDept;
+use Carbon\Carbon;
+use support\Db;
+use support\exception\BusinessException;
+use Tinywan\Jwt\JwtToken;
+
+class UserService
+{
+    /**
+     * Notes: 获取所有顾问
+     * User: yb
+     * Date: 2024/8/15
+     * Time: 11:51
+     * @param $params
+     */
+    public static function getAll($params)
+    {
+        $ids = self::getIds(1);
+        $where = [
+            [function($query) use ($ids) {
+                $query->whereIn('id', $ids);
+            }]
+        ];
+        if (!empty($params['not_id'])) {
+            $where[] = ['id', '<>', $params['not_id']];
+        }
+        $list = Consultant::where($where)->select(['id','name','mobile','gender','status','dept_id','created_at'])->orderBy('created_at', 'desc')->get();
+        return json_success('请求成功', $list);
+    }
+
+    /**
+     * Notes: 团队成员
+     * User: yb
+     * Date: 2024/8/14
+     * Time: 15:34
+     * @param $params
+     */
+    public static function index($params)
+    {
+        $page = $params['page'] ?? 1;
+        $size = $params['size'] ?? 10;
+        $ids = self::getIds(1);
+        $where = [
+            [function($query) use ($ids) {
+            $query->whereIn('id', $ids);
+            }]
+        ];
+        if (!empty($params['status'])) {
+            $where[] = ['status', '=', $params['status']];
+        }
+        if (!empty($params['consultant'])) {
+            $keywords = $params['consultant'];
+            $where[] = [function($query) use ($keywords) {
+                $query->orWhere('name', 'like', "%{$keywords}%")->orWhere('mobile', 'like', "%{$keywords}%");
+            }];
+        }
+        if (!empty($params['dept_id'])) {
+            $deptIds = $params['dept_id'];
+            $deptId = end($deptIds);
+            $where[] = ['dept_id', '=', $deptId];
+        }
+        if (!empty($params['created_at'])) {
+            $datetime = $params['created_at'];
+            $startTime = strtotime($datetime['start'].' 00:00:00');
+            $endTime = strtotime($datetime['end'].' 23:59:59');
+            $where[] = [function($query) use ($startTime, $endTime) {
+                $query->whereBetween('created_at', [$startTime, $endTime]);
+            }];
+        }
+        $paginator = Consultant::where($where)->orderBy('created_at', 'desc')->paginate($size, ['id','name','mobile','gender','status','dept_id','created_at'], 'page', $page);
+        $total = $paginator->total();
+        $items = $paginator->items();
+        $data = [
+            'total' => $total,
+            'rows' => $items
+        ];
+        return json_success('success', $data);
+    }
+
+    /**
+     * Notes: 删除成员
+     * User: yb
+     * Date: 2024/8/15
+     * Time: 11:39
+     * @param $id
+     */
+    public static function del($id)
+    {
+        if(MarketCustomer::where('consultant_id', $id)->exists()) {
+            return json_fail('成员下存在客户,请移交客户后删除成员');
+        }
+        $result = Consultant::where('id', $id)->delete();
+        if ($result) {
+            return json_success('删除成功');
+        } else {
+            return json_fail('删除失败');
+        }
+    }
+
+    /**
+     * Notes: 成员详情
+     * User: yb
+     * Date: 2024/8/15
+     * Time: 10:19
+     * @param $id
+     */
+    public static function getInfoById($id)
+    {
+        $info = Consultant::firstWhere(['id' => $id]);
+        if (empty($info)) {
+            return json_fail('成员信息不存在');
+        }
+        return json_success('请求成功', $info);
+    }
+
+    /**
+     * Notes: 编辑成员
+     * User: yb
+     * Date: 2024/8/15
+     * Time: 10:28
+     * @param $params
+     */
+    public static function updateById($params)
+    {
+        if (empty($params['id'])) {
+            return json_fail('成员id不能为空');
+        }
+        //查找员工信息
+        $info = Consultant::find($params['id']);
+        $oldDeptId = $info->dept_id;
+        if (empty($info)) {
+            return json_fail('成员不存在');
+        }
+        if (!self::checkMobile($params['mobile'])) {
+            return json_fail('请输入正确的手机号');
+        }
+        //查询员工是否已被注册
+        if (Consultant::where('mobile', $params['mobile'])->where('id', '<>', $params['id'])->exists()) {
+            return json_fail('手机号已存在,成员已被注册');
+        }
+        //校验密码规则
+        if (!empty($params['password'])) {
+            $passwordLen = strlen($params['password']);
+            if ($passwordLen > 20 || $passwordLen < 6) {
+                return json_fail('请输入6-20位密码');
+            }
+            $password = self::handlePassword($params['password']);
+        }
+        //查询上级团队
+        $topDeptId = SysDept::where('dept_id', $params['dept_id'])->where('dept_category', TeamService::DEPT_CATEGORY)->value('dept_super_id');
+        if (!is_numeric($topDeptId)) {
+            return json_fail('团队不存在');
+        }
+        $updateData = [
+            'name' => $params['name'],
+            'mobile' => $params['mobile'],
+            'dept_id' => $params['dept_id'],
+            'top_dept_id' => $topDeptId,
+            'gender' => $params['gender'] ?? 1,
+            'status' => $params['status'] ?? 1,
+            'updated_at' => time()
+        ];
+        if (!empty($params['password'])) {
+            $updateData['password'] = $password;
+        }
+        $result = false;
+        Db::beginTransaction();
+        try {
+            $result =  Consultant::where('id', $params['id'])->update($updateData);
+            if ($updateData['dept_id'] != $oldDeptId) {
+                //修改成员所属部门
+                MarketCustomer::where('consultant_id', '=', $params['id'])->update(['updated_at' => time(), 'dept_id' => $updateData['dept_id']]);
+            }
+            Db::commit();
+        }catch (BusinessException|\Exception $e) {
+            Db::rollBack();
+            return json_fail($e->getMessage());
+        }
+        if ($result) {
+            return json_success('编辑成功');
+        } else {
+            return json_fail('编辑失败');
+        }
+    }
+
+    /**
+     * Notes: 添加员工
+     * User: yb
+     * Date: 2024/8/15
+     * Time: 9:55
+     * @param $params
+     */
+    public static function add($params)
+    {
+        if (!self::checkMobile($params['mobile'])) {
+            return json_fail('请输入正确的手机号');
+        }
+        //查询员工是否已被注册
+        if (Consultant::where('mobile', $params['mobile'])->exists()) {
+            return json_fail('手机号已存在,成员已被注册');
+        }
+        //校验密码规则
+        if (!empty($params['password'])) {
+            $passwordLen = strlen($params['password']);
+            if ($passwordLen > 20 || $passwordLen < 6) {
+                return json_fail('请输入6-20位密码');
+            }
+        }
+        //查询上级团队
+        $topDeptId = SysDept::where('dept_id', $params['dept_id'])->where('dept_category', TeamService::DEPT_CATEGORY)->value('dept_super_id');
+        if (!is_numeric($topDeptId)) {
+            return json_fail('团队不存在');
+        }
+        if (empty($params['password'])) {
+            $showPassword = substr($params['mobile'], 5);
+            $password = self::handlePassword($showPassword);
+        } else {
+            $password = self::handlePassword($params['password']);
+        }
+        $insertData = [
+            'name' => $params['name'],
+            'mobile' => $params['mobile'],
+            'dept_id' => $params['dept_id'],
+            'top_dept_id' => $topDeptId,
+            'gender' => $params['gender'] ?? 1,
+            'password' => $password,
+            'status' => $params['status'] ?? 1,
+            'relation_user_id' => $params['relation_user_id'] ?? null,
+            'type' => 2,
+            'created_at' => time()
+        ];
+        $result = Consultant::insert($insertData);
+        if ($result) {
+            return json_success('新增成功');
+        } else {
+            return json_fail('新增失败');
+        }
+    }
+
+    /**
+     * Notes: 登录
+     * User: yb
+     * Date: 2024/8/8
+     * Time: 9:20
+     * @param $data
+     */
+    public static function login($data)
+    {
+        $password = $data['password'];
+        $mobile = $data['mobile'];
+        $userInfo = Consultant::firstWhere(['mobile' => $mobile]);
+        if (empty($userInfo)) {
+            return json_fail('用户不存在');
+        }
+        //校验密码
+        $confoundPassword = $userInfo->password;
+        if ($confoundPassword != md5(md5($password))) {
+            return json_fail('密码错误');
+        }
+        $status = $userInfo->status;
+        if ($status != 1) {
+            return json_fail('用户已离职');
+        }
+        $extend = [
+            'id' => $userInfo->id,
+            'client' => 'wechat',
+            'name'=> $userInfo->name,
+            'user_type' => $userInfo->type
+        ];
+        $token = JwtToken::generateToken($extend);
+        $token['user_type'] = $extend['user_type'];
+        return json_success('', $token);
+    }
+
+    /**
+     * Notes: 获取身份信息
+     * User: yb
+     * Date: 2024/8/8
+     * Time: 10:09
+     */
+    public static function auth()
+    {
+        $userId = JwtToken::getCurrentId();
+        $userInfo = Consultant::firstWhere(['id' => $userId]);
+        if (empty($userInfo)) {
+            return json_fail('用户不存在');
+        }
+        $status = $userInfo->status;
+        if ($status != 1) {
+            return json_fail('用户已离职');
+        }
+        $type = $userInfo->type;
+        $relationUserId = $userInfo->relation_user_id;
+        return json_success('请求成功', ['type' => $type, 'relation_user_id' => $relationUserId]);
+    }
+
+    /**
+     * Notes: 获取需要查询的ids
+     * User: yb
+     * Date: 2024/8/13
+     * Time: 15:58
+     * @return array
+     */
+    public static function getIds($clearSelf = 0)
+    {
+        $userId = JwtToken::getCurrentId();
+        $userInfo = Consultant::firstWhere(['id' => $userId]);
+        if (empty($userInfo)) {
+            return [];
+        }
+        $ids = [];
+        if ($userInfo->type == 1) {
+            //管理账号
+            $deptId = $userInfo->dept_id;
+            $deptIds = TeamService::getIds($deptId);
+            //团队下的所有员工
+            $where = [
+                [function($query) use ($deptIds) {
+                $query->whereIn('dept_id', $deptIds);
+                }]
+            ];
+            if ($clearSelf == 1) {
+                $where[] = ['id', '<>', $userId];
+            }
+            $ids = Consultant::where($where)->pluck('id')->toArray();
+        } else {
+            //普通账号
+            $ids[] = $userId;
+        }
+        return $ids;
+    }
+
+    /**
+     * Notes: 首页统计分析
+     * User: yb
+     * Date: 2024/8/14
+     * Time: 10:23
+     */
+    public static function statistics($params)
+    {
+        //统计客户总数
+        $userIds = self::getIds();
+        $currentTime = time();
+        $diffNums = CustomService::DIFF_TIME;
+
+        $where = [
+            [function($query) use ($userIds) {
+            $query->whereIn('consultant_id', $userIds);
+            }]
+        ];
+        //客户总量
+        $customNums = MarketCustomer::where($where)->count();
+        if (!empty($params['time'])) {
+            $time = $params['time'];
+            $start = strtotime($time[0]);
+            $end = strtotime($time[1]);
+        } else {
+            $startDate = date('Y-m-01', strtotime(date('Y-m-d')));
+            $endDate = date('Y-m-t', strtotime(date('Y-m-d')));
+            $start = strtotime($startDate.' 00:00:00');
+            $end  = strtotime($endDate.' 23:59:59');
+        }
+        $where[] = [function($query) use ($start, $end) {
+            $query->whereBetween('created_at', [$start, $end]);
+        }];
+        //待审核客户数量
+        $checkCustomNums = MarketCustomer::where($where)->where('check_status', '=', 1)->count();
+        //新增客户
+        $newCustomNums = MarketCustomer::where($where)->count();
+        //已报备客户数
+        $whereReport = [
+            [function($query) use ($diffNums, $currentTime) {
+                $query->whereRaw("((visit_time + {$diffNums}) - {$currentTime} > 0)")->where('current_status', '=', 1);
+            }],
+            ['check_status', '=', 2]
+        ];
+        $reportCustomNums = MarketCustomer::where($where)->where($whereReport)->count();
+        //已到访客户数量
+        $visitCustomNums = MarketCustomer::where($where)->where('current_status', 2)->where('check_status', 2)->count();
+        //已缴费客户数量
+        $payCustomNums = MarketCustomer::where($where)->where('current_status', 3)->where('check_status', 2)->count();
+        //已成交客户数量
+        $dealCustomNums = MarketCustomer::where($where)->where('current_status', 4)->where('check_status', 2)->count();
+        //新增跟进记录数量
+        $newFollowNums = MarketCustomer::where($where)->count();
+        //男女性别占比
+        $whereEffective = [
+            [function($query) {
+                $query->orWhere(function ($query){
+                    $diffNums = (60 * 60 * 24 * 30);
+                    $currentTime = time();
+                    $query->whereRaw("((visit_time + {$diffNums}) - {$currentTime} > 0)")->where('current_status', '=', 1);
+                })->orWhereIn('current_status', [2,3,4]);
+            }],
+            ['check_status', '=', 2]
+        ];
+
+        /* 男女数量 */
+        $genderDataNums = [
+            ['label' => '男', 'value' => 1, 'nums' => 0],
+            ['label' => '女', 'value' => 2, 'nums' => 0],
+        ];
+        $genderData = MarketCustomer::where($where)->select(Db::raw('gender, count(*) as nums'))->where($whereEffective)->groupBy('gender')->get();
+        if (!$genderData->isEmpty()) {
+            $genderData = $genderData->toArray();
+            foreach ($genderDataNums as $genderKey => $genderVal) {
+                foreach ($genderData as $genderItem) {
+                    if ($genderItem['gender'] == $genderVal['value']) {
+                        $genderDataNums[$genderKey]['nums'] = $genderItem['nums'];
+                    }
+                }
+            }
+        }
+        /* 客户级别 */
+        $levelDataNums = [
+            ['label' => 'A类', 'value' => 1, 'nums' => 0],
+            ['label' => 'B类', 'value' => 2, 'nums' => 0],
+            ['label' => 'C类', 'value' => 3, 'nums' => 0],
+            ['label' => 'D类', 'value' => 4, 'nums' => 0],
+            ['label' => '其他', 'value' => "", 'nums' => 0],
+        ];
+        $levelData = MarketCustomer::where($where)->select(Db::raw('level, count(*) as nums'))->where($whereEffective)->groupBy('level')->get();
+        if (!$levelData->isEmpty()) {
+            $levelData = $levelData->toArray();
+            $levelData = array_column($levelData, 'nums', 'level');
+            foreach ($levelDataNums as $levelKey => $levelVal) {
+                if (isset($levelData[$levelVal['value']])) {
+                    $levelDataNums[$levelKey]['nums'] = $levelData[$levelVal['value']];
+                }
+            }
+        }
+        $data = [
+            'custom_nums' => $customNums,
+            'check_custom_nums' => $checkCustomNums,
+            'new_custom_nums' => $newCustomNums,
+            'report_custom_nums' => $reportCustomNums,
+            'visit_custom_nums' => $visitCustomNums,
+            'pay_custom_nums' => $payCustomNums,
+            'deal_custom_nums' => $dealCustomNums,
+            'new_follow_nums' => $newFollowNums,
+            'gender_data_nums' => $genderDataNums,
+            'level_data_nums' => $levelDataNums
+        ];
+        return json_success('请求成功', $data);
+    }
+
+    /**
+     * Notes: 近7日客户走势
+     * User: yb
+     * Date: 2024/8/17
+     * Time: 17:44
+     */
+    public static function customTrend($params)
+    {
+        //统计客户总数
+        $userIds = self::getIds();
+        $where = [
+            [function($query) use ($userIds) {
+                $query->whereIn('consultant_id', $userIds);
+            }],
+            [function($query) {
+                $query->orWhere(function ($query){
+                    $diffNums = (60 * 60 * 24 * 30);
+                    $currentTime = time();
+                    $query->whereRaw("((visit_time + {$diffNums}) - {$currentTime} > 0)")->where('current_status', '=', 1);
+                })->orWhereIn('current_status', [2,3,4]);
+            }],
+            ['check_status', '=', 2]
+        ];
+        $whereTrend = [];
+        if (!empty($params['type'])) {
+            $whereTrend[] = ['current_status', '=', $params['type']];
+        }
+        // 获取当前日期
+        $today = Carbon::now();
+        // 初始化日期数组和数量数组
+        $dates = [date('Y-m-d')];
+        // 循环7次,获取从今天开始往前推7天的日期
+        for ($i = 0; $i < 6; $i++) {
+            // 将日期推回到7天前
+            $date = $today->subDay()->toDateString();
+            // 将日期字符串存入数组
+            $dates[] = $date;
+        }
+        // 逆序日期数组,因为我们是从今天往前计算的
+        $dates = array_reverse($dates);
+        $dateList = [];
+        foreach ($dates as $item) {
+            $dateList[] = ['label' => $item, 'nums' => 0, 'label_sim' => date('m/d', strtotime($item))];
+        }
+        $datesStr = implode('","', $dates);
+        $datesStr = '"'.$datesStr.'"';
+        $dateRaw = Db::raw("DATE_FORMAT(FROM_UNIXTIME(created_at), '%Y-%m-%d') IN ({$datesStr})");
+
+        // 构建查询,使用whereBetween或whereIn根据需要筛选日期
+        $results = MarketCustomer::select(Db::raw("DATE_FORMAT(FROM_UNIXTIME(created_at), '%Y-%m-%d') AS date,count(*) AS nums"))
+            ->where($where)
+            ->where($whereTrend)
+            ->whereRaw($dateRaw)
+            ->groupBy('date')->get();
+        if (!$results->isEmpty()) {
+            $results = $results->toArray();
+            $results = array_column($results, 'nums', 'date');
+            foreach ($dateList as $key => $val) {
+                $dateList[$key]['nums'] = $results[$val['label']] ?? 0;
+            }
+        }
+
+        //总数
+        $customNums = MarketCustomer::where($where)->whereRaw($dateRaw)->count();
+        //到访数
+        $visitCustomNums = MarketCustomer::where($where)->whereRaw($dateRaw)->where('current_status', 2)->count();
+        //已缴费数
+        $payCustomNums = MarketCustomer::where($where)->whereRaw($dateRaw)->where('current_status', 3)->count();
+        //已成交数
+        $dealCustomNums = MarketCustomer::where($where)->whereRaw($dateRaw)->where('current_status', 4)->count();
+        $data = [
+            'dateList' => $dateList,
+            'visit_nums' => $visitCustomNums,
+            'pay_nums' => $payCustomNums,
+            'deal_nums' => $dealCustomNums,
+            'total_nums' => $customNums
+        ];
+        return json_success('请求成功', $data);
+    }
+    /**
+     * Notes: 用户详情
+     * User: yb
+     * Date: 2024/8/14
+     * Time: 13:35
+     */
+    public static function info()
+    {
+        $userId = JwtToken::getCurrentId();
+        $info = Consultant::firstWhere(['id' => $userId]);
+        if (!empty($info)) {
+            $info->dept_name = SysDept::where('dept_id', $info->dept_id)->value('dept_name');
+        }
+        return json_success('请求成功', $info);
+    }
+
+    /**
+     * Notes: 修改个人信息
+     * User: yb
+     * Date: 2024/8/14
+     * Time: 14:19
+     * @param $params
+     */
+    public static function update($params)
+    {
+        $userId = JwtToken::getCurrentId();
+        if (!self::checkMobile($params['mobile'])) {
+            return json_fail('请输入正确的手机号');
+        }
+        //查询员工是否已被注册
+        if (Consultant::where('mobile', $params['mobile'])->where('id', '<>', $userId)->exists()) {
+            return json_fail('手机号已存在');
+        }
+        $info = Consultant::firstWhere(['id' => $userId]);
+        if (empty($info)) {
+            return json_fail('用户不存在');
+        }
+        $info->name = $params['name'];
+        $info->gender = $params['gender'];
+        $info->mobile = $params['mobile'];
+        $result = $info->save();
+        if ($result) {
+            return json_success('修改成功');
+        } else {
+            return json_fail('修改失败');
+        }
+
+    }
+
+    /**
+     * Notes: 修改密码
+     * User: yb
+     * Date: 2024/8/14
+     * Time: 14:20
+     * @param $params
+     */
+    public static function editPassword($params)
+    {
+        $oldPassword = $params['old_password'];
+        $password = $params['new_password'];
+        $rePassword = $params['check_password'] ?? '';
+        $len = mb_strlen($password);
+        if ($len < 6) {
+            return json_fail('密码长度最少为6位');
+        }
+        if ($len > 20) {
+            return json_fail('密码长度最大为20位');
+        }
+        if ($password != $rePassword) {
+            return json_fail('密码与确认密码不一致');
+        }
+        $userId = JwtToken::getCurrentId();
+        $info = Consultant::firstWhere(['id' => $userId]);
+        if (empty($info)) {
+            return json_fail('用户不存在');
+        }
+        //校验密码
+        $confoundPassword = $info->password;
+        if ($confoundPassword != md5(md5($oldPassword))) {
+            return json_fail('旧密码错误');
+        }
+        $info->password = md5(md5($password));
+        $result = $info->save();
+        if ($result) {
+            return json_success('修改成功');
+        } else {
+            return json_fail('修改失败');
+        }
+    }
+
+    /**
+     * Notes: 团队列表
+     * User: yb
+     * Date: 2024/8/14
+     * Time: 16:32
+     */
+    public static function getTeamList()
+    {
+        $userId = JwtToken::getCurrentId();
+        $info = Consultant::firstWhere(['id' => $userId]);
+        if (empty($info)) {
+            return json_success('请求成功');
+        }
+        $deptId = $info->dept_id;
+        $list = TeamService::getTeamList($deptId);
+        return $list;
+    }
+
+    /**
+     * Notes:校验手机号
+     * User: yb
+     * Date: 2024/8/2
+     * Time: 11:12
+     * @param $mobile
+     * @return bool
+     */
+    protected static function checkMobile($mobile)
+    {
+        if (preg_match('/^1[0-9]\d{9}$/', $mobile)) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Notes: 处理密码
+     * User: yb
+     * Date: 2024/8/2
+     * Time: 11:19
+     * @param $password
+     * @return mixed
+     */
+    protected static function handlePassword($password)
+    {
+        return md5(md5($password));
+    }
+}

+ 30 - 0
app/wechat/validate/CustomValidate.php

@@ -0,0 +1,30 @@
+<?php
+
+
+namespace app\wechat\validate;
+
+
+use support\Validate;
+
+class CustomValidate extends Validate
+{
+    protected $rule = [
+        'id|ID' => 'require|integer',
+        'mobile|联系电话' => 'require|mobile',
+        'name|客户姓名' => 'require',
+        'gender|客户性别' => 'require',
+        'type|来访方式' => 'require',
+        'current_status|当前状态' => 'require',
+        'check_status|报备状态' => 'require'
+    ];
+
+    protected $message = [];
+
+    protected $scene = [
+        'add' => ['mobile', 'type','name', 'gender'],
+        'update' => ['id','type', 'name', 'gender'],
+        'phone' => ['mobile'],
+        'status' => ['id', 'current_status'],
+        'check' => ['id', 'check_status']
+    ];
+}

+ 20 - 0
app/wechat/validate/FollowValidate.php

@@ -0,0 +1,20 @@
+<?php
+
+
+namespace app\wechat\validate;
+
+
+use support\Validate;
+
+class FollowValidate extends Validate
+{
+    protected $rule = [
+        'market_customer_id|客户id' => 'require',
+        'visit_type|来访状态' => 'require',
+        'intention_type|客户意向' => 'require',
+        'follow_time|跟进时间' => 'require',
+        'follow_way|跟进方式' => 'require'
+    ];
+
+    protected $message = [];
+}

+ 1 - 0
config/middleware.php

@@ -15,5 +15,6 @@
 return [
     '' => [
         app\middleware\AccessControlCors::class,
+        app\middleware\WechatAuthCheck::class,
     ]
 ];

+ 1 - 0
config/route.php

@@ -14,6 +14,7 @@
 
 require_once base_path('/route/admin.php');
 require_once base_path('/route/api.php');
+require_once base_path('/route/wechat.php');
 
 
 

+ 11 - 0
route/admin.php

@@ -616,6 +616,7 @@ Route::group('/admin', function () {
             Route::post('/update', [\app\admin\controller\consultant\TeamController::class, 'updateDept']);
             Route::delete('/delete', [\app\admin\controller\consultant\TeamController::class, 'delDept']);
             Route::get('/parent', [\app\admin\controller\consultant\TeamController::class, 'parentList']);
+            Route::get('/check', [\app\admin\controller\consultant\TeamController::class, 'checkDept']);
         });
         /* 员工管理 */
         Route::group('/consultant', function (){
@@ -629,6 +630,16 @@ Route::group('/admin', function () {
         Route::group('/custom', function (){
             Route::get('/config', [\app\admin\controller\consultant\CustomController::class, 'getOptionConfig']);
             Route::post('/add', [\app\admin\controller\consultant\CustomController::class, 'addCustom']);
+            Route::post('/update', [\app\admin\controller\consultant\CustomController::class, 'updateCustom']);
+            Route::get('/list', [\app\admin\controller\consultant\CustomController::class, 'select']);
+            Route::delete('/delete', [\app\admin\controller\consultant\CustomController::class, 'deleteCustom']);
+            Route::get('/follow', [\app\admin\controller\consultant\CustomController::class, 'followList']);
+            Route::delete('/deleteFollow', [\app\admin\controller\consultant\CustomController::class, 'deleteFollow']);
+            Route::post('/move', [\app\admin\controller\consultant\CustomController::class, 'moveCustom']);
+            Route::get('/moveLogs', [\app\admin\controller\consultant\CustomController::class, 'moveLogs']);
+            Route::post('/check', [\app\admin\controller\consultant\CustomController::class, 'checkCustom']);
+            Route::get('/statistics', [\app\admin\controller\consultant\CustomController::class, 'statisticsIndex']);
+            Route::get('/export', [\app\admin\controller\consultant\CustomController::class, 'exportData']);
         });
     });
     Route::group('/customer', function () {

+ 37 - 0
route/wechat.php

@@ -0,0 +1,37 @@
+<?php
+use Webman\Route;
+Route::group('/wechat',function (){
+    Route::group('/user', function () {
+        Route::post('/login', [\app\wechat\controller\UserController::class, 'login']);
+        Route::post('/auth', [\app\wechat\controller\UserController::class, 'auth']);
+        Route::post('/statistics', [\app\wechat\controller\UserController::class, 'statistics']);
+        Route::post('/info', [\app\wechat\controller\UserController::class, 'info']);
+        Route::post('/edit', [\app\wechat\controller\UserController::class, 'updateUser']);
+        Route::post('/password', [\app\wechat\controller\UserController::class, 'setPassword']);
+        Route::post('/list', [\app\wechat\controller\UserController::class, 'userList']);
+        Route::post('/team', [\app\wechat\controller\UserController::class, 'teamList']);
+        Route::post('/add', [\app\wechat\controller\UserController::class, 'addUser']);
+        Route::post('/infoById', [\app\wechat\controller\UserController::class, 'infoById']);
+        Route::post('/editById', [\app\wechat\controller\UserController::class, 'updateUserById']);
+        Route::post('/del', [\app\wechat\controller\UserController::class, 'deleteUser']);
+        Route::post('/all', [\app\wechat\controller\UserController::class, 'index']);
+        Route::post('/trend', [\app\wechat\controller\UserController::class, 'customTrend']);
+    });
+    Route::group('/custom', function () {
+        Route::post('/config', [\app\wechat\controller\CustomController::class, 'options']);
+        Route::post('/add', [\app\wechat\controller\CustomController::class, 'addCustom']);
+        Route::post('/info', [\app\wechat\controller\CustomController::class, 'customInfo']);
+        Route::post('/edit', [\app\wechat\controller\CustomController::class, 'editCustom']);
+        Route::post('/list', [\app\wechat\controller\CustomController::class, 'customList']);
+        Route::post('/select', [\app\wechat\controller\CustomController::class, 'selectList']);
+        Route::post('/status', [\app\wechat\controller\CustomController::class, 'updateStatus']);
+        Route::post('/move', [\app\wechat\controller\CustomController::class, 'moveCustom']);
+        Route::post('/logs', [\app\wechat\controller\CustomController::class, 'moveLogs']);
+        Route::post('/check', [\app\wechat\controller\CustomController::class, 'checkCustom']);
+    });
+    Route::group('/follow', function () {
+        Route::post('/add', [\app\wechat\controller\FollowController::class, 'addFollow']);
+        Route::post('/list', [\app\wechat\controller\FollowController::class, 'followList']);
+        Route::post('/info', [\app\wechat\controller\FollowController::class, 'followInfo']);
+    });
+})->middleware(\app\middleware\WechatAuthCheck::class);