Поделиться через


Сбор отзывов пользователей в библиотеке пользовательского интерфейса ACS

Введение

Это комплексное руководство поможет разработчикам интегрировать расширенную поддержку в библиотеку пользовательского интерфейса ACS, используя службы Azure для обработки серверной части. Руководство делится на клиентские и серверные шаги для ясности и простоты реализации.

Необходимые компоненты

  • Подписка Azure. Нужна активная подписка Azure. Если у вас нет учетной записи, вы можете создать бесплатную учетную запись в бесплатной учетной записи Azure.
  • ресурс Службы коммуникации Azure: Ресурс ACS требуется для использования функций звонков и чатов. Его можно создать на портале Azure.
  • Настройка среды разработки. Убедитесь, что среда разработки настроена для одной или нескольких целевых платформ — Android, iOS или Интернета.
  • учетная запись служба хранилища Azure: Для безопасного хранения отзывов пользователей и связанных данных требуется учетная запись служба хранилища Azure.
  • Node.js и Express.js. Базовые знания о Node.js и Express.js полезны для настройки серверного приложения для получения и обработки запросов на поддержку.
  • Знание API RESTful: общие сведения о создании и использовании API RESTful в развертывании и настройке клиента и сервера.
  • Навыки разработки клиентов: знание разработки приложений Android или iOS.

Наличие этих предварительных требований обеспечивает плавное начало интеграции комплексной системы отзывов пользователей с помощью Службы коммуникации Azure и других ресурсов Azure.

Что вы узнаете

В этом руководстве вы получите исчерпывающую информацию об интеграции механизмов обратной связи пользователей в приложениях Службы коммуникации Azure (ACS). Основное внимание уделяется повышению поддержки клиентов через библиотеку пользовательского интерфейса ACS, используя серверные службы Azure для обработки. Следуя этому руководству, разработчики учатся:

  • Реализация отслеживания отзывов на стороне клиента: узнайте, как записывать отзывы пользователей, журналы ошибок и поддерживать запросы непосредственно из приложений Android и iOS с помощью библиотеки пользовательского интерфейса ACS.
  • Настройка серверного приложения. Пошаговые инструкции по настройке приложения Node.js с помощью Express.js для получения, обработки и хранения запросов на поддержку в Хранилище BLOB-объектов Azure. Этот сервер включает обработку многопартийных и форм-данных для отправки файлов и безопасного управления данными пользователей.
  • Создание запросов в службу поддержки. Узнайте, как создавать уникальные номера запросов в службу поддержки, хранить отзывы пользователей вместе с соответствующими данными приложения.
  • Используйте Хранилище BLOB-объектов Azure. Узнайте, как использовать Хранилище BLOB-объектов Azure для хранения данных запроса обратной связи и поддержки, обеспечения безопасного и структурированного управления данными, поддерживающего эффективное извлечение и анализ.
  • Повышение надежности приложений и удовлетворенности пользователей. Разработчики могут быстро устранять и устранять проблемы пользователей, реализуя стратегии, описанные в этом руководстве.

Настройка на стороне сервера

Настройка приложения Node.js для обработки запросов на поддержку

Цель раздела. Цель — создать приложение Node.js с помощью Express.js, которая служит серверной частью для получения запросов на поддержку от пользователей. Эти запросы могут включать текстовые отзывы, журналы ошибок, снимки экрана и другие важные сведения, которые могут помочь в диагностике и устранении проблем с пользователем. Приложение хранит эти данные в Хранилище BLOB-объектов Azure для упорядоченного и безопасного доступа.

Платформа и инструменты

  • Express.js: платформа Node.js для создания веб-приложений и API. Он служит основой для настройки сервера и обработки запросов.
  • Formidable: библиотека для синтаксического анализа данных формы, особенно предназначенная для обработки многопартийных или форм-данных, которая часто используется для отправки файлов.
  • Хранилище BLOB-объектов Azure: Служба Microsoft Azure для хранения больших объемов неструктурированных данных.

Шаг 1. Настройка среды

