['0', '模拟管理总开关:1 开启'], 'simulation_max_concurrency' => ['2', '模拟自动任务同时运行数上限'], 'simulation_rate_per_minute' => ['30', '模拟自动任务每分钟动作数上限'], 'simulation_max_members_per_task' => ['20', '单次创建/单个任务的模拟会员数上限'], 'simulation_max_members_total' => ['500', '模拟会员总数上限'], 'simulation_max_amount' => ['10000', '模拟充值/提现/下注单次金额上限'], 'simulation_initial_balance' => ['0', '新建模拟会员的初始余额'], 'simulation_wager_bypass' => ['1', '模拟提现是否跳过打码量:1 跳过'], 'simulation_default_domain' => ['sim.local', '模拟会员默认注册/登录域名'], 'simulation_task_members' => ['5', '一键任务:注册的模拟会员数'], 'simulation_task_recharge' => ['1000', '一键任务:每人充值金额'], 'simulation_task_bet_category' => ['mixed', '一键任务:下注分类 sport 体育 / lottery 彩票 / third 第三方游戏 / mixed 混合'], 'simulation_task_bet_amount' => ['100', '一键任务:单次下注金额'], 'simulation_task_bet_count' => ['3', '一键任务:每人下注次数'], 'simulation_task_withdraw' => ['200', '一键任务:每人提现金额,0 为不提现'], 'simulation_task_interval' => ['2', '一键任务:每个动作之间的间隔秒数'], 'simulation_schedule_enabled' => ['0', '持续运行开关:1 在日期区间内一轮接一轮自动启动一键任务'], 'simulation_schedule_start' => ['', '持续运行开始日期 YYYY-MM-DD'], 'simulation_schedule_end' => ['', '持续运行结束日期 YYYY-MM-DD(含当天)'], 'simulation_schedule_gap_minutes' => ['10', '持续运行:上一轮结束到下一轮开始的间隔分钟数'], ]; public function up(): void { if (!Schema::hasTable('simulation_tasks')) { Schema::create('simulation_tasks', function (Blueprint $t) { $t->id(); $t->string('name', 100); $t->json('steps'); $t->string('target_mode', 16)->default('new')->comment('new 新建会员 / existing 使用已有模拟会员'); $t->unsignedInteger('member_count')->default(1); $t->string('remark', 255)->default(''); $t->unsignedBigInteger('created_by')->nullable(); $t->timestamps(); }); } if (!Schema::hasTable('simulation_task_runs')) { Schema::create('simulation_task_runs', function (Blueprint $t) { $t->id(); $t->unsignedBigInteger('task_id')->nullable(); $t->string('name', 100); $t->json('steps'); $t->json('member_ids'); $t->unsignedInteger('member_count')->default(1); $t->string('status', 16)->default('queued')->comment('queued running paused stopped completed failed'); $t->unsignedInteger('total')->default(0); $t->unsignedInteger('completed')->default(0); $t->unsignedInteger('failed')->default(0); $t->json('cursor')->nullable(); $t->dateTime('next_run_at')->nullable(); $t->dateTime('started_at')->nullable(); $t->dateTime('finished_at')->nullable(); $t->unsignedBigInteger('created_by')->nullable(); $t->string('last_error', 500)->nullable(); $t->timestamps(); $t->index(['status', 'next_run_at']); }); } if (!Schema::hasTable('simulation_task_events')) { Schema::create('simulation_task_events', function (Blueprint $t) { $t->id(); $t->unsignedBigInteger('run_id'); $t->string('member_id', 64)->default(''); $t->unsignedInteger('step_index'); $t->unsignedInteger('seq'); $t->string('action', 16); $t->string('status', 16)->comment('ok failed'); $t->string('ref_type', 32)->nullable(); $t->string('ref_id', 64)->nullable(); $t->decimal('amount', 22, 2)->nullable(); $t->string('message', 500)->default(''); $t->dateTime('created_at'); $t->unique(['run_id', 'step_index', 'member_id', 'seq'], 'simulation_task_events_step_unique'); }); } if (Schema::hasTable('config')) { $hasRemark = Schema::hasColumn('config', 'remark'); $hasGroup = Schema::hasColumn('config', 'group_id'); $hasTimes = Schema::hasColumn('config', 'created_at'); foreach (self::SETTINGS as $field => [$value, $remark]) { if (DB::table('config')->where('field', $field)->exists()) continue; $row = ['field' => $field, 'val' => $value]; if ($hasRemark) $row['remark'] = $remark; if ($hasGroup) $row['group_id'] = 1; if ($hasTimes) $row += ['created_at' => now(), 'updated_at' => now()]; DB::table('config')->insert($row); } } } public function down(): void { Schema::dropIfExists('simulation_task_events'); Schema::dropIfExists('simulation_task_runs'); Schema::dropIfExists('simulation_tasks'); if (Schema::hasTable('config')) DB::table('config')->whereIn('field', array_keys(self::SETTINGS))->delete(); } };