Files
cunkebao_v3/Server/application/command/CalculateWechatAccountScoreCommand.php
2025-11-21 14:38:29 +08:00

312 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\command;
use think\console\Command;
use think\console\Input;
use think\console\Output;
use think\Db;
use app\common\service\WechatAccountHealthScoreService;
/**
* 统一计算微信账号健康分命令
* 一个命令完成所有评分工作:
* 1. 初始化未计算的账号(基础分只计算一次)
* 2. 更新评分记录根据wechatId和alias不一致情况
* 3. 批量更新健康分(只更新动态分)
*/
class CalculateWechatAccountScoreCommand extends Command
{
protected function configure()
{
$this->setName('wechat:calculate-score')
->setDescription('统一计算微信账号健康分(包含初始化、更新评分记录、批量计算)');
}
protected function execute(Input $input, Output $output)
{
$output->writeln("==========================================");
$output->writeln("开始统一计算微信账号健康分...");
$output->writeln("==========================================");
$startTime = time();
$service = new WechatAccountHealthScoreService();
try {
// 步骤1: 初始化未计算基础分的账号
$output->writeln("\n[步骤1] 初始化未计算基础分的账号...");
$initStats = $this->initUncalculatedAccounts($service, $output);
$output->writeln("初始化完成:成功 {$initStats['success']} 条,失败 {$initStats['failed']}");
// 步骤2: 更新评分记录根据wechatId和alias不一致情况
$output->writeln("\n[步骤2] 更新评分记录根据wechatId和alias不一致情况...");
$updateStats = $this->updateScoreRecords($service, $output);
$output->writeln("更新完成:处理了 {$updateStats['total']} 条记录");
// 步骤3: 批量更新健康分(只更新动态分,不重新计算基础分)
$output->writeln("\n[步骤3] 批量更新健康分(只更新动态分)...");
$batchStats = $this->batchUpdateHealthScore($service, $output);
$output->writeln("批量更新完成:成功 {$batchStats['success']} 条,失败 {$batchStats['failed']}");
// 统计信息
$endTime = time();
$duration = $endTime - $startTime;
$output->writeln("\n==========================================");
$output->writeln("任务完成!");
$output->writeln("==========================================");
$output->writeln("总耗时: {$duration}");
$output->writeln("初始化: 成功 {$initStats['success']} 条,失败 {$initStats['failed']}");
$output->writeln("更新评分记录: {$updateStats['total']}");
$output->writeln("批量更新: 成功 {$batchStats['success']} 条,失败 {$batchStats['failed']}");
if (!empty($initStats['errors'])) {
$output->writeln("\n初始化错误详情:");
foreach (array_slice($initStats['errors'], 0, 10) as $error) {
$output->writeln(" 账号ID {$error['accountId']}: {$error['error']}");
}
if (count($initStats['errors']) > 10) {
$output->writeln(" ... 还有 " . (count($initStats['errors']) - 10) . " 个错误");
}
}
if (!empty($batchStats['errors'])) {
$output->writeln("\n批量更新错误详情:");
foreach (array_slice($batchStats['errors'], 0, 10) as $error) {
$output->writeln(" 账号ID {$error['accountId']}: {$error['error']}");
}
if (count($batchStats['errors']) > 10) {
$output->writeln(" ... 还有 " . (count($batchStats['errors']) - 10) . " 个错误");
}
}
} catch (\Exception $e) {
$output->writeln("\n错误: " . $e->getMessage());
$output->writeln($e->getTraceAsString());
}
}
/**
* 初始化未计算基础分的账号
*
* @param WechatAccountHealthScoreService $service
* @param Output $output
* @return array
*/
private function initUncalculatedAccounts($service, $output)
{
$stats = [
'total' => 0,
'success' => 0,
'failed' => 0,
'errors' => []
];
// 获取所有未计算基础分的账号
$accounts = Db::table('s2_wechat_account')
->alias('a')
->leftJoin(['s2_wechat_account_score' => 's'], 's.accountId = a.id')
->where('a.isDeleted', 0)
->where(function($query) {
$query->whereNull('s.id')
->whereOr('s.baseScoreCalculated', 0);
})
->field('a.id, a.wechatId')
->select();
$stats['total'] = count($accounts);
if ($stats['total'] == 0) {
$output->writeln("没有需要初始化的账号");
return $stats;
}
$output->writeln("找到 {$stats['total']} 个需要初始化的账号");
$batchSize = 100;
$batches = array_chunk($accounts, $batchSize);
foreach ($batches as $batchIndex => $batch) {
foreach ($batch as $account) {
try {
$service->calculateAndUpdate($account['id']);
$stats['success']++;
if ($stats['success'] % 100 == 0) {
$output->write(".");
}
} catch (\Exception $e) {
$stats['failed']++;
$stats['errors'][] = [
'accountId' => $account['id'],
'error' => $e->getMessage()
];
}
}
if (($batchIndex + 1) % 10 == 0) {
$output->writeln(" 已处理 " . ($batchIndex + 1) * $batchSize . "");
}
}
return $stats;
}
/**
* 更新评分记录根据wechatId和alias不一致情况
*
* @param WechatAccountHealthScoreService $service
* @param Output $output
* @return array
*/
private function updateScoreRecords($service, $output)
{
$stats = ['total' => 0];
// 查找wechatId和alias不一致的账号
$inconsistentAccounts = Db::table('s2_wechat_account')
->where('isDeleted', 0)
->where('wechatId', '<>', '')
->where('alias', '<>', '')
->whereRaw('wechatId != alias')
->field('id, wechatId, alias')
->select();
// 查找wechatId和alias一致的账号
$consistentAccounts = Db::table('s2_wechat_account')
->where('isDeleted', 0)
->where('wechatId', '<>', '')
->where('alias', '<>', '')
->whereRaw('wechatId = alias')
->field('id, wechatId, alias')
->select();
$allAccounts = array_merge($inconsistentAccounts, $consistentAccounts);
$stats['total'] = count($allAccounts);
if ($stats['total'] == 0) {
$output->writeln("没有需要更新的账号");
return $stats;
}
$output->writeln("找到 {$stats['total']} 个需要更新的账号(不一致: " . count($inconsistentAccounts) . ",一致: " . count($consistentAccounts) . "");
$updatedCount = 0;
foreach ($allAccounts as $account) {
$isModifiedAlias = in_array($account['id'], array_column($inconsistentAccounts, 'id'));
$this->updateScoreRecord($account['id'], $isModifiedAlias, $service);
$updatedCount++;
if ($updatedCount % 100 == 0) {
$output->write(".");
}
}
if ($updatedCount > 0 && $updatedCount % 100 == 0) {
$output->writeln("");
}
return $stats;
}
/**
* 批量更新健康分(只更新动态分)
*
* @param WechatAccountHealthScoreService $service
* @param Output $output
* @return array
*/
private function batchUpdateHealthScore($service, $output)
{
// 获取所有已计算基础分的账号
$accountIds = Db::table('s2_wechat_account_score')
->where('baseScoreCalculated', 1)
->column('accountId');
$total = count($accountIds);
if ($total == 0) {
$output->writeln("没有需要更新的账号");
return ['success' => 0, 'failed' => 0, 'errors' => []];
}
$output->writeln("找到 {$total} 个需要更新动态分的账号");
$stats = $service->batchCalculateAndUpdate($accountIds, 100, false);
return $stats;
}
/**
* 更新评分记录
*
* @param int $accountId 账号ID
* @param bool $isModifiedAlias 是否已修改微信号
* @param WechatAccountHealthScoreService $service 评分服务
*/
private function updateScoreRecord($accountId, $isModifiedAlias, $service)
{
// 获取账号数据
$accountData = Db::table('s2_wechat_account')
->where('id', $accountId)
->find();
if (empty($accountData)) {
return;
}
// 确保评分记录存在
$scoreRecord = Db::table('s2_wechat_account_score')
->where('accountId', $accountId)
->find();
if (empty($scoreRecord)) {
// 如果记录不存在,创建并计算基础分
$service->calculateAndUpdate($accountId);
$scoreRecord = Db::table('s2_wechat_account_score')
->where('accountId', $accountId)
->find();
}
if (empty($scoreRecord)) {
return;
}
// 更新isModifiedAlias字段
$updateData = [
'isModifiedAlias' => $isModifiedAlias ? 1 : 0,
'updateTime' => time()
];
// 如果基础分已计算,需要更新基础信息分和基础分
if ($scoreRecord['baseScoreCalculated']) {
$oldBaseInfoScore = $scoreRecord['baseInfoScore'] ?? 0;
$newBaseInfoScore = $isModifiedAlias ? 10 : 0; // 已修改微信号得10分
if ($oldBaseInfoScore != $newBaseInfoScore) {
$oldBaseScore = $scoreRecord['baseScore'] ?? 60;
$newBaseScore = $oldBaseScore - $oldBaseInfoScore + $newBaseInfoScore;
$updateData['baseInfoScore'] = $newBaseInfoScore;
$updateData['baseScore'] = $newBaseScore;
// 重新计算健康分
$dynamicScore = $scoreRecord['dynamicScore'] ?? 0;
$healthScore = $newBaseScore + $dynamicScore;
$healthScore = max(0, min(100, $healthScore));
$updateData['healthScore'] = $healthScore;
$updateData['maxAddFriendPerDay'] = (int)floor($healthScore * 0.2);
}
} else {
// 基础分未计算,只更新标记和基础信息分
$updateData['baseInfoScore'] = $isModifiedAlias ? 10 : 0;
}
Db::table('s2_wechat_account_score')
->where('accountId', $accountId)
->update($updateData);
}
}