如何在 laravel 中减少较长的处理时间

How to reduce long processing time in laravel

我已经使用 Laravel 5.7 构建了一个员工管理系统。计算工资对 100-150 名员工工作正常(但需要很长时间来处理数据)& 对于超过 150 名员工,它显示超时错误。

我想添加一次性计算所有员工工资的功能,同时减少处理时间。为此使用 chunk() 合适吗?如果是,我该如何实现?

注: Web 应用程序用于 BPO,因此在计算薪水时要应用多种类型的逻辑。代码太长就不分享了,如果有人想看代码,我可以分享。

Front-end form screenshot
Page after calculation

    <?php 
      public function calculate_salary(Request $request){

        $this->validate($request,[
              'employee_id' => 'required',
              'attendance_types' => 'required',
              'month'  => 'required_without:choose_date',
              'choose_date' => 'required_without:month'
      ]);

    $download_salary = false;
    $submit_salary = false;
    if ($request->has('submit_data'))
    {
        $submit_salary = true;

    }
    else
    {
        $submit_salary = false;
    }

        if ($request->has('download_data'))
    {
        $download_salary = true;

    }
    else
    {
        $download_salary = false;
    }

        $dept = $request->input('salary_department');
        $role = $request->input('salary_role');
        $process = $request->input('salary_process');

        $salary_dept = Department::where('id', $dept)->pluck('dept_name')->first();
        $salary_role = Role::where('id', $role)->pluck('role_name')->first();
        $salary_process = Process::where('id', $process)->pluck('process_name')->first();

        if ($submit_salary == true || $download_salary == true)
    {
        $months = $request->input('month');
        $month = $months[0];
        $employee_ids = $request->input('employee_id');
        $attendance_types = $request->input('attendance_types');
        $attendance_type_temp_insert = $attendance_types[0];
        $attendance_type = explode(',', $attendance_type_temp_insert);
        if (!empty($request->input('choose_date')))
        {
            $dates = $request->input('choose_date');
            $date = $dates[0];
        }
        else
        {
            $date = "";
        }
    }
    else
    {
        $month = $request->input('month');
        $employee_ids = $request->input('employee_id');
        $attendance_type = $request->input('attendance_types');
        $attendance_type_temp_insert = implode(',', $request->input('attendance_types'));
        $date = $request->input('choose_date');
    }

        // Get start & end dates
        $data = $this->get_salary_dates($month, $date);
        $start_date = $data['start_date'];
        $end_date = $data['end_date'];
        $salary_start_date = $start_date->toDateString();
        $salary_end_date = $end_date->toDateString();

        $working_days_by_month = $this->get_working_days_of_given_month($start_date, $end_date);

        $working_days = $working_days_by_month['working_days'];
        $no_days = $working_days_by_month['no_days'];

        Schema::create('temp_salary', function (Blueprint $table) {
        $table->increments('id');
        $table->integer('employee_id');
        $table->string('employee_name');
        $table->integer('working_days');
        $table->float('emp_working_days');
        $table->float('basic');
        $table->float('hra');
        $table->float('conveyance');
        $table->float('spcl_inc');
        $table->float('gross_salary');
        $table->float('pf');
        $table->float('esi');
        $table->integer('headphone');
        $table->integer('id_card');
        $table->float('deductions');
        $table->integer('pl_balance');
        $table->integer('combo_balance');
        $table->float('net_payable');
        $table->string('month')->nullable();
        $table->string('attendance_types');
        $table->string('choose_date')->nullable();
        $table->string('department')->nullable();
        $table->string('role')->nullable();
        $table->string('process')->nullable();
        $table->timestamps();
        $table->temporary();
    });


        $csv[] = ['Employee ID', 'Name','Department','Role','Process','Bank','Bank Account Number', 'Basic', 'HRA', 'Conveyance', 'Spcl Inc', 'Gross Salary','PF', 'ESI', 'Deductions', 'Total Working Days', 'Net Payable','Salary Month', 'Salary Start Date','Salary End Date'];
       $csv1[] = ['Employee ID', 'Name','Department','Role','Process','Bank','Bank Account Number', 'Basic', 'HRA', 'Conveyance', 'Spcl Inc', 'Gross Salary','PF', 'ESI', 'Deductions', 'P','H','A','WO','WOP','WOH','UAL','Paid Leaves','PL','Combo','ual_deduct','Total Working Days', 'Net Payable','Salary Month', 'Salary Start Date','Salary End Date'];

        foreach($employee_ids as $id) {

            if($submit_salary == true){
            DB::table('attendances')->where('employee_id', '=', $id)->whereBetween('attendance_date', [$start_date, $end_date])->update(['salary_status' => 1]);
            }

        $employee = Employee::find($id);
        $name = $employee->name;
        $emp_role = $employee->empRole->role_name;
        $emp_department = $employee->empDepartment->dept_name;
        $emp_process = $employee->empProcess->process_name;
        $bank_account_number = $employee->bank_account_number;
        $bank_name = $employee->bank_name;
        $salary = Salary::where('employee_id', $id)->first();
        $pl_balance = $salary->pl_balance;
        $combo_balance = $salary->combo_balance;


        $attendances = DB::table('attendances')->where('employee_id', '=', $id)->get();
        if ($attendances->count() > 0)
        {
            foreach($attendances as $attendance)
            {
                $att_id = $attendance->id;
                $att_date = Carbon::parse($attendance->attendance_date);
                $dialer_in = strtotime($attendance->dialer_in_time);
                $dialer_out = strtotime($attendance->dialer_out_time);
                $bio_in = strtotime($attendance->biometric_in_time);
                $bio_out = strtotime($attendance->biometric_out_time);
                $bio_diff = $bio_out - $bio_in;
                $crm_in = strtotime($attendance->crm_in_time);
                $crm_out = strtotime($attendance->crm_out_time);
                $week_off = $attendance->week_off;
                $combo = $attendance->combo;
                $holiday = $attendance->holiday;
                $ual = $attendance->ual;
                $dialer_duration = $attendance->dialer_difference;
                $bio_duration = $attendance->biometric_difference;
                $crm_duration = $attendance->crm_difference;
                $chkatt = $this->get_attendance($attendance_type, $attendance->dialer_in_time, $attendance->dialer_out_time,$attendance->crm_in_time,$attendance->crm_out_time, $dialer_duration, $bio_duration, $crm_duration);

                $attendance_status = $this->attendance_status($id, $chkatt, $holiday, $week_off, $ual);
                DB::table('attendances')->where('id', $att_id)->update(['attendance_status' => $attendance_status]);
                //echo $attendance_status;
            }
        }

        $totalWorkingDays = $this->get_work_days($id,$start_date,$end_date,$combo_balance,$pl_balance, $submit_salary);
        $totalWorkingDays = number_format((float)$totalWorkingDays, 2, '.', '');
        $salary_data = $this->get_calculated_salary_data($no_days, $totalWorkingDays, $id, $submit_salary);
        $basic = $salary_data['basic'];
        $hra = $salary_data['hra'];
        $conveyance = $salary_data['conveyance'];
        $spcl_inc = $salary_data['spcl_inc'];
        $gross_salary = $salary_data['gross_salary'];
        $pf = $salary_data['pf'];
        $esi = $salary_data['esi'];
        $hp_charges = $salary_data['hp_charges'];
        $idcard_charges = $salary_data['idcard_charges'];
        $deductions = $salary_data['deductions'];
        $net_payable = $salary_data['net_payable'];
        DB::table('temp_salary')->insert(['employee_id' => $id, 'employee_name' => $name, 'working_days' => $working_days, 'emp_working_days' => $totalWorkingDays, 'basic' => $basic, 'hra' => $hra, 'conveyance' => $conveyance, 'spcl_inc' => $spcl_inc, 'gross_salary' => $gross_salary, 'pf' => $pf, 'esi' => $esi, 'headphone' => $hp_charges, 'id_card' => $idcard_charges, 'deductions' => $deductions, 'pl_balance' => $pl_balance, 'combo_balance' => $combo_balance, 'net_payable' => $net_payable, 'month' => $month, 'attendance_types' => $attendance_type_temp_insert, 'choose_date' => $date, 'department' => $salary_dept, 'role' => $salary_role, 'process' => $salary_process]);
        $csv[] = [$id, $name, $emp_department, $emp_role, $emp_process, $bank_name, $bank_account_number, $basic, $hra, $conveyance, $spcl_inc, $gross_salary, $pf, $esi, $deductions, $totalWorkingDays, $net_payable, $month, $start_date, $end_date];



        $get_days_status = $this->get_days_status($id, $start_date, $end_date);

  $pCount = $get_days_status['pCount'];
  $HalfDaysCount = $get_days_status['hCount'];
  $lateLeaveCount = $get_days_status['late_leave'];
    $wopCount = $get_days_status['wopCount'];
    $leaves_count = $get_days_status['leaves_count'];
  $UalCount = $get_days_status['UalCount'];
  $woCount = $get_days_status['woCount'];
  $wohCount = $get_days_status['wohCount'];

  $ual_deduct = ($UalCount * 1.5) - $UalCount;


    $get_paid_leaves_row = $get_days_status['pl_leaves'];

    $csv1[] = [$id, $name, $emp_department, $emp_role, $emp_process, $bank_name, $bank_account_number, $basic, $hra, $conveyance, $spcl_inc, $gross_salary, $pf, $esi, $deductions, $pCount,$HalfDaysCount,$leaves_count,$woCount,$wopCount,$wohCount,$UalCount,$get_paid_leaves_row,$pl_balance, $combo_balance,$ual_deduct, $totalWorkingDays, $net_payable, $month, $start_date, $end_date];

    }

        $datas = DB::table('temp_salary')->get();



        if($submit_salary == true){
            return Excel::create('Employee_salary_report', function($excel) use ($csv) {
            $excel->sheet('Employee_salary_report', function($sheet) use ($csv) {
            $sheet->fromArray($csv, null, 'A1', false, false)
            ->getStyle('A1')
                ->getAlignment()
                ->setWrapText(true);
            }); 
        })->download('csv'); 
        } 

        if($download_salary == true){
            return Excel::create('salary_report', function($excel) use ($csv1) {
            $excel->sheet('salary_report', function($sheet) use ($csv1) {
            $sheet->fromArray($csv1, null, 'A1', false, false)
            ->getStyle('A1')
                ->getAlignment()
                ->setWrapText(true);
            }); 
        })->download('csv'); 
        } 


        Schema::drop('temp_salary');

        $departments = Department::all();
        $processes = Process::all();
        $roles = Role::all();
        return view('sys_mg.salaries.get-salary')->with(['datas'=>$datas,'departments' => $departments, 'processes' => $processes, 'roles' => $roles, 'salary_dept' => $salary_dept, 'salary_role' => $salary_role, 'salary_process'=>$salary_process, 'salary_month'=>$month,'salary_startDate' => $salary_start_date, 'salary_endDate' => $salary_end_date, 'attendance_check_type' => $attendance_type]);

 }

