О системе
О компании
Цены
Наши статьи
Наши клиенты
ООО «Результат»
Введение
ORM или ДОБД
Стереотипы поведения и навыки адаптации объектов в документоориентированной БД
ДОБД на SQL
Доменный принцип построения системы
Иерархия документов в журнале
Пример построения сложной СЭД на основе RSF
Статья о фрейморке Result-Systems
в журнале Linux Format № 2 (180) 2014.
Публикуется с разрешения редакции.

Печальное будущее толстых клиентов или
нетрадиционная ориентация веб-браузеров



Толстый и богатый клиент всегда вызывает большее уважение, чем тонкий и бедный [2][3].

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

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

Однако время идет, серверные технологии, усложняясь, упрощают программирование серверов приложений. Технология «клиент — sql-сервер» вытесняется технологией «клиент — сервер приложений — sql-nosql-серверы». Сила толстых превращается в бесполезный жирок: настройки и обновления проще делать на сервере, туда же перетекают запросы к базе и обработка данных.

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

Есть еще один важный момент. Тонкий клиент — экономный клиент (lean client)[3]. Разработка толстого клиента дело дорогое: языки более дорогие, инструменты недешевые, специалистов найти сложнее. Для тонкого клиента в свою очередь усложняется серверное программирование.


Скептики говорят: «Звучит красиво. А вы сделайте и покажите».




Сделали, показываем.


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

Постановка задачи:

  1. Есть многоуровневые госструктуры, и в каждой есть много разных журналов с документами.

  2. С документом что-то делается, это должно сохраняться в журнале.

  3. На основании одного документа могут создаваться новые документы. Такие документы должны быть связаны.

  4. Если документ отправляют в другое подразделение (в другую организацию), он должен копироваться в журнал подразделения (организации).

  5. Еще 228 требований по мелочи.



Проектируем систему:

Берем за основу доменную структуру (у каждой организации и у каждого подразделения свое доменное имя), добавляем к домену алиас журнала, — получаем доступ к журналу. Добавляем GUID (uuid) документа, — получаем доступ к документу. Домен пишем слева направо, чтобы не путать с интернетом.

Пример:

RF.TVR.MF/DP_2013журнал «Входящие 2013» Министерства финансов Тверской области

RF.TVR.MF/DP_2013&BABAD9A62B0B5A1016627D7FC005F176 — документ в этом журнале.

Тонкому клиенту в большинстве случаев этого вполне достаточно:

он может открыть журнал

http://result-systems.ru/dbopen?RF.TVR/OG_2014

или документ

http://result-systems.ru/docopen?RF.TVR/OG_2014&57ED03385E2E5D061BE0C2224D82F558



Проектируем базу данных:

В крупном министерстве регистрируют более 1000 разных документов день. Общее количество документов за несколько лет измеряется миллионами, плюс каждый документ имеет историю (кто был исполнителем, связи с другими документами и т. д.). Общее количество записей в базе приближается к сотне миллионов. Делаем распределенную базу, в основание базы кладем журнал документов. Распределяем журналы по sql серверам и получаем быстрое и современное решение.

Журнал — это именованная сущность (sql или nosql таблица). Много журналов — значит много таблиц. В журнале документы — это главные записи журнала, в каждой записи много полей (реквизиты документа и вложения). Добавляем к документу подчиненные записи (весь жизненный цикл документа вплоть до вечной жизни в оцифрованном архиве или до бесславной смерти в бумагорубящем чудовище) и получаем то, что просили в п.1,2,3 ТЗ.



Проектируем документооборот.

В ГОСТе на делопроизводство есть очень важное понятие: «Отметка о передаче» (иногда ее называют «Переметка»). Отправил документ на рассмотрение шефу — сделай в журнале отметку, шеф расписал документ троим исполнителям — сделай три отметки, отправил проект на согласование (не важно, электронно или на бумаге), — отметка о передаче должна быть.

Такой подход понятен и людям, и серверу. Человек заполняет в переметке поле «Кому направлено» и нажимает кнопку «сохранить». Сервер после сохранения переметки ищет в справочнике, кому же это направлено, находит и проверяет домен получателя. Если домен тот же, что и домен журнала, в котором все происходит, отправлять не надо. Если в домене получателя свои журналы не ведутся, отправлять не надо. Если все-таки отправлять надо, сервер выбирает в домене получателя наиболее подходящий журнал и аккуратно копирует в него документ вместе с отметкой передаче.