Перед началом работы убедитесь, что среда разработки готова к установке Node.js. Для хранения отправленных данных также требуется доступ к учетной записи служба хранилища Azure.

  1. Установите Node.js. Убедитесь, что Node.js установлен в системе. Его можно скачать из Node.js.

  2. Создайте учетную запись Хранилище BLOB-объектов Azure. Если вы еще не сделали этого, создайте учетную запись служба хранилища Azure через портал Azure. Эта учетная запись используется для хранения данных запроса на поддержку.

  3. Сбор необходимых учетных данных. Убедитесь, что у вас есть строка подключения для учетной записи Хранилище BLOB-объектов Azure.

Шаг 2. Настройка приложения

  1. Инициализация нового проекта Node.js:

    • Создайте каталог для проекта и инициализируйте его с npm init помощью создания package.json файла.

    • Установите Express.js, Formidable, пакет SDK служба хранилища Azure BLOB-объектов и другие необходимые библиотеки с помощью npm.

      npm install express formidable @azure/storage-blob uuid
      
  2. Реализация сервера:

    • Используйте Express.js для настройки базового веб-сервера, который прослушивает запросы POST на определенной конечной точке.
    • Используйте Formidable для синтаксического анализа входящих данных формы, обработки содержимого с несколькими частями или данными формы.
    • Создайте уникальный номер билета для каждого запроса на поддержку, который можно использовать для упорядочивания данных в Хранилище BLOB-объектов Azure и предоставления ссылки для пользователей.
    • Храните структурированные данные, такие как сообщения пользователя и метаданные файла журнала, в JSON-файле в служба хранилища BLOB-объекта. Храните фактические файлы журнала и все снимки экрана или вложения в отдельных больших двоичных объектах в одном каталоге билета.
    • Предоставьте конечную точку для получения сведений о поддержке, которая включает получение и отображение данных из Хранилище BLOB-объектов Azure.
  3. Вопросы безопасности:

    • Убедитесь, что приложение проверяет входящие данные для защиты от вредоносных полезных данных.
    • Используйте переменные среды для безопасного хранения конфиденциальных данных, таких как служба хранилища Azure строка подключения.

Шаг 3. Запуск и тестирование приложения

  1. Переменные среды:

    • Настройте переменные среды для Хранилище BLOB-объектов Azure строка подключения и любой другой конфиденциальной информации. Например, можно использовать .env файл (и dotenv пакет npm для загрузки этих переменных).
  2. Запуск сервера:

    • Запустите приложение Node.js, выполнив node <filename>.jsкоманду , где <filename> находится имя основного файла сервера.
    • Проверьте сервер с помощью подходящего средства для веб-разработки.

Код сервера:

Приведенная ниже рабочая реализация для начала. Этот код является базовой реализацией, адаптированной для демонстрации создания билетов из примеров приложений пользовательского интерфейса ACS.

const express = require('express');
const formidable = require('formidable');
const fs = require('fs').promises
const { BlobServiceClient } = require('@azure/storage-blob');
const { v4: uuidv4 } = require('uuid');
const app = express();
const connectionString = process.env.SupportTicketStorageConnectionString
const port = process.env.PORT || 3000;
const portPostfix = (!process.env.PORT || port === 3000 || port === 80 || port === 443) ? '' : `:${port}`;

app.use(express.json());

