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

Laravel 10:订单创建代码优化问题咨询

订单创建函数优化方案

原代码的核心问题集中在多次重复数据库查询和逻辑可读性差上,以下是针对性的优化方案:

主要优化措施

1. 批量查询脚本,消灭N+1查询

原代码在循环里挨个调用Script::find($id),每传一个脚本ID就发起一次SQL查询,脚本数量多的时候性能会急剧下降。改成用whereIn批量查询,一次SQL就能获取所有有效脚本。

2. 一次性获取用户已购脚本,避免重复校验

之前每次循环都调用hasScript检查用户是否拥有该脚本,会产生大量重复查询。现在直接一次性拉取用户所有已拥有的脚本ID,通过集合对比就能快速找出重复购买的脚本。

3. 拆解复杂链式调用,提升代码可读性

原来的嵌套链式调用过于绕弯,还需要在闭包里引用外部变量,维护成本高。现在把逻辑拆成独立步骤,每一步的职责清晰明了。

4. 修复低级错误+事务兜底

  • 原代码里$data["cost"]是笔误,应为$order_data["cost"],已修正。
  • 将订单创建、余额扣除、脚本关联等写操作包裹在数据库事务中,确保所有操作要么全部成功,要么全部回滚,避免出现数据不一致的情况。

优化后的完整代码

/**
 * @param array $request_data
 * @return Order
 * @throws AuthException
 * @throws OrderException
 * @throws PromocodeException
 */
public function create(array $request_data): Order
{
    if (!auth()->check()) {
        throw AuthException::unauthenticated();
    }

    /** @var User $user */
    $user = auth()->user();

    $promocodeService = new PromocodeService();
    $userService = new UserService();

    // 提取请求中的脚本ID并去重,避免重复处理
    $scriptIds = collect($request_data["scripts"])->unique()->values()->all();
    if (empty($scriptIds)) {
        throw OrderException::noScriptIsSelected();
    }

    // 批量查询有效脚本,一次SQL搞定所有
    $scripts = Script::whereIn('id', $scriptIds)->get();
    if ($scripts->count() !== count($scriptIds)) {
        // 存在无效脚本ID,可根据业务需求抛异常或过滤
        throw OrderException::invalidScript();
    }

    // 一次性获取用户已拥有的所有脚本ID
    $userOwnedScriptIds = $user->scripts()->pluck('id')->toArray();
    $duplicateScripts = $scripts->whereIn('id', $userOwnedScriptIds);
    if ($duplicateScripts->isNotEmpty()) {
        $duplicateNames = $duplicateScripts->pluck('name')->implode(', ');
        throw OrderException::alreadyHasScript($duplicateNames);
    }

    // 组装订单基础数据,直接用sum计算总金额
    $orderData = [
        "user_id" => $user->id,
        "scripts" => $scripts->pluck('id')->toArray(),
        "cost" => $scripts->sum('cost'),
        "promocode_id" => null
    ];

    // 处理优惠码逻辑
    if (isset($request_data["promocode"])) {
        $promoResult = $promocodeService->useByName($request_data["promocode"], $orderData["cost"]);
        $orderData["cost"] = $promoResult["new_cost"];
        $orderData["promocode_id"] = $promoResult["promocode_id"];
    }

    // 检查余额是否足够
    if ($orderData["cost"] > $user->balance) {
        throw OrderException::notEnoughMoney();
    }

    // 事务包裹所有写操作,保证数据一致性
    DB::transaction(function () use ($user, $orderData) {
        /** @var Order $order */
        $order = $user->orders()->create($orderData);
        $order->scripts()->sync($orderData["scripts"]);
        // 用Laravel内置的decrement方法,原子性更安全
        $user->decrement('balance', $orderData["cost"]);
        $user->scripts()->attach($orderData["scripts"]);
    });

    // 重新查询订单并加载关联脚本,确保返回完整数据
    return $user->orders()->with('scripts')->latest()->first();
}

额外优化细节

  • 用unique()去重脚本ID,避免重复处理同一个脚本。
  • 对比查询到的脚本数量和请求ID数量,快速识别无效脚本。
  • 替换手动累加金额为sum('cost'),代码更简洁高效。
  • 用decrement方法扣余额,比手动计算后更新更安全,避免并发问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 23:57:17