Read article node counts from the published v2 release

This commit is contained in:
hellcat 2026-10-22 13:35:03 +08:00
parent 568aa9e4b8
commit cc9d1444a3
3 changed files with 88 additions and 5 deletions

View File

@ -4,6 +4,8 @@ declare(strict_types=1);
namespace App\Services;
use App\Models\FreenodePool;
use App\Services\Freenode\Pipeline;
use RuntimeException;
final class CountrySpeedService
{
@ -60,16 +62,35 @@ final class CountrySpeedService
//// 速度
$iNodeSpeed = rand(config("freenode.speed_min"), config("freenode.speed_max"));
$oFnp = FreenodePool::whereDate('date', date("Y-m-d"))->first();
// var_dump($oFnp->id);exit;
$aFnpContent = json_decode($oFnp->content ?? '[]', true); // 有问题吗
$iNodeCount = count($aFnpContent["v2ray"] ?? []) ?? rand(30, 98);
// $iNodeCount = rand(config("freenode.count_min"), config("freenode.count_max"));
$iNodeCount = self::nodeCount();
$sContent = "本次更新共<b style='color:pink'>".$iNodeCount."</b>个可用节点,最高速度".$iNodeSpeed."M/S。
覆盖".$sNodeZone."等多个区域。";
return $sContent;
}
public static function nodeCount(): int
{
if (Pipeline::enabled()) {
$store = Pipeline::store();
return $store->locked(function () use ($store): int {
$release = $store->current();
if (!$release || ($release['validated'] ?? false) !== true
|| ($release['date'] ?? '') !== date('Y-m-d')
|| time() - ($release['created_at'] ?? 0) > 86400) {
throw new RuntimeException('no_today_published_release_for_article');
}
$count = $release['counts']['v2ray'] ?? null;
if (!is_int($count) || $count < 1) {
throw new RuntimeException('invalid_published_node_count');
}
return $count;
}, false);
}
$pool = FreenodePool::whereDate('date', date('Y-m-d'))->first();
$content = json_decode($pool->content ?? '[]', true);
return count($content['v2ray'] ?? []);
}
}

View File

@ -229,3 +229,11 @@ Nodehub仍运行`507cbf7`,已在今天00:15正常生成20261022四站10份文
回退:先`systemctl disable --now gitea-archive-cache-cleaner.timer`,停止service,再按部署备份恢复/移除本次独立脚本和两个unit并`systemctl daemon-reload`;无需回退免费节点业务代码。已删除缓存可按需重新生成。部署前检查生产HEAD、并行状态及旧文件,保留安装前状态和验收记录到服务器私有备份目录;本地5项测试覆盖过期/容量淘汰、打开文件/新文件/未知文件保护、符号链接、预览与重复执行。
2026-10-22部署验收:功能提交`48dc3ba6529226fae866d440625080632c1d88a0`已推送并部署;生产5项测试通过,systemd unit校验通过。timer已enabled/active,13:04:53首轮实际定时触发成功(退出0),清理53份过期缓存、共1,000,799,388字节,缓存由约956MiB降为0;复跑预览无额外候选。根盘可用3,323,936,768字节,Gitea仍active;当前订阅manifest逐字节不变,原并行Git状态保持。安装前状态、Git增量、预览与验收回执保存在`/var/backups/master01-gitea-cache-cleaner-20261022/`。已完成服务器侧验收,未做线上页面验收;尚不将一次成功视为长期磁盘容量保障。
### V2文章节点数量仍读旧池的问题
2026-10-22用户指出cfn首页“本次更新共0个可用节点”。服务器侧HTTP读取确认首页19/20/21日三篇均显示0,cfn数据库对应slug为`fn20261019`(id356)、`fn20261020`(id358)、`fn20261021`(id360),22日文章尚不存在。master01旧`freenode_pool`最新记录为18日;V2 prepare已经不再写该表,但`CountrySpeedService::fn_b()`仍按今天日期读旧池,空值经count变0,`?? rand()`不可能生效。这是重构遗漏的文章统计调用方,与订阅文件为空无关。
修正`CountrySpeedService::nodeCount()`:V2从当天current已验证批次的v2ray统计取值,不取pending、不回退旧池,不存在当日有效发布或数量无效则抛异常阻止生成0节点文章。legacy沿用原池逻辑;其他随机文案保持原样,本次未实现节点联网可用性/测速。测试覆盖published优先、pending拒绝、过期日期和零值拒绝;整套23项53断言通过。
现存cfn文案仅计划按对应日期已发布产物修正19/20/21日三个0计数,备份正文并事务更新`sTitleSub`/`sContent`内的数字,不重发文章、不改订阅/图片/点赞/标签、不触发Telegram或IndexNow;不把今天2592条套到历史日期。部署和实际修正结果待验收。

View File

@ -0,0 +1,54 @@
<?php
namespace Tests\Unit\Freenode;
use App\Services\CountrySpeedService;
use Illuminate\Config\Repository;
use Illuminate\Foundation\Application;
use PHPUnit\Framework\TestCase;
final class ArticleCountTest extends TestCase
{
private string $dir;
protected function setUp(): void
{
$this->dir = sys_get_temp_dir().'/article-count-'.bin2hex(random_bytes(6));
mkdir($this->dir.'/app/freenode-v2', 0700, true);
$app = new Application(dirname(__DIR__, 3));
$app->useStoragePath($this->dir);
$app->instance('config', new Repository(['freenode' => ['engine' => 'v2']]));
}
protected function tearDown(): void
{
foreach (glob($this->dir.'/app/freenode-v2/*') as $p) unlink($p);
rmdir($this->dir.'/app/freenode-v2'); rmdir($this->dir.'/app'); rmdir($this->dir);
}
private function write(array $override = [], string $name = 'current'): void
{
$m = array_replace(['id'=>'published', 'validated'=>true, 'date'=>date('Y-m-d'),
'created_at'=>time(), 'counts'=>['v2ray'=>2592]], $override);
file_put_contents($this->dir.'/app/freenode-v2/'.$name.'.json', json_encode($m));
}
public function testV2ReadsPublishedCountWithoutLegacyDatabase(): void
{
$this->write(); $this->write(['counts'=>['v2ray'=>9999]], 'pending');
self::assertSame(2592, CountrySpeedService::nodeCount());
}
public function testPendingAloneCannotProduceAnArticleCount(): void
{
$this->write([], 'pending');
$this->expectExceptionMessage('no_today_published_release_for_article');
CountrySpeedService::nodeCount();
}
public function testPreviousDayIsRejectedInsteadOfPublishingZero(): void
{
$this->write(['date'=>date('Y-m-d', time()-86400)]);
$this->expectExceptionMessage('no_today_published_release_for_article');
CountrySpeedService::nodeCount();
}
public function testZeroCountIsRejected(): void
{
$this->write(['counts'=>['v2ray'=>0]]);
$this->expectExceptionMessage('invalid_published_node_count');
CountrySpeedService::nodeCount();
}
}