Автор Анна Евкова
Преподаватель который помогает студентам и школьникам в учёбе.

Технология «Клиент-сервис»

Содержание:

Введение

Рынок мобильных технологий растет очень быстро. Несколько лет назад дневной объем интернет-трафика с мобильных устройств превысил аналогичный показатель для компьютеров [1]. Четко прослеживается тенденция на постепенную мобилизацию многих сфер жизни человека. Мобильные приложения меняют привычки людей и предлагают новые удобные способы решения каждодневных задач, таких как вызов такси, ведение списка дел, общение с друзьями, чтение книг.

Удаленные медицинские консультации обладают потенциалом навсегда изменить рынок оказания медицинских услуг, ведь они позволяют пациенту получить консультацию со врачом в течение нескольких минут, что может быть удобно как для врача, так и для пациента. Стоит отметить, что использование телемедицины особенно актуально в Российской Федерации, которая, будучи самой большой страной в мире, содержит населенные пункты, в которых очное посещение врача может быть сложным из-за отсутствия необходимого персонала и удаленности от крупных городов.

Клиент-серверное Androidприложение «e-Doctor»позволяетпациентам отслеживать персональную медицинскую информацию и получать удаленные медицинские консультации через чат, видео- и аудиосвязьсо врачом желаемой специализации. Операционная система Android уверенно лидирует в списке мобильных платформ и занимает более 70% рынка [2]. Разработанное клиентское приложение доступно на версиях операционной системы начиная с 5.0 и выше, которые установлены на не менее чем 80% Android устройств [3]. Таким образом, разработанное приложение будет доступно большой части целевой аудитории.

У разработанного программного продукта есть некоторое количество конкурентов на российском рынке мобильных Android приложений; их можно разделить на три группы. Первая группа приложений позволяет пользователю общаться с врачом, а функции редактирования медкарты недоступны. Вторая группа предоставляет лишь функциональность отслеживания персональной медицинской информации, такой как посещения врачей или уровень сахара в крови. Приложения третьей группы совмещают в себе функции ведения медкарты и телемедицины, однако единственным подобным приложением на рынке является ONDOC [4].

В свою очередь, разработанный программный продукт обладает набором функций, на данный момент не доступным ни в одном Android приложении на рынке. Приложение «e-Doctor» предоставляет наиболее полный по сравнению с конкурентами список типов записей медкарты. Помимо этого, пациент имеет возможность создавать свои типы медицинских показателей, что повышает качество пользовательского опыта. Создание, редактирование и удаление всех записей медкарты доступно в условиях отсутствия интернет-соединения с последующей синхронизацией изменений с сервером. Пациент также может отправить любую запись медкарты в виде текста в одно из установленных на его телефоне приложений.

Цель данной работы была сформулирована так: разработать клиент-серверное Android приложение для отслеживания пациентами персональной медицинской информации и проведения удаленных медицинских консультаций между врачом и пациентом. Для достижение этой цели необходимо выполнить следующий список задач:

  1. Проанализировать существующие решения;
  2. Разработать алгоритм работы и определить функциональность приложения;
  3. Разработать серверное API, определить основные сущности бизнес-логики сервера;
  4. Определить архитектуру и используемые библиотек на клиентской и серверной частях;
  5. Создать прототипы дизайна и навигации;
  6. Разработать клиентскуючасть приложения;
  7. Разработать серверную часть приложения;
  8. Протестировать и отладить приложение;
  9. Написать техническую документацию.

Глава 1. Обзор существующих решений «клиент-сервер»

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

    1. Приложения для проведения удаленных медицинских консультаций

Рисунок 1.1. «Яндекс.Здоровье»Рисунок 1.2. «Doc+» Рисунок 1.3. «DocDoc»

Такие популярные программные продукты, как «Яндекс.Здоровье» [5] (рис. 1.1), «Doc+» [6] (рис. 1.2) или «DocDoc» [7] (рис. 1.3) позволяют пациенту получить консультацию со врачом желаемой специализации через чат, видео- и аудиосвязь. Как только общение заканчивается, пациент получает доступ к списку рекомендаций и заключению от медицинского сотрудника.

Однако, качество оказания медицинских услуг может быть потенциально улучшено, если врач будет иметь возможность ознакомиться с медкартой пациента перед началом консультации и видеть всю картину текущего состояния пациента. Для пациента это может быть удобно еще и тем, что он сможет хранить всю свою информацию о здоровье в одном месте, вне зависимости от того, получает ли он медицинские услуги лично или удаленно. Также, существенным недостатком приложений «Яндекс.Здоровье» и «Doc+»является то, что онапозволяет лишь связаться со случайным врачом без возможности поиска наиболее подходящего врача вручную.

    1. Приложения для отслеживания персональной медицинской информации



Рисунок 1.4. «Финтехклаб Медкарта» Рисунок 1.5. «MedicalNote»

«Финтехклаб Медкарта» [8] (рис. 1.4)и «MedicalNote» [9] (рис. 1.5) это самые популярные приложения для ведения медкарты в телефоне. Главная проблема этих сервисов в том, что они не поддерживают экспорт данных медкарты ни в каком виде. Пользователь может отслеживать свою медкарту, но не может использовать ее для консультаций с врачом, так как перечисленные сервисы не обладают функцией проведения удаленных консультаций.

Более того, перечисленные программные продукты имеют ряд недостатков в сценарии редактирования медицинских данных. Например, «Финтехклаб Медкарта» поддерживает добавление записей лишь в виде комментариев и просмотр их всех разом на одном экране, что означает полное отсутствие структурирования записей по типам. Также, это приложение не поддерживает синхронизацию между устройствами. Так, при смене мобильного телефона пользователю придется заново вводить все записи. «MedicalNote», в свою очередь, не позволяет вводить данные о своей группе крови, создавать свои типы медицинских показателей, отслеживать изменение показателей здоровья с течением времени на графике.

    1. Приложения с совмещенными функциями


 







