Files
cunkebao_v3/Server/application/command/CalculateWechatAccountScoreCommand.php

312 lines
12 KiB
PHP
Raw Normal View History

2025-11-21 14:38:29 +08:00
<?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);
}
}