Files
cunkebao_v3/Server/application/common/service/WechatAccountHealthScoreService.php
2025-11-20 16:07:57 +08:00

379 lines
12 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace app\common\service;
use think\Db;
use think\Exception;
/**
* 微信账号健康分评分服务
* 基于《微信健康分规则v2.md》实现
*
* 健康分 = 基础分 + 动态分
* 基础分60-100分默认60分 + 基础信息10分 + 好友数量30分
* 动态分:扣分和加分规则
*/
class WechatAccountHealthScoreService
{
// 默认基础分
const DEFAULT_BASE_SCORE = 60;
// 基础信息权重和分数
const BASE_INFO_WEIGHT = 0.2;
const BASE_INFO_SCORE = 10;
// 好友数量权重和分数
const FRIEND_COUNT_WEIGHT = 0.3;
const FRIEND_COUNT_MAX_SCORE = 30;
// 动态分扣分规则
const PENALTY_FIRST_FREQUENT = -15; // 首次频繁扣15分
const PENALTY_SECOND_FREQUENT = -25; // 再次频繁扣25分
const PENALTY_BANNED = -60; // 封号扣60分
// 动态分加分规则
const BONUS_NO_FREQUENT_PER_DAY = 5; // 连续3天不触发频繁每天+5分
/**
* 计算并更新账号健康分
*
* @param int $accountId 账号IDs2_wechat_account表的id
* @param array $accountData 账号数据(可选,如果不传则从数据库查询)
* @return array 返回评分结果
*/
public function calculateAndUpdate($accountId, $accountData = null)
{
try {
// 获取账号数据
if (empty($accountData)) {
$accountData = Db::table('s2_wechat_account')
->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;
}
}