Рисунок 1.6. «ONDOC»

Единственным программным продуктом, совмещающим в себе обе рассмотренные выше функциональности, является «ONDOC» (рис. 1.6). Однако, он имеет ряд недостатков. Приложение не поддерживает такие типы событий, как проведение процедуры или период болезни, не дает возможность добавлять комментарии к событиям и отправлять записи в виде текста в другие установленные на телефоне приложения. Из медицинских показателей доступны лишь два – давление и вес. Редактирование медкарты без интернет-соединения недоступно. Наконец, «ONDOC» не обладает функцией гибкой настройки доступа врача к медицинским записям пациента. Пациент может либо дать медицинскому сотруднику полный доступ ко всем записям, либо не давать его вообще. Невозможно отправить врачу лишь некоторые записи. Более того, записи, отправляемые врачом, автоматически добавляются в медкарту. Пациент не имеет возможности отредактировать их или отказаться от их добавления.

    1. Обоснование необходимости разработки нового приложения

Таким образом, исходя из обзора приведенных выше Android приложений, разработка нового программного продукта имеет смысл. Каждое из рассмотренных приложений имеет недостатки в сценариях общения врача с пациентом или ведения медкарты. Разработанный программный продукт «e-Doctor» обладает набором функций, на момент разработки программы не доступным ни в одном приложении на рынке. Дополнительно стоит отметить, что клиентская часть приложения полностью локализована на русский и английский языки, а ее интерфейс спроектирован с учетом MeterialDesign. Сравнение разработанного приложения с конкурентами представлено на рис. 1.7.

Яндекс.

Здоровье

DOCDOC

DOC+

ONDOC

Финтехклаб Медкарта

MedicalNote

e-Doctor

Базовая информация о пациенте

-

-

-

+

+

-

+

События в медкарте

-

-

-

+/-

-

+

+

Отслеживание показателей в медкарте

-

-

-

+/-

-

+/-

+

Настройки доступа врача к медкарте

-

-

-

+/-

-

-

+

Offline-firstредактированиемедкарты

-

-

-

-

-

+

+

Экспорт записей медкарты в виде текста в другие приложения

-

-

-

-

+

-

+

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

+

+

+

+

-

-

+

Поиск врачей по имени и специальности

-

+

-

+

-

-

+

Общение с врачом

+

+

+

+

-

-

+

Просмотр истории чатов без интернета

+

+

+

-

-

-

+


Рисунок1.7. Сравнение разработанногоприложения с аналогами

Глава 2. Архитектура приложения «клиент-сервер»

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

2.1. Архитектура приложения в целом

C:\Users\Данил\Downloads\Untitled Diagram (1).jpg


Рисунок 2.1. Архитектура приложения в целом

В качестве клиентской части приложения выступает нативное мобильное Android приложение. Серверная часть приложения, в свою очередь, написана с помощью Spring [11], фреймворка для разработки надежных серверных приложений. Программный продукт полностью написан на языках Kotlin [12] и Java [13], при этом Kotlin, как более современный и гибкий язык, использовался в большинстве случаев. Использование одного набора языков для реализации обеих частей приложения позволяет в некоторых случаях писать код один раз вместо двух, использовать одни и те же библиотеки и IDE.

Серверное Springприложение хранит информацию в базе данных и файловом хранилище, таким образом, пользователи могут взаимодействовать друг с другом и использовать сразу несколько устройств для доступа к своим данным. Для передачи данных между клиентом и сервером используетсяпротокол HTTPS вместе с архитектурным стилемREST [14].Однако, некоторые функции приложения, такие как чат или аудиозвонки, требуют установления длительного соединения между клиентом и сервером, не поддерживаемого протоколом HTTPS самим по себе: для этих целей был использован протокол WebSocket над протоколом HTTPS.Передаваемые данные представляются в виде форматов JSON и multipart/form-data.

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

2.2. Архитектура клиентской части приложения

Image result for android clean architectureВыбор архитектуры для программного продукта очень важен, ведь в будущем неподходящая архитектура может сделать разработку некоторых функций невозможной или потребовать полного переписывания всего приложения при внесении лишь небольших изменений в пользовательские сценарии. И, напротив, правильно продуманные высокоуровневые принципы устройства приложения способны сделать еголегко тестируемым, расширяемым и независимым от пользовательского интерфейса и источников данных.



Рисунок 2.2. Схема Чистой Архитектуры[15]

В среде Android разработчиков большой популярностью пользуется так называемая Чистая Архитектура [15], схема которой представлена на рис. 2.2. Следуя этой архитектуре, приложение должно делиться на 3 слоя: данные (data), бизнес-логика (domain), представление (presentation). У каждого из слоев своя зона ответственности. Первый слой отвечает за получение данных из различных источников, таких как сервер или база данных, и последующий перевод данных в структуры, понятные слою бизнес-логики. Слой бизнес-логики отвечает за основную логику приложения, такую как, например, определение

того, истекла ли у пользователя подписка или вычисление суммы платежа, которую пользователь будет должен заплатить за желаемую транзакцию. Наконец, слой представления ответственен https://lh3.googleusercontent.com/PADh5uXSJvuZuSHcSiCrsR1IhlbrJeG0KOnelJnSUbbo4BViNNK52u8hfrt8oUS6yQsXU5qAM4l6Jxxzcbx9ICkQwuz5cFbm6WRLio5UPhwS8NAXgm-HK039I1-uG1kw-DLa00tfIRAза отображение данных.

Рисунок 2.3. Архитектура клиентана примере некоторых классов

