master
cody 3 months ago
parent 6a519aa145
commit b21c5d355b

@ -0,0 +1,233 @@
<?php
namespace App\Console\Commands;
use App\Models\Course;
use App\Models\CourseType;
use App\Models\CourseTypeDataOverviewConfig;
use App\Models\HistoryCourse;
use App\Models\CourseSign;
use Illuminate\Console\Command;
/**
* 对比 home-v2 的 yearConfigs 与 courses-home 的 courseTypesSum
* 在 2024-01-01 2027-01-01 时间段内,找出课程数量、去重培养人数差异的原因
*/
class DiffHomeV2CoursesHome extends Command
{
protected $signature = 'diff:home-v2-courses-home {--start=2024-01-01} {--end=2027-01-01}';
protected $description = '对比 home-v2 yearConfigs 与 courses-home courseTypesSum 在指定时间段内的期数、去重培养人数差异';
public function handle()
{
$start = $this->option('start');
$end = $this->option('end');
$this->info("=== 对比时间段: {$start} {$end} ===\n");
// 1. 找到覆盖该时间段的 yearConfig或使用该时间段模拟
$config = CourseTypeDataOverviewConfig::where('status', true)
->where('start_date', '<=', $start)
->where(function ($q) use ($end) {
$q->where('end_date', '>=', $end)->orWhereNull('end_date');
})
->orderBy('sort')
->first();
if (!$config) {
$config = CourseTypeDataOverviewConfig::where('status', true)
->whereBetween('start_date', [$start, $end])
->orWhereBetween('end_date', [$start, $end])
->orderBy('sort')
->first();
}
$configStart = $config ? $config->start_date : $start;
$configEnd = $config && $config->end_date ? $config->end_date : $end;
$this->info("yearConfig: " . ($config ? "id={$config->id} [{$configStart} {$configEnd}]" : "无覆盖配置,使用 {$configStart} {$configEnd}"));
// 2. 按 home-v2 逻辑统计(仅用 2024-01-012027-01-01 做筛选,与 config 自身范围不一致时以 2024-2027 为准则需单独算)
// 为与 courses-home 可比,我们在这里用 startend 作为统一日期范围
$homeV2 = $this->computeHomeV2Style($start, $end);
$this->info("\n【home-v2 逻辑】期数: {$homeV2['course_periods_total']}, 去重培养人数: {$homeV2['course_signs_unique_total']}");
// 3. 按 courses-home 逻辑统计
$coursesHome = $this->computeCoursesHomeStyle($start, $end);
$this->info("【courses-home 逻辑】课程数量(行数): {$coursesHome['course_count']}, 去重培养人数: {$coursesHome['course_signs_unique_total']}");
// 4. 差异
$diffCount = $coursesHome['course_count'] - $homeV2['course_periods_total'];
$this->info("\n--- 差异: 课程数量 courses-home 多 " . $diffCount . " ---");
// 5. 定位多出来的课程来源
$this->findExtraSources($start, $end, $homeV2, $coursesHome);
return 0;
}
/**
* home-v2 风格is_chart=1 且 is_history=0 的 CourseType + 按 name like 的 HistoryCourse + 其他
*/
protected function computeHomeV2Style(string $start, string $end): array
{
$allCourseTypes = CourseType::where('is_chart', 1)->where('is_history', 0)->orderBy('sort')->get();
$coursePeriodsTotal = 0;
$courseSignsUniqueTotal = 0;
$dateFilter = function ($q) use ($start, $end) {
$q->whereBetween('start_date', [$start, $end])->orWhereBetween('end_date', [$start, $end]);
};
$historyDateFilter = function ($q) use ($start, $end) {
$q->whereBetween('start_time', [$start, $end])->orWhereBetween('end_time', [$start, $end]);
};
foreach ($allCourseTypes as $ct) {
$historyCourse = HistoryCourse::whereHas('typeDetail', fn($q) => $q->where('name', 'like', '%' . $ct->name . '%'))
->where($historyDateFilter)->get();
$courses = Course::where('type', $ct->id)->where('is_chart', 1)->where($dateFilter)->get();
$coursePeriodsTotal += $historyCourse->count() + $courses->count();
$courseSignsUniqueTotal += $historyCourse->sum('course_type_signs_pass_unique')
+ (int)CourseSign::courseSignsTotalByUnique($start, $end, 1, $courses->pluck('id'), false, false);
}
$other = CourseType::getOtherStatistics($start, $end);
$coursePeriodsTotal += $other->course_periods_total;
$courseSignsUniqueTotal += $other->course_signs_total;
return ['course_periods_total' => $coursePeriodsTotal, 'course_signs_unique_total' => $courseSignsUniqueTotal];
}
/**
* courses-home 风格:全部 CourseType含 is_history=1的 Course(is_chart=1) + is_history=1 的 HistoryCourse(calendar.is_count_people=1)
*/
protected function computeCoursesHomeStyle(string $start, string $end): array
{
$course_type_id = CourseType::pluck('id')->toArray();
$courseTypesSum = [];
$allCourseIdsForUnique = [];
$dateFilter = function ($q) use ($start, $end) {
if ($start && $end) {
$q->whereBetween('start_date', [$start, $end])->orWhereBetween('end_date', [$start, $end]);
}
};
// 第一循环:所有 CourseType含 is_history=1
$courseTypes = CourseType::whereIn('id', $course_type_id)->get();
foreach ($courseTypes as $ct) {
$courses2 = Course::where('type', $ct->id)->where($dateFilter)->where('is_chart', 1)->orderBy('start_date')->get();
foreach ($courses2 as $c) {
$courseTypesSum[] = ['source' => 'Course', 'course_type_id' => $ct->id, 'course_type_name' => $ct->name, 'is_history' => $ct->is_history ?? 0, 'course_id' => $c->id, 'course_name' => $c->name];
$allCourseIdsForUnique[] = $c->id;
}
}
// 第二循环is_history=1 的 HistoryCourse与 home-v2 一致:仅 typeDetail.name 能匹配某个 is_chart=1 且 is_history=0 的 name
$chartHistoryTypeNames = CourseType::where('is_chart', 1)->where('is_history', 0)->pluck('name')->toArray();
$courseTypesHistory = CourseType::where('is_history', 1)->whereIn('id', $course_type_id)->get();
$historySignsTotal = 0;
foreach ($courseTypesHistory as $hc) {
$historyQ = HistoryCourse::whereHas('calendar', fn($q) => $q->where('is_count_people', 1))
->where(fn($q) => $q->whereBetween('start_time', [$start, $end])->orWhereBetween('end_time', [$start, $end]))
->where('type', $hc->id);
if (!empty($chartHistoryTypeNames)) {
$historyQ->whereHas('typeDetail', function ($query) use ($chartHistoryTypeNames) {
$query->where(function ($q2) use ($chartHistoryTypeNames) {
foreach ($chartHistoryTypeNames as $n) {
$q2->orWhere('name', 'like', '%' . $n . '%');
}
});
});
} else {
$historyQ->whereHas('typeDetail', fn($query) => $query->whereRaw('1=0'));
}
$courses3 = $historyQ->get();
foreach ($courses3 as $c) {
$courseTypesSum[] = ['source' => 'HistoryCourse', 'course_type_id' => $hc->id, 'course_type_name' => $hc->name, 'is_history' => 1, 'history_course_id' => $c->id, 'course_name' => $c->course_name];
$historySignsTotal += (int)($c->course_type_signs_pass_unique ?? 0);
}
}
$courseSignsUniqueTotal = (int)CourseSign::courseSignsTotalByUnique($start, $end, 1, $allCourseIdsForUnique ?: [], false, false) + $historySignsTotal;
return [
'course_count' => count($courseTypesSum),
'course_signs_unique_total' => $courseSignsUniqueTotal,
'rows' => $courseTypesSum,
];
}
/**
* 找出 courses-home 多出来的课程来源
*/
protected function findExtraSources(string $start, string $end, array $homeV2, array $coursesHome): void
{
$allCourseTypes = CourseType::where('is_chart', 1)->where('is_history', 0)->pluck('id')->toArray();
$historyDateFilter = function ($q) use ($start, $end) {
$q->whereBetween('start_time', [$start, $end])->orWhereBetween('end_time', [$start, $end]);
};
$dateFilter = function ($q) use ($start, $end) {
$q->whereBetween('start_date', [$start, $end])->orWhereBetween('end_date', [$start, $end]);
};
// home-v2 会计入的1) Course: type in is_history=0, is_chart=1, 日期 2) HistoryCourse: typeDetail.name like (is_history=0 的 name), 日期 3) 其他
$homeV2CourseIds = [];
foreach (CourseType::where('is_chart', 1)->where('is_history', 0)->get() as $ct) {
$ids = Course::where('type', $ct->id)->where('is_chart', 1)->where($dateFilter)->pluck('id')->toArray();
$homeV2CourseIds = array_merge($homeV2CourseIds, $ids);
}
$otherCourseIds = Course::whereIn('type', CourseType::where('is_chart', 0)->where('is_history', 0)->pluck('id'))
->where('is_chart', 1)->where($dateFilter)->pluck('id')->toArray();
$homeV2CourseIds = array_unique(array_merge($homeV2CourseIds, $otherCourseIds));
$homeV2HistoryIds = [];
foreach (CourseType::where('is_chart', 1)->where('is_history', 0)->get() as $ct) {
$ids = HistoryCourse::whereHas('typeDetail', fn($q) => $q->where('name', 'like', '%' . $ct->name . '%'))
->where($historyDateFilter)->pluck('id')->toArray();
$homeV2HistoryIds = array_merge($homeV2HistoryIds, $ids);
}
$otherHistory = HistoryCourse::whereIn('type', CourseType::where('is_chart', 0)->where('is_history', 0)->pluck('id'))
->where($historyDateFilter)->pluck('id')->toArray();
$homeV2HistoryIds = array_unique(array_merge($homeV2HistoryIds, $otherHistory));
// courses-home 多出的:在 rows 里但不在 home-v2 的
$extras = [];
foreach ($coursesHome['rows'] as $r) {
if ($r['source'] === 'Course') {
if (!in_array($r['course_id'], $homeV2CourseIds)) {
$extras[] = $r;
}
} else {
if (!in_array($r['history_course_id'], $homeV2HistoryIds)) {
$extras[] = $r;
}
}
}
$this->info("\n【多出的课程/期courses-home 有、home-v2 无)】共 " . count($extras) . " 条:");
foreach ($extras as $e) {
$this->line(sprintf(
" - %s | type_id=%s is_history=%s | %s | %s",
$e['source'],
$e['course_type_id'],
$e['is_history'],
$e['course_name'] ?? '-',
isset($e['course_id']) ? "course_id={$e['course_id']}" : "history_id={$e['history_course_id']}"
));
}
// 单独列出type 属于 is_history=1 的 Coursehome-v2 不统计此类)
$isHistory1TypeIds = CourseType::where('is_history', 1)->pluck('id')->toArray();
$courseFromHistory1Type = Course::whereIn('type', $isHistory1TypeIds)
->where('is_chart', 1)
->where($dateFilter)
->get();
if ($courseFromHistory1Type->isNotEmpty()) {
$this->info("\n【属于 is_history=1 课程类型的 Coursehome-v2 不统计)】共 " . $courseFromHistory1Type->count() . " 条:");
foreach ($courseFromHistory1Type as $c) {
$ct = CourseType::find($c->type);
$this->line(" - course_id={$c->id} type={$c->type} ({$ct->name}) | {$c->name} | {$c->start_date}{$c->end_date}");
}
}
}
}

