From d016376687d787cdc40272ae496b84507a6fe934 Mon Sep 17 00:00:00 2001 From: "WIN-C27C5MSVTPO\\16961" <1696136552@qq.com> Date: Fri, 1 Aug 2025 14:20:12 +0800 Subject: [PATCH 1/4] =?UTF-8?q?=E5=AE=A1=E6=A0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/crm/controller/CustomerController.php | 25 +++ app/crm/service/CustomerService.php | 32 ++- app/crmAdmin/controller/BaseController.php | 51 +++++ .../controller/CustomerController.php | 83 ++++++++ app/crmAdmin/logic/CustomerLogic.php | 24 +++ app/crmAdmin/service/CustomerService.php | 196 ++++++++++++++++++ config/route.php | 9 + 7 files changed, 419 insertions(+), 1 deletion(-) create mode 100644 app/crmAdmin/controller/BaseController.php create mode 100644 app/crmAdmin/controller/CustomerController.php create mode 100644 app/crmAdmin/logic/CustomerLogic.php create mode 100644 app/crmAdmin/service/CustomerService.php diff --git a/app/crm/controller/CustomerController.php b/app/crm/controller/CustomerController.php index 42003c0..2fd8f78 100644 --- a/app/crm/controller/CustomerController.php +++ b/app/crm/controller/CustomerController.php @@ -195,6 +195,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')); + } + } + /** * 客户详情 @@ -337,8 +360,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 b54eeab..df1f7c5 100644 --- a/app/crm/service/CustomerService.php +++ b/app/crm/service/CustomerService.php @@ -2,11 +2,12 @@ namespace app\crm\service; -use app\crm\logic\CustomerLogic; +use app\crmAdmin\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; @@ -35,6 +36,7 @@ class CustomerService }else{ $where[] = ['user_id', $user_id]; } + $where[] = ['status', 1]; $grade = []; @@ -100,6 +102,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..cab0d24 --- /dev/null +++ b/app/crmAdmin/controller/CustomerController.php @@ -0,0 +1,83 @@ +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; +// $param['corp_abbr'] = $request->basic_data['corp_abbr']; + $result = (new CustomerService())->export($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 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()); + } + } + +} \ 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..1ff4651 --- /dev/null +++ b/app/crmAdmin/logic/CustomerLogic.php @@ -0,0 +1,24 @@ +status = $status; + $res = $customer->save(); + return $res; + } + + public function delete(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..f7f57f1 --- /dev/null +++ b/app/crmAdmin/service/CustomerService.php @@ -0,0 +1,196 @@ +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(); + } + + $customer = $query->paginate($perPage, ['*'], 'page', $page); + foreach ($customer as &$item) { + $item = $this->getCustomer($item); + if($seas == 'seas' || $scene == 'seas'){ + $item['seas_time'] = time()-strtotime($item['deleted_at']); + } + } + unset($item); + + return $customer; + } + + /** + * @param $params + * @return bool + */ + public function audit($params){ + + $id = $params['id']; + + $where = [ + 'id' => $id, + ]; + + $logic = new CustomerLogic(); + if($params['scene'] == 'delete'){ + return $logic->delete($params['id']); + }else{ + return $logic->status($params['id'], 2); + } + + + + } + + /** + * @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')->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(); + + return $customer; + } +} \ No newline at end of file diff --git a/config/route.php b/config/route.php index 303c4aa..88720e1 100644 --- a/config/route.php +++ b/config/route.php @@ -90,6 +90,15 @@ 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']); + }); + }); }); From ebfec84fc3f132427d0a3d690c9bc2a447b59723 Mon Sep 17 00:00:00 2001 From: "WIN-C27C5MSVTPO\\16961" <1696136552@qq.com> Date: Fri, 1 Aug 2025 17:18:08 +0800 Subject: [PATCH 2/4] =?UTF-8?q?=E4=B8=8B=E8=BD=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/crm/controller/CustomerController.php | 2 +- .../controller/CustomerController.php | 31 +- app/crmAdmin/logic/CustomerLogic.php | 3 +- app/crmAdmin/service/CustomerService.php | 174 +++++++- composer.json | 3 +- composer.lock | 372 +++++++++++++++++- config/route.php | 1 + 7 files changed, 564 insertions(+), 22 deletions(-) diff --git a/app/crm/controller/CustomerController.php b/app/crm/controller/CustomerController.php index 2fd8f78..6f43f04 100644 --- a/app/crm/controller/CustomerController.php +++ b/app/crm/controller/CustomerController.php @@ -196,7 +196,7 @@ class CustomerController extends BaseController } /** - * 客户恢复 + * 无用线索举报 * @param Request $request * @return \support\Response */ diff --git a/app/crmAdmin/controller/CustomerController.php b/app/crmAdmin/controller/CustomerController.php index cab0d24..bc4b69d 100644 --- a/app/crmAdmin/controller/CustomerController.php +++ b/app/crmAdmin/controller/CustomerController.php @@ -45,14 +45,32 @@ class CustomerController extends BaseController { try { $param = $request->all(); - $corp = Corp::where(['abbr' => $request->basic_data['corp_abbr']])->first(); - $param['corp_abbr'] = $corp->id; -// $param['corp_abbr'] = $request->basic_data['corp_abbr']; +// $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']; - return $this->success($this->page($result)); + // 设置响应头 + $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().$e->getFile().$e->getLine().$e->getTraceAsString()); + return $this->error($e->getMessage()); } } @@ -68,7 +86,8 @@ class CustomerController extends BaseController if (empty($param['id'])) { return $this->error('ID'.trans('not_null')); } - if (in_array($param['scene'],['delete','report'])) { + + if (!in_array($param['scene'],['delete','report'])) { return $this->error('scene 不合法'); } $result = (new CustomerService())->audit($param); diff --git a/app/crmAdmin/logic/CustomerLogic.php b/app/crmAdmin/logic/CustomerLogic.php index 1ff4651..99af464 100644 --- a/app/crmAdmin/logic/CustomerLogic.php +++ b/app/crmAdmin/logic/CustomerLogic.php @@ -15,9 +15,10 @@ class CustomerLogic return $res; } - public function delete(int $id) + public function forceDelete(int $id) { $customer = Customer::find($id); + //强制删除 $res = $customer->forceDelete(); return $res; } diff --git a/app/crmAdmin/service/CustomerService.php b/app/crmAdmin/service/CustomerService.php index f7f57f1..e5f1cf2 100644 --- a/app/crmAdmin/service/CustomerService.php +++ b/app/crmAdmin/service/CustomerService.php @@ -3,6 +3,7 @@ namespace app\crmAdmin\service; use app\common\model\User; +use app\crm\model\CustomerHis; use app\crmAdmin\logic\CustomerLogic; use app\crm\model\CarType; use app\crm\model\Channel; @@ -11,11 +12,28 @@ use app\crm\model\Customer; use app\crm\model\Follow; use app\crm\utils\Util; use config\constants\Constants; +use PhpOffice\PhpSpreadsheet\Spreadsheet; +use PhpOffice\PhpSpreadsheet\Writer\Xlsx; +use PhpOffice\PhpSpreadsheet\Style\Alignment; +use PhpOffice\PhpSpreadsheet\Style\Border; +use PhpOffice\PhpSpreadsheet\Style\Fill; class CustomerService { public function list($params) { + $customer = $this->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; @@ -31,9 +49,9 @@ class CustomerService $where = []; if ($scene == 'audit') { - $where = ['status', 2]; + $where[] = ['status', '=', 2]; }else { - $where = ['status', 1]; + $where[] = ['status', '=', 1]; } if($seas == 'seas' || $scene == 'seas'){ $where[] = ['user_id', 0]; @@ -92,11 +110,22 @@ class CustomerService $query->onlyTrashed(); } - $customer = $query->paginate($perPage, ['*'], 'page', $page); - foreach ($customer as &$item) { - $item = $this->getCustomer($item); - if($seas == 'seas' || $scene == 'seas'){ - $item['seas_time'] = time()-strtotime($item['deleted_at']); + 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); @@ -104,6 +133,130 @@ class CustomerService 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 @@ -118,13 +271,10 @@ class CustomerService $logic = new CustomerLogic(); if($params['scene'] == 'delete'){ - return $logic->delete($params['id']); + return $logic->forceDelete($params['id']); }else{ - return $logic->status($params['id'], 2); + return $logic->status($params['id'], 1); } - - - } /** diff --git a/composer.json b/composer.json index 64b3e58..ec04c9f 100644 --- a/composer.json +++ b/composer.json @@ -43,7 +43,8 @@ "webman/redis-queue": "^2.1", "symfony/translation": "^7.3", "symfony/filesystem": "^7.3", - "workerman/crontab": "^1.0" + "workerman/crontab": "^1.0", + "phpoffice/phpspreadsheet": "^4.5" }, "suggest": { "ext-event": "For better performance. " diff --git a/composer.lock b/composer.lock index 7f84b75..d88df56 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "8ef9d9a07b2be477345002a7623ea951", + "content-hash": "471a31011b58e1e514bdb9c8963055ad", "packages": [ { "name": "aliyuncs/oss-sdk-php", @@ -186,6 +186,85 @@ ], "time": "2024-02-09T16:56:22+00:00" }, + { + "name": "composer/pcre", + "version": "3.3.2", + "source": { + "type": "git", + "url": "https://github.com/composer/pcre.git", + "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/pcre/zipball/b2bed4734f0cc156ee1fe9c0da2550420d99a21e", + "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<1.11.10" + }, + "require-dev": { + "phpstan/phpstan": "^1.12 || ^2", + "phpstan/phpstan-strict-rules": "^1 || ^2", + "phpunit/phpunit": "^8 || ^9" + }, + "type": "library", + "extra": { + "phpstan": { + "includes": [ + "extension.neon" + ] + }, + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Pcre\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "PCRE wrapping library that offers type-safe preg_* replacements.", + "keywords": [ + "PCRE", + "preg", + "regex", + "regular expression" + ], + "support": { + "issues": "https://github.com/composer/pcre/issues", + "source": "https://github.com/composer/pcre/tree/3.3.2" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2024-11-12T16:29:46+00:00" + }, { "name": "defuse/php-encryption", "version": "v2.4.0", @@ -2524,6 +2603,191 @@ ], "time": "2024-12-08T08:18:47+00:00" }, + { + "name": "maennchen/zipstream-php", + "version": "3.1.2", + "source": { + "type": "git", + "url": "https://github.com/maennchen/ZipStream-PHP.git", + "reference": "aeadcf5c412332eb426c0f9b4485f6accba2a99f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/maennchen/ZipStream-PHP/zipball/aeadcf5c412332eb426c0f9b4485f6accba2a99f", + "reference": "aeadcf5c412332eb426c0f9b4485f6accba2a99f", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "ext-zlib": "*", + "php-64bit": "^8.2" + }, + "require-dev": { + "brianium/paratest": "^7.7", + "ext-zip": "*", + "friendsofphp/php-cs-fixer": "^3.16", + "guzzlehttp/guzzle": "^7.5", + "mikey179/vfsstream": "^1.6", + "php-coveralls/php-coveralls": "^2.5", + "phpunit/phpunit": "^11.0", + "vimeo/psalm": "^6.0" + }, + "suggest": { + "guzzlehttp/psr7": "^2.4", + "psr/http-message": "^2.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "ZipStream\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paul Duncan", + "email": "pabs@pablotron.org" + }, + { + "name": "Jonatan Männchen", + "email": "jonatan@maennchen.ch" + }, + { + "name": "Jesse Donat", + "email": "donatj@gmail.com" + }, + { + "name": "András Kolesár", + "email": "kolesar@kolesar.hu" + } + ], + "description": "ZipStream is a library for dynamically streaming dynamic zip files from PHP without writing to the disk at all on the server.", + "keywords": [ + "stream", + "zip" + ], + "support": { + "issues": "https://github.com/maennchen/ZipStream-PHP/issues", + "source": "https://github.com/maennchen/ZipStream-PHP/tree/3.1.2" + }, + "funding": [ + { + "url": "https://github.com/maennchen", + "type": "github" + } + ], + "time": "2025-01-27T12:07:53+00:00" + }, + { + "name": "markbaker/complex", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/MarkBaker/PHPComplex.git", + "reference": "95c56caa1cf5c766ad6d65b6344b807c1e8405b9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/MarkBaker/PHPComplex/zipball/95c56caa1cf5c766ad6d65b6344b807c1e8405b9", + "reference": "95c56caa1cf5c766ad6d65b6344b807c1e8405b9", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "dev-master", + "phpcompatibility/php-compatibility": "^9.3", + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0", + "squizlabs/php_codesniffer": "^3.7" + }, + "type": "library", + "autoload": { + "psr-4": { + "Complex\\": "classes/src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mark Baker", + "email": "mark@lange.demon.co.uk" + } + ], + "description": "PHP Class for working with complex numbers", + "homepage": "https://github.com/MarkBaker/PHPComplex", + "keywords": [ + "complex", + "mathematics" + ], + "support": { + "issues": "https://github.com/MarkBaker/PHPComplex/issues", + "source": "https://github.com/MarkBaker/PHPComplex/tree/3.0.2" + }, + "time": "2022-12-06T16:21:08+00:00" + }, + { + "name": "markbaker/matrix", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/MarkBaker/PHPMatrix.git", + "reference": "728434227fe21be27ff6d86621a1b13107a2562c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/MarkBaker/PHPMatrix/zipball/728434227fe21be27ff6d86621a1b13107a2562c", + "reference": "728434227fe21be27ff6d86621a1b13107a2562c", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "dev-master", + "phpcompatibility/php-compatibility": "^9.3", + "phpdocumentor/phpdocumentor": "2.*", + "phploc/phploc": "^4.0", + "phpmd/phpmd": "2.*", + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0", + "sebastian/phpcpd": "^4.0", + "squizlabs/php_codesniffer": "^3.7" + }, + "type": "library", + "autoload": { + "psr-4": { + "Matrix\\": "classes/src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mark Baker", + "email": "mark@demon-angel.eu" + } + ], + "description": "PHP Class for working with matrices", + "homepage": "https://github.com/MarkBaker/PHPMatrix", + "keywords": [ + "mathematics", + "matrix", + "vector" + ], + "support": { + "issues": "https://github.com/MarkBaker/PHPMatrix/issues", + "source": "https://github.com/MarkBaker/PHPMatrix/tree/3.0.1" + }, + "time": "2022-12-02T22:17:43+00:00" + }, { "name": "monolog/monolog", "version": "2.10.0", @@ -2831,6 +3095,112 @@ }, "time": "2020-10-15T08:29:30+00:00" }, + { + "name": "phpoffice/phpspreadsheet", + "version": "4.5.0", + "source": { + "type": "git", + "url": "https://github.com/PHPOffice/PhpSpreadsheet.git", + "reference": "2ea9786632e6fac1aee601b6e426bcc723d8ce13" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/2ea9786632e6fac1aee601b6e426bcc723d8ce13", + "reference": "2ea9786632e6fac1aee601b6e426bcc723d8ce13", + "shasum": "" + }, + "require": { + "composer/pcre": "^1||^2||^3", + "ext-ctype": "*", + "ext-dom": "*", + "ext-fileinfo": "*", + "ext-gd": "*", + "ext-iconv": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-simplexml": "*", + "ext-xml": "*", + "ext-xmlreader": "*", + "ext-xmlwriter": "*", + "ext-zip": "*", + "ext-zlib": "*", + "maennchen/zipstream-php": "^2.1 || ^3.0", + "markbaker/complex": "^3.0", + "markbaker/matrix": "^3.0", + "php": "^8.1", + "psr/http-client": "^1.0", + "psr/http-factory": "^1.0", + "psr/simple-cache": "^1.0 || ^2.0 || ^3.0" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "dev-main", + "dompdf/dompdf": "^2.0 || ^3.0", + "friendsofphp/php-cs-fixer": "^3.2", + "mitoteam/jpgraph": "^10.3", + "mpdf/mpdf": "^8.1.1", + "phpcompatibility/php-compatibility": "^9.3", + "phpstan/phpstan": "^1.1 || ^2.0", + "phpstan/phpstan-deprecation-rules": "^1.0 || ^2.0", + "phpstan/phpstan-phpunit": "^1.0 || ^2.0", + "phpunit/phpunit": "^10.5", + "squizlabs/php_codesniffer": "^3.7", + "tecnickcom/tcpdf": "^6.5" + }, + "suggest": { + "dompdf/dompdf": "Option for rendering PDF with PDF Writer", + "ext-intl": "PHP Internationalization Functions", + "mitoteam/jpgraph": "Option for rendering charts, or including charts with PDF or HTML Writers", + "mpdf/mpdf": "Option for rendering PDF with PDF Writer", + "tecnickcom/tcpdf": "Option for rendering PDF with PDF Writer" + }, + "type": "library", + "autoload": { + "psr-4": { + "PhpOffice\\PhpSpreadsheet\\": "src/PhpSpreadsheet" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Maarten Balliauw", + "homepage": "https://blog.maartenballiauw.be" + }, + { + "name": "Mark Baker", + "homepage": "https://markbakeruk.net" + }, + { + "name": "Franck Lefevre", + "homepage": "https://rootslabs.net" + }, + { + "name": "Erik Tilt" + }, + { + "name": "Adrien Crivelli" + } + ], + "description": "PHPSpreadsheet - Read, Create and Write Spreadsheet documents in PHP - Spreadsheet engine", + "homepage": "https://github.com/PHPOffice/PhpSpreadsheet", + "keywords": [ + "OpenXML", + "excel", + "gnumeric", + "ods", + "php", + "spreadsheet", + "xls", + "xlsx" + ], + "support": { + "issues": "https://github.com/PHPOffice/PhpSpreadsheet/issues", + "source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/4.5.0" + }, + "time": "2025-07-24T05:15:59+00:00" + }, { "name": "phpoption/phpoption", "version": "1.9.3", diff --git a/config/route.php b/config/route.php index 88720e1..a23bdfe 100644 --- a/config/route.php +++ b/config/route.php @@ -148,6 +148,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']); From 22e7db10534c0a87ee13787b9cd6e316ea789bc7 Mon Sep 17 00:00:00 2001 From: "WIN-C27C5MSVTPO\\16961" <1696136552@qq.com> Date: Mon, 4 Aug 2025 11:17:38 +0800 Subject: [PATCH 3/4] =?UTF-8?q?=E5=AE=A2=E6=88=B7=E8=BD=AC=E7=A7=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/CustomerController.php | 46 ++++ app/crmAdmin/service/CustomerService.php | 218 ++++++++++-------- 2 files changed, 173 insertions(+), 91 deletions(-) diff --git a/app/crmAdmin/controller/CustomerController.php b/app/crmAdmin/controller/CustomerController.php index bc4b69d..6b3bb8a 100644 --- a/app/crmAdmin/controller/CustomerController.php +++ b/app/crmAdmin/controller/CustomerController.php @@ -99,4 +99,50 @@ class CustomerController extends BaseController } } + + /** + * 批量转让 + * @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/service/CustomerService.php b/app/crmAdmin/service/CustomerService.php index e5f1cf2..ac766ce 100644 --- a/app/crmAdmin/service/CustomerService.php +++ b/app/crmAdmin/service/CustomerService.php @@ -17,6 +17,7 @@ use PhpOffice\PhpSpreadsheet\Writer\Xlsx; use PhpOffice\PhpSpreadsheet\Style\Alignment; use PhpOffice\PhpSpreadsheet\Style\Border; use PhpOffice\PhpSpreadsheet\Style\Fill; +use support\Db; class CustomerService { @@ -32,7 +33,8 @@ class CustomerService * @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){ + public function search($params, $export = false) + { // $page = $request->input('page', 1); // $perPage = $request->input('per_page', 10); @@ -50,10 +52,10 @@ class CustomerService if ($scene == 'audit') { $where[] = ['status', '=', 2]; - }else { + } else { $where[] = ['status', '=', 1]; } - if($seas == 'seas' || $scene == 'seas'){ + if ($seas == 'seas' || $scene == 'seas') { $where[] = ['user_id', 0]; } // if($recycle == 'recycle'){ @@ -80,18 +82,18 @@ class CustomerService $query = Customer::where($where); - if(!empty($user_id)){ - $query->whereIn('user_id', explode(',',$user_id)); + if (!empty($user_id)) { + $query->whereIn('user_id', explode(',', $user_id)); } - if(!empty($tagId)){ + 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->whereHas('tags', function ($query) use ($id) { $query->where('tag.id', $id); }); } @@ -110,20 +112,20 @@ class CustomerService $query->onlyTrashed(); } - if(!$export){ - $customer = $query->orderBy('id','desc')->paginate($perPage, ['*'], 'page', $page); - }else{ - $customer = $query->orderBy('id','desc')->get(); + if (!$export) { + $customer = $query->orderBy('id', 'desc')->paginate($perPage, ['*'], 'page', $page); + } else { + $customer = $query->orderBy('id', 'desc')->get(); return $customer; } - if(!$export){ + if (!$export) { foreach ($customer as &$item) { $item = $this->getCustomer($item); - if($seas == 'seas' || $scene == 'seas'){ - $item['seas_time'] = time()-strtotime($item['deleted_at']); + 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']); + $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'); } } @@ -136,31 +138,31 @@ class CustomerService 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' => '录入人', - ]; - + + // 设置表头 + $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' => [ @@ -182,31 +184,31 @@ class CustomerService ], ], ]; - - $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++; - } - + + $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' => [ @@ -219,35 +221,35 @@ class CustomerService '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); - } - + + $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, @@ -261,7 +263,8 @@ class CustomerService * @param $params * @return bool */ - public function audit($params){ + public function audit($params) + { $id = $params['id']; @@ -270,13 +273,45 @@ class CustomerService ]; $logic = new CustomerLogic(); - if($params['scene'] == 'delete'){ + if ($params['scene'] == 'delete') { return $logic->forceDelete($params['id']); - }else{ + } 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 @@ -289,21 +324,21 @@ class CustomerService $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) { + if ($customer['grade'] <= 2) { $customer['follow'] = 0; - $customer['follow_txt'] = Util::language($language,'Following','跟进中','Продолжение'); - }elseif ($customer['grade']<5) { + $customer['follow_txt'] = Util::language($language, 'Following', '跟进中', 'Продолжение'); + } elseif ($customer['grade'] < 5) { $customer['follow'] = 1; - $customer['follow_txt'] = Util::language($language,'transacted','已成交','Транзакция завершена'); - }else{ + $customer['follow_txt'] = Util::language($language, 'transacted', '已成交', 'Транзакция завершена'); + } else { $customer['follow'] = 2; - $customer['follow_txt'] = Util::language($language,'lost','已流失','Потерянный'); + $customer['follow_txt'] = Util::language($language, 'lost', '已流失', 'Потерянный'); } $where = [ 'customer_id' => $customer['id'] ]; - $begin_follow = Follow::select('id','created_at','updated_at') + $begin_follow = Follow::select('id', 'created_at', 'updated_at') ->where($where) ->min('created_at'); @@ -312,12 +347,12 @@ class CustomerService // $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 = 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['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) { @@ -336,10 +371,11 @@ class CustomerService public function detail($params) { - $id =$params['id']; - $customer = 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') + $id = $params['id']; + $customer = Customer::with('tags') ->where('id', $id) ->first(); + $customer = $this->getCustomer($customer); return $customer; } From bec1820299560a349687976045348d9e73084799 Mon Sep 17 00:00:00 2001 From: "WIN-C27C5MSVTPO\\16961" <1696136552@qq.com> Date: Mon, 4 Aug 2025 11:24:40 +0800 Subject: [PATCH 4/4] =?UTF-8?q?=E8=BD=AC=E7=A7=BB=E5=AE=A2=E6=88=B7?= =?UTF-8?q?=E8=B7=AF=E7=94=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/crm/service/CustomerService.php | 3 +-- config/route.php | 2 ++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/crm/service/CustomerService.php b/app/crm/service/CustomerService.php index df1f7c5..f211a28 100644 --- a/app/crm/service/CustomerService.php +++ b/app/crm/service/CustomerService.php @@ -2,8 +2,7 @@ namespace app\crm\service; -use app\crmAdmin\logic\CustomerLogic; -use app\crm\model\CarType; +use app\crm\logic\CustomerLogic; use app\crm\model\Channel; use app\crm\model\Country; use app\crm\model\Customer; diff --git a/config/route.php b/config/route.php index a23bdfe..90b9b16 100644 --- a/config/route.php +++ b/config/route.php @@ -97,6 +97,8 @@ Route::group('/admin', 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']); }); }); });