From 4f07de429dd87116774397170bbf6c3ac3e0b0a4 Mon Sep 17 00:00:00 2001 From: wangzeyan <258785420@qq.com> Date: Thu, 26 Jun 2025 08:17:33 +0800 Subject: [PATCH 1/5] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E8=8E=B7=E5=8F=96?= =?UTF-8?q?=E8=B5=84=E6=BA=90=E5=88=97=E8=A1=A8=E7=9A=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/command/ClientCommand/TestCommand.php | 15 ++ niucloud/app/command/TestCommand.php | 29 ++++ niucloud/app/dict/schedule/schedule.php | 47 +++-- .../job/custmer/ResourceAutoAllocation.php | 20 --- .../schedule/ResourceAutoAllocation.php | 163 ++++++++++++++++-- niucloud/config/console.php | 2 + 6 files changed, 227 insertions(+), 49 deletions(-) create mode 100644 niucloud/app/command/ClientCommand/TestCommand.php create mode 100644 niucloud/app/command/TestCommand.php delete mode 100644 niucloud/app/job/custmer/ResourceAutoAllocation.php diff --git a/niucloud/app/command/ClientCommand/TestCommand.php b/niucloud/app/command/ClientCommand/TestCommand.php new file mode 100644 index 00000000..dce27442 --- /dev/null +++ b/niucloud/app/command/ClientCommand/TestCommand.php @@ -0,0 +1,15 @@ +writeln('test'); + } +} \ No newline at end of file diff --git a/niucloud/app/command/TestCommand.php b/niucloud/app/command/TestCommand.php new file mode 100644 index 00000000..c31f9458 --- /dev/null +++ b/niucloud/app/command/TestCommand.php @@ -0,0 +1,29 @@ +setName('testcommand') + ->setDescription('the testcommand command'); + } + + protected function execute(Input $input, Output $output) + { + // 指令输出 + $obj = new ResourceAutoAllocation(); + $obj->doJob(); + $output->writeln('testcommand'); + } +} diff --git a/niucloud/app/dict/schedule/schedule.php b/niucloud/app/dict/schedule/schedule.php index 7709b7e4..a73db7f2 100644 --- a/niucloud/app/dict/schedule/schedule.php +++ b/niucloud/app/dict/schedule/schedule.php @@ -1,31 +1,44 @@ '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' => 'order_close', - 'name' => '未支付订单自动关闭', - 'desc' => '', - 'time' => [ - 'type' => 'min', - 'min' => 1 - ], - 'class' => '', - 'function' => '' - ], - [ - 'key' => 'transfer_check_finish', - 'name' => '检验在线转账是否处理完毕', + 'key' => 'resource_auto_allocation', + 'name' => '自动分配资源', 'desc' => '', 'time' => [ - 'type' => 'min', + 'type' => 'day', + 'day' => 1, + 'hour' => 0, 'min' => 5 ], 'class' => 'app\job\transfer\schedule\CheckFinish', 'function' => '' ], [ - 'key' => 'resource_auto_allocation', - 'name' => '自动分配资源', + 'key' => 'course_schedule_job', + 'name' => '自动排课', 'desc' => '', 'time' => [ 'type' => 'day', @@ -33,7 +46,7 @@ return [ 'hour' => 0, 'min' => 5 ], - 'class' => 'app\job\transfer\schedule\CheckFinish', + 'class' => 'app\job\transfer\schedule\CourseScheduleJob', 'function' => '' ] ]; diff --git a/niucloud/app/job/custmer/ResourceAutoAllocation.php b/niucloud/app/job/custmer/ResourceAutoAllocation.php deleted file mode 100644 index fb26380c..00000000 --- a/niucloud/app/job/custmer/ResourceAutoAllocation.php +++ /dev/null @@ -1,20 +0,0 @@ - - //遍历资源列表按照资源数量的最少的人员优先分配 - return true; - } -} \ No newline at end of file diff --git a/niucloud/app/job/transfer/schedule/ResourceAutoAllocation.php b/niucloud/app/job/transfer/schedule/ResourceAutoAllocation.php index 0ec2256e..4641b933 100644 --- a/niucloud/app/job/transfer/schedule/ResourceAutoAllocation.php +++ b/niucloud/app/job/transfer/schedule/ResourceAutoAllocation.php @@ -2,7 +2,10 @@ namespace app\job\transfer\schedule; +use app\model\campus_person_role\CampusPersonRole; +use app\model\resource_sharing\ResourceSharing; use core\base\BaseJob; +use think\facade\Db; use think\facade\Log; /** @@ -10,37 +13,173 @@ use think\facade\Log; */ class ResourceAutoAllocation extends BaseJob { + /** + * 执行任务 + */ public function doJob() { - Log::write('自动分配资源'); + Log::write('开始自动分配资源'); + + // 获取待分配的资源 + $resources = $this->getResource(); + if (empty($resources)) { + Log::write('没有可分配的资源'); + return; + } + + // 获取销售人员 + $salesmen = $this->getSalesman(); + if (empty($salesmen)) { + Log::write('没有可用的销售人员'); + return; + } + + // 分配资源 + $this->allocateResource($resources, $salesmen); + + Log::write('资源分配完成'); } /** - * 获取销售人员 + * 获取销售人员(角色ID为6或7的人员) + * @return array 销售人员列表 */ public function getSalesman() { Log::write('获取销售人员'); + + // 获取角色ID为6或7的人员ID + $salesmen = CampusPersonRole::where('role_id', 'in', [6, 7]) + ->where('deleted_at', 0) // 未删除的记录 + ->field('person_id, role_id') + ->select() + ->toArray(); + + if (empty($salesmen)) { + Log::write('未找到销售人员'); + return []; + } + + // 获取每个销售人员当前拥有的资源数量 + foreach ($salesmen as &$salesman) { + $resourceCount = ResourceSharing::where('shared_by', $salesman['person_id']) + ->count(); + $salesman['resource_count'] = $resourceCount; + } + + // 按资源数量升序排序,资源少的排在前面 + array_multisort(array_column($salesmen, 'resource_count'), SORT_ASC, $salesmen); + + Log::write('找到' . count($salesmen) . '个销售人员'); + return $salesmen; } /** - * 获取资源 + * 获取待分配的资源 + * @return array 待分配资源列表 */ public function getResource() { - Log::write('获取资源'); - course_schedule - ::where('status', 1)->select(); + Log::write('获取待分配资源'); + + // 获取role_id不是6,7的,shared_by是0的资源ID + $resources = ResourceSharing::where(function ($query) { + $query->where('role_id', 'not in', [6, 7]) + ->whereOr('role_id', 'null'); + }) + ->where('shared_by', 0) + ->field('id, resource_id') + ->select() + ->toArray(); + + Log::write('找到' . count($resources) . '个待分配资源'); + return $resources; } /** - * 按照现在销售人员的资源拥有情况分配资源 + * 按照销售人员的资源拥有情况分配资源 + * @param array $resources 待分配的资源列表 + * @param array $salesmen 销售人员列表 */ - public function allocateResource() + public function allocateResource($resources, $salesmen) { - Log::write('按照现在销售人员的资源拥有情况分配资源'); + Log::write('按照销售人员的资源拥有情况分配资源'); + + if (empty($resources) || empty($salesmen)) { + Log::write('没有资源或销售人员,无法分配'); + return; + } + + // 记录分配结果 + $allocations = []; + + // 开始分配 + foreach ($resources as $resource) { + // 重新获取销售人员的资源数量排序,确保每次分配都是给最少资源的人 + $currentSalesmen = $this->refreshSalesmenResourceCount($salesmen); + + if (empty($currentSalesmen)) { + Log::write('没有可用的销售人员'); + break; + } + + // 选择资源最少的销售人员 + $targetSalesman = $currentSalesmen[0]; + + // 插入新的资源共享记录 + try { + Db::startTrans(); + + // 插入新的资源分配记录 + $insertData = [ + 'resource_id' => $resource['resource_id'], + 'user_id' => $targetSalesman['person_id'], + 'role_id' => $targetSalesman['role_id'], + 'shared_by' => $targetSalesman['person_id'], // shared_by是接收资源的人员ID + 'shared_at' => date('Y-m-d H:i:s') + ]; + + ResourceSharing::create($insertData); + + // 记录分配结果 + $allocations[] = [ + 'resource_id' => $resource['resource_id'], + 'salesman_id' => $targetSalesman['person_id'] + ]; + + Db::commit(); + + Log::write('资源ID:' . $resource['resource_id'] . ' 分配给销售人员ID:' . $targetSalesman['person_id']); + + } catch (\Exception $e) { + Db::rollback(); + Log::write('资源分配失败:' . $e->getMessage()); + } + } + + Log::write('成功分配' . count($allocations) . '个资源'); + } + + /** + * 刷新销售人员的资源数量并重新排序 + * @param array $salesmen 销售人员列表 + * @return array 更新后的销售人员列表 + */ + private function refreshSalesmenResourceCount($salesmen) + { + if (empty($salesmen)) { + return []; + } + + foreach ($salesmen as &$salesman) { + $resourceCount = ResourceSharing::where('shared_by', $salesman['person_id']) + ->count(); + $salesman['resource_count'] = $resourceCount; + } + + // 按资源数量升序排序 + array_multisort(array_column($salesmen, 'resource_count'), SORT_ASC, $salesmen); + + return $salesmen; } - - - } \ No newline at end of file diff --git a/niucloud/config/console.php b/niucloud/config/console.php index 244e1bf5..d2d266f8 100644 --- a/niucloud/config/console.php +++ b/niucloud/config/console.php @@ -19,6 +19,8 @@ $data = [ //wokrerman的启动停止和重启 'workerman' => 'app\command\workerman\Workerman', + + 'testcommand'=>'app\command\TestCommand' ], ]; return (new DictLoader("Console"))->load($data); 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 2/5] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=AE=9A=E6=97=B6?= =?UTF-8?q?=E4=BB=BB=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, ]; } } From 44b785686cc490a88cffe8040a7d1a5ae506f84f Mon Sep 17 00:00:00 2001 From: wangzeyan <258785420@qq.com> Date: Fri, 27 Jun 2025 10:26:29 +0800 Subject: [PATCH 3/5] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/controller/apiController/Course.php | 9 ++++ niucloud/app/api/route/route.php | 2 + niucloud/app/command/TestCommand.php | 3 +- niucloud/app/dict/schedule/schedule.php | 14 ++++- .../app/job/schedule/HandleCourseSchedule.php | 43 +++++++++++++++ .../transfer/schedule/CourseScheduleJob.php | 2 +- .../service/api/apiService/CourseService.php | 52 +++++++++++++++++-- 7 files changed, 119 insertions(+), 6 deletions(-) create mode 100644 niucloud/app/job/schedule/HandleCourseSchedule.php diff --git a/niucloud/app/api/controller/apiController/Course.php b/niucloud/app/api/controller/apiController/Course.php index 80829641..99ce3419 100644 --- a/niucloud/app/api/controller/apiController/Course.php +++ b/niucloud/app/api/controller/apiController/Course.php @@ -114,5 +114,14 @@ class Course extends BaseApiService return success((new CourseService())->schedule_list($data)); } + public function schedule_del(Request $request) + { + $data = $this->request->params([ + ["id", ''], + ["resources_id", ''], + ["remark", ''] + ]); + return (new CourseService())->schedule_del($data); + } } diff --git a/niucloud/app/api/route/route.php b/niucloud/app/api/route/route.php index ca9eeb89..9b49719e 100644 --- a/niucloud/app/api/route/route.php +++ b/niucloud/app/api/route/route.php @@ -341,6 +341,8 @@ Route::group(function () { Route::get('course/scheduleList', 'apiController.course/schedule_list'); + Route::post('course/schedule_del', 'apiController.course/schedule_del'); + Route::get('per_list_call_up', 'member.Member/list_call_up'); Route::post('per_update_call_up', 'member.Member/update_call_up'); diff --git a/niucloud/app/command/TestCommand.php b/niucloud/app/command/TestCommand.php index c31f9458..31e30875 100644 --- a/niucloud/app/command/TestCommand.php +++ b/niucloud/app/command/TestCommand.php @@ -3,6 +3,7 @@ declare (strict_types = 1); namespace app\command; +use app\job\transfer\schedule\CourseScheduleJob; use app\job\transfer\schedule\ResourceAutoAllocation; use think\console\Command; use think\console\Input; @@ -22,7 +23,7 @@ class TestCommand extends Command protected function execute(Input $input, Output $output) { // 指令输出 - $obj = new ResourceAutoAllocation(); + $obj = new CourseScheduleJob(); $obj->doJob(); $output->writeln('testcommand'); } diff --git a/niucloud/app/dict/schedule/schedule.php b/niucloud/app/dict/schedule/schedule.php index 89e52859..af8c5baf 100644 --- a/niucloud/app/dict/schedule/schedule.php +++ b/niucloud/app/dict/schedule/schedule.php @@ -40,5 +40,17 @@ return [ 'class' => 'app\job\transfer\schedule\PerformanceCalculation', 'function' => '' ], - + [ + 'key' => 'HandleCourseSchedule', + 'name' => '处理课程状态和学员状态', + 'desc' => '', + 'time' => [ + 'type' => 'day', + 'day' => 1, + 'hour' => 0, + 'min' => 5 + ], + 'class' => 'app\job\schedule\HandleCourseSchedule', + 'function' => '' + ], ]; diff --git a/niucloud/app/job/schedule/HandleCourseSchedule.php b/niucloud/app/job/schedule/HandleCourseSchedule.php new file mode 100644 index 00000000..cce3f58a --- /dev/null +++ b/niucloud/app/job/schedule/HandleCourseSchedule.php @@ -0,0 +1,43 @@ +handleCourseStatus(); + return true; + } + + private function handleCourseStatus() + { + $list = CourseSchedule::where('course_date','<',date('Y-m-d'))->select(); + if (!empty($list)) { + foreach ($list as $item) { + CourseSchedule::update([ + 'status' => 'completed' + ], [ + 'id' => $item['id'] + ]); + } + } + } +} diff --git a/niucloud/app/job/transfer/schedule/CourseScheduleJob.php b/niucloud/app/job/transfer/schedule/CourseScheduleJob.php index 10bdeed6..e3c410fe 100644 --- a/niucloud/app/job/transfer/schedule/CourseScheduleJob.php +++ b/niucloud/app/job/transfer/schedule/CourseScheduleJob.php @@ -189,7 +189,7 @@ class CourseScheduleJob extends BaseJob $newSchedule->auto_schedule = 1; // 复制其他所有字段(除了id和主键相关字段) - $attributes = $schedule->getAttributes(); + $attributes = $schedule->toArray(); // 使用toArray()替代getAttributes() foreach ($attributes as $key => $value) { // 跳过id和主键相关字段 if ($key !== 'id' && $key !== 'course_date' && $key !== 'auto_schedule') { diff --git a/niucloud/app/service/api/apiService/CourseService.php b/niucloud/app/service/api/apiService/CourseService.php index c7eb1868..8a366ccf 100644 --- a/niucloud/app/service/api/apiService/CourseService.php +++ b/niucloud/app/service/api/apiService/CourseService.php @@ -263,7 +263,8 @@ class CourseService extends BaseApiService $query->select(); },'venue' => function($query) { $query->select(); - },'campus','studentCourses'])->select()->toArray(); + },'campus','studentCourses']) + ->select()->toArray(); foreach ($list as $k => $v) { $student = Db::name('person_course_schedule') ->alias('pcs') @@ -307,12 +308,57 @@ class CourseService extends BaseApiService $list = $personCourseSchedule ->alias('a') ->join(['school_customer_resources' => 'b'],'a.resources_id = b.id','left') + ->join(['school_course_schedule' => 'c'],'c.id = a.schedule_id','left') ->where('a.schedule_id',$data['schedule_id']) - ->field("b.name,a.status") - ->select()->toArray(); + ->field("b.name,a.status,a.person_type,c.campus_id,b.id as resources_id") + ->select() + ->toArray(); return $list; } + public function schedule_del(array $data) + { + $personCourseSchedule = new PersonCourseSchedule(); + + // 查询记录 + $record = $personCourseSchedule->where([ + 'schedule_id' => $data['id'], + 'resources_id' => $data['resources_id'] + ])->find(); + + if (!$record) { + return fail('未找到相关记录'); + } + + // 根据person_type执行不同操作 + if ($record['person_type'] == 'customer_resource') { + // 如果是客户资源类型,直接删除记录 + $personCourseSchedule->where([ + 'schedule_id' => $data['id'], + 'resources_id' => $data['resources_id'] + ])->delete(); + + // 更新课程安排表的可用容量 + $CourseSchedule = new CourseSchedule(); + $CourseSchedule->where(['id' => $record['schedule_id']])->inc("available_capacity")->update(); + + return success('删除成功'); + } else if ($record['person_type'] == 'student') { + // 如果是学生类型,更新状态为2并更新备注 + $personCourseSchedule->where([ + 'id' => $data['id'], + 'resources_id' => $data['resources_id'] + ])->update([ + 'status' => 2, + 'remark' => $data['remark'] + ]); + + return success('更新成功'); + } else { + return fail('未知的人员类型'); + } + } + } From 58cde53c4a48d142f6a92161e9746197f849dbbb Mon Sep 17 00:00:00 2001 From: wangzeyan <258785420@qq.com> Date: Fri, 27 Jun 2025 13:57:39 +0800 Subject: [PATCH 4/5] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=AE=9A=E6=97=B6?= =?UTF-8?q?=E4=BB=BB=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 273 ++++---- .../views/service/components/service-edit.vue | 636 +++++++++--------- admin/src/app/views/service/service.vue | 465 ++++++------- niucloud/app/api/route/route.php | 2 +- .../transfer/schedule/CourseScheduleJob.php | 5 +- 5 files changed, 703 insertions(+), 678 deletions(-) diff --git a/README.md b/README.md index 119db50a..216e4685 100644 --- a/README.md +++ b/README.md @@ -1,144 +1,133 @@ -![输入图片说明](https://media.niucloud.com/1712133019020249f332e83457a01a6799472e0495_aliyun.png) +# 智慧教务系统 + +![系统Logo](https://media.niucloud.com/1712133019020249f332e83457a01a6799472e0495_aliyun.png) + +## 项目概述 + +智慧教务系统是一套基于ThinkPHP 8和Vue3开发的现代化教育管理平台,专为教育培训机构、学校等教育组织设计。系统提供全面的学生管理、课程管理、排课管理、校区管理、场地管理、人员管理、合同管理等功能,帮助教育机构实现数字化转型,提高管理效率。 + +## 技术架构 +### 后端技术栈 +- PHP 8 +- ThinkPHP 8 +- MySQL 数据库 +- Workman 高性能框架(消息队列、计划任务) - :fa-quote-left: 如果对您有帮助,您可以点右上角 ⭐“Star” 收藏一下 ,获取第一时间更新,谢谢! :fa-quote-right: - - -### NIUSHOP 开源商城 V6 技术选型 -NIUSHOP V6 使用 **NIUCLOUD-ADMIN** 底层框架设计, 国内首家唯一支持TP8框架 ,前端采用市面最流行的技术栈 **Vite+TypeScript+Vue3+ElementPlus** ,后端采用 **THINKPHP8、PHP8** 语言搭建。配合 **Workman** 高性能框架实现消息队列,计划任务处理。内置集成用户权限、代码生成器、表单设计、云存储、短信发送、素材中心、微信及公众号、支付、模版消息推送Api模块一系列开箱即用功能,这是一款快速可以开发企业级应用的软件系统。 - - -### 设计理念 - -强大的多应用+插件组合设计理念,低耦合,高内聚 - -全新生态设计,多应用聚合+多插件组合运营模式全新升级 ,支持共同会员体系下商城,会员卡,上门服务等等多种商业模式随机组合,DIY装修出最强的软件系统 -![输入图片说明](https://www.niushop.com/app/web/view/public/img/product/b2cv6/low-play.mp4?v=4) - -### 插件化,完全为开发者二次开发而生 -V6底层采用插件化模式设计,可以做到多种插件共存,组合使用。比如您有一个项目是旅游的项目,这个项目的要求是,既有商城的功能,又有旅游项目的销售,还需要进行会员的管理,甚至于还要客服系统。传统的实现方式是,找多个源码,东拼西凑,二次开发,或者部署多套独立的系统,配合起来。而今天,使用V6,可以通过组装的方式,在一套体系中实现,随着发展,会有越来越多的各行各业的插件和应用上架。您对于项目的定制,可能只需要简单组装,装修页面,就可以最终实现功能交付。 -![输入图片说明](https://www.niushop.com/app/web/view/public/img/product/b2cv6/addon-right.png) - -### 首创强大的一键云安装,云编译,云发布,升级引擎 -给我一个支点,必能撬动地球。V6简单方便的一键云安装,云编译工具,让您小白也能变大师。 -V6内置在线升级功能,系统会全自动化帮您升级文件。产品的更新只需一键完成 。 -HBUILDER, VSCODE,微信小程序开发工具,打包,上传,发布! V6强大的小程序一键傻瓜式发布系统,任何开发环境都不再需要搭建!鼠标一点完成小程序升级发布。 -![输入图片说明](https://media.niucloud.com/171214000404e2574b6bfa3ff0a05fafbbb93ea23b_aliyun.mp4) -![输入图片说明](https://media.niucloud.com/17121421916f5969317fac428cb7001711e93d8ae3_aliyun.mp4) -![输入图片说明](https://media.niucloud.com/17121430761c9bef9042d7275c4227993149ddb2df_aliyun.mp4) - -### NIUCLOUD-ADMIN 是什么? -NIUCLOUD-ADMIN是一款快速开发通用管理后台框架,整体功能架构全部精心设计!代码干净整洁!低耦合,高质量!!!前后端API接口完全分离 :raised_hands: !!!前端采用最新技术 **Vite+TypeScript+Vue3+ElementPlus** ,后台采用PHP8、MYSQL8、THINKPHP8 全部最新技术栈,内置Workman高性能消息队列,计划任务处理,完全兼容容器路由运行技术。 内置代码生成器,插件生成器,一键云编译、一键云部署,集成用户权限、表单设计、云存储、短信发送、素材中心、微信及公众号、Api模块一系列开箱即用功能,是一款快速搭建开发企业级应用的软件系统。源码100%开源无加密!框架采用MIT协议,终身免费,商用免费! - -请到官方网站了解更多 http://www.niucloud.com - -### NIUSHOP V6 和 NIUCLOUD-ADMIN 的区别和关系怎样的? -首先,NIUSHOP 产品系列是以商城系统(2016年立项研发,V1一直升级到V5版本, V6是完全从零研发的新产品)为主的独立的产品线。NIUCLOUD产品系列(从2022年底开始立项研发)是以NIUCLOUD-ADMIN框架(分单用户独立版、SAAS版)为根本,在此基础上发展各种应用插件,包括第三方开发者生态产品,主要以SAAS产品系列为主。而 NIUSHOP V6 是 使用 **NIUCLOUD-ADMIN** 框架单用户独立版设计的商城应用,以NIUSHOP品牌推广。一句话概括就是,单用户专业化系统以NIUSHOP品牌整体运营推广,SAAS版本插件和应用市场以及NIUCLOUD框架(单用户、SAAS)以NIUCLOUD品牌运营推广。NIUSHOP和NIUCLOUD都是牛之云科技有限公司投资研发运作。 - -### NIUCLOUD-ADMIN 技术特点 - -- 支持composer快速安装扩展,支持 **redis** 缓存以及消息队列,支持多语言设计开发,采用严格的 **restful** 的api设计开发。 -- 后台前后端分离采用 **element-plus、vue3.0、typescript、vite、pina** 等前端技术,同时使用i18n支持国际化多语言开发。 -- 手机端采用uniapp前后端分离,使用 **uview、vue3.0、typescript、vite、pina** 前端技术,支持h5,微信小程序,支付宝小程序,抖音小程序等使用场景。 -- 支持安装多个应用多插件组合使用。 -- 前端以及后端采用严格的多语言开发规范,包括前端展示,api接口返回,数据验证,错误返回等全部使用多语言设计规范,使开发者能够真生意义上实现多语言的开发需求。 -- 框架已经搭建好常规系统的开发底层,具体的底层功能包括:管理员管理,权限管理,网站设置,计划任务管理,素材管理,会员管理,会员账户管理,微信公众号以及小程序管理,支付管理,第三方登录管理,消息管理,短信管理,文章管理,前端装修等全面的基础功能,这样开发者不需要开发基础的结构而专心开发业务。 -- 内置支持微信/支付宝支付,微信公众号/小程序/短信消息管理,阿里云/腾讯云短信,七牛云/阿里云存储等基础的功能扩展,后续会根据实际业务不断扩展基础组件。 -- 强大的代码生成器。开发者根据数据表可以一键生成基础的业务代码,包括:后台php业务代码以及对应的前端vue代码。 -- 手机端内置了自定义装修,同时提供了基础的开发组件,强大的DIY组件自定义功能,允许开发者按照规范开发第三方DIY组件及自定义页面实现业务需求 - - -### 强者归来,选择NIUSHOP 开源商城 V6, 不止于此,未来无限可能 - -酒香不怕巷子深,花香自有蝶飞来,NIUSHOP和NIUCLOUD开发者生态圈正在快速的膨胀发展,越来越多的开发者正在积极参与,统一的代码规范,统一的开发模式和思路,产品的二次开发和项目定制正在,规范化,积木化,快速简单化。只需用心细读一回代码,二次开发效率和质量完全得到保证!完全插件化的设计,多应用,多插件模式。随着生态的逐步完善,组合即用! 我们官方会努力帮大家搭建好基础服务平台,为所有的开发者,创业者,码农,互联网从业者,提供一个资源互换,信息共享,产品推广的生态圈。共享百万开发者产品,共享亿万市场资源。 - - -### 界面截图 :point_right: -![输入图片说明](https://media.niucloud.com/1712132244c781785a8822b281c8d03f10134c9f97_aliyun.png) - -![输入图片说明](https://media.niucloud.com/17121362221b4f7f3c15be7077a4fb351a829f1b35_aliyun.png) - -![输入图片说明](https://media.niucloud.com/1716457294000dce7b84b5b719b0131e54f8dc38b9_aliyun.webp) - -![输入图片说明](https://media.niucloud.com/171645729466dde1cba500222482ef11541cbff589_aliyun.webp) - -![输入图片说明](https://media.niucloud.com/1716457294c65849f48ae7274a309f14fa960bb75a_aliyun.webp) - -![输入图片说明](https://media.niucloud.com/171645729445f037decf7c4947501391af3a8f4d59_aliyun.webp) - -### 操作指南 - [NIUSHOP官网地址](https://www.niushop.com) - | [NIUCLOUD官网地址](https://www.niucloud.com) - | [服务市场](https://www.niucloud.com) - | [使用手册](https://www.niucloud.com/doc) - | [二开手册](https://www.niucloud.com/doc) - | [开发视频](https://www.niucloud.com/doc) - | [API接口手册](https://api.niucloud.com/apidoc.html?target_id=001) - | [论坛地址](https://bbs.niucloud.com) - -### V6安装教程 - - - [安装指引说明](https://www.kancloud.cn/niushop/niushop_v6/3224842) - - [宝塔安装部署V6](https://www.kancloud.cn/niushop/niushop_v6/3226724) - - [PHPStudy安装部署V6](https://www.kancloud.cn/niushop/niushop_v6/3226728) - - -### 二次开发视频教程 - -- [开发准备工作与创建插件](https://niucloud-document-video.oss-cn-beijing.aliyuncs.com/video/1-1.mp4) -- [插件目录整体说明](https://niucloud-document-video.oss-cn-beijing.aliyuncs.com/video/2-1.mp4) -- [插件安装与打包原理](https://niucloud-document-video.oss-cn-beijing.aliyuncs.com/video/8-1.mp4) -- [消息队列](https://niucloud-document-video.oss-cn-beijing.aliyuncs.com/video/9-1.mp4) -- [计划任务](https://niucloud-document-video.oss-cn-beijing.aliyuncs.com/video/10-1.mp4) -- [DIY自定义小组件和页面装修开发](https://niucloud-document-video.oss-cn-beijing.aliyuncs.com/video/11-1.mp4) -- [支付接口开发](https://niucloud-document-video.oss-cn-beijing.aliyuncs.com/video/12-1.mp4) -- [插件升级包打包流程以及云编译](https://niucloud-document-video.oss-cn-beijing.aliyuncs.com/video/13-1.mp4) - - -### 演示地址 -- 管理后台演示网址:[ 查看 ] -http://v6.site.niucloud.com 账号:admin 密码:123456 - -- H5前端演示网址:[ 查看 ] -https://v6.site.niucloud.com/wap/addon/shop/pages/index - -### 加入开发者生态,一起助力成就程序员创业梦想!!! - -加入企业微信群技术交流,请扫描下面二维码 :point_down: - -![输入图片说明](https://media.niucloud.com/170312377249fc5bc70c5f914fda3d7c5cf3413ddc_aliyun.jpg) - - -### 产品LOGO - -![输入图片说明](https://media.niucloud.com/1712452101f24e83b1d36c9078c433015d0b6f44c1_aliyun.png) - -![输入图片说明](https://foruda.gitee.com/avatar/1682227978769691031/1342405_niushop_1682227978.png) - -![输入图片说明](https://www.niucloud.com/_nuxt/login_logo.650a27e2.png) - - -### 开源使用须知 - -1.允许用于个人学习、毕业设计、教学案例、公益事业、商业使用; - -2.本框架应用源代码所有权和著作权归niucloud官方所有,基于niucloud-admin框架开发的应用,所有权和著作权归应用开发商所有。但必须明确声明是基于niucloud-admin框架开发,请自觉遵守,否则产生的一切任何后果责任由侵权者自负; - -3.禁止修改框架代码并再次发布框架衍生版等与niucloud-admin框架产生恶意竞争或对抗的行为; - -4.本框架源码全部开源;包括前端,后端,无任何加密; - -5.商用请仔细审查代码和漏洞,不得用于任一国家许可范围之外的商业应用,产生的一切任何后果责任自负; - -6.一切事物有个人喜好的标准,本开源代码意在分享,不喜勿喷。 - - -### 版权信息 -版权所有Copyright © 2015-2025 niucloud-admin 版权所有 - -All rights reserved。 - -杭州数字云动科技有限公司 -杭州牛之云科技有限公司 - -提供技术支持 \ No newline at end of file +### 前端技术栈 +- Vue 3 +- TypeScript +- Element Plus +- Vite + +### 移动端技术栈 +- UniApp +- Vue 3 +- TypeScript + +## 系统功能模块 + +### 学生管理 +- 学生信息管理 +- 学生档案管理 +- 学生考勤管理 +- 学生成绩管理 + +### 课程管理 +- 课程信息管理 +- 课程分类管理 +- 课程资源管理 + +### 排课管理 +- 课表编排 +- 教师排课 +- 教室安排 +- 时间段管理 + +### 校区管理 +- 校区信息管理 +- 校区资源配置 + +### 场地管理 +- 教室管理 +- 场地预约 +- 场地使用记录 + +### 人员管理 +- 教师管理 +- 职工管理 +- 人员排班 + +### 合同管理 +- 合同创建 +- 合同审批 +- 合同执行跟踪 + +### 学生课程管理 +- 课时管理(总课时、赠送课时) +- 课程有效期管理 +- 已用课时统计 +- 单次课时设置 + +### 用户权限管理 +- 用户管理 +- 角色管理 +- 菜单权限管理 +- 操作日志记录 + +### 系统配置管理 +- 系统参数配置 +- 字典管理 +- 附件管理 + +### 通知管理 +- 微信通知 +- 小程序通知 +- 短信通知 +- 通知日志记录 + +### 计划任务管理 +- 定时任务配置 +- 任务执行记录 +- 任务调度管理 + +## 系统特点 + +### 插件化设计 +系统采用插件化设计,支持多插件共存和组合使用,便于功能扩展和定制开发。 + +### 多端支持 +同时支持PC管理端、H5移动端、微信小程序等多种终端,满足不同场景的使用需求。 + +### 多语言支持 +系统内置多语言支持,包括前端展示、API接口返回、数据验证、错误提示等全方位的多语言设计。 + +### 高性能架构 +采用ThinkPHP 8框架,结合Workman高性能消息队列和计划任务处理,保证系统的高效运行。 + +### 安全可靠 +完善的权限管理机制,详细的操作日志记录,确保系统数据安全和操作可追溯。 + +## 数据库设计 + +系统采用MySQL数据库,主要表类别包括: + +- 学生课程关联表(school_student_courses):记录学生选课信息、课时信息等 +- 系统用户表(school_sys_user):管理系统用户信息 +- 系统角色表(school_sys_role):管理角色及权限信息 +- 系统菜单表(school_sys_menu):管理系统菜单及权限 +- 系统配置表(school_sys_config):存储系统配置信息 +- 系统字典表(school_sys_dict):管理系统字典数据 +- 系统通知表(school_sys_notice):管理系统通知模板 +- 通知日志表(school_sys_notice_log):记录通知发送日志 +- 短信日志表(school_sys_notice_sms_log):记录短信发送日志 +- 计划任务表(school_sys_cron_task):管理系统定时任务 +- 计划任务日志表(school_sys_schedule_log):记录任务执行日志 +- 用户操作日志表(school_sys_user_log):记录用户操作日志 +- 附件管理表(school_sys_attachment):管理系统附件 + +## 版权信息 + +版权所有 Copyright © 2023-2024 智慧教务系统 + +杭州盛宇网络科技有限公司提供技术支持 \ No newline at end of file diff --git a/admin/src/app/views/service/components/service-edit.vue b/admin/src/app/views/service/components/service-edit.vue index a272f1ec..ad8b5b61 100644 --- a/admin/src/app/views/service/components/service-edit.vue +++ b/admin/src/app/views/service/components/service-edit.vue @@ -1,302 +1,334 @@ - - - - - - + + + + + + diff --git a/admin/src/app/views/service/service.vue b/admin/src/app/views/service/service.vue index fd5b98f1..a205ef50 100644 --- a/admin/src/app/views/service/service.vue +++ b/admin/src/app/views/service/service.vue @@ -1,231 +1,234 @@ - - - - - + + + + + diff --git a/niucloud/app/api/route/route.php b/niucloud/app/api/route/route.php index 9b49719e..0f6097c4 100644 --- a/niucloud/app/api/route/route.php +++ b/niucloud/app/api/route/route.php @@ -354,7 +354,7 @@ Route::group(function () { Route::get('personnel/reimbursement_list', 'apiController.Personnel/reimbursement_list'); Route::post('personnel/reimbursement_add', 'apiController.Personnel/reimbursement_add'); Route::get('personnel/reimbursement_info', 'apiController.Personnel/reimbursement_info'); - + //更新学员主教练、助教、教务 })->middleware(ApiChannel::class) ->middleware(ApiPersonnelCheckToken::class, true) diff --git a/niucloud/app/job/transfer/schedule/CourseScheduleJob.php b/niucloud/app/job/transfer/schedule/CourseScheduleJob.php index e3c410fe..11581f4b 100644 --- a/niucloud/app/job/transfer/schedule/CourseScheduleJob.php +++ b/niucloud/app/job/transfer/schedule/CourseScheduleJob.php @@ -187,9 +187,10 @@ class CourseScheduleJob extends BaseJob $newSchedule->time_slot = $schedule->time_slot; $newSchedule->course_id = $schedule->course_id; $newSchedule->auto_schedule = 1; - + $newSchedule->created_by = 'system'; + // 复制其他所有字段(除了id和主键相关字段) - $attributes = $schedule->toArray(); // 使用toArray()替代getAttributes() + $attributes = $schedule->toArray(); foreach ($attributes as $key => $value) { // 跳过id和主键相关字段 if ($key !== 'id' && $key !== 'course_date' && $key !== 'auto_schedule') { From a42cfc739a419f8c62d54ce23ae6ec6f20ecd9af Mon Sep 17 00:00:00 2001 From: wangzeyan <258785420@qq.com> Date: Fri, 27 Jun 2025 14:45:56 +0800 Subject: [PATCH 5/5] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=AE=9A=E6=97=B6?= =?UTF-8?q?=E4=BB=BB=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apiController/CustomerResources.php | 23 ++++++++++ niucloud/app/api/route/route.php | 2 +- .../apiService/CustomerResourcesService.php | 45 +++++++++++++++++++ 3 files changed, 69 insertions(+), 1 deletion(-) diff --git a/niucloud/app/api/controller/apiController/CustomerResources.php b/niucloud/app/api/controller/apiController/CustomerResources.php index ca92ff54..b4c83424 100644 --- a/niucloud/app/api/controller/apiController/CustomerResources.php +++ b/niucloud/app/api/controller/apiController/CustomerResources.php @@ -230,5 +230,28 @@ class CustomerResources extends BaseApiService return success($res); } + //修改用户课程的主教、助教、教务 + public function updateUserCourse(Request $request) + { + $main_coach_id = $request->param('main_coach_id', ''); + $assistant_ids = $request->param('assistant_ids', ''); + $id = $request->param('id', ''); + $education_id = $request->param('education_id', '');//查询类型|resource=客户资源,six_speed=六要素 + + if (empty($customer_resource_id) || empty($course_id) || empty($staff_id) || empty($type)) { + return fail('缺少必要参数'); + } + //修改用户课程的主教、助教、教务 + $res = (new CustomerResourcesService())->updateUserCourseInfo([ + 'id' => $id, + 'main_coach_id' => $main_coach_id, + 'assistant_ids' => $assistant_ids, + 'education_id' => $education_id + ]); + if (!$res['code']) { + return fail($res['msg']); + } + return success([]); + } } diff --git a/niucloud/app/api/route/route.php b/niucloud/app/api/route/route.php index 0f6097c4..9b49719e 100644 --- a/niucloud/app/api/route/route.php +++ b/niucloud/app/api/route/route.php @@ -354,7 +354,7 @@ Route::group(function () { Route::get('personnel/reimbursement_list', 'apiController.Personnel/reimbursement_list'); Route::post('personnel/reimbursement_add', 'apiController.Personnel/reimbursement_add'); Route::get('personnel/reimbursement_info', 'apiController.Personnel/reimbursement_info'); - //更新学员主教练、助教、教务 + })->middleware(ApiChannel::class) ->middleware(ApiPersonnelCheckToken::class, true) diff --git a/niucloud/app/service/api/apiService/CustomerResourcesService.php b/niucloud/app/service/api/apiService/CustomerResourcesService.php index f88b7b95..24b8e111 100644 --- a/niucloud/app/service/api/apiService/CustomerResourcesService.php +++ b/niucloud/app/service/api/apiService/CustomerResourcesService.php @@ -666,4 +666,49 @@ class CustomerResourcesService extends BaseApiService } + public function updateUserCourseInfo($data) + { + // 验证必要参数 + if (empty($data['id'])) { + return ['code' => false, 'msg' => '缺少必要参数id']; + } + + try { + // 更新school_student_courses表中的字段 + $update_data = []; + + // 更新主教练ID + if (isset($data['main_coach_id'])) { + $update_data['main_coach_id'] = $data['main_coach_id']; + } + + // 更新助教IDs + if (isset($data['assistant_ids'])) { + $update_data['assistant_ids'] = $data['assistant_ids']; + } + + // 更新教务ID + if (isset($data['education_id'])) { + $update_data['education_id'] = $data['education_id']; + } + + // 如果没有需要更新的数据,直接返回成功 + if (empty($update_data)) { + return ['code' => true, 'msg' => '更新成功']; + } + + // 执行更新操作 + $res = \think\facade\Db::name('student_courses') + ->where('id', $data['id']) + ->update($update_data); + + if ($res !== false) { + return ['code' => true, 'msg' => '更新成功']; + } else { + return ['code' => false, 'msg' => '更新失败']; + } + } catch (\Exception $e) { + return ['code' => false, 'msg' => '更新失败:' . $e->getMessage()]; + } + } }