create([ "object_id" => $objectId, "object_type" => $objectType->value, "action" => $action->value, "project_id" => $projectId, "comment" => $comment, "extra_fields" => $extraFields ?: null, "created_by" => Auth::id(), "additional_id" => $additionalId, ]); if ($objectChanges) { History::query()->insert(array_map(fn($change) => [...$change, 'action_id' => $action->id], $objectChanges)); } ObjectActionCreate::dispatch($action); return $action; } public static function createByFile( int $objectId, string $objectType, ObjectAction $action, string $comment = null, array $extraFields = [], array $objectChanges = [], ): \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Builder { $actionObjectType = FileObjectType::from($objectType)->actionObjectType(); return self::create( $objectId, $actionObjectType, $action, $actionObjectType == ActionObjectType::PROJECT ? $objectId : null, $comment, $extraFields, $objectChanges ); } public static function createByApproval( Approval $approval, ObjectAction $action, string $comment = null, array $extraFields = [], array $objectChanges = [], ): \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Builder { $actionObjectType = ApprovalObjectType::from($approval->object_type)->actionObjectType(); return self::create( $approval->object_id, $actionObjectType, $action, $actionObjectType == ActionObjectType::PROJECT ? $approval->object_id : null, $comment, $extraFields, $objectChanges, $approval->id, ); } public static function createByProject( Project $project, ObjectAction $action, string $comment = null, array $extraFields = [], array $objectChanges = [], ): \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Builder { return self::create( $project->id, ActionObjectType::PROJECT, $action, $project->id, $comment, $extraFields, $objectChanges, ); } public static function createRequirement( Requirement $requirement, ObjectAction $action, string $comment = null, array $extraFields = [], array $objectChanges = [], ): \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Builder { return self::create( $requirement->id, ActionObjectType::REQUIREMENT, $action, null, $comment, $extraFields, $objectChanges, ); } public static function createByTask( Task $task, ObjectAction $action, string $comment = null, array $extraFields = [], array $objectChanges = [], ): \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Builder { return self::create( $task->id, ActionObjectType::TASK, $action, $task->project_id, $comment, $extraFields, $objectChanges, ); } public static function createByContainer( Container $container, ObjectAction $action, string $comment = null, array $extraFields = [], array $objectChanges = [], ): \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Builder { return self::create( $container->id, ActionObjectType::CONTAINER, $action, $container->library?->project_id, $comment, $extraFields, $objectChanges, ); } public static function createByAsset( Asset $asset, ObjectAction $action, string $comment = null, array $extraFields = [], array $objectChanges = [], ): \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Builder { return self::create( $asset->id, ActionObjectType::ASSET, $action, null, $comment, $extraFields, $objectChanges, ); } public static function latestDynamic(Project $project) { $actions = Action::query() ->where("project_id", $project->id) ->with(['createdBy']) ->orderByDesc("created_at") ->limit(20) ->get(); $objectNames = self::objectNamesGroupByType($actions); $items = []; foreach ($actions as $action) { $items[] = self::actionFormat($action->toArray(), $objectNames); } return $items; } public static function objectEmailActions(ActionObjectType $actionObjectType, string $objectId, int $lastActionId): array { $actions = Action::query() ->with(['createdBy']) ->where("object_type", $actionObjectType->value) ->where("object_id", $objectId) ->where("id", "<=", $lastActionId) ->orderByDesc("created_at") ->limit(5) ->get(); return self::actionsFormat($actions); } protected static function actionsFormat(Collection $actions): array { $objectNames = self::objectNamesGroupByType($actions); $items = []; foreach ($actions as $action) { $items[] = self::actionFormat($action->toArray(), $objectNames); } return $items; } public static function actionFormat(array $action, array $objectNames): array { $labelKey = sprintf("action-labels.label.%s", $action['action']); $objectLabelKey = sprintf("action-labels.object_type.%s", $action['object_type']); $cratedAt = Carbon::parse($action['created_at'])->timezone('Asia/Shanghai'); $imgService=new ImageUrlService(); //存在评论时才会执行获取图片路径 if(isset($action['comment'])){ $action['comment']=$imgService->getImageUrl($action['comment']); } return [ 'id' => $action['id'], 'time' => $cratedAt->toTimeString(), 'created_at' => $cratedAt->toDateTimeString(), 'created_by' => [ 'id' => $action['created_by']['id'], 'name' => $action['created_by']['name'], 'username' => $action['created_by']['username'], ], 'action_label' => app('translator')->has($labelKey) ? __($labelKey) : $action['action'], 'object_label' => app('translator')->has($objectLabelKey) ? __($objectLabelKey) : $action['object_type'], 'object_id' => $action['object_id'], 'object_type' => $action['object_type'], 'object_name' => data_get($objectNames, sprintf("%s.%d", $action['object_type'], $action['object_id'])), 'comment' => $action['comment'], ]; } public static function dynamic(Project $project, array $filter = []) { $actions = Action::query()->filter($filter) ->where("project_id", $project->id) ->with(['createdBy']) ->selectRaw("*, date_format(created_at, '%Y-%m-%d') as created_date") ->orderBy("created_at") ->get(); $objectNames = self::objectNamesGroupByType($actions); $items = $actions->groupBy("created_date")->toArray(); krsort($items); $data = []; foreach ($items as $day => $actionItems) { $dayItems = [ 'date' => $day, 'items' => [] ]; foreach ($actionItems as $actionItem) { $dayItems['items'][] = self::actionFormat($actionItem, $objectNames); } $data[] = $dayItems; } return $data; } public static function AssetDynamic(Asset $asset, array $filter = []) { $actions = Action::query()->filter($filter) ->where("object_type", "asset") ->where("object_id",$asset->id) ->with(['createdBy']) ->selectRaw("*, date_format(created_at, '%Y-%m-%d') as created_date") ->orderBy("created_at") ->get(); $objectNames = self::objectNamesGroupByType($actions); $items = $actions->groupBy("created_date")->toArray(); krsort($items); $data = []; foreach ($items as $day => $actionItems) { $dayItems = [ 'date' => $day, 'items' => [] ]; foreach ($actionItems as $actionItem) { $dayItems['items'][] = self::actionFormat($actionItem, $objectNames); } $data[] = $dayItems; } return $data; } /** * @param Collection $actions * @return array */ protected static function objectNamesGroupByType(Collection $actions): array { $groupActions = $actions->groupBy("object_type"); $objectNames = []; foreach ($groupActions as $group => $items) { $objectType = ActionObjectType::tryFrom($group); $actionObjectBuilder= $objectType?->modelBuilder(); if (! $actionObjectBuilder) { continue; } $objectNames[$group] = $actionObjectBuilder ->whereIn("id", $items->pluck("object_id")->unique()->toArray()) ->pluck($objectType->nameField(), "id"); } return $objectNames; } public static function actionWithHistory(ActionObjectType $actionObjectType, string $objectId): array { $actions = Action::query() ->with(['histories', 'createdBy']) ->where("object_type", $actionObjectType->value) ->where("object_id", $objectId) ->orderBy("created_at") ->get(); $objectNames = self::objectNamesGroupByType($actions); self::eagerLoadingHistoryConverters($actionObjectType, $actions); $items = []; foreach ($actions as $action) { $item = self::actionFormat($action->toArray(), $objectNames); $item['histories'] = self::formatHistories($actionObjectType, $action->histories); $items[] = $item; } return $items; } public static function eagerLoadingHistoryConverters(ActionObjectType $actionObjectType, Collection $actions) { $detector = $actionObjectType->detectorClassName(); $converters = []; foreach(call_user_func([$detector, "converters"]) as $key => $converter) { if (! $converter->isEagerLoading()) { continue; } $converters[$key] = $converter; } if (! $converters) { return; } $arrayFields = call_user_func([$detector, "arrayFields"]); $converterFields = array_keys($converters); $values = []; $pushValue = function (string $field, $fieldValue) use (&$values) { if (! $fieldValue) { return; } if (is_array($fieldValue)) { foreach ($fieldValue as $v) { if (in_array($v, $values[$field] ?? [])) { continue; } $values[$field][] = $v; } } else { if (! in_array($fieldValue, $values[$field] ?? [])) { $values[$field][] = $fieldValue; } } }; foreach ($actions as $action) { foreach ($action->histories as $history) { if (! in_array($history->field, $converterFields)) { continue; } $old = in_array($history->field, $arrayFields) ? json_decode($history->old, true) : $history->old; $new = in_array($history->field, $arrayFields) ? json_decode($history->new, true) : $history->new; $pushValue($history->field, $old); $pushValue($history->field, $new); } } foreach ($values as $key => $value) { call_user_func([$converters[$key], "eagerLoad"], $value); } } public static function formatHistories(ActionObjectType $actionObjectType, Collection $histories): array { $detector = $actionObjectType->detectorClassName(); $items = []; foreach ($histories as $history) { $labelKey = sprintf("fields.%s", $history->field); $item = [ 'field' => $history->field, 'field_label' => app('translator')->has($labelKey) ? __($labelKey) : $history->field, 'new' => self::coverFieldValue($detector, $history->field ?? '', $history->new ?? ''), 'old' => self::coverFieldValue($detector, $history->field ?? '', $history->old ?? ''), 'diff' => (string)$history->diff, ]; $items[] = $item; } return $items; } protected static function coverFieldValue(string $detector, string $field, string $value): mixed { if (! $detector) { return $value; } $converter = call_user_func([$detector, "converter"], $field); return $converter ? $converter->handle($value) : $value; } }