You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel 9使用Job调用存储过程无数据库操作问题排查

Laravel Job异步调用存储过程无数据库操作排查与解决

我有一个执行特定任务的存储过程SubjectAllocation,控制器直接调用时正常,但改用Laravel Job异步执行后,Job能正常运行且成功发送邮件,但MySQL数据库无任何操作,手动单独执行该存储过程则可正常生效。相关代码如下:

控制器代码

try {
    $emailAddress = auth()->user()->email ?? NULL;
    SubjectAllocationAllJob::dispatch(
        $request->course,
        $request->regulation,
        $request->batch,
        $request->semester,
        $emailAddress
    );
    Alert::success('Exam Application Started Successfully. You will recieve email shortly');

    return redirect()->route('admin.exam.preprocess.subjectmapping.all');
} catch (Exception $e) {
    Alert::error('Error Mapping Exam Application');

    return redirect()->back()->withInput();
}

SubjectAllocationAllJob代码

<?php

namespace App\Jobs\ExamPreProcess;

use Illuminate\Bus\Queueable;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Mail;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use App\Mail\SubjectMappingAllSuccessMail;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Contracts\Queue\ShouldBeUnique;

class SubjectAllocationAllJob implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public $regulation;
    public $course;
    public $batch;
    public $semester;
    public $email;

    /**
     * Create a new job instance.
     *
     * @return void
     */
    public function __construct($regulation, $course, $batch, $semester, $email)
    {
        $this->regulation = $regulation;
        $this->course = $course;
        $this->batch = $batch;
        $this->semester = $semester;
        $this->email = $email;
    }

    /**
     * Execute the job.
     *
     * @return void
     */
    public function handle()
    {
        DB::select(
            'CALL SubjectAllocation(?,?,?,?,?)',
            array(
                $this->course,
                $this->regulation,
                $this->batch,
                $this->semester,
                session('ExamID')
            )
        );

        if (!empty($this->email)) {
            Mail::to($this->email)->send(new SubjectMappingAllSuccessMail());
        }
    }
}

存储过程代码

DELIMITER $$
DROP PROCEDURE IF EXISTS SubjectAllocation $$
CREATE PROCEDURE `SubjectAllocation`(
    IN coursesin VARCHAR(25),
    IN regulationsin VARCHAR(25),
    IN batchin VARCHAR(25),
    IN semesterin VARCHAR(25),
    IN examyearin VARCHAR(2)
)
BEGIN
DROP
    TEMPORARY TABLE IF EXISTS `subjectassign`; CREATE TEMPORARY TABLE `subjectassign` (
        `subject_id` INT, `student_id` INT,
        `subjecttype_id` INT
    ); INSERT INTO `subjectassign`
SELECT
    `subjects`.`id`,
    `students`.`id`,
    `subjects`.`subjecttype_id`
FROM
    `subjects`,
    `students`
WHERE
    `subjects`.`programme_id` = `students`.`programme_id`
    AND `subjects`.`regulation_id` = `students`.`regulation_id`
    AND `subjects`.`course_id` = `students`.`course_id`
    AND `subjects`.`subjectallocationtype_id` IS NULL
    AND `subjects`.`semester` = semesterin
    AND `students`.`regulation_id` = regulationsin
    AND `students`.`course_id` = coursesin
    AND `students`.`batch_id` = batchin; DELETE `subjectassign`
FROM
    `subjectassign`
    RIGHT JOIN `exam_applications` ON `subjectassign`.`student_id` = `exam_applications`.`student_id`
    AND `subjectassign`.`subject_id` = `exam_applications`.`subject_id`; INSERT INTO `exam_applications`(
        `student_id`, `subject_id`, `exammonth_id`,
        `examtype_id`, `exampapertype_id`,
        `verification`, `display`
    )
SELECT
    DISTINCT `student_id`,
    `subject_id`,
    examyearin,
    '1',
    '1',
    'N',
    'Y'
FROM
    `subjectassign`;
DROP
    TEMPORARY TABLE IF EXISTS `tmp_internalmark`; CREATE TEMPORARY TABLE `tmp_internalmark` (
        `subject_id` INT, `student_id` INT
    ); INSERT INTO `tmp_internalmark`
SELECT
    `subjects`.`id`,
    `students`.`id`
FROM
    `subjects`,
    `students`
WHERE
    `subjects`.`programme_id` = `students`.`programme_id`
    AND `subjects`.`regulation_id` = `students`.`regulation_id`
    AND `subjects`.`course_id` = `students`.`course_id`
    AND `subjects`.`subjectallocationtype_id` IS NULL
    AND `subjects`.`semester` = semesterin
    AND `students`.`regulation_id` = regulationsin
    AND `students`.`course_id` = coursesin
    AND `students`.`batch_id` = batchin; DELETE `tmp_internalmark`
FROM
    `tmp_internalmark`
    RIGHT JOIN `internal_marks` ON `tmp_internalmark`.`student_id` = `internal_marks`.`student_id`
    AND `tmp_internalmark`.`subject_id` = `internal_marks`.`subject_id`; 
    INSERT INTO `internal_marks`(
        `student_id`, `subject_id`, `exammonth_id`,
        `batch_id`
    )
SELECT
    DISTINCT `student_id`,
    `subject_id`,
    examyearin,
    batchin
FROM
    `tmp_internalmark`; 
END $$
DELIMITER ;

问题排查与解决

1. 参数传递顺序错误

控制器调用SubjectAllocationAllJob::dispatch()时,参数顺序为course, regulation, batch, semester, email,但Job构造函数的参数顺序是regulation, course, batch, semester, email,两者顺序错位,导致存储过程接收到的coursesin和regulationsin参数值颠倒,查询条件不匹配,因此没有数据被处理。

修复方法:
调整控制器中的dispatch参数顺序,与Job构造函数一致:

SubjectAllocationAllJob::dispatch(
    $request->regulation,
    $request->course,
    $request->batch,
    $request->semester,
    $emailAddress
);

2. Session无法在异步Job中获取

异步Job运行在独立进程中,不属于请求上下文,因此session('ExamID')无法获取有效值,导致存储过程的examyearin参数为空,插入操作无法匹配正确条件。

修复方法:
将ExamID作为参数传递给Job,而非依赖Session:

  • 修改Job的构造函数,新增$examId属性与参数:
public $regulation;
public $course;
public $batch;
public $semester;
public $email;
public $examId;

public function __construct($regulation, $course, $batch, $semester, $email, $examId)
{
    $this->regulation = $regulation;
    $this->course = $course;
    $this->batch = $batch;
    $this->semester = $semester;
    $this->email = $email;
    $this->examId = $examId;
}
  • 控制器dispatch时传入ExamID:
SubjectAllocationAllJob::dispatch(
    $request->regulation,
    $request->course,
    $request->batch,
    $request->semester,
    $emailAddress,
    session('ExamID')
);
  • 修改Job的handle方法,使用$this->examId替代session调用:
DB::select(
    'CALL SubjectAllocation(?,?,?,?,?)',
    array(
        $this->course,
        $this->regulation,
        $this->batch,
        $this->semester,
        $this->examId
    )
);

3. 查看队列日志排查隐藏错误

即使邮件发送成功,存储过程调用仍可能存在参数错误或其他异常,可查看Laravel队列日志(默认路径storage/logs/queue.log)获取具体报错信息,进一步定位问题。


内容的提问来源于stack exchange,提问作者VPR

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 06:55:57