diff --git a/app/controller/CustomerController.php b/app/controller/CustomerController.php index fa1b67f..aa17637 100644 --- a/app/controller/CustomerController.php +++ b/app/controller/CustomerController.php @@ -5,6 +5,7 @@ namespace app\controller; use app\model\Channel; use app\model\Customer; use app\model\Follow; +use app\service\UserService; use app\util\Util; use config\constants\Constants; use support\Log; @@ -12,6 +13,13 @@ use support\Request; class CustomerController { + + + /** + * 不需要登录的方法 + */ + protected $noNeedLogin = ['collect']; + /** * 客户分级 * @param Request $request @@ -73,6 +81,41 @@ class CustomerController } + /** + * 待跟进客户列表 + * @param Request $request + * @return \support\Response + */ + public function waitFollow(Request $request) + { + $userId = $request->user['id']; + $filter = Util::getParam($request, 'filter'); + $page = $request->input('page', 1); + $perPage = $request->input('per_page', 10); + $where = [ + ['user_id', '=', $userId], + ['follow_next', '<>', 0], + ]; + + if($filter == 0){ + $where[] = ['follow_next', '<=', time() + 3600 * 24]; + }elseif($filter == 1){ + $where[] = ['follow_next', '<=', time() + 3600 * 24*3]; + }elseif($filter == 2){ + $where[] = ['follow_next', '<=', time() + 3600 * 24*7]; + } + $customer = Customer::where($where) + ->whereNotNull('follow_next') + ->orderBy('follow_next') + ->paginate($perPage, ['*'], 'page', $page); + + foreach ($customer as &$item) { + $item['follow_remaining'] = $item->follow_next-time(); + $item['follow_next'] = date('Y-m-d H:i:s',$item->follow_next); + } + return Util::page($customer); + } + /** * 客户列表 * @param Request $request @@ -83,9 +126,9 @@ class CustomerController $page = $request->input('page', 1); $perPage = $request->input('per_page', 10); - $follow = $request->input('follow',''); //跟进状态 - $keyword = $request->input('keyword',''); //follow_last - $follow_last = $request->input('follow_last',''); //最后跟进:1:当天跟进客户,2:近三天跟进客户,3:近七天跟进客户 + $follow = $request->input('follow', ''); //跟进状态 + $keyword = $request->input('keyword', ''); //follow_last + $follow_last = $request->input('follow_last', ''); //最后跟进:1:当天跟进客户,2:近三天跟进客户,3:近七天跟进客户 $where = []; @@ -93,31 +136,30 @@ class CustomerController if (Util::isEmpty($userId)) { return Util::fail(['user_id' => $userId], trans('user_none')); } - $where[] =['user_id',$userId] ; + $where[] = ['user_id', $userId]; //验证权限 // if ($request->user['role'] != 1) { // $where[] =['user_id',$userId] ; // } $grade = []; if (!Util::isEmpty($follow) && $follow == 0) { - $where[] = ['grade','<=', 2]; + $where[] = ['grade', '<=', 2]; } elseif (!Util::isEmpty($follow) && $follow == 1) { - $grade = [3, 4]; + $grade = [3, 4]; } elseif (!Util::isEmpty($follow) && $follow == 2) { - $where[] = ['grade','=', 5]; + $where[] = ['grade', '=', 5]; } if (!Util::isEmpty($follow_last) && $follow_last == 0) { - $where[] = ['follow_last','>', strtotime('-1 day')]; - }elseif (!Util::isEmpty($follow_last) && $follow_last == 1) { - $where[] = ['follow_last','>', strtotime('-3 day')]; - }elseif (!Util::isEmpty($follow_last) && $follow_last == 2) { - $where[] = ['follow_last','>', strtotime('-7 day')]; + $where[] = ['follow_last', '>', strtotime('-1 day')]; + } elseif (!Util::isEmpty($follow_last) && $follow_last == 1) { + $where[] = ['follow_last', '>', strtotime('-3 day')]; + } elseif (!Util::isEmpty($follow_last) && $follow_last == 2) { + $where[] = ['follow_last', '>', strtotime('-7 day')]; } - - // 执行分页查询 + // 执行分页查询 //关联标签tags //关联标签及分组tags.group $query = Customer::where($where) ->orderBy('id', 'desc'); if (!Util::isEmpty($keyword)) { @@ -131,16 +173,46 @@ class CustomerController $query->whereIn('grade', $grade); } -// return Util::success($query->toSql().serialize($where),$follow); + + $recycle = Util::getParam($request,'recycle', ''); + if ($recycle == 'recycle') { + $query->onlyTrashed(); + } + $data = $query->paginate($perPage, ['*'], 'page', $page); foreach ($data->items() as &$value) { - $value = Customer::getCustomer($value); + $value = Customer::getCustomer($value, $request); } return Util::page($data); } + + /** + * 客户恢复 + * @param Request $request + * @return \support\Response + */ + public function restore(Request $request) + { + $id = Util::getParam($request,'id', ''); + if (Util::isEmpty($id)) { + return Util::fail([], trans('id_empty')); + } + $userId = $request->user['id']; + $where = [ + 'id' => $id, + 'user_id' => $userId, + ]; + $res = Customer::where($where)->restore(); + if($res){ + return Util::success($res, trans('success')); + }else{ + return Util::fail([], trans('fail')); + } + } + /** * 客户详情 * @param Request $request @@ -153,7 +225,7 @@ class CustomerController return Util::fail([], 'id' . trans('not_null')); } - $res = Customer::select('id', 'user_id', 'grade', 'channel_id', 'car_type_id', 'country_id', 'magnitude', 'email', 'phone', 'WhatsApp', 'contacts', 'describe', 'address', 'lat', 'lng', 'follow_next', 'follow_remark', 'sex') + $res = Customer::with('tags')->select('id', 'user_id', 'grade', 'channel_id', 'car_type_id', 'country_id', 'magnitude', 'email', 'phone', 'WhatsApp', 'contacts', 'describe', 'address', 'lat', 'lng', 'follow_next', 'follow_remark', 'sex') ->where('id', $id) ->first(); if ($res) { @@ -162,7 +234,7 @@ class CustomerController return Util::fail([], '记录不存在'); } - $res = Customer::getCustomer($res); + $res = Customer::getCustomer($res, $request, true); return Util::success($res); } @@ -191,7 +263,7 @@ class CustomerController $email = Util::getParam($request, 'email'); $phone = Util::getParam($request, 'phone'); if (Util::isEmpty($contacts) && Util::isEmpty($email) && Util::isEmpty($phone)) { - return Util::fail([], trans('contacts_not_null')); + return Util::fail([], trans('contacts_not_null')); } $data = [ @@ -271,10 +343,11 @@ class CustomerController $data = [ 'follow_next' => Util::getParam($request, 'follow_next', 0), 'follow_remark' => Util::getParam($request, 'follow_remark', 0), + 'warn' => 0, ]; $res = Customer::where('id', $id) - ->update($data); + ->update($data); if ($res) { return Util::success($res, trans('save_success')); @@ -291,8 +364,22 @@ class CustomerController public function delete(Request $request) { $id = Util::getParam($request, 'id'); - $customer = Customer::where('id', $id)->first(); - $res = $customer->delete(); + $force = Util::getParam($request, 'force',''); + + $userId = $request->user['id']; + $where = [ + 'id' => $id, + 'user_id' => $userId, + ]; + $customer = Customer::where($where)->withTrashed()->first(); + if(empty($customer)){ + return Util::fail('', trans('fail')); + } + if($force == 'force'){ + $res = $customer->forceDelete(); + }else{ + $res = $customer->delete(); + } if ($res) { return Util::success($res, trans('success')); } else { @@ -313,19 +400,19 @@ class CustomerController } $userId = $request->user['id']; $created_at = $request->user['created_at']; - $day = ceil((time()-$created_at+1)/60/60/24); + $day = ceil((time() - $created_at + 1) / 60 / 60 / 24); $where[] = ['user_id', '=', $userId]; - $start = strtotime($created.'-01'); + $start = strtotime($created . '-01'); $year = date('Y', $start); - $month = date('n',$start); //获取月份 - $end = mktime(00, 00, 00, $month+1, 1, $year); + $month = date('n', $start); //获取月份 + $end = mktime(00, 00, 00, $month + 1, 1, $year); $where[] = ['created_at', '>=', $start]; $where[] = ['created_at', '<', $end]; $customer = Customer::where($where)->count(); //初步接触 $whereGrade = $where; - $whereGrade[] = ['grade','<=', 2]; + $whereGrade[] = ['grade', '<=', 2]; $customerInit = Customer::where($whereGrade)->count(); //跟进中 @@ -333,7 +420,7 @@ class CustomerController //已成交 $whereGrade = $where; - $whereGrade[] = ['grade','=', 5]; + $whereGrade[] = ['grade', '=', 5]; $customerDeal = Customer::where($whereGrade)->count(); //拜访数 @@ -347,13 +434,13 @@ class CustomerController $follow = Follow::where($whereVisit)->distinct('customer_id')->count(); $res = [ - 'customer'=>$customer, //客户数 - 'visit'=>$visit, //拜访 - 'follow'=>$follow, //跟进 - 'day'=>$day, //注册天数 - 'customer_init'=>$customerInit, //初步接触 - 'customero_fllow'=>$customeroFllow, //跟进中 - 'customer_deal'=>$customerDeal, //已成交 + 'customer' => $customer, //客户数 + 'visit' => $visit, //拜访 + 'follow' => $follow, //跟进 + 'day' => $day, //注册天数 + 'customer_init' => $customerInit, //初步接触 + 'customero_fllow' => $customeroFllow, //跟进中 + 'customer_deal' => $customerDeal, //已成交 ]; if ($res) { return Util::success($res, trans('success')); @@ -362,4 +449,63 @@ class CustomerController } } + /** + * 线索分配 + * @param Request $request + * @return \support\Response + */ + public function collect(Request $request) + { + Log::info('send follow start',$request->all()); + $user_sn = Util::getParam($request, 'user_sn'); //用户sn + $user_email = Util::getParam($request, 'user_email'); //邮箱 + $sn = Util::getParam($request, 'order_no'); //客户sn验证客户唯一性 + $channel_id = Util::getParam($request, 'channel_id'); //渠道 + $country_id = Util::getParam($request, 'country_id'); //国家 + $magnitude = Util::getParam($request, 'magnitude'); //商机 + $describe = Util::getParam($request, 'describe'); //客户详情 + $phone = Util::getParam($request, 'phone'); //用户详情 + $email = Util::getParam($request, 'email'); //用户详情 + $WhatsApp = Util::getParam($request, 'WhatsApp'); + $contacts = Util::getParam($request, 'contacts'); //联系人 + + + if (Util::isEmpty($user_sn)) { + return Util::fail([], '用户sn不能为空'); + } + if (Util::isEmpty($user_email)) { + return Util::fail([], '用户邮箱不能为空'); + } + if (Util::isEmpty($sn)) { + return Util::fail([], '客户sn不能为空'); + } + + $user = UserService::updateSn($user_sn, $user_email); + + if(!$user){ + return Util::fail($user, $user_email.'用户不存在'); + } + + $res = Customer::firstOrCreate( + ['sn' => $sn], + [ + 'user_id' => $user->id, + 'channel_id' => $channel_id, + 'country_id' => $country_id, + 'magnitude' => $magnitude, + 'describe' => $describe, + 'phone' => $phone, + 'email' => $email, + 'WhatsApp' => $WhatsApp, + 'contacts' => $contacts, + ] + ); + + if ($res) { + return Util::success($res, trans('success')); + } else { + return Util::fail($res, trans('fail')); + } + } + } \ No newline at end of file diff --git a/app/controller/FollowController.php b/app/controller/FollowController.php index c4bd59a..c92e599 100644 --- a/app/controller/FollowController.php +++ b/app/controller/FollowController.php @@ -146,6 +146,8 @@ class FollowController $customer = Customer::find($customer_id); $customer->follow_last = strtotime($res->created_at); + $customer->follow_next = null; + $customer->warn = 0; $customer->save(); if ($res) { diff --git a/app/controller/TagGroupController.php b/app/controller/TagGroupController.php new file mode 100644 index 0000000..7edb77a --- /dev/null +++ b/app/controller/TagGroupController.php @@ -0,0 +1,65 @@ +get(); + $languageArr = config('translation.fallback_locale'); + $language = $request->user['language']; + foreach ($list as &$item) { + $item['names'] = unserialize($item['names']); + if (in_array($language, $languageArr)) { + $item['name'] = $item['names'][$language]; + } + foreach ($item['tags'] as &$tag) { + $tag['names'] = unserialize($tag['names']); + if (in_array($language, $languageArr)) { + $item['name'] = $item['names'][$language]; + } + } + } + + return Util::success($list); + } + + + /** + * 更新客户标签关系 + * @param Request $request + * @return \support\Response + */ + public function updatePivot(Request $request) + { + $customerId = Util::getParam($request, 'customer_id'); + $groupId = Util::getParam($request, 'group_id'); + $tagId = Util::getParam($request, 'tag_id'); + if (Util::isEmpty($customerId)) { + return Util::fail([], trans('customer_id') . trans('not_null')); + } + if (Util::isEmpty($groupId)) { + return Util::fail([], trans('tag_group_id') . trans('not_null')); + } + + if(empty($tagId)){ + $res = CustomerTag::where(['customer_id' => $customerId, 'group_id' => $groupId])->delete(); + }else{ + $res = CustomerTag::updateOrCreate(['customer_id' => $customerId, 'group_id' => $groupId], ['tag_id' => $tagId]); + } + + if($res){ + return Util::success($res, trans('success')); + }else{ + return Util::fail($res, trans('fail')); + } + } +} \ No newline at end of file diff --git a/app/controller/UploadController.php b/app/controller/UploadController.php index f13ec13..b080a21 100644 --- a/app/controller/UploadController.php +++ b/app/controller/UploadController.php @@ -115,7 +115,7 @@ class UploadController } $result = Storage::adapter('oss') - ->path('vehicle_album/'.date('Ymd')) + ->path('lingke_crm/'.date('Ymd')) ->size(1024*1024*5) ->extYes(['image/jpeg','image/png']) ->extNo(['image/gif']) diff --git a/app/controller/UserController.php b/app/controller/UserController.php index e225579..a1fb4ff 100644 --- a/app/controller/UserController.php +++ b/app/controller/UserController.php @@ -154,7 +154,7 @@ class UserController 'role' => 1, 'password' => $hashedPassword, 'status' => 0, - 'avatar' => 'lingke_crm/lingke.png' + 'avatar' => 'lingke_crm/logo.png' ]; diff --git a/app/middleware/AuthMiddleware.php b/app/middleware/AuthMiddleware.php index 8c96a0d..8306cc3 100644 --- a/app/middleware/AuthMiddleware.php +++ b/app/middleware/AuthMiddleware.php @@ -54,11 +54,15 @@ class AuthMiddleware implements MiddlewareInterface // 从 Header 获取 Token $token = $request->header('Authorization'); + $language = Util::getParam($request, 'language'); + $language = $language=='zh_CN'?:'en'; //需要登录并且未提供token if ($isLogin && (!$token || !str_starts_with($token, 'Bearer '))) { - return Util::fail('','请先登录',401); + locale($language); + return Util::fail('',trans('user_login'),401); // throw new BusinessException('未提供 Token'.$isLogin, 401); }elseif(!$isLogin){ + locale($language); return $handler($request); } @@ -68,8 +72,11 @@ class AuthMiddleware implements MiddlewareInterface // 验证 Token 并获取用户ID $decoded = JwtUtil::verifyToken($token); - $userId = $decoded->sub; + if (!isset($decoded->sub)) { + return Util::fail('', trans('user_login'),401); + } + $userId = $decoded->sub; $user = User::select(['id','name','sex','role','avatar','email','password','language','created_at']) ->find($userId); if(!$user){ diff --git a/app/model/Customer.php b/app/model/Customer.php index a166ee8..86adea5 100644 --- a/app/model/Customer.php +++ b/app/model/Customer.php @@ -59,11 +59,18 @@ class Customer extends Model return $date->format(Carbon::parse($date)->toDateTimeString()); } - public static function getCustomer($customer) + /** + * @param $customer + * @param $request + * @param $tag 是否返回客户标签 + * @return mixed + */ + public static function getCustomer($customer, $request, $tag = false) { $customer['grade_txt'] = Constants::GRADE[$customer['grade']]; $customer['magnitude_txt'] = Constants::MAGNITUDE[$customer['magnitude']]; $customer['sex_txt'] = Constants::SEX[$customer['sex']]; + $language = $request->user['language'] ?: 'en'; if ($customer['grade']<=2) { $customer['follow'] = 0; @@ -94,6 +101,22 @@ class Customer extends Model $customer['channel_name'] = empty($customer['channel_id'])?'':Channel::where(['id'=>$customer['channel_id']])->value('name'); // $customer['last_follow'] = $last_follow; + if ($tag) { + foreach ($customer['tags'] as &$tag) { + $tag['names'] = unserialize($tag['names']); + $tag['name'] = $tag['names'][$language]; + } + } + return $customer; } + + /** + * 客户的标签 + */ + public function tags() + { + return $this->belongsToMany(Tag::class, 'customer_tag', 'customer_id', 'tag_id') + ->withTimestamps(); // 如果需要记录关联时间 + } } \ No newline at end of file diff --git a/app/model/CustomerTag.php b/app/model/CustomerTag.php new file mode 100644 index 0000000..f3f96a3 --- /dev/null +++ b/app/model/CustomerTag.php @@ -0,0 +1,36 @@ +belongsTo(TagGroup::class, 'group_id', 'id'); + } + + public function customers() + { + return $this->belongsToMany(Customer::class, 'customer_tag', 'tag_id', 'customer_id'); + } +} \ No newline at end of file diff --git a/app/model/TagGroup.php b/app/model/TagGroup.php new file mode 100644 index 0000000..fbba597 --- /dev/null +++ b/app/model/TagGroup.php @@ -0,0 +1,50 @@ +hasMany(Tag::class, 'group_id', 'id'); + } +} \ No newline at end of file diff --git a/app/service/UserService.php b/app/service/UserService.php new file mode 100644 index 0000000..b934a91 --- /dev/null +++ b/app/service/UserService.php @@ -0,0 +1,32 @@ +where('email',$email) + ->first(); + return $user; + if(!$user){ + return false; + } + if($user->sn != $sn){ + UserSn::where(['sn' => $user->sn])->update(['user_id' => 0]); + UserSn::where(['sn' => $sn])->update(['user_id' => $user->id]); + $user->sn = $sn; + $user->save(); + } + return $user; + } +} \ No newline at end of file diff --git a/config/process.php b/config/process.php index fb2d3f1..d093e40 100644 --- a/config/process.php +++ b/config/process.php @@ -22,7 +22,7 @@ return [ 'webman' => [ 'handler' => Http::class, 'listen' => getenv('WEBMAN_LISTEN'), - 'count' => cpu_count() * 1, + 'count' => 8, 'user' => '', 'group' => '', 'reusePort' => false, diff --git a/resource/translations/en/messages.php b/resource/translations/en/messages.php index dffe5e5..2a20ae4 100644 --- a/resource/translations/en/messages.php +++ b/resource/translations/en/messages.php @@ -6,6 +6,7 @@ return [ 'login_success' => ' login success', 'password_error' => ' password error', 'user_none' => ' user none', + 'user_login' => ' Please log in first', 'not_null' => ' not null', 'code_error' => ' code error', 'format_error' => ' format error', @@ -26,4 +27,7 @@ return [ 'email_consistent' => ' Inconsistent email addresses', 'sign_not_null' => ' No affiliated company has been set', 'password_short' => ' Password cannot be less than 6 characters', + 'id_empty' => ' ID cannot be empty', + 'customer_id' => ' customer ID', + 'tag_group_id' => ' tag group ID', ]; \ No newline at end of file diff --git a/resource/translations/zh_CN/messages.php b/resource/translations/zh_CN/messages.php index 09ede6c..1a6428c 100644 --- a/resource/translations/zh_CN/messages.php +++ b/resource/translations/zh_CN/messages.php @@ -6,6 +6,7 @@ return [ 'login_success' => ' 登录成功', 'password_error' => ' 密码错误', 'user_none' => ' 用户不存在', + 'user_login' => ' 请先登录', 'not_null' => ' 不能为空', 'code_error' => ' 验证码错误', 'format_error' => ' 格式错误', @@ -26,4 +27,7 @@ return [ 'email_consistent' => ' 邮箱不一致', 'sign_not_null' => ' 未设置所属公司', 'password_short' => ' 密码不能少于6位', + 'id_empty' => ' id不能为空', + 'customer_id' => ' 客户ID', + 'tag_group_id' => ' 标签组ID', ]; \ No newline at end of file