Однако, во многих приложениях бизнес-логика хранится на сервере для использования ее на многих клиентах и из соображений безопасности. В разработанном приложении бизнес-логика также хранится на сервере, таким образом, поддержка слоя domain была бы излишней и не оправдывала бы себя, ведь этот слой бы просто передавал объекты от Данных к Представлению. Поэтому архитектура разработанного мобильного приложения состоит из двух слоев: данные и представление (рис. 2.3). Слой данных реализует паттерн Repository: в зависимости от параметров запроса, текущего состояния сети и многих других параметров, Repository сам решает, откуда получать данные – из локального хранилища или сервера, инкапсулируя в себе все взаимодействие с источниками данных. Mapper ответственен за преобразования объектов одного типа в другой: например, из типа MessageEntity, которым оперирует локальное хранилище, в тип Message, о котором знает слой представления. Примененная архитектура делает приложение тестируемым и расширяемым за счет использования принципов единственной ответственности и инверсии зависимости, входящими в список принципов SOLID[16]. Так как классы выполняют ровно одну задачу и не зависят напрямую от реализаций других классов, приложение будет легко покрыть unit-тестами. Архитектура разработанного приложения позволяет ему быть независимым от источников данных и интерфейса, таким образом, изменения, вносимые в приложение при изменении способа хранения данных или их отображения, затронут минимальное количество классов, что положительно сказывается на простоте поддержки разработанного программного продукта.

Фреймворк Android содержит 4 основных компонента, используемых при разработке приложений [17]. Один из них, Activity, используется для работы с пользовательским интерфейсом. У этого компонента достаточно сложный жизненный цикл [18], включающий в себя последовательный вызов более 15 методов, таких как «onStart», «onStop»,

«onRequestPermissionResult», «onDestroy» и другие. Во многих случаях, от разработчика требуется переопределение как минимум нескольких из этих методов. Более того, некоторые действия, такие, как открытие диалога, получение разрешений на действия в системе, старт фонового действия, доступны только из контекста Activity. Это часто приводит к увеличению количества строк в классе, наследующемся от Activity. Другой проблемой компонента является то, что он по умолчанию не сохраняет свое состояние при изменении конфигурации, например, повороте экрана.

Image result for mvp vs mvvm pattern

Рисунок 2.4. Сравнение MVPи MVVM.

Для решения вышеперечисленных проблем можно использовать такой паттерн, как MVP [19] (рис. 2.4). Этот паттерн предполагает, что слой представления разделяется на две части: View и Presenter. Model в MVP – часть, не связанная с отображением, в нашей архитектуре это слой данных. View ответственна за работу с системой, описанную выше. Presenter же ответственен за логику, которая не имеет отношения к системе, например, сортировку записей перед их показом, получение записей от Model, а также за хранениесостояния экрана. Таким образом, связанность кода уменьшается, ведьView ничего не знает про Model, Model про View, между собой их связывает Presenter.

Однако, к недостаткам приведенного выше паттерна можно отнести то, что после многократного вызова Presenter’ом методов View может быть сложно отследить итоговое состояние системы, а также восстановить его после изменения конфигурации. Это делает связь двух перечисленных классов менее надежной, а Presenter менее тестируемым. Поэтому в последнее время паттерн MVVM [19](рис. 2.4) пользуется все большей популярностью. Этот паттерн отличается от MVP введением новой сущности ViewState, которая представляет из себя буквально «состояние отображения» и используется для передачи данных из Presenter во View.Presenter не имеет права вызывать методы View, вместо этого, View сама подписывается на поток ViewState и обрабатывает его так, как захочет.Для восстановления состояния отображения после смены конфигурации,Presenter’у достаточно передать в поток изменений последний сохраненный ViewState.

Архитектура MVVM в мобильном приложении была реализована самостоятельно с помощью системных компонентов и библиотеки для реактивного программирования RxJava [20], так как на этапе разработки ни одна из библиотек, реализующих MVVMнаAndroid, не показалась надежной.

2.3 Архитектура серверной части приложения































Рисунок 2.5. Архитектура сервера на примере некоторых классов

Как говорилось ранее, в задачи серверавходитзаимодействие с клиентом через протоколы HTTPS иWebSocket. В контексте сервераклассы, принимающие запросы от клиентской части приложения, играют рольслоя представления, ответственного за взаимодействие с внешним миром. Такие классыименуются в фреймворкеSpring как Controllers. Эти классы хранят логику обмена информацией с клиентом, работы с FileStorage, предоставляющим доступ к файлам, а также Repository, который является оберткой для упрощенной работы с базой данных (рис. 2.5). Перед отправкой данных на клиент, сервер должен преобразовать их в понятную клиенту форму, поэтому, для преобразования объектов одного типа в объекты другого, типа используются Mappers.

Глава 3. Детали реализации приложения «клиент-сервер»

При разработке приложения был использован фреймворкRxJava [20], реализующий принципы реактивного программирования [21], в основе которого лежит паттерн Observer [22]. Реактивное программирование позволяет оперировать потоками данных, используя многочисленные операторы обработки, фильтрации и изменения данных. Также, RxJava из коробки поддерживает переключение потоков выполнения кода по мере прохождения данных по потоку, что может быть использовано для работы с сервером и базой данных.Реактивное программирование используется на всех слоях мобильного приложения. Например, в виде потоков представлены: сообщения, получаемые через WebSocket от сервера; записи, получаемые из базы данных; действия с элементами пользовательского интерфейса; изменения состояния интернет-соединения; изменения ViewState в паттерне MVVM.

Для хранения данных на устройстве была выбрана СУБД SQLite [23]. Она является компактной, полностью покрыта тестами и встроена в фреймворкAndroid, поэтому для подключения СУБД не нужно будет добавлять в зависимости приложения дополнительную библиотеку. Однако, работа с SQLite напрямую требует написания большого количества однотипного кода, поэтому в качестве обертки над SQLiteбыла использована легковеснаябиблиотека StorIO [24].