@ -377,11 +377,22 @@ class OtherController extends CommonController
];
}
}
// 附加历史课程数据
// 附加历史课程数据(与 home-v2 yearConfigs 口径一致:仅统计 typeDetail.name 能匹配某个 is_chart=1 且 is_history=0 的 CourseType 的 HistoryCourse
$chartHistoryTypeNames = CourseType::where('is_chart', 1)->where('is_history', 0)->pluck('name')->toArray();
$courseTypesHistory = CourseType::where('is_history', 1)->whereIn('id', $course_type_id)->get();
foreach ($courseTypesHistory as $historyCourse) {
$courses3 = HistoryCourse::whereHas('calendar', function ($query) {
$query->where('is_count_people', 1);
})->whereHas('typeDetail', function ($query) use ($chartHistoryTypeNames) {
if (empty($chartHistoryTypeNames)) {
$query->whereRaw('1=0');
return;
}
$query->where(function ($q) use ($chartHistoryTypeNames) {
foreach ($chartHistoryTypeNames as $n) {
$q->orWhere('name', 'like', '%' . $n . '%');
}
});
})->where(function ($query) use ($start_date, $end_date) {
// 开始结束日期的筛选。or查询
$query->whereBetween('start_time', [$start_date, $end_date])
@ -863,11 +874,22 @@ class OtherController extends CommonController
];
}
}
// 附加历史课程数据
// 附加历史课程数据(与 home-v2 yearConfigs 口径一致:仅统计 typeDetail.name 能匹配某个 is_chart=1 且 is_history=0 的 CourseType 的 HistoryCourse
$chartHistoryTypeNames = CourseType::where('is_chart', 1)->where('is_history', 0)->pluck('name')->toArray();
$courseTypesHistory = CourseType::where('is_history', 1)->whereIn('id', $course_type_id)->get();
foreach ($courseTypesHistory as $historyCourse) {
$courses3 = HistoryCourse::whereHas('calendar', function ($query) {
$query->where('is_count_people', 1);
})->whereHas('typeDetail', function ($query) use ($chartHistoryTypeNames) {
if (empty($chartHistoryTypeNames)) {
$query->whereRaw('1=0');
return;
}
$query->where(function ($q) use ($chartHistoryTypeNames) {
foreach ($chartHistoryTypeNames as $n) {
$q->orWhere('name', 'like', '%' . $n . '%');
}
});
})->where(function ($query) use ($start_date, $end_date) {
// 开始结束日期的筛选。or查询
$query->whereBetween('start_time', [$start_date, $end_date])

Loading…
Cancel
Save