laravel 工单回复接口要在写入前确认工单属于当前用户,而且仍处于可回复状态。若关单和回复可能同时发生,可在同一事务内锁定工单记录、检查状态并插入回复。
把回复操作接到已认证路由
示例使用 Laravel 12 和 MySQL InnoDB。tickets 含 id、user_id、status,ticket_replies 含 ticket_id、user_id、body 和时间戳。
use App\Http\Controllers\TicketReplyController;
use Illuminate\Support\Facades\Route;
Route::post('/tickets/{id}/replies', [TicketReplyController::class, 'store'])
->whereNumber('id')->middleware('auth');
工单创建人从认证会话识别,工单 ID 从路由读取。
在事务中检查状态再写入
把以下方法加入控制器。关闭操作也应更新同一张工单表中的同一行。
Laravel Cloud(cloud.laravel.com)。此技能用于处理所有 Laravel Cloud 请求,包括搜索和读取数据。只要任务涉及 Laravel Cloud,即使用此技能。
public function store(\Illuminate\Http\Request $request, string $id)
{
$data = $request->validate(['body' => ['required', 'string', 'max:4000']]);
$userId = $request->user()->id;
$replyId = \Illuminate\Support\Facades\DB::transaction(function () use ($id, $userId, $data) {
$ticket = \Illuminate\Support\Facades\DB::table('tickets')
->where('id', $id)->where('user_id', $userId)
->lockForUpdate()->first();
abort_if($ticket === null, 404);
abort_unless($ticket->status === 'open', 409, '工单已不可回复');
return \Illuminate\Support\Facades\DB::table('ticket_replies')->insertGetId([
'ticket_id' => $ticket->id,
'user_id' => $userId,
'body' => $data['body'],
'created_at' => now(),
'updated_at' => now(),
]);
});
return response()->json(['id' => $replyId], 201);
}
关单时保留已有回复。若系统允许多个可回复状态,应按业务定义列出允许值。
验证关闭前后的响应
在功能测试中准备当前用户的打开工单和关闭工单。
$this->actingAs($owner)->postJson("/tickets/{$open->id}/replies", [
'body' => '补充说明',
])->assertCreated();
$this->actingAs($owner)->postJson("/tickets/{$closed->id}/replies", [
'body' => '补充说明',
])->assertStatus(409);再查询关闭工单的回复数量,确认拒绝请求没有插入记录。使用其他账号请求同一工单应得到 404。

