Если у получателя указан e-mail, сервер отправляет документ вместе с переметкой по электронной почте. Во-первых, это модно, во-вторых, есть в рекомендациях MoReq2010 (европейские требования к СЭД). Когда коллеги возмутятся: «Уберите же наконец этот спам», — можно будет отменить отправку.

Кроме отметок о передаче добавим в документооборот обмен ссылками на документы. Когда создается связанный документ (п. 3 ТЗ), у документа-основания появляется ссылка. Эта ссылка должна копироваться во все журналы, куда/откуда ранее отправлялся документ. Увидев такую ссылку пользователь, возможно, не сможет ее открыть, т. к. у него нет прав на тот журнал, где находится документ, но все равно пусть видит.

Простой и красивый документооборот, но не всемирный. Чтобы сделать его всемирным необходимо и достаточно научить сервер приложений обмениваться со своими собратьями по документообороту в сети. Т.е. сделать аналог DNS для разрешения (в смысле resolving) внешних документо-доменных имен. Если домен получателя в зоне ответственности сервера, он сам формирует запрос к базе, если нет, пусть найдет в таблице документо-доменов адрес собрата и отправит ему документ с переметкой (или ссылку). Для таких целей в Python есть сетевой фреймворк Twisted, а можно самим написать.



Проектируем тонкого клиента.

Небольшое отступление о всплывающих окнах при работе с клавиатурой. JS [5] событийно-ориентированный язык: произошло событие, - выполнилась функция. Если событие инициировано пользователем (мышь, палец, клавиатура), то обработчик события может открыть новое окно и оно не будет считаться всплывающим. Если окно открывается по таймеру или по окончанию сетевой операции, оно всплывающее и его следует заблокировать. С мышкой (пальцем) так и происходит, с клавиатурой все сложнее. Mozilla Firefox (МФ) и Google Chrome (ГХ) ведут себя по разному, объединяет их одно: раз w3c не прописал, когда надо блокировать всплывающие окна, значит, все на наше усмотрение (по keyDown – блокирую, по keyPress – не блокирую, но Alt-цифра все равно блокирую и т. д.). Разработчики МФ и ГХ сами отучили нас от танцев с бубнами: мы делаем так, как прописано в рекомендациях и, как правило, в обоих браузерах все работает. В данном случае мы просто советуем разрешить всплывающие окна для конкретного сервера.

К чему это отступление? К тому, что браузеры пока что ориентированы исключительно на работу с интернетом. Использование их в качестве рабочего места для работы с базой данных требует от программистов определенного напряжения. Мы надеемся, что со временем разработчики браузеров и w3c разберутся с всплывающими окнами и дадут на этот счет четкие рекомендации. Пока что можно написать свой браузер, что мы и сделали, но это уже другая история.

Все, что мы хотим показать, работает на любом браузере, кроме Microsoft Internet Explorer (ИЕ). Это не ограничение, — это разумная достаточность. Дело в том, что навигация в интернете и профессиональная работа с базой принципиально отличаются, и лучше, если для этого будут использоваться разные приложения. Если Вы поклонник МФ, используйте для работы с базой ГХ или Оперу. Если работаете с хромом, - скачайте Оперу или МФ. Если у Вас ИЕ, значит, Вам повезло и у Вас выбор богаче.

Еще одно отступление. Мы (как и все) видели множество интерфейсов и безусловно использовали удачные решения. Если какие-то элементы дизайна защищены авторскими правами, а мы, не спросив разрешения, сделали свой дизайн похожим, значит, мы не правы, готовы извинится, обсудить и т. д. В свою очередь мы готовы делиться своими решениями. Если кто-то захочет использовать наши наработки, — ради бога, а если этот кто-то еще и сошлется на нас, мы будем благодарны.



  1. Интерактивная работа с журналом.