StorIOпозволяет описывать таблицы базы данных в виде data классов, доступных в языке Kotlin, с помощью аннотаций. Таким образом, в одном классе можно описать создаваемую таблицу в базе данных, а также тип, объектами которого другие классы будут оперировать для работы с базой данных. Например, на схеме 3.1. приведено описание таблицы сообщений в виде класса MessageEntity.
 

@StorIOSQLiteType(table = TABLE_NAME)
dataclassMessageEntity @StorIOSQLiteCreatorconstructor(
@StorIOSQLiteColumn(name = COLUMN_UUID, key = true) valuuid: String,
@StorIOSQLiteColumn(name = COLUMN_TYPE) val type: Int,
@StorIOSQLiteColumn(name = COLUMN_TIMESTAMP) val timestamp: Long,
@StorIOSQLiteColumn(name = COLUMN_SENDER_UUID) valsenderUuid: String,
@StorIOSQLiteColumn(name = COLUMN_RECIPIENT_UUID) valrecipientUuid: String,
@StorIOSQLiteColumn(name = COLUMN_SENDER_FULL_NAME) valsenderFullName: String?,
@StorIOSQLiteColumn(name = COLUMN_RECIPIENT_FULL_NAME) valrecipientFullName: String?,
@StorIOSQLiteColumn(name =COLUMN_TEXT) val text: String?,
@StorIOSQLiteColumn(name = COLUMN_IMAGE_RELATIVE_URL) valimageRelativeUrl: String?,
@StorIOSQLiteColumn(name = COLUMN_CALL_STATUS) valcallStatus: Int?,
@StorIOSQLiteColumn(name = COLUMN_CALL_UUID) valcallUuid: String?
)

Схема 3.1. КлассMessageEntity

private fun getConversationMessageQuery(
fromTimestamp: Long,
userUuid: String,
recipientUuid: String
) = Query.builder()
.table(MessageEntityContract.TABLE_NAME)
.where(
"$COLUMN_TIMESTAMP > ? " +
"AND ($COLUMN_SENDER_UUID = ? OR $COLUMN_RECIPIENT_UUID = ?) " +
"AND ($COLUMN_SENDER_UUID = ? OR $COLUMN_RECIPIENT_UUID = ?)"
)
.whereArgs(fromTimestamp, userUuid, userUuid, recipientUuid, recipientUuid)
.build()

Схема 3.2. Метод getConversationMessageQuery

Одной из удобных функций библиотеки StorIO, ради которых ее и стоит использовать, является возможность создания запросов к базе данных с помощью паттерна Builder [25]. Например, можно описать запрос на получения всех сообщений в чате, отправленных после определенного момента (схема 3.2).

Всего в локальной базе данных мобильного приложения хранятся 3 не связанные между собой таблицы: сообщения, показатели здоровья, медицинские события. Таблица сообщений используется для хранения всей истории переписки, две другие таблицы – для обеспечения подхода Offline-first [10] при работе пациента с медицинской картой. Для каждой таблицы в приложении создается отдельный класс хранилища, наследуемый от BaseLocalStore, содержащего в себе удобные методы для работы с StorIO. Один из таких методов принимает на вход запрос к базе данных, а возвращает список объектов, полученных по запросу, в виде потока RxJava (cхема 3.3).

fun getAllByQuery(query: Query): Single<List<T>> =
storIOSQLite.get()
.listOfObjects(objectClass)
.withQuery(query)
.prepare()
.asRxSingle()

Схема 3.3. Метод getAllByQuery

Для отправки REST-запросов использовалась библиотека Retrofit [26]позволяющая описывать запросы к серверу в виде интерфейсов, реализация которых создается с помощью кодогенерации. Для примера, рассмотрим интерфейсAccountRestApi для работы с аккаунтом, в котором описаны методы получения текущего аккаунта с сервера и обновления аккаунта на сервере (cхема 3.4).

interfaceAccountRestApi {

@GET("/account")
fungetAccount(): Single<UserModelWrapper>

@Multipart
@POST("/account")
funupdateAccount(
@Part("userRequest")userRequest: UserModelWrapper,
@Partimage: MultipartBody.Part?
): Single<UserModelWrapper>

}

Схема 3.4. Интерфейс AccountRestApi


Как видно на схеме 3.4, Retrofit позволяет отправлять запросы на сервер буквально в несколько строк. Для создания запросов используются специальные аннотации, применяемые к методам и их параметрам. Возможность использования в качестве возвращаемого значения методов потоков данных RxJava и автоматическоепреобразование Kotlin объектов в формат JSON и обратно делают использование этой библиотеки еще более удобным.

Однако, Retrofit не поддерживает протокол WebSocket, поэтому дополнительно была использована библиотека Scarlet [27], позволяющая описывать взаимодействие с сервером в похожем стиле с использованием протокола WebSocket. На схеме 3.5. представлен интерфейс, ответственный за получение от сервера и отправку на сервер сообщений.

interfaceChatSocketApi {

@Receive
funobserveEvents(): Flowable<WebSocketEvent>

@Send
funsendMessage(message: MessageRequestWrapper)

}

Схема 3.5. Интерфейс ChatSocketApi

Для хранения данных на сервере была выбрана СУБД MySQL [28]. Она широко распространена и поддерживается фреймворкомSpring.Всего в базе данных на сервере хранится 9 таблиц (рис. 3.1).

За хранение данных аккаунтов пользователей отвечают таблицы «patients» и «doctors». Было принято решение разделить пользователей на два типа: пациент и врач, так как пользовательские сценарии для пациентов и врачей кардинально отличаются, и в будущем не предполагается создание регистрации аккаунтов, имеющих права на действия для обоих типов.

