Browse Source

新增定时任务

master
王泽彦 9 months ago
parent
commit
15fa0d868f
  1. 14
      niucloud/app/api/controller/apiController/CustomerResources.php
  2. 3
      niucloud/app/api/controller/apiController/Statistics.php
  3. 60
      niucloud/app/common.php
  4. 38
      niucloud/app/dict/schedule/schedule.php
  5. 108
      niucloud/app/job/transfer/schedule/CourseScheduleJob.php
  6. 829
      niucloud/app/job/transfer/schedule/PerformanceCalculation.php
  7. 257
      niucloud/app/service/admin/performance/PerformanceService.php
  8. 82
      niucloud/app/service/api/apiService/CustomerResourcesService.php
  9. 3
      niucloud/app/service/api/login/LoginService.php

14
niucloud/app/api/controller/apiController/CustomerResources.php

@ -122,9 +122,6 @@ class CustomerResources extends BaseApiService
//客户资源-编辑 //客户资源-编辑
public function edit(Request $request) public function edit(Request $request)
{ {
$resource_sharing_id = $request->param('resource_sharing_id', '');//资源共享id
$customer_resources_id = $request->param('id', '');//客户资源表id $customer_resources_id = $request->param('id', '');//客户资源表id
$promised_visit_time = $request->param('promised_visit_time', ''); $promised_visit_time = $request->param('promised_visit_time', '');
@ -174,7 +171,6 @@ class CustomerResources extends BaseApiService
"preferred_class_time" => $optional_class_time,//可选上课时间 "preferred_class_time" => $optional_class_time,//可选上课时间
"distance" => $request->param('distance', ''),//距离 "distance" => $request->param('distance', ''),//距离
"communication" => $request->param('communication', ''),//沟通备注 "communication" => $request->param('communication', ''),//沟通备注
"staff_id" => $request->param('staff_id', ''),//人员ID//如果没有就是当前登录人的员工id
"efficacious" => $request->param('efficacious', 1), "efficacious" => $request->param('efficacious', 1),
"first_visit_time" => $request->param('first_visit_time', ''), "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_time" => $request->param('second_visit_time', ''),
"second_visit_status" => $request->param('second_visit_status', ''), "second_visit_status" => $request->param('second_visit_status', ''),
"chasing_orders" => $request->param('chasing_orders', ''), "chasing_orders" => $request->param('chasing_orders', ''),
"is_bm" => $request->param('is_bm', 1),
"consultation_remark" => $request->param('consultation_remark', ''), "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; $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); $res = (new CustomerResourcesService())->editData($where, $customer_resources_data, $six_speed_data);
if (!$res['code']) { if (!$res['code']) {
return fail($res['msg']); return fail($res['msg']);

3
niucloud/app/api/controller/apiController/Statistics.php

@ -147,7 +147,8 @@ class Statistics extends BaseApiService
'yjsr' => $yjsr, 'yjsr' => $yjsr,
'wfpsl' => $wfpsl, 'wfpsl' => $wfpsl,
'bytc' => $bytc, '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)){ }elseif(in_array('sale',$role_key_arr) || in_array('sale_manager',$role_key_arr)){

60
niucloud/app/common.php

@ -12,6 +12,7 @@ use think\facade\Cache;
use core\util\Snowflake; use core\util\Snowflake;
use app\service\core\upload\CoreImageService; use app\service\core\upload\CoreImageService;
use app\service\core\sys\CoreSysConfigService; use app\service\core\sys\CoreSysConfigService;
use app\service\admin\performance\PerformanceService;
// 应用公共文件 // 应用公共文件
@ -1301,3 +1302,62 @@ function getChineseWeekday($date)
return '星期' . $weekdays[$date->format('w')]; 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;
}
}

38
niucloud/app/dict/schedule/schedule.php

@ -1,28 +1,6 @@
<?php <?php
return [ return [
// [
// 'key' => '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', 'key' => 'resource_auto_allocation',
'name' => '自动分配资源', 'name' => '自动分配资源',
@ -48,5 +26,19 @@ return [
], ],
'class' => 'app\job\transfer\schedule\CourseScheduleJob', 'class' => 'app\job\transfer\schedule\CourseScheduleJob',
'function' => '' 'function' => ''
] ],
[
'key' => 'PerformanceCalculation',
'name' => '核算绩效',
'desc' => '',
'time' => [
'type' => 'day',
'day' => 1,
'hour' => 1,
'min' => 5
],
'class' => 'app\job\transfer\schedule\PerformanceCalculation',
'function' => ''
],
]; ];

