performanceService = new PerformanceService(); } /** * 执行任务 */ public function doJob() { // 添加执行锁,防止重复执行 $lockFile = runtime_path() . 'performance_calculation.lock'; if (file_exists($lockFile) && (time() - filemtime($lockFile)) < 1800) { // 30分钟锁定 Log::write('销售绩效核算任务正在执行中,跳过'); return ['status' => 'skipped', 'reason' => 'locked']; } // 创建锁文件 file_put_contents($lockFile, time()); try { Log::write('开始执行销售绩效核算'); // 获取所有需要计算绩效的订单 $orders = $this->getOrders(); if (empty($orders)) { Log::write('没有需要计算绩效的订单'); return ['status' => 'success', 'processed' => 0, 'message' => '没有需要计算绩效的订单']; } // 获取绩效配置 $performanceConfig = $this->getPerformanceConfig(); if (empty($performanceConfig)) { Log::write('未找到绩效配置'); return ['status' => 'failed', 'message' => '未找到绩效配置']; } // 计算每个订单的绩效 $results = []; $successCount = 0; $failedCount = 0; foreach ($orders as $order) { try { // 首先判断是否为内部员工资源 $isInternalStaffResource = $this->isInternalStaffResource($order); if ($isInternalStaffResource) { // 处理内部员工资源的绩效计算 $internalResult = $this->calculateInternalStaffPerformance($order, $performanceConfig); if (!empty($internalResult) && $internalResult['status'] == 'success') { $results[] = $internalResult; $successCount++; } else { $failedCount++; } } else { // 判断是否为多人介入的订单 $isMultiPersonInvolved = $this->isMultiPersonInvolved($order); if ($isMultiPersonInvolved) { // 处理多人介入的绩效计算 $multiResults = $this->calculateMultiPersonPerformance($order, $performanceConfig); if (!empty($multiResults)) { foreach ($multiResults as $result) { if ($result['status'] == 'success') { $results[] = $result; $successCount++; } else { $failedCount++; } } } else { $failedCount++; } } else { // 处理单人的绩效计算 $result = $this->calculateOrderPerformance($order, $performanceConfig); if (!empty($result) && $result['status'] == 'success') { $results[] = $result; $successCount++; } else { $failedCount++; } } } } catch (\Exception $e) { Log::write('处理订单绩效计算失败,订单ID:' . $order['id'] . ',错误:' . $e->getMessage()); $failedCount++; } } // 保存绩效结果 $saveResult = $this->savePerformanceResults($results); Log::write('销售绩效核算完成,共处理' . count($orders) . '个订单,成功:' . $successCount . '个,失败:' . $failedCount . '个'); return [ 'status' => 'success', 'total_orders' => count($orders), 'success_count' => $successCount, 'failed_count' => $failedCount, 'save_result' => $saveResult ]; } finally { // 删除锁文件 if (file_exists($lockFile)) { unlink($lockFile); } } } /** * 判断是否为内部员工资源 * @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->whereNull('accounting_time'); // 或者核算时间为空的订单 }) ->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 绩效计算结果 * @return array 保存结果统计 */ protected function savePerformanceResults($results) { if (empty($results)) { return ['saved' => 0, 'skipped' => 0, 'failed' => 0]; } $savedCount = 0; $skippedCount = 0; $failedCount = 0; $processedOrders = []; try { Db::startTrans(); // 按订单分组处理,避免重复更新订单状态 $orderGroups = []; foreach ($results as $result) { if ($result['status'] == 'success') { $orderGroups[$result['order_id']][] = $result; } } foreach ($orderGroups as $orderId => $orderResults) { try { // 检查订单是否已经完全处理过 $existingCount = Db::name('school_performance_summary') ->where('order_id', $orderId) ->where('performance_type', PerformanceService::PERFORMANCE_TYPE_SALES) ->count(); if ($existingCount > 0) { Log::write('订单ID:' . $orderId . ' 已存在绩效记录,跳过整个订单'); $skippedCount += count($orderResults); continue; } // 处理当前订单的所有绩效记录 $orderPerformanceAmount = 0; foreach ($orderResults as $result) { // 保存到绩效表 school_sales_performance $performanceData = [ 'personnel_id' => $result['personnel_id'], 'campus_id' => $result['campus_id'] ?? 0, 'performance_amount' => $result['performance_amount'], 'new_resource_count' => $result['new_resource_count'] ?? 0, 'renew_resource_count' => $result['renew_resource_count'] ?? 0, '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); $orderPerformanceAmount += $result['performance_amount']; $savedCount++; Log::write('成功保存绩效记录,订单ID:' . $orderId . ',员工ID:' . $result['personnel_id'] . ',绩效金额:' . $result['performance_amount']); } // 统一更新订单状态(每个订单只更新一次) OrderTable::where('id', $orderId) ->update([ 'performance_calculated' => 1, 'performance_amount' => $orderPerformanceAmount, 'accounting_time' => time() ]); $processedOrders[] = $orderId; } catch (\Exception $e) { Log::write('处理订单ID:' . $orderId . ' 的绩效记录失败:' . $e->getMessage()); $failedCount += count($orderResults); } } Db::commit(); Log::write('成功保存绩效计算结果,保存:' . $savedCount . '个,跳过:' . $skippedCount . '个,失败:' . $failedCount . '个'); Log::write('成功更新' . count($processedOrders) . '个订单的核算状态'); return [ 'saved' => $savedCount, 'skipped' => $skippedCount, 'failed' => $failedCount, 'processed_orders' => count($processedOrders) ]; } catch (\Exception $e) { Db::rollback(); Log::write('保存绩效计算结果失败:' . $e->getMessage()); return [ 'saved' => 0, 'skipped' => 0, 'failed' => count($results), 'error' => $e->getMessage() ]; } } /** * 添加绩效汇总记录 * @param array $data 绩效数据 * @return int|string 插入的ID */ public function addPerformanceSummary(array $data) { try { if ($this->performanceService) { return $this->performanceService->addPerformance($data); } else { Log::write('PerformanceService 未初始化'); return 0; } } catch (\Exception $e) { Log::write('添加绩效汇总记录失败:' . $e->getMessage()); return 0; } } }