first(); if (!$customerPoints) { return 0; } return $customerPoints->mw_reward_point; } /** * 添加积分(支持传入规则信息) */ public function addPoints($customerId, $type, $amount, $orderId = null, $detail = null, $rule = null) { try { return DB::transaction(function () use ($customerId, $type, $amount, $orderId, $detail, $rule) { // 1. 获取或创建用户积分记录 $customerPoints = RewardPointCustomer::firstOrCreate( ['customer_id' => $customerId], [ 'mw_reward_point' => 0, 'mw_friend_id' => 0, 'subscribed_balance_update' => 1, 'subscribed_point_expiration' => 1, 'last_checkout' => Carbon::now() ] ); // 2. 确定积分状态 // 订单类型的积分初始状态为 PENDING,其他类型直接 COMPLETED $status = ($type === RewardActiveRule::TYPE_ORDER) ? RewardPointHistory::STATUS_PENDING : RewardPointHistory::STATUS_COMPLETED; // 3. 只有 COMPLETED 状态的积分才计入用户总积分 if ($status === RewardPointHistory::STATUS_COMPLETED) { $customerPoints->increment('mw_reward_point', $amount); } // 3. 更新最后检查时间 $customerPoints->last_checkout = Carbon::now(); $customerPoints->save(); // 4. 重新查询获取最新的余额 $customerPoints = RewardPointCustomer::where('customer_id', $customerId)->first(); $newBalance = $customerPoints ? (int) $customerPoints->mw_reward_point : $amount; /* Log::info('Points added', [ 'customer_id' => $customerId, 'type' => $type, 'amount' => $amount, 'new_balance' => $newBalance ]);*/ // 5. 获取过期信息(如果没有传入规则,则查询) if ($rule) { $expiredDay = $rule->expired_day ?? 0; $expiredTime = $expiredDay > 0 ? Carbon::now()->addDays($expiredDay) : null; } else { $expiredDay = 365; $expiredTime = $expiredDay > 0 ? Carbon::now()->addDays($expiredDay) : null; } // 6. 创建历史记录 $history = $this->create([ 'customer_id' => $customerId, 'type_of_transaction' => $type, 'amount' => $amount, 'balance' => $newBalance, 'transaction_detail' => $detail, 'transaction_time' => Carbon::now(), 'history_order_id' => $orderId ?? 0, 'expired_day' => $expiredDay, 'expired_time' => $expiredTime, 'point_remaining' => $amount, 'check_time' => 1, 'status' => $status ]); return $history; }); } catch (\Exception $e) { Log::error('Error adding points', [ 'customer_id' => $customerId, 'error' => $e->getMessage(), 'trace' => $e->getTraceAsString() ]); throw $e; } } /** * 批量添加积分(用于同时多个事件) * 确保每条历史记录都有正确的余额值 */ public function addPointsBatch($customerId, array $pointsList) { try { return DB::transaction(function () use ($customerId, $pointsList) { // 1. 获取或创建用户积分记录(不使用 lockForUpdate 避免问题) $customerPoints = RewardPointCustomer::firstOrCreate( ['customer_id' => $customerId], [ 'mw_reward_point' => 0, 'mw_friend_id' => 0, 'subscribed_balance_update' => 1, 'subscribed_point_expiration' => 1, 'last_checkout' => Carbon::now() ] ); // 确保 $customerPoints 存在 if (!$customerPoints) { throw new \Exception('Failed to create or retrieve customer points record'); } // 2. 获取当前余额 $currentBalance = (int) $customerPoints->mw_reward_point; $histories = []; $totalAmount = 0; // 3. 逐条创建历史记录(每条记录使用累加后的余额) foreach ($pointsList as $index => $pointData) { $amount = (int) $pointData['amount']; $type = $pointData['type']; $detail = $pointData['detail'] ?? null; $orderId = $pointData['order_id'] ?? null; $rule = $pointData['rule'] ?? null; // 累加总积分 $totalAmount += $amount; // 计算当前这条记录后的余额(累加后的余额) $newBalance = $currentBalance + $totalAmount; // 获取过期信息 $expiredDay = 0; $expiredTime = null; if ($rule) { $expiredDay = $rule->expired_day ?? 0; $expiredTime = $expiredDay > 0 ? Carbon::now()->addDays($expiredDay) : null; } // 创建历史记录(每条记录独立) $history = $this->create([ 'customer_id' => $customerId, 'type_of_transaction' => $type, 'amount' => $amount, 'balance' => $newBalance, 'transaction_detail' => $detail, 'transaction_time' => Carbon::now(), 'history_order_id' => $orderId ?? 0, 'expired_day' => $expiredDay, 'expired_time' => $expiredTime, 'point_remaining' => $amount, 'check_time' => 1, 'status' => RewardPointHistory::STATUS_COMPLETED ]); $histories[] = $history; } // 4. 最后一次性更新用户总积分 $updated = DB::table('mw_reward_point_customer') ->where('customer_id', $customerId) ->update([ 'mw_reward_point' => DB::raw('mw_reward_point + ' . (float)$totalAmount), 'last_checkout' => Carbon::now() ]); return $histories; }); } catch (\Exception $e) { Log::error('Error adding batch points', [ 'customer_id' => $customerId, 'error' => $e->getMessage(), 'trace' => $e->getTraceAsString() ]); throw $e; } } public function deductPoints($customerId, $amount, $orderId = null, $detail = null) { try { return DB::transaction(function () use ($customerId, $amount, $orderId, $detail) { $customerPoints = RewardPointCustomer::where('customer_id', $customerId) ->lockForUpdate() ->first(); if (!$customerPoints) { Log::warning('Customer points record not found', ['customer_id' => $customerId]); return false; } if ($customerPoints->mw_reward_point < $amount) { Log::warning('Insufficient points', [ 'customer_id' => $customerId, 'current' => $customerPoints->mw_reward_point, 'required' => $amount ]); return false; } $currentBalance = (int) $customerPoints->mw_reward_point; $newBalance = $currentBalance - (int) $amount; // 更新积分 $customerPoints->mw_reward_point = $newBalance; $customerPoints->last_checkout = Carbon::now(); $customerPoints->save(); // 创建历史记录 $history = $this->create([ 'customer_id' => $customerId, 'type_of_transaction' => 0, 'amount' => -$amount, 'balance' => $newBalance, 'transaction_detail' => $detail, 'transaction_time' => Carbon::now(), 'history_order_id' => $orderId ?? 0, 'expired_day' => 0, 'expired_time' => null, 'point_remaining' => 0, 'check_time' => 1, 'status' => RewardPointHistory::STATUS_COMPLETED ]); Log::info('Points deducted successfully', [ 'customer_id' => $customerId, 'amount' => $amount, 'new_balance' => $newBalance ]); return $history; }); } catch (\Exception $e) { Log::error('Error deducting points', [ 'customer_id' => $customerId, 'amount' => $amount, 'error' => $e->getMessage() ]); return false; } } public function getHistory($customerId, $limit = 20, $page = null) { $query = $this->where('customer_id', $customerId) ->orderBy('transaction_time', 'desc'); if ($page) { return $query->paginate($limit, ['*'], 'page', $page); } return $query->paginate($limit); } public function checkExpiredPoints() { // 只检查已过期的、状态为 COMPLETED 的、还有剩余积分的记录 // PENDING 状态的积分不会过期(因为订单还未完成) $expiredHistories = $this->where('expired_time', '<', Carbon::now()) ->where('status', RewardPointHistory::STATUS_COMPLETED) ->where('point_remaining', '>', 0) ->get(); foreach ($expiredHistories as $history) { try { DB::transaction(function () use ($history) { $customerPoints = RewardPointCustomer::where('customer_id', $history->customer_id) ->lockForUpdate() ->first(); if ($customerPoints && $customerPoints->mw_reward_point >= $history->point_remaining) { $customerPoints->mw_reward_point -= $history->point_remaining; $customerPoints->save(); $history->status = RewardPointHistory::STATUS_EXPIRED; $history->point_remaining = 0; $history->save(); Log::info('Expired points processed', [ 'history_id' => $history->history_id, 'customer_id' => $history->customer_id, 'points_expired' => $history->getOriginal('point_remaining') ]); } }); } catch (\Exception $e) { Log::error('Error processing expired points', [ 'history_id' => $history->history_id, 'error' => $e->getMessage() ]); } } } /** * 确认待处理的积分(将 PENDING 状态改为 COMPLETED 并计入用户余额) */ public function confirmPendingPoints($customerId, $orderId) { try { return DB::transaction(function () use ($customerId, $orderId) { // 查找所有 PENDING 状态的订单积分记录 $pendingHistories = $this->where('customer_id', $customerId) ->where('history_order_id', $orderId) ->where('type_of_transaction', RewardActiveRule::TYPE_ORDER) ->where('status', RewardPointHistory::STATUS_PENDING) ->get(); if ($pendingHistories->isEmpty()) { Log::info('No pending points to confirm', [ 'customer_id' => $customerId, 'order_id' => $orderId ]); return 0; } $totalConfirmedPoints = 0; foreach ($pendingHistories as $history) { // 增加用户总积分 $customerPoints = RewardPointCustomer::where('customer_id', $customerId) ->lockForUpdate() ->first(); if ($customerPoints) { $pointsToAdd = $history->point_remaining; // 增加积分 $customerPoints->increment('mw_reward_point', $pointsToAdd); $customerPoints->refresh(); // 刷新获取最新值 $newBalance = (int) $customerPoints->mw_reward_point; // 直接更新原记录的状态和余额 $history->status = RewardPointHistory::STATUS_COMPLETED; $history->balance = $newBalance; $history->save(); $totalConfirmedPoints += $pointsToAdd; Log::info('Pending points confirmed', [ 'history_id' => $history->history_id, 'customer_id' => $customerId, 'order_id' => $orderId, 'points' => $pointsToAdd, 'old_balance' => $history->getOriginal('balance'), 'new_balance' => $newBalance ]); } } return $totalConfirmedPoints; }); } catch (\Exception $e) { Log::error('Error confirming pending points', [ 'customer_id' => $customerId, 'order_id' => $orderId, 'error' => $e->getMessage(), 'trace' => $e->getTraceAsString() ]); throw $e; } } }