feat(admin-frontend): 补齐活跃筛选与支付快照能力
新增用户管理“活跃状态”高级筛选,并在后端支持 activity_status 复合规则,支持按活跃与非活跃筛选用户。 补齐订单支付成功快照落库与后台展示,保存支付渠道、 支付方法、实付金额和支付 IP,并在订单详情中优先展示。 同时增强节点页在线/离线筛选与批量删除、仪表盘快捷入口, 并修复已关闭工单再次回复后自动重开的统一语义。 附带同步测试、迁移、CI 工作流命名及知识库记录
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Unit\Admin;
|
||||
|
||||
use App\Http\Controllers\V2\Admin\UserController;
|
||||
use Illuminate\Database\Capsule\Manager as Capsule;
|
||||
use Illuminate\Database\Query\Builder as QueryBuilder;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use ReflectionMethod;
|
||||
|
||||
class UserControllerActivityStatusFilterTest extends TestCase
|
||||
{
|
||||
private static ?Capsule $capsule = null;
|
||||
|
||||
public function test_resolve_activity_status_value_supports_eq_payloads(): void
|
||||
{
|
||||
$controller = new UserController();
|
||||
$method = new ReflectionMethod(UserController::class, 'resolveActivityStatusValue');
|
||||
$method->setAccessible(true);
|
||||
|
||||
$this->assertTrue($method->invoke($controller, 'eq:1'));
|
||||
$this->assertFalse($method->invoke($controller, 'eq:0'));
|
||||
$this->assertNull($method->invoke($controller, 'gte:1'));
|
||||
}
|
||||
|
||||
public function test_active_activity_status_filter_requires_plan_remaining_traffic_and_recent_online(): void
|
||||
{
|
||||
$builder = $this->newQueryBuilder();
|
||||
|
||||
$this->applyFilter($builder, 'activity_status', 'eq:1');
|
||||
|
||||
$sql = $builder->toSql();
|
||||
|
||||
$this->assertStringContainsString('"plan_id" is not null', $sql);
|
||||
$this->assertStringContainsString('COALESCE(transfer_enable, 0) > COALESCE(u, 0) + COALESCE(d, 0)', $sql);
|
||||
$this->assertStringContainsString('"last_online_at" is not null', $sql);
|
||||
$this->assertStringContainsString('"last_online_at" >= ?', $sql);
|
||||
$this->assertCount(1, $builder->getBindings());
|
||||
}
|
||||
|
||||
public function test_inactive_activity_status_filter_uses_reverse_condition_set(): void
|
||||
{
|
||||
$builder = $this->newQueryBuilder();
|
||||
|
||||
$this->applyFilter($builder, 'activity_status', 'eq:0');
|
||||
|
||||
$sql = $builder->toSql();
|
||||
|
||||
$this->assertStringContainsString('("plan_id" is null or COALESCE(transfer_enable, 0) <= COALESCE(u, 0) + COALESCE(d, 0) or "last_online_at" is null or "last_online_at" < ?)', $sql);
|
||||
$this->assertCount(1, $builder->getBindings());
|
||||
}
|
||||
|
||||
private function applyFilter(QueryBuilder $builder, string $field, mixed $value): void
|
||||
{
|
||||
$controller = new UserController();
|
||||
$method = new ReflectionMethod(UserController::class, 'buildFilterQuery');
|
||||
$method->setAccessible(true);
|
||||
$method->invoke($controller, $builder, $field, $value);
|
||||
}
|
||||
|
||||
private function newQueryBuilder(): QueryBuilder
|
||||
{
|
||||
if (!self::$capsule) {
|
||||
$capsule = new Capsule();
|
||||
$capsule->addConnection([
|
||||
'driver' => 'sqlite',
|
||||
'database' => ':memory:',
|
||||
'prefix' => '',
|
||||
]);
|
||||
$capsule->setAsGlobal();
|
||||
$capsule->bootEloquent();
|
||||
self::$capsule = $capsule;
|
||||
}
|
||||
|
||||
return self::$capsule->getConnection()->table('v2_user');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user