From 15fa0d868f8c88ffa0599555a6dc78085ae1099a Mon Sep 17 00:00:00 2001 From: wangzeyan <258785420@qq.com> Date: Fri, 27 Jun 2025 07:27:48 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=AE=9A=E6=97=B6=E4=BB=BB?= =?UTF-8?q?=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apiController/CustomerResources.php | 14 +- .../controller/apiController/Statistics.php | 3 +- niucloud/app/common.php | 60 ++ niucloud/app/dict/schedule/schedule.php | 38 +- .../transfer/schedule/CourseScheduleJob.php | 108 ++- .../schedule/PerformanceCalculation.php | 829 ++++++++++++++++++ .../admin/performance/PerformanceService.php | 257 ++++++ .../apiService/CustomerResourcesService.php | 82 +- .../app/service/api/login/LoginService.php | 3 +- 9 files changed, 1315 insertions(+), 79 deletions(-) create mode 100644 niucloud/app/job/transfer/schedule/PerformanceCalculation.php create mode 100644 niucloud/app/service/admin/performance/PerformanceService.php diff --git a/niucloud/app/api/controller/apiController/CustomerResources.php b/niucloud/app/api/controller/apiController/CustomerResources.php index 53cc2dee..ca92ff54 100644 --- a/niucloud/app/api/controller/apiController/CustomerResources.php +++ b/niucloud/app/api/controller/apiController/CustomerResources.php @@ -122,9 +122,6 @@ class CustomerResources extends BaseApiService //客户资源-编辑 public function edit(Request $request) { - - $resource_sharing_id = $request->param('resource_sharing_id', '');//资源共享id - $customer_resources_id = $request->param('id', '');//客户资源表id $promised_visit_time = $request->param('promised_visit_time', ''); @@ -174,7 +171,6 @@ class CustomerResources extends BaseApiService "preferred_class_time" => $optional_class_time,//可选上课时间 "distance" => $request->param('distance', ''),//距离 "communication" => $request->param('communication', ''),//沟通备注 - "staff_id" => $request->param('staff_id', ''),//人员ID//如果没有就是当前登录人的员工id "efficacious" => $request->param('efficacious', 1), "first_visit_time" => $request->param('first_visit_time', ''), @@ -182,8 +178,9 @@ class CustomerResources extends BaseApiService "second_visit_time" => $request->param('second_visit_time', ''), "second_visit_status" => $request->param('second_visit_status', ''), "chasing_orders" => $request->param('chasing_orders', ''), - "is_bm" => $request->param('is_bm', 1), "consultation_remark" => $request->param('consultation_remark', ''), + "call_intent" => $request->param('call_intent', '') + ]; @@ -197,13 +194,6 @@ class CustomerResources extends BaseApiService $six_speed_data['staff_id'] = $this->member_id; } - foreach ($six_speed_data as $k => $v) { - // 排除 first_visit_status 和 second_visit_status 的必填校验 - if (in_array($k, ['first_visit_status', 'second_visit_status'])) { - continue; - } - } - $res = (new CustomerResourcesService())->editData($where, $customer_resources_data, $six_speed_data); if (!$res['code']) { return fail($res['msg']); diff --git a/niucloud/app/api/controller/apiController/Statistics.php b/niucloud/app/api/controller/apiController/Statistics.php index 03ef4917..18b98561 100644 --- a/niucloud/app/api/controller/apiController/Statistics.php +++ b/niucloud/app/api/controller/apiController/Statistics.php @@ -147,7 +147,8 @@ class Statistics extends BaseApiService 'yjsr' => $yjsr, 'wfpsl' => $wfpsl, 'bytc' => $bytc, - 'gdsl' => $gdsl + 'gdsl' => $gdsl, + 'qtjl' => get_staff_performance_total($personnel_id, 'sales', '', date('Y-m-01'), date('Y-m-t')) ] ]; }elseif(in_array('sale',$role_key_arr) || in_array('sale_manager',$role_key_arr)){ diff --git a/niucloud/app/common.php b/niucloud/app/common.php index 82e6605a..b755957a 100644 --- a/niucloud/app/common.php +++ b/niucloud/app/common.php @@ -12,6 +12,7 @@ use think\facade\Cache; use core\util\Snowflake; use app\service\core\upload\CoreImageService; use app\service\core\sys\CoreSysConfigService; +use app\service\admin\performance\PerformanceService; // 应用公共文件 @@ -1301,3 +1302,62 @@ function getChineseWeekday($date) return '星期' . $weekdays[$date->format('w')]; } +/** + * 添加绩效汇总记录 + * @param array $data 绩效数据 + * - staff_id: 员工ID (必填) + * - resource_id: 资源ID (必填) + * - order_id: 订单ID (可选) + * - order_status: 订单状态 (可选,默认pending) + * - performance_type: 绩效类型 (必填,如sales) + * - performance_value: 绩效金额 (必填) + * - remarks: 备注 (可选) + * @return int|string 插入的ID,失败返回0 + */ +function add_performance_summary(array $data) +{ + try { + $performanceService = new PerformanceService(); + return $performanceService->addPerformance($data); + } catch (\Exception $e) { + \think\facade\Log::write('添加绩效汇总记录失败:' . $e->getMessage()); + return 0; + } +} + +/** + * 批量添加绩效汇总记录 + * @param array $dataList 绩效数据列表 + * @return int 插入的记录数 + */ +function add_batch_performance_summary(array $dataList) +{ + try { + $performanceService = new PerformanceService(); + return $performanceService->addBatchPerformance($dataList); + } catch (\Exception $e) { + \think\facade\Log::write('批量添加绩效汇总记录失败:' . $e->getMessage()); + return 0; + } +} + +/** + * 获取员工绩效总额 + * @param int $staffId 员工ID + * @param string $performanceType 绩效类型 (可选) + * @param string $orderStatus 订单状态 (可选) + * @param string $startDate 开始日期 (可选,格式:Y-m-d) + * @param string $endDate 结束日期 (可选,格式:Y-m-d) + * @return float 绩效总额 + */ +function get_staff_performance_total(int $staffId, string $performanceType = '', string $orderStatus = '', string $startDate = '', string $endDate = '') +{ + try { + $performanceService = new PerformanceService(); + return $performanceService->calculateStaffPerformanceTotal($staffId, $performanceType, $orderStatus, $startDate, $endDate); + } catch (\Exception $e) { + \think\facade\Log::write('获取员工绩效总额失败:' . $e->getMessage()); + return 0; + } +} + diff --git a/niucloud/app/dict/schedule/schedule.php b/niucloud/app/dict/schedule/schedule.php index a73db7f2..89e52859 100644 --- a/niucloud/app/dict/schedule/schedule.php +++ b/niucloud/app/dict/schedule/schedule.php @@ -1,28 +1,6 @@ 'order_close', -// 'name' => '未支付订单自动关闭', -// 'desc' => '', -// 'time' => [ -// 'type' => 'min', -// 'min' => 1 -// ], -// 'class' => '', -// 'function' => '' -// ], -// [ -// 'key' => 'transfer_check_finish', -// 'name' => '检验在线转账是否处理完毕', -// 'desc' => '', -// 'time' => [ -// 'type' => 'min', -// 'min' => 5 -// ], -// 'class' => 'app\job\transfer\schedule\CheckFinish', -// 'function' => '' -// ], [ 'key' => 'resource_auto_allocation', 'name' => '自动分配资源', @@ -48,5 +26,19 @@ return [ ], 'class' => 'app\job\transfer\schedule\CourseScheduleJob', 'function' => '' - ] + ], + [ + 'key' => 'PerformanceCalculation', + 'name' => '核算绩效', + 'desc' => '', + 'time' => [ + 'type' => 'day', + 'day' => 1, + 'hour' => 1, + 'min' => 5 + ], + 'class' => 'app\job\transfer\schedule\PerformanceCalculation', + 'function' => '' + ], + ]; diff --git a/niucloud/app/job/transfer/schedule/CourseScheduleJob.php b/niucloud/app/job/transfer/schedule/CourseScheduleJob.php index 81d9208d..10bdeed6 100644 --- a/niucloud/app/job/transfer/schedule/CourseScheduleJob.php +++ b/niucloud/app/job/transfer/schedule/CourseScheduleJob.php @@ -4,9 +4,104 @@ namespace app\job\transfer\schedule; use app\model\course_schedule\CourseSchedule; use core\base\BaseJob; +use think\facade\Log; class CourseScheduleJob extends BaseJob { + /** + * 执行任务,将今天的自动排课复制到未来30天 + * @return array 处理结果 + */ + public function doJob() + { + Log::write('开始执行自动排课任务'); + return $this->copyCoursesToFutureDays(30); + } + + /** + * 将今天的自动排课复制到未来指定天数 + * @param int $days 未来天数 + * @return array 处理结果 + */ + public function copyCoursesToFutureDays($days = 30) + { + // 获取今天日期 + $today = date('Y-m-d'); + + // 获取所有今天auto_schedule=1的课程 + $autoSchedules = CourseSchedule::where('auto_schedule', 1) + ->where('course_date', $today) + ->select(); + + Log::write('找到' . count($autoSchedules) . '个今天需要自动排课的课程'); + + $results = [ + 'total' => count($autoSchedules), + 'inserted' => 0, + 'skipped' => 0, + 'details' => [] + ]; + + // 遍历每个课程,复制到未来30天 + foreach ($autoSchedules as $schedule) { + $courseResults = $this->copyCourseToFutureDays($schedule, $days); + $results['inserted'] += $courseResults['inserted']; + $results['skipped'] += $courseResults['skipped']; + $results['details'][] = $courseResults; + } + + Log::write('自动排课完成,共插入' . $results['inserted'] . '个课程,跳过' . $results['skipped'] . '个已存在课程'); + + return $results; + } + + /** + * 将单个课程复制到未来指定天数 + * @param CourseSchedule $schedule 课程安排 + * @param int $days 未来天数 + * @return array 处理结果 + */ + protected function copyCourseToFutureDays(CourseSchedule $schedule, $days) + { + $result = [ + 'course_id' => $schedule->course_id, + 'campus_id' => $schedule->campus_id, + 'venue_id' => $schedule->venue_id, + 'time_slot' => $schedule->time_slot, + 'inserted' => 0, + 'skipped' => 0, + 'dates' => [] + ]; + + // 从明天开始,复制到未来指定天数 + for ($i = 1; $i <= $days; $i++) { + // 计算目标日期 + $targetDate = date('Y-m-d', strtotime("+{$i} days")); + + // 检查该日期是否有相同的课程安排 + $exists = $this->checkCourseExists($schedule, $targetDate); + + if (!$exists) { + // 如果不存在,则插入新的课程安排 + $newSchedule = $this->createNewSchedule($schedule, $targetDate); + $result['inserted']++; + $result['dates'][] = [ + 'date' => $targetDate, + 'status' => 'inserted', + 'id' => $newSchedule->id + ]; + } else { + $result['skipped']++; + $result['dates'][] = [ + 'date' => $targetDate, + 'status' => 'skipped' + ]; + } + } + + return $result; + } + /** * 处理自动排课数据,按月份检查并插入不存在的课程 * @param int $month 月份(1-12) @@ -59,7 +154,7 @@ class CourseScheduleJob extends BaseJob } /** - * 检查指定月份是否有相同的课程安排 + * 检查指定日期是否有相同的课程安排 * @param CourseSchedule $schedule 原始课程安排 * @param string $courseDate 课程日期 * @return bool 是否存在 @@ -92,7 +187,16 @@ class CourseScheduleJob extends BaseJob $newSchedule->time_slot = $schedule->time_slot; $newSchedule->course_id = $schedule->course_id; $newSchedule->auto_schedule = 1; - // 复制其他需要的字段... + + // 复制其他所有字段(除了id和主键相关字段) + $attributes = $schedule->getAttributes(); + foreach ($attributes as $key => $value) { + // 跳过id和主键相关字段 + if ($key !== 'id' && $key !== 'course_date' && $key !== 'auto_schedule') { + $newSchedule->$key = $value; + } + } + $newSchedule->save(); return $newSchedule; diff --git a/niucloud/app/job/transfer/schedule/PerformanceCalculation.php b/niucloud/app/job/transfer/schedule/PerformanceCalculation.php new file mode 100644 index 00000000..0a37f859 --- /dev/null +++ b/niucloud/app/job/transfer/schedule/PerformanceCalculation.php @@ -0,0 +1,829 @@ +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; + } + } +} \ No newline at end of file diff --git a/niucloud/app/service/admin/performance/PerformanceService.php b/niucloud/app/service/admin/performance/PerformanceService.php new file mode 100644 index 00000000..9590ee9a --- /dev/null +++ b/niucloud/app/service/admin/performance/PerformanceService.php @@ -0,0 +1,257 @@ +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; + } + } +} \ No newline at end of file diff --git a/niucloud/app/service/api/apiService/CustomerResourcesService.php b/niucloud/app/service/api/apiService/CustomerResourcesService.php index cb39284d..f88b7b95 100644 --- a/niucloud/app/service/api/apiService/CustomerResourcesService.php +++ b/niucloud/app/service/api/apiService/CustomerResourcesService.php @@ -23,6 +23,7 @@ use app\model\six_speed_modification_log\SixSpeedModificationLog; use core\base\BaseApiService; use think\facade\Db; use think\facade\Event; +use think\facade\Log; /** * 客户资源服务层 @@ -196,54 +197,53 @@ class CustomerResourcesService extends BaseApiService $customer_resources_data['updated_at'] = $date; $six_speed_data['updated_at'] = $date; - //开启事物 - Db::startTrans(); + try { $customer_resources = CustomerResources::where('id', $where['id'])->find(); + $six_speed = SixSpeed::where('resource_id', $where['id'])->find(); if ($customer_resources) { $customer_resources = $customer_resources->toArray(); if (!$customer_resources['member_id'] && $six_speed) { - //新数据存在一访问 或者旧数据存在一访的情况 && 这用户没注册过member账号的情况下才给他创建member账号 - if (!empty($six_speed_data['first_visit_status']) || $six_speed['first_visit_status']) { - $sex = 0; - switch ($customer_resources_data['gender']) { - case 'male'://男 - $sex = 1; - break; - case 'female'://女 - $sex = 2; - break; - default://其他 - $sex = 0; - break; - } - $password = create_password($customer_resources_data['phone_number']);//创建密码 - //给用户创建member账号 - $member_id = Member::insertGetId([ - 'username' => $customer_resources_data['phone_number'],//会员用户名 - 'mobile' => $customer_resources_data['phone_number'],//手机号 - 'password' => $password,//会员密码 - 'nickname' => $customer_resources_data['name'],//会员昵称 - 'sex' => $sex,//性别 0保密 1男 2女 - 'member_time' => time(),//成为会员时间 - ]); - if ($member_id) { - $customer_resources_data['member_id'] = $member_id; - } else { - Db::rollback(); - $res['msg'] = '创建用户账号失败'; - return $res; - } + $sex = 0; + switch ($customer_resources_data['gender']) { + case 'male'://男 + $sex = 1; + break; + case 'female'://女 + $sex = 2; + break; + default://其他 + $sex = 0; + break; + } + $password = create_password($customer_resources_data['phone_number']);//创建密码 + //开启事物 +// Db::startTrans(); + //给用户创建member账号 + $member_id = Member::insertGetId([ + 'username' => $customer_resources_data['phone_number'],//会员用户名 + 'mobile' => $customer_resources_data['phone_number'],//手机号 + 'password' => $password,//会员密码 + 'nickname' => $customer_resources_data['name'],//会员昵称 + 'sex' => $sex,//性别 0保密 1男 2女 + 'member_time' => time(),//成为会员时间 + ]); + + if ($member_id) { + $customer_resources_data['member_id'] = $member_id; + } else { + Db::rollback(); + $res['msg'] = '创建用户账号失败'; + return $res; } - } } $update_1 = CustomerResources::where('id', $where['id'])->update($customer_resources_data);//客户资源表 if (!$update_1) { - Db::rollback(); +// Db::rollback(); return $res; } @@ -262,7 +262,7 @@ class CustomerResourcesService extends BaseApiService $id = CustomerResourceChanges::insertGetId($data); if (!$id) { - Db::rollback(); +// Db::rollback(); return $res; } } @@ -276,7 +276,7 @@ class CustomerResourcesService extends BaseApiService //更新六要素 $sixSpeedUpdate = SixSpeed::where('id', $six_speed['id'])->update($six_speed_data); if (!$sixSpeedUpdate) { - Db::rollback(); +// Db::rollback(); return $res; } @@ -294,7 +294,7 @@ class CustomerResourcesService extends BaseApiService $id = SixSpeedModificationLog::insertGetId($data); if (!$id) { - Db::rollback(); +// Db::rollback(); return $res; } } @@ -302,12 +302,12 @@ class CustomerResourcesService extends BaseApiService //创建六要素 $sixSpeedUpdate = SixSpeed::create($six_speed_data); if (!$sixSpeedUpdate) { - Db::rollback(); +// Db::rollback(); return $res; } } - Db::commit(); +// Db::commit(); $res = [ 'code' => 1, 'msg' => '操作成功' @@ -315,6 +315,8 @@ class CustomerResourcesService extends BaseApiService return $res; } catch (\Exception $exception) { Db::rollback(); + dd($exception); + Log::error(print_r($exception, true)); return $exception->getMessage(); } } diff --git a/niucloud/app/service/api/login/LoginService.php b/niucloud/app/service/api/login/LoginService.php index 2908673c..fd51c820 100644 --- a/niucloud/app/service/api/login/LoginService.php +++ b/niucloud/app/service/api/login/LoginService.php @@ -376,7 +376,8 @@ class LoginService extends BaseApiService return [ 'token' => $token_info['token'],//token 'expires_time' => $token_info['params']['exp'],//过期时间 - 'user_type' => $user_type//用户类型 + 'user_type' => $user_type,//用户类型 + 'userinfo' => $member_info, ]; } }