where('id', $accountId) ->find(); } if (empty($accountData)) { throw new Exception("账号不存在:{$accountId}"); } // 计算基础分 $baseScore = $this->calculateBaseScore($accountData); // 计算动态分 $dynamicScore = $this->calculateDynamicScore($accountData); // 计算总分 $healthScore = $baseScore + $dynamicScore; // 确保健康分在合理范围内(0-100) $healthScore = max(0, min(100, $healthScore)); // 更新数据库 $updateData = [ 'healthScore' => $healthScore, 'baseScore' => $baseScore, 'dynamicScore' => $dynamicScore, 'scoreUpdateTime' => time() ]; Db::table('s2_wechat_account') ->where('id', $accountId) ->update($updateData); return [ 'accountId' => $accountId, 'wechatId' => $accountData['wechatId'] ?? '', 'healthScore' => $healthScore, 'baseScore' => $baseScore, 'dynamicScore' => $dynamicScore, 'baseInfoScore' => $this->getBaseInfoScore($accountData), 'friendCountScore' => $this->getFriendCountScore($accountData['totalFriend'] ?? 0), 'maxAddFriendPerDay' => $this->getMaxAddFriendPerDay($healthScore) ]; } catch (Exception $e) { throw new Exception("计算健康分失败:" . $e->getMessage()); } } /** * 计算基础分 * 基础分 = 默认60分 + 基础信息分(10分) + 好友数量分(30分) * * @param array $accountData 账号数据 * @return int 基础分 */ private function calculateBaseScore($accountData) { $baseScore = self::DEFAULT_BASE_SCORE; // 基础信息分(已修改微信号得10分) $baseScore += $this->getBaseInfoScore($accountData); // 好友数量分(最高30分) $totalFriend = $accountData['totalFriend'] ?? 0; $baseScore += $this->getFriendCountScore($totalFriend); return $baseScore; } /** * 获取基础信息分 * 已修改微信号:10分 * * @param array $accountData 账号数据 * @return int 基础信息分 */ private function getBaseInfoScore($accountData) { // 检查是否已修改微信号 // 如果isModifiedAlias字段为1,或者wechatId和alias不一致,则认为已修改 $isModifiedAlias = isset($accountData['isModifiedAlias']) ? (int)$accountData['isModifiedAlias'] : 0; $wechatId = trim($accountData['wechatId'] ?? ''); $alias = trim($accountData['alias'] ?? ''); // 如果字段标记为已修改,或者wechatId和alias不一致,则得分 if ($isModifiedAlias == 1 || (!empty($wechatId) && !empty($alias) && $wechatId !== $alias)) { return self::BASE_INFO_SCORE; } return 0; } /** * 获取好友数量分 * 根据好友数量区间得分(最高30分) * * @param int $totalFriend 总好友数 * @return int 好友数量分 */ private function getFriendCountScore($totalFriend) { if ($totalFriend <= 50) { return 3; // 0-50: 3分 } elseif ($totalFriend <= 500) { return 6; // 51-500: 6分 } elseif ($totalFriend <= 3000) { return 8; // 501-3000: 8分 } else { return 12; // 3001以上: 12分 } } /** * 计算动态分 * 动态分 = 扣分 + 加分 * * @param array $accountData 账号数据 * @return int 动态分 */ private function calculateDynamicScore($accountData) { $dynamicScore = 0; // 处理扣分 $dynamicScore += $this->calculatePenalty($accountData); // 处理加分 $dynamicScore += $this->calculateBonus($accountData); return $dynamicScore; } /** * 计算扣分 * 首次频繁:-15分 * 再次频繁:-25分 * 封号:-60分 * * @param array $accountData 账号数据 * @return int 扣分数 */ private function calculatePenalty($accountData) { $penalty = 0; // 检查是否有频繁记录 $lastFrequentTime = $accountData['lastFrequentTime'] ?? null; $frequentCount = $accountData['frequentCount'] ?? 0; if (!empty($lastFrequentTime)) { // 判断是首次频繁还是再次频繁 if ($frequentCount == 1) { $penalty += self::PENALTY_FIRST_FREQUENT; // 首次频繁-15分 } elseif ($frequentCount >= 2) { $penalty += self::PENALTY_SECOND_FREQUENT; // 再次频繁-25分 } } // 检查是否封号(这里需要根据实际业务逻辑判断,比如status字段或其他标识) // 假设status=0表示封号 $status = $accountData['status'] ?? 1; if ($status == 0) { $penalty += self::PENALTY_BANNED; // 封号-60分 } return $penalty; } /** * 计算加分 * 连续3天不触发频繁:每天+5分 * * @param array $accountData 账号数据 * @return int 加分数 */ private function calculateBonus($accountData) { $bonus = 0; $lastNoFrequentTime = $accountData['lastNoFrequentTime'] ?? null; $consecutiveNoFrequentDays = $accountData['consecutiveNoFrequentDays'] ?? 0; // 如果连续不频繁天数>=3,则每天+5分 if ($consecutiveNoFrequentDays >= 3) { $bonus = $consecutiveNoFrequentDays * self::BONUS_NO_FREQUENT_PER_DAY; } return $bonus; } /** * 根据健康分计算每日最大加人次数 * 公式:每日最大加人次数 = 健康分 * 0.2 * * @param int $healthScore 健康分 * @return int 每日最大加人次数 */ public function getMaxAddFriendPerDay($healthScore) { return (int)floor($healthScore * 0.2); } /** * 批量计算并更新多个账号的健康分 * * @param array $accountIds 账号ID数组 * @param int $batchSize 每批处理数量 * @return array 处理结果统计 */ public function batchCalculateAndUpdate($accountIds = [], $batchSize = 100) { $stats = [ 'total' => 0, 'success' => 0, 'failed' => 0, 'errors' => [] ]; // 如果没有指定账号ID,则处理所有账号 if (empty($accountIds)) { $accountIds = Db::table('s2_wechat_account') ->where('isDeleted', 0) ->column('id'); } $stats['total'] = count($accountIds); // 分批处理 $batches = array_chunk($accountIds, $batchSize); foreach ($batches as $batch) { foreach ($batch as $accountId) { try { $this->calculateAndUpdate($accountId); $stats['success']++; } catch (Exception $e) { $stats['failed']++; $stats['errors'][] = [ 'accountId' => $accountId, 'error' => $e->getMessage() ]; } } } return $stats; } /** * 记录频繁事件 * * @param int $accountId 账号ID * @return bool */ public function recordFrequent($accountId) { $accountData = Db::table('s2_wechat_account') ->where('id', $accountId) ->find(); if (empty($accountData)) { return false; } $frequentCount = ($accountData['frequentCount'] ?? 0) + 1; $updateData = [ 'lastFrequentTime' => time(), 'frequentCount' => $frequentCount, 'consecutiveNoFrequentDays' => 0, // 重置连续不频繁天数 'lastNoFrequentTime' => null ]; Db::table('s2_wechat_account') ->where('id', $accountId) ->update($updateData); // 重新计算健康分 $this->calculateAndUpdate($accountId); return true; } /** * 记录不频繁事件(用于加分) * * @param int $accountId 账号ID * @return bool */ public function recordNoFrequent($accountId) { $accountData = Db::table('s2_wechat_account') ->where('id', $accountId) ->find(); if (empty($accountData)) { return false; } $lastNoFrequentTime = $accountData['lastNoFrequentTime'] ?? null; $consecutiveNoFrequentDays = $accountData['consecutiveNoFrequentDays'] ?? 0; $currentTime = time(); // 如果上次不频繁时间是昨天或更早,则增加连续天数 if (empty($lastNoFrequentTime) || ($currentTime - $lastNoFrequentTime) >= 86400) { // 如果间隔超过1天,重置为1天 if (!empty($lastNoFrequentTime) && ($currentTime - $lastNoFrequentTime) > 86400 * 2) { $consecutiveNoFrequentDays = 1; } else { $consecutiveNoFrequentDays++; } } $updateData = [ 'lastNoFrequentTime' => $currentTime, 'consecutiveNoFrequentDays' => $consecutiveNoFrequentDays ]; Db::table('s2_wechat_account') ->where('id', $accountId) ->update($updateData); // 重新计算健康分 $this->calculateAndUpdate($accountId); return true; } }