Пациенты в приложении могут инициировать общение со врачами через чат, для чего в базе данных сервера были созданы две таблицы: «conversations» и «messages». Таблица «conversations» связанас «patients», «doctors» и «messages» связями one-to-many.То есть, у любого пользователя может быть сколь угодно большое число переписок, состоящих из сколь угодно большого числа сообщений.

Двумя другими таблицами в базе данных являются «body_parameters» и «medical_events». Они представляют собой записи пациента о показателях здоровья и медицинских событиях, и связаны с таблицей «patients» связями one-to-many. Таким образом, пациент может добавлять в приложение неограниченное число записей. Важной функцией приложения является возможность предоставления пациентом врачу доступа к медицинским записям:таблица «medical_accesses» хранит для пары пациент-врач доступные для предоставленные для просмотра типы записей.

C:\Users\Данил\Downloads\BD (1).jpgНаконец, таблицы «oauth_access_tokens» и «oauth_refresh_tokens» используются для хранения токенов сессий пользователей, используемых для аутентификации по протоколу OAuth2.

Рисунок 3.1. Схема таблиц базы данных на сервере

Механизм SpringData существенно облегчает взаимодействие с базой данных. Например, вместо описания таблица базы данных может быть описанав виде data классов, доступных в языке Kotlin.Для этого, класс должен быть унаследован от класса Persistable, являющегося частью SpringData, и содержать аннотации из пакета «javax.persistence», примененные к самому классу и его полям.Например на схеме 3.6. приведено описание таблицы сообщений в виде класса MessageEntity.

@NoArg
@Entity
@Table(name = "messages")
class MessageEntity constructor(
givenUuid: UUID?,

@Column(nullable = false) val timestamp: Long,

@Column(nullable = true)val text: String? = null,

@Column(nullable = true) val type: Int? = null,

@Column(nullable = true) valimageUuid: String? = null,

@Column(nullable = true) valcallStatus: Int? = null,

@Column(nullable = true) valcallUuid: String? = null,

@Column(nullable = false) valisFromPatient: Boolean,

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "conversationUuid", nullable = false)
val conversation: ConversationEntity

) : RandomUuidEntity(givenUuid)

Cхема 3.6. КлассMessageEntity
 

SpringData также предоставляет удобный механизм для изменения данных таблицы. Рассмотрим интерфейс DoctorsRepository (рис. 3.7), отвечающий за взаимодействие с таблицей «doctors».

@Repository
interfaceDoctorRepository :JpaRepository<DoctorEntity, String> {

funfindByEmail(email: String) : DoctorEntity?

fun findByFullNameIgnoreCaseContainingOrSpecializationIgnoreCaseContaining(
fullName: String,
specialization: String
): List<DoctorEntity>

fun existsByEmail(email: String) : Boolean

}

Схема 3.7. ИнтерфейсDoctorRepository
 

Каквиднонасхеме 3.7, интерфейсDoctorRepositoryнаследуетсяотинтерфейсаJpaRepository, являющегосячастьюSpringData. За реализацию описанного интерфейса отвечает SpringData, создающий для каждого метода реализацию на чистом SQL в соответствии с названием метода. Если для названия метода невозможно сформировать SQL запрос, SpringData скажет об этом на этапе компиляции еще до запуска программы. Более того, многие методы, такие как получение или удаление записи из таблицы, предоставляются JpaRepository по умолчанию, что еще больше сокращает количество однотипного кода в проекте.

Рисунок 3.2. Экран «Приветствие»Рисунок 3.3. Экран «Приветствие»
после изменения текущего действия

После успешного запуска программы откроется экран «Приветствие» (рис. 3.2). Пользователь имеет возможность создать новую учетную запись или войти в ранее созданную учетную запись, введя адрес электронной почты и пароль в поля ввода текста с надписями «Email» и «Пароль» и нажав на зеленую кнопку под полями ввода.

Во многих приложениях функциональности входа в аккаунт и регистрации представлены на разных экранах. Однаков нашем приложении данные, вводимые пользователем в этих двух случаях, абсолютно идентичны, поэтому было решено реализовать вход в аккаунт и регистрации на одном экране.Зеленая кнопка под полями ввода отображает действие, которое произойдет после нажатия на нее. Текущее действие может быть изменено нажатием на прозрачные кнопки с серым текстом снизу. Всего возможны 4 действия: «Войти как пациент», «Зарегистрироваться как пациент», «Войти как врач», «Зарегистрироваться как врач».

interfaceAuthRestApi {

@FormUrlEncoded
@POST("/oauth/token")
fun getFreshestTokenByRefreshToken(
@Field("refresh_token") refreshToken: String,
@Field("grant_type") grantType: String = "refresh_token"
): Call<TokenResponse>

@FormUrlEncoded
@POST("/oauth/token")
fun getFreshestTokenByPassword(
@Field("password") password: String,
@Field("username") email: String,
@Field("grant_type")grantType: String = "password"
): Single<TokenResponse>

@POST("/register")
fun register(
@Body loginData: LoginDataRequest
): Single<UserModelWrapper>

@POST("/login")
fun login(
@Body loginData: LoginDataRequest
): Single<UserModelWrapper>

}

Cхема 3.8. Интерфейс AuthRestApi, ответственный за авторизацию пользователей

Для того, чтобы пользователь получал доступ только к доступным для него данным, необходимо продумать систему регистрации и последующей авторизации пользователей. В качестве протокола авторизации был выбран OAuth 2.0 [29]. Для обеспечения безопасности передачи данных, все данные передаются между клиентской и серверной частями приложения с использованием протокола HTTPS.

