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. 使用 increment 方法增加积分 $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. 获取过期信息(如果没有传入规则,则查询) $expiredDay = 0; $expiredTime = null; if ($rule) { $expiredDay = $rule->expired_day ?? 0; $expiredTime = $expiredDay > 0 ? Carbon::now()->addDays($expiredDay) : null; } else { // 兼容旧代码,如果没有传入规则则查询 $rule = RewardActiveRule::where('type_of_transaction', $type) ->where('status', 1) ->first(); $expiredDay = $rule ? $rule->expired_day : 0; $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' => RewardPointHistory::STATUS_COMPLETED ]); 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) { return $this->where('customer_id', $customerId) ->orderBy('transaction_time', 'desc') ->paginate($limit); } public function checkExpiredPoints() { $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' => $history->point_remaining ]); } }); } catch (\Exception $e) { Log::error('Error processing expired points', [ 'history_id' => $history->history_id, 'error' => $e->getMessage() ]); } } } }