You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

170 lines
4.5 KiB

<?php
namespace App\Support\News;
use App\Models\CrawlAddress;
use App\Models\DictItem;
use App\Models\DictType;
use App\Models\News;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\DB;
class UniversityNewsCategory
{
public const LABEL = '高校要闻';
public const VALUE = 'university_news';
/**
* 合并重复的「高校要闻」字典项,保留已有数据引用最多的那条(通常即爬虫沿用的原始分类)。
*/
public static function consolidateDuplicates(): ?int
{
$typeId = DictType::query()->where('code', 'news_category')->where('status', 1)->value('id');
if (! $typeId) {
return null;
}
$items = DictItem::query()
->where('dict_type_id', $typeId)
->where(function ($q) {
$q->where('value', self::VALUE)
->orWhere('label', self::LABEL);
})
->orderBy('id')
->get();
if ($items->isEmpty()) {
return null;
}
if ($items->count() === 1) {
$item = $items->first();
$item->fill(['label' => self::LABEL, 'value' => self::VALUE, 'status' => 1]);
$item->save();
return (int) $item->id;
}
$keeper = $items->sort(function (DictItem $a, DictItem $b) {
$countA = self::referenceCount((int) $a->id);
$countB = self::referenceCount((int) $b->id);
if ($countA !== $countB) {
return $countB <=> $countA;
}
return $a->id <=> $b->id;
})->first();
if (! $keeper) {
return null;
}
return DB::transaction(function () use ($items, $keeper) {
$keeperId = (int) $keeper->id;
foreach ($items as $item) {
if ((int) $item->id === $keeperId) {
continue;
}
News::query()
->where('category_dict_item_id', $item->id)
->update(['category_dict_item_id' => $keeperId]);
CrawlAddress::query()
->where('category_dict_item_id', $item->id)
->update(['category_dict_item_id' => $keeperId]);
$item->delete();
}
$keeper->fill([
'label' => self::LABEL,
'value' => self::VALUE,
'status' => 1,
]);
$keeper->save();
return $keeperId;
});
}
public static function canonicalId(): ?int
{
$typeId = DictType::query()->where('code', 'news_category')->where('status', 1)->value('id');
if (! $typeId) {
return null;
}
$item = DictItem::query()
->where('dict_type_id', $typeId)
->where('status', 1)
->where('value', self::VALUE)
->orderBy('id')
->first();
if ($item) {
return (int) $item->id;
}
$item = DictItem::query()
->where('dict_type_id', $typeId)
->where('status', 1)
->where('label', self::LABEL)
->orderBy('id')
->first();
return $item ? (int) $item->id : null;
}
/**
* @return list<int>
*/
public static function categoryIds(): array
{
$id = self::canonicalId();
return $id ? [$id] : [];
}
public static function applyUniversityNewsOnly(Builder $query): Builder
{
$id = self::canonicalId();
return $query->where('category_dict_item_id', $id ?? -1);
}
public static function applyExcludeUniversityNews(Builder $query): Builder
{
$id = self::canonicalId();
if (! $id) {
return $query;
}
return $query->where(function ($q) use ($id) {
$q->whereNull('category_dict_item_id')
->orWhere('category_dict_item_id', '!=', $id);
});
}
public static function isUniversityNewsCategory(?DictItem $item): bool
{
if (! $item) {
return false;
}
$canonicalId = self::canonicalId();
return $canonicalId
? (int) $item->id === $canonicalId
: ($item->value === self::VALUE || $item->label === self::LABEL);
}
protected static function referenceCount(int $dictItemId): int
{
return News::query()->where('category_dict_item_id', $dictItemId)->count()
+ CrawlAddress::query()->where('category_dict_item_id', $dictItemId)->count();
}
}