Из множества вариантов представления документов в виде списка нам приглянулся Lotus Notes. Почтовики и поисковики показывают список с разбивкой на страницы. Это удобно, если вас интересуют первые в списке документы. При работе с базой должен быть быстрый доступ к любому документу, поэтому мы выводим сразу весь список, точнее скользящее окно с динамической подкачкой в буфер (ничего нового). Выглядит это так:


Журнал имеет несколько представлений: по номеру, по заявителю, по исполнителю и т.д.
В примерах нет реальных персональных данных. Любые совпадения случайны.

Для пользователя полный сервис: выбрал нужную категорию (Иванов Иван Иванович) — в списке отфильтровались только нужные документы, документы можно выбирать мышкой или с клавиатуры, быстрый поиск по первым буквам, сложный поиск тоже есть, для кнопок назначены сочетания клавиш. Редкий толстый настолько удобен.

Однако не все так просто.

  • Быстродействие представления. Разработчики Lotus Notes добились удивительных результатов. Список из миллиона документов работает почти мгновенно. Чтобы приблизиться к ним по скорости, нам пришлось сделать дополнительные индексы и значительно усложнить базу. Соответственно увеличились задержки при сохранении документа, но это проблема не тонкого клиента.

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

  • Многобазовый поиск. Я говорю многобазовый, потому что журналы могут находиться в разных базах на разных sql и даже nosql серверах. Если пользователь при поиске по атрибутам указал «искать во всех журналах», серверу предстоит выполнить несколько запросов, сформировать по результатам сводку и вернуть ее клиенту в виде мини-журнала. Это не просто, но, если сделать красиво, пользователю понравится.

  • Всплывающие окна. Если мне нужно создать отметку о передаче в новом окне, я встаю на нужный документ и нажимаю <Alt-2> (кнопка «Перем»). Получаю всплывающее окно, потому что такой пока браузер. Я об этом уже писал и, надеюсь, со временем проблема решится.

  • При разработке тонкого клиента хочется побольше функциональности возложить на сервер. Это не всегда удается, и у тонкого клиента начинает расти яваскриптовый животик. Предположим, я хочу создать связанный документ (п. 3 ТЗ) и нажимаю <Alt-3> (кнопка «СД»). Если для данного документа прописано несколько шаблонов, я должен увидеть диалог с предложением выбрать шаблон. Тут все просто: выбрал шаблон, нажал «Enter», - открылось окно с новым документом и никакого всплытия. А если шаблон один? В этом случае диалог не нужен, нужно сразу открыть новое окно. Вот здесь и проблема. Клиент отправляет XHR [4] на сервер, чтобы сервер вернул список шаблонов. Сервер возвращает один шаблон, клиент определяет, что диалог не нужен, открывает новое окно и получает от браузера совершенно законную блокировку всплывающего окна (открытие окна при завершении сетевой операции). Некрасиво. Выход простой: поручить разбираться с шаблонами клиенту. Загружаем в JS-переменную справочник шаблонов и храним ее в окне. На несколько килобайт больше, на несколько XHR меньше, — мелочь, но такой мелочи набегает довольно много, и разработчикам надо быть готовым к тому, что каждое окно будет набито JS-переменными.



  1. Программирование экранных форм.

Ввод текста.

Пользователю при вводе текста вполне достаточно, чтобы область ввода автоматически расширялась вниз. Сделать это можно по разному:

  • использовать моноширный шрифт, вычислять при вводе длину текста и менять высоту TextArea. Просто, но моноширный шрифт некрасив. Вычеркиваем.

  • Использовать для ввода <td> с атрибутом «contenteditable». Просто и красиво, правда со своими фокусами: пользователь может вводить любой текст, в том числе html-теги. Чтобы все работало корректно потребуется JS. Мы рекомендуем этот метод, хотя сами его не используем.

  • При вводе в TextArea сравнивать scrollHeight и clientHeight и менять высоту области ввода. Мы используем этот способ давно (когда contenteditable еще не было), он хорошо подходит для небольших полей. Если поле содержит сотни строк, браузеры начинают тормозить.

Работа со справочниками.

