Создает идентификатор сообщения из указанной строки. Данный конструктор можно использовать, если нужно произвести действие над сообщением, идентификатор которого хранится в информационной базе в виде строки. Корректность идентификатора не проверяется.
Синтаксис
Новый ИдентификаторСообщенияСистемыВзаимодействия(<Строка>)Полное описание, параметры и ограничения
ИдентификаторСообщенияСистемыВзаимодействия.По умолчанию
ИдентификаторСообщенияСистемыВзаимодействия (CollaborationSystemMessageID)
По умолчанию
Доступен, начиная с версии 8.3.11.
Синтаксис:
Новый ИдентификаторСообщенияСистемыВзаимодействия(<Строка>)
Параметры:
<Строка> (обязательный)
Тип: Строка.
Строковое представление идентификатора.
Описание:
Создает идентификатор сообщения из указанной строки.
Данный конструктор можно использовать, если нужно произвести действие над сообщением, идентификатор которого хранится в информационной базе в виде строки.
Корректность идентификатора не проверяется.
Использование в версии:
Доступен, начиная с версии 8.3.11.
Методическая информация
Практические примеры
Поток своей сохраненной карточки: Ждать и callback с UUID-запросом
Полный учебный маршрут создает реальное собственное обсуждение и сообщение с двумя UTF-8 вложениями. Работает с коллекцией как редактором нового сообщения, затем отдельно читает серверный результат и сравнивает байты через sync, Ждать и ОписаниеОповещения.
Где и как запускать
Полный модуль собственной управляемой формы, толстый клиент/управляемое приложение. Серверный поддерживающий модуль УчебныеВложения85 обязателен и приведен полностью. Каждый модуль вставляется отдельно в свое место, не склеивается.
Подготовка
Используйте отдельную VM, платформу8.5.1.1343 и новую пустую конфигурацию УчебноеВзаимодействие85: русский язык, управляемый интерфейс, совместимость Не использовать. Предметных объектов/регламентных заданий/подписок/расширений нет. Не запускайте в УНФ/КА/УТ/БП или на рабочем сервере.
Вручную разверните отдельный собственный сервер взаимодействия по документации своей серверной поставки. Единственный разрешенный адрес регистрации http://localhost:9094; никаких внешних мессенджеров, SMTP, общего абонента или публичной публикации. Адрес с другим хостом намеренно не принимается.
На этом же стенде вручную подключите внешнее файловое хранилище только с собственными учебными учетными данными и локальной конечной точкой localhost в этой VM. Создайте собственный пустой учебный бакет; не используйте рабочие S3-ключи/бакеты. Формат серверного конфигурационного файла зависит от отдельной поставки сервера и здесь не придумывается.
В справке ПоддерживаетсяРаботаСВложениями означает подключение файлового хранилища, а не его доступность по сети или достаточную квоту. Локальные разрешения/вместимость/срок хранения проверьте вручную; отключите короткое удаление только на своем пустом стенде. Без хранилища код отказывает до создания обсуждения.
В конфигураторе создайте ровно одного своего пользователя Уч85Связь_<ваш новый канонический ненулевой UUID>, полное имя Учебный участник. Права назначаются вручную только в собственном стенде. Войдите под ним; его реальный IB UUID будет сверяться в обе стороны с реальным ID системы взаимодействия.
Вручную зарегистрируйте свое приложение под именем УчебноеВзаимодействие85. Свой пользователь внутренний, не заблокирован, без внешнего ID/почты/телефона/ключа сопоставления. Он должен совпадать с реальным пользователем базы. Ни аккаунты, ни права этот образец не создает.
Создайте серверный общий модуль УчебныеВложения85, только флаг Сервер; Привилегированный выключен. Вставьте главный модуль полностью. Здесь включены проверка пустой среды, паспорт своего пользователя/приложения, создание данных и проверка потока.
Создайте свою внешнюю обработку с управляемой формой и реквизитом Подтверждение типа Строка длиной100. Добавьте команды Подготовить, ПрочитатьЖдать, ПрочитатьЧерезОповещение и назначьте одноименные обработчики. В модуль формы вставьте полный поддерживающий код. Установите реквизит формы Подтверждение в доступное поле.
Откройте форму в толстом клиенте, управляемое приложение. Сам AsyncOpenStream доступен также thin/web, но полный маршрут сознательно использует толстый клиент для синхронного GetMessage/получения обсуждения; серверный модуль не переносится на клиент.
Сначала оставьте Подтверждение пустым: подготовка должна завершиться исключением без создания обсуждения. Закройте и заново откройте форму; после прочтения предупреждений введите СВОИ ДВА ВЛОЖЕНИЯ и нажмите Подготовить один раз. Флаг повторного запуска выставляется до серверного вызова.
Оставьте только этот собственный сеанс и не меняйте созданную карточку параллельно. Вложения действительно передаются в свое файловое хранилище при ручном запуске. Метод Очистить показан только на отдельном новом неотправленном черновике, а не на сохраненном сообщении.
После ошибки не нажимайте подготовку повторно и не удаляйте данные автоматически. Сохраните показанный UUID-кандидат ключа, проверьте возможный частичный результат вручную в своем сервере. Этот текст и статические тесты не запускали 1С, не создавали файлов/учетных записей и не передавали вложений.
Перем СвойПакет85;
Перем НачатОпыт85;
Перем ЗапросЧтения85;
Перем Занято85;
&НаКлиенте
Процедура Подготовить(Команда)
#Если ТолстыйКлиентУправляемоеПриложение Тогда
Если НачатОпыт85 = Истина Тогда
ВызватьИсключение "Повтор подготовки запрещен; проверьте возможный частичный результат вручную";
КонецЕсли;
НачатОпыт85 = Истина;
СвойПакет85 = ПодготовитьНаСервере85();
Сообщить("Созданы и проверены свои два вложения. Доступны кнопки Ждать и ЧерезОповещение");
#Иначе
ВызватьИсключение "Полный образец использует синхронный GetMessage: нужен толстый управляемый клиент";
#КонецЕсли
КонецПроцедуры
&НаСервере
Функция ПодготовитьНаСервере85()
Возврат УчебныеВложения85.ПодготовитьСвоиВложения85(Подтверждение);
КонецФункции
&НаКлиенте
Асинх Процедура ПрочитатьЖдать(Команда)
#Если ТолстыйКлиентУправляемоеПриложение Тогда
Если Занято85 = Истина Тогда
ВызватьИсключение "Предыдущее чтение еще не закончено";
КонецЕсли;
Вложение = ПолучитьСвоюКарточку85();
ОжидаемыеБайты = ПолучитьДвоичныеДанныеИзСтроки(СвойПакет85.Ключ + "|Книга|3|250|750", "UTF-8", Ложь);
Занято85 = Истина;
Попытка
ПотокТела = Ждать Вложение.ОткрытьПотокДляЧтенияАсинх();
ПроверитьИЗакрытьСвойПоток85(ПотокТела, ОжидаемыеБайты);
Исключение
Занято85 = Ложь;
ВызватьИсключение;
КонецПопытки;
Занято85 = Ложь;
Сообщить("Ждать вернул настоящий поток: все байты своей карточки совпали");
#Иначе
ВызватьИсключение "Нужен толстый управляемый клиент";
#КонецЕсли
КонецПроцедуры
&НаКлиенте
Процедура ПрочитатьЧерезОповещение(Команда)
#Если ТолстыйКлиентУправляемоеПриложение Тогда
Если Занято85 = Истина Тогда
ВызватьИсключение "Предыдущее чтение еще не закончено";
КонецЕсли;
Вложение = ПолучитьСвоюКарточку85();
ЗапросЧтения85 = Строка(Новый УникальныйИдентификатор);
Параметры = Новый Структура("Запрос,Форма,Ключ", ЗапросЧтения85, Строка(УникальныйИдентификатор), СвойПакет85.Ключ);
Оповещение = Новый ОписаниеОповещения("СвойПотокОткрыт85", ЭтотОбъект, Параметры, "ОшибкаСвоегоПотока85", ЭтотОбъект);
Занято85 = Истина;
Попытка
Вложение.НачатьОткрытиеПотокаДляЧтения(Оповещение);
Исключение
Занято85 = Ложь;
ВызватьИсключение;
КонецПопытки;
#Иначе
ВызватьИсключение "Нужен толстый управляемый клиент";
#КонецЕсли
КонецПроцедуры
&НаКлиенте
Процедура СвойПотокОткрыт85(ПотокТела, ДополнительныеПараметры) Экспорт
Если ДополнительныеПараметры.Форма <> Строка(УникальныйИдентификатор)
Или ДополнительныеПараметры.Запрос <> ЗапросЧтения85
Или СвойПакет85 = Неопределено Или ДополнительныеПараметры.Ключ <> СвойПакет85.Ключ Тогда
Если ТипЗнч(ПотокТела) = Тип("Поток") Тогда
ПотокТела.Закрыть();
КонецЕсли;
Возврат;
КонецЕсли;
Занято85 = Ложь;
ОжидаемыеБайты = ПолучитьДвоичныеДанныеИзСтроки(ДополнительныеПараметры.Ключ + "|Книга|3|250|750", "UTF-8", Ложь);
ПроверитьИЗакрытьСвойПоток85(ПотокТела, ОжидаемыеБайты);
Сообщить("Оповещение передало настоящий поток: все байты своей карточки совпали");
КонецПроцедуры
&НаКлиенте
Процедура ОшибкаСвоегоПотока85(ИнформацияОбОшибке, ДополнительныеПараметры) Экспорт
Если ДополнительныеПараметры.Форма = Строка(УникальныйИдентификатор)
И ДополнительныеПараметры.Запрос = ЗапросЧтения85 Тогда
Занято85 = Ложь;
Сообщить("Открытие своего потока не завершилось; сырые сведения об ошибке не выводятся");
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Функция ПолучитьСвоюКарточку85()
#Если ТолстыйКлиентУправляемоеПриложение Тогда
Если СвойПакет85 = Неопределено Тогда
ВызватьИсключение "Сначала создайте собственную карточку через Подготовить";
КонецЕсли;
Если Не СистемаВзаимодействия.ИнформационнаяБазаЗарегистрирована()
Или СистемаВзаимодействия.АдресСервера() <> "http://localhost:9094"
Или Строка(СистемаВзаимодействия.ИдентификаторТекущегоПользователя()) <> СвойПакет85.Автор Тогда
ВызватьИсключение "Собственный сервер или текущий пользователь изменен";
КонецЕсли;
ИДОбсуждения = Новый ИдентификаторОбсужденияСистемыВзаимодействия(СвойПакет85.Обсуждение);
Обсуждение = СистемаВзаимодействия.ПолучитьОбсуждение(СвойПакет85.Ключ);
Если Обсуждение = Неопределено Тогда
ВызватьИсключение "Собственное обсуждение не найдено";
КонецЕсли;
Если Обсуждение.Идентификатор <> ИДОбсуждения Или Обсуждение.Ключ <> СвойПакет85.Ключ
Или Обсуждение.Интеграция <> Неопределено Или Обсуждение.КонтекстОбсуждения <> Неопределено
Или Обсуждение.Участники.Количество() <> 1
Или Не Обсуждение.Участники.Содержит(СистемаВзаимодействия.ИдентификаторТекущегоПользователя()) Тогда
ВызватьИсключение "Состав собственного обсуждения изменен";
КонецЕсли;
ИДСообщения = Новый ИдентификаторСообщенияСистемыВзаимодействия(СвойПакет85.Сообщение);
Сообщение = СистемаВзаимодействия.ПолучитьСообщение(ИДСообщения);
Если Сообщение.Идентификатор <> ИДСообщения Или Сообщение.Обсуждение <> ИДОбсуждения
Или Строка(Сообщение.Автор) <> СвойПакет85.Автор Или Сообщение.Данные <> СвойПакет85.Ключ + "|Книга|3|250|750"
Или Сообщение.Вложения.Количество() <> 2 Тогда
ВызватьИсключение "Паспорт сохраненной собственной карточки изменен";
КонецЕсли;
Вложение = Сообщение.Вложения.Получить(0);
ОжидаемыеБайты = ПолучитьДвоичныеДанныеИзСтроки(СвойПакет85.Ключ + "|Книга|3|250|750", "UTF-8", Ложь);
Если Вложение.Наименование <> СвойПакет85.Ключ + "-card.txt" Или Вложение.ТипСодержимого <> "text/plain"
Или Не Вложение.Отображаемое Или Вложение.СодержимоеУдалено Или Вложение.Размер <> ОжидаемыеБайты.Размер() Тогда
ВызватьИсключение "Собственное вложение изменено или уже удалено хранилищем";
КонецЕсли;
Возврат Вложение;
#Иначе
ВызватьИсключение "Нужен толстый управляемый клиент";
#КонецЕсли
КонецФункции
&НаКлиенте
Процедура ПроверитьИЗакрытьСвойПоток85(ПотокТела, ОжидаемыеБайты)
Если ТипЗнч(ПотокТела) <> Тип("Поток") Тогда
ВызватьИсключение "Ожидался реальный поток вложения";
КонецЕсли;
Попытка
Если Не ПотокТела.ДоступноЧтение Тогда
ВызватьИсключение "Поток вложения недоступен для чтения";
КонецЕсли;
ОжидаемыйБуфер = ПолучитьБуферДвоичныхДанныхИзДвоичныхДанных(ОжидаемыеБайты);
Буфер = Новый БуферДвоичныхДанных(128);
Смещение = 0;
Пока Истина Цикл
Прочитано = ПотокТела.Прочитать(Буфер, 0, 128);
Если Прочитано = 0 Тогда
Прервать;
КонецЕсли;
Если ТипЗнч(Прочитано) <> Тип("Число") Или Прочитано < 0 Или Прочитано > 128
Или Смещение + Прочитано > ОжидаемыеБайты.Размер() Тогда
ВызватьИсключение "Длина потока собственного вложения неверна";
КонецЕсли;
Для Индекс = 0 По Прочитано - 1 Цикл
Если Буфер[Индекс] <> ОжидаемыйБуфер[Смещение + Индекс] Тогда
ВызватьИсключение "Байты собственного вложения отличаются";
КонецЕсли;
КонецЦикла;
Смещение = Смещение + Прочитано;
КонецЦикла;
Если Смещение <> ОжидаемыеБайты.Размер() Тогда
ВызватьИсключение "Поток собственного вложения завершился преждевременно";
КонецЕсли;
Исключение
ПотокТела.Закрыть();
ВызватьИсключение;
КонецПопытки;
ПотокТела.Закрыть();
КонецПроцедурыПолный серверный общий модуль УчебныеВложения85: собственные реальные данные и все проверки
Собственная конфигурация → Общие → Общие модули → УчебныеВложения85.
Серверный общий модуль собственного пустого учебного стенда, Привилегированный выключен.
Функция ПодготовитьСвоиВложения85(Подтверждение) Экспорт
Пользователь = ПроверитьСвоегоПользователя85();
Если Подтверждение <> "СВОИ ДВА ВЛОЖЕНИЯ" Тогда
ВызватьИсключение "Сначала подготовьте свой стенд. Ничего не создано";
КонецЕсли;
Если Не СистемаВзаимодействия.ПоддерживаетсяРаботаСВложениями() Тогда
ВызватьИсключение "На своем сервере нет внешнего файлового хранилища";
КонецЕсли;
Ключ = "Уч85Вложения_" + Строка(Новый УникальныйИдентификатор);
Сообщить("Свой кандидат обсуждения: " + Ключ);
Если СистемаВзаимодействия.ПолучитьОбсуждение(Ключ) <> Неопределено Тогда
ВызватьИсключение "Ключ занят. Существующие данные не изменяются";
КонецЕсли;
Обсуждение = СистемаВзаимодействия.СоздатьОбсуждение();
Обсуждение.Ключ = Ключ;
Обсуждение.Заголовок = Ключ + " | Книга: 3 по 250";
Обсуждение.Групповое = Истина;
Обсуждение.Отображаемое = Истина;
Обсуждение.ТолькоПросмотр = Ложь;
Обсуждение.КонтекстОбсуждения = Неопределено;
Обсуждение.Интеграция = Неопределено;
Обсуждение.ВнешнийИдентификатор = "";
Обсуждение.Участники.Добавить(Пользователь.Идентификатор);
Обсуждение.Записать();
ИДОбсуждения = Обсуждение.Идентификатор;
ПроверитьСвоеОбсуждениеВложений85(ИДОбсуждения, Ключ, Пользователь.Идентификатор);
Сообщение = СистемаВзаимодействия.СоздатьСообщение(ИДОбсуждения);
Сообщение.Текст = Ключ + " | Книга: 3 по 250 = 750";
Сообщение.Данные = Ключ + "|Книга|3|250|750";
Сообщение.ФорматДанныхСтроки = "text/plain";
Сообщение.Получатели.Добавить(Новый ИдентификаторПользователяСистемыВзаимодействия);
Если Сообщение.Вложения.Количество() <> 0 Тогда
ВызватьИсключение "Новый объект сообщения уже содержит вложения";
КонецЕсли;
БайтыКарточки = ПолучитьДвоичныеДанныеИзСтроки(Ключ + "|Книга|3|250|750", "UTF-8", Ложь);
БайтыПроверки = ПолучитьДвоичныеДанныеИзСтроки(Ключ + "|Проверка|750", "UTF-8", Ложь);
БайтыЧерновика = ПолучитьДвоичныеДанныеИзСтроки(Ключ + "|Черновик", "UTF-8", Ложь);
ПотокКарточки = БайтыКарточки.ОткрытьПотокДляЧтения();
ПотокПроверки = БайтыПроверки.ОткрытьПотокДляЧтения();
ПотокЧерновика = БайтыЧерновика.ОткрытьПотокДляЧтения();
Попытка
Вложения = Сообщение.Вложения;
Вложения.Добавить(ПотокКарточки, Ключ + "-card.txt", "text/plain", Истина);
Карточка = Вложения.Получить(0);
Проверка = Вложения.Вставить(0, ПотокПроверки, Ключ + "-check.bin", "application/octet-stream", Ложь);
Вложения.Добавить(ПотокЧерновика, Ключ + "-draft.txt", "text/plain", Истина);
Если Вложения.Количество() <> 3 Или Вложения.Индекс(Карточка) <> 1
Или Вложения.Индекс(Проверка) <> 0 Тогда
ВызватьИсключение "Вставка своих вложений не подтверждена";
КонецЕсли;
Вложения.Переместить(Карточка, 0);
Вложения.Сдвинуть(2, -1);
Если Вложения.Индекс(Карточка) <> 0 Или Вложения.Индекс(Проверка) <> 2
Или Вложения.Получить(1).Наименование <> Ключ + "-draft.txt" Тогда
ВызватьИсключение "Порядок своих вложений неверен";
КонецЕсли;
Вложения.Удалить(1);
Если Вложения.Количество() <> 2 Или Вложения.Получить(0).Наименование <> Ключ + "-card.txt"
Или Вложения.Получить(1).Наименование <> Ключ + "-check.bin" Тогда
ВызватьИсключение "Из своего нового сообщения удалено неверное вложение";
КонецЕсли;
ПроверитьСвоеОбсуждениеВложений85(ИДОбсуждения, Ключ, Пользователь.Идентификатор);
Сообщение.Записать();
Исключение
ПотокКарточки.Закрыть();
ПотокПроверки.Закрыть();
ПотокЧерновика.Закрыть();
ВызватьИсключение;
КонецПопытки;
ПотокКарточки.Закрыть();
ПотокПроверки.Закрыть();
ПотокЧерновика.Закрыть();
ИДСообщения = Сообщение.Идентификатор;
Если ТипЗнч(ИДСообщения) <> Тип("ИдентификаторСообщенияСистемыВзаимодействия") Тогда
ВызватьИсключение "Запись могла произойти, но реальный ID сообщения не получен";
КонецЕсли;
Факт = СистемаВзаимодействия.ПолучитьСообщение(ИДСообщения);
Если Факт.Идентификатор <> ИДСообщения Или Факт.Обсуждение <> ИДОбсуждения
Или Факт.Автор <> Пользователь.Идентификатор Или Факт.Данные <> Ключ + "|Книга|3|250|750"
Или Факт.Текст <> Ключ + " | Книга: 3 по 250 = 750" Или Факт.Вложения.Количество() <> 2 Тогда
ВызватьИсключение "Собственное сохраненное сообщение не подтверждено";
КонецЕсли;
ПроверитьСвойПаспортВложения85(Факт.Вложения.Получить(0), Ключ + "-card.txt", "text/plain", Истина, БайтыКарточки);
ПроверитьСвойПаспортВложения85(Факт.Вложения.Получить(1), Ключ + "-check.bin", "application/octet-stream", Ложь, БайтыПроверки);
Для Каждого Вложение Из Факт.Вложения Цикл
Если Вложение.СодержимоеУдалено Тогда
ВызватьИсключение "Содержимое уже удалено файловым хранилищем";
КонецЕсли;
КонецЦикла;
ПроверитьИЗакрытьСвойПоток85(Факт.Вложения.Получить(0).ОткрытьПотокДляЧтения(), БайтыКарточки);
ПроверитьИЗакрытьСвойПоток85(Факт.Вложения.Получить(1).ОткрытьПотокДляЧтения(), БайтыПроверки);
Черновик = СистемаВзаимодействия.СоздатьСообщение(ИДОбсуждения);
ПотокОтдельногоЧерновика = БайтыЧерновика.ОткрытьПотокДляЧтения();
Попытка
Черновик.Вложения.Добавить(ПотокОтдельногоЧерновика, Ключ + "-unsent.txt", "text/plain", Истина);
Если Черновик.Вложения.Количество() <> 1 Тогда
ВызватьИсключение "Свой отдельный черновик не заполнен";
КонецЕсли;
Черновик.Вложения.Очистить();
Если Черновик.Вложения.Количество() <> 0 Тогда
ВызватьИсключение "Свой отдельный черновик не очищен";
КонецЕсли;
Исключение
ПотокОтдельногоЧерновика.Закрыть();
ВызватьИсключение;
КонецПопытки;
ПотокОтдельногоЧерновика.Закрыть();
Возврат Новый Структура("Ключ,Обсуждение,Сообщение,Автор", Ключ, Строка(ИДОбсуждения), Строка(ИДСообщения), Строка(Пользователь.Идентификатор));
КонецФункции
Процедура ПроверитьСвоеОбсуждениеВложений85(ИДОбсуждения, Ключ, ИДПользователя)
Если ТипЗнч(ИДОбсуждения) <> Тип("ИдентификаторОбсужденияСистемыВзаимодействия") Тогда
ВызватьИсключение "Нужен полученный от сервера ID собственного обсуждения";
КонецЕсли;
Факт = СистемаВзаимодействия.ПолучитьОбсуждение(Ключ);
Если Факт = Неопределено Тогда
ВызватьИсключение "Собственное обсуждение не найдено по ключу";
КонецЕсли;
Если Факт.Идентификатор <> ИДОбсуждения Или Факт.Ключ <> Ключ
Или Факт.Заголовок <> Ключ + " | Книга: 3 по 250" Или Не Факт.Групповое
Или Не Факт.Отображаемое Или Факт.ТолькоПросмотр Или Факт.КонтекстОбсуждения <> Неопределено
Или Факт.Интеграция <> Неопределено Или Факт.ВнешнийИдентификатор <> ""
Или Факт.Участники.Количество() <> 1 Или Не Факт.Участники.Содержит(ИДПользователя) Тогда
ВызватьИсключение "Паспорт собственного обсуждения изменен";
КонецЕсли;
КонецПроцедуры
Процедура ПроверитьСвойПаспортВложения85(Вложение, Имя, MIME, Отображать, Байты)
Если ТипЗнч(Вложение) <> Тип("ВложениеСистемыВзаимодействия") Тогда
ВызватьИсключение "Ожидалось настоящее вложение сохраненного сообщения";
КонецЕсли;
Если Вложение.Наименование <> Имя Или Вложение.ТипСодержимого <> MIME
Или Вложение.Отображаемое <> Отображать Или Вложение.Размер <> Байты.Размер()
Или Вложение.СодержимоеУдалено Тогда
ВызватьИсключение "Паспорт собственного вложения не совпал";
КонецЕсли;
КонецПроцедуры
Процедура ПроверитьИЗакрытьСвойПоток85(ПотокТела, ОжидаемыеБайты)
Если ТипЗнч(ПотокТела) <> Тип("Поток") Тогда
ВызватьИсключение "Ожидался реальный поток вложения";
КонецЕсли;
Попытка
Если Не ПотокТела.ДоступноЧтение Тогда
ВызватьИсключение "Поток вложения недоступен для чтения";
КонецЕсли;
ОжидаемыйБуфер = ПолучитьБуферДвоичныхДанныхИзДвоичныхДанных(ОжидаемыеБайты);
Буфер = Новый БуферДвоичныхДанных(128);
Смещение = 0;
Пока Истина Цикл
Прочитано = ПотокТела.Прочитать(Буфер, 0, 128);
Если Прочитано = 0 Тогда
Прервать;
КонецЕсли;
Если ТипЗнч(Прочитано) <> Тип("Число") Или Прочитано < 0 Или Прочитано > 128
Или Смещение + Прочитано > ОжидаемыеБайты.Размер() Тогда
ВызватьИсключение "Длина потока собственного вложения неверна";
КонецЕсли;
Для Индекс = 0 По Прочитано - 1 Цикл
Если Буфер[Индекс] <> ОжидаемыйБуфер[Смещение + Индекс] Тогда
ВызватьИсключение "Байты собственного вложения отличаются";
КонецЕсли;
КонецЦикла;
Смещение = Смещение + Прочитано;
КонецЦикла;
Если Смещение <> ОжидаемыеБайты.Размер() Тогда
ВызватьИсключение "Поток собственного вложения завершился преждевременно";
КонецЕсли;
Исключение
ПотокТела.Закрыть();
ВызватьИсключение;
КонецПопытки;
ПотокТела.Закрыть();
КонецПроцедуры
Функция ПроверитьСвоегоПользователя85()
ПроверитьСвойПустойСтенд();
Если ПривилегированныйРежим() Тогда
ВызватьИсключение "Входной привилегированный режим не допускается";
КонецЕсли;
Пользователи = ПользователиИнформационнойБазы.ПолучитьПользователей();
Текущий = ПользователиИнформационнойБазы.ТекущийПользователь();
Если Пользователи.Количество() <> 1 Или Текущий = Неопределено Тогда
ВызватьИсключение "Нужен единственный собственный учебный пользователь";
КонецЕсли;
Если Пользователи[0].УникальныйИдентификатор <> Текущий.УникальныйИдентификатор
Или Лев(Текущий.Имя, СтрДлина("Уч85Связь_")) <> "Уч85Связь_" Тогда
ВызватьИсключение "Учебный пользователь не подтвержден";
КонецЕсли;
UUIDМетки = Новый УникальныйИдентификатор(Сред(Текущий.Имя, СтрДлина("Уч85Связь_") + 1));
Если Текущий.Имя <> "Уч85Связь_" + Строка(UUIDМетки)
Или UUIDМетки = Новый УникальныйИдентификатор("00000000-0000-0000-0000-000000000000") Тогда
ВызватьИсключение "Имя должно содержать собственный канонический ненулевой UUID";
КонецЕсли;
Если Не СистемаВзаимодействия.ИнформационнаяБазаЗарегистрирована()
Или Не СистемаВзаимодействия.ИспользованиеДоступно() Тогда
ВызватьИсключение "Вручную подготовьте регистрацию собственной учебной базы";
КонецЕсли;
Если СистемаВзаимодействия.АдресСервера() <> "http://localhost:9094" Тогда
ВызватьИсключение "Разрешен только отдельно подготовленный сервер localhost:9094 в своей VM";
КонецЕсли;
Версия = СистемаВзаимодействия.ВерсияСервера();
Если ТипЗнч(Версия) <> Тип("Строка") Или Не ЗначениеЗаполнено(Версия) Тогда
ВызватьИсключение "Версия своего сервера не получена";
КонецЕсли;
ИД = СистемаВзаимодействия.ИдентификаторТекущегоПользователя();
Если ТипЗнч(ИД) <> Тип("ИдентификаторПользователяСистемыВзаимодействия") Тогда
ВызватьИсключение "Не получен реальный идентификатор собственного участника";
КонецЕсли;
Если СистемаВзаимодействия.ПолучитьИдентификаторПользователя(Текущий.УникальныйИдентификатор) <> ИД
Или СистемаВзаимодействия.ПолучитьИдентификаторПользователяИнформационнойБазы(ИД) <> Текущий.УникальныйИдентификатор Тогда
ВызватьИсключение "Двустороннее соответствие своей учетной записи неверно";
КонецЕсли;
Пользователь = СистемаВзаимодействия.ПолучитьПользователя(ИД);
Если Пользователь.Идентификатор <> ИД Или Пользователь.Имя <> Текущий.Имя
Или Пользователь.ПолноеИмя <> Текущий.ПолноеИмя
Или Пользователь.ИдентификаторПользователяИнформационнойБазы <> Текущий.УникальныйИдентификатор Тогда
ВызватьИсключение "Паспорт своего пользователя не совпал с информационной базой";
КонецЕсли;
Если Пользователь.Внешний Или Пользователь.Заблокирован
Или Пользователь.ТипВнешнейСистемы <> ""
Или Пользователь.ИдентификаторПользователяВнешнейСистемы <> Неопределено
Или Пользователь.АдресЭлектроннойПочты <> ""
Или Пользователь.АдресЭлектроннойПочтыДляАутентификации <> ""
Или Пользователь.НомерТелефона <> ""
Или Пользователь.КлючСопоставления <> "" Тогда
ВызватьИсключение "Нужен чистый внутренний пользователь без персональных контактов и объединений";
КонецЕсли;
Если Пользователь.АдминистраторАбонента <> СистемаВзаимодействия.ТекущийПользовательЯвляетсяАдминистраторомАбонента() Тогда
ВызватьИсключение "Признак администратора абонента не согласован";
КонецЕсли;
ВсеПользователи = СистемаВзаимодействия.СтандартныеПользователи.ВсеПользователиПриложения;
Если ИД = ВсеПользователи Или ИД = Новый ИдентификаторПользователяСистемыВзаимодействия Тогда
ВызватьИсключение "Стандартный адресат и Никого не оповещать не являются своей учетной записью";
КонецЕсли;
ВосстановленныйИД = Новый ИдентификаторПользователяСистемыВзаимодействия(Строка(ИД));
Если СистемаВзаимодействия.ПолучитьПользователя(ВосстановленныйИД).Идентификатор <> ИД Тогда
ВызватьИсключение "Восстановленный из строки реальный ID не подтвержден сервером";
КонецЕсли;
Приложение = СистемаВзаимодействия.ПолучитьПриложение(СистемаВзаимодействия.ИдентификаторТекущегоПриложения());
Если Приложение.Идентификатор <> СистемаВзаимодействия.ИдентификаторТекущегоПриложения()
Или Приложение.Наименование <> "УчебноеВзаимодействие85" Тогда
ВызватьИсключение "Нужно свое приложение с точным учебным наименованием";
КонецЕсли;
Возврат Пользователь;
КонецФункции
Процедура ПроверитьСвойПустойСтенд()
Если Метаданные.Справочники.Количество() <> 0 Или Метаданные.Документы.Количество() <> 0 Или Метаданные.Перечисления.Количество() <> 0 Или Метаданные.РегистрыСведений.Количество() <> 0 Или Метаданные.РегистрыНакопления.Количество() <> 0 Или Метаданные.РегистрыБухгалтерии.Количество() <> 0 Или Метаданные.РегистрыРасчета.Количество() <> 0 Или Метаданные.ПланыВидовХарактеристик.Количество() <> 0 Или Метаданные.ПланыВидовРасчета.Количество() <> 0 Или Метаданные.ПланыСчетов.Количество() <> 0 Или Метаданные.ПланыОбмена.Количество() <> 0 Или Метаданные.Последовательности.Количество() <> 0 Или Метаданные.БизнесПроцессы.Количество() <> 0 Или Метаданные.Задачи.Количество() <> 0 Или Метаданные.ОбщиеРеквизиты.Количество() <> 0 Или Метаданные.ПодпискиНаСобытия.Количество() <> 0 Или Метаданные.РегламентныеЗадания.Количество() <> 0 Или Метаданные.ВнешниеИсточникиДанных.Количество() <> 0 Тогда
ВызватьИсключение "Нужен только свой новый пустой учебный стенд";
КонецЕсли;
Если Метаданные.ЕстьИзмененияРасширениямиКонфигурации() Тогда
ВызватьИсключение "Свой сеанс не должен изменяться расширениями";
КонецЕсли;
Источники = Новый Массив;
Источники.Добавить(ИсточникРасширенийКонфигурации.БазаДанных);
Источники.Добавить(ИсточникРасширенийКонфигурации.СеансАктивные);
Источники.Добавить(ИсточникРасширенийКонфигурации.СеансОтключенные);
Для Каждого Источник Из Источники Цикл
Расширения = РасширенияКонфигурации.Получить(, Источник);
Если ТипЗнч(Расширения) <> Тип("Массив") Тогда
ВызватьИсключение "Native список расширений должен иметь тип Массив";
КонецЕсли;
Если Расширения.Количество() <> 0 Тогда
ВызватьИсключение "В своём новом стенде нет расширений базы или сеанса";
КонецЕсли;
КонецЦикла;
Если ТранзакцияАктивна() Тогда
ВызватьИсключение "Чужая или вложенная активная транзакция не допускается";
КонецЕсли;
КонецПроцедурыРазбор по шагам
Сначала проверяется пустая конфигурация, отсутствие привилегированного режима и собственный real-user/real-application паспорт. Адрес localhost сам по себе не доказывает владельца сервера: VM и регистрацию проверяете вы.
После точной фразы и проверки поддержки файлов новый UUID-ключ используется только для собственного нового обсуждения. Перед записью сообщения свежая копия обсуждения проверяет единственного своего участника, отсутствие внешней интеграции и контекста.
Книга: 3 по 250 дает750. Карточка содержит свой ключ и этот расчет; второе двоичное вложение содержит собственную проверку750. UTF-8 без BOM и MIME передаются явно; расширение .bin не является доказательством содержимого.
Добавить не объявлен в native-справке как возвращающий вложение: результат не присваивается. Объект карточки получаем через Получить(0). Вставить возвращает новый настоящий объект проверки и ставит его в начало.
Третий собственный черновик нужен для реальной задачи редактирования списка. Последовательность: [проверка,карточка,черновик] → Переместить → [карточка,проверка,черновик] → Сдвинуть → [карточка,черновик,проверка] → Удалить(1) → [карточка,проверка].
После каждого этапа проверяются Количество/Индекс/Получить и точные имена своих элементов. Индексы начинаются с0. Удаляется только собственный лишний черновик нового сообщения, а не файл по произвольному пути.
Все исходные потоки остаются открыты до завершения Записать и закрываются как при успехе, так и при исключении. При ошибке записи неизвестен частичный результат: код не повторяет отправку и не делает автоматический откат.
ПолучитьСообщение читает реальный ID, полученный после записи. Сверяем автора, обсуждение, UUID-маркер, текст и количество вложений, затем каждый readonly паспорт: имя, MIME, размер в байтах, отображение, содержимое удалено.
Серверный OpenStreamForRead открывает сохраненное содержимое. Чтение порциями128 байт сравнивает только фактически прочитанный участок. Короткий ненулевой read не равен концу; EOF только0. Нельзя полагаться на Size/Seek у произвольного потока.
Для Очистить создается отдельный новый объект сообщения и новый поток черновика. После удаления всех его вложений проверяется0; Записать для этого черновика отсутствует. Возможные временные объекты хранилища зависят от серверной поставки.
Команда ПрочитатьЖдать повторно проверяет текущего пользователя/локальный адрес/паспорт сохраненной карточки. Ждать превращает обещание в Поток, затем тот же полный byte-check закрывает поток.
Команда ПрочитатьЧерезОповещение передает request UUID и form UUID. Подпись успеха ровно (ПотокТела,ДополнительныеПараметры); ошибка имеет (ИнформацияОбОшибке,ДополнительныеПараметры). Запоздалый чужой callback не обрабатывает карточку и закрывает полученный поток.
Как проверить результат
Без точного подтверждения/с правильным пользователем, но без файлового хранилища, обсуждение и сообщение не создаются.
У сохраненного собственного сообщения ровно два вложения: <ключ>-card.txt показано, <ключ>-check.bin скрыто. Скрытое тоже реально сохраняется; это не секретность/ограничение доступа.
Ни один readonly паспорт и readonly свойство Сообщение.Вложения не присваивается. Изменяется только содержимое коллекции своего нового объекта.
После успешного серверного чтения и каждой клиентской команды все байты собственного UUID-маркера/расчета750 совпадают, а потоки закрыты.
Измененное имя/MIME/размер/тело, добавленный участник, чужой автор или СодержимоеУдалено вызывают отказ. СодержимоеУдалено нельзя установить для имитации; это readonly состояние настоящего хранилища.
Возврат обещания или вызов callback сам по себе не доказывает корректность содержимого: положительный результат выдается только после точного чтения всех байтов до EOF.
Ожидаемый результат
При ручном успешном запуске на своем подготовленном стенде остается одно новое обсуждение с собственной карточкой книги и двумя проверенными вложениями; отдельный очищенный черновик не отправляется. Три API-стиля читают одинаковые реальные байты750. Здесь подтверждены исходный код/нативные сигнатуры и статические проверки, но runtime1С отсутствует.
На что обратить внимание
localhost не заменяет проверку выделенного сервера, абонента и локального бакета. Подключенное хранилище может быть недоступно; исключение не означает, что запись не произошла частично.
Вложения — readonly коллекция свойства сообщения, но ее методы изменяют состав нового сообщения. Нельзя присвоить новую коллекцию или создавать Вложение/Коллекция через неподтвержденный конструктор.
У Добавить нет документированного return, у Вставить есть ВложениеСистемыВзаимодействия. Не копируйте произвольные параметры из похожих коллекций.
Размер означает байты, не СтрДлина; UTF-8 кириллицы занимает несколько байтов. MIME/Отображаемое не проверяют и не защищают содержимое.
Удалить(индекс)/Очистить здесь только собственные новые объекты. Применение к чужому/сохраненному сообщению требует отдельного согласованного сценария и проверки прав; этот пример такого разрешения не дает.
Синхронный OpenStreamForRead отсутствует в thin/web. Async/callback API доступны клиенту, но весь пример с GetMessage ограничен толстым управляемым клиентом честно.
Поток не читается огромным неограниченным массивом и не сохраняется в файл. Сравнение с маленьким собственным ожидаемым буфером ограничивает фактически принимаемую длину.
После таймаута записи не повторяйте отправку вслепую. После закрытия формы поздний callback не подтверждает сохранение, повторное открытие формы не восстанавливает ownership-пакет автоматически.
Сырые ошибки/ключи S3/пути/содержимое внешних файлов не журналируются. Образец не выполнялся и не является измерением доступности/квоты конкретного сервера.
Пример не запускался в вашей базе. Перед применением проверьте его в указанном учебном контексте.
Создать свое обсуждение с двумя сообщениями, проверить ответ и обновить собственную карточку
Полный цикл начинается с нового собственного UUID-ключа и реального текущего участника. Создает обсуждение, выключает собственные оповещения, отправляет две учебные карточки, проверяет их IDs/данные/ответ, точную выборку и обновление.
Где и как запускать
Полный серверный общий модуль УчебноеВзаимодействие своей пустой конфигурации УчебноеВзаимодействие85. Реальная работа системы взаимодействия только после вашего ручного запуска в отдельной локальной VM.
Подготовка
Нужны только ваша изолированная VM, новая пустая база8.5.1.1343 и отдельный локальный сервер взаимодействия, не связанный с рабочими абонентами/серверами. Имя конфигурации УчебноеВзаимодействие85, русский язык, совместимость Не использовать, управляемый интерфейс. Пустоту предметных объектов, фоновых заданий, подписок и отсутствие расширений проверяет код. Реальные API здесь не выполнялись.
Разверните собственный учебный сервер взаимодействия в той же VM с локальным адресом http://localhost:9094, без публичной публикации, SMTP, внешних интеграций, совместного использования приложений и чужих учетных записей. Его установку и настройку выполняйте вручную по документации своей серверной поставки. Эта программа не устанавливает сервер и не меняет маршрут localhost. Если фактически зарегистрированный адрес отличается, образец намеренно отказывает; не подставляйте рабочий адрес.
В конфигураторе вручную создайте единственного пользователя с именем Уч85Связь_ и добавленным вашим новым ненулевым UUID в каноническом строковом формате; полное имя Учебный участник. Назначьте только в этом стенде необходимые права Администрирование данных/администрирование пользователей и системы взаимодействия. Войдите под ним. Учетные записи/права этим кодом не создаются и не расширяются.
Вручную зарегистрируйте только эту пустую базу на своем отдельном сервере взаимодействия; наименование приложения задайте УчебноеВзаимодействие85. Синхронизируйте своего внутреннего пользователя базы: имя/полное имя совпадают, учетная запись не внешняя, не заблокирована, без почты/телефона/аутентификационной почты/ключа сопоставления. Для урока профиля псевдоним должен быть пустым. Не используйте персональные сведения, действительные адреса или контакты.
Создайте серверный общий модуль УчебноеВзаимодействие с флагом Сервер, без флагов Клиент и Привилегированный. Вставьте полный код выбранного урока целиком, не одновременно оба варианта модуля. Все вызываемые внутренние функции включены. Нельзя вставлять серверные методы в клиентский модуль формы: thin/web для этих синхронных методов не объявлены в native-справке.
Создайте свою внешнюю обработку с одной управляемой формой, командой Выполнить и строковым реквизитом Подтверждение длиной100. Обработчик команды клиента вызывает ВыполнитьНаСервере(); серверная процедура вызывает УчебноеВзаимодействие.ВыполнитьПример(Подтверждение). Эти две короткие процедуры даны отдельным поддерживающим модулем. Кнопка должна быть нажата сознательно только после чтения предупреждений; автоматического запуска при открытии нет.
В VM оставьте только свой сеанс, не включайте ботов, обработчики сообщений, сторонние клиенты или задания, способные менять свои объекты параллельно. Защитные маркеры/ID уменьшают риск чужого доступа, но не являются криптографическим доказательством собственника сервера и не заменяют вашу проверку выделенного стенда. Профиль и сообщения — настоящие изменения на своем сервере при вашем ручном запуске.
Сначала оставьте подтверждение пустым: программа должна остановиться до изменения профиля/создания обсуждения. Затем введите только фразу выбранного урока. После исключения никакие автоматические повторы/откаты/удаления не выполняются. Сохраните показанный собственный кандидат UUID-ключа/псевдонима и проверяйте возможный частичный результат вручную только в своем сервере. Новые резервные копии не создаются.
Процедура ВыполнитьПример(Подтверждение = "") Экспорт
Пользователь = ПроверитьСвоегоПользователя85();
Если Подтверждение <> "СВОЕ ОБСУЖДЕНИЕ И ДВА СООБЩЕНИЯ" Тогда
ВызватьИсключение "Сначала прочитайте подготовку. Обсуждение не создано";
КонецЕсли;
ИДПользователя = Пользователь.Идентификатор;
Ключ = "Уч85Обсуждение_" + Строка(Новый УникальныйИдентификатор);
Сообщить("Собственный кандидат ключа: " + Ключ);
Если СистемаВзаимодействия.ПолучитьОбсуждение(Ключ) <> Неопределено Тогда
ВызватьИсключение "Коллизия ключа: существующее обсуждение не меняется";
КонецЕсли;
Выбор = Новый КоллекцияИдентификаторовПользователейСистемыВзаимодействия;
Выбор.Добавить(ИДПользователя);
Если Выбор.Количество() <> 1 Или Не Выбор.Содержит(ИДПользователя) Тогда
ВызватьИсключение "Состав своего участника не собран";
КонецЕсли;
Выбор.Удалить(ИДПользователя);
Если Выбор.Количество() <> 0 Или Выбор.Содержит(ИДПользователя) Тогда
ВызватьИсключение "Удаление из локального подбора не подтверждено";
КонецЕсли;
Выбор.Добавить(ИДПользователя);
Выбор.Очистить();
Если Выбор.Количество() <> 0 Тогда
ВызватьИсключение "Локальный подбор не очищен";
КонецЕсли;
Выбор.Добавить(ИДПользователя);
Обсуждение = СистемаВзаимодействия.СоздатьОбсуждение();
Обсуждение.Ключ = Ключ;
Обсуждение.Заголовок = Ключ + " | Проверка карточки книги";
Обсуждение.Групповое = Истина;
Обсуждение.Отображаемое = Истина;
Обсуждение.ТолькоПросмотр = Ложь;
Обсуждение.КонтекстОбсуждения = Неопределено;
Обсуждение.Интеграция = Неопределено;
Обсуждение.ВнешнийИдентификатор = "";
Для Каждого Участник Из Выбор Цикл
Обсуждение.Участники.Добавить(Участник);
КонецЦикла;
Обсуждение.Записать();
ИДОбсуждения = Обсуждение.Идентификатор;
Если ТипЗнч(ИДОбсуждения) <> Тип("ИдентификаторОбсужденияСистемыВзаимодействия") Тогда
ВызватьИсключение "Запись могла произойти, но реальный ID обсуждения не получен";
КонецЕсли;
Сообщить("Свой ID обсуждения: " + Строка(ИДОбсуждения));
Восстановленное = Новый ИдентификаторОбсужденияСистемыВзаимодействия(Строка(ИДОбсуждения));
ПроверитьСвоеОбсуждение85(Восстановленное, Ключ, ИДПользователя);
Отбор = Новый ОтборОбсужденийСистемыВзаимодействия;
Отбор.Ключ = Ключ;
Отбор.Групповое = Истина;
Отбор.Отображаемое = Истина;
Отбор.КонтекстноеОбсуждение = Ложь;
Отбор.КонтекстОбсуждения = Неопределено;
Отбор.ТекущийПользовательЯвляетсяУчастником = Истина;
Отбор.Количество = 2;
Отбор.НаправлениеСортировки = НаправлениеСортировки.Возр;
Отбор.ДатаНачала = Неопределено;
Отбор.ВнешнийИдентификатор = "";
Если Отбор.Интеграция <> Неопределено Тогда
ВызватьИсключение "Новый отбор не должен выбирать внешнюю интеграцию";
КонецЕсли;
Список = СистемаВзаимодействия.ПолучитьОбсуждения(Отбор);
Если Список.Количество() <> 1 Или Список[0].Идентификатор <> ИДОбсуждения Тогда
ВызватьИсключение "Точный отбор собственного обсуждения не совпал";
КонецЕсли;
СистемаВзаимодействия.УстановитьРежимНаблюдения(ИДОбсуждения, Ложь, ИДПользователя);
СистемаВзаимодействия.УстановитьОтображениеОповещенийОбсуждения(ИДОбсуждения,
ОтображениеОповещенийСистемыВзаимодействия.НеБеспокоить, ИДПользователя);
Если СистемаВзаимодействия.ПолучитьРежимНаблюдения(ИДОбсуждения, ИДПользователя)
Или СистемаВзаимодействия.ПолучитьОтображениеОповещенийОбсуждения(ИДОбсуждения, ИДПользователя)
<> ОтображениеОповещенийСистемыВзаимодействия.НеБеспокоить Тогда
ВызватьИсключение "Собственные настройки оповещений не подтверждены";
КонецЕсли;
ПроверитьСвоеОбсуждение85(ИДОбсуждения, Ключ, ИДПользователя);
ОтборДоЗаписи = Новый ОтборСообщенийСистемыВзаимодействия;
ОтборДоЗаписи.Обсуждение = ИДОбсуждения;
ОтборДоЗаписи.Количество = 2;
ОтборДоЗаписи.НаправлениеСортировки = НаправлениеСортировки.Возр;
ОтборДоЗаписи.После = Неопределено;
Если СистемаВзаимодействия.ПолучитьСообщения(ОтборДоЗаписи).Количество() <> 0 Тогда
ВызватьИсключение "Перед первой своей записью обсуждение должно быть пусто";
КонецЕсли;
Исходное = СистемаВзаимодействия.СоздатьСообщение(ИДОбсуждения);
Исходное.Текст = Новый ФорматированнаяСтрока(Ключ + " | Книга: количество 3, цена 250");
Исходное.Данные = Ключ + "|Книга|3|250";
Исходное.ФорматДанныхСтроки = "text/plain";
Исходное.Получатели.Добавить(Новый ИдентификаторПользователяСистемыВзаимодействия);
ПроверитьСвойЧерновик85(Исходное);
Исходное.Записать();
ИДИсходного = Исходное.Идентификатор;
Сообщить("Свой ID первого сообщения: " + Строка(ИДИсходного));
ПервыйФакт = СистемаВзаимодействия.ПолучитьСообщение(
Новый ИдентификаторСообщенияСистемыВзаимодействия(Строка(ИДИсходного)));
ПроверитьСвоеСообщение85(ПервыйФакт, ИДИсходного, ИДОбсуждения, ИДПользователя, Ключ,
Ключ + " | Книга: количество 3, цена 250", Неопределено);
ПроверитьСвоеОбсуждение85(ИДОбсуждения, Ключ, ИДПользователя);
ДоВторойЗаписи = СистемаВзаимодействия.ПолучитьСообщения(ОтборДоЗаписи);
Если ДоВторойЗаписи.Количество() <> 1 Или ДоВторойЗаписи[0].Идентификатор <> ИДИсходного Тогда
ВызватьИсключение "Перед второй своей записью ожидается только подтвержденное первое сообщение";
КонецЕсли;
Второе = СистемаВзаимодействия.СоздатьСообщение(ИДОбсуждения);
Второе.Текст = Новый ФорматированнаяСтрока(Ключ + " | Проверка: сумма 750");
Второе.Данные = Ключ + "|Книга|3|250";
Второе.ФорматДанныхСтроки = "text/plain";
Второе.ИсходноеСообщение = ИДИсходного;
Второе.Получатели.Добавить(Новый ИдентификаторПользователяСистемыВзаимодействия);
ПроверитьСвойЧерновик85(Второе);
Второе.Записать();
ИДВторого = Второе.Идентификатор;
Сообщить("Свой ID второго сообщения: " + Строка(ИДВторого));
ВторойФакт = СистемаВзаимодействия.ПолучитьСообщение(ИДВторого);
ПроверитьСвоеСообщение85(ВторойФакт, ИДВторого, ИДОбсуждения, ИДПользователя, Ключ,
Ключ + " | Проверка: сумма 750", ИДИсходного);
ПроверитьСвойСписок85(ИДОбсуждения, ИДИсходного, ИДВторого);
ПроверитьСвоеОбсуждение85(ИДОбсуждения, Ключ, ИДПользователя);
ВторойФакт = СистемаВзаимодействия.ПолучитьСообщение(ИДВторого);
ПроверитьСвоеСообщение85(ВторойФакт, ИДВторого, ИДОбсуждения, ИДПользователя, Ключ,
Ключ + " | Проверка: сумма 750", ИДИсходного);
ВторойФакт.Текст = Новый ФорматированнаяСтрока(Ключ + " | Проверка подтверждена: 750");
ВторойФакт.Записать();
ПроверитьСвоеСообщение85(СистемаВзаимодействия.ПолучитьСообщение(ИДВторого),
ИДВторого, ИДОбсуждения, ИДПользователя, Ключ,
Ключ + " | Проверка подтверждена: 750", ИДИсходного);
Сообщить("Два своих сообщения сохранены, отобраны и одно обновлено. Удаление не выполняется автоматически");
КонецПроцедуры
Процедура ПроверитьСвойЧерновик85(Сообщение)
Если Сообщение.Вложения.Количество() <> 0 Или Сообщение.Действия.Количество() <> 0
Или Сообщение.ПанельКнопок.РядыКнопок.Количество() <> 0
Или Сообщение.ЗначенияПодстановокШаблонаСообщения.Количество() <> 0
Или Сообщение.ШаблонСообщения <> Неопределено Тогда
ВызватьИсключение "Новый собственный черновик содержит неподготовленные вложения или шаблон";
КонецЕсли;
КонецПроцедуры
Процедура ПроверитьСвоеОбсуждение85(Идентификатор, Ключ, Участник)
Факт = СистемаВзаимодействия.ПолучитьОбсуждение(Идентификатор);
Если Факт.Идентификатор <> Идентификатор Или Факт.Ключ <> Ключ
Или Факт.Заголовок <> Ключ + " | Проверка карточки книги"
Или Не Факт.Групповое Или Не Факт.Отображаемое Или Факт.ТолькоПросмотр
Или Факт.КонтекстОбсуждения <> Неопределено Или Факт.Интеграция <> Неопределено
Или Факт.ВнешнийИдентификатор <> ""
Или Факт.Участники.Количество() <> 1 Или Не Факт.Участники.Содержит(Участник) Тогда
ВызватьИсключение "Полный собственный паспорт обсуждения изменен или не совпал";
КонецЕсли;
КонецПроцедуры
Процедура ПроверитьСвоеСообщение85(Факт, Идентификатор, Обсуждение, Автор, Ключ, Текст, Исходное)
Если Факт.Идентификатор <> Идентификатор Или Факт.Обсуждение <> Обсуждение
Или Факт.Автор <> Автор Или Строка(Факт.Текст) <> Текст
Или Факт.Данные <> Ключ + "|Книга|3|250" Или Факт.ФорматДанныхСтроки <> "text/plain"
Или Факт.ИсходноеСообщение <> Исходное Или ТипЗнч(Факт.Дата) <> Тип("Дата")
Или Факт.Дата = Дата(1, 1, 1) Тогда
ВызватьИсключение "Полное собственное сообщение не подтверждено. Чужое содержимое не выводится";
КонецЕсли;
ПроверитьСвойЧерновик85(Факт);
КонецПроцедуры
Процедура ПроверитьСвойСписок85(Обсуждение, Первый, Второй)
Отбор = Новый ОтборСообщенийСистемыВзаимодействия;
Отбор.Обсуждение = Обсуждение;
Отбор.Количество = 3;
Отбор.НаправлениеСортировки = НаправлениеСортировки.Возр;
Отбор.После = Неопределено;
Список = СистемаВзаимодействия.ПолучитьСообщения(Отбор);
Если Список.Количество() <> 2 Тогда
ВызватьИсключение "В собственном обсуждении должно быть ровно два собственных сообщения";
КонецЕсли;
Для Каждого СтрокаСообщения Из Список Цикл
Если СтрокаСообщения.Обсуждение <> Обсуждение
Или СтрокаСообщения.Идентификатор <> Первый И СтрокаСообщения.Идентификатор <> Второй Тогда
ВызватьИсключение "В список попало неподтвержденное сообщение";
КонецЕсли;
КонецЦикла;
Отбор.После = Второй;
Если СистемаВзаимодействия.ПолучитьСообщения(Отбор).Количество() <> 0 Тогда
ВызватьИсключение "После второго собственного сообщения не ожидаются записи";
КонецЕсли;
КонецПроцедуры
Функция ПроверитьСвоегоПользователя85()
ПроверитьСвойПустойСтенд();
Если ПривилегированныйРежим() Тогда
ВызватьИсключение "Входной привилегированный режим не допускается";
КонецЕсли;
Пользователи = ПользователиИнформационнойБазы.ПолучитьПользователей();
Текущий = ПользователиИнформационнойБазы.ТекущийПользователь();
Если Пользователи.Количество() <> 1 Или Текущий = Неопределено Тогда
ВызватьИсключение "Нужен единственный собственный учебный пользователь";
КонецЕсли;
Если Пользователи[0].УникальныйИдентификатор <> Текущий.УникальныйИдентификатор
Или Лев(Текущий.Имя, СтрДлина("Уч85Связь_")) <> "Уч85Связь_" Тогда
ВызватьИсключение "Учебный пользователь не подтвержден";
КонецЕсли;
UUIDМетки = Новый УникальныйИдентификатор(Сред(Текущий.Имя, СтрДлина("Уч85Связь_") + 1));
Если Текущий.Имя <> "Уч85Связь_" + Строка(UUIDМетки)
Или UUIDМетки = Новый УникальныйИдентификатор("00000000-0000-0000-0000-000000000000") Тогда
ВызватьИсключение "Имя должно содержать собственный канонический ненулевой UUID";
КонецЕсли;
Если Не СистемаВзаимодействия.ИнформационнаяБазаЗарегистрирована()
Или Не СистемаВзаимодействия.ИспользованиеДоступно() Тогда
ВызватьИсключение "Вручную подготовьте регистрацию собственной учебной базы";
КонецЕсли;
Если СистемаВзаимодействия.АдресСервера() <> "http://localhost:9094" Тогда
ВызватьИсключение "Разрешен только отдельно подготовленный сервер localhost:9094 в своей VM";
КонецЕсли;
Версия = СистемаВзаимодействия.ВерсияСервера();
Если ТипЗнч(Версия) <> Тип("Строка") Или Не ЗначениеЗаполнено(Версия) Тогда
ВызватьИсключение "Версия своего сервера не получена";
КонецЕсли;
ИД = СистемаВзаимодействия.ИдентификаторТекущегоПользователя();
Если ТипЗнч(ИД) <> Тип("ИдентификаторПользователяСистемыВзаимодействия") Тогда
ВызватьИсключение "Не получен реальный идентификатор собственного участника";
КонецЕсли;
Если СистемаВзаимодействия.ПолучитьИдентификаторПользователя(Текущий.УникальныйИдентификатор) <> ИД
Или СистемаВзаимодействия.ПолучитьИдентификаторПользователяИнформационнойБазы(ИД) <> Текущий.УникальныйИдентификатор Тогда
ВызватьИсключение "Двустороннее соответствие своей учетной записи неверно";
КонецЕсли;
Пользователь = СистемаВзаимодействия.ПолучитьПользователя(ИД);
Если Пользователь.Идентификатор <> ИД Или Пользователь.Имя <> Текущий.Имя
Или Пользователь.ПолноеИмя <> Текущий.ПолноеИмя
Или Пользователь.ИдентификаторПользователяИнформационнойБазы <> Текущий.УникальныйИдентификатор Тогда
ВызватьИсключение "Паспорт своего пользователя не совпал с информационной базой";
КонецЕсли;
Если Пользователь.Внешний Или Пользователь.Заблокирован
Или Пользователь.ТипВнешнейСистемы <> ""
Или Пользователь.ИдентификаторПользователяВнешнейСистемы <> Неопределено
Или Пользователь.АдресЭлектроннойПочты <> ""
Или Пользователь.АдресЭлектроннойПочтыДляАутентификации <> ""
Или Пользователь.НомерТелефона <> ""
Или Пользователь.КлючСопоставления <> "" Тогда
ВызватьИсключение "Нужен чистый внутренний пользователь без персональных контактов и объединений";
КонецЕсли;
Если Пользователь.АдминистраторАбонента <> СистемаВзаимодействия.ТекущийПользовательЯвляетсяАдминистраторомАбонента() Тогда
ВызватьИсключение "Признак администратора абонента не согласован";
КонецЕсли;
ВсеПользователи = СистемаВзаимодействия.СтандартныеПользователи.ВсеПользователиПриложения;
Если ИД = ВсеПользователи Или ИД = Новый ИдентификаторПользователяСистемыВзаимодействия Тогда
ВызватьИсключение "Стандартный адресат и Никого не оповещать не являются своей учетной записью";
КонецЕсли;
ВосстановленныйИД = Новый ИдентификаторПользователяСистемыВзаимодействия(Строка(ИД));
Если СистемаВзаимодействия.ПолучитьПользователя(ВосстановленныйИД).Идентификатор <> ИД Тогда
ВызватьИсключение "Восстановленный из строки реальный ID не подтвержден сервером";
КонецЕсли;
Приложение = СистемаВзаимодействия.ПолучитьПриложение(СистемаВзаимодействия.ИдентификаторТекущегоПриложения());
Если Приложение.Идентификатор <> СистемаВзаимодействия.ИдентификаторТекущегоПриложения()
Или Приложение.Наименование <> "УчебноеВзаимодействие85" Тогда
ВызватьИсключение "Нужно свое приложение с точным учебным наименованием";
КонецЕсли;
Возврат Пользователь;
КонецФункции
Процедура ПроверитьСвойПустойСтенд()
Если Метаданные.Справочники.Количество() <> 0 Или Метаданные.Документы.Количество() <> 0 Или Метаданные.Перечисления.Количество() <> 0 Или Метаданные.РегистрыСведений.Количество() <> 0 Или Метаданные.РегистрыНакопления.Количество() <> 0 Или Метаданные.РегистрыБухгалтерии.Количество() <> 0 Или Метаданные.РегистрыРасчета.Количество() <> 0 Или Метаданные.ПланыВидовХарактеристик.Количество() <> 0 Или Метаданные.ПланыВидовРасчета.Количество() <> 0 Или Метаданные.ПланыСчетов.Количество() <> 0 Или Метаданные.ПланыОбмена.Количество() <> 0 Или Метаданные.Последовательности.Количество() <> 0 Или Метаданные.БизнесПроцессы.Количество() <> 0 Или Метаданные.Задачи.Количество() <> 0 Или Метаданные.ОбщиеРеквизиты.Количество() <> 0 Или Метаданные.ПодпискиНаСобытия.Количество() <> 0 Или Метаданные.РегламентныеЗадания.Количество() <> 0 Или Метаданные.ВнешниеИсточникиДанных.Количество() <> 0 Тогда
ВызватьИсключение "Нужен только свой новый пустой учебный стенд";
КонецЕсли;
Если Метаданные.ЕстьИзмененияРасширениямиКонфигурации() Тогда
ВызватьИсключение "Свой сеанс не должен изменяться расширениями";
КонецЕсли;
Источники = Новый Массив;
Источники.Добавить(ИсточникРасширенийКонфигурации.БазаДанных);
Источники.Добавить(ИсточникРасширенийКонфигурации.СеансАктивные);
Источники.Добавить(ИсточникРасширенийКонфигурации.СеансОтключенные);
Для Каждого Источник Из Источники Цикл
Расширения = РасширенияКонфигурации.Получить(, Источник);
Если ТипЗнч(Расширения) <> Тип("Массив") Тогда
ВызватьИсключение "Native список расширений должен иметь тип Массив";
КонецЕсли;
Если Расширения.Количество() <> 0 Тогда
ВызватьИсключение "В своём новом стенде нет расширений базы или сеанса";
КонецЕсли;
КонецЦикла;
Если ТранзакцияАктивна() Тогда
ВызватьИсключение "Чужая или вложенная активная транзакция не допускается";
КонецЕсли;
КонецПроцедурыМодуль своей управляемой формы: команда и серверный вызов
Своя внешняя обработка → основная управляемая форма → модуль формы
Полный клиентский обработчик команды и серверная процедура этой формы; общий модуль отдельно. Не вставляйте общий модуль внутрь обработчика.
&НаКлиенте
Процедура Выполнить(Команда)
ВыполнитьНаСервере();
КонецПроцедуры
&НаСервере
Процедура ВыполнитьНаСервере()
УчебноеВзаимодействие.ВыполнитьПример(Подтверждение);
КонецПроцедурыРазбор по шагам
Сначала повторяется полный паспорт своего стенда/участника/приложения. Подтверждение СВОЕ ОБСУЖДЕНИЕ И ДВА СООБЩЕНИЯ обязательно. Новый Ключ из UUID выводится до первой серверной записи; ПолучитьОбсуждение(Ключ) должен вернуть Неопределено, существующий чат не переиспользуется.
Отдельная локальная КоллекцияИдентификаторовПользователей демонстрирует Добавить/Содержит/Количество/Удалить/Очистить с настоящим своим участником. Это подбор в памяти, не удаление серверной учетной записи. В окончательном составе снова ровно один свой ID.
СоздатьОбсуждение возвращает объект еще до серверного создания. Только новому объекту задаются Ключ, Групповое, ТолькоПросмотр, КонтекстОбсуждения, Интеграция и ВнешнийИдентификатор. Никакой внешней интеграции нет, контекст пуст, заголовок содержит свою метку; readonly Участники не заменяются массивом, а наполняются Добавить.
После Записать получен реальный readonly ID, выводится только он. Конструктор ID обсуждения принимает Строка этого фактического ID. Новый ПолучитьОбсуждение проверяет весь паспорт: ключ/заголовок/флаги/пустые внешние значения/ровно свой участник.
ОтборОбсуждений ограничивается своим точным Ключом, групповым видимым неконтекстным режимом и текущим участником; Количество=2 обнаруживает неожиданные записи. ДатаНачала=Неопределено не гадает часовой пояс/время первого сообщения. Readonly Интеграция только проверяется на отсутствие, не назначается.
УстановитьРежимНаблюдения(false) и УстановитьОтображениеОповещенийОбсуждения(НеБеспокоить) применяются только к новому своему обсуждению/текущему участнику. Оба значения читаются заново. Это два разных состояния, не блокировка доставки сообщений или серверной записи.
СоздатьСообщение требует реальный ИдентификаторОбсуждения, не строковый UUID и не объект обсуждения. Текст — настоящий Новый ФорматированнаяСтрока, Данные — небольшая собственная строка, MIME text/plain. В Получатели добавлен пустой ID со специальной семантикой Никого не оповещать, не все пользователи приложения.
Черновик должен не иметь непредусмотренных вложений/действий/кнопок/шаблона/подстановок. После Записать первый реальный ID сохраняется и читается через собственный строковый roundtrip. Паспорт включает обсуждение, автора, точный текст, небольшие данные, MIME, отсутствие исходного ответа и ненулевую серверную дату; чужое содержимое не выводится.
Вторая карточка с суммой750 создается в том же собственном обсуждении, а ИсходноеСообщение получает реальный ID первой. Fresh read подтверждает именно эту связь. ШаблонСообщения не устанавливается: шаблонный режим иначе игнорировал бы обычные Текст/Данные/Действия.
ОтборСообщений ограничен собственным обсуждением, Количество=3 позволяет обнаружить лишнюю третью запись. Получено ровно два допустимых собственных ID; никаких чужих ID/текстов не печатается. После второго ID в Возр ожидается пустая страница.
Перед обновлением заново проверяются обсуждение и свежая вторая карточка с исходным текстом/данными/автором. Только её Текст меняется на Проверка подтверждена:750 и Записать. Повторное ПолучитьСообщение подтверждает измененный текст при том же реальном ID/данных/ответе.
Удаления и отката нет. Новый чат, два сообщения и настройки остаются учебными артефактами для вашей ручной проверки. При исключении программа не посылает карточки заново и не удаляет что-либо в catch; показанный ключ и полученные IDs служат только собственной ручной сверке частичного состояния.
Как проверить результат
Без правильной фразы, при втором пользователе, внешних данных, неверном приложении/адресе, коллизии или неподготовленных правах Записать не должен выполняться до соответствующего guard.
В собственном интерфейсе после положительного ручного опыта новый UUID-чат содержит карточку Книги и ответ с подтвержденной суммой750. Список участников — только ваш пользователь, подключенных внешних интеграций/контекстного объекта нет.
Fresh read первого/второго IDs проверяет точные Текст, Данные, text/plain, автора и связь ответа. Строковое восстановление идентификаторов не является доказательством существования до этих real reads.
Первый отбор обсуждений возвращает ровно один свой чат, отбор сообщений — ровно два своих ID, следующая страница после второго пуста. Лишняя третья карточка или посторонний ID должны остановить урок до изменения второй.
У своего обсуждения РежимНаблюдения=false и ОтображениеОповещений=НеБеспокоить. Отсутствие визуального оповещения не используется как доказательство, что сообщения не записаны или не доставлены.
Второй ID после изменения тот же, ИсходноеСообщение равно первому ID, Текст изменен и отдельно прочитан. После ошибки/потери ответа проверьте частичный результат вручную по своему ключу; автоматического повтора/удаления нет.
Ожидаемый результат
На отдельном своем сервере создан собственный UUID-чат, две реальные карточки с проверенной связью ответа и одна подтвержденная правка; отборы и собственные настройки оповещений проверены либо зафиксирован конкретный отказ. Никакой сетевой опыт здесь не выполнялся.
На что обратить внимание
Синхронные СоздатьОбсуждение/СоздатьСообщение/ПолучитьСообщения/Записать доступны на сервере, не в тонком клиентском обработчике. Асинхронные методы не имитируются синхронными; неоднозначная native-страница ПолучитьОбсуждениеАсинх здесь не используется.
Идентификаторы обсуждения/сообщения конструктором по строке не валидируются. Ввод пользовательского произвольного ID не разрешен: только ID объектов, созданных этим опытом, и свежее подтверждение их паспорта.
Ключ уникален в пределах базы с учетом независимых разделителей, а не глобально по всем серверам. UUID не гарантирует владение чужим сервером; требуется отдельная VM и вручную проверенная регистрация своего приложения.
Групповое, ТолькоПросмотр, Ключ, КонтекстОбсуждения, Интеграция и ВнешнийИдентификатор могут назначаться только новому обсуждению. Заголовок на клиенте readonly. Участники/Получатели readonly как свойства, но методы их коллекций изменяют состав.
Данные сериализуются сервером в JSON с ограничением100Кб и требованиями XDTO. Урок посылает маленькую строку со своей меткой; он не демонстрирует передачу ссылок базы/секретов/объектов с неподдерживаемой сериализацией. Серверную Дата и Автор код только читает, не подменяет.
Пустой ID адресата означает Никого не оповещать; это не конкретный пользователь и не запрет серверной записи/чтения. РежимНеБеспокоить и РежимНаблюдения тоже не являются настройками безопасности или запретом доставки.
Пагинация После задает относительное положение по серверной сортировке. Два сообщения иногда имеют одинаковую дату, поэтому урок не требует различия секунд или самодельной сортировки ID. Отказ пустой следующей страницы требует ручной сверки, не догадки об успехе.
Проверка/обновление не атомарны, нет транзакционного отката внешнего сервера и автоматического удаления созданного чата. Не запускайте параллельных клиентов/ботов. При потере ответа запись могла выполниться; список/ключ/ID сверяйте вручную только на своем сервере.
Права Администрирование данных на новом стенде заранее нужны для ТолькоПросмотр и получения списка пользователей; privilege не включается программой. Сервер/аккаунты/права/сообщения реально не создавались и сеть не вызывалась. RuntimeVerifiedScenarios=0.
Пример не запускался в вашей базе. Перед применением проверьте его в указанном учебном контексте.