You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
340 lines
12 KiB
340 lines
12 KiB
<?php
|
|
|
|
namespace app\job\transfer\schedule;
|
|
|
|
use app\model\campus_person_role\CampusPersonRole;
|
|
use app\model\customer_resources\CustomerResources;
|
|
use app\model\resource_assignment\ResourceAssignment;
|
|
use app\model\personnel\Personnel;
|
|
use core\base\BaseScheduleJob;
|
|
use think\facade\Db;
|
|
use think\facade\Log;
|
|
|
|
/**
|
|
* 自动分配资源
|
|
*/
|
|
class ResourceAutoAllocation extends BaseScheduleJob
|
|
{
|
|
/**
|
|
* 任务名称
|
|
* @var string
|
|
*/
|
|
protected $jobName = 'resource_allocation';
|
|
|
|
/**
|
|
* 锁定时间(5分钟)
|
|
* @var int
|
|
*/
|
|
protected $lockTimeout = 300;
|
|
|
|
/**
|
|
* 执行具体任务
|
|
* @return array
|
|
*/
|
|
protected function executeJob()
|
|
{
|
|
// 获取待分配的资源
|
|
$resources = $this->getResource();
|
|
if (empty($resources)) {
|
|
Log::write('没有可分配的资源');
|
|
return $this->getSuccessResult([
|
|
'allocated' => 0,
|
|
'message' => '没有可分配的资源'
|
|
]);
|
|
}
|
|
|
|
// 获取销售人员
|
|
$salesmen = $this->getSalesman();
|
|
if (empty($salesmen)) {
|
|
Log::write('没有可用的销售人员');
|
|
return $this->getSuccessResult([
|
|
'allocated' => 0,
|
|
'message' => '没有可用的销售人员'
|
|
]);
|
|
}
|
|
|
|
// 分配资源
|
|
$result = $this->allocateResource($resources, $salesmen);
|
|
|
|
return $this->getSuccessResult([
|
|
'allocated' => $result['allocated'] ?? 0,
|
|
'updated' => $result['updated'] ?? 0,
|
|
'created' => $result['created'] ?? 0,
|
|
'total_resources' => count($resources),
|
|
'total_salesmen' => count($salesmen)
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* 获取销售人员(按角色ID为6或7的人员,支持校区优先和主管兜底)
|
|
* @param int|null $campus_id 校区ID,用于优先分配
|
|
* @return array 销售人员列表
|
|
*/
|
|
public function getSalesman(?int $campus_id = null)
|
|
{
|
|
Log::write('获取销售人员' . ($campus_id ? ',优先校区ID:' . $campus_id : ''));
|
|
|
|
// 获取所有角色ID为6或7的人员
|
|
$all_salesmen = CampusPersonRole::where('role_id', 'in', [6, 7])
|
|
->where('deleted_at', 0)
|
|
->field('person_id, role_id, campus_id')
|
|
->select()
|
|
->toArray();
|
|
|
|
if (empty($all_salesmen)) {
|
|
Log::write('未找到销售人员');
|
|
return [];
|
|
}
|
|
|
|
// 获取人员详细信息
|
|
$person_ids = array_column($all_salesmen, 'person_id');
|
|
$personnel_info = Personnel::whereIn('id', $person_ids)
|
|
->field('id, name, status')
|
|
->where('status', 1) // 只获取在职人员
|
|
->select()
|
|
->toArray();
|
|
|
|
$personnel_map = array_column($personnel_info, null, 'id');
|
|
|
|
// 过滤在职人员并整理数据
|
|
$valid_salesmen = [];
|
|
foreach ($all_salesmen as $salesman) {
|
|
if (isset($personnel_map[$salesman['person_id']])) {
|
|
$salesman['name'] = $personnel_map[$salesman['person_id']]['name'];
|
|
$valid_salesmen[] = $salesman;
|
|
}
|
|
}
|
|
|
|
if (empty($valid_salesmen)) {
|
|
Log::write('未找到在职销售人员');
|
|
return [];
|
|
}
|
|
|
|
// 获取每个销售人员当前拥有的资源数量(使用新表)
|
|
foreach ($valid_salesmen as &$salesman) {
|
|
$resourceCount = ResourceAssignment::where('assignee_type', 'user')
|
|
->where('assignee_id', $salesman['person_id'])
|
|
->count();
|
|
$salesman['resource_count'] = $resourceCount;
|
|
}
|
|
|
|
// 如果指定了校区,优先分配给该校区的销售人员
|
|
if ($campus_id) {
|
|
$campus_salesmen = array_filter($valid_salesmen, function($salesman) use ($campus_id) {
|
|
return $salesman['campus_id'] == $campus_id;
|
|
});
|
|
|
|
if (!empty($campus_salesmen)) {
|
|
// 校区内销售人员按资源数量排序
|
|
array_multisort(array_column($campus_salesmen, 'resource_count'), SORT_ASC, $campus_salesmen);
|
|
|
|
// 其他销售人员(主管兜底)按资源数量排序
|
|
$other_salesmen = array_filter($valid_salesmen, function($salesman) use ($campus_id) {
|
|
return $salesman['campus_id'] != $campus_id;
|
|
});
|
|
|
|
if (!empty($other_salesmen)) {
|
|
array_multisort(array_column($other_salesmen, 'resource_count'), SORT_ASC, $other_salesmen);
|
|
|
|
// 主管优先(角色ID=7)
|
|
usort($other_salesmen, function($a, $b) {
|
|
if ($a['role_id'] == 7 && $b['role_id'] != 7) return -1;
|
|
if ($a['role_id'] != 7 && $b['role_id'] == 7) return 1;
|
|
return $a['resource_count'] - $b['resource_count'];
|
|
});
|
|
}
|
|
|
|
Log::write('找到' . count($campus_salesmen) . '个校区销售人员,' . count($other_salesmen) . '个其他销售人员');
|
|
|
|
// 返回校区优先,主管兜底的列表
|
|
return array_merge($campus_salesmen, $other_salesmen);
|
|
}
|
|
}
|
|
|
|
// 没有指定校区或没有校区销售人员,按主管优先排序
|
|
usort($valid_salesmen, function($a, $b) {
|
|
// 主管优先(角色ID=7)
|
|
if ($a['role_id'] == 7 && $b['role_id'] != 7) return -1;
|
|
if ($a['role_id'] != 7 && $b['role_id'] == 7) return 1;
|
|
// 相同角色按资源数量排序
|
|
return $a['resource_count'] - $b['resource_count'];
|
|
});
|
|
|
|
Log::write('找到' . count($valid_salesmen) . '个销售人员,主管优先排序');
|
|
return $valid_salesmen;
|
|
}
|
|
|
|
/**
|
|
* 获取待分配的资源(使用新表结构)
|
|
* @return array 待分配资源列表
|
|
*/
|
|
public function getResource()
|
|
{
|
|
Log::write('获取待分配资源');
|
|
|
|
// 方法1:获取新表中没有分配记录的资源
|
|
$customer_resources = CustomerResources::alias('cr')
|
|
->leftJoin('school_resource_assignment ra', 'cr.id = ra.resource_id')
|
|
->whereNull('ra.resource_id')
|
|
->field('cr.id as resource_id, cr.campus_id, cr.name, cr.phone_number')
|
|
->select()
|
|
->toArray();
|
|
|
|
if (empty($customer_resources)) {
|
|
Log::write('未找到待分配资源');
|
|
return [];
|
|
}
|
|
|
|
Log::write('找到' . count($customer_resources) . '个待分配资源');
|
|
return $customer_resources;
|
|
}
|
|
|
|
/**
|
|
* 按照销售人员的资源拥有情况分配资源(使用新表结构)
|
|
* @param array $resources 待分配的资源列表
|
|
* @param array $salesmen 销售人员列表
|
|
*/
|
|
public function allocateResource($resources, $salesmen)
|
|
{
|
|
Log::write('按照销售人员的资源拥有情况分配资源');
|
|
|
|
if (empty($resources) || empty($salesmen)) {
|
|
Log::write('没有资源或销售人员,无法分配');
|
|
return [
|
|
'allocated' => 0,
|
|
'updated' => 0,
|
|
'created' => 0
|
|
];
|
|
}
|
|
|
|
// 记录分配结果
|
|
$allocations = [];
|
|
$createdCount = 0;
|
|
|
|
// 开始分配
|
|
foreach ($resources as $resource) {
|
|
// 重新获取销售人员的资源数量排序,确保每次分配都是给最少资源的人
|
|
$currentSalesmen = $this->refreshSalesmenResourceCount($salesmen, $resource['campus_id']);
|
|
|
|
if (empty($currentSalesmen)) {
|
|
Log::write('没有可用的销售人员');
|
|
break;
|
|
}
|
|
|
|
// 选择最优销售人员
|
|
$targetSalesman = $currentSalesmen[0];
|
|
|
|
// 使用新表创建分配记录
|
|
try {
|
|
Db::startTrans();
|
|
|
|
// 检查是否已存在分配记录
|
|
$existingAssignment = ResourceAssignment::where('resource_id', $resource['resource_id'])
|
|
->where('assignee_type', 'user')
|
|
->where('assignee_id', $targetSalesman['person_id'])
|
|
->find();
|
|
|
|
if ($existingAssignment) {
|
|
Log::write('资源ID ' . $resource['resource_id'] . ' 已分配给销售人员 ' . $targetSalesman['name'] . ',跳过');
|
|
Db::commit();
|
|
continue;
|
|
}
|
|
|
|
// 创建新的分配记录
|
|
$assignmentData = [
|
|
'resource_id' => $resource['resource_id'],
|
|
'assignee_type' => 'user', // 精确分配到人
|
|
'assignee_id' => $targetSalesman['person_id'],
|
|
'is_primary' => 0, // 非主责分配
|
|
'assigned_by' => 0, // 系统自动分配
|
|
'assigned_at' => date('Y-m-d H:i:s'),
|
|
'campus_id' => $resource['campus_id'], // 使用资源的校区
|
|
'assigned_source' => 'auto_allocation' // 标记为自动分配
|
|
];
|
|
|
|
$assignment = ResourceAssignment::create($assignmentData);
|
|
|
|
if ($assignment) {
|
|
$createdCount++;
|
|
Log::write('成功分配资源ID:' . $resource['resource_id'] . ' 给销售人员:' . $targetSalesman['name'] . ' (ID:' . $targetSalesman['person_id'] . ')');
|
|
}
|
|
|
|
Db::commit();
|
|
|
|
} catch (\Exception $e) {
|
|
Db::rollback();
|
|
Log::write('资源分配失败,资源ID:' . $resource['resource_id'] . ',错误:' . $e->getMessage());
|
|
}
|
|
}
|
|
|
|
Log::write('成功分配 ' . $createdCount . ' 个资源');
|
|
|
|
return [
|
|
'allocated' => $createdCount,
|
|
'updated' => 0,
|
|
'created' => $createdCount
|
|
];
|
|
}
|
|
|
|
/**
|
|
* 刷新销售人员的资源数量并重新排序(使用新表结构)
|
|
* @param array $salesmen 销售人员列表
|
|
* @param int|null $campus_id 资源所属校区ID
|
|
* @return array 更新后的销售人员列表
|
|
*/
|
|
private function refreshSalesmenResourceCount($salesmen, ?int $campus_id = null)
|
|
{
|
|
if (empty($salesmen)) {
|
|
return [];
|
|
}
|
|
|
|
foreach ($salesmen as &$salesman) {
|
|
// 使用新表查询资源数量
|
|
$resourceCount = ResourceAssignment::where('assignee_type', 'user')
|
|
->where('assignee_id', $salesman['person_id'])
|
|
->count();
|
|
$salesman['resource_count'] = $resourceCount;
|
|
}
|
|
|
|
// 如果指定了校区,按校区优先和资源数量重新排序
|
|
if ($campus_id) {
|
|
$campus_salesmen = array_filter($salesmen, function($salesman) use ($campus_id) {
|
|
return $salesman['campus_id'] == $campus_id;
|
|
});
|
|
|
|
if (!empty($campus_salesmen)) {
|
|
// 校区内销售人员按资源数量排序
|
|
array_multisort(array_column($campus_salesmen, 'resource_count'), SORT_ASC, $campus_salesmen);
|
|
|
|
// 其他销售人员按主管优先和资源数量排序
|
|
$other_salesmen = array_filter($salesmen, function($salesman) use ($campus_id) {
|
|
return $salesman['campus_id'] != $campus_id;
|
|
});
|
|
|
|
if (!empty($other_salesmen)) {
|
|
usort($other_salesmen, function($a, $b) {
|
|
// 主管优先(角色ID=7)
|
|
if ($a['role_id'] == 7 && $b['role_id'] != 7) return -1;
|
|
if ($a['role_id'] != 7 && $b['role_id'] == 7) return 1;
|
|
// 相同角色按资源数量排序
|
|
return $a['resource_count'] - $b['resource_count'];
|
|
});
|
|
}
|
|
|
|
// 返回校区优先,主管兜底的列表
|
|
return array_merge($campus_salesmen, $other_salesmen);
|
|
}
|
|
}
|
|
|
|
// 没有校区或没有校区销售人员,按主管优先和资源数量排序
|
|
usort($salesmen, function($a, $b) {
|
|
// 主管优先(角色ID=7)
|
|
if ($a['role_id'] == 7 && $b['role_id'] != 7) return -1;
|
|
if ($a['role_id'] != 7 && $b['role_id'] == 7) return 1;
|
|
// 相同角色按资源数量排序
|
|
return $a['resource_count'] - $b['resource_count'];
|
|
});
|
|
|
|
return $salesmen;
|
|
}
|
|
}
|