Загрузка данных


CREATE OR REPLACE FUNCTION ourpension.f_process_snv_file_safe(
    p_file_id integer,
    p_username text,
    p_user_id integer
)
RETURNS jsonb
LANGUAGE plpgsql
AS $function$
DECLARE
    v_result jsonb;
    v_sqlstate text;
    v_message text;
    v_detail text;

    v_folder text;
    v_loaded_name text;
    v_loaded_bytes bytea;
    v_original_name text;
    v_candidate record;
    v_matches integer := 0;

    v_target_path text;
    v_log_sid text := uuid_generate_v4()::text;
BEGIN
    -- Сначала определяем исходный физический файл.
    -- При неоднозначности ничего не обрабатываем и не перемещаем.
    BEGIN
        SELECT sd.input_folder_name
        INTO STRICT v_folder
        FROM robot.service_description sd
        WHERE sd.name = 'SMEF FNS';

        IF v_folder IS NULL OR btrim(v_folder) = '' THEN
            RAISE EXCEPTION 'Не настроена входящая папка SMEF FNS';
        END IF;

        SELECT f.name, f.bytes
        INTO STRICT v_loaded_name, v_loaded_bytes
        FROM dev.query_datasource_file f
        JOIN dev.query_datasource d
            ON d.id = f.query_datasource_id
        WHERE f.id = p_file_id
          AND d.code = 'a33r8zRX';

        FOR v_candidate IN
            SELECT d.name
            FROM utils.get_dir_list(v_folder) d
            WHERE d.name = v_loaded_name
               OR robot.f_russian_transliteration_code(d.name)
                    = v_loaded_name
        LOOP
            IF utils.get_file_content(
                v_folder || '/' || v_candidate.name
            ) = v_loaded_bytes THEN
                v_original_name := v_candidate.name;
                v_matches := v_matches + 1;
            END IF;
        END LOOP;

        IF v_matches <> 1 THEN
            RAISE EXCEPTION
                'Для fileId=% найдено исходных файлов: %. Требуется один',
                p_file_id, v_matches;
        END IF;

    EXCEPTION WHEN OTHERS THEN
        GET STACKED DIAGNOSTICS
            v_sqlstate = RETURNED_SQLSTATE,
            v_message = MESSAGE_TEXT,
            v_detail = PG_EXCEPTION_DETAIL;

        v_result := jsonb_build_object(
            'success', false,
            'file_id', p_file_id,
            'stage', 'find_source_file',
            'moved', false,
            'sqlstate', v_sqlstate,
            'message', v_message,
            'detail', NULLIF(v_detail, '')
        );
    END;

    IF v_result IS NULL THEN
        -- Ошибка обработки откатывает изменения справок,
        -- но не прерывает экшен.
        BEGIN
            v_result := ourpension.f_process_snv_file(
                p_file_id,
                p_username,
                p_user_id
            );

        EXCEPTION WHEN OTHERS THEN
            GET STACKED DIAGNOSTICS
                v_sqlstate = RETURNED_SQLSTATE,
                v_message = MESSAGE_TEXT,
                v_detail = PG_EXCEPTION_DETAIL;

            v_result := jsonb_build_object(
                'success', false,
                'file_id', p_file_id,
                'registered_count', 0,
                'stage', 'processing',
                'sqlstate', v_sqlstate,
                'message', v_message,
                'detail', NULLIF(v_detail, '')
            );
        END;

        -- Перемещаем только однозначно найденный исходный файл.
        -- Ошибка перемещения не откатывает успешное создание справок.
        BEGIN
            v_target_path := utils.move_processed_file(
                v_folder,
                v_original_name,
                (v_result->>'success')::boolean
            );

            v_result := v_result || jsonb_build_object(
                'moved', true,
                'original_name', v_original_name,
                'target_path', v_target_path
            );

        EXCEPTION WHEN OTHERS THEN
            GET STACKED DIAGNOSTICS
                v_message = MESSAGE_TEXT,
                v_detail = PG_EXCEPTION_DETAIL;

            v_result := v_result || jsonb_build_object(
                'moved', false,
                'original_name', v_original_name,
                'move_error', v_message,
                'move_error_detail', NULLIF(v_detail, '')
            );
        END;
    END IF;

    -- Причина отказа остаётся в журнале без исключения
    -- и без уведомления об ошибке конструктора.
    BEGIN
        PERFORM robot.log_message(
            'работа'::varchar,
            ('СНВ: ' || v_result::text)::varchar,
            v_log_sid
        );

        v_result := v_result || jsonb_build_object(
            'log_session_id', v_log_sid
        );

    EXCEPTION WHEN OTHERS THEN
        v_result := v_result || jsonb_build_object(
            'log_error', SQLERRM
        );
    END;

    RETURN v_result;
END;
$function$;