Схема клиент-серверного взаимодействия авторизации пользователей состоит из трех шагов:

  1. Получение данных аккаунта;
  2. Получение access token и refresh token;
  3. Обновление access token.

Первые два шага происходят на экране «Приветствие» (рис. 3.2), когда пользователь вводит данные своего существующего или нового аккаунта.

На первом в шаге, в зависимости от того, хочет ли пользователь войти в существующий аккаунт или же создать новый, клиент вызывает методы сервера POST«/register» или POST«/login», передавая в качестве тела запроса объект JSON, хранящий в себе поля«email», «password» и «isDoctor». В ответ, сервер отправляет информацию аккаунта пользователя в формате JSONили ошибку. При ошибке, пользователю показывается соответствующее ошибке сообщение и схема завершается. Стоит отметить, что эти два метода являются единственными методами сервера, доступными без предоставления accesstoken.

На втором шаге, клиент вызывает метод сервера POST«/oauth/token», передавая с помощью типа данных «application/x-www-form-urlencoded» поля «password», «username» и «grant_type». Поле «password» – пароль пользователя, «username» – его адрес электронной почты, «grant_type» на этом шаге равен тексту «password», так как получение токена происходит по предоставлению пароля. На этом шаге, сервер проверяет, действительно ли в системе существует данный пользователь, и если он находит его, то возвращает JSON, содержащий поля «access_token», «refresh_token» и «expires_in», иначе – ошибку. В случае успеха, клиент сохраняет информацию аккаунта пользователя, accesstoken и refreshtokenв настройки приложения. Плюсом такой схемы является то, что пароль приложения не хранится на устройстве и не используется для получения доступа к методам сервера.

Наконец, на третьем шаге, происходит обновление accesstoken. Этот шаг происходит тогда, когда сервер в ответ на один из запросов клиента возвращает сообщение о том, что предоставленный accesstoken истек (код ошибки 401).Клиент вызывает метод сервера POST«/oauth/token», передавая с помощью типа данных «application/x-www-form-urlencoded» поля «refresh_token» и «grant_type», равных тексту «refresh_token». В ответ, сервер возвращает JSON, содержащий поля «access_token», «refresh_token» и «expires_in», и клиент accesstoken и refreshtoken в настройках.

Как было сказано ранее, для успешного выполнения любого запроса к серверу, кроме «/login» и «/register», требуется передача accesstoken вместе с запросом. Accesstoken передается на сервер в виде заголовка вида«Authorization: Bearer csaf41214safh214oiwuencr». На любой такой запрос сервер может ответить кодом ошибки 401, в случае чего клиент должен получить новый токен с сервера, используя refreshtoken, после чего отправить запрос заново. Однако, если делать это в месте вызова каждого запроса к серверу, количество однотипного кода в приложении существенно увеличится. Чтобы ненарушать правило «Don’trepeatyourself» [30], необходимо вынести описанную выше логику в отдельный класс.

Специально для таких целей, в библиотеке OkHttp [31],используемой в разработанном мобильном приложении, существует механизм Interceptors. Классы типа Interceptor отвечают за перехват запроса к серверу и ответа от него, дополняя запрос или обрабатывая ответ от сервера. В нашем случае, класс CredentialsInterceptor добавляет к запросу заголовок с текущим accesstoken, а в случае необходимости получает новый accesstoken сервера, используя заголовок с refreshtoken, после чего отправляет запрос с использованием обновленного accesstokenзаново.

Рисунок 3.4. Экран «Чат» Рисунок 3.5. Экран «Чат» в условиях
отсутствия интернет-соединения

Экран «Чат» содержит список всех сообщений чата, отсортированный по дате так, что последнее сообщение находится внизу списка, а наиболее ранее – вверху. Этот экран позволяет пациенту отправлять врачу текстовые сообщения и изображения. Также, существуют четыре типа системных сообщений – «Изменение пациентом настроек доступа врача к медкарте», «Отправка врачом записи в медкарту пациента», «Начало вызова», «Окончание вызова». По нажатию на сообщения первых двух типов происходит переход на экраны «Записи от врача» и «Врач», соответственно. Системные сообщения отправляются автоматически без каких-либо действий со стороны пользователей. По нажатию на имя врача происходит переход на экран «Врач».

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

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

Было сформулировано несколько требований к формату клиент-серверного взаимодействия при обмене пользователями сообщениями в чате. Во-первых, сообщения должны передаваться мгновенно, без задержки. Во-вторых, клиент и сервер должны обмениваться лишь сообщениями текущего чата, а не всех активных чатов пользователя, что позволяет избавить клиентское приложение от необходимости фильтрации получаемых сообщений и делает систему более предсказуемой. Протокол STOMP над протоколом WebSocketудовлетворяет обоим требованиями, к тому же, он хорошо поддерживается фреймворкомSpring. Однако, библиотек под Android, реализующих протокол STOMP, практически нет. В то же время, использование чистого протоколаWebSocket в мобильной разработке достаточно распространено; существует множество библиотек под Android, облегчающих работу с ним. В разработанном мобильном приложении была использована библиотека Scarlet, позволяющая описывать взаимодействие с сервером в виде интерфейсов с помощью аннотаций (схема 3.5). Поэтому было решено использовать чистый протокол WebSocket над протоколом HTTPS, при этом немного расширив его, передавая при установлении соединения в заголовкеc названием «recipient-uuid» идентификатор пользователя, с которым будет вестись общение.

Фреймворк Spring позволяет получить для текущего WebSocket соединения объект типа Principal, хранящий информацию о соединении. Унаследовав класс WebSocketPrincipal (схема 3.9) от класса Principal, можно хранить дополнительную информацию о соединении.

 

