From ad23809a103d1e68681ae6e2a0b5d54b6df67888 Mon Sep 17 00:00:00 2001 From: mzl92 Date: Wed, 30 Jul 2025 16:12:19 +0800 Subject: [PATCH 01/32] 1 --- app/common/logic/AuthLogic.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/common/logic/AuthLogic.php b/app/common/logic/AuthLogic.php index 754504b..4211446 100644 --- a/app/common/logic/AuthLogic.php +++ b/app/common/logic/AuthLogic.php @@ -93,7 +93,7 @@ class AuthLogic ->orWhere('email', $email); }) ->first(); - + $user->isLogin = true;; if (!$user) { // 获取编号 $userSn = $this->getAvailableUserSn(); From e8af949f3637e4254a7afff6ab6cbfe153d3fd8e Mon Sep 17 00:00:00 2001 From: mzl Date: Wed, 30 Jul 2025 16:13:53 +0800 Subject: [PATCH 02/32] Merge branch 'develop' into feature/mzl_dev --- app/command/ImportCustomerCommand.php | 167 ++++++++++++ app/crm/service/CustomerService.php | 10 + composer.json | 3 +- composer.lock | 372 +++++++++++++++++++++++++- 4 files changed, 550 insertions(+), 2 deletions(-) create mode 100644 app/command/ImportCustomerCommand.php diff --git a/app/command/ImportCustomerCommand.php b/app/command/ImportCustomerCommand.php new file mode 100644 index 0000000..8e79eda --- /dev/null +++ b/app/command/ImportCustomerCommand.php @@ -0,0 +1,167 @@ +addArgument('file', InputArgument::REQUIRED, 'Excel文件路径') +// ->addOption('user-id', 'u', InputOption::VALUE_REQUIRED, '用户ID', 0) +// ->addOption('channel-id', 'c', InputOption::VALUE_REQUIRED, '渠道ID', 5) +// ->addOption('corp-abbr', 'a', InputOption::VALUE_REQUIRED, '公司缩写', 2); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { +// $filePath = $input->getArgument('file'); +// $userId = $input->getOption('user-id'); +// $channelId = $input->getOption('channel-id'); +// $corpAbbr = $input->getOption('corp-abbr'); + $filePath = 'C:\Users\16961\Downloads\中国制造 2023-2025-询盘导出.xls'; + $userId = 0; + $channelId = 5; + $corpAbbr = 2; + + // 检查文件是否存在 + if (!file_exists($filePath)) { + $output->writeln("文件不存在: {$filePath}"); + return self::FAILURE; + } + + try { + $output->writeln("开始导入文件: {$filePath}"); + + // 加载Excel文件 + $output->writeln("正在加载Excel文件..."); + $spreadsheet = IOFactory::load($filePath); + $worksheet = $spreadsheet->getActiveSheet(); + $output->writeln("Excel文件加载成功"); + + // 获取数据范围 + $highestRow = $worksheet->getHighestRow(); + $highestColumn = $worksheet->getHighestColumn(); + + $output->writeln("文件包含 {$highestRow} 行数据"); + + // 获取所有国家数据,用于匹配 + $output->writeln("正在获取国家数据..."); + $countries = Country::select('id', 'cn_addr', 'en_addr')->get()->keyBy(function($item) { + return strtolower(trim($item->cn_addr)); + }); + $output->writeln("获取到 " . $countries->count() . " 个国家数据"); + + $successCount = 0; + $errorCount = 0; + $errors = []; + + // 从第二行开始读取数据(第一行是标题) + $output->writeln("开始读取Excel数据..."); + for ($row = 2; $row <= $highestRow; $row++) { + try { + $output->writeln("正在处理第 {$row} 行..."); + + // 读取各列数据 - 使用坐标方法 + try { + $countryName = trim($worksheet->getCell('B' . $row)->getValue() ?? ''); + $customerName = trim($worksheet->getCell('C' . $row)->getValue() ?? ''); + $inquiryProduct = trim($worksheet->getCell('E' . $row)->getValue() ?? ''); + } catch (\Exception $cellError) { + $output->writeln("读取第{$row}行单元格时出错: " . $cellError->getMessage() . ""); + continue; + } + + $output->writeln("读取数据: 国家={$countryName}, 客户={$customerName}, 产品={$inquiryProduct}"); + + // 跳过空行 + if (empty($customerName)) { + continue; + } + + // 查找国家ID + $countryId = 0; + if (!empty($countryName)) { + $countryKey = strtolower($countryName); + if ($countries->has($countryKey)) { + $countryId = $countries->get($countryKey)->id; + } else { + // 尝试英文名称匹配 + $countryByEn = $countries->first(function($item) use ($countryName) { + return strtolower(trim($item->en_addr)) === strtolower($countryName); + }); + if ($countryByEn) { + $countryId = $countryByEn->id; + } + } + } + + // 检查是否已存在相同客户名 +// $existingCustomer = Customer::where('contacts', $customerName)->first(); +// if ($existingCustomer) { +// $output->writeln("跳过已存在的客户: {$customerName}"); +// continue; +// } + + // 创建客户记录 + $customerData = [ + 'user_id' => $userId, + 'corp_abbr' => $corpAbbr, + 'channel_id' => $channelId, + 'country_id' => $countryId, + 'contacts' => $customerName, + 'desc_collect' => $inquiryProduct, + 'grade' => 0, // 默认等级 + 'magnitude' => 1, // 默认商机 + 'type' => 1, // 默认类型 + 'sex' => 0, // 默认性别 + 'enter_user_id' => $userId, + ]; + + $customer = Customer::create($customerData); + $successCount++; + + $output->writeln("成功导入客户: {$customerName} (ID: {$customer->id})"); + + } catch (\Exception $e) { + $errorCount++; + $errorMsg = "第{$row}行导入失败: " . $e->getMessage(); + $errorMsg .= "\n文件: " . $e->getFile() . " 行: " . $e->getLine(); + $errorMsg .= "\n堆栈: " . $e->getTraceAsString(); + $errors[] = $errorMsg; + $output->writeln("{$errorMsg}"); + } + } + + $output->writeln("\n导入完成!"); + $output->writeln("成功导入: {$successCount} 条记录"); + $output->writeln("失败: {$errorCount} 条记录"); + + if (!empty($errors)) { + $output->writeln("\n错误详情:"); + foreach ($errors as $error) { + $output->writeln("{$error}"); + } + } + + return self::SUCCESS; + + } catch (\Exception $e) { + $output->writeln("导入过程中发生错误: " . $e->getMessage() . ""); + $output->writeln("文件: " . $e->getFile() . " 行: " . $e->getLine() . ""); + $output->writeln("堆栈: " . $e->getTraceAsString() . ""); + return self::FAILURE; + } + } +} \ No newline at end of file diff --git a/app/crm/service/CustomerService.php b/app/crm/service/CustomerService.php index c6518aa..41bb8ad 100644 --- a/app/crm/service/CustomerService.php +++ b/app/crm/service/CustomerService.php @@ -2,6 +2,7 @@ namespace app\crm\service; +use app\common\model\Corp; use app\common\model\User; use app\crm\logic\CustomerLogic; use app\crm\model\CarType; @@ -83,6 +84,15 @@ class CustomerService $query->onlyTrashed(); } if ($seas == 'seas') { + $corp = Corp::where('abbr', $params['corp_abbr'])->first(); + $corpId = $corp?$corp->id:0; + if($corpId == 1){ + $query->whereIn('channel_id', [1,2,3,4,5,6,7,8]); + } elseif($corpId == 3) { + $query->whereIn('channel_id', [9,10,11]); + } else{ + $query->whereIn('channel_id', [0]); + } $query->withTrashed(); } 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", From 8f4db9ed9790c27426754ca0592f44b6b4fdd071 Mon Sep 17 00:00:00 2001 From: mzl Date: Wed, 30 Jul 2025 18:41:37 +0800 Subject: [PATCH 03/32] Merge branch 'master' into develop # Conflicts: # app/common/logic/AuthLogic.php --- app/common/logic/AuthLogic.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/common/logic/AuthLogic.php b/app/common/logic/AuthLogic.php index 4211446..f0261ed 100644 --- a/app/common/logic/AuthLogic.php +++ b/app/common/logic/AuthLogic.php @@ -93,7 +93,7 @@ class AuthLogic ->orWhere('email', $email); }) ->first(); - $user->isLogin = true;; + $user->isLogin = true; if (!$user) { // 获取编号 $userSn = $this->getAvailableUserSn(); From 0f650f6bfc022ede9c1c9091ca72af8f318b5c7a Mon Sep 17 00:00:00 2001 From: mzl Date: Thu, 31 Jul 2025 10:36:30 +0800 Subject: [PATCH 04/32] Merge branch 'master' into develop --- app/command/InitUserPasswordCommand.php | 34 +++++++++++++++++++++++ app/common/logic/AuthLogic.php | 2 ++ app/crm/controller/CustomerController.php | 3 ++ app/crm/service/CustomerService.php | 2 +- 4 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 app/command/InitUserPasswordCommand.php diff --git a/app/command/InitUserPasswordCommand.php b/app/command/InitUserPasswordCommand.php new file mode 100644 index 0000000..bdf76b5 --- /dev/null +++ b/app/command/InitUserPasswordCommand.php @@ -0,0 +1,34 @@ +orWhere('password', '')->get(); + $count = 0; + + foreach ($users as $user) { + if (!empty($user->sn)) { + $user->password = password_hash($user->sn, PASSWORD_DEFAULT); + $user->save(); + $output->writeln("用户ID {$user->id} 密码已初始化"); + $count++; + } else { + $output->writeln("用户ID {$user->id} 的sn字段为空,无法初始化密码"); + } + } + + $output->writeln("共初始化 {$count} 个用户密码"); + return self::SUCCESS; + } +} \ No newline at end of file diff --git a/app/common/logic/AuthLogic.php b/app/common/logic/AuthLogic.php index f0261ed..59155dc 100644 --- a/app/common/logic/AuthLogic.php +++ b/app/common/logic/AuthLogic.php @@ -133,6 +133,8 @@ class AuthLogic } // 更新飞书信息 $user->update($updated); + // 是否登陆 + $user->isLogin = true; } return $user; } catch (\Throwable $e) { diff --git a/app/crm/controller/CustomerController.php b/app/crm/controller/CustomerController.php index e1e3f79..4eb3a6b 100644 --- a/app/crm/controller/CustomerController.php +++ b/app/crm/controller/CustomerController.php @@ -162,6 +162,9 @@ class CustomerController extends BaseController try { $param = $request->all(); $param['user_id'] = $request->user->uid; + if($param['user_id']==78){ + $param['user_id'] = 101; + } $param['corp_abbr'] = $request->basic_data['corp_abbr']; $result = (new CustomerService())->list($param); diff --git a/app/crm/service/CustomerService.php b/app/crm/service/CustomerService.php index 41bb8ad..631da7e 100644 --- a/app/crm/service/CustomerService.php +++ b/app/crm/service/CustomerService.php @@ -96,7 +96,7 @@ class CustomerService $query->withTrashed(); } - $customer = $query->paginate($perPage, ['*'], 'page', $page); + $customer = $query->orderBy('id','desc')->paginate($perPage, ['*'], 'page', $page); foreach ($customer as &$item) { $item = $this->getCustomer($item); if($seas == 'seas'){ 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 05/32] =?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 06/32] =?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 07/32] =?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 08/32] =?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']); }); }); }); From d56ad1a9191a93ba3671ddac61e347232690a02b Mon Sep 17 00:00:00 2001 From: "WIN-C27C5MSVTPO\\16961" <1696136552@qq.com> Date: Tue, 5 Aug 2025 14:03:06 +0800 Subject: [PATCH 09/32] =?UTF-8?q?=E5=A4=84=E7=90=86=E5=95=86=E6=9C=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/crmAdmin/service/CustomerService.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/crmAdmin/service/CustomerService.php b/app/crmAdmin/service/CustomerService.php index ac766ce..979bd83 100644 --- a/app/crmAdmin/service/CustomerService.php +++ b/app/crmAdmin/service/CustomerService.php @@ -321,6 +321,7 @@ class CustomerService 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'] = ($customer['magnitude']==0)?1:$customer['magnitude']; $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']]); From 76d4093727a114f0e2672e8800e2696c17577477 Mon Sep 17 00:00:00 2001 From: "WIN-C27C5MSVTPO\\16961" <1696136552@qq.com> Date: Tue, 5 Aug 2025 14:13:31 +0800 Subject: [PATCH 10/32] =?UTF-8?q?=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/crmAdmin/service/CustomerService.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/crmAdmin/service/CustomerService.php b/app/crmAdmin/service/CustomerService.php index 979bd83..b0e9ddb 100644 --- a/app/crmAdmin/service/CustomerService.php +++ b/app/crmAdmin/service/CustomerService.php @@ -88,13 +88,13 @@ class CustomerService if (!empty($tagId)) { //查询有$tagId 里任意一个标签的客户 - //$query->whereHas('tags', function($query) use ($tagId){ + //$query->whereHas('crm_tag', 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); + $query->whereHas('crm_tag', function ($query) use ($id) { + $query->where('crm_tag.id', $id); }); } } From 46c6a950e09b5b4dc951a09336e0003b273618af Mon Sep 17 00:00:00 2001 From: "WIN-C27C5MSVTPO\\16961" <1696136552@qq.com> Date: Tue, 5 Aug 2025 14:21:41 +0800 Subject: [PATCH 11/32] =?UTF-8?q?=E6=A0=87=E7=AD=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/crmAdmin/service/CustomerService.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/crmAdmin/service/CustomerService.php b/app/crmAdmin/service/CustomerService.php index b0e9ddb..b439402 100644 --- a/app/crmAdmin/service/CustomerService.php +++ b/app/crmAdmin/service/CustomerService.php @@ -93,8 +93,8 @@ class CustomerService //}); //查询有$tagId 里所有标签的客户 foreach ($tagId as $id) { - $query->whereHas('crm_tag', function ($query) use ($id) { - $query->where('crm_tag.id', $id); + $query->whereHas('tags', function($query) use ($id){ + $query->where('tag_id', $id); }); } } From 1c040943edbf3d2a0bb6de4aa2acb2ecafa5bb27 Mon Sep 17 00:00:00 2001 From: "WIN-C27C5MSVTPO\\16961" <1696136552@qq.com> Date: Tue, 5 Aug 2025 15:16:05 +0800 Subject: [PATCH 12/32] =?UTF-8?q?=E8=BE=93=E5=87=BA=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E6=B5=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/CustomerController.php | 23 +++++++++++++------ app/crmAdmin/service/CustomerService.php | 17 ++++++++------ 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/app/crmAdmin/controller/CustomerController.php b/app/crmAdmin/controller/CustomerController.php index 6b3bb8a..4b46fc9 100644 --- a/app/crmAdmin/controller/CustomerController.php +++ b/app/crmAdmin/controller/CustomerController.php @@ -13,8 +13,9 @@ class CustomerController extends BaseController * 不需要头信息的方法 */ protected $noNeedHeader = []; + protected $noNeedHeaderData = ['export']; - protected $noNeedLogin = []; + protected $noNeedLogin = ['export']; /** * 客户列表 @@ -43,13 +44,21 @@ class CustomerController extends BaseController */ public function export(Request $request) { + set_time_limit(120); try { $param = $request->all(); // $corp = Corp::where(['abbr' => $request->basic_data['corp_abbr']])->first(); // $param['corp_abbr'] = $corp->id; - - $result = (new CustomerService())->export($param); - + + $excelContent = (new CustomerService())->export($param); + // 生成文件名 + $filename = '客户数据_' . date('Y-m-d_H-i-s') . '.xlsx'; + return response() + ->withHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') + ->withHeader('Content-Disposition', 'attachment; filename="'.$filename.'"') + ->withBody($excelContent); + + /*$result = (new CustomerService())->export($param); // 检查文件是否存在 if (!file_exists($result['filepath'])) { return $this->error('导出文件生成失败'); @@ -66,9 +75,9 @@ class CustomerController extends BaseController $response->header('Content-Length', $fileSize); $response->header('Cache-Control', 'no-cache, must-revalidate'); $response->header('Pragma', 'no-cache'); - - return $response; - + + return $response;*/ + } catch (\Throwable $e) { return $this->error($e->getMessage()); } diff --git a/app/crmAdmin/service/CustomerService.php b/app/crmAdmin/service/CustomerService.php index b439402..131446f 100644 --- a/app/crmAdmin/service/CustomerService.php +++ b/app/crmAdmin/service/CustomerService.php @@ -248,15 +248,18 @@ class CustomerService // 创建Excel写入器 $writer = new Xlsx($spreadsheet); - $writer->save($filepath); +// $writer->save($filepath); + $writer->save('php://output'); + $excelContent = ob_get_clean(); + return $excelContent; // 返回文件信息 - return [ - 'filename' => $filename, - 'filepath' => $filepath, - 'size' => filesize($filepath), - 'count' => count($customers) - ]; +// return [ +// 'filename' => $filename, +// 'filepath' => $filepath, +// 'size' => filesize($filepath), +// 'count' => count($customers) +// ]; } /** From c92e93968e452970575f5731be7c5f1065ff3ba7 Mon Sep 17 00:00:00 2001 From: "WIN-C27C5MSVTPO\\16961" <1696136552@qq.com> Date: Tue, 5 Aug 2025 15:29:31 +0800 Subject: [PATCH 13/32] =?UTF-8?q?=E6=9F=A5=E8=AF=A2=E4=B8=9A=E5=8A=A1?= =?UTF-8?q?=E5=91=98=E5=A7=93=E5=90=8D=E6=97=B6=E6=8F=90=E9=AB=98=E5=85=BC?= =?UTF-8?q?=E5=AE=B9=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/crmAdmin/service/CustomerService.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/crmAdmin/service/CustomerService.php b/app/crmAdmin/service/CustomerService.php index 131446f..5ea6c8f 100644 --- a/app/crmAdmin/service/CustomerService.php +++ b/app/crmAdmin/service/CustomerService.php @@ -366,8 +366,9 @@ class CustomerService } } - $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'); + + $customer['enter_user_name'] = empty($customer['enter_user_id']) ? '' : User::where('id',$customer['enter_user_id'])->value('name'); + $customer['user_name'] = empty($customer['user_id']) ? '公海' : User::where('id',$customer['user_id'])->value('name'); return $customer; } From 0b4e3a3f31881bf62aebdd869852661264dbeca3 Mon Sep 17 00:00:00 2001 From: "WIN-C27C5MSVTPO\\16961" <1696136552@qq.com> Date: Tue, 5 Aug 2025 15:37:26 +0800 Subject: [PATCH 14/32] =?UTF-8?q?=E5=AF=BC=E5=87=BA=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/CustomerController.php | 5 +++-- app/crmAdmin/service/CustomerService.php | 22 +++++++++---------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/app/crmAdmin/controller/CustomerController.php b/app/crmAdmin/controller/CustomerController.php index 4b46fc9..ba2d213 100644 --- a/app/crmAdmin/controller/CustomerController.php +++ b/app/crmAdmin/controller/CustomerController.php @@ -44,13 +44,14 @@ class CustomerController extends BaseController */ public function export(Request $request) { - set_time_limit(120); + set_time_limit(300); try { $param = $request->all(); // $corp = Corp::where(['abbr' => $request->basic_data['corp_abbr']])->first(); // $param['corp_abbr'] = $corp->id; - $excelContent = (new CustomerService())->export($param); + $result = (new CustomerService())->export($param); + $excelContent = file_get_contents($result['filepath']); // 生成文件名 $filename = '客户数据_' . date('Y-m-d_H-i-s') . '.xlsx'; return response() diff --git a/app/crmAdmin/service/CustomerService.php b/app/crmAdmin/service/CustomerService.php index 5ea6c8f..17ae60f 100644 --- a/app/crmAdmin/service/CustomerService.php +++ b/app/crmAdmin/service/CustomerService.php @@ -248,18 +248,18 @@ class CustomerService // 创建Excel写入器 $writer = new Xlsx($spreadsheet); -// $writer->save($filepath); - $writer->save('php://output'); - $excelContent = ob_get_clean(); - return $excelContent; + $writer->save($filepath); +// $writer->save('php://output'); +// $excelContent = ob_get_clean(); +// return $excelContent; - // 返回文件信息 -// return [ -// 'filename' => $filename, -// 'filepath' => $filepath, -// 'size' => filesize($filepath), -// 'count' => count($customers) -// ]; +// 返回文件信息 + return [ + 'filename' => $filename, + 'filepath' => $filepath, + 'size' => filesize($filepath), + 'count' => count($customers) + ]; } /** From 02a66d3e68918c765dc544c322f94fc52e4fdd8e Mon Sep 17 00:00:00 2001 From: "WIN-C27C5MSVTPO\\16961" <1696136552@qq.com> Date: Tue, 5 Aug 2025 15:44:18 +0800 Subject: [PATCH 15/32] no message --- app/crmAdmin/controller/CustomerController.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/crmAdmin/controller/CustomerController.php b/app/crmAdmin/controller/CustomerController.php index ba2d213..d08efbf 100644 --- a/app/crmAdmin/controller/CustomerController.php +++ b/app/crmAdmin/controller/CustomerController.php @@ -49,6 +49,9 @@ class CustomerController extends BaseController $param = $request->all(); // $corp = Corp::where(['abbr' => $request->basic_data['corp_abbr']])->first(); // $param['corp_abbr'] = $corp->id; + if(empty($param['user_id']) && empty($param['tag_id'])){ + return $this->error('请勿一次性下载所有数据,请筛选后下载需要的数据'); + } $result = (new CustomerService())->export($param); $excelContent = file_get_contents($result['filepath']); From ee3b0edb337aac2293e7c8f03a7666c5e4dc4712 Mon Sep 17 00:00:00 2001 From: "WIN-C27C5MSVTPO\\16961" <1696136552@qq.com> Date: Tue, 5 Aug 2025 16:00:16 +0800 Subject: [PATCH 16/32] 1 --- app/crmAdmin/controller/CustomerController.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/app/crmAdmin/controller/CustomerController.php b/app/crmAdmin/controller/CustomerController.php index d08efbf..153941a 100644 --- a/app/crmAdmin/controller/CustomerController.php +++ b/app/crmAdmin/controller/CustomerController.php @@ -44,14 +44,10 @@ class CustomerController extends BaseController */ public function export(Request $request) { - set_time_limit(300); try { $param = $request->all(); // $corp = Corp::where(['abbr' => $request->basic_data['corp_abbr']])->first(); // $param['corp_abbr'] = $corp->id; - if(empty($param['user_id']) && empty($param['tag_id'])){ - return $this->error('请勿一次性下载所有数据,请筛选后下载需要的数据'); - } $result = (new CustomerService())->export($param); $excelContent = file_get_contents($result['filepath']); From b696c3354835a707dfdb586617618bb222604326 Mon Sep 17 00:00:00 2001 From: "WIN-C27C5MSVTPO\\16961" <1696136552@qq.com> Date: Tue, 5 Aug 2025 16:06:55 +0800 Subject: [PATCH 17/32] =?UTF-8?q?=E4=B8=8B=E8=BD=BD=E8=BF=87=E6=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/crmAdmin/service/CustomerService.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/crmAdmin/service/CustomerService.php b/app/crmAdmin/service/CustomerService.php index 17ae60f..07681cd 100644 --- a/app/crmAdmin/service/CustomerService.php +++ b/app/crmAdmin/service/CustomerService.php @@ -84,6 +84,8 @@ class CustomerService if (!empty($user_id)) { $query->whereIn('user_id', explode(',', $user_id)); + } elseif ($user_id === 0 && $export) { + $query->where('user_id', 0); } if (!empty($tagId)) { @@ -137,6 +139,9 @@ class CustomerService public function export($params) { + if(empty($param['user_id']) && empty($param['tag_id'])){ + $params['user_id'] = 0; + } $customers = $this->search($params, true); // 创建新的Spreadsheet对象 From 40d3942a8c025d6931712a1cf155f7ed386f1acb Mon Sep 17 00:00:00 2001 From: "WIN-C27C5MSVTPO\\16961" <1696136552@qq.com> Date: Tue, 5 Aug 2025 17:19:12 +0800 Subject: [PATCH 18/32] =?UTF-8?q?=E7=94=A8=E6=88=B7=E5=88=97=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/admin/controller/UserController.php | 53 +++++++++++++++++++ app/admin/logic/UserLogic.php | 17 ++++++ app/admin/service/UserService.php | 69 +++++++++++++++++++++++++ app/admin/validate/UserValidate.php | 29 +++++++++++ config/route.php | 6 +++ 5 files changed, 174 insertions(+) create mode 100644 app/admin/controller/UserController.php create mode 100644 app/admin/logic/UserLogic.php create mode 100644 app/admin/service/UserService.php create mode 100644 app/admin/validate/UserValidate.php diff --git a/app/admin/controller/UserController.php b/app/admin/controller/UserController.php new file mode 100644 index 0000000..33baa25 --- /dev/null +++ b/app/admin/controller/UserController.php @@ -0,0 +1,53 @@ +service = new UserService(); + } + + /** + * 列表页数据格式化 + * + * @param array $data + * @return array + */ + protected function indexFormat($data) + { + if(isset($data['records'])){ + return $data; + } + $data['sex_txt'] = ''; + if (isset($data['sex']) && $data['sex'] == 1){ + $data['sex_txt'] = '男'; + }elseif(isset($data['sex']) && $data['sex'] == 2){ + $data['sex_txt'] = '女'; + } + + switch ($data['status']){ + case 1: + $data['status_txt'] = '正常'; + break; + case 2: + $data['status_txt'] = '离职'; + break; + default: + $data['status_txt'] = ''; + break; + } + + $data['customer_count'] = Customer::where('user_id', $data['id'])->count(); + return $data; + } +} \ No newline at end of file diff --git a/app/admin/logic/UserLogic.php b/app/admin/logic/UserLogic.php new file mode 100644 index 0000000..de0d15d --- /dev/null +++ b/app/admin/logic/UserLogic.php @@ -0,0 +1,17 @@ +validator; + } + + + /** + * 构造 CrudUtil 查询配置 + */ + protected function buildQueryConfig(Request $request, string $scene = 'default'): array + { + $p = $request->all(); + + $config = []; + $config['model'] = $this->modelClass; + switch ($scene) { + case 'default': + $config['where']['status'] = 1; + $config['order']['id'] = 'desc'; + break; + case 'index': + $config['where']['status'] = 1; + $config['order']['id'] = 'desc'; + $config['fields'] = ['id', 'name', 'email', 'sex', 'phone', 'avatar', 'corp_id', 'country_id', 'position', 'status', 'created_at']; + break; + case 'show': + $config['where']['id'] = $p['id']; + $config['where']['status'] = 1; + break; + case 'store': + break; + case 'update': + $config['where']['id'] = $p['id']; + break; + case 'destroy': + $config['where']['id'] = $p['id']; + break; + } + + return $config; + } +} \ No newline at end of file diff --git a/app/admin/validate/UserValidate.php b/app/admin/validate/UserValidate.php new file mode 100644 index 0000000..e478c5b --- /dev/null +++ b/app/admin/validate/UserValidate.php @@ -0,0 +1,29 @@ + [ + + ], + + 'update' => [ + + ] + ]; +} \ No newline at end of file diff --git a/config/route.php b/config/route.php index 5d0fa46..fb27d10 100644 --- a/config/route.php +++ b/config/route.php @@ -71,6 +71,12 @@ Route::group('/admin', function () { Route::any('/update', [app\admin\controller\RoleController::class, 'update']); Route::any('/destroy', [app\admin\controller\RoleController::class, 'destroy']); }); + + // 用户 + Route::group('/user', function () { + Route::any('/list', [app\admin\controller\UserController::class, 'index']); + }); + // 公司 Route::group('/corp', function () { Route::any('/list', [app\admin\controller\CorpController::class, 'index']); From ca18a05554bf76205604755587be958c37647d8f Mon Sep 17 00:00:00 2001 From: "WIN-C27C5MSVTPO\\16961" <1696136552@qq.com> Date: Tue, 5 Aug 2025 17:58:10 +0800 Subject: [PATCH 19/32] =?UTF-8?q?=E7=94=A8=E6=88=B7=E6=90=9C=E7=B4=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/admin/service/UserService.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/admin/service/UserService.php b/app/admin/service/UserService.php index 412bef4..246ca44 100644 --- a/app/admin/service/UserService.php +++ b/app/admin/service/UserService.php @@ -48,6 +48,13 @@ class UserService case 'index': $config['where']['status'] = 1; $config['order']['id'] = 'desc'; + + // 关键字 + if (isset($p['keyword']) && $p['keyword']) { + $k = '%' . $p['keyword'] . '%'; + $config['where']['name'] = ['like', $k]; + } + $config['fields'] = ['id', 'name', 'email', 'sex', 'phone', 'avatar', 'corp_id', 'country_id', 'position', 'status', 'created_at']; break; case 'show': From 7d405fab97b794a52236da0ae95e91125fa63be4 Mon Sep 17 00:00:00 2001 From: "WIN-C27C5MSVTPO\\16961" <1696136552@qq.com> Date: Wed, 6 Aug 2025 09:45:56 +0800 Subject: [PATCH 20/32] =?UTF-8?q?=E7=94=A8=E6=88=B7id=E6=90=9C=E7=B4=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/admin/service/UserService.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/admin/service/UserService.php b/app/admin/service/UserService.php index 246ca44..f1c5f1d 100644 --- a/app/admin/service/UserService.php +++ b/app/admin/service/UserService.php @@ -54,6 +54,9 @@ class UserService $k = '%' . $p['keyword'] . '%'; $config['where']['name'] = ['like', $k]; } + if (isset($p['id']) && $p['id']) { + $config['where']['id'] = $p['id']; + } $config['fields'] = ['id', 'name', 'email', 'sex', 'phone', 'avatar', 'corp_id', 'country_id', 'position', 'status', 'created_at']; break; From 61b41fe5f7308337b936e34c50c365c1828615c3 Mon Sep 17 00:00:00 2001 From: "WIN-C27C5MSVTPO\\16961" <1696136552@qq.com> Date: Wed, 6 Aug 2025 10:15:40 +0800 Subject: [PATCH 21/32] =?UTF-8?q?=E7=94=A8=E6=88=B7=E8=AF=A6=E6=83=85?= =?UTF-8?q?=E3=80=81=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/admin/service/UserService.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/admin/service/UserService.php b/app/admin/service/UserService.php index f1c5f1d..34457ed 100644 --- a/app/admin/service/UserService.php +++ b/app/admin/service/UserService.php @@ -62,7 +62,6 @@ class UserService break; case 'show': $config['where']['id'] = $p['id']; - $config['where']['status'] = 1; break; case 'store': break; From b0cbf36315ab1ee14818b27ca20dcb0863ddca2d Mon Sep 17 00:00:00 2001 From: "WIN-C27C5MSVTPO\\16961" <1696136552@qq.com> Date: Wed, 6 Aug 2025 13:52:17 +0800 Subject: [PATCH 22/32] =?UTF-8?q?=E7=94=A8=E6=88=B7=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E4=B8=8E=E8=AF=A6=E6=83=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/route.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/config/route.php b/config/route.php index fb27d10..2ae131d 100644 --- a/config/route.php +++ b/config/route.php @@ -75,6 +75,8 @@ Route::group('/admin', function () { // 用户 Route::group('/user', function () { Route::any('/list', [app\admin\controller\UserController::class, 'index']); + Route::any('/show', [app\admin\controller\UserController::class, 'show']); + Route::any('/update', [app\admin\controller\UserController::class, 'update']); }); // 公司 From 657652031c97ee7d115787735d214a1621dcb1bf Mon Sep 17 00:00:00 2001 From: "WIN-C27C5MSVTPO\\16961" <1696136552@qq.com> Date: Wed, 6 Aug 2025 14:40:52 +0800 Subject: [PATCH 23/32] =?UTF-8?q?=E5=8E=BB=E9=99=A4=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/crm/controller/CustomerController.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/crm/controller/CustomerController.php b/app/crm/controller/CustomerController.php index c54fea7..1eb285e 100644 --- a/app/crm/controller/CustomerController.php +++ b/app/crm/controller/CustomerController.php @@ -162,9 +162,6 @@ class CustomerController extends BaseController try { $param = $request->all(); $param['user_id'] = $request->user->uid; - if($param['user_id']==78){ - $param['user_id'] = 101; - } $param['corp_abbr'] = $request->basic_data['corp_abbr']; $result = (new CustomerService())->list($param); From e4ce464effede07570c563b32efc9617828128b7 Mon Sep 17 00:00:00 2001 From: "WIN-C27C5MSVTPO\\16961" <1696136552@qq.com> Date: Wed, 6 Aug 2025 14:59:38 +0800 Subject: [PATCH 24/32] =?UTF-8?q?=E5=AE=A1=E6=A0=B8=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/crm/controller/CustomerController.php | 2 +- app/crm/service/CustomerService.php | 1 + app/crmAdmin/service/CustomerService.php | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/crm/controller/CustomerController.php b/app/crm/controller/CustomerController.php index 1eb285e..dba4ae7 100644 --- a/app/crm/controller/CustomerController.php +++ b/app/crm/controller/CustomerController.php @@ -167,7 +167,7 @@ class CustomerController extends BaseController return $this->success($this->page($result)); } catch (\Throwable $e) { - return $this->error($e->getMessage().$e->getFile().$e->getLine()); + return $this->error($e->getMessage()); } } diff --git a/app/crm/service/CustomerService.php b/app/crm/service/CustomerService.php index a21eb3e..608ec04 100644 --- a/app/crm/service/CustomerService.php +++ b/app/crm/service/CustomerService.php @@ -132,6 +132,7 @@ class CustomerService 'user_id' => 0, ]); + $customer->user_id = 0; $customer->status = 2; $res = $customer->save(); diff --git a/app/crmAdmin/service/CustomerService.php b/app/crmAdmin/service/CustomerService.php index 07681cd..be9d819 100644 --- a/app/crmAdmin/service/CustomerService.php +++ b/app/crmAdmin/service/CustomerService.php @@ -110,7 +110,7 @@ class CustomerService $query->whereIn('grade', $grade); } - if ($recycle == 'recycle' || $scene == 'recycle' || $seas == 'seas' || $scene == 'seas') { + if ($recycle == 'recycle' || $scene == 'recycle' || $seas == 'seas' || $scene == 'seas' || $scene == 'audit') { $query->onlyTrashed(); } @@ -128,7 +128,7 @@ class CustomerService } 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'); + $customer['user_name'] = empty($customerHis['user_his']) ? '' : User::where('id',$customer['user_his'])->value('name'); } } } From a7a956d1e4ce95294ecab761f90d4c2a7ca138ce Mon Sep 17 00:00:00 2001 From: "WIN-C27C5MSVTPO\\16961" <1696136552@qq.com> Date: Wed, 6 Aug 2025 15:19:48 +0800 Subject: [PATCH 25/32] =?UTF-8?q?=E5=AE=A1=E6=A0=B8=E5=88=97=E8=A1=A8?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/crmAdmin/service/CustomerService.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/crmAdmin/service/CustomerService.php b/app/crmAdmin/service/CustomerService.php index be9d819..520d335 100644 --- a/app/crmAdmin/service/CustomerService.php +++ b/app/crmAdmin/service/CustomerService.php @@ -128,7 +128,7 @@ class CustomerService } if ($scene == 'audit') { //审核时查询举报人姓名 $customerHis = CustomerHis::where('customer_id', $item->id)->orderBy('id', 'desc')->first(['user_his']); - $customer['user_name'] = empty($customerHis['user_his']) ? '' : User::where('id',$customer['user_his'])->value('name'); + $item['user_name'] = empty($customerHis['user_his']) ? '' : User::where('id',$customer['user_his'])->value('name'); } } } From 330ced58fd42f59b4792a38a76403dcbff3b3c65 Mon Sep 17 00:00:00 2001 From: "WIN-C27C5MSVTPO\\16961" <1696136552@qq.com> Date: Wed, 6 Aug 2025 15:25:10 +0800 Subject: [PATCH 26/32] =?UTF-8?q?=E5=AE=A1=E6=A0=B8=E5=88=97=E8=A1=A8?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/crmAdmin/logic/CustomerLogic.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/crmAdmin/logic/CustomerLogic.php b/app/crmAdmin/logic/CustomerLogic.php index 99af464..3185775 100644 --- a/app/crmAdmin/logic/CustomerLogic.php +++ b/app/crmAdmin/logic/CustomerLogic.php @@ -9,7 +9,7 @@ class CustomerLogic public function status(int $id,int $status) { - $customer = Customer::find($id); + $customer = Customer::withTrashed()->find($id); $customer->status = $status; $res = $customer->save(); return $res; From 9eb1a7fc1987bf6a3d78e089ea973b4f1273fe95 Mon Sep 17 00:00:00 2001 From: "WIN-C27C5MSVTPO\\16961" <1696136552@qq.com> Date: Wed, 6 Aug 2025 15:28:29 +0800 Subject: [PATCH 27/32] =?UTF-8?q?=E5=AE=A1=E6=A0=B8=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/crmAdmin/logic/CustomerLogic.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/crmAdmin/logic/CustomerLogic.php b/app/crmAdmin/logic/CustomerLogic.php index 3185775..8169215 100644 --- a/app/crmAdmin/logic/CustomerLogic.php +++ b/app/crmAdmin/logic/CustomerLogic.php @@ -10,8 +10,10 @@ class CustomerLogic public function status(int $id,int $status) { $customer = Customer::withTrashed()->find($id); + $customer->restore(); $customer->status = $status; $res = $customer->save(); + return $res; } From b5c73e524a6df66af9bc0ccd90481500a7aa6754 Mon Sep 17 00:00:00 2001 From: "WIN-C27C5MSVTPO\\16961" <1696136552@qq.com> Date: Wed, 6 Aug 2025 15:42:01 +0800 Subject: [PATCH 28/32] 1 --- app/crmAdmin/logic/CustomerLogic.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/crmAdmin/logic/CustomerLogic.php b/app/crmAdmin/logic/CustomerLogic.php index 8169215..8f364e8 100644 --- a/app/crmAdmin/logic/CustomerLogic.php +++ b/app/crmAdmin/logic/CustomerLogic.php @@ -10,7 +10,6 @@ class CustomerLogic public function status(int $id,int $status) { $customer = Customer::withTrashed()->find($id); - $customer->restore(); $customer->status = $status; $res = $customer->save(); From 601936a129c10cae72f8ab02e961a59bdb31cdac Mon Sep 17 00:00:00 2001 From: "WIN-C27C5MSVTPO\\16961" <1696136552@qq.com> Date: Wed, 6 Aug 2025 15:56:57 +0800 Subject: [PATCH 29/32] =?UTF-8?q?=E5=AE=A1=E6=A0=B8=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/crmAdmin/logic/CustomerLogic.php | 2 +- app/crmAdmin/service/CustomerService.php | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/app/crmAdmin/logic/CustomerLogic.php b/app/crmAdmin/logic/CustomerLogic.php index 8f364e8..869982f 100644 --- a/app/crmAdmin/logic/CustomerLogic.php +++ b/app/crmAdmin/logic/CustomerLogic.php @@ -18,7 +18,7 @@ class CustomerLogic public function forceDelete(int $id) { - $customer = Customer::find($id); + $customer = Customer::withTrashed()->find($id); //强制删除 $res = $customer->forceDelete(); return $res; diff --git a/app/crmAdmin/service/CustomerService.php b/app/crmAdmin/service/CustomerService.php index 520d335..d003a0b 100644 --- a/app/crmAdmin/service/CustomerService.php +++ b/app/crmAdmin/service/CustomerService.php @@ -128,7 +128,8 @@ class CustomerService } if ($scene == 'audit') { //审核时查询举报人姓名 $customerHis = CustomerHis::where('customer_id', $item->id)->orderBy('id', 'desc')->first(['user_his']); - $item['user_name'] = empty($customerHis['user_his']) ? '' : User::where('id',$customer['user_his'])->value('name'); + var_dump($customerHis['user_his']); + $item['user_name'] = empty($customerHis['user_his']) ? '' : User::where('id',$customerHis['user_his'])->value('name'); } } } @@ -276,10 +277,6 @@ class CustomerService $id = $params['id']; - $where = [ - 'id' => $id, - ]; - $logic = new CustomerLogic(); if ($params['scene'] == 'delete') { return $logic->forceDelete($params['id']); From e3c985e6014e31bafcfdd2581b2a34cf7978517f Mon Sep 17 00:00:00 2001 From: "WIN-C27C5MSVTPO\\16961" <1696136552@qq.com> Date: Wed, 6 Aug 2025 17:42:24 +0800 Subject: [PATCH 30/32] =?UTF-8?q?=E8=B7=9F=E5=8D=95=E4=BA=BA=E4=BF=AE?= =?UTF-8?q?=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/admin/service/UserService.php | 2 +- app/command/CustomerCodeCommand.php | 61 ++++++++++++++++ app/crm/controller/CustomerController.php | 89 +++++++++++++++++------ app/crmAdmin/service/CustomerService.php | 8 ++ 4 files changed, 137 insertions(+), 23 deletions(-) create mode 100644 app/command/CustomerCodeCommand.php diff --git a/app/admin/service/UserService.php b/app/admin/service/UserService.php index 34457ed..62e4d44 100644 --- a/app/admin/service/UserService.php +++ b/app/admin/service/UserService.php @@ -58,7 +58,7 @@ class UserService $config['where']['id'] = $p['id']; } - $config['fields'] = ['id', 'name', 'email', 'sex', 'phone', 'avatar', 'corp_id', 'country_id', 'position', 'status', 'created_at']; + $config['fields'] = ['id','sn', 'name', 'email', 'sex', 'phone', 'avatar', 'corp_id', 'country_id', 'position', 'status', 'created_at']; break; case 'show': $config['where']['id'] = $p['id']; diff --git a/app/command/CustomerCodeCommand.php b/app/command/CustomerCodeCommand.php new file mode 100644 index 0000000..2d3e447 --- /dev/null +++ b/app/command/CustomerCodeCommand.php @@ -0,0 +1,61 @@ +setDescription('为crm_customer表所有记录生成唯一code码,分批处理'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $batchSize = 1000; + $total = Customer::withTrashed()->where('code','')->count(); + $output->writeln("总计需处理:$total 条记录"); + + $processed = 0; + do { + // 查询一批未生成code的记录 + $customers = Customer::withTrashed()->where('code','')->limit($batchSize)->get(); + if ($customers->isEmpty()) { + break; + } + + // Eloquent 批量处理 + foreach ($customers as $customer) { + // 生成唯一16位code,CM+14位 + $unique = strtoupper(substr(md5($customer->id . microtime(true) . mt_rand(1000, 9999)), 0, 14)); + $code = 'CM' . $unique; + + // 防止极小概率重复 + while (Customer::where('code', $code)->exists()) { + $unique = strtoupper(substr(md5($customer->id . microtime(true) . mt_rand(1000, 9999)), 0, 14)); + $code = 'CM' . $unique; + } + + $obtain = $customer->created_at>strtotime('2025-07-29')?$customer->created_at:strtotime('2025-07-29'); + + $customer->code = $code; + $customer->obtain = $obtain; + $customer->save(); + $processed++; + } + + $output->writeln("已处理:$processed / $total"); + } while (true); + + $output->writeln('全部处理完成!'); + return self::SUCCESS; + } +} \ No newline at end of file diff --git a/app/crm/controller/CustomerController.php b/app/crm/controller/CustomerController.php index dba4ae7..2efbceb 100644 --- a/app/crm/controller/CustomerController.php +++ b/app/crm/controller/CustomerController.php @@ -3,6 +3,7 @@ namespace app\crm\controller; use app\common\model\Corp; +use app\common\model\InquiryLog; use app\common\model\User; use app\crm\model\Channel; use app\crm\model\Country; @@ -12,6 +13,7 @@ use app\crm\model\Follow; use app\crm\service\CustomerService; use app\crm\utils\Util; use config\constants\Constants; +use support\Db; use support\Request; class CustomerController extends BaseController @@ -454,22 +456,38 @@ class CustomerController extends BaseController $data = [ 'user_id' => $request->user->uid ]; - $customer= Customer::withTrashed()->find($customerId); - if(empty($customer) || $customer->user_id != 0){ - return $this->error( trans('claim_fail')); + + try { + Db::beginTransaction(); + $customer= Customer::withTrashed()->find($customerId); + + if(empty($customer) || $customer->user_id != 0){ + return $this->error( trans('claim_fail')); + } + + $customer->restore(); + $res = $customer->update($data); + + //记录客户关系变化 + $his = CustomerHis::create([ + 'customer_id' => $customerId, + 'user_his' => 0, + 'user_id' => $userId, + ]); + if (!empty($customer->sn) && $customer->type==1){ + $inquiryLog = InquiryLog::where('order_no', $customer->sn)->first(); + if (!empty($inquiryLog)) { + $inquiryLog->recipient = $userId; + $inquiryLog->save(); + } + } + Db::commit(); + } catch (\Throwable $e) { + Db::rollback(); + return $this->error($e->getMessage()); } - $customer->restore(); - $res = $customer->update($data); - - //记录客户关系变化 - $his = CustomerHis::create([ - 'customer_id' => $customerId, - 'user_his' => 0, - 'user_id' => $userId, - ]); - if ($res) { return $this->success($res, trans('success')); } else { @@ -506,17 +524,44 @@ class CustomerController extends BaseController }else{ $query= Customer::whereIn('id',explode(',',$customerId)); } - $customer = $query->get(); +// $customer = $query->get(); +// +// foreach ($customer as $item) { +// //记录客户关系变化 +// $his = CustomerHis::create([ +// 'customer_id' => $item->id, +// 'user_his' => $item->user_id, +// 'user_id' => $user['id'], +// ]); +// } +// $res = $query->update(['user_id' => $user->id]); - foreach ($customer as $item) { - //记录客户关系变化 - $his = CustomerHis::create([ - 'customer_id' => $item->id, - 'user_his' => $item->user_id, - 'user_id' => $user['id'], - ]); + try { + $customer = $query->get(); + + foreach ($customer as $item) { + //记录客户关系变化 + $his = CustomerHis::create([ + 'customer_id' => $item->id, + 'user_his' => $item->user_id, + 'user_id' => $user['id'], + ]); + if (!empty($customer->sn) && $customer->type==1){ + $inquiryLog = InquiryLog::where('order_no', $customer->sn)->first(); + if (!empty($inquiryLog)) { + $inquiryLog->recipient = $userId; + $inquiryLog->save(); + } + } + } + $res = $query->update(['user_id' => $user->id]); + + Db::commit(); + } catch (\Throwable $e) { + Db::rollback(); + return $this->error($e->getMessage()); } - $res = $query->update(['user_id' => $user->id]); + if ($res) { diff --git a/app/crmAdmin/service/CustomerService.php b/app/crmAdmin/service/CustomerService.php index d003a0b..79c9193 100644 --- a/app/crmAdmin/service/CustomerService.php +++ b/app/crmAdmin/service/CustomerService.php @@ -2,6 +2,7 @@ namespace app\crmAdmin\service; +use app\common\model\InquiryLog; use app\common\model\User; use app\crm\model\CustomerHis; use app\crmAdmin\logic\CustomerLogic; @@ -306,6 +307,13 @@ class CustomerService 'user_his' => $item->user_id, 'user_id' => $userId, ]); + if (!empty($item->sn) && $item->type==1){ + $inquiryLog = InquiryLog::where('order_no', $item->sn)->first(); + if (!empty($inquiryLog)) { + $inquiryLog->recipient = $userId; + $inquiryLog->save(); + } + } } $res = $query->update(['user_id' => $userId]); From cea22a16a6f2838004883bdb58ed43a2d3b5b914 Mon Sep 17 00:00:00 2001 From: "WIN-C27C5MSVTPO\\16961" <1696136552@qq.com> Date: Thu, 7 Aug 2025 09:51:30 +0800 Subject: [PATCH 31/32] =?UTF-8?q?=E6=9F=A5=E8=AF=A2=E6=89=80=E6=9C=89?= =?UTF-8?q?=E7=94=A8=E6=88=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/admin/service/UserService.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/admin/service/UserService.php b/app/admin/service/UserService.php index 62e4d44..6926bc5 100644 --- a/app/admin/service/UserService.php +++ b/app/admin/service/UserService.php @@ -46,7 +46,7 @@ class UserService $config['order']['id'] = 'desc'; break; case 'index': - $config['where']['status'] = 1; +// $config['where']['status'] = 1; $config['order']['id'] = 'desc'; // 关键字 From 575004b034a327da8f6437d3eb510b58e5998524 Mon Sep 17 00:00:00 2001 From: "WIN-C27C5MSVTPO\\16961" <1696136552@qq.com> Date: Thu, 7 Aug 2025 11:07:31 +0800 Subject: [PATCH 32/32] =?UTF-8?q?=E4=B8=8B=E6=AC=A1=E8=B7=9F=E8=BF=9B?= =?UTF-8?q?=E6=97=B6=E9=97=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/crmAdmin/service/CustomerService.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/crmAdmin/service/CustomerService.php b/app/crmAdmin/service/CustomerService.php index 79c9193..25fb519 100644 --- a/app/crmAdmin/service/CustomerService.php +++ b/app/crmAdmin/service/CustomerService.php @@ -124,6 +124,7 @@ class CustomerService if (!$export) { foreach ($customer as &$item) { $item = $this->getCustomer($item); + $item['follow_next'] = empty($item['follow_next'])?'':date('Y-m-d H:i:s', $item['follow_next']); if ($seas == 'seas' || $scene == 'seas') { $item['seas_time'] = time() - strtotime($item['deleted_at']); }