Files
fdesk/app/crmAdmin/service/CustomerService.php
T
2025-10-12 17:24:54 +08:00

557 lines
20 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace app\crmAdmin\service;
use app\common\model\CustomerAudit;
use app\common\model\InquiryLog;
use app\common\model\User;
use app\common\service\OssService;
use app\crm\model\CustomerHis;
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\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;
use support\Db;
use support\exception\BusinessException;
use support\Log;
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;
$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'] ?? ''; //recyclea:管理端回收站,audit:待审核列表,seas:公海
$scene = $params['scene'] ?? '';
$channelId = $params['channel_id'] ?? '';
$where = [];
if ($scene == 'audit') {
$where[] = ['status', '=', 2];
} elseif ($scene == 'recyclea') {
$where[] = ['status', '>', 2];
} else {
$where[] = ['status', '=', 1];
}
if ($seas == 'seas' || $scene == 'seas') {
$where[] = ['user_id', 0];
}
// if($recycle == 'recycle'){
// $where[] = ['user_id', $user_id];
// }
if (!empty($channelId)){
$where[] = ['channel_id', $channelId];
}
$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));
} elseif ($user_id === 0 && $export) {
$query->where('user_id', 0);
}
if (!empty($tagId)) {
//查询有$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);
});
}
}
if (!Util::isEmpty($keyword)) {
$query->where(function ($query) use ($keyword) {
$query->where('contacts', 'like', "%{$keyword}%")
->orWhere('phone', 'like', "%{$keyword}%")
->orWhere('email', 'like', "%{$keyword}%")
->orWhere('WhatsApp', 'like', "%{$keyword}%")
->orWhere('code', 'like', "%{$keyword}%");
});
}
if (!Util::isEmpty($grade)) {
$query->whereIn('grade', $grade);
}
if ($recycle == 'recycle' || $scene == 'recycle' || $scene == 'audit') {
$query->onlyTrashed();
}
if ($seas == 'seas' || $scene == 'recyclea') {
$query = $query->withTrashed();
}
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);
$item['desc_collect'] = $item['desc_collect'] ?? '';
if ($seas == 'seas' || $scene == 'seas') {
$item['seas_time'] = time() - (empty($item['deleted_at'])?strtotime($item['updated_at']):strtotime($item['deleted_at']));
}
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',$customerHis['user_his'])->value('name');
$audit = CustomerAudit::where('customer_id', $item->id)->orderBy('id', 'desc')->first();
if (!empty($audit)&&!empty($audit['img'])) {
$audit = $audit?$audit->toArray():'';
$audit['img'] = explode(',', $audit['img']);
$ossservice = new OssService();
foreach ($audit['img'] as $k => $img) {
$audit['img'][$k] = $ossservice->generatePrivateUrl($img);
}
}
$item['audit'] = $audit;
}
if ($scene == 'recyclea' && $item['status'] == 3) { //回收站删除来源
$item['del_source'] = '公海';
}elseif ($scene == 'recyclea' && $item['status'] == 4) {
$item['del_source'] = '管理端';
}elseif ($scene == 'recyclea' && $item['status'] == 5) {
$source = (!empty($item['user_id'])) ? (':' . User::where('id',$item['user_id'])->value('name')) : '';
$item['del_source'] = '业务员' . $source;
}
}
}
unset($item);
return $customer;
}
public function export($params)
{
if(empty($params['user_id']) && empty($params['tag_id']) && empty($params['channel_id'])){
$params['user_id'] = 0;
}
$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' => '录入人',
'L1' => '详情',
'M1' => '录入时间',
];
// 设置表头样式
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:M1')->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']);
$sheet->setCellValue('L' . $row, empty($customerData['desc_collect'])? '' :strip_tags($customerData['desc_collect']));
$sheet->setCellValue('M' . $row, $customerData['created_at']->toDateTimeString());
$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);
// $writer->save('php://output');
// $excelContent = ob_get_clean();
// return $excelContent;
// 返回文件信息
return [
'filename' => $filename,
'filepath' => $filepath,
'size' => filesize($filepath),
'count' => count($customers)
];
}
/**
* @param $params
* @return bool
*/
public function audit($params)
{
$id = $params['id'];
$logic = new CustomerLogic();
$audit = CustomerAudit::where('customer_id', $id)->orderBy('id', 'desc')->first();
if ($params['scene'] == 'delete') {
if(!empty($audit)){
$audit->status = 3;
$audit->save();
}
return $logic->forceDelete($params['id']);
} else {
if(!empty($audit)){
$audit->status = 2;
$audit->save();
}
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();
$user = User::where(['id' => $userId])->first();
foreach ($customer as $item) {
//记录客户关系变化
$his = CustomerHis::create([
'customer_id' => $item->id,
'user_his' => $item->user_id,
'user_id' => $userId,
]);
\app\crm\service\CustomerService::inquiryUpdate($item,$userId);
}
$data = [
'user_id' => $userId,
'corp_id' => $user->corp_id,
'obtain' => time(),
];
$res = $query->update($data);
Db::commit();
return $res;
} catch (\Throwable $e) {
Db::rollback();
throw $e;
}
}
/**
* @param $params
* @return bool
*/
public function delete($params)
{
try {
Db::beginTransaction();
$customerId = $params['customer_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' => 0,
]);
\app\crm\service\CustomerService::inquiryUpdate($item,0);
}
$res = $query->update(['user_id' => 0]);
Db::commit();
return $res;
} catch (\Throwable $e) {
Db::rollback();
throw $e;
}
}
/**
* @param $customer
* @param $request
* @param $tag 是否返回客户标签
* @return mixed
*/
public static function getCustomer($customer, $language = 'zh_CN', $tag = false)
{
$customer['grade_txt'] = Util::language($language, Constants::GRADE_EN[$customer['grade']], Constants::GRADE_ZH[$customer['grade']], Constants::GRADE_RU[$customer['grade']]);
$customer['magnitude'] = ($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']]);
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_next','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::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;
}
public function detail($params)
{
$id = $params['id'];
$customer = Customer::with('tags')
->where('id', $id)
->first();
$customer = $this->getCustomer($customer);
return $customer;
}
public function follow($params)
{
$id = $params['id'];
$follow = Follow::
where('customer_id', $id)
->get();
$ossservice = new OssService();
foreach ($follow as $item) {
$item = Util::formatDate($item,['created_at','updated_at','follow_time']);
$item['follow_time'] = empty($item['follow_time'])?'':$item['follow_time'];
$item['picture_full'] = empty($item->picture)?'':$ossservice->generatePrivateUrl($item->picture);
}
return $follow;
}
/**
* 离职员工客户分配
* @param $params
* @return string[]
*/
public function allocation($params)
{
$corpId = $params['corp_id'];
$userId = explode(',', $params['user_id']);
log::info('开始分配离职员工客户,corp_id:'.$corpId.' 被分配员工'. implode(',', $userId));
//查询离职客户
$userResign = User::where('status', 2)
->where('corp_id', $corpId)
->get(['id','name']);
$resignUerIds = [];
$userNames = [];
foreach ($userResign as $row) {
$resignUerIds[] = (int)$row->id;
$userNames[] = $row->name;
}
if (empty($userResign)) {
log::info('未匹配到离职用户');
throw new BusinessException('未匹配到离职用户', 500);
}
log::info('匹配到离职用户数:' . count($resignUerIds) .' '. implode(',',$userNames));
$customerCount = Customer::whereIn('user_id', $resignUerIds)
->orderBy('id', 'asc')
->count();
if ($customerCount === 0) {
log::info('未找到离职员工相关的客户记录。');
throw new BusinessException('未找到离职员工相关的客户记录', 500);
}
// 初始化 UID 轮询器
$uidRotator = new \app\common\utils\RedisUidRotator('allocation_customer_user');
$uidRotator->setUids($userId, false);
Customer::whereIn('user_id', $resignUerIds)
->orderBy('id', 'asc')
->chunkById(500, function ($customers) use ($uidRotator) {
foreach ($customers as $customer) {
// 获取下一个分配的UID
$newUserId = $uidRotator->next();
$customer->user_id = $newUserId;
$customer->save();
}
}, 'id');
return [
'userCount' => count($resignUerIds),
'customerCount' => $customerCount,
];
}
}