dataclassWebSocketPrincipal(
valuuid: String,valrecipientUuid: String,valisPatient: Boolean
) : Principal {
valpatientUuid = if (isPatient) uuidelserecipientUuid
valdoctorUuid = if (isPatient) recipientUuidelseuuid
override fun getName() = uuid
}

Схема 3.9. КлассWebSocketPrincipal

ПриконфигурацииWebSocketсоединения, возможно переопределить логику создания объекта Principal в методе «determineUser» (схема 3.10)для передачи в него данных, полученных из заголовков запроса на установление соединения.

override fun determineUser(
request: ServerHttpRequest,
wsHandler: WebSocketHandler,
attributes: MutableMap<String, Any>
): Principal? {
val user = super.determineUser(request, wsHandler, attributes)
?: return null
valrecipientUuid = request.headers[RECIPIENT_UUID_HEADER]?.firstOrNull()
?: return null

val doctor = doctorRepository.findByEmail(user.name)
if (doctor != null) return WebSocketPrincipal(doctor.uuid, recipientUuid, false)

val patient = patientRepository.findByEmail(user.name)
if (patient != null) return WebSocketPrincipal(patient.uuid, recipientUuid, true)

return null
}

Схема 3.10. Метод determineUser

Одной из важных функций приложения в целом является возможность общения пациента с врачом через аудио- и видеосвязь. Написание платформы для видеосвязи с нуля отняло бы непозволительно большое количество времени, поэтому было использовано SaaS решение, платформа Jitsi [32]. Она полностью бесплатна, имеет открытый исходный код и не требует наличия собственного сервера. Платформа построена так, что любой желающий может подключаться к публичным комнатам по ее названию. Однако очевидно, что наше приложение должно обеспечивать приватность и безопасность общения между врачом и пациентом. Поэтому, для создания имени комнаты используется класс UUID из пакета «java.util» позволяющий создавать такие идентификаторы, что вероятность получения доступа к комнате при таком подходе составляет [33].

Платформа Jitsi представляет SDK для операционной системы Android, позволяющая буквально в пару строк отображать интерфейс необходимой комнаты. На рисунках 3.6 и 3.7 представлен пользовательский интерфейс при взаимодействии с комнатой.


 

Рисунок 3.6. Экран «Звонок» Рисунок 3.7. Экран «Звонок»
при включенной камере
Участие пользователей в звонке должно происходить естественно и удобно с точки зрения пользовательского интерфейса. Например, в одно нажатие доктор должен иметь возможность инициировать звонок и завершить его, пациент – принять или отклонить приглашение (рис. 3.8 и 3.9). После выхода одного из пользователей из разговора необходимо закрывать экран «Звонок» на обоих устройствах.

Рисунок 3.8. Экран «Исходящий вызов» Рисунок 3.9. Экран «Входящий вызов»

Однако SDKJitsi для Androidпредоставляет лишь возможность взаимодействия пользователя с комнатой. Логика входа и выхода из комнаты должна быть реализована самостоятельно. Таким образом, необходимо было создать систему управления состоянием текущего звонка. Для этого был применен концепт StateMachine [34]. Диаграмма состояний звонка в системе представлена на рисунке 3.10.

C:\Users\Данил\Downloads\Untitled Diagram (4).jpg


 


Рисунок 3.10. Диаграмма состояний звонка

Концепт StateMachine удобен своей простотой реализации и предсказуемостью. Звонок может находиться в одном из трех состояний: Initiated (Инициирован), Started (Начат), Cancelled (Закончен). Сервер в зависимости от происходящих событий меняет текущее состояние звонка, уведомляя об этом клиентские приложения, отображающие пользовательский интерфейс в соответствии с текущим состоянием. Клиент-серверное взаимодействие в этом случае ведется по протоколу WebSocket, используя текущее соединение для чата.

После нажатия врачом на кнопку «Позвонить» на экране «Чат» (рис. 3.4), сообщение о произошедшем действии отправляется с клиента на сервер. Сервер решает, что текущее состояние звонка меняется на «Initiated» и отправляет сообщение о произошедшем изменении состояния звонка на все связанные со звонком клиенты. После этого, мобильное приложениеоткрывает дляврачаэкран «Исходящий вызов» (рис. 3.8), а для пациента – экран «Входящий вызов» (рис. 3.9).

Если пациент принимает вызов на экране «Входящий вызов», звонок переходит в состояние «Started» и у обоих пользователей открывается экран «Звонок» (рис. 3.6).

В случае, если любой из пользователей в какой-либо момент времени решает прекратить звонок, либо же WebSocket соединение одного из пользователей закрывается по какой-либо причине, система переходит в состояние «Cancelled»; у обоих пользователей закрываются все экраны, связанные со звонком.

Одна из главных функций приложения – создание, редактирование и удаление записей медкарты пациентом. Записи делятся на два основных типа: «Событиe» и «Показатель здоровья». Для удобства пользователя, был реализован подход Offline-first [10], таким образом, при обновлении, записи будут сохранены в локальную базу данных, а затем синхронизированы с сервером.При этом, события двух типов синхронизируются отдельно.

Заключение

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

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

После определения функциональности программного продукта, были разработаны мобильное Android приложение и серверное приложение с использованием фреймворкаSpring, архитектура и детали реализации которых подробно описаны в данной работе.

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