Списковые поля помечены символом "▼" (справа от области ввода). При наборе текста на клавиатуре по первым буквам автоматически появляются значения из справочника. Для вызова справочника нажимаем "Enter", находясь в поле ввода. Открывается окно, в котором показаны список и еще одна область ввода. Если набрать на клавиатуре текст, произойдет фильтрация списка по первым буквам для всех слов. На рисунке показано, как отфильтровался список после ввода букв «фи».




При выборе из списка главное — не забыть поставить галочку напротив фамилии, клавишей пробела или мышью.

Если поле допускает множественное значение (как на картинке), при вводе с клавиатуры разделителями значений служат <запятая> или <Shift-Enter>. При выборе значений из списка программа запоминает, что выбрано первым и заносит значения в поле не по алфавиту, а по очередности выбора.



Ввод даты

20 лет назад в Clipper'е и FoxPro дату было удобнее вводить, чем в современных браузерах. Человек работает с базой, часто в конвейерном режиме. Ему некогда мышкой выбирать день и месяц, он вводит дату с клавиатуры и без разделителей. А если нужен календарь, он должен открывать его «Enter'ом» и «Enter'ом» закрывать. Совершенно очевидные требования, но из множества просмотренных календарей мы не смогли найти то, что хотели. Наиболее удобное поле «Дата» в ГХ (<input type="date">), но: в МФ оно не такое; календарь нельзя вызвать программно; таймаут на клавиатуре очень маленький, если вводить дату не спеша, браузер сбросит набор и начнет ввод с начала.

Мы взяли за образец календарь ГХ (таймаут увеличили до 0.5с) и написали свой. Html из 10 строчек и 700 строк JS. Зато получили удобное и красивое поле с календарем, в котором обеспечена проверка даты, значения по умолчанию и пр. Пиктограмма справа от поля — это не рисунок, это текущая дата. Если введена недействительная дата, скрипт не дает выйти из поля, мигает красным и тихонько хрюкает в динамик. ГХ долго не хотел хрюкать, но начиная с какой-то версии одумался и сейчас ведет себя правильно.


Отличие профессиональной системы от полупрофессиональной — в мелочах,
да только мелочей таких много. Календарь — одна из них.

 

 

Вложения

Делаем скрытый элемент для загрузки файлов (<input type="file" multiple id="file" style="display:none"/>) и в нужный момент открываем файл-диалог: document.getElementById('file').click(); и ждем события "change". Для перетаскивания файлов мышкой надо добавить обработчики на события window 'dragover' (return false) и на 'drop' (получить информацию о файлах из event.dataTransfer.files). После того, как информация о файлах получена, ее надо отобразить в форме. Почему-то в свойствах файла (объект File) отсутствует дата создания. Странно, но факт.



Первое, что сказал пользователь, посмотрев на список файлов:
«А почему нет всплывающих эскизов страниц?». Красота безгранична...

Загрузка файлов на сервер делается перед сохранением документа с использованием объекта FormData методом XHR — POST. Для отображения прогресс-бара у XHR имеется событие 'progress', передающее обработчику общее количество байт (event.total) и сколько загружено (event.loaded).

На сервере вложения могут загружаться в базу в виде блобов или записываться на диск в виде файлов. Тесты с MySQL и PostgreSQL показали, что с файлами сервер работает быстрее. Возможно, это свойство именно нашей системы (мы работаем с базой через Python DB API 2.0). Попытка загрузить на сервер 8 Гб привела к зависанию МФ еще до начала загрузки.

Если вы проектируете команды сервера надо помнить, что при скачивании файла браузер определяет имя файла из URL между слешем ('/') и знаком вопроса.

...mmm.ru/download/myScript.txt?RF/OG_2013&3510A... имя файла myScript.txt

...mmm.ru/download?myScript.txt?RF/OG_2013&3510A... неправильно



Пример сложной формы

Самой сложной формой в системе оказался диалог для связывания документов. Пользователь создает новый документ, нажимает в форме кнопку «СД» и выбирает сначала журнал, затем документ в журнале. Кнопка "СД" в форме работает более гибко, чем кнопка "СД" в представлении можно выбрать журнал для основания и просмотреть основание, не выходя из формы. В одних случаях удобней пользоваться кнопкой "СД" в представлении, в других удобней из формы.

Работу с основаниями проектировали Енаева С.Х. (Москва, почетный член гильдии ДОУ), Семенова Е.В. (Тверь) и мы тоже.

