升级节日福利年度调度与批次领取
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* 文件功能:节日福利发放批次表迁移
|
||||
*
|
||||
* 将节日福利模板的每一次实际发放拆成独立批次,
|
||||
* 用于支持按年复用、连续多天、多轮次领取与历史追踪。
|
||||
*/
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* 创建 holiday_event_runs 表。
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('holiday_event_runs', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('holiday_event_id')->comment('所属节日福利模板 ID');
|
||||
|
||||
// 批次快照:避免模板后续被修改后影响历史记录展示。
|
||||
$table->string('event_name', 100)->comment('活动名称快照');
|
||||
$table->text('event_description')->nullable()->comment('活动描述快照');
|
||||
$table->unsignedInteger('total_amount')->comment('总金币奖池快照');
|
||||
$table->unsignedInteger('max_claimants')->default(0)->comment('本批次最大领取人数(0=不限)');
|
||||
$table->enum('distribute_type', ['random', 'fixed'])->default('random')->comment('分配方式快照');
|
||||
$table->unsignedInteger('min_amount')->default(1)->comment('随机模式最低金额快照');
|
||||
$table->unsignedInteger('max_amount')->nullable()->comment('随机模式单人上限快照');
|
||||
$table->unsignedInteger('fixed_amount')->nullable()->comment('定额模式单人金额快照');
|
||||
$table->enum('target_type', ['all', 'vip', 'level'])->default('all')->comment('目标用户类型快照');
|
||||
$table->string('target_value', 50)->nullable()->comment('目标用户条件快照');
|
||||
$table->enum('repeat_type', ['once', 'daily', 'weekly', 'monthly', 'cron', 'yearly'])->default('once')->comment('模板重复方式快照');
|
||||
|
||||
// 批次状态与时间。
|
||||
$table->dateTime('scheduled_for')->comment('本批次原定触发时间');
|
||||
$table->dateTime('triggered_at')->comment('本批次实际触发时间');
|
||||
$table->dateTime('expires_at')->nullable()->comment('本批次领取截止时间');
|
||||
$table->enum('status', ['active', 'completed', 'expired', 'cancelled'])->default('active')->comment('批次状态');
|
||||
|
||||
// 统计信息。
|
||||
$table->unsignedInteger('audience_count')->default(0)->comment('本批次待领取总人数');
|
||||
$table->unsignedInteger('claimed_count')->default(0)->comment('本批次已领取人数');
|
||||
$table->unsignedInteger('claimed_amount')->default(0)->comment('本批次已领取金币总额');
|
||||
|
||||
$table->timestamps();
|
||||
|
||||
$table->foreign('holiday_event_id')->references('id')->on('holiday_events')->cascadeOnDelete();
|
||||
$table->index(['status', 'expires_at']);
|
||||
$table->index(['holiday_event_id', 'scheduled_for']);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 回滚 holiday_event_runs 表。
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('holiday_event_runs');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* 文件功能:节日福利模板年度调度字段迁移
|
||||
*
|
||||
* 为节日福利模板补充按年复用、连续多天与单日多轮发送所需的调度字段。
|
||||
*/
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* 升级 holiday_events 表结构。
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('holiday_events', function (Blueprint $table) {
|
||||
$table->tinyInteger('schedule_month')->unsigned()->nullable()->after('repeat_type')->comment('年度节日触发月份');
|
||||
$table->tinyInteger('schedule_day')->unsigned()->nullable()->after('schedule_month')->comment('年度节日触发日');
|
||||
$table->string('schedule_time', 5)->nullable()->after('schedule_day')->comment('年度节日首轮开始时间(HH:ii)');
|
||||
$table->unsignedSmallInteger('duration_days')->default(1)->after('schedule_time')->comment('连续发放天数');
|
||||
$table->unsignedSmallInteger('daily_occurrences')->default(1)->after('duration_days')->comment('每天触发次数');
|
||||
$table->unsignedSmallInteger('occurrence_interval_minutes')->nullable()->after('daily_occurrences')->comment('同一天多轮发送间隔(分钟)');
|
||||
});
|
||||
|
||||
Schema::table('holiday_events', function (Blueprint $table) {
|
||||
// Laravel 12 / MySQL 变更列时需要显式保留原有默认值。
|
||||
$table->enum('repeat_type', ['once', 'daily', 'weekly', 'monthly', 'cron', 'yearly'])
|
||||
->default('once')
|
||||
->change();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 回滚 holiday_events 的年度调度字段。
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('holiday_events', function (Blueprint $table) {
|
||||
$table->enum('repeat_type', ['once', 'daily', 'weekly', 'monthly', 'cron'])
|
||||
->default('once')
|
||||
->change();
|
||||
|
||||
$table->dropColumn([
|
||||
'schedule_month',
|
||||
'schedule_day',
|
||||
'schedule_time',
|
||||
'duration_days',
|
||||
'daily_occurrences',
|
||||
'occurrence_interval_minutes',
|
||||
]);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,119 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* 文件功能:节日福利领取记录批次化迁移
|
||||
*
|
||||
* 将领取记录与具体发放批次绑定,并为历史待领取数据补建 run 记录。
|
||||
*/
|
||||
|
||||
use Carbon\CarbonImmutable;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* 升级 holiday_claims 表并回填历史批次。
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('holiday_claims', function (Blueprint $table) {
|
||||
$table->unsignedBigInteger('run_id')->nullable()->after('event_id')->comment('关联发放批次 ID');
|
||||
$table->index('event_id');
|
||||
});
|
||||
|
||||
Schema::table('holiday_claims', function (Blueprint $table) {
|
||||
$table->dropUnique('uq_holiday_event_user');
|
||||
$table->dateTime('claimed_at')->nullable()->change();
|
||||
$table->foreign('run_id')->references('id')->on('holiday_event_runs')->cascadeOnDelete();
|
||||
$table->unique(['run_id', 'user_id'], 'uq_holiday_run_user');
|
||||
});
|
||||
|
||||
$now = CarbonImmutable::now();
|
||||
|
||||
$events = DB::table('holiday_events')
|
||||
->whereExists(function ($query) {
|
||||
$query->selectRaw('1')
|
||||
->from('holiday_claims')
|
||||
->whereColumn('holiday_claims.event_id', 'holiday_events.id');
|
||||
})
|
||||
->orderBy('id')
|
||||
->get();
|
||||
|
||||
foreach ($events as $event) {
|
||||
$audienceCount = (int) DB::table('holiday_claims')->where('event_id', $event->id)->count();
|
||||
$scheduledFor = $event->triggered_at ?? $event->send_at ?? $now->toDateTimeString();
|
||||
$expiresAt = $event->expires_at;
|
||||
|
||||
$runStatus = $expiresAt !== null && CarbonImmutable::parse($expiresAt)->isPast()
|
||||
? 'expired'
|
||||
: 'active';
|
||||
|
||||
$runId = DB::table('holiday_event_runs')->insertGetId([
|
||||
'holiday_event_id' => $event->id,
|
||||
'event_name' => $event->name,
|
||||
'event_description' => $event->description,
|
||||
'total_amount' => $event->total_amount,
|
||||
'max_claimants' => $event->max_claimants,
|
||||
'distribute_type' => $event->distribute_type,
|
||||
'min_amount' => $event->min_amount,
|
||||
'max_amount' => $event->max_amount,
|
||||
'fixed_amount' => $event->fixed_amount,
|
||||
'target_type' => $event->target_type,
|
||||
'target_value' => $event->target_value,
|
||||
'repeat_type' => $event->repeat_type,
|
||||
'scheduled_for' => $scheduledFor,
|
||||
'triggered_at' => $event->triggered_at ?? $scheduledFor,
|
||||
'expires_at' => $expiresAt,
|
||||
'status' => $runStatus,
|
||||
'audience_count' => $audienceCount,
|
||||
'claimed_count' => 0,
|
||||
'claimed_amount' => 0,
|
||||
'created_at' => $now,
|
||||
'updated_at' => $now,
|
||||
]);
|
||||
|
||||
// 旧结构下表里剩余记录全部表示“尚未领取”,统一迁移为未领取状态。
|
||||
DB::table('holiday_claims')
|
||||
->where('event_id', $event->id)
|
||||
->update([
|
||||
'run_id' => $runId,
|
||||
'claimed_at' => null,
|
||||
]);
|
||||
|
||||
if (in_array($event->repeat_type, ['daily', 'weekly', 'monthly'], true) && $event->send_at !== null) {
|
||||
$currentSendAt = CarbonImmutable::parse($event->send_at);
|
||||
$nextSendAt = match ($event->repeat_type) {
|
||||
'daily' => $currentSendAt->addDay(),
|
||||
'weekly' => $currentSendAt->addWeek(),
|
||||
'monthly' => $currentSendAt->addMonth(),
|
||||
default => $currentSendAt,
|
||||
};
|
||||
|
||||
DB::table('holiday_events')
|
||||
->where('id', $event->id)
|
||||
->update([
|
||||
'send_at' => $nextSendAt->toDateTimeString(),
|
||||
'status' => 'pending',
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 回滚 holiday_claims 的批次化关联。
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('holiday_claims', function (Blueprint $table) {
|
||||
$table->dropUnique('uq_holiday_run_user');
|
||||
$table->dropForeign(['run_id']);
|
||||
$table->dateTime('claimed_at')->nullable(false)->change();
|
||||
$table->dropColumn('run_id');
|
||||
$table->dropIndex(['event_id']);
|
||||
$table->unique(['event_id', 'user_id'], 'uq_holiday_event_user');
|
||||
});
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user