qualifiedOrderQuery($startTime, $endTime); $orderTable = (new OrderModel())->getQuery()->getTable(); $rewardTable = (new RewardModel())->getQuery()->getTable(); // 核心排除:在 shopro_partner_reward 表中已存在该订单的有效流水或标记 $query->whereNotExists(function ($q) use ($rewardTable) { $q->table($rewardTable)->alias('pr') ->whereRaw('pr.order_id = o.id') ->where('pr.order_id', '>', 0); }); return $query->order('o.id', 'asc')->limit($limit)->select(); } /** * 查询结算标记失败的异常订单 * * @param int $limit * @return \think\Collection */ public function findFailedMarkedOrders($limit = 50) { return OrderModel::alias('o') ->where('o.type', '<>', 'score') ->where('o.pay_fee', '>', 0) ->whereIn('o.status', [OrderModel::STATUS_PAID, OrderModel::STATUS_COMPLETED]) ->where('o.ext', 'like', '%"reward_settle_failed":1%') ->order('o.id', 'asc') ->limit($limit) ->select(); } /** * 执行自动对账与收益安全补偿 * * @param int $limit 单次最大补偿订单数 * @param bool $dryRun 是否仅试算对账不写入 * @return array 对账与补偿执行报表 */ public function compensateAll($limit = 100, $dryRun = false) { $unsettled = $this->findUnsettledOrders($limit); $rewardService = new PartnerRewardService(); $report = [ 'total_found' => count($unsettled), 'dry_run' => $dryRun, 'success_count' => 0, 'failed_count' => 0, 'skipped_count' => 0, 'details' => [], 'time' => date('Y-m-d H:i:s'), ]; foreach ($unsettled as $order) { $orderId = intval($order->id); $orderSn = $order->order_sn; $payFee = (float) $order->pay_fee; if ($dryRun) { $report['details'][] = [ 'order_id' => $orderId, 'order_sn' => $orderSn, 'pay_fee' => $payFee, 'status' => 'preview', 'message' => '待补偿', ]; continue; } try { $result = Db::transaction(function () use ($rewardService, $orderId) { return $rewardService->settleOrder($orderId); }); if ($result) { $report['success_count']++; $rewards = RewardModel::where('order_id', $orderId)->where('order_id', '>', 0)->select(); $cashTotal = 0; $types = []; foreach ($rewards as $r) { if ((float) $r->amount > 0 && $r->status == 1) { $cashTotal = bcadd((string)$cashTotal, (string)$r->amount, 2); } $types[] = $r->reward_type; } $report['details'][] = [ 'order_id' => $orderId, 'order_sn' => $orderSn, 'pay_fee' => $payFee, 'status' => 'success', 'cash_distributed' => $cashTotal, 'reward_types' => array_values(array_unique($types)), ]; } else { $report['skipped_count']++; $report['details'][] = [ 'order_id' => $orderId, 'order_sn' => $orderSn, 'pay_fee' => $payFee, 'status' => 'skipped', 'message' => '订单不满足计奖条件或已结', ]; } } catch (\Throwable $e) { $report['failed_count']++; $errorMsg = $e->getMessage(); \think\Log::record("[PartnerCompensate] 订单 ID:{$orderId} 补偿执行异常: {$errorMsg}", 'error'); $report['details'][] = [ 'order_id' => $orderId, 'order_sn' => $orderSn, 'pay_fee' => $payFee, 'status' => 'failed', 'error' => $errorMsg, ]; } } return $report; } /** * 收益中心与队列运行健康度全景诊断 * * @return array */ public function checkHealth() { $queueConfig = \think\Config::get('queue'); $expire = $queueConfig['expire'] ?? null; $selectDb = $queueConfig['select'] ?? null; $unsettledCount = count($this->findUnsettledOrders(500)); $failedMarkCount = count($this->findFailedMarkedOrders(500)); $warnings = []; if ($expire === 0) { $warnings[] = "致命配置警告:queue.expire 为 0,会导致队列任务被反复超时重放与并发重复消费!"; } elseif ($expire === null || $expire < 60) { $warnings[] = "配置建议:queue.expire 应设置为 >= 60 秒(推荐 180 秒)。"; } if ($unsettledCount > 0) { $warnings[] = "发现 {$unsettledCount} 笔已支付但未进行收益结算的订单,建议执行补偿操作。"; } if ($failedMarkCount > 0) { $warnings[] = "发现 {$failedMarkCount} 笔标记为结算重试失败的订单,需重点关注。"; } return [ 'status' => empty($warnings) ? 'healthy' : ($expire === 0 ? 'critical' : 'warning'), 'queue' => [ 'connector' => $queueConfig['connector'] ?? 'unknown', 'expire_seconds' => $expire, 'redis_db' => $selectDb, 'default_queue' => $queueConfig['default'] ?? 'default', ], 'unsettled_orders_count' => $unsettledCount, 'failed_marked_orders_count' => $failedMarkCount, 'warnings' => $warnings, 'check_time' => date('Y-m-d H:i:s'), ]; } }