Форма состоит из 4 фреймов (iframe) и нескольких кнопок. Под кнопками расположен фрейм с таблицей, в таблице список выбранных оснований. Если выбрать мышкой основание, у главного фрейма меняется URL, и вместо списка документов в нем отображается документ-основание. Пользователь может убедиться в том, что это нужный документ, просмотрев его реквизиты или открыв вложения.

При просмотре основания мы используем масштабирование (CSS3 transform). И МФ, и ГХ отрабатывают идеально.

Фреймы и кнопки взаимозависимы, обработка событий достаточно сложная, но могу уверенно заявить, что разработка такой формы в толстом клиенте не будет проще. Кроме того, имея в своем распоряжении html, Javascript и css мы, возможно, имеем больше свободы в выборе элементов интерфейса, чем программист толстого клиента.

  1. Аутентификация и стартовая страница

Мы используем для ввода пароля стандартный механизм браузера с Digest аутентификацией (в справочнике пользователей хранятся MD5 хэш-суммы паролей). Решение простое, но не идеальное. Пароль из 6 символов ломается методом перебора (brute force) за несколько часов. Кроме того и МФ, и ГХ при аутентификации не признают кириллицу, что крайне неудобно, т.к. нельзя исполнителя просто сделать пользователем. Более перспективно взять готовое решение (Tornado или что-то другое).

При работе через https мы столкнулись со следующим:

  • соединение через https вызывает ощутимые задержки, независимо от длины ключа;

  • бдительные браузеры очень недоверчиво относятся к самоподписанным сертификатам и нервируют пользователя сообщениями о том, что серверу нельзя доверять;



После того, как прошла аутентификация, выполняется авторизация. Сервер определяет, к какой группе принадлежит пользователь, и открывает соответствующую стартовую html-страницу. Для самого главного пользователя она может выглядеть так:

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

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



      1. Основные события при работе с формами и полями.

Опять же, ничего нового:

  • beforeOpen — скрипт выполняется на сервере. Для нового документа заполнение полей по умолчанию, для любого документа заполнение вычисляемых полей.

  • postOpenDoc – выполняется на клиенте. Все формы разные, у некоторых своя специфика, - без своего скрипта не обойтись. Сначала мы назвали функцию postOpen, она поработала и перестала. Оказалось, что в МФ появилась своя функция postOpen. Чем лучше вы знаете английский и чем правильней назовете функцию (переменную), тем больше вероятность вступить в конфликт с JS-именами браузера.

  • setAndVerify(true) – выполняется на клиенте. Заполняет вычисляемые поля в форме.

  • setAndVerify(false) – выполняется на клиенте. Проверяет значения полей в форме.

  • beforeSave — выполняется на сервере. Обработка документа перед сохранением.

  • afterSave — выполняется на сервере. Действия с базой после сохранения документа.

  • recalc – пересчет полей. Это событие поля, оно возникает, когда поле меняет значение. Пример: в форме есть поле «Подразделение», а под ним поле «Исполнитель». Оба поля списковые. Когда выбираем подразделение, справочник исполнителей должен обновиться, чтобы в нем были исполнители выбранного подразделения. Исполнителей тысячи, всех в форму не загрузишь, - без XHR не обойтись.

Заключение

Приведенный пример системы это не картинки с выставки. Это реально работающая система, которую можно посмотреть и потрогать на сайте http://result-systems.ru. В системе даже есть графическая капча, которая строится по точкам:

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

Вообще-то мы рисовали с помощью <canvas> почтовый индекс на конверте, а получилась капча. Индекс тоже получился, но кому это интересно?



Всё, что делает наша система, может сделать любой толстый клиент. Или не любой. Или не всё. Или не может.



Алексей Олегович Носиков, делопроизводитель

aon24@mail.ru

моб: +7-921-9935515



  1. http://result-systems.ru

  2. http://en.wikipedia.org/wiki/Fat_client

  3. http://en.wikipedia.org/wiki/Thin_client

  4. http://ru.wikipedia.org/wiki/XMLHttpRequest

  5. http://ru.wikipedia.org/wiki/JS

 




Яндекс.Метрика