diff --git a/app/crm/controller/CustomerController.php b/app/crm/controller/CustomerController.php index 4eb3a6b..c54fea7 100644 --- a/app/crm/controller/CustomerController.php +++ b/app/crm/controller/CustomerController.php @@ -198,6 +198,29 @@ class CustomerController extends BaseController } } + /** + * 无用线索举报 + * @param Request $request + * @return \support\Response + */ + public function report(Request $request) + { + $param = $request->all(); + if (Util::isEmpty($param['id'])) { + return $this->error(trans('id_empty')); + } + + $param['user_id'] = $request->user->uid; + $param['corp_abbr'] = $request->basic_data['corp_abbr']; + $res = (new CustomerService())->report($param); + + if($res){ + return $this->success($res, trans('success')); + }else{ + return $this->error(trans('fail')); + } + } + /** * 客户详情 @@ -341,8 +364,10 @@ class CustomerController extends BaseController return $this->error( trans('fail')); } if($force == 'force'){ + //强制删除 $res = $customer->forceDelete(); }else{ + //软删除 $res = $customer->delete(); } if ($res) { diff --git a/app/crm/service/CustomerService.php b/app/crm/service/CustomerService.php index 631da7e..a21eb3e 100644 --- a/app/crm/service/CustomerService.php +++ b/app/crm/service/CustomerService.php @@ -5,10 +5,10 @@ namespace app\crm\service; use app\common\model\Corp; use app\common\model\User; use app\crm\logic\CustomerLogic; -use app\crm\model\CarType; use app\crm\model\Channel; use app\crm\model\Country; use app\crm\model\Customer; +use app\crm\model\CustomerHis; use app\crm\model\Follow; use app\crm\utils\Util; use config\constants\Constants; @@ -37,6 +37,7 @@ class CustomerService }else{ $where[] = ['user_id', $user_id]; } + $where[] = ['status', 1]; $grade = []; @@ -111,6 +112,34 @@ class CustomerService return $customer; } + public function report($params) + { + $id = $params['id']; + $userId = $params['user_id']; + + $where = [ + 'id' => $id, + 'user_id' => $userId, + ]; + $customer = Customer::where($where)->withTrashed()->first(); + if(empty($customer)){ + return false; + } + + CustomerHis::create([ + 'customer_id' => $id, + 'user_his' => $userId, + 'user_id' => 0, + ]); + + $customer->status = 2; + $res = $customer->save(); + + //软删除 + $customer->delete(); + return $res; + } + /** * @param $customer diff --git a/app/crmAdmin/controller/BaseController.php b/app/crmAdmin/controller/BaseController.php new file mode 100644 index 0000000..1c066b5 --- /dev/null +++ b/app/crmAdmin/controller/BaseController.php @@ -0,0 +1,51 @@ +scene($scene); + } + // 执行数据验证 + if (!$validator->check($data)) { + // 验证失败,抛出业务异常,异常信息为验证器返回的错误信息 + throw new BusinessException($validator->getError()); + } + // 验证成功,返回原始数据 + return $data; + } + + public function page($result){ + $current = $result->currentPage(); + $total = $result->total(); + $size = $result->perPage(); + $result = $result->toArray(); + $result['current'] = $current; + $result['total'] = $total; + $result['size'] = $size; + return $result; + } +} \ No newline at end of file diff --git a/app/crmAdmin/controller/CustomerController.php b/app/crmAdmin/controller/CustomerController.php new file mode 100644 index 0000000..6b3bb8a --- /dev/null +++ b/app/crmAdmin/controller/CustomerController.php @@ -0,0 +1,148 @@ +all(); + $corp = Corp::where(['abbr' => $request->basic_data['corp_abbr']])->first(); + $param['corp_abbr'] = $corp->id; +// $param['corp_abbr'] = $request->basic_data['corp_abbr']; + $result = (new CustomerService())->list($param); + + return $this->success($this->page($result)); + } catch (\Throwable $e) { + return $this->error($e->getMessage().$e->getFile().$e->getLine().$e->getTraceAsString()); + } + } + + /** + * 客户导出 + * @param Request $request + * @return \support\Response + */ + public function export(Request $request) + { + try { + $param = $request->all(); +// $corp = Corp::where(['abbr' => $request->basic_data['corp_abbr']])->first(); +// $param['corp_abbr'] = $corp->id; + + $result = (new CustomerService())->export($param); + + // 检查文件是否存在 + if (!file_exists($result['filepath'])) { + return $this->error('导出文件生成失败'); + } + + // 获取文件信息 + $fileSize = filesize($result['filepath']); + $filename = $result['filename']; + + // 设置响应头 + $response = response()->file($result['filepath']); + $response->header('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'); + $response->header('Content-Disposition', 'attachment; filename="' . $filename . '"'); + $response->header('Content-Length', $fileSize); + $response->header('Cache-Control', 'no-cache, must-revalidate'); + $response->header('Pragma', 'no-cache'); + + return $response; + + } catch (\Throwable $e) { + return $this->error($e->getMessage()); + } + } + + /** + * 客户列表 + * @param Request $request + * @return \support\Response + */ + public function audit(Request $request) + { + try { + $param = $request->all(); + if (empty($param['id'])) { + return $this->error('ID'.trans('not_null')); + } + + if (!in_array($param['scene'],['delete','report'])) { + return $this->error('scene 不合法'); + } + $result = (new CustomerService())->audit($param); + + return $this->success($result); + + } catch (\Throwable $e) { + return $this->error($e->getMessage().$e->getFile().$e->getLine().$e->getTraceAsString()); + } + } + + + /** + * 批量转让 + * @param Request $request + * @return \support\Response + */ + public function transfer(Request $request) + { + try { + $param = $request->all(); + if (empty($param['customer_id'])) { + return $this->error('customer_id'.trans('not_null')); + } + if (empty($param['user_id'])) { + return $this->error('user_id'.trans('not_null')); + } + + $result = (new CustomerService())->transfer($param); + + return $this->success($result); + } catch (\Throwable $th) { + return $this->error($th->getMessage()); + } + } + + /** + * 客户详情 + * @param Request $request + * @return \support\Response + */ + public function detail(Request $request) + { + try { + $param = $request->all(); + if (empty($param['id'])) { + return $this->error('id'.trans('not_null')); + } + + $result = (new CustomerService())->detail($param); + + return $this->success($result); + } catch (\Throwable $th) { + return $this->error($th->getMessage()); + } + } + +} \ No newline at end of file diff --git a/app/crmAdmin/logic/CustomerLogic.php b/app/crmAdmin/logic/CustomerLogic.php new file mode 100644 index 0000000..99af464 --- /dev/null +++ b/app/crmAdmin/logic/CustomerLogic.php @@ -0,0 +1,25 @@ +status = $status; + $res = $customer->save(); + return $res; + } + + public function forceDelete(int $id) + { + $customer = Customer::find($id); + //强制删除 + $res = $customer->forceDelete(); + return $res; + } +} \ No newline at end of file diff --git a/app/crmAdmin/service/CustomerService.php b/app/crmAdmin/service/CustomerService.php new file mode 100644 index 0000000..ac766ce --- /dev/null +++ b/app/crmAdmin/service/CustomerService.php @@ -0,0 +1,382 @@ +search($params); + return $customer; + } + + /** + * 查询列表数据 + * @param $params + * @param $export + * @return Customer[]|\Illuminate\Contracts\Pagination\LengthAwarePaginator|\Illuminate\Database\Eloquent\Builder[]|\Illuminate\Database\Eloquent\Collection|\Illuminate\Database\Eloquent\Collection[]|\Illuminate\Pagination\LengthAwarePaginator + */ + public function search($params, $export = false) + { + +// $page = $request->input('page', 1); +// $perPage = $request->input('per_page', 10); + $perPage = $params['size'] ?? 10; + $page = $params['current'] ?? 1; + $keyword = $params['keyword'] ?? ''; + $recycle = $params['recycle'] ?? ''; + $follow = $params['follow'] ?? ''; + $follow_last = $params['follow_last'] ?? ''; + $tagId = $params['tag_id'] ?? []; + $user_id = $params['user_id'] ?? ''; + $seas = $params['seas'] ?? ''; + $scene = $params['scene'] ?? ''; + $where = []; + + if ($scene == 'audit') { + $where[] = ['status', '=', 2]; + } else { + $where[] = ['status', '=', 1]; + } + if ($seas == 'seas' || $scene == 'seas') { + $where[] = ['user_id', 0]; + } +// if($recycle == 'recycle'){ +// $where[] = ['user_id', $user_id]; +// } + + + $grade = []; + if (!Util::isEmpty($follow) && $follow == 0) { + $where[] = ['grade', '<=', 2]; + } elseif (!Util::isEmpty($follow) && $follow == 1) { + $grade = [3, 4]; + } elseif (!Util::isEmpty($follow) && $follow == 2) { + $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')]; + } + + $query = Customer::where($where); + + if (!empty($user_id)) { + $query->whereIn('user_id', explode(',', $user_id)); + } + + if (!empty($tagId)) { + //查询有$tagId 里任意一个标签的客户 + //$query->whereHas('tags', function($query) use ($tagId){ + // $query->whereIn ('tag.id', $tagId); + //}); + //查询有$tagId 里所有标签的客户 + foreach ($tagId as $id) { + $query->whereHas('tags', function ($query) use ($id) { + $query->where('tag.id', $id); + }); + } + } + if (!Util::isEmpty($keyword)) { + $query->where(function ($query) use ($keyword) { + $query->where('contacts', 'like', "%{$keyword}%") + ->orWhere('phone', 'like', "%{$keyword}%"); + }); + } + if (!Util::isEmpty($grade)) { + $query->whereIn('grade', $grade); + } + + if ($recycle == 'recycle' || $scene == 'recycle' || $seas == 'seas' || $scene == 'seas') { + $query->onlyTrashed(); + } + + if (!$export) { + $customer = $query->orderBy('id', 'desc')->paginate($perPage, ['*'], 'page', $page); + } else { + $customer = $query->orderBy('id', 'desc')->get(); + return $customer; + } + if (!$export) { + foreach ($customer as &$item) { + $item = $this->getCustomer($item); + if ($seas == 'seas' || $scene == 'seas') { + $item['seas_time'] = time() - strtotime($item['deleted_at']); + } + if ($scene == 'audit') { //审核时查询举报人姓名 + $customerHis = CustomerHis::where('customer_id', $item->id)->orderBy('id', 'desc')->first(['user_his']); + $customer['user_name'] = empty($customerHis['user_his']) ? '' : User::find($customer['user_his'])->value('name'); + } + } + } + unset($item); + + return $customer; + } + + public function export($params) + { + $customers = $this->search($params, true); + + // 创建新的Spreadsheet对象 + $spreadsheet = new Spreadsheet(); + $sheet = $spreadsheet->getActiveSheet(); + + // 设置表头 + $headers = [ + 'A1' => '客户ID', + 'B1' => '联系人', + 'C1' => '电话', + 'D1' => '邮箱', + 'E1' => 'WhatsApp', + 'F1' => '性别', + 'G1' => '国家/地区', + 'H1' => '渠道来源', + 'I1' => '描述', + 'J1' => '所属人', + 'K1' => '录入人', + ]; + + // 设置表头样式 + foreach ($headers as $cell => $value) { + $sheet->setCellValue($cell, $value); + } + + // 设置表头样式 + $headerStyle = [ + 'font' => [ + 'bold' => true, + 'color' => ['rgb' => 'FFFFFF'], + ], + 'fill' => [ + 'fillType' => Fill::FILL_SOLID, + 'startColor' => ['rgb' => '4472C4'], + ], + 'alignment' => [ + 'horizontal' => Alignment::HORIZONTAL_CENTER, + 'vertical' => Alignment::VERTICAL_CENTER, + ], + 'borders' => [ + 'allBorders' => [ + 'borderStyle' => Border::BORDER_THIN, + 'color' => ['rgb' => '000000'], + ], + ], + ]; + + $sheet->getStyle('A1:K1')->applyFromArray($headerStyle); + + // 填充数据 + $row = 2; + foreach ($customers as $customer) { + // 获取完整的客户信息 + $customerData = $this->getCustomer($customer); + + // 填充数据行 + $sheet->setCellValue('A' . $row, $customerData['id']); + $sheet->setCellValue('B' . $row, $customerData['contacts']); + $sheet->setCellValue('C' . $row, $customerData['phone']); + $sheet->setCellValue('D' . $row, $customerData['email']); + $sheet->setCellValue('E' . $row, $customerData['WhatsApp']); + $sheet->setCellValue('F' . $row, $customerData['sex_txt']); + $sheet->setCellValue('G' . $row, $customerData['country_name']); + $sheet->setCellValue('H' . $row, $customerData['channel_name']); + $sheet->setCellValue('I' . $row, $customerData['describe']); + $sheet->setCellValue('J' . $row, $customerData['user_name']); // 所属人姓名 + $sheet->setCellValue('K' . $row, $customerData['enter_user_name']); + + $row++; + } + + // 设置数据行样式 + $dataStyle = [ + 'borders' => [ + 'allBorders' => [ + 'borderStyle' => Border::BORDER_THIN, + 'color' => ['rgb' => '000000'], + ], + ], + 'alignment' => [ + 'vertical' => Alignment::VERTICAL_CENTER, + ], + ]; + + $lastRow = $row - 1; + if ($lastRow >= 2) { + $sheet->getStyle('A2:K' . $lastRow)->applyFromArray($dataStyle); + } + + // 自动调整列宽 + foreach (range('A', 'K') as $column) { + $sheet->getColumnDimension($column)->setAutoSize(true); + } + + // 设置行高 + $sheet->getRowDimension(1)->setRowHeight(25); + + // 生成文件名 + $filename = '客户数据_' . date('Y-m-d_H-i-s') . '.xlsx'; + + // 创建输出目录 + $exportDir = runtime_path() . '/export/'; + if (!is_dir($exportDir)) { + mkdir($exportDir, 0755, true); + } + + $filepath = $exportDir . $filename; + + // 创建Excel写入器 + $writer = new Xlsx($spreadsheet); + $writer->save($filepath); + + // 返回文件信息 + return [ + 'filename' => $filename, + 'filepath' => $filepath, + 'size' => filesize($filepath), + 'count' => count($customers) + ]; + } + + /** + * @param $params + * @return bool + */ + public function audit($params) + { + + $id = $params['id']; + + $where = [ + 'id' => $id, + ]; + + $logic = new CustomerLogic(); + if ($params['scene'] == 'delete') { + return $logic->forceDelete($params['id']); + } else { + return $logic->status($params['id'], 1); + } + } + + /** + * @param $params + * @return bool + */ + public function transfer($params) + { + try { + Db::beginTransaction(); + $customerId = $params['customer_id']; + $userId = $params['user_id']; + + $query= Customer::whereIn('id',explode(',',$customerId)); + $customer = $query->get(); + + foreach ($customer as $item) { + //记录客户关系变化 + $his = CustomerHis::create([ + 'customer_id' => $item->id, + 'user_his' => $item->user_id, + 'user_id' => $userId, + ]); + } + $res = $query->update(['user_id' => $userId]); + + Db::commit(); + return $res; + } catch (\Throwable $e) { + Db::rollback(); + throw $e; + } + } + + /** + * @param $customer + * @param $request + * @param $tag 是否返回客户标签 + * @return mixed + */ + public static function getCustomer($customer, $language = 'zh_CN', $tag = false) + { + $customer['grade_txt'] = Util::language($language, Constants::GRADE_EN[$customer['grade']], Constants::GRADE_ZH[$customer['grade']], Constants::GRADE_RU[$customer['grade']]); + $customer['magnitude_txt'] = Util::language($language, Constants::MAGNITUDE_EN[$customer['magnitude']], Constants::MAGNITUDE[$customer['magnitude']], Constants::MAGNITUDE_RU[$customer['magnitude']]); + $customer['sex_txt'] = Util::language($language, Constants::SEX_EN[$customer['sex']], Constants::SEX[$customer['sex']], Constants::SEX_RU[$customer['sex']]); + + if ($customer['grade'] <= 2) { + $customer['follow'] = 0; + $customer['follow_txt'] = Util::language($language, 'Following', '跟进中', 'Продолжение'); + } elseif ($customer['grade'] < 5) { + $customer['follow'] = 1; + $customer['follow_txt'] = Util::language($language, 'transacted', '已成交', 'Транзакция завершена'); + } else { + $customer['follow'] = 2; + $customer['follow_txt'] = Util::language($language, 'lost', '已流失', 'Потерянный'); + } + + $where = [ + 'customer_id' => $customer['id'] + ]; + $begin_follow = Follow::select('id', 'created_at', 'updated_at') + ->where($where) + ->min('created_at'); + + + $customer['begin_follow'] = Util::timeTotDate($begin_follow); +// $customer['created_at'] = Util::timeTotDate($customer['created_at']); +// $customer['updated_at'] = Util::timeTotDate($customer['updated_at']); + + $customer = Util::formatDate($customer, ['follow_last']); + $nameLang = $language == 'en' ? 'name_en' : ($language == 'ru' ? 'name_ru' : 'name'); + $countryLang = $language == 'en' ? 'en_addr' : 'cn_addr'; +// $customer['car_type_name'] = empty($customer['car_type_id'])?'':CarType::where(['id'=>$customer['car_type_id']])->value($nameLang); + $customer['country_name'] = empty($customer['country_id']) ? '' : Country::where(['id' => $customer['country_id']])->value($countryLang); + $customer['channel_name'] = empty($customer['channel_id']) ? '' : Channel::where(['id' => $customer['channel_id']])->value($nameLang); +// $customer['last_follow'] = $last_follow; + + if ($tag) { + foreach ($customer['tags'] as &$tag) { + $tag['names'] = unserialize($tag['names']); + $tag['name'] = $tag['names'][$language]; + } + } + + $customer['enter_user_name'] = empty($customer['enter_user_id']) ? '' : User::find($customer['enter_user_id'])->value('name'); + $customer['user_name'] = empty($customer['user_id']) ? '公海' : User::find($customer['user_id'])->value('name'); + + return $customer; + } + + + public function detail($params) + { + $id = $params['id']; + $customer = Customer::with('tags') + ->where('id', $id) + ->first(); + $customer = $this->getCustomer($customer); + + return $customer; + } +} \ No newline at end of file diff --git a/config/route.php b/config/route.php index 303c4aa..90b9b16 100644 --- a/config/route.php +++ b/config/route.php @@ -90,6 +90,17 @@ Route::group('/admin', function () { Route::any('/release', [app\inquiry\controller\InquiryLogController::class, 'release']); Route::any('/allocation', [app\inquiry\controller\InquiryLogController::class, 'allocation']); }); + + // crm + Route::group('/crm', function () { + Route::group('/customer', function () { + Route::any('/list', [app\crmAdmin\controller\CustomerController::class, 'list']); + Route::any('/export', [app\crmAdmin\controller\CustomerController::class, 'export']); + Route::any('/audit', [app\crmAdmin\controller\CustomerController::class, 'audit']); + Route::any('/transfer', [app\crmAdmin\controller\CustomerController::class, 'transfer']); + Route::any('/detail', [app\crmAdmin\controller\CustomerController::class, 'detail']); + }); + }); }); @@ -139,6 +150,7 @@ Route::group('/crm', function () { Route::any('/customer/getChannel', [app\crm\controller\CustomerController::class, 'getChannel']); Route::any('/customer/getCountry', [app\crm\controller\CustomerController::class, 'getCountry']); Route::any('/customer/followNext', [app\crm\controller\CustomerController::class, 'followNext']); + Route::any('/customer/report', [app\crm\controller\CustomerController::class, 'report']); Route::any('/tagGroup/list', [app\crm\controller\TagGroupController::class, 'list']); Route::any('/tagGroup/updatePivot', [app\crm\controller\TagGroupController::class, 'updatePivot']);