108
niucloud/app/job/transfer/schedule/CourseScheduleJob.php

@ -4,9 +4,104 @@ namespace app\job\transfer\schedule;
use app\model\course_schedule\CourseSchedule; use app\model\course_schedule\CourseSchedule;
use core\base\BaseJob; use core\base\BaseJob;
use think\facade\Log;
class CourseScheduleJob extends BaseJob 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) * @param int $month 月份(1-12)
@ -59,7 +154,7 @@ class CourseScheduleJob extends BaseJob
} }
/** /**
* 检查指定月份是否有相同的课程安排 * 检查指定日期是否有相同的课程安排
* @param CourseSchedule $schedule 原始课程安排 * @param CourseSchedule $schedule 原始课程安排
* @param string $courseDate 课程日期 * @param string $courseDate 课程日期
* @return bool 是否存在 * @return bool 是否存在
@ -92,7 +187,16 @@ class CourseScheduleJob extends BaseJob
$newSchedule->time_slot = $schedule->time_slot; $newSchedule->time_slot = $schedule->time_slot;
$newSchedule->course_id = $schedule->course_id; $newSchedule->course_id = $schedule->course_id;
$newSchedule->auto_schedule = 1; $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(); $newSchedule->save();
return $newSchedule; return $newSchedule;

829
niucloud/app/job/transfer/schedule/PerformanceCalculation.php

@ -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;
}
}
}

257
niucloud/app/service/admin/performance/PerformanceService.php

@ -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;
}
}
}

82
niucloud/app/service/api/apiService/CustomerResourcesService.php

@ -23,6 +23,7 @@ use app\model\six_speed_modification_log\SixSpeedModificationLog;
use core\base\BaseApiService; use core\base\BaseApiService;
use think\facade\Db; use think\facade\Db;
use think\facade\Event; use think\facade\Event;
use think\facade\Log;
/** /**
* 客户资源服务层 * 客户资源服务层
@ -196,54 +197,53 @@ class CustomerResourcesService extends BaseApiService
$customer_resources_data['updated_at'] = $date; $customer_resources_data['updated_at'] = $date;
$six_speed_data['updated_at'] = $date; $six_speed_data['updated_at'] = $date;
//开启事物
Db::startTrans();
try { try {
$customer_resources = CustomerResources::where('id', $where['id'])->find(); $customer_resources = CustomerResources::where('id', $where['id'])->find();
$six_speed = SixSpeed::where('resource_id', $where['id'])->find(); $six_speed = SixSpeed::where('resource_id', $where['id'])->find();
if ($customer_resources) { if ($customer_resources) {
$customer_resources = $customer_resources->toArray(); $customer_resources = $customer_resources->toArray();
if (!$customer_resources['member_id'] && $six_speed) { if (!$customer_resources['member_id'] && $six_speed) {
//新数据存在一访问 或者旧数据存在一访的情况 && 这用户没注册过member账号的情况下才给他创建member账号 $sex = 0;
if (!empty($six_speed_data['first_visit_status']) || $six_speed['first_visit_status']) { switch ($customer_resources_data['gender']) {
$sex = 0; case 'male'://男
switch ($customer_resources_data['gender']) { $sex = 1;
case 'male'://男 break;
$sex = 1; case 'female'://女
break; $sex = 2;
case 'female'://女 break;
$sex = 2; default://其他
break; $sex = 0;
default://其他 break;
$sex = 0; }
break; $password = create_password($customer_resources_data['phone_number']);//创建密码
} //开启事物
$password = create_password($customer_resources_data['phone_number']);//创建密码 // Db::startTrans();
//给用户创建member账号 //给用户创建member账号
$member_id = Member::insertGetId([ $member_id = Member::insertGetId([
'username' => $customer_resources_data['phone_number'],//会员用户名 'username' => $customer_resources_data['phone_number'],//会员用户名
'mobile' => $customer_resources_data['phone_number'],//手机号 'mobile' => $customer_resources_data['phone_number'],//手机号
'password' => $password,//会员密码 'password' => $password,//会员密码
'nickname' => $customer_resources_data['name'],//会员昵称 'nickname' => $customer_resources_data['name'],//会员昵称
'sex' => $sex,//性别 0保密 1男 2女 'sex' => $sex,//性别 0保密 1男 2女
'member_time' => time(),//成为会员时间 'member_time' => time(),//成为会员时间
]); ]);
if ($member_id) {
$customer_resources_data['member_id'] = $member_id; if ($member_id) {
} else { $customer_resources_data['member_id'] = $member_id;
Db::rollback(); } else {
$res['msg'] = '创建用户账号失败'; Db::rollback();
return $res; $res['msg'] = '创建用户账号失败';
} return $res;
} }
} }
} }
$update_1 = CustomerResources::where('id', $where['id'])->update($customer_resources_data);//客户资源表 $update_1 = CustomerResources::where('id', $where['id'])->update($customer_resources_data);//客户资源表
if (!$update_1) { if (!$update_1) {
Db::rollback(); // Db::rollback();
return $res; return $res;
} }
@ -262,7 +262,7 @@ class CustomerResourcesService extends BaseApiService
$id = CustomerResourceChanges::insertGetId($data); $id = CustomerResourceChanges::insertGetId($data);
if (!$id) { if (!$id) {
Db::rollback(); // Db::rollback();
return $res; return $res;
} }
} }
@ -276,7 +276,7 @@ class CustomerResourcesService extends BaseApiService
//更新六要素 //更新六要素
$sixSpeedUpdate = SixSpeed::where('id', $six_speed['id'])->update($six_speed_data); $sixSpeedUpdate = SixSpeed::where('id', $six_speed['id'])->update($six_speed_data);
if (!$sixSpeedUpdate) { if (!$sixSpeedUpdate) {
Db::rollback(); // Db::rollback();
return $res; return $res;
} }
@ -294,7 +294,7 @@ class CustomerResourcesService extends BaseApiService
$id = SixSpeedModificationLog::insertGetId($data); $id = SixSpeedModificationLog::insertGetId($data);
if (!$id) { if (!$id) {
Db::rollback(); // Db::rollback();
return $res; return $res;
} }
} }
@ -302,12 +302,12 @@ class CustomerResourcesService extends BaseApiService
//创建六要素 //创建六要素
$sixSpeedUpdate = SixSpeed::create($six_speed_data); $sixSpeedUpdate = SixSpeed::create($six_speed_data);
if (!$sixSpeedUpdate) { if (!$sixSpeedUpdate) {
Db::rollback(); // Db::rollback();
return $res; return $res;
} }
} }
Db::commit(); // Db::commit();
$res = [ $res = [
'code' => 1, 'code' => 1,
'msg' => '操作成功' 'msg' => '操作成功'
@ -315,6 +315,8 @@ class CustomerResourcesService extends BaseApiService
return $res; return $res;
} catch (\Exception $exception) { } catch (\Exception $exception) {
Db::rollback(); Db::rollback();
dd($exception);
Log::error(print_r($exception, true));
return $exception->getMessage(); return $exception->getMessage();
} }
} }

3
niucloud/app/service/api/login/LoginService.php

@ -376,7 +376,8 @@ class LoginService extends BaseApiService
return [ return [
'token' => $token_info['token'],//token 'token' => $token_info['token'],//token
'expires_time' => $token_info['params']['exp'],//过期时间 'expires_time' => $token_info['params']['exp'],//过期时间
'user_type' => $user_type//用户类型 'user_type' => $user_type,//用户类型
'userinfo' => $member_info,
]; ];
} }
} }

Loading…
Cancel
Save