Compartir a través de


Recopilación de comentarios de usuario en la biblioteca de interfaz de usuario de ACS

Introducción

Esta guía integral está diseñada para ayudar a los desarrolladores a integrar la compatibilidad mejorada en la biblioteca de interfaz de usuario de ACS mediante servicios de Azure para el procesamiento de back-end. La guía se divide en pasos del lado cliente y del lado servidor para proporcionar una mayor claridad y facilidad de implementación.

Requisitos previos

  • Suscripción de Azure: necesita una suscripción de Azure activa. Si no tiene una, puede crear una cuenta gratuita en Cuenta gratuita de Azure.
  • Recurso de Azure Communication Services: se requiere un recurso de ACS para usar las funcionalidades de llamadas y chat. Puede crear uno en Azure Portal.
  • Configuración del entorno de desarrollo: asegúrese de que el entorno de desarrollo esté configurado para una o varias plataformas de destino: Android, iOS o web.
  • Cuenta de Azure Storage: para almacenar los comentarios de los usuarios y los datos relacionados de forma segura, se necesita una cuenta de Azure Storage.
  • Node.js y Express.js: resulta útil tener conocimientos básicos de Node.js y Express.js para configurar la aplicación del lado servidor para recibir y procesar las solicitudes de soporte técnico.
  • Conocimiento de las API de RESTful: comprender cómo crear y consumir API de RESTful ayuda en la implementación y configuración del cliente y el servidor.
  • Aptitudes de desarrollo de cliente: competencia en el desarrollo de aplicaciones Android o iOS.

Tener estos requisitos previos implementados garantiza un inicio sin problemas para integrar un sistema de comentarios de usuario completo mediante Azure Communication Services y otros recursos de Azure.

Conocimientos que adquirirá

En esta guía, obtendrá información completa sobre la integración de los mecanismos de comentarios de usuario en las aplicaciones de Azure Communication Services (ACS). El objetivo es mejorar la asistencia al cliente a través de la biblioteca de interfaz de usuario de ACS mediante el uso de servicios back-end de Azure para el procesamiento. En esta guía, los desarrolladores aprenderán a:

  • Implementar la captura de comentarios del lado cliente: obtenga información sobre cómo capturar comentarios de usuario, registros de errores y solicitudes de soporte técnico directamente desde aplicaciones Android e iOS mediante la biblioteca de interfaz de usuario de ACS.
  • Configurar una aplicación del lado servidor: instrucciones paso a paso sobre cómo configurar una aplicación de Node.js mediante Express.js para recibir, procesar y almacenar solicitudes de soporte técnico en Azure Blob Storage. Este servidor incluye el control de datos de varias partes o formularios para cargas de archivos y la administración segura de los datos de usuario.
  • Crear incidencias de soporte técnico: comprenda cómo generar números únicos de incidencias de soporte técnico y almacenar comentarios de los usuarios junto con los datos de aplicación pertinentes.
  • Usar Azure Blob Storage: profundice en cómo usar Azure Blob Storage para almacenar comentarios y datos de solicitud de soporte técnico, lo que garantiza una administración segura y estructurada de los datos que admite la recuperación y el análisis eficaces.
  • Mejorar la confiabilidad de las aplicaciones y la satisfacción del usuario: los desarrolladores pueden solucionar y resolver rápidamente los problemas de los usuarios mediante la implementación de las estrategias descritas en esta guía.

Configuración del lado servidor

Configuración de una aplicación de Node.js para controlar las solicitudes de soporte técnico

Objetivo de la sección: el objetivo es crear una aplicación de Node.js mediante Express.js que actúa como back-end para recibir solicitudes de soporte técnico de los usuarios. Estas solicitudes pueden incluir comentarios textuales, registros de errores, capturas de pantalla y otra información relevante que puede ayudar a diagnosticar y resolver problemas de usuario. La aplicación almacena estos datos en Azure Blob Storage para garantizar un acceso organizado y seguro.

Marco y herramientas

  • Express.js: un marco de Node.js para compilar aplicaciones web y API. Sirve como base para la configuración del servidor y el control de solicitudes.
  • Formidable: biblioteca para analizar datos de formulario, especialmente diseñada para controlar datos de varias partes o formularios, que a menudo se usan para cargas de archivos.
  • Azure Blob Storage: un servicio de Microsoft Azure para el almacenamiento de grandes cantidades de datos no estructurados.

Paso 1: configuración del entorno