app.all('/receiveEvent', async (req, res) => {
    try {
        const form = new formidable.IncomingForm();
        form.parse(req, async (err, fields, files) => {
            if (err) {
                return res.status(500).send("Error processing request: " + err.message);
            }
            // Generate a unique ticket number
            const ticketNumber = uuidv4();
            const blobServiceClient = BlobServiceClient.fromConnectionString(connectionString);
            const containerClient = blobServiceClient.getContainerClient('supporttickets');
            await containerClient.createIfNotExists();

            // Prepare and upload support data
            const supportData = {
                userMessage: fields.user_message,
                uiVersion: fields.ui_version,
                sdkVersion: fields.sdk_version,
                callHistory: fields.call_history
            };
            const supportDataBlobClient = containerClient.getBlockBlobClient(`${ticketNumber}/supportdata.json`);
            await supportDataBlobClient.upload(JSON.stringify(supportData), Buffer.byteLength(JSON.stringify(supportData)));

            // Upload log files
            Object.values(files).forEach(async (fileOrFiles) => {
                // Check if the fileOrFiles is an array (multiple files) or a single file object
                const fileList = Array.isArray(fileOrFiles) ? fileOrFiles : [fileOrFiles];
            
                for (let file of fileList) {
                    const blobClient = containerClient.getBlockBlobClient(`${ticketNumber}/logs/${file.originalFilename}`);
                    
                    // Read the file content into a buffer
                    const fileContent = await fs.readFile(file.filepath);
                    
                    // Now upload the buffer
                    await blobClient.uploadData(fileContent); // Upload the buffer instead of the file path
                }
            });
            // Return the ticket URL
            const endpointUrl = `${req.protocol}://${req.headers.host}${portPostfix}/ticketDetails?id=${ticketNumber}`;
            res.send(endpointUrl);
        });
    } catch (err) {
        res.status(500).send("Error processing request: " + err.message);
    }
});

// ticketDetails endpoint to serve details page
app.get('/ticketDetails', async (req, res) => {
    const ticketNumber = req.query.id;
    if (!ticketNumber) {
        return res.status(400).send("Ticket number is required");
    }

    // Fetch the support data JSON blob to display its contents
    try {
        const blobServiceClient = BlobServiceClient.fromConnectionString(connectionString);
        const containerClient = blobServiceClient.getContainerClient('supporttickets');
        const blobClient = containerClient.getBlobClient(`${ticketNumber}/supportdata.json`);
        const downloadBlockBlobResponse = await blobClient.download(0);
        const downloadedContent = (await streamToBuffer(downloadBlockBlobResponse.readableStreamBody)).toString();
        const supportData = JSON.parse(downloadedContent);

        // Generate links for log files
        let logFileLinks = `<h3>Log Files:</h3>`;
        const listBlobs = containerClient.listBlobsFlat({ prefix: `${ticketNumber}/logs/` });
        for await (const blob of listBlobs) {
            logFileLinks += `<a href="/getLogFile?id=${ticketNumber}&file=${encodeURIComponent(blob.name.split('/')[2])}">${blob.name.split('/')[2]}</a><br>`;
        }

        // Send a simple HTML page with support data and links to log files
        res.send(`
            <h1>Ticket Details</h1>
            <p><strong>User Message:</strong> ${supportData.userMessage}</p>
            <p><strong>UI Version:</strong> ${supportData.uiVersion}</p>
            <p><strong>SDK Version:</strong> ${supportData.sdkVersion}</p>
            <p><strong>Call History:</strong> </p> <pre>${supportData.callHistory}</pre>
            ${logFileLinks}
        `);
    } catch (err) {
        res.status(500).send("Error fetching ticket details: " + err.message);
    }
});

// getLogFile endpoint to allow downloading of log files
app.get('/getLogFile', async (req, res) => {
    const { id: ticketNumber, file } = req.query;
    if (!ticketNumber || !file) {
        return res.status(400).send("Ticket number and file name are required");
    }

    try {
        const blobServiceClient = BlobServiceClient.fromConnectionString(connectionString);
        const containerClient = blobServiceClient.getContainerClient('supporttickets');
        const blobClient = containerClient.getBlobClient(`${ticketNumber}/logs/${file}`);

        // Stream the blob to the response
        const downloadBlockBlobResponse = await blobClient.download(0);
        res.setHeader('Content-Type', 'application/octet-stream');
        res.setHeader('Content-Disposition', `attachment; filename=${file}`);
        downloadBlockBlobResponse.readableStreamBody.pipe(res);
    } catch (err) {
        res.status(500).send("Error downloading file: " + err.message);
    }
});

// Helper function to stream blob content to a buffer
async function streamToBuffer(stream) {
    const chunks = [];
    return new Promise((resolve, reject) => {
        stream.on('data', (chunk) => chunks.push(chunk));
        stream.on('end', () => resolve(Buffer.concat(chunks)));
        stream.on('error', reject);
    });
}


app.listen(port, () => {
    console.log(`Server running on port ${port}`);
});

Настройка на стороне клиента

В этом разделе рассматриваются настройки на стороне клиента и способы достижения следующих целей:

  1. Зарегистрируйтесь для зарегистрированных пользователем проблем.
  2. Сериализация данных.
  3. Перенаправите его на сервер.
  4. Получение ответа.
  5. Представить ответ пользователю.

Включение отзывов пользователей в библиотеке пользовательского интерфейса Службы коммуникации Azure (ACS) требует действия в части разработчиков. Используя onUserReportedIssueEventHandler интеграцию библиотеки, разработчики могут включить встроенную форму поддержки, позволяя пользователям сообщать о проблемах напрямую. В этом разделе описано, как настроить форму обратной связи на стороне клиента.

Реализация отслеживания отзывов на стороне клиента в Android

Включение формы поддержки

  1. Регистрация обработчика событий:

    • Чтобы активировать форму поддержки в приложении Android, зарегистрируйте onUserReportedIssueEventHandler ее в соответствующей точке жизненного цикла приложения. Эта регистрация включает не только форму, но и гарантирует, что она становится видимой и доступной для пользователей.
  2. Видимость форм и специальные возможности:

    • Наличие зарегистрированного onUserReportedIssueEventHandler непосредственно влияет на видимость формы поддержки. Без этого обработчика форма остается скрытой от пользовательского интерфейса, отрисовывая ее недоступной для отчетов о проблемах.

Запись и обработка событий поддержки

  1. Выбросы событий при отчетах о проблемах:

    • Когда пользователи сообщают о проблемах с помощью включенной формы поддержки, onUserReportedIssueEventHandler записи создают события. Эти события инкапсулируют все необходимые сведения, связанные с проблемой, сообщаемой пользователем, например описаниями, журналами ошибок и потенциально снимками экрана.
  2. Подготовка данных к отправке:

    • Когда пользователь сообщает о проблеме, следующий шаг включает подготовку данных о проблеме для отправки сервера. Эта подготовка включает структурирование захваченных сведений в формат, подходящий для передачи HTTP, в соответствии с ожиданиями сервера.

Отправка данных о проблеме на сервер

  1. Асинхронная передача данных:

    • Используйте асинхронные механизмы для передачи подготовленных данных в назначенную конечную точку сервера. Этот подход гарантирует, что приложение остается адаптивным, обеспечивая гладкое взаимодействие с пользователем во время отправки данных в фоновом режиме.
  2. Обработка ответов сервера:

    • При отправке данных важно обрабатывать ответы сервера. Эта обработка может включать анализ отзывов сервера, чтобы подтвердить успешную передачу данных и, возможно, извлечь ссылку на отправленную проблему (например, номер билета или URL-адрес), которую можно передать пользователю.

Предоставление отзывов пользователей и уведомлений

  1. Немедленная обратная связь пользователей:

    • Уведомляйте пользователей о состоянии отправки отчета о проблеме через пользовательский интерфейс приложения. Для успешной отправки рекомендуется указать ссылку на отправленную проблему, которая позволяет пользователям отслеживать ход выполнения отчета.
  2. Стратегия уведомлений для Android O и более позднюю версию:

    • Для устройств под управлением Android O (уровень API 26) и более новых версий убедитесь, что реализация канала уведомлений, относящегося к отправке отчетов. Эта настройка необходима для эффективной доставки уведомлений и является обязательным требованием для этих версий Android.

Следуя этим инструкциям, разработчики могут интегрировать надежный механизм обратной связи пользователей в свои приложения Android, используя для эффективного onUserReportedIssueEventHandler создания отчетов о проблемах и отслеживания. Этот процесс не только упрощает своевременное решение проблем с пользователем, но и значительно способствует повышению общего взаимодействия с пользователем и удовлетворенности приложением.

Пример кода Android

Фрагмент кода Kotlin демонстрирует процесс интеграции системы для обработки проблем, сообщаемых пользователем, в приложении Android с помощью Службы коммуникации Azure. Эта интеграция направлена на упрощение процесса поддержки путем прямого взаимодействия между пользователями и группами поддержки. Ниже приведен обзор описанных действий.

  1. Запись событий: система прослушивает проблемы, сообщаемые пользователем, через библиотеку пользовательского интерфейса ACS. Он используется onUserReportedIssueEventHandler для записи отзывов из пользовательского интерфейса приложения, включая ошибки и проблемы пользователя.

  2. Передача данных на сервер: когда сообщается о проблеме, система упаковает соответствующие данные, включая сообщения пользователей, журналы ошибок, версии и диагностические сведения. Затем эти данные отправляются в конечную точку сервера с помощью асинхронного запроса POST, чтобы процесс не препятствовал производительности приложения.

  3. Отзывы пользователей и уведомления. После отправки пользователи сразу же получают сведения о состоянии отчета с помощью уведомлений в приложении. Для успешной отправки уведомление содержит ссылку или ссылку на отправленный билет, позволяя пользователям отслеживать ход выполнения разрешения.

Эта настройка помогает не только быстро решать проблемы пользователей, но и значительно способствует повышению удовлетворенности пользователей и надежности приложений, предоставляя четкий канал для поддержки и обратной связи.

package com.azure.android.communication.ui.callingcompositedemoapp

import android.app.Application
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Intent
import android.net.Uri
import android.os.Build
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import com.azure.android.communication.ui.calling.CallCompositeEventHandler
import com.azure.android.communication.ui.calling.models.CallCompositeCallHistoryRecord
import com.azure.android.communication.ui.calling.models.CallCompositeUserReportedIssueEvent
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
import okhttp3.*
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.RequestBody.Companion.asRequestBody
import org.threeten.bp.format.DateTimeFormatter
import java.io.File
import java.io.IOException

/**
 * This class is responsible for handling user-reported issues within the Azure Communication Services Calling UI composite.
 * It implements the CallCompositeEventHandler interface to listen for CallCompositeUserReportedIssueEvents.
 * The class demonstrates how to send diagnostic information to a server endpoint for support purposes and
 * how to provide user feedback through notifications.
 */
class UserReportedIssueHandler : CallCompositeEventHandler<CallCompositeUserReportedIssueEvent> {
    // Flow to observe user reported issues.
    val userIssuesFlow = MutableStateFlow<CallCompositeUserReportedIssueEvent?>(null)

    // Reference to the application context, used to display notifications.
    lateinit var context: Application

    // Lazy initialization of the NotificationManagerCompat for managing notifications.
    private val notificationManager by lazy { NotificationManagerCompat.from(context) }

    /**
     * Handles the event when a user reports an issue.
     * - Creates a notification channel for Android O and above.
     * - Updates the userIssuesFlow with the new event data.
     * - Sends the event data including user message, app and SDK versions, call history, and log files to a server.
     */
    override fun handle(eventData: CallCompositeUserReportedIssueEvent?) {
        createNotificationChannel()
        userIssuesFlow.value = eventData
        eventData?.apply {
            sendToServer(
                userMessage,
                debugInfo.versions.azureCallingUILibrary,
                debugInfo.versions.azureCallingLibrary,
                debugInfo.callHistoryRecords,
                debugInfo.logFiles
            )
        }
    }

    /**
     * Prepares and sends a POST request to a server with the user-reported issue data.
     * Constructs a multipart request body containing the user message, app versions, call history, and log files.
     */
    private fun sendToServer(
        userMessage: String?,
        callingUIVersion: String?,
        callingSDKVersion: String?,
        callHistoryRecords: List<CallCompositeCallHistoryRecord>,
        logFiles: List<File>
    ) {
        if (SERVER_URL.isBlank()) { // Check if the server URL is configured.
            return
        }
        showProgressNotification()
        CoroutineScope(Dispatchers.IO).launch {
            val client = OkHttpClient()
            val requestBody = MultipartBody.Builder().setType(MultipartBody.FORM).apply {
                userMessage?.let { addFormDataPart("user_message", it) }
                callingUIVersion?.let { addFormDataPart("ui_version", it) }
                callingSDKVersion?.let { addFormDataPart("sdk_version", it) }
                addFormDataPart(
                    "call_history",
                    callHistoryRecords.map { "\n\n${it.callStartedOn.format(DateTimeFormatter.BASIC_ISO_DATE)}\n${it.callIds.joinToString("\n")}" }
                        .joinToString("\n"))
                logFiles.filter { it.length() > 0 }.forEach { file ->
                    val mediaType = "application/octet-stream".toMediaTypeOrNull()
                    addFormDataPart("log_files", file.name, file.asRequestBody(mediaType))
                }
            }.build()

            val request = Request.Builder()
                .url("$SERVER_URL/receiveEvent")
                .post(requestBody)
                .build()

            client.newCall(request).enqueue(object : Callback {
                override fun onFailure(call: Call, e: IOException) {
                    CoroutineScope(Dispatchers.Main).launch {
                        onTicketFailed(e.message ?: "Unknown error")
                    }
                }

                override fun onResponse(call: Call, response: Response) {
                    CoroutineScope(Dispatchers.Main).launch {
                        if (response.isSuccessful) {
                            onTicketCreated(response.body?.string() ?: "No URL provided")
                        } else {
                            onTicketFailed("Server error: ${response.message}")
                        }
                    }
                }
            })
        }
    }

    /**
     * Displays a notification indicating that the issue ticket has been created successfully.
     * The notification includes a URL to view the ticket status, provided by the server response.
     */
    private fun onTicketCreated(url: String) {
        showCompletionNotification(url)
    }

    /**
     * Displays a notification indicating that the submission of the issue ticket failed.
     * The notification includes the error reason.
     */
    private fun onTicketFailed(error: String) {
        showErrorNotification(error)
    }

    companion object {
        // The server URL to which the user-reported issues will be sent. Must be configured.
        private const val SERVER_URL = "${INSERT_YOUR_SERVER_ENDPOINT_HERE}"
    }

    /**
     * Creates a notification channel for Android O and above.
     * This is necessary to display notifications on these versions of Android.
     */
    private fun createNotificationChannel() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val name = "Report Submission"
            val descriptionText = "Notifications for report submission status"
            val importance = NotificationManager.IMPORTANCE_DEFAULT
            val channel = NotificationChannel("report_submission_channel", name, importance).apply {
                description = descriptionText
            }
            notificationManager.createNotificationChannel(channel)
        }
    }

    /**
     * Shows a notification indicating that the report submission is in progress.
     * This uses an indeterminate progress indicator to signify ongoing activity.
     */
    private fun showProgressNotification() {
        val notification = NotificationCompat.Builder(context, "report_submission_channel")
            .setContentTitle("Submitting Report")
            .setContentText("Your report is being submitted...")
            .setSmallIcon(R.drawable.image_monkey) // Replace with an appropriate icon for your app
            .setPriority(NotificationCompat.PRIORITY_DEFAULT)
            .setProgress(0, 0, true) // Indeterminate progress
            .build()

        notificationManager.notify(1, notification)
    }

    /**
     * Shows a notification indicating that the report has been successfully submitted.
     * The notification includes an action to view the report status via a provided URL.
     */
    private fun showCompletionNotification(url: String) {
        val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
        val pendingIntent = PendingIntent.getActivity(
            context,
            0,
            intent,
            PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
        )

        val notification = NotificationCompat.Builder(context, "report_submission_channel")
            .setContentTitle("Report Submitted")
            .setContentText("Tap to view")
            .setSmallIcon(R.drawable.image_monkey) // Replace with an appropriate icon for your app
            .setContentIntent(pendingIntent)
            .setAutoCancel(true) // Removes notification after tap
            .build()

        notificationManager.notify(1, notification)
    }

    /**
     * Shows a notification indicating an error in submitting the report.
     * The notification includes the reason for the submission failure.
     */
    private fun showErrorNotification(error: String) {
        val notification = NotificationCompat.Builder(context, "report_submission_channel")
            .setContentTitle("Submission Error")
            .setContentText("Error submitting report\nReason: $error")
            .setSmallIcon(R.drawable.image_monkey) // Replace with an appropriate icon for your app
            .setPriority(NotificationCompat.PRIORITY_DEFAULT)
            .build()

        notificationManager.notify(1, notification)
    }
}

После создания составного вызова обработчика для события его можно зарегистрировать.

            callComposite.addOnUserReportedEventHandler(userReportedIssueEventHandler)

Общие сведения о поддержке iOS

Чтобы интегрировать сбор отзывов пользователей в приложениях iOS с помощью библиотеки пользовательского интерфейса Службы коммуникации Azure (ACS), разработчикам необходимо выполнить структурированный подход. Этот процесс включает запись отзывов пользователей, включая журналы ошибок и сведения о пользователе. После завершения эти сведения передаются на сервер для обработки. В этом разделе описаны шаги, необходимые для выполнения этой задачи.

В этом примере мы используем библиотеку Alamofire для обработки отправки многокомпонентной формы, включая файлы журналов на сервер.

Реализация формы поддержки

  1. Регистрация обработчика событий. Начните с регистрации обработчика событий, который прослушивает проблемы, сообщаемые пользователем. Этот обработчик имеет решающее значение для записи отзывов непосредственно из интерфейса приложения iOS с помощью возможностей библиотеки пользовательского интерфейса ACS.

  2. Видимость и специальные возможности формы: убедитесь, что форма поддержки легко доступна и видна пользователям в приложении. Активация формы напрямую связана с реализацией обработчика событий, которая активирует его внешний вид в пользовательском интерфейсе, позволяя пользователям сообщать о проблемах.

Сбор и обработка запросов на поддержку

  1. Выброс событий для действия пользователя: когда пользователь сообщает о проблеме через форму поддержки, обработчик событий фиксирует это действие. Такие сведения, как описание проблемы, журналы ошибок и все идентификаторы вызовов, должны быть подготовлены для отправки на сервер.

  2. Структурирование данных для отправки: упорядочение захваченных данных в структурированном формате, подходящем для передачи. Подготовьте данные таким образом, чтобы он соответствовал ожидаемому формату конечной точки сервера, которая получает и обрабатывает запрос на поддержку.

Отправка данных на сервер

  1. Асинхронная отправка: используйте асинхронные сетевые вызовы для отправки структурированных данных на сервер. Этот подход гарантирует, что приложение остается адаптивным, обеспечивая простой интерфейс для пользователя во время передачи данных в фоновом режиме.

  2. Обработка ответов сервера: при отправке эффективно обрабатывает ответы сервера. Получение и анализ ответа, чтобы подтвердить успешное получение данных. Извлеките ссылку на запрос в службу поддержки из проанализированного ответа, который можно передать пользователю для последующего выполнения.

Отзывы и уведомления пользователям

  1. Немедленное подтверждение. Немедленно подтвердите отправку запроса на поддержку в приложении, предоставив пользователям подтверждение того, что их отчет был получен.

  2. Стратегия уведомлений. Реализуйте стратегию доставки уведомлений пользователям, особенно на устройствах под управлением версий iOS, поддерживающих определенные платформы уведомлений. Локальные уведомления можно использовать для информирования пользователей о состоянии отчета или предоставлении обновлений по мере устранения проблемы.

Пример кода iOS

Этот пример кода Swift описывает базовую реализацию для записи проблем, сообщаемых пользователем, и отправки их на сервер для обработки. В этом примере показано, как создать обработчик событий поддержки, включая отзывы пользователей и диагностические сведения о приложении и доставку на сервер. Код также включает в себя стратегии обработки ошибок и уведомлений пользователей, чтобы обеспечить гладкое взаимодействие с пользователем.

Следующий пример предназначен для установки перехватчика в обработчике событий.

Установка

let onUserReportedIssueHandler: (CallCompositeUserReportedIssue) -> Void = { issue in
    // Add a hook to this method, and provide it the Server endpoint + a result callback
    sendSupportEventToServer(server: self.issueUrl, event: issue) { success, result in
        if success {
            // Success: Convey the result link back to the user
        } else {
            // Error: Let the user know something has happened
        }
    }
}

Сетевой перехватчик

import Foundation
import UIKit
import Combine
import AzureCommunicationUICalling
import Alamofire

/// Sends a support event to a server with details from a `CallCompositeUserReportedIssue`.
/// - Parameters:
///   - server: The URL of the server where the event will be sent.
///   - event: The `CallCompositeUserReportedIssue` containing details about the issue reported by the user.
///   - callback: A closure that is called when the operation is complete.
///               It provides a `Bool` indicating success or failure, and a `String`
///               containing the server's response or an error message.
func sendSupportEventToServer(server: String,
                              event: CallCompositeUserReportedIssue,
                              callback: @escaping (Bool, String) -> Void) {
    // Construct the URL for the endpoint.
    let url = "\(server)/receiveEvent" // Ensure this is replaced with the actual server URL.

    // Extract debugging information from the event.
    let debugInfo = event.debugInfo

    // Prepare the data to be sent as key-value pairs.
    let parameters: [String: String] = [
        "user_message": event.userMessage, // User's message about the issue.
        "ui_version": debugInfo.versions.callingUIVersion, // Version of the calling UI.
        "call_history": debugInfo.callHistoryRecords
            .map { $0.callIds.joined(separator: ",") }
            .joined(separator: "\n") // Call history, formatted.
    ]

    // Define the headers for the HTTP request.
    let headers: HTTPHeaders = [
        .contentType("multipart/form-data")
    ]

    // Perform the multipart/form-data upload.
    AF.upload(multipartFormData: { multipartFormData in
        // Append each parameter as a part of the form data.
        for (key, value) in parameters {
            if let data = value.data(using: .utf8) {
                multipartFormData.append(data, withName: key)
            }
        }

        // Append log files.
        debugInfo.logFiles.forEach { fileURL in
            do {
                let fileData = try Data(contentsOf: fileURL)
                multipartFormData.append(fileData,
                                         withName: "log_files",
                                         fileName: fileURL.lastPathComponent,
                                         mimeType: "application/octet-stream")
            } catch {
                print("Error reading file data: \(error)")
            }
        }
    }, to: url, method: .post, headers: headers).response { response in
        // Handle the response from the server.
        switch response.result {
        case .success(let responseData):
            // Attempt to decode the response.
            if let data = responseData, let responseString = String(data: data, encoding: .utf8) {
                callback(true, responseString) // Success case.
            } else {
                callback(false, "Failed to decode response.") // Failed to decode.
            }
        case .failure(let error):
            // Handle any errors that occurred during the request.
            print("Error sending support event: \(error)")
            callback(false, "Error sending support event: \(error.localizedDescription)")
        }
    }
}

Этот код Swift демонстрирует процесс отправки пользовательских проблем из приложения iOS с помощью Службы коммуникации Azure. Она обрабатывает сбор отзывов пользователей, упаковку диагностических сведений и асинхронную отправку в конечную точку сервера. Кроме того, он предоставляет основу для реализации механизмов обратной связи, обеспечивая информирование пользователей о состоянии своих отчетов и повышение общей надежности приложений и удовлетворенности пользователей.

Заключение

Интеграция механизмов обратной связи пользователей в приложения с помощью Службы коммуникации Azure (ACS) имеет решающее значение для разработки адаптивных и ориентированных на пользователей приложений. В этом руководстве представлен четкий путь для настройки обработки на стороне сервера с помощью Node.js и отслеживания отзывов на стороне клиента для приложений Android и iOS. Благодаря такой интеграции разработчики могут повысить надежность приложений и удовлетворенность пользователей при использовании облачных служб Azure для эффективного управления данными.

В этом руководстве описаны практические шаги по сбору отзывов пользователей, журналов ошибок и запросов на поддержку непосредственно из приложений. Интеграция событий поддержки обеспечивает безопасный и упорядоченный способ обработки отзывов, позволяя разработчикам быстро устранять и устранять проблемы пользователей, что приводит к улучшению общего взаимодействия с пользователем.

Следуя инструкциям, описанным в этом руководстве, разработчики могут повысить скорость реагирования своих приложений и лучше соответствовать потребностям пользователей. Эти интеграции не только помогают лучше понять отзывы пользователей, но и используют облачные службы для обеспечения плавной и эффективной сбора отзывов и механизма обработки. В конечном счете интеграция механизмов обратной связи пользователей является важной для создания привлекательных и надежных приложений, которые определяют удовлетворенность пользователей.