这是一个巨大的过程,同时做很多事情,其中​​一些事情相互嵌套得很深。

函数非常非常长,这意味着它可能需要抽象成各种方法和classes(更好的OOP)。

您的某些 Laravel 查询也可能 运行 陷入 N+1 问题,尽管乍一看很难说。

我建议使用 Laravel 的队列,并将每个员工的工资计算添加到队列中:https://laravel.com/docs/5.8/queues

然后您可以使用工作进程单独执行每一个。

所有数据库更新和事务意味着 PHP 不断从您的数据库中获取 to-and-fro。尝试在纯代码中执行尽可能多的操作,然后在完成后写入数据库。 (在可能的情况下,这不是一揽子规则)。

最初我会说,考虑使用 getAttendances()calculateSalaryFromEmployeeAttendances().

等方法创建 SalaryCalculator class

另外,创建一个 CSV 导出器 class。首先处理所有工资计算,将结果存储在数据库中,然后根据需要转换为 CSV。

一旦您能够查看所有不同的部分,稍后您将对其进行重构,其他人可能会提出更好的分解方法,但在某些方面没有正确的答案...只需从一些 OOP 和方法抽象开始,它就会变得更好。

使用 microtime(true) 获取和计算开始和结束操作之间的时间差,开始跟踪每个函数运行的时间...并首先寻找重大优化。最慢的部分是什么?为什么?

您可能会问一打 Stack Overflow 问题来优化这些方法中的每一个,没关系!