9 changed files with 1315 additions and 79 deletions
@ -0,0 +1,829 @@ |
|||
<?php |
|||
|
|||
namespace app\job\transfer\schedule; |
|||
|
|||
use app\model\course\Course; |
|||
use app\model\order_table\OrderTable; |
|||
use app\model\personnel\Personnel; |
|||
use app\model\resource_sharing\ResourceSharing; |
|||
use app\model\six_speed\SixSpeed; |
|||
use app\model\customer_resources\CustomerResources; |
|||
use app\service\admin\performance\PerformanceService; |
|||
use app\service\api\apiService\CommonService; |
|||
use core\base\BaseJob; |
|||
use think\facade\Db; |
|||
use think\facade\Log; |
|||
|
|||
/** |
|||
* 销售绩效核算 |
|||
* Class PerformanceCalculation |
|||
* @package app\job\transfer\schedule |
|||
*/ |
|||
class PerformanceCalculation extends BaseJob |
|||
{ |
|||
/** |
|||
* 员工工龄阶段 |
|||
*/ |
|||
const STAGE_TRIAL = '试用期1'; // 试用期 |
|||
const STAGE_REGULAR = '转正'; // 转正 |
|||
const STAGE_HALF_YEAR = '转正后半年'; // 转正后半年 |
|||
|
|||
/** |
|||
* 资源来源类型 |
|||
*/ |
|||
const SOURCE_INTERNAL_STAFF = 4; // 内部员工资源 |
|||
|
|||
/** |
|||
* 绩效服务类实例 |
|||
* @var PerformanceService |
|||
*/ |
|||
protected $performanceService; |
|||
|
|||
/** |
|||
* 构造函数 |
|||
*/ |
|||
public function __construct() |
|||
{ |
|||
parent::__construct(); |
|||
$this->performanceService = new PerformanceService(); |
|||
} |
|||
|
|||
/** |
|||
* 执行任务 |
|||
*/ |
|||
public function doJob() |
|||
{ |
|||
Log::write('开始执行销售绩效核算'); |
|||
|
|||
// 获取所有需要计算绩效的订单 |
|||
$orders = $this->getOrders(); |
|||
if (empty($orders)) { |
|||
Log::write('没有需要计算绩效的订单'); |
|||
return; |
|||
} |
|||
|
|||
// 获取绩效配置 |
|||
$performanceConfig = $this->getPerformanceConfig(); |
|||
if (empty($performanceConfig)) { |
|||
Log::write('未找到绩效配置'); |
|||
return; |
|||
} |
|||
|
|||
// 计算每个订单的绩效 |
|||
$results = []; |
|||
foreach ($orders as $order) { |
|||
// 首先判断是否为内部员工资源 |
|||
$isInternalStaffResource = $this->isInternalStaffResource($order); |
|||
|
|||
if ($isInternalStaffResource) { |
|||
// 处理内部员工资源的绩效计算 |
|||
$internalResult = $this->calculateInternalStaffPerformance($order, $performanceConfig); |
|||
if (!empty($internalResult)) { |
|||
$results[] = $internalResult; |
|||
} |
|||
} else { |
|||
// 判断是否为多人介入的订单 |
|||
$isMultiPersonInvolved = $this->isMultiPersonInvolved($order); |
|||
|
|||
if ($isMultiPersonInvolved) { |
|||
// 处理多人介入的绩效计算 |
|||
$multiResults = $this->calculateMultiPersonPerformance($order, $performanceConfig); |
|||
if (!empty($multiResults)) { |
|||
$results = array_merge($results, $multiResults); |
|||
} |
|||
} else { |
|||
// 处理单人的绩效计算 |
|||
$result = $this->calculateOrderPerformance($order, $performanceConfig); |
|||
$results[] = $result; |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 保存绩效结果 |
|||
$this->savePerformanceResults($results); |
|||
|
|||
Log::write('销售绩效核算完成,共处理' . count($results) . '个订单'); |
|||
|
|||
return $results; |
|||
} |
|||
|
|||
/** |
|||
* 判断是否为内部员工资源 |
|||
* @param array $order 订单信息 |
|||
* @return bool 是否为内部员工资源 |
|||
*/ |
|||
protected function isInternalStaffResource($order) |
|||
{ |
|||
if (empty($order['resource_id'])) { |
|||
return false; |
|||
} |
|||
|
|||
// 查询资源表中的记录 |
|||
$resource = CustomerResources::where('id', $order['resource_id'])->find(); |
|||
|
|||
// 如果没有找到资源记录,则不是内部员工资源 |
|||
if (empty($resource)) { |
|||
return false; |
|||
} |
|||
|
|||
// 判断资源来源是否为内部员工 |
|||
return isset($resource['source']) && $resource['source'] == self::SOURCE_INTERNAL_STAFF; |
|||
} |
|||
|
|||
/** |
|||
* 计算内部员工资源的绩效 |
|||
* @param array $order 订单信息 |
|||
* @param array $config 绩效配置 |
|||
* @return array 绩效计算结果 |
|||
*/ |
|||
protected function calculateInternalStaffPerformance($order, $config) |
|||
{ |
|||
$staffId = $order['staff_id']; |
|||
$campusId = isset($order['campus_id']) ? $order['campus_id'] : 0; |
|||
$resourceId = isset($order['resource_id']) ? $order['resource_id'] : 0; |
|||
|
|||
// 获取员工信息 |
|||
$staff = Personnel::where('id', $staffId)->find(); |
|||
if (empty($staff)) { |
|||
Log::write('未找到员工信息,员工ID:' . $staffId); |
|||
return [ |
|||
'order_id' => $order['id'], |
|||
'staff_id' => $staffId, |
|||
'performance_amount' => 0, |
|||
'status' => 'failed', |
|||
'message' => '未找到员工信息' |
|||
]; |
|||
} |
|||
|
|||
// 获取课程类型前缀 |
|||
$courseType = $this->getCourseTypePrefix($order); |
|||
|
|||
// 获取内部员工绩效配置 |
|||
$xspjConfig = $config['XSPJ']; |
|||
$internalStaffKey = $courseType . '_internalStaff'; |
|||
$performanceAmount = isset($xspjConfig[$internalStaffKey]) ? floatval($xspjConfig[$internalStaffKey]) : 0; |
|||
|
|||
if ($performanceAmount <= 0) { |
|||
Log::write('未找到有效的内部员工绩效配置,订单ID:' . $order['id']); |
|||
return [ |
|||
'order_id' => $order['id'], |
|||
'staff_id' => $staffId, |
|||
'performance_amount' => 0, |
|||
'status' => 'failed', |
|||
'message' => '未找到有效的内部员工绩效配置' |
|||
]; |
|||
} |
|||
|
|||
// 判断订单类型(新订单或续费) |
|||
$isRenewal = $this->isRenewalOrder($order); |
|||
|
|||
// 获取新资源数和续费资源数 |
|||
$newResourceCount = $isRenewal ? 0 : 1; |
|||
$renewResourceCount = $isRenewal ? 1 : 0; |
|||
|
|||
// 保存使用的绩效配置和算法 |
|||
$performanceConfig = json_encode([ |
|||
'is_renewal' => $isRenewal, |
|||
'is_internal_staff' => true, |
|||
'performance_key' => $internalStaffKey |
|||
], JSON_UNESCAPED_UNICODE); |
|||
|
|||
$performanceAlgorithm = json_encode([ |
|||
'order_id' => $order['id'], |
|||
'resource_id' => $resourceId, |
|||
'internal_staff_resource' => true |
|||
], JSON_UNESCAPED_UNICODE); |
|||
|
|||
// 添加一条绩效汇总记录 |
|||
$this->addPerformanceSummary([ |
|||
'staff_id' => $staffId, |
|||
'resource_id' => $resourceId, |
|||
'order_id' => $order['id'], |
|||
'order_status' => PerformanceService::ORDER_STATUS_PENDING, |
|||
'performance_type' => PerformanceService::PERFORMANCE_TYPE_SALES, |
|||
'performance_value' => $performanceAmount, |
|||
'remarks' => '内部员工关单绩效' |
|||
]); |
|||
|
|||
return [ |
|||
'order_id' => $order['id'], |
|||
'staff_id' => $staffId, |
|||
'personnel_id' => $staffId, |
|||
'campus_id' => $campusId, |
|||
'resource_id' => $resourceId, |
|||
'is_renewal' => $isRenewal, |
|||
'performance_amount' => $performanceAmount, |
|||
'new_resource_count' => $newResourceCount, |
|||
'renew_resource_count' => $renewResourceCount, |
|||
'performance_date' => date('Y-m-d'), |
|||
'performance_config' => $performanceConfig, |
|||
'performance_algorithm' => $performanceAlgorithm, |
|||
'remarks' => '内部员工关单绩效', |
|||
'status' => 'success', |
|||
'created_at' => time(), |
|||
'updated_at' => time() |
|||
]; |
|||
} |
|||
|
|||
/** |
|||
* 获取需要计算绩效的订单 |
|||
* @return array 订单列表 |
|||
*/ |
|||
protected function getOrders() |
|||
{ |
|||
// 这里可以根据实际需求筛选需要计算绩效的订单 |
|||
// 例如:只计算已完成的订单、特定时间段内的订单等 |
|||
$orders = OrderTable::with(['course', 'personnel']) |
|||
->where('order_status', 'completed') // 假设只计算已完成的订单 |
|||
->where(function ($query) { |
|||
$query->where('performance_calculated', 0) // 未计算过绩效的订单 |
|||
->whereOr('accounting_time', null); // 或者核算时间为空的订单 |
|||
}) |
|||
->select() |
|||
->toArray(); |
|||
|
|||
// 额外检查:过滤掉已经在绩效汇总表中存在的订单 |
|||
if (!empty($orders)) { |
|||
$orderIds = array_column($orders, 'id'); |
|||
$existingOrderIds = Db::name('school_performance_summary') |
|||
->whereIn('order_id', $orderIds) |
|||
->where('performance_type', PerformanceService::PERFORMANCE_TYPE_SALES) |
|||
->column('order_id'); |
|||
|
|||
if (!empty($existingOrderIds)) { |
|||
Log::write('发现' . count($existingOrderIds) . '个订单已在绩效汇总表中存在,将被跳过'); |
|||
$orders = array_filter($orders, function($order) use ($existingOrderIds) { |
|||
return !in_array($order['id'], $existingOrderIds); |
|||
}); |
|||
} |
|||
} |
|||
|
|||
Log::write('找到' . count($orders) . '个需要计算绩效的订单'); |
|||
return $orders; |
|||
} |
|||
|
|||
/** |
|||
* 获取绩效配置 |
|||
* @return array 绩效配置 |
|||
*/ |
|||
protected function getPerformanceConfig() |
|||
{ |
|||
$commonService = new CommonService(); |
|||
|
|||
// 获取销售基础绩效配置 |
|||
$xsyjConfig = $commonService->getDictionary(['key' => 'XSYJ']); |
|||
|
|||
// 获取多人介入绩效配置 |
|||
$xspjConfig = $commonService->getDictionary(['key' => 'XSPJ']); |
|||
|
|||
// 获取课程基础绩效配置 |
|||
$courseTypeConfig = $commonService->getDictionary(['key' => 'course_type']); |
|||
|
|||
return [ |
|||
'XSYJ' => $xsyjConfig, |
|||
'XSPJ' => $xspjConfig, |
|||
'course_type' => $courseTypeConfig |
|||
]; |
|||
} |
|||
|
|||
/** |
|||
* 判断是否为多人介入的订单 |
|||
* @param array $order 订单信息 |
|||
* @return bool 是否为多人介入 |
|||
*/ |
|||
protected function isMultiPersonInvolved($order) |
|||
{ |
|||
if (empty($order['resource_id']) || empty($order['staff_id'])) { |
|||
return false; |
|||
} |
|||
|
|||
// 查询资源共享表中的记录 |
|||
$resourceSharing = ResourceSharing::where('resource_id', $order['resource_id']) |
|||
->where('shared_by', '<>', 0) |
|||
->order('id', 'asc') |
|||
->find(); |
|||
|
|||
// 如果没有找到资源共享记录,则不是多人介入 |
|||
if (empty($resourceSharing)) { |
|||
return false; |
|||
} |
|||
|
|||
// 如果资源共享记录中的user_id与订单的staff_id不同,则是多人介入 |
|||
return $resourceSharing['user_id'] != $order['staff_id']; |
|||
} |
|||
|
|||
/** |
|||
* 计算多人介入的绩效 |
|||
* @param array $order 订单信息 |
|||
* @param array $config 绩效配置 |
|||
* @return array 绩效计算结果数组 |
|||
*/ |
|||
protected function calculateMultiPersonPerformance($order, $config) |
|||
{ |
|||
$results = []; |
|||
$resourceId = $order['resource_id']; |
|||
$staffId = $order['staff_id']; |
|||
|
|||
// 查询资源共享表中的记录(资源归属人) |
|||
$resourceOwner = ResourceSharing::where('resource_id', $resourceId) |
|||
->where('shared_by', '<>', 0) |
|||
->order('id', 'asc') |
|||
->find(); |
|||
|
|||
if (empty($resourceOwner)) { |
|||
Log::write('未找到资源归属人,订单ID:' . $order['id']); |
|||
return []; |
|||
} |
|||
|
|||
$resourceOwnerId = $resourceOwner['user_id']; |
|||
|
|||
// 查询六速表中的访问记录 |
|||
$sixSpeed = SixSpeed::where('resource_id', $resourceId)->find(); |
|||
|
|||
// 确定介入类型 |
|||
$visitType = 'followUp'; // 默认为跟进 |
|||
$visitTypeDesc = '跟进关单'; |
|||
|
|||
if (!empty($sixSpeed)) { |
|||
if (!empty($sixSpeed['first_visit_time'])) { |
|||
$visitType = 'firstVisit'; |
|||
$visitTypeDesc = '一访关单'; |
|||
} elseif (!empty($sixSpeed['second_visit_time'])) { |
|||
$visitType = 'secondVisit'; |
|||
$visitTypeDesc = '二访关单'; |
|||
} |
|||
} |
|||
|
|||
// 获取课程类型前缀 |
|||
$courseType = $this->getCourseTypePrefix($order); |
|||
|
|||
// 获取多人介入绩效配置 |
|||
$xspjConfig = $config['XSPJ']; |
|||
$percentageKey = $courseType . '_' . $visitType; |
|||
$percentage = isset($xspjConfig[$percentageKey]) ? floatval($xspjConfig[$percentageKey]) : 0; |
|||
|
|||
if ($percentage <= 0) { |
|||
Log::write('未找到有效的多人介入绩效配置,订单ID:' . $order['id']); |
|||
return []; |
|||
} |
|||
|
|||
// 计算订单的基础绩效 |
|||
$basePerformance = $this->calculateOrderBasePerformance($order, $config); |
|||
|
|||
// 计算完成订单人的绩效 |
|||
$staffPerformance = $basePerformance * ($percentage / 100); |
|||
|
|||
// 计算资源归属人的绩效 |
|||
$ownerPerformance = $basePerformance * (1 - $percentage / 100); |
|||
|
|||
// 创建完成订单人的绩效记录 |
|||
$staffResult = $this->createPerformanceResult($order, $staffId, $staffPerformance, $visitTypeDesc . '(完成订单人)'); |
|||
$results[] = $staffResult; |
|||
|
|||
// 创建资源归属人的绩效记录 |
|||
$ownerResult = $this->createPerformanceResult($order, $resourceOwnerId, $ownerPerformance, $visitTypeDesc . '(资源归属人)'); |
|||
$results[] = $ownerResult; |
|||
|
|||
return $results; |
|||
} |
|||
|
|||
/** |
|||
* 获取课程类型前缀 |
|||
* @param array $order 订单信息 |
|||
* @return string 课程类型前缀(xj, xf, qt) |
|||
*/ |
|||
protected function getCourseTypePrefix($order) |
|||
{ |
|||
// 判断订单类型 |
|||
if (isset($order['order_type'])) { |
|||
if ($order['order_type'] == 1) { |
|||
return 'xj'; // 新建 |
|||
} elseif ($order['order_type'] == 2) { |
|||
return 'xf'; // 续费 |
|||
} |
|||
} |
|||
|
|||
return 'qt'; // 其他 |
|||
} |
|||
|
|||
/** |
|||
* 计算订单的基础绩效金额 |
|||
* @param array $order 订单信息 |
|||
* @param array $config 绩效配置 |
|||
* @return float 基础绩效金额 |
|||
*/ |
|||
protected function calculateOrderBasePerformance($order, $config) |
|||
{ |
|||
// 获取员工信息 |
|||
$staff = Personnel::where('id', $order['staff_id'])->find(); |
|||
if (empty($staff)) { |
|||
return 0; |
|||
} |
|||
|
|||
// 获取课程信息 |
|||
$course = Course::where('id', $order['course_id'])->find(); |
|||
if (empty($course)) { |
|||
return 0; |
|||
} |
|||
|
|||
// 判断员工工龄阶段 |
|||
$employmentStage = $this->getEmploymentStage($staff); |
|||
|
|||
// 判断订单类型(新订单或续费) |
|||
$isRenewal = $this->isRenewalOrder($order); |
|||
|
|||
// 计算绩效 |
|||
return $this->calculatePerformance($order, $course, $employmentStage, $isRenewal, $config); |
|||
} |
|||
|
|||
/** |
|||
* 创建绩效计算结果 |
|||
* @param array $order 订单信息 |
|||
* @param int $staffId 员工ID |
|||
* @param float $performance 绩效金额 |
|||
* @param string $remarks 备注 |
|||
* @return array 绩效计算结果 |
|||
*/ |
|||
protected function createPerformanceResult($order, $staffId, $performance, $remarks) |
|||
{ |
|||
$campusId = isset($order['campus_id']) ? $order['campus_id'] : 0; |
|||
$resourceId = isset($order['resource_id']) ? $order['resource_id'] : 0; |
|||
|
|||
// 获取员工信息 |
|||
$staff = Personnel::where('id', $staffId)->find(); |
|||
if (empty($staff)) { |
|||
return [ |
|||
'order_id' => $order['id'], |
|||
'staff_id' => $staffId, |
|||
'performance_amount' => 0, |
|||
'status' => 'failed', |
|||
'message' => '未找到员工信息' |
|||
]; |
|||
} |
|||
|
|||
// 判断员工工龄阶段 |
|||
$employmentStage = $this->getEmploymentStage($staff); |
|||
|
|||
// 判断订单类型(新订单或续费) |
|||
$isRenewal = $this->isRenewalOrder($order); |
|||
|
|||
// 获取新资源数和续费资源数 |
|||
$newResourceCount = $isRenewal ? 0 : 1; |
|||
$renewResourceCount = $isRenewal ? 1 : 0; |
|||
|
|||
// 保存使用的绩效配置和算法 |
|||
$performanceConfig = json_encode([ |
|||
'employment_stage' => $employmentStage, |
|||
'is_renewal' => $isRenewal, |
|||
'remarks' => $remarks |
|||
], JSON_UNESCAPED_UNICODE); |
|||
|
|||
$performanceAlgorithm = json_encode([ |
|||
'order_id' => $order['id'], |
|||
'resource_id' => $resourceId, |
|||
'multi_person_involved' => true |
|||
], JSON_UNESCAPED_UNICODE); |
|||
|
|||
// 添加一条绩效汇总记录 |
|||
$this->addPerformanceSummary([ |
|||
'staff_id' => $staffId, |
|||
'resource_id' => $resourceId, |
|||
'order_id' => $order['id'], |
|||
'order_status' => PerformanceService::ORDER_STATUS_PENDING, |
|||
'performance_type' => PerformanceService::PERFORMANCE_TYPE_SALES, |
|||
'performance_value' => $performance, |
|||
'remarks' => $remarks |
|||
]); |
|||
|
|||
return [ |
|||
'order_id' => $order['id'], |
|||
'staff_id' => $staffId, |
|||
'personnel_id' => $staffId, |
|||
'campus_id' => $campusId, |
|||
'resource_id' => $resourceId, |
|||
'employment_stage' => $employmentStage, |
|||
'is_renewal' => $isRenewal, |
|||
'performance_amount' => $performance, |
|||
'new_resource_count' => $newResourceCount, |
|||
'renew_resource_count' => $renewResourceCount, |
|||
'performance_date' => date('Y-m-d'), |
|||
'performance_config' => $performanceConfig, |
|||
'performance_algorithm' => $performanceAlgorithm, |
|||
'remarks' => $remarks, |
|||
'status' => 'success', |
|||
'created_at' => time(), |
|||
'updated_at' => time() |
|||
]; |
|||
} |
|||
|
|||
/** |
|||
* 计算单个订单的绩效 |
|||
* @param array $order 订单信息 |
|||
* @param array $config 绩效配置 |
|||
* @return array 绩效计算结果 |
|||
*/ |
|||
protected function calculateOrderPerformance($order, $config) |
|||
{ |
|||
$staffId = $order['staff_id']; |
|||
$courseId = $order['course_id']; |
|||
$campusId = isset($order['campus_id']) ? $order['campus_id'] : 0; |
|||
$resourceId = isset($order['resource_id']) ? $order['resource_id'] : 0; |
|||
|
|||
// 获取员工信息 |
|||
$staff = Personnel::where('id', $staffId)->find(); |
|||
if (empty($staff)) { |
|||
Log::write('未找到员工信息,员工ID:' . $staffId); |
|||
return [ |
|||
'order_id' => $order['id'], |
|||
'staff_id' => $staffId, |
|||
'performance_amount' => 0, |
|||
'status' => 'failed', |
|||
'message' => '未找到员工信息' |
|||
]; |
|||
} |
|||
|
|||
// 获取课程信息 |
|||
$course = Course::where('id', $courseId)->find(); |
|||
if (empty($course)) { |
|||
Log::write('未找到课程信息,课程ID:' . $courseId); |
|||
return [ |
|||
'order_id' => $order['id'], |
|||
'staff_id' => $staffId, |
|||
'performance_amount' => 0, |
|||
'status' => 'failed', |
|||
'message' => '未找到课程信息' |
|||
]; |
|||
} |
|||
|
|||
// 判断员工工龄阶段 |
|||
$employmentStage = $this->getEmploymentStage($staff); |
|||
|
|||
// 判断订单类型(新订单或续费) |
|||
$isRenewal = $this->isRenewalOrder($order); |
|||
|
|||
// 计算绩效 |
|||
$performance = $this->calculatePerformance($order, $course, $employmentStage, $isRenewal, $config); |
|||
|
|||
// 获取新资源数和续费资源数 |
|||
$newResourceCount = $isRenewal ? 0 : 1; |
|||
$renewResourceCount = $isRenewal ? 1 : 0; |
|||
|
|||
// 保存使用的绩效配置和算法 |
|||
$performanceConfig = json_encode([ |
|||
'employment_stage' => $employmentStage, |
|||
'is_renewal' => $isRenewal, |
|||
'course_type' => $course['course_type'] |
|||
], JSON_UNESCAPED_UNICODE); |
|||
|
|||
$performanceAlgorithm = json_encode([ |
|||
'base_commission' => $isRenewal ? 'renewal_commission' : 'course_type_num', |
|||
'order_id' => $order['id'], |
|||
'course_id' => $courseId |
|||
], JSON_UNESCAPED_UNICODE); |
|||
|
|||
// 同时添加一条绩效汇总记录 |
|||
$this->addPerformanceSummary([ |
|||
'staff_id' => $staffId, |
|||
'resource_id' => $resourceId, |
|||
'order_id' => $order['id'], |
|||
'order_status' => PerformanceService::ORDER_STATUS_PENDING, |
|||
'performance_type' => PerformanceService::PERFORMANCE_TYPE_SALES, |
|||
'performance_value' => $performance, |
|||
'remarks' => '订单绩效自动计算' |
|||
]); |
|||
|
|||
return [ |
|||
'order_id' => $order['id'], |
|||
'staff_id' => $staffId, |
|||
'personnel_id' => $staffId, |
|||
'campus_id' => $campusId, |
|||
'course_id' => $courseId, |
|||
'employment_stage' => $employmentStage, |
|||
'is_renewal' => $isRenewal, |
|||
'performance_amount' => $performance, |
|||
'new_resource_count' => $newResourceCount, |
|||
'renew_resource_count' => $renewResourceCount, |
|||
'performance_date' => date('Y-m-d'), |
|||
'performance_config' => $performanceConfig, |
|||
'performance_algorithm' => $performanceAlgorithm, |
|||
'status' => 'success', |
|||
'created_at' => time(), |
|||
'updated_at' => time() |
|||
]; |
|||
} |
|||
|
|||
/** |
|||
* 判断员工工龄阶段 |
|||
* @param object $staff 员工信息 |
|||
* @return string 工龄阶段 |
|||
*/ |
|||
protected function getEmploymentStage($staff) |
|||
{ |
|||
$joinTime = strtotime($staff['join_time']); |
|||
$now = time(); |
|||
$monthsDiff = floor(($now - $joinTime) / (30 * 24 * 60 * 60)); |
|||
|
|||
if ($monthsDiff < 3) { |
|||
return self::STAGE_TRIAL; // 试用期 |
|||
} else if ($monthsDiff < 6) { |
|||
return self::STAGE_REGULAR; // 转正 |
|||
} else { |
|||
return self::STAGE_HALF_YEAR; // 转正后半年 |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 判断是否为续费订单 |
|||
* @param array $order 订单信息 |
|||
* @return bool 是否为续费订单 |
|||
*/ |
|||
protected function isRenewalOrder($order) |
|||
{ |
|||
// 这里需要根据实际业务逻辑判断是否为续费订单 |
|||
// 假设订单中有一个字段标记是否为续费 |
|||
return isset($order['order_type']) && $order['order_type'] == 2; |
|||
} |
|||
|
|||
/** |
|||
* 计算绩效金额 |
|||
* @param array $order 订单信息 |
|||
* @param object $course 课程信息 |
|||
* @param string $employmentStage 工龄阶段 |
|||
* @param bool $isRenewal 是否为续费订单 |
|||
* @param array $config 绩效配置 |
|||
* @return float 绩效金额 |
|||
*/ |
|||
protected function calculatePerformance($order, $course, $employmentStage, $isRenewal, $config) |
|||
{ |
|||
// 获取对应工龄阶段的配置 |
|||
$stageConfig = null; |
|||
foreach ($config['XSYJ'] as $item) { |
|||
if ($item['name'] == $employmentStage) { |
|||
$stageConfig = $item; |
|||
break; |
|||
} |
|||
} |
|||
|
|||
if (empty($stageConfig)) { |
|||
Log::write('未找到对应工龄阶段的配置:' . $employmentStage); |
|||
return 0; |
|||
} |
|||
|
|||
// 获取课程类型配置 |
|||
$courseTypeConfig = null; |
|||
$courseType = $course['course_type']; |
|||
foreach ($config['course_type'] as $item) { |
|||
if ($item['value'] == $courseType) { |
|||
$courseTypeConfig = $item; |
|||
break; |
|||
} |
|||
} |
|||
|
|||
if (empty($courseTypeConfig)) { |
|||
Log::write('未找到对应课程类型的配置:' . $courseType); |
|||
return 0; |
|||
} |
|||
|
|||
// 获取基础提成金额 |
|||
$baseCommission = $courseTypeConfig['num']; |
|||
|
|||
// 如果是续费订单,根据续费率计算提成 |
|||
if ($isRenewal) { |
|||
// 获取续费率 |
|||
$renewalRate = $this->getRenewalRate($order['staff_id']); |
|||
|
|||
// 查找适用的规则 |
|||
$rule = null; |
|||
foreach ($stageConfig['rules'] as $r) { |
|||
$minRate = floatval($r['renewal_standard_min']); |
|||
$maxRate = !empty($r['renewal_standard_max']) ? floatval($r['renewal_standard_max']) : PHP_FLOAT_MAX; |
|||
|
|||
if ($renewalRate >= $minRate && $renewalRate < $maxRate) { |
|||
$rule = $r; |
|||
break; |
|||
} |
|||
} |
|||
|
|||
if (!empty($rule)) { |
|||
return floatval($rule['renewal_commission']); |
|||
} |
|||
} else { |
|||
// 新订单,直接返回基础提成 |
|||
return floatval($baseCommission); |
|||
} |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
/** |
|||
* 获取员工的续费率 |
|||
* @param int $staffId 员工ID |
|||
* @return float 续费率 |
|||
*/ |
|||
protected function getRenewalRate($staffId) |
|||
{ |
|||
// 这里需要根据实际业务逻辑计算员工的续费率 |
|||
// 假设有一个表记录了员工的续费率 |
|||
$rate = 95.0; // 默认值 |
|||
|
|||
// TODO: 从数据库中获取实际续费率 |
|||
|
|||
return $rate; |
|||
} |
|||
|
|||
/** |
|||
* 保存绩效计算结果 |
|||
* @param array $results 绩效计算结果 |
|||
*/ |
|||
protected function savePerformanceResults($results) |
|||
{ |
|||
if (empty($results)) { |
|||
return; |
|||
} |
|||
|
|||
try { |
|||
Db::startTrans(); |
|||
|
|||
foreach ($results as $result) { |
|||
if ($result['status'] == 'success') { |
|||
// 先检查订单是否已经计算过绩效 |
|||
$existingRecord = Db::name('school_performance_summary') |
|||
->where('order_id', $result['order_id']) |
|||
->where('staff_id', $result['staff_id']) |
|||
->where('performance_type', PerformanceService::PERFORMANCE_TYPE_SALES) |
|||
->find(); |
|||
|
|||
if ($existingRecord) { |
|||
Log::write('订单ID:' . $result['order_id'] . ' 员工ID:' . $result['staff_id'] . ' 已存在绩效记录,跳过'); |
|||
continue; |
|||
} |
|||
|
|||
// 更新订单表,标记已计算绩效并记录核算时间 |
|||
// 只有在处理完所有绩效记录后才更新订单表的核算状态 |
|||
if (!isset($result['is_multi_person']) || !$result['is_multi_person']) { |
|||
OrderTable::where('id', $result['order_id']) |
|||
->update([ |
|||
'performance_calculated' => 1, |
|||
'performance_amount' => $result['performance_amount'], |
|||
'accounting_time' => time() // 记录核算时间 |
|||
]); |
|||
} |
|||
|
|||
// 保存到绩效表 school_sales_performance |
|||
$performanceData = [ |
|||
'personnel_id' => $result['personnel_id'], |
|||
'campus_id' => $result['campus_id'], |
|||
'performance_amount' => $result['performance_amount'], |
|||
'new_resource_count' => $result['new_resource_count'], |
|||
'renew_resource_count' => $result['renew_resource_count'], |
|||
'performance_date' => $result['performance_date'], |
|||
'performance_config' => $result['performance_config'], |
|||
'performance_algorithm' => $result['performance_algorithm'], |
|||
'created_at' => $result['created_at'], |
|||
'updated_at' => $result['updated_at'] |
|||
]; |
|||
|
|||
Db::name('school_sales_performance')->insert($performanceData); |
|||
|
|||
Log::write('成功保存绩效记录,员工ID:' . $result['personnel_id'] . ',绩效金额:' . $result['performance_amount'] . ',核算时间:' . date('Y-m-d H:i:s', time())); |
|||
} |
|||
} |
|||
|
|||
// 更新所有已处理订单的状态 |
|||
$orderIds = array_unique(array_column(array_filter($results, function($result) { |
|||
return $result['status'] == 'success'; |
|||
}), 'order_id')); |
|||
|
|||
if (!empty($orderIds)) { |
|||
OrderTable::whereIn('id', $orderIds) |
|||
->update([ |
|||
'performance_calculated' => 1, |
|||
'accounting_time' => time() |
|||
]); |
|||
|
|||
Log::write('成功更新' . count($orderIds) . '个订单的核算状态'); |
|||
} |
|||
|
|||
Db::commit(); |
|||
Log::write('成功保存绩效计算结果'); |
|||
} catch (\Exception $e) { |
|||
Db::rollback(); |
|||
Log::write('保存绩效计算结果失败:' . $e->getMessage()); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 添加绩效汇总记录 |
|||
* @param array $data 绩效数据 |
|||
* @return int|string 插入的ID |
|||
*/ |
|||
public function addPerformanceSummary(array $data) |
|||
{ |
|||
try { |
|||
return $this->performanceService->addPerformance($data); |
|||
} catch (\Exception $e) { |
|||
Log::write('添加绩效汇总记录失败:' . $e->getMessage()); |
|||
return 0; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,257 @@ |
|||
<?php |
|||
// +---------------------------------------------------------------------- |
|||
// | Niucloud-admin 企业快速开发的多应用管理平台 |
|||
// +---------------------------------------------------------------------- |
|||
// | 官方网址:https://www.niucloud.com |
|||
// +---------------------------------------------------------------------- |
|||
// | niucloud团队 版权所有 开源版本可自由商用 |
|||
// +---------------------------------------------------------------------- |
|||
// | Author: Niucloud Team |
|||
// +---------------------------------------------------------------------- |
|||
|
|||
namespace app\service\admin\performance; |
|||
|
|||
use core\base\BaseService; |
|||
use think\facade\Db; |
|||
use think\facade\Log; |
|||
|
|||
/** |
|||
* 绩效服务类 |
|||
* Class PerformanceService |
|||
* @package app\service\admin\performance |
|||
*/ |
|||
class PerformanceService extends BaseService |
|||
{ |
|||
/** |
|||
* 绩效类型:销售绩效 |
|||
*/ |
|||
const PERFORMANCE_TYPE_SALES = 'sales'; |
|||
|
|||
/** |
|||
* 绩效类型:市场绩效 |
|||
*/ |
|||
const PERFORMANCE_TYPE_MARKETING = 'marketing'; |
|||
|
|||
/** |
|||
* 订单状态:待处理 |
|||
*/ |
|||
const ORDER_STATUS_PENDING = 'pending'; |
|||
|
|||
/** |
|||
* 订单状态:已完成 |
|||
*/ |
|||
const ORDER_STATUS_COMPLETED = 'completed'; |
|||
|
|||
/** |
|||
* 添加绩效记录 |
|||
* @param array $data 绩效数据 |
|||
* @return int|string 插入的ID |
|||
*/ |
|||
public function addPerformance(array $data) |
|||
{ |
|||
try { |
|||
// 必填字段验证 |
|||
$requiredFields = ['staff_id', 'resource_id', 'performance_type', 'performance_value']; |
|||
foreach ($requiredFields as $field) { |
|||
if (!isset($data[$field]) || empty($data[$field])) { |
|||
throw new \Exception('缺少必填字段:' . $field); |
|||
} |
|||
} |
|||
|
|||
// 默认值设置 |
|||
if (!isset($data['order_status'])) { |
|||
$data['order_status'] = self::ORDER_STATUS_PENDING; |
|||
} |
|||
|
|||
if (!isset($data['created_at'])) { |
|||
$data['created_at'] = time(); |
|||
} |
|||
|
|||
if (!isset($data['updated_at'])) { |
|||
$data['updated_at'] = time(); |
|||
} |
|||
|
|||
// 插入数据 |
|||
$id = Db::name('school_performance_summary')->insertGetId($data); |
|||
|
|||
Log::write('成功添加绩效记录,ID:' . $id . ',员工ID:' . $data['staff_id'] . ',绩效类型:' . $data['performance_type']); |
|||
|
|||
return $id; |
|||
} catch (\Exception $e) { |
|||
Log::write('添加绩效记录失败:' . $e->getMessage()); |
|||
throw $e; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 更新绩效记录 |
|||
* @param int $id 记录ID |
|||
* @param array $data 更新数据 |
|||
* @return bool 是否成功 |
|||
*/ |
|||
public function updatePerformance(int $id, array $data) |
|||
{ |
|||
try { |
|||
if (empty($id)) { |
|||
throw new \Exception('缺少绩效记录ID'); |
|||
} |
|||
|
|||
// 更新时间 |
|||
if (!isset($data['updated_at'])) { |
|||
$data['updated_at'] = time(); |
|||
} |
|||
|
|||
// 更新数据 |
|||
$result = Db::name('school_performance_summary')->where('id', $id)->update($data); |
|||
|
|||
Log::write('成功更新绩效记录,ID:' . $id); |
|||
|
|||
return $result !== false; |
|||
} catch (\Exception $e) { |
|||
Log::write('更新绩效记录失败:' . $e->getMessage()); |
|||
throw $e; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 批量添加绩效记录 |
|||
* @param array $dataList 绩效数据列表 |
|||
* @return int 插入的记录数 |
|||
*/ |
|||
public function addBatchPerformance(array $dataList) |
|||
{ |
|||
try { |
|||
if (empty($dataList)) { |
|||
return 0; |
|||
} |
|||
|
|||
// 设置默认值 |
|||
foreach ($dataList as &$data) { |
|||
if (!isset($data['order_status'])) { |
|||
$data['order_status'] = self::ORDER_STATUS_PENDING; |
|||
} |
|||
|
|||
if (!isset($data['created_at'])) { |
|||
$data['created_at'] = time(); |
|||
} |
|||
|
|||
if (!isset($data['updated_at'])) { |
|||
$data['updated_at'] = time(); |
|||
} |
|||
} |
|||
|
|||
// 批量插入 |
|||
$count = Db::name('school_performance_summary')->insertAll($dataList); |
|||
|
|||
Log::write('成功批量添加绩效记录,数量:' . $count); |
|||
|
|||
return $count; |
|||
} catch (\Exception $e) { |
|||
Log::write('批量添加绩效记录失败:' . $e->getMessage()); |
|||
throw $e; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 获取绩效记录 |
|||
* @param int $id 记录ID |
|||
* @return array|null 绩效记录 |
|||
*/ |
|||
public function getPerformance(int $id) |
|||
{ |
|||
try { |
|||
if (empty($id)) { |
|||
throw new \Exception('缺少绩效记录ID'); |
|||
} |
|||
|
|||
$record = Db::name('school_performance_summary')->where('id', $id)->find(); |
|||
|
|||
return $record; |
|||
} catch (\Exception $e) { |
|||
Log::write('获取绩效记录失败:' . $e->getMessage()); |
|||
throw $e; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 获取员工绩效列表 |
|||
* @param int $staffId 员工ID |
|||
* @param string $performanceType 绩效类型 |
|||
* @param string $orderStatus 订单状态 |
|||
* @param string $startDate 开始日期 |
|||
* @param string $endDate 结束日期 |
|||
* @return array 绩效列表 |
|||
*/ |
|||
public function getStaffPerformanceList(int $staffId, string $performanceType = '', string $orderStatus = '', string $startDate = '', string $endDate = '') |
|||
{ |
|||
try { |
|||
$query = Db::name('school_performance_summary')->where('staff_id', $staffId); |
|||
|
|||
if (!empty($performanceType)) { |
|||
$query->where('performance_type', $performanceType); |
|||
} |
|||
|
|||
if (!empty($orderStatus)) { |
|||
$query->where('order_status', $orderStatus); |
|||
} |
|||
|
|||
if (!empty($startDate)) { |
|||
$startTime = strtotime($startDate . ' 00:00:00'); |
|||
$query->where('created_at', '>=', $startTime); |
|||
} |
|||
|
|||
if (!empty($endDate)) { |
|||
$endTime = strtotime($endDate . ' 23:59:59'); |
|||
$query->where('created_at', '<=', $endTime); |
|||
} |
|||
|
|||
$list = $query->order('created_at', 'desc')->select()->toArray(); |
|||
|
|||
return $list; |
|||
} catch (\Exception $e) { |
|||
Log::write('获取员工绩效列表失败:' . $e->getMessage()); |
|||
throw $e; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 计算员工绩效总额 |
|||
* @param int $staffId 员工ID |
|||
* @param string $performanceType 绩效类型 |
|||
* @param string $orderStatus 订单状态 |
|||
* @param string $startDate 开始日期 |
|||
* @param string $endDate 结束日期 |
|||
* @return float 绩效总额 |
|||
*/ |
|||
public function calculateStaffPerformanceTotal(int $staffId, string $performanceType = '', string $orderStatus = '', string $startDate = '', string $endDate = '') |
|||
{ |
|||
try { |
|||
$query = Db::name('school_performance_summary')->where('staff_id', $staffId); |
|||
|
|||
if (!empty($performanceType)) { |
|||
$query->where('performance_type', $performanceType); |
|||
} |
|||
|
|||
if (!empty($orderStatus)) { |
|||
$query->where('order_status', $orderStatus); |
|||
} |
|||
|
|||
if (!empty($startDate)) { |
|||
$startTime = strtotime($startDate . ' 00:00:00'); |
|||
$query->where('created_at', '>=', $startTime); |
|||
} |
|||
|
|||
if (!empty($endDate)) { |
|||
$endTime = strtotime($endDate . ' 23:59:59'); |
|||
$query->where('created_at', '<=', $endTime); |
|||
} |
|||
|
|||
$total = $query->sum('performance_value'); |
|||
|
|||
return floatval($total); |
|||
} catch (\Exception $e) { |
|||
Log::write('计算员工绩效总额失败:' . $e->getMessage()); |
|||
throw $e; |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue