New_Micro_Learning/backend/app/Modules/Taxonomy/Application/ContentMappingService.php

85 خطوط
3.2 KiB
PHP

<?php
namespace App\Modules\Taxonomy\Application;
use App\Models\User;
use App\Modules\Courses\Domain\CourseVersion;
use App\Modules\Courses\Domain\Enums\CourseVersionStatus;
use App\Modules\Taxonomy\Domain\ContentTaxonomyMapping;
use App\Modules\Taxonomy\Domain\Enums\MappableType;
use App\Modules\Taxonomy\Domain\Enums\MappingConfirmationStatus;
use App\Modules\Taxonomy\Domain\Enums\MappingSource;
use App\Modules\Taxonomy\Domain\Enums\MappingType;
use App\Modules\Taxonomy\Domain\TaxonomyNode;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Validation\ValidationException;
final class ContentMappingService
{
public function create(
string $organizationId,
string $courseVersionId,
MappableType $mappableType,
string $mappableId,
string $taxonomyNodeId,
MappingType $mappingType,
?string $masteryLevel,
float $weight,
MappingSource $source,
?float $confidence,
User $actor,
): ContentTaxonomyMapping {
$version = CourseVersion::query()->where('organization_id', $organizationId)->findOrFail($courseVersionId);
if ($version->status === CourseVersionStatus::Published) {
$this->invalid('courseVersionId', 'Published course versions cannot receive new mappings.');
}
TaxonomyNode::query()->where('organization_id', $organizationId)->findOrFail($taxonomyNodeId);
$this->resolveTarget($organizationId, $version, $mappableType, $mappableId);
$confirmation = $source === MappingSource::AiSuggested
? MappingConfirmationStatus::Draft
: MappingConfirmationStatus::Confirmed;
return ContentTaxonomyMapping::query()->create([
'organization_id' => $organizationId,
'course_version_id' => $version->getKey(),
'mappable_type' => $mappableType,
'mappable_id' => $mappableId,
'taxonomy_node_id' => $taxonomyNodeId,
'mapping_type' => $mappingType,
'mastery_level' => $masteryLevel,
'weight' => $weight,
'source' => $source,
'confidence' => $confidence,
'confirmation_status' => $confirmation,
'confirmed_by' => $confirmation === MappingConfirmationStatus::Confirmed ? $actor->getKey() : null,
'confirmed_at' => $confirmation === MappingConfirmationStatus::Confirmed ? now() : null,
]);
}
private function resolveTarget(string $organizationId, CourseVersion $version, MappableType $type, string $id): Model
{
$modelClass = $type->modelClass();
$query = $modelClass::query()->where('organization_id', $organizationId);
if ($type !== MappableType::Course) {
$query->where('course_version_id', $version->getKey());
}
$target = $query->find($id);
if (! $target || ($type === MappableType::Course && $target->getKey() !== $version->getKey())) {
$this->invalid('mappableId', 'The selected content does not belong to this course version.');
}
return $target;
}
private function invalid(string $field, string $message): never
{
throw ValidationException::withMessages([$field => [$message]]);
}
}