Antes de empezar, asegúrese de que el entorno de desarrollo esté listo con Node.js instalado. También necesita acceso a una cuenta de Azure Storage para almacenar los datos enviados.

  1. Instalar Node.js: asegúrese de que Node.js esté instalado en el sistema. Puede descargarlo en Node.js.

  2. Creación de una cuenta de Azure Blob Storage: si aún no lo ha hecho, cree una cuenta de Azure Storage a través de Azure Portal. Esta cuenta se usa para almacenar los datos de la solicitud de soporte técnico.

  3. Recopilación de las credenciales necesarias: asegúrese de que tiene la cadena de conexión de la cuenta de Azure Blob Storage.

Paso 2: configuración de la aplicación

  1. Inicialice un nuevo proyecto de Node.js:

    • Cree un directorio nuevo para el proyecto e inicialícelo con npm init para crear un archivo package.json.

    • Instale Express.js, Formidable, el SDK de Azure Storage Blob y otras bibliotecas necesarias mediante npm.

      npm install express formidable @azure/storage-blob uuid
      
  2. Implementación de servidor:

    • Use Express.js para configurar un servidor web básico que escuche las solicitudes POST en un punto de conexión específico.
    • Use Formidable para analizar los datos de formulario entrantes y controlar el contenido de datos de varias partes o formularios.
    • Genere un número de vale único para cada solicitud de soporte técnico, que se puede usar para organizar los datos en Azure Blob Storage y proporcionar una referencia para los usuarios.
    • Almacene datos estructurados, como mensajes de usuario y metadatos de archivo de registro, en un archivo JSON dentro de Blob Storage. Almacene los archivos de registro reales y las capturas de pantalla o los datos adjuntos en blobs independientes dentro del mismo directorio del vale.
    • Proporcione un punto de conexión para recuperar los detalles de soporte técnico, lo que implica capturar y mostrar datos de Azure Blob Storage.
  3. Consideraciones sobre la seguridad:

    • Asegúrese de que la aplicación valide los datos entrantes para protegerse frente a cargas malintencionadas.
    • Use variables de entorno para almacenar de forma segura la información confidencial, como la cadena de conexión de Azure Storage.

Paso 3: ejecutar y probar la aplicación

  1. Variables de entorno:

    • Configure variables de entorno para la cadena de conexión de Azure Blob Storage y cualquier otra información confidencial. Por ejemplo, puede usar un archivo .env (y el paquete npm dotenv para cargar estas variables).
  2. Ejecución del servidor:

    • Inicie la aplicación Node.js ejecutando node <filename>.js, donde <filename> es el nombre del archivo de servidor principal.
    • Valide el servidor con una herramienta adecuada para el desarrollo web.

Código de servidor:

Aquí se proporciona una implementación en funcionamiento con la que empezar. Este código es una implementación básica adaptada para demostrar la creación de vales a partir de las aplicaciones de ejemplo de interfaz de usuario de 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}`);
});

Configuración del lado cliente

En esta sección se describe la configuración del lado cliente y cómo lograr los siguientes objetivos:

  1. Registrarse para los problemas notificados por el usuario.
  2. Serializar los datos.
  3. Reenviarlos al servidor.
  4. Recibir una respuesta.
  5. Presentar la respuesta al usuario.

La habilitación de los comentarios de los usuarios en la biblioteca de interfaz de usuario de Azure Communication Services (ACS) requiere una acción de parte de los desarrolladores. Mediante el uso de onUserReportedIssueEventHandler en la integración de la biblioteca, los desarrolladores pueden habilitar el formulario de soporte técnico integrado, lo que permite a los usuarios notificar problemas directamente. Esta sección le guía a través de la configuración del formulario de comentarios del lado cliente.

Implementación de la captura de comentarios del lado cliente en Android

Habilitación del formulario de soporte técnico

  1. Registro del controlador de eventos:

    • Para activar el formulario de soporte técnico dentro de la aplicación Android, registre en onUserReportedIssueEventHandler un punto adecuado del ciclo de vida de la aplicación. Este registro no solo habilita el formulario, sino que también garantiza que sea visible y accesible para los usuarios.
  2. Visibilidad y accesibilidad de los formularios:

    • La presencia del onUserReportedIssueEventHandler registrado afecta directamente a la visibilidad del formulario de soporte técnico. Sin este controlador, el formulario permanece oculto de la interfaz de usuario, lo que lo hace inaccesible para los informes de problemas.

Captura y procesamiento de eventos de soporte técnico

  1. Emisión de eventos tras la notificación del problema:

    • Cuando los usuarios notifican problemas mediante el formulario de soporte técnico habilitado, onUserReportedIssueEventHandler captura los eventos emitidos. Estos eventos encapsulan todos los detalles necesarios relacionados con el problema notificado por el usuario, como las descripciones, los registros de errores y potencialmente las capturas de pantalla.
  2. Preparación de datos para el envío:

    • Una vez que un usuario notifica un problema, el siguiente paso implica preparar los datos del problema notificados para el envío al servidor. Esta preparación incluye estructurar la información capturada en un formato adecuado para la transmisión HTTP, cumpliendo con las expectativas del servidor.

Envío de datos de problemas al servidor

  1. Transmisión asincrónica de datos:

    • Utilice mecanismos asincrónicos para transmitir los datos preparados al punto de conexión de servidor designado. Este enfoque garantiza que la aplicación siga respondiendo, lo que proporciona una experiencia de usuario fluida mientras se envían los datos en segundo plano.
  2. Control de respuestas del servidor:

    • Tras el envío de datos, es fundamental controlar las respuestas del servidor de forma hábil. Este control puede implicar analizar los comentarios del servidor para confirmar la transmisión correcta de datos y posiblemente extraer una referencia al problema enviado (como un número de vale o una dirección URL) que se pueda comunicar al usuario.

Proporcionar notificaciones y comentarios de usuario

  1. Comentarios de usuario inmediatos:

    • Notifique a los usuarios inmediatamente el estado de su envío de informes de problemas a través de la interfaz de usuario de la aplicación. Para envíos correctos, considere la posibilidad de proporcionar una referencia al problema enviado que permita a los usuarios realizar un seguimiento del progreso de su informe.
  2. Estrategia de notificación para Android O y versiones más recientes:

    • En el caso de los dispositivos que ejecutan Android O (nivel de API 26) y versiones más recientes, asegúrese de implementar un canal de notificación específico para los envíos de informes. Esta configuración es esencial para entregar las notificaciones de forma eficaz y es un requisito en estas versiones de Android.

A través de estos pasos, los desarrolladores pueden integrar un mecanismo de comentarios de usuario sólido en sus aplicaciones Android mediante el uso de onUserReportedIssueEventHandler para la generación eficaz de informes y el seguimiento de problemas. Este proceso no solo facilita la resolución oportuna de los problemas de usuario, sino que también contribuye significativamente a mejorar la experiencia general del usuario y la satisfacción con la aplicación.

Código de ejemplo de Android

El fragmento de código de Kotlin muestra el proceso de integración de un sistema para controlar los problemas notificados por el usuario en una aplicación Android mediante Azure Communication Services. Esta integración tiene como objetivo simplificar el proceso de soporte técnico habilitando la comunicación directa entre los usuarios y los equipos de soporte técnico. Esta es una introducción a los pasos implicados:

  1. Captura de eventos: el sistema escucha los problemas notificados por el usuario a través de la biblioteca de interfaz de usuario de ACS. Utiliza onUserReportedIssueEventHandler para capturar comentarios de la interfaz de usuario de la aplicación, incluidos los errores y las preocupaciones del usuario.

  2. Transmisión de datos al servidor: cuando se notifica un problema, el sistema empaqueta los datos pertinentes, incluidos los mensajes de usuario, los registros de errores, las versiones y la información de diagnóstico. A continuación, estos datos se envían a un punto de conexión de servidor mediante una solicitud POST asincrónica, lo que garantiza que el proceso no dificulte el rendimiento de la aplicación.

  3. Notificaciones y comentarios de usuario: después del envío, los usuarios se informan inmediatamente sobre el estado de su informe a través de notificaciones en la aplicación. Para envíos correctos, una notificación incluye un vínculo o una referencia al vale enviado, lo que permite a los usuarios realizar un seguimiento del progreso de la resolución.

Esta configuración no solo ayuda a abordar rápidamente los problemas de los usuarios, sino que también contribuye significativamente a mejorar la satisfacción del usuario y la confiabilidad de las aplicaciones al proporcionar un canal claro para el soporte técnico y los comentarios.

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)
    }
}

Una vez que tenga un controlador para el evento, puede registrarlo al crear la composición de llamadas.

            callComposite.addOnUserReportedEventHandler(userReportedIssueEventHandler)

Introducción al soporte técnico de iOS

Para integrar la recopilación de comentarios de usuarios en aplicaciones iOS mediante la biblioteca de interfaz de usuario de Azure Communication Services (ACS), los desarrolladores deben seguir un enfoque estructurado. Este proceso implica la captura de los comentarios de usuarios, incluidos los registros de errores y la información del usuario. Una vez completada, esta información se envía a un servidor para su procesamiento. En esta sección, detallamos los pasos necesarios para realizar esta tarea.

En este ejemplo, se usa la biblioteca Alamofire para controlar el envío de un formulario de varias partes, incluidos los archivos de registro, al servidor.

Implementar el formulario de soporte técnico

  1. Registro del controlador de eventos: empiece por registrar un controlador de eventos que escuche los problemas notificados por el usuario. Este controlador es fundamental para capturar comentarios directamente desde la interfaz de la aplicación iOS mediante las funcionalidades de la biblioteca de interfaz de usuario de ACS.

  2. Visibilidad y accesibilidad del formulario: asegúrese de que el formulario de soporte técnico sea fácilmente accesible y visible para los usuarios dentro de la aplicación. La activación del formulario está vinculada directamente a la implementación del controlador de eventos, que desencadena su aparición dentro de la interfaz de usuario, lo que permite a los usuarios notificar problemas.

Captura y procesamiento de solicitudes de soporte técnico

  1. Emisión de eventos en la acción del usuario: cuando un usuario notifica un problema mediante el formulario de soporte técnico, el controlador de eventos captura esta acción. La información, como la descripción del problema por parte del usuario, los registros de errores y los identificadores de llamada, deben estar preparados para enviarse al servidor.

  2. Estructuración de datos para envío: organice la información capturada en un formato estructurado adecuado para la transmisión. Prepare los datos de una manera que se alinee con el formato esperado del punto de conexión del servidor que recibe y procesa la solicitud de soporte técnico.

Envío de datos al servidor

  1. Envío asincrónico: use llamadas de red asincrónicas para enviar los datos estructurados al servidor. Este enfoque garantiza que la aplicación siga respondiendo, lo que proporciona una experiencia fluida para el usuario mientras los datos se transmiten en segundo plano.

  2. Control de respuestas del servidor: tras el envío, procese eficazmente las respuestas del servidor. Reciba y analice la respuesta para confirmar la recepción correcta de los datos. Extraiga el vínculo de la incidencia de soporte técnico de la respuesta analizada, que se puede comunicar al usuario para su seguimiento.

Comentarios y notificaciones a los usuarios

  1. Confirmación inmediata: confirme inmediatamente el envío de una solicitud de soporte técnico dentro de la aplicación, lo que proporciona a los usuarios la confirmación de que se recibió su informe.

  2. Estrategia de notificación: implemente una estrategia para entregar notificaciones a los usuarios, especialmente en dispositivos que ejecutan versiones de iOS que admiten marcos de notificaciones específicos. Puede usar notificaciones locales para informar a los usuarios sobre el estado de su informe o proporcionar actualizaciones a medida que se soluciona su problema.

Código de ejemplo de iOS

En este ejemplo de código de Swift se describe una implementación básica para capturar problemas notificados por el usuario y enviarlos a un servidor para su procesamiento. En este ejemplo se muestra cómo construir un controlador de eventos de soporte técnico, incluidos los comentarios del usuario y la información de diagnóstico de la aplicación, y la entrega al servidor. El código también incluye estrategias de control de errores y notificaciones de usuario para garantizar una experiencia de usuario fluida.

El ejemplo siguiente está diseñado para ser un enlace que se va a instalar en el controlador de eventos.

Instalación

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
        }
    }
}

Enlace de red

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)")
        }
    }
}

Este código de Swift muestra el proceso de envío de problemas notificados por el usuario desde una aplicación iOS mediante Azure Communication Services. Controla la recopilación de comentarios del usuario, el empaquetado de información de diagnóstico y el envío asincrónico a un punto de conexión de servidor. Además, proporciona la base para implementar mecanismos de comentarios, lo que garantiza que los usuarios se informen sobre el estado de sus informes y mejora la confiabilidad general de las aplicaciones y la satisfacción del usuario.

Conclusión

La integración de mecanismos de comentarios de usuarios en aplicaciones que usan Azure Communication Services (ACS) es fundamental para desarrollar aplicaciones con capacidad de respuesta y centradas en el usuario. En esta guía se proporciona una ruta clara para configurar el procesamiento del lado servidor con Node.js y la captura de comentarios del lado cliente para aplicaciones Android e iOS. A través de esta integración, los desarrolladores pueden mejorar la confiabilidad de las aplicaciones y la satisfacción del usuario a la vez que usan los servicios en la nube de Azure para una administración eficaz de los datos.

En la guía se describen los pasos prácticos para capturar los comentarios de los usuarios, los registros de errores y las solicitudes de soporte técnico directamente desde las aplicaciones. La integración de eventos de soporte técnico garantiza una manera segura y organizada de controlar los comentarios, lo que permite a los desarrolladores abordar y resolver rápidamente los problemas de los usuarios, lo que da lugar a una experiencia de usuario general mejorada.

A través de las instrucciones detalladas en esta guía, los desarrolladores pueden mejorar la capacidad de respuesta de sus aplicaciones y satisfacer mejor las necesidades del usuario. Estas integraciones no solo ayudan a comprender los comentarios de los usuarios de forma más eficaz, sino que también usan servicios en la nube para garantizar un mecanismo de procesamiento y recopilación de comentarios fluido y eficaz. En última instancia, la integración de los mecanismos de comentarios de usuarios es esencial para crear aplicaciones atractivas y confiables que priorizan la satisfacción del usuario.