Merge remote-tracking branch 'online/master'

master
cody 2 months ago
commit b8e342c173

@ -230,40 +230,69 @@ class OtherController extends CommonController
->orderBy('sort', 'asc')
->get();
// 2. 查询对应的 CourseType
// 2. 查询对应的 CourseType仅一层归集overview_parent_id 指向根体系)
// - 根体系:通常是 is_history=0
// - 子体系:允许 is_history=1额外添加体系用于归集到根体系参与统计
$allCourseTypes = CourseType::where('is_chart', 1)
->orderBy('sort', 'asc')
->where('is_history', 0)
->get();
// 根体系:没有归集到其他体系的类型
$rootCourseTypes = $allCourseTypes
->whereNull('overview_parent_id')
->where('is_history', 0)
->values();
// 子体系:归集到某个根体系(仅一层)
$childrenByRootId = $allCourseTypes
->whereNotNull('overview_parent_id')
->groupBy('overview_parent_id');
// 3. 循环所有配置,对每个配置进行统计
foreach ($yearConfigs as $config) {
// 根据配置获取日期范围
$configStartDate = $config->start_date;
$configEndDate = $config->end_date ? $config->end_date : date('Y-m-d', strtotime('+10 year'));
// 复制 CourseType 集合(避免引用问题)
$courseTypes = $allCourseTypes->map(function ($item) {
// 复制 CourseType 集合(避免引用问题)
$courseTypes = $rootCourseTypes->map(function ($item) {
return clone $item;
});
})->values();
// 汇总本配置下全部 Course id用于 course_signs_unique_total 与 courses-home 口径一致courseSignsTotalByUnique
$configCourseIds = collect();
// 对每个 CourseType 进行统计
foreach ($courseTypes as $courseType) {
$includedTypeIds = collect([$courseType->id]);
$children = $childrenByRootId->get($courseType->id);
if ($children && $children->count() > 0) {
$includedTypeIds = $includedTypeIds->merge($children->pluck('id'));
}
// history_courses.type 是字符串,统一按字符串比对更稳定
$includedTypeIds = $includedTypeIds->unique()->values()->map(function ($id) {
return (string)$id;
});
// 历史课程数据(添加时间范围限制;仅统计 type 为 is_history=0 的is_history=1 的由下方「与 courses-home 口径一致」块统计,避免重复)
$historyCourse = HistoryCourse::whereHas('typeDetail', function ($query) use ($courseType) {
$query->where('name', 'like', '%' . $courseType->name . '%')->where('is_history', 0);
})->where(function ($query) use ($configStartDate, $configEndDate) {
$query->whereBetween('start_time', [$configStartDate, $configEndDate])
->orWhereBetween('end_time', [$configStartDate, $configEndDate]);
})->get();
// 口径升级history_courses.type 存的是课程体系ID字符串直接按 includedTypeIds 归集(避免名称匹配漂移)
$historyCourse = HistoryCourse::whereIn('type', $includedTypeIds)
->where(function ($query) use ($configStartDate, $configEndDate) {
// 采用“区间相交”口径;若 end_time 为空,则按 start_time 作为结束,避免漏算
$query->where('start_time', '<=', $configEndDate)
->where(function ($q) use ($configStartDate) {
$q->where('end_time', '>=', $configStartDate)
->orWhereNull('end_time');
});
})->get();
// 实际课程数据(添加时间范围限制)
$courses = Course::where('type', $courseType->id)->where('is_chart', 1)
$courses = Course::whereIn('type', $includedTypeIds)->where('is_chart', 1)
->where(function ($query) use ($configStartDate, $configEndDate) {
$query->whereBetween('start_date', [$configStartDate, $configEndDate])
->orWhereBetween('end_date', [$configStartDate, $configEndDate]);
// 采用“区间相交”口径;若 end_date 为空,则按 start_date 作为结束,避免漏算
$query->where('start_date', '<=', $configEndDate)
->where(function ($q) use ($configStartDate) {
$q->where('end_date', '>=', $configStartDate)
->orWhereNull('end_date');
});
})->get();
$configCourseIds = $configCourseIds->merge($courses->pluck('id'));
// 历史课程期数

@ -0,0 +1,30 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('course_types', function (Blueprint $table) {
$table->unsignedInteger('overview_parent_id')->nullable()->index()->comment('数据总览归集到的课程体系ID仅一层');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('course_types', function (Blueprint $table) {
$table->dropIndex(['overview_parent_id']);
$table->dropColumn('overview_parent_id');
});
}
};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -0,0 +1,83 @@
<?php
// Usage:
// php scripts/debug_homev2_overview.php "2022-01-01" "" 7 19
// args: startDate endDate rootTypeId childTypeId(optional)
use App\Models\Course;
use App\Models\CourseType;
use App\Models\CourseTypeDataOverviewConfig;
use App\Models\HistoryCourse;
require __DIR__ . '/../vendor/autoload.php';
$app = require_once __DIR__ . '/../bootstrap/app.php';
$kernel = $app->make(Illuminate\Contracts\Console\Kernel::class);
$kernel->bootstrap();
$start = $argv[1] ?? null;
$end = $argv[2] ?? null;
$rootId = $argv[3] ?? null;
$childId = $argv[4] ?? null;
if (!$start || !$rootId) {
fwrite(STDERR, "Missing args. Example:\n php scripts/debug_homev2_overview.php 2022-01-01 '' 7 19\n");
exit(2);
}
$end = $end ?: date('Y-m-d', strtotime('+10 year'));
$all = CourseType::where('is_chart', 1)->orderBy('sort', 'asc')->get();
$root = $all->firstWhere('id', (int)$rootId);
$children = $all->where('overview_parent_id', (int)$rootId)->values();
$includedTypeIds = collect([(string)$rootId])->merge($children->pluck('id')->map(fn($id) => (string)$id))->unique()->values();
echo "range: {$start} .. {$end}\n";
echo "root: {$rootId} " . ($root ? $root->name : '(not found)') . "\n";
echo "children: " . $children->pluck('id')->implode(',') . "\n";
echo "includedTypeIds(strings): " . $includedTypeIds->implode(',') . "\n\n";
$allCoursesForTypes = Course::whereIn('type', $includedTypeIds->map(fn($id) => (int)$id))
->where('is_chart', 1)
->get(['id', 'type', 'start_date', 'end_date']);
$courses = Course::whereIn('type', $includedTypeIds->map(fn($id) => (int)$id))
->where('is_chart', 1)
->where('start_date', '<=', $end)
->where('end_date', '>=', $start)
->get(['id', 'type', 'start_date', 'end_date']);
echo "courses count (filtered overlap): " . $courses->count() . "\n";
echo "courses count (no date filter): " . $allCoursesForTypes->count() . "\n";
if ($allCoursesForTypes->count() !== $courses->count()) {
$missing = $allCoursesForTypes->pluck('id')->diff($courses->pluck('id'))->values();
echo "courses missing due to date filter: " . $missing->implode(',') . "\n";
}
if ($childId) {
$coursesRoot = $courses->where('type', (int)$rootId)->count();
$coursesChild = $courses->where('type', (int)$childId)->count();
echo "courses root({$rootId}) count in range: {$coursesRoot}\n";
echo "courses child({$childId}) count in range: {$coursesChild}\n";
}
$allHistoryForTypes = HistoryCourse::whereIn('type', $includedTypeIds)->get(['id', 'type', 'start_time', 'end_time']);
$history = HistoryCourse::whereIn('type', $includedTypeIds)
->where('start_time', '<=', $end)
->where('end_time', '>=', $start)
->get(['id', 'type', 'start_time', 'end_time']);
echo "history_courses count (filtered overlap): " . $history->count() . "\n";
echo "history_courses count (no date filter): " . $allHistoryForTypes->count() . "\n";
if ($allHistoryForTypes->count() !== $history->count()) {
$missing = $allHistoryForTypes->pluck('id')->diff($history->pluck('id'))->values();
echo "history_courses missing due to date filter: " . $missing->implode(',') . "\n";
}
if ($childId) {
$historyRoot = $history->where('type', (string)$rootId)->count();
$historyChild = $history->where('type', (string)$childId)->count();
echo "history root({$rootId}) count in range: {$historyRoot}\n";
echo "history child({$childId}) count in range: {$historyChild}\n";
}
echo "\nDone.\n";

@ -0,0 +1,52 @@
<?php
use App\Models\Course;
use App\Models\CourseType;
require __DIR__ . '/../vendor/autoload.php';
$app = require_once __DIR__ . '/../bootstrap/app.php';
$kernel = $app->make(Illuminate\Contracts\Console\Kernel::class);
$kernel->bootstrap();
$start = '2022-01-01';
$end = date('Y-m-d', strtotime('+10 year'));
$otherTypes = CourseType::where('is_chart', 0)
->where('is_history', 0)
->whereNull('deleted_at')
->orderBy('sort', 'asc')
->get(['id', 'name', 'is_chart', 'is_history', 'sort']);
echo "range: {$start} .. {$end}\n";
echo "other course types count: " . $otherTypes->count() . "\n";
foreach ($otherTypes as $t) {
echo "type_id={$t->id}\tname={$t->name}\tsort={$t->sort}\n";
}
$typeIds = $otherTypes->pluck('id')->values();
echo "\n";
echo "type ids for 'other': " . $typeIds->implode(',') . "\n";
$courses = collect();
if ($typeIds->count() > 0) {
$courses = Course::with('typeDetail:id,name')
->whereIn('type', $typeIds)
->where('is_chart', 1)
->whereNull('deleted_at')
->where(function ($q) use ($start, $end) {
$q->where('start_date', '<=', $end)
->where('end_date', '>=', $start);
})
->orderBy('type', 'asc')
->orderBy('start_date', 'asc')
->get(['id', 'name', 'type', 'is_chart', 'start_date', 'end_date']);
}
echo "courses in 'other' (overlap range) count: " . $courses->count() . "\n\n";
foreach ($courses as $c) {
$typeName = $c->typeDetail ? $c->typeDetail->name : '';
echo "course_id={$c->id}\ttype={$c->type}\ttype_name={$typeName}\tname={$c->name}\t{$c->start_date}~{$c->end_date}\n";
}
echo "\nDone.\n";
Loading…
Cancel
Save