Список использованны хисточников

  1. Mobile And Tablet Internet Usage Exceeds Desktop For First Time Worldwide [Электронныйресурс] / Statcounter. Режим доступа: http://gs.statcounter.com/press/mobile-andtablet-internet-usage-exceeds-desktop-for-first-time-worldwide, свободный.
  2. Mobile Operating System Market Share Worldwide [Электронныйресурс] / Statscounter. Режим доступа: http://gs.statcounter.com/os-market-share/mobile/worldwide, свободный.
  3. Mobile And Tablet Android Version Market Share Worldwide [Электронныйресурк] / Statscounter. Режим доступа: http://gs.statcounter.com/android-version-market-share/mobile-tablet/worldwide, свободный.
  4. ONDOC [Электронный ресурс] / GooglePlay. Режим доступа: https://play.google.com/store/apps/details?id=me.ondoc.main, свободный.
  5. Яндекс.Здоровье [Электронный ресурс] / GooglePlay. Режим доступа: https://play.google.com/store/apps/details?id=ru.yandex.med, свободный.
  6. Doc+ [Электронный ресурс] / GooglePlay. Режим доступа: https://play.google.com/store/apps/details?id=ru.doconcall.docplus, свободный.
  7. DocDoc [Электронный ресурс] / GooglePlay. Режим доступа: https://play.google.com/store/apps/details?id=com.docdoc.docdoc, свободный. (дата обращения: 05.11.18)
  8. Финтехклаб Медкарта [Электронный ресурс] / GooglePlay. Режим доступа: https://play.google.com/store/apps/details?id=biz.ftclub.healthypatient.medcard, свободный. (дата обращения: 06.11.18)
  9. Medical Note [Электронныйресурс] / Google Play. Режим доступа: https://play.google.com/store/apps/details?id=site.mons.mednote, свободный. (датаобращения: 06.02.19).
  10. You Get an Offline First App, and You Get an Offline First App, and… [Электронный ресурс] /Medium. Режим доступа: https://medium.com/offline-camp/you-get-an-offline-first-app-and-you-get-an-offline-first-app-and-5452f1cbb942, свободный. (дата обращения:02.02.19).
  11. Spring: the source for modern java [Электронныйресурс] /Spring. Режим доступа:https://spring.io, свободный. (дата обращения:01.02.19).
  12. Try Kotlin[Электронныйресурс] /Kotlin Language. Режим доступа: https://kotlinlang.org, свободный. (дата обращения: 05.02.19).
  13. TheJavaTutorials[Электронный ресурс] /Oracle. Режим доступа: https://docs.oracle.com/javase/tutorial/index.html, свободный. (дата обращения: 03.10.18).
  14. Representational state transfer [Электронныйресурс] / Wikipedia. Режим доступа:https://en.wikipedia.org/wiki/Representational_state_transfer, свободный. (дата обращения: 03.10.18).
  15. A detailed guide on developing Android apps using the Clean Architecture pattern [Электронныйресурс] / Medium. Режим доступа:https://medium.com/@dmilicic/a-detailed-guide-on-developing-android-apps-using-the-clean-architecture-pattern-d38d71e94029, свободный. (дата обращения: 05.11.18).
  16. SOLID[Электронный ресурс] /Wikipedia. Режим доступа:
    https://ru.wikipedia.org/wiki/SOLID_(объектно-ориентированное_программирование), свободный. (дата обращения: 22.12.18).
  17. Fundamentals [Электронныйресурс] / Android Developer. Режим доступа:https://developer.android.com/guide/components/fundamentals, свободный.
  18. Activity Lifecycle [Электронныйресурс] / Android Developer. Режим доступа:https://developer.android.com/guide/components/activities/activity-lifecycle, свободный.
  19. MVC, MVP and MVVM design patterns [Электронныйресурс] /. Режим доступа: https://medium.com/@ankit.sinhal/mvc-mvp-and-mvvm-design-pattern-6e169567bbad, свободный.
  20. RxJava[Электронный ресурс] /GitHub. Режим доступа: https://github.com/ReactiveX/RxJava, свободный.
  21. Reactive Programming [Электронныйресурс] / Wikipedia. Режим доступа:https://en.wikipedia.org/wiki/Reactive_programming, свободный.
  22. Observer and Observable [Электронныйресурс] /ReactiveX. Режим доступа:http://reactivex.io/documentation/observable.html, свободный.
  23. SQLite[Электронный ресурс] /Wikipedia. Режим доступа:https://ru.wikipedia.org/wiki/SQLite, свободный.
  24. StorIO[Электронный ресурс] /GitHub. Режим доступа:https://github.com/pushtorefresh/storio, свободный.
  25. Builder Pattern [Электронныйресурс] / Wikipedia. Режим доступа: https://en.wikipedia.org/wiki/Builder_pattern, свободный.
  26. Retrofit[Электронный ресурс] /GitHub. Режим доступа:https://square.github.io/retrofit/, свободный.
  27. Scarlet[Электронный ресурс] /GitHub. Режим доступа: https://github.com/Tinder/Scarlet, свободный.
  28. MySQLDocumentation[Электронный ресурс] /MySQL. Режим доступа: https://dev.mysql.com/doc, свободный.
  29. OAuth 2 [Электронный ресурс] /OAuth. Режим доступа: https://oauth.net/2/, свободный.
  30. Don’t Repeat Yourself [Электронныйресурс] / Wikipedia. Режим доступа:https://ru.wikipedia.org/wiki/Don’t_repeat_yourself, свободный. (дата обращения: 01.11.18).
  31. OkHttp[Электронный ресурс] /GitHub. Режим доступа:https://square.github.io/okhttp/, свободный.
  32. Multi-platformopen-sourcevideoconferencing[Электронный ресурс] /Jitsi. Режим доступа: https://jitsi.org, свободный.
  33. UUIDCollisions[Электронный ресурс] /Wikipedia. Режим доступа:https://en.wikipedia.org/wiki/Universally_unique_identifier#Collisions, свободный.
  34. Finite-state Machine [Электронныйресурс] / Wikipedia. Режим доступа: https://en.wikipedia.org/wiki/Finite-state_machine, свободный.
  35. Picasso[Электронный ресурс] /GitHub. Режим доступа: https://square.github.io/picasso/, свободный.