МенеджерСистемыВзаимодействия.УдалитьСообщение

Методы · DeleteMessage

Удаляет указанное сообщение из обсуждения. Вызов метода возможен только в привилегированном режиме.

Синтаксис

УдалитьСообщение(<ИдентификаторСообщения>)
Полное описание, параметры и ограничения

МенеджерСистемыВзаимодействия.УдалитьСообщение (CollaborationSystemManager.DeleteMessage)

МенеджерСистемыВзаимодействия (CollaborationSystemManager)

УдалитьСообщение (DeleteMessage)

Доступен, начиная с версии 8.3.11.

Синтаксис:
УдалитьСообщение(<ИдентификаторСообщения>)
Параметры:

<ИдентификаторСообщения> (обязательный)
Тип: ИдентификаторСообщенияСистемыВзаимодействия.
Идентификатор удаляемого сообщения.
Описание:

Удаляет указанное сообщение из обсуждения.
Вызов метода возможен только в привилегированном режиме.

Доступность:

Сервер, толстый клиент, внешнее соединение.

Использование в версии:

Доступен, начиная с версии 8.3.11.

Методическая информация

Практические примеры

Настоящие свои оповещения: видимое/скрытое обсуждение, внутреннее приглашение и отдельное удаление своей карточки

Собственный получатель получает реальные оповещения двух своих обсуждений после сообщений другого своего участника. Уведомление содержит readonly real discussion ID, не создается конструктором. Внутренний URI не открывается; отдельное подтвержденное удаление выбирает только собственную скрытую карточку.

Где и как запускать

Полный серверный общий модуль УчебныеОповещения85 и полный supporting модуль управляемой формы. Новый собственный УчебныйАбонентСВ85, exactly2real users/one own app, localhost:9094; GetCurrentNotifications только server, DeleteMessage только отдельный author-confirmed own scoped target.

Подготовка

  1. Новая пустая CF УчебныйАбонентСВ85 на8.5.1.1343, русский язык, управляемый интерфейс, совместимость Не использовать. Пустые предметные объекты/подписки/задания, ботов/расширений нет. Не используйте реальный абонент, существующий сервер, рабочую базу или чужие данные.

  2. В изолированной VM вручную разверните собственный отдельный сервер взаимодействия, адрес строго http://localhost:9094. Зарегистрируйте собственный новый абонент только для этого урока. В абоненте ровно одно приложение, его имя УчебныйАбонентСВ85. GetSubscriberApplications проверит real ID/name/count, не только строку адреса.

  3. На своем сервере отключите SMTP, внешний доступ, интеграции и совместное использование приложений. Никаких публичных публикаций, чужих аккаунтов или дополнительных приложений. localhost/UUID — дополнительные страховки, не доказательство владельца сервера; VM/абонент проверяете вручную.

  4. В конфигураторе вручную создайте ровно две свои учетные записи Уч85Получатель_<ваш новый канонический ненулевой UUID> и Уч85Отправитель_<другой ваш UUID>, полные имена Учебный получатель/Учебный отправитель. UUID имени — метка; реальные IB UUID и real collaboration IDs получает и подтверждает код.

  5. Зарегистрируйте и синхронизируйте эти два внутренних профиля со своей базой. Нет внешних ID/почты/телефона/ключей сопоставления; не заблокированы. У Получателя заранее вручную назначено администрирование только собственного абонента; Отправитель первоначально не администратор.

  6. Регистрационный адрес этого собственного абонента задайте lab-<реальный IB UUID пользователя Получатель>@example.invalid. Реальный IB UUID найдите в свойствах собственного пользователя или получите в своем сеансе через ПользователиИнформационнойБазы.ТекущийПользователь().УникальныйИдентификатор. Не подставляйте UUID из имени и действительный email.

  7. example.invalid здесь только формальный учебный адрес. Сервер должен быть заранее подготовлен принимать такой адрес без исходящего SMTP/подтверждений на внешние ящики. Если серверная поставка этого не разрешает, сценарий адреса не запускайте и не заменяйте его персональной почтой; ограничение сохраните честно.

  8. Для этого notification-урока создайте отдельный серверный общий модуль УчебныеОповещения85, только Сервер, Привилегированный/Клиент выключены. Вставьте главный код целиком. В форму вставьте полный supporting module; admin-модуль предыдущего урока для работы оповещений не нужен.

  9. В форме создайте строковые реквизиты ПодтверждениеПодготовки/ПодтверждениеОтправки/ПодтверждениеУдаления длиной100, СвойВидимыйКлюч/СвойСкрытыйКлюч длиной100, СобственноеПриглашение неограниченной длины. Поле приглашения только чтение, обычная строка без автоматического открытия URI. Команды: ПодготовитьПолучателя, ОтправитьДвеКарточки, ПроверитьОповещения, УдалитьСвоюСкрытуюКарточку, одноименные обработчики.

  10. В первом собственном сеансе Получателя задайте ДВА СВОИХ ОПОВЕЩЕНИЯ750 и нажмите подготовку один раз. У нового профиля начальный список оповещений должен быть пуст; прежние/неожиданные не очищаются. Два новых обсуждения содержат ровно собственные реальные Receiver/Sender IDs; одно отображаемое, другое скрытое.

  11. Второй собственный сеанс откройте под другим реальным пользователем Отправителем. Копируйте туда только два полученных UUID-ключа. Задайте ДВЕ МОИ КАРТОЧКИ ПОЛУЧАТЕЛЮ и нажмите отправку один раз. Не используйте fake ID, посторонние учетные записи или отправку ВсеПользователиПриложения.

  12. До проверки оповещений не открывайте/не читайте свои обсуждения интерактивно и не подключайте обработчики новых сообщений: UI/handler может пометить сообщение прочитанным. В обоих сеансах отключены сторонние клиенты/боты/параллельные операции.

  13. В первом сеансе вручную нажимайте ПроверитьОповещения. Она вызывает GetCurrentUserNotifications только на сервере: сам тип доступен thick, но метод по справке только server/external. Ноль/один результат — еще не полная готовность; отправку повторять нельзя.

  14. Native приглашение получают только при ПоддерживаетсяВнешнийДоступ=Ложь и ТолькоВидеоконференция=Ложь. Возвращенный внутренний URI хранится лишь в собственной форме, код не разбирает недокументированный маршрут/токен и не открывает ссылку. До завершения оповещений даже вручную не переходите по ней; не публикуйте/не пересылайте внешним людям.

  15. После успешной проверки двух собственных оповещений и карточек отдельный необязательный опыт удаления выполняет только автор Отправитель. Задайте УДАЛИТЬ МОЮ СКРЫТУЮ КАРТОЧКУ750 и нажмите одноименную команду один раз. Это сознательно разрешает удаление одной своей ранее проверенной карточки, не очищение обсуждений/профилей/уведомлений.

  16. Перед удалением своего сообщения повторно проверяется exact owned discussion/participants/author/marker; реальный MessageID берется из единственной строки отбора только этого обсуждения, не из введенного UUID. Входной privilegedMode запрещен; он включается только вокруг native DeleteMessage и выключается при success/catch.

  17. После удаления программа подтверждает пустое свое скрытое обсуждение и неизменную единственную свою видимую карточку750. Повторную notification-ready проверку этого опыта больше не требуйте: удаление карточки не обязано очищать/удалять backend оповещение автоматически.

  18. Любая ошибка/таймаут может оставить частичный собственный результат. Сохраните показанные кандидаты ключей и проверьте свой выделенный сервер вручную; никакого autoretry/cleanup/восстановления/backup. Реальные 1С/network/DB/files операции при создании этого материала не выполнялись.

Функция ПодготовитьДваСвоихОбсуждения85(Подтверждение) Экспорт
    Пара = ПроверитьСвоюПару85("Получатель");
    Если Подтверждение <> "ДВА СВОИХ ОПОВЕЩЕНИЯ750" Тогда
        ВызватьИсключение "Сначала прочитайте подготовку. Обсуждения не созданы";
    КонецЕсли;
    Если СистемаВзаимодействия.ПоддерживаетсяВнешнийДоступ() Тогда
        ВызватьИсключение "Внешний сайт должен быть отключен. Урок не создает внешнее приглашение";
    КонецЕсли;
    НачальныеОповещения = СистемаВзаимодействия.ПолучитьОповещенияТекущегоПользователя();
    Если ТипЗнч(НачальныеОповещения) <> Тип("Массив") Или НачальныеОповещения.Количество() <> 0 Тогда
        ВызватьИсключение "Нужен новый собственный получатель без прежних оповещений; чужие не очищаются";
    КонецЕсли;
    Пакет = Новый Структура("ВидимыйКлюч,СкрытыйКлюч,ВидимоеОбсуждение,СкрытоеОбсуждение,Получатель,Отправитель,Приглашение",
        "", "", "", "", Строка(Пара.Получатель), Строка(Пара.Отправитель), "");
    Для Индекс = 0 По 1 Цикл
        Видимое = Индекс = 0;
        Ключ = "Уч85Уведомление_" + Строка(Новый УникальныйИдентификатор);
        Сообщить("Свой новый кандидат ключа: " + Ключ);
        Если СистемаВзаимодействия.ПолучитьОбсуждение(Ключ) <> Неопределено Тогда
            ВызватьИсключение "Ключ занят; существующее обсуждение не меняется";
        КонецЕсли;
        Обсуждение = СистемаВзаимодействия.СоздатьОбсуждение();
        Обсуждение.Ключ = Ключ;
        Обсуждение.Заголовок = Ключ + " | Своя карточка750";
        Обсуждение.Групповое = Истина;
        Обсуждение.Отображаемое = Видимое;
        Обсуждение.ТолькоПросмотр = Ложь;
        Обсуждение.КонтекстОбсуждения = Неопределено;
        Обсуждение.Интеграция = Неопределено;
        Обсуждение.ВнешнийИдентификатор = "";
        Обсуждение.Участники.Добавить(Пара.Получатель);
        Обсуждение.Участники.Добавить(Пара.Отправитель);
        Обсуждение.Записать();
        Факт = ПроверитьСвоеОбсуждение85(Ключ, Видимое, Пара);
        Если Факт.Идентификатор <> Обсуждение.Идентификатор Тогда
            ВызватьИсключение "Собственный полученный ID обсуждения не подтвердился";
        КонецЕсли;
        Если Видимое Тогда
            Пакет.ВидимыйКлюч = Ключ;
            Пакет.ВидимоеОбсуждение = Строка(Факт.Идентификатор);
            Пакет.Приглашение = СистемаВзаимодействия.ПолучитьНавигационнуюСсылкуПриглашенияВОбсуждение(Факт.Идентификатор, Ложь);
            Если ТипЗнч(Пакет.Приглашение) <> Тип("Строка") Или Не ЗначениеЗаполнено(Пакет.Приглашение)
                Или СтрДлина(Пакет.Приглашение) > 8192 Тогда
                ВызватьИсключение "Собственное внутреннее приглашение не получено. Обсуждение могло сохраниться";
            КонецЕсли;
        Иначе
            Пакет.СкрытыйКлюч = Ключ;
            Пакет.СкрытоеОбсуждение = Строка(Факт.Идентификатор);
        КонецЕсли;
    КонецЦикла;
    Если Пакет.ВидимоеОбсуждение = Пакет.СкрытоеОбсуждение Тогда
        ВызватьИсключение "Нужны два разных реальных собственных обсуждения";
    КонецЕсли;
    Возврат Пакет;
КонецФункции

Процедура ОтправитьДвеСвоиКарточки85(ВидимыйКлюч, СкрытыйКлюч, Подтверждение) Экспорт
    Пара = ПроверитьСвоюПару85("Отправитель");
    Если Подтверждение <> "ДВЕ МОИ КАРТОЧКИ ПОЛУЧАТЕЛЮ" Или ВидимыйКлюч = СкрытыйКлюч Тогда
        ВызватьИсключение "Нужно отдельное подтверждение отправки двум собственным обсуждениям";
    КонецЕсли;
    Видимое = ПроверитьСвоеОбсуждение85(ВидимыйКлюч, Истина, Пара);
    Скрытое = ПроверитьСвоеОбсуждение85(СкрытыйКлюч, Ложь, Пара);
    Если ПолучитьСобственныеСообщения85(Видимое.Идентификатор).Количество() <> 0
        Или ПолучитьСобственныеСообщения85(Скрытое.Идентификатор).Количество() <> 0 Тогда
        ВызватьИсключение "Обсуждение уже содержит сообщения. Повтор отправки запрещен";
    КонецЕсли;
    Для Индекс = 0 По 1 Цикл
        Если Индекс = 0 Тогда
            Обсуждение = ПроверитьСвоеОбсуждение85(ВидимыйКлюч, Истина, Пара);
        Иначе
            Обсуждение = ПроверитьСвоеОбсуждение85(СкрытыйКлюч, Ложь, Пара);
        КонецЕсли;
        Сообщение = СистемаВзаимодействия.СоздатьСообщение(Обсуждение.Идентификатор);
        Сообщение.Текст = Обсуждение.Ключ + "|Книга|3|250|750";
        Сообщение.Данные = Обсуждение.Ключ + "|750";
        Сообщение.ФорматДанныхСтроки = "text/plain";
        Сообщение.Получатели.Добавить(Пара.Получатель);
        Если Сообщение.Получатели.Количество() <> 1 Или Не Сообщение.Получатели.Содержит(Пара.Получатель)
            Или Сообщение.Вложения.Количество() <> 0 Или Сообщение.Действия.Количество() <> 0
            Или Сообщение.ПанельКнопок.РядыКнопок.Количество() <> 0 Тогда
            ВызватьИсключение "В собственной отправке неожиданное содержимое или адресат";
        КонецЕсли;
        Сообщение.Записать();
        Факт = ПолучитьЕдинственнуюСвоюКарточку85(Обсуждение, Пара.Отправитель);
        Если Факт.Идентификатор <> Сообщение.Идентификатор Тогда
            ВызватьИсключение "Собственная карточка могла сохраниться, но отдельная проверка ID не совпала";
        КонецЕсли;
    КонецЦикла;
    Сообщить("Две свои карточки отдельно подтверждены на сервере. Это не доказательство доставки оповещений");
КонецПроцедуры

Функция ПрочитатьСвоиОповещения85(Пакет) Экспорт
    Пара = ПроверитьСвоюПару85("Получатель");
    ПроверитьСвоиIDПакета85(Пакет, Пара);
    Видимое = ПроверитьСвоеОбсуждение85(Пакет.ВидимыйКлюч, Истина, Пара);
    Скрытое = ПроверитьСвоеОбсуждение85(Пакет.СкрытыйКлюч, Ложь, Пара);
    Если Строка(Видимое.Идентификатор) <> Пакет.ВидимоеОбсуждение
        Или Строка(Скрытое.Идентификатор) <> Пакет.СкрытоеОбсуждение Тогда
        ВызватьИсключение "ID собственных обсуждений изменены";
    КонецЕсли;
    Оповещения = СистемаВзаимодействия.ПолучитьОповещенияТекущегоПользователя();
    Если ТипЗнч(Оповещения) <> Тип("Массив") Тогда
        ВызватьИсключение "Native GetCurrentUserNotifications должен вернуть Массив";
    КонецЕсли;
    Видимых = 0;
    Скрытых = 0;
    Для Каждого Оповещение Из Оповещения Цикл
        Если ТипЗнч(Оповещение) <> Тип("ОповещениеСистемыВзаимодействия") Тогда
            ВызватьИсключение "Ожидалось настоящее серверное оповещение, не самостоятельно созданный объект";
        КонецЕсли;
        ИДОбсуждения = Оповещение.Обсуждение;
        Если ТипЗнч(ИДОбсуждения) <> Тип("ИдентификаторОбсужденияСистемыВзаимодействия") Тогда
            ВызватьИсключение "Оповещение не содержит native ID обсуждения";
        КонецЕсли;
        Если ИДОбсуждения = Видимое.Идентификатор Тогда
            Видимых = Видимых + 1;
        ИначеЕсли ИДОбсуждения = Скрытое.Идентификатор Тогда
            Скрытых = Скрытых + 1;
        Иначе
            ВызватьИсключение "В новом собственном профиле неожиданное другое оповещение: оно не выводится и не очищается";
        КонецЕсли;
    КонецЦикла;
    Если Видимых > 1 Или Скрытых > 1 Тогда
        ВызватьИсключение "Неожиданное дублирование оповещений своих обсуждений";
    КонецЕсли;
    Готово = Видимых = 1 И Скрытых = 1;
    Если Готово Тогда
        ПолучитьЕдинственнуюСвоюКарточку85(Видимое, Пара.Отправитель);
        ПолучитьЕдинственнуюСвоюКарточку85(Скрытое, Пара.Отправитель);
    КонецЕсли;
    Возврат Новый Структура("Готово,Видимых,Скрытых", Готово, Видимых, Скрытых);
КонецФункции

Процедура УдалитьТолькоСвоюСкрытуюКарточку85(ВидимыйКлюч, СкрытыйКлюч, Подтверждение) Экспорт
    Пара = ПроверитьСвоюПару85("Отправитель");
    Если Подтверждение <> "УДАЛИТЬ МОЮ СКРЫТУЮ КАРТОЧКУ750" Или ВидимыйКлюч = СкрытыйКлюч Тогда
        ВызватьИсключение "Удаление не выполняется без отдельного точного подтверждения";
    КонецЕсли;
    Видимое = ПроверитьСвоеОбсуждение85(ВидимыйКлюч, Истина, Пара);
    Скрытое = ПроверитьСвоеОбсуждение85(СкрытыйКлюч, Ложь, Пара);
    ПолучитьЕдинственнуюСвоюКарточку85(Видимое, Пара.Отправитель);
    СвояКарточка = ПолучитьЕдинственнуюСвоюКарточку85(Скрытое, Пара.Отправитель);
    ИДСвоейКарточки = СвояКарточка.Идентификатор;
    Если ТипЗнч(ИДСвоейКарточки) <> Тип("ИдентификаторСообщенияСистемыВзаимодействия") Тогда
        ВызватьИсключение "Нужен real ID собственного сообщения из exact scoped selection";
    КонецЕсли;
    ПроверитьСвоеОбсуждение85(СкрытыйКлюч, Ложь, Пара);
    Попытка
        УстановитьПривилегированныйРежим(Истина);
        СистемаВзаимодействия.УдалитьСообщение(ИДСвоейКарточки);
    Исключение
        УстановитьПривилегированныйРежим(Ложь);
        ВызватьИсключение "Удаление могло произойти, но не подтверждено. PrivilegedMode выключен; повторов и автоматического cleanup нет";
    КонецПопытки;
    УстановитьПривилегированныйРежим(Ложь);
    ПроверитьСвоюПару85("Отправитель");
    Скрытое = ПроверитьСвоеОбсуждение85(СкрытыйКлюч, Ложь, Пара);
    Если ПолучитьСобственныеСообщения85(Скрытое.Идентификатор).Количество() <> 0 Тогда
        ВызватьИсключение "Удаление собственного скрытого сообщения отдельно не подтверждено";
    КонецЕсли;
    Видимое = ПроверитьСвоеОбсуждение85(ВидимыйКлюч, Истина, Пара);
    ПолучитьЕдинственнуюСвоюКарточку85(Видимое, Пара.Отправитель);
    Сообщить("Удалена только своя скрытая карточка750; своя видимая сохранена. Обсуждение/профили/оповещения отдельно не удалялись");
КонецПроцедуры

Процедура ПроверитьСвоиIDПакета85(Пакет, Пара)
    Если ТипЗнч(Пакет) <> Тип("Структура") Или Пакет.Получатель <> Строка(Пара.Получатель)
        Или Пакет.Отправитель <> Строка(Пара.Отправитель)
        Или Пакет.ВидимыйКлюч = Пакет.СкрытыйКлюч Или Пакет.ВидимоеОбсуждение = Пакет.СкрытоеОбсуждение Тогда
        ВызватьИсключение "Нужен собственный пакет двух разных реальных обсуждений";
    КонецЕсли;
КонецПроцедуры

Функция ПроверитьСвоеОбсуждение85(Ключ, Видимость, Пара)
    Если ТипЗнч(Ключ) <> Тип("Строка") Или Лев(Ключ, СтрДлина("Уч85Уведомление_")) <> "Уч85Уведомление_" Тогда
        ВызватьИсключение "Передайте только полученный собственный UUID-ключ";
    КонецЕсли;
    UUIDКлюча = Новый УникальныйИдентификатор(Сред(Ключ, СтрДлина("Уч85Уведомление_") + 1));
    Если Ключ <> "Уч85Уведомление_" + Строка(UUIDКлюча)
        Или UUIDКлюча = Новый УникальныйИдентификатор("00000000-0000-0000-0000-000000000000") Тогда
        ВызватьИсключение "Канонический собственный UUID-ключ не подтвержден";
    КонецЕсли;
    Факт = СистемаВзаимодействия.ПолучитьОбсуждение(Ключ);
    Если Факт = Неопределено Тогда
        ВызватьИсключение "Собственное обсуждение не найдено; IDs не выдумываются";
    КонецЕсли;
    Если Факт.Ключ <> Ключ Или Факт.Заголовок <> Ключ + " | Своя карточка750"
        Или Не Факт.Групповое Или Факт.Отображаемое <> Видимость Или Факт.ТолькоПросмотр
        Или Факт.КонтекстОбсуждения <> Неопределено Или Факт.Интеграция <> Неопределено
        Или Факт.ВнешнийИдентификатор <> "" Или Факт.Участники.Количество() <> 2
        Или Не Факт.Участники.Содержит(Пара.Получатель) Или Не Факт.Участники.Содержит(Пара.Отправитель) Тогда
        ВызватьИсключение "Паспорт собственного обсуждения двух участников изменен";
    КонецЕсли;
    Возврат Факт;
КонецФункции

Функция ПолучитьСобственныеСообщения85(ИДОбсуждения)
    Отбор = Новый ОтборСообщенийСистемыВзаимодействия;
    Отбор.Обсуждение = ИДОбсуждения;
    Отбор.Количество = 2;
    Отбор.НаправлениеСортировки = НаправлениеСортировки.Возр;
    Возврат СистемаВзаимодействия.ПолучитьСообщения(Отбор);
КонецФункции

Функция ПолучитьЕдинственнуюСвоюКарточку85(Обсуждение, ИДАвтора)
    Сообщения = ПолучитьСобственныеСообщения85(Обсуждение.Идентификатор);
    Если ТипЗнч(Сообщения) <> Тип("Массив") Или Сообщения.Количество() <> 1 Тогда
        ВызватьИсключение "Нужна ровно одна собственная карточка750 в выбранном обсуждении";
    КонецЕсли;
    Факт = Сообщения[0];
    Если Факт.Обсуждение <> Обсуждение.Идентификатор Или Факт.Автор <> ИДАвтора
        Или Факт.Текст <> Обсуждение.Ключ + "|Книга|3|250|750" Или Факт.Данные <> Обсуждение.Ключ + "|750"
        Или Факт.Вложения.Количество() <> 0 Или Факт.Действия.Количество() <> 0 Тогда
        ВызватьИсключение "Собственный автор, marker или содержимое карточки изменены";
    КонецЕсли;
    Возврат Факт;
КонецФункции

Функция ПроверитьСвоюПару85(Роль)
    ПроверитьСвойПустойСтенд();
    Если ПривилегированныйРежим() Или (Роль <> "Получатель" И Роль <> "Отправитель") Тогда
        ВызватьИсключение "Роль или привилегированный режим неверны";
    КонецЕсли;
    Если Не СистемаВзаимодействия.ИнформационнаяБазаЗарегистрирована()
        Или Не СистемаВзаимодействия.ИспользованиеДоступно()
        Или СистемаВзаимодействия.АдресСервера() <> "http://localhost:9094" Тогда
        ВызватьИсключение "Нужно собственное изолированное приложение на localhost:9094";
    КонецЕсли;
    Приложение = СистемаВзаимодействия.ПолучитьПриложение(СистемаВзаимодействия.ИдентификаторТекущегоПриложения());
    Если Приложение.Идентификатор <> СистемаВзаимодействия.ИдентификаторТекущегоПриложения()
        Или Приложение.Наименование <> "УчебныйАбонентСВ85" Тогда
        ВызватьИсключение "Наименование своего нового приложения не совпало";
    КонецЕсли;
    Пользователи = ПользователиИнформационнойБазы.ПолучитьПользователей();
    Текущий = ПользователиИнформационнойБазы.ТекущийПользователь();
    Если Пользователи.Количество() <> 2 Или Текущий = Неопределено Тогда
        ВызватьИсключение "Нужны ровно две собственные UUID-учетные записи";
    КонецЕсли;
    Пара = Новый Структура("Получатель,Отправитель", Неопределено, Неопределено);
    ТекущийПодтвержден = Ложь;
    Для Каждого ПользовательБазы Из Пользователи Цикл
        Если Лев(ПользовательБазы.Имя, СтрДлина("Уч85Получатель_")) = "Уч85Получатель_" Тогда
            ИмяРоли = "Получатель";
        ИначеЕсли Лев(ПользовательБазы.Имя, СтрДлина("Уч85Отправитель_")) = "Уч85Отправитель_" Тогда
            ИмяРоли = "Отправитель";
        Иначе
            ВызватьИсключение "Посторонняя учетная запись недопустима";
        КонецЕсли;
        Префикс = "Уч85" + ИмяРоли + "_";
        UUIDИмени = Новый УникальныйИдентификатор(Сред(ПользовательБазы.Имя, СтрДлина(Префикс) + 1));
        Если ПользовательБазы.Имя <> Префикс + Строка(UUIDИмени)
            Или UUIDИмени = Новый УникальныйИдентификатор("00000000-0000-0000-0000-000000000000") Тогда
            ВызватьИсключение "Имя своего участника должно содержать канонический ненулевой UUID";
        КонецЕсли;
        ИД = СистемаВзаимодействия.ПолучитьИдентификаторПользователя(ПользовательБазы.УникальныйИдентификатор);
        Если ТипЗнч(ИД) <> Тип("ИдентификаторПользователяСистемыВзаимодействия")
            Или ИД = Новый ИдентификаторПользователяСистемыВзаимодействия
            Или ИД = СистемаВзаимодействия.СтандартныеПользователи.ВсеПользователиПриложения
            Или СистемаВзаимодействия.ПолучитьИдентификаторПользователяИнформационнойБазы(ИД) <> ПользовательБазы.УникальныйИдентификатор Тогда
            ВызватьИсключение "Реальная связка своего пользователя с базой не подтверждена";
        КонецЕсли;
        Пользователь = СистемаВзаимодействия.ПолучитьПользователя(ИД);
        Если Пользователь.Идентификатор <> ИД Или Пользователь.Имя <> ПользовательБазы.Имя
            Или Пользователь.ПолноеИмя <> ПользовательБазы.ПолноеИмя
            Или Пользователь.ИдентификаторПользователяИнформационнойБазы <> ПользовательБазы.УникальныйИдентификатор
            Или Пользователь.Внешний Или Пользователь.Заблокирован Или Пользователь.ТипВнешнейСистемы <> ""
            Или Пользователь.ИдентификаторПользователяВнешнейСистемы <> Неопределено
            Или Пользователь.АдресЭлектроннойПочты <> "" Или Пользователь.АдресЭлектроннойПочтыДляАутентификации <> ""
            Или Пользователь.НомерТелефона <> "" Или Пользователь.КлючСопоставления <> "" Тогда
            ВызватьИсключение "Нужен собственный внутренний учебный паспорт без персональных контактов";
        КонецЕсли;
        Если Пара[ИмяРоли] <> Неопределено Тогда
            ВызватьИсключение "Роль продублирована";
        КонецЕсли;
        Пара[ИмяРоли] = ИД;
        Если ПользовательБазы.УникальныйИдентификатор = Текущий.УникальныйИдентификатор Тогда
            Если ИмяРоли <> Роль Или ИД <> СистемаВзаимодействия.ИдентификаторТекущегоПользователя() Тогда
                ВызватьИсключение "Войдите под своей учетной записью нужной роли";
            КонецЕсли;
            ТекущийПодтвержден = Истина;
        КонецЕсли;
    КонецЦикла;
    Если Не ТекущийПодтвержден Или Пара.Получатель = Неопределено Или Пара.Отправитель = Неопределено
        Или Пара.Получатель = Пара.Отправитель Тогда
        ВызватьИсключение "Не получены два разных реальных собственных ID";
    КонецЕсли;
    Приложения = СистемаВзаимодействия.ПолучитьПриложенияАбонента();
    Если ТипЗнч(Приложения) <> Тип("Массив") Или Приложения.Количество() <> 1 Тогда
        ВызватьИсключение "У своего отдельного абонента должно быть ровно одно собственное приложение";
    КонецЕсли;
    Если ТипЗнч(Приложения[0]) <> Тип("ПриложениеСистемыВзаимодействия")
        Или Приложения[0].Идентификатор <> СистемаВзаимодействия.ИдентификаторТекущегоПриложения()
        Или Приложения[0].Наименование <> "УчебныйАбонентСВ85" Тогда
        ВызватьИсключение "Список собственного абонента содержит другое приложение";
    КонецЕсли;
    Возврат Пара;
КонецФункции

Процедура ПроверитьСвойПустойСтенд()
    Если Метаданные.Справочники.Количество() <> 0 Или Метаданные.Документы.Количество() <> 0 Или Метаданные.Перечисления.Количество() <> 0 Или Метаданные.РегистрыСведений.Количество() <> 0 Или Метаданные.РегистрыНакопления.Количество() <> 0 Или Метаданные.РегистрыБухгалтерии.Количество() <> 0 Или Метаданные.РегистрыРасчета.Количество() <> 0 Или Метаданные.ПланыВидовХарактеристик.Количество() <> 0 Или Метаданные.ПланыВидовРасчета.Количество() <> 0 Или Метаданные.ПланыСчетов.Количество() <> 0 Или Метаданные.ПланыОбмена.Количество() <> 0 Или Метаданные.Последовательности.Количество() <> 0 Или Метаданные.БизнесПроцессы.Количество() <> 0 Или Метаданные.Задачи.Количество() <> 0 Или Метаданные.ОбщиеРеквизиты.Количество() <> 0 Или Метаданные.ПодпискиНаСобытия.Количество() <> 0 Или Метаданные.РегламентныеЗадания.Количество() <> 0 Или Метаданные.ВнешниеИсточникиДанных.Количество() <> 0 Тогда
        ВызватьИсключение "Нужен только свой новый пустой учебный стенд";
    КонецЕсли;
    Если Метаданные.ЕстьИзмененияРасширениямиКонфигурации() Тогда
        ВызватьИсключение "Свой сеанс не должен изменяться расширениями";
    КонецЕсли;
    Источники = Новый Массив;
    Источники.Добавить(ИсточникРасширенийКонфигурации.БазаДанных);
    Источники.Добавить(ИсточникРасширенийКонфигурации.СеансАктивные);
    Источники.Добавить(ИсточникРасширенийКонфигурации.СеансОтключенные);
    Для Каждого Источник Из Источники Цикл
        Расширения = РасширенияКонфигурации.Получить(, Источник);
        Если ТипЗнч(Расширения) <> Тип("Массив") Тогда
            ВызватьИсключение "Native список расширений должен иметь тип Массив";
        КонецЕсли;
        Если Расширения.Количество() <> 0 Тогда
            ВызватьИсключение "В своём новом стенде нет расширений базы или сеанса";
        КонецЕсли;
    КонецЦикла;
    Если ТранзакцияАктивна() Тогда
        ВызватьИсключение "Чужая или вложенная активная транзакция не допускается";
    КонецЕсли;
КонецПроцедуры
Полный модуль собственной управляемой формы: два сеанса, ручной poll и отдельная own-delete команда

Собственная внешняя обработка → Управляемая форма → Модуль формы.

Полный клиент-серверный модуль собственной управляемой формы; GetCurrentUserNotifications остается на сервере.

Перем СвойПакет85;
Перем НачатаПодготовка85;
Перем НачатаОтправка85;
Перем НачатоУдаление85;

&НаКлиенте
Процедура ПодготовитьПолучателя(Команда)
    Если НачатаПодготовка85 = Истина Тогда
        ВызватьИсключение "Повтор запрещен; возможный частичный результат проверяйте вручную";
    КонецЕсли;
    НачатаПодготовка85 = Истина;
    СвойПакет85 = ПодготовитьНаСервере85();
    СвойВидимыйКлюч = СвойПакет85.ВидимыйКлюч;
    СвойСкрытыйКлюч = СвойПакет85.СкрытыйКлюч;
    СобственноеПриглашение = СвойПакет85.Приглашение;
    Сообщить("Два своих обсуждения подготовлены. Во второй собственный сеанс копируйте только два UUID-ключа. Внутреннее приглашение в поле, не в журнале");
КонецПроцедуры

&НаСервере
Функция ПодготовитьНаСервере85()
    Возврат УчебныеОповещения85.ПодготовитьДваСвоихОбсуждения85(ПодтверждениеПодготовки);
КонецФункции

&НаКлиенте
Процедура ОтправитьДвеКарточки(Команда)
    Если НачатаОтправка85 = Истина Тогда
        ВызватьИсключение "Повтор отправки запрещен; возможный частичный результат проверяйте вручную";
    КонецЕсли;
    НачатаОтправка85 = Истина;
    ОтправитьНаСервере85(СвойВидимыйКлюч, СвойСкрытыйКлюч, ПодтверждениеОтправки);
КонецПроцедуры

&НаСервереБезКонтекста
Процедура ОтправитьНаСервере85(ВидимыйКлюч, СкрытыйКлюч, Фраза)
    УчебныеОповещения85.ОтправитьДвеСвоиКарточки85(ВидимыйКлюч, СкрытыйКлюч, Фраза);
КонецПроцедуры

&НаКлиенте
Процедура ПроверитьОповещения(Команда)
    Если СвойПакет85 = Неопределено Тогда
        ВызватьИсключение "Сначала подготовьте собственный пакет именно в сеансе Получателя";
    КонецЕсли;
    Результат = ПрочитатьНаСервере85(СвойПакет85);
    Сообщить("Native оповещения своих обсуждений: видимых=" + Строка(Результат.Видимых) + ", скрытых=" + Строка(Результат.Скрытых));
    Если Результат.Готово Тогда
        Сообщить("Получены два настоящих своих оповещения и отдельно подтверждены обе карточки750");
    Иначе
        Сообщить("Полного результата пока нет. Не повторяйте отправку; позже вручную нажмите только эту проверку");
    КонецЕсли;
КонецПроцедуры

&НаСервереБезКонтекста
Функция ПрочитатьНаСервере85(Пакет)
    Возврат УчебныеОповещения85.ПрочитатьСвоиОповещения85(Пакет);
КонецФункции

&НаКлиенте
Процедура УдалитьСвоюСкрытуюКарточку(Команда)
    Если НачатоУдаление85 = Истина Тогда
        ВызватьИсключение "Повтор удаления запрещен; состояние проверяйте вручную";
    КонецЕсли;
    НачатоУдаление85 = Истина;
    УдалитьНаСервере85(СвойВидимыйКлюч, СвойСкрытыйКлюч, ПодтверждениеУдаления);
КонецПроцедуры

&НаСервереБезКонтекста
Процедура УдалитьНаСервере85(ВидимыйКлюч, СкрытыйКлюч, Фраза)
    УчебныеОповещения85.УдалитьТолькоСвоюСкрытуюКарточку85(ВидимыйКлюч, СкрытыйКлюч, Фраза);
КонецПроцедуры

Разбор по шагам

  1. Проверка пары подтверждает actual IB users ↔ actual collaboration IDs в обе стороны, текущую роль, пустой собственный CF, точную платформу, localhost и единственное свое приложение абонента. Все API-вызовы подготовки выполняются на сервере.

  2. До изменений точная фраза разрешает только два новых own discussions. ExternalAccessSupported=true дает отказ: этот урок не публикует внешних пользователей/сайт и не создает внешнее приглашение.

  3. Пустой начальный GetCurrentUserNotifications защищает от смешивания с прежними результатами. Никакие чужие уведомления/профили не читаются по содержимому, не очищаются и не журналируются.

  4. Создаются два разных реальных обсуждения с двумя собственными участниками, exact UUID key/title, без контекста/интеграции/внешних ID. Отличается только Отображаемое: Истина и Ложь.

  5. GetInviteUrlToConversation получает фактический ID только своего отображаемого обсуждения и явное Ложь. При выключенном внешнем доступе native возвращает внутреннюю ссылку; ее форма не угадывается. Оpaque строка не является API-endpoint/командой и сама ничего не открывает.

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

  7. В каждое собственное обсуждение записывается книга3×250=750; recipients содержит только real Получатель ID. Записанные own message IDs сверяются отдельным scoped server read; факт записи не равен факту доставки alert.

  8. Получатель свежими чтениями подтверждает собственные IDs/participants/displayed. Затем GetCurrentUserNotifications возвращает настоящий Массив объектов ОповещениеСистемыВзаимодействия; readonly Обсуждение содержит typed native ID, а не весь объект обсуждения.

  9. Подсчет сравнивает реальные IDs с двумя подтвержденными своими обсуждениями. Другой ID не раскрывается, не загружается как чужое обсуждение и вызывает безопасный отказ. Дубли одного own ID также не маскируются.

  10. Только один alert каждого собственного обсуждения означает Готово=Истина. Тогда отдельные own scoped reads подтверждают обе реальные карточки750. Частичная доставка0/1 не считается успехом и не вызывает повторную отправку.

  11. GetCurrentUserNotifications по native возвращает alerts всех обсуждений независимо от Отображаемое: скрытый own discussion здесь дает реальный учебный положительный случай, не формальное чтение поля.

  12. Опыт DeleteMessage выполняет только author Sender после отдельной фразы. В обоих обсуждениях должна быть ровно одна собственная карточка; targetrealID выбирается исключительно из own hidden scoped selection с проверенным автором/text/data.

  13. Native DeleteMessage требует privilegedMode. После всех guards режим0→1 только на одну операцию; success и catch устанавливают0. Нет API SetCurrentUser/имперсонации и нет входного привилегированного сеанса.

  14. После выключения режима свежие role/app/user guards выполняются снова. Hidden own message list=0, visible own exact card остается1. Не удаляются обсуждения, профили или notification objects; неизвестна политика удаления alert на конкретном сервере.

Как проверить результат

  1. Пустое подтверждение, другой сервер/приложение/участник, внешняя публикация или непустой начальный список → отказ до создания нового опыта.

  2. Уведомления настоящие typed objects из server GetCurrentUserNotifications; readonly Обсуждение не присваивается, ctor отсутствует/не имитируется.

  3. В положительном маршруте alerts есть для видимого и скрытого своих обсуждений; их actual ID совпадает с отдельно полученными own discussion IDs. Backend список и UI-показ — разные вещи.

  4. Внутреннее приглашение получено по actualown ID и хранится только в собственной форме; нигде не запускается/не логируется/не интерпретируется как выдуманный маршрут.

  5. До выполнения DeleteMessage подтверждены current Sender/own app/exact two participants/one own target card. Удаляется ее actual ID из scoped query, а не любой переданный клиентом UUID.

  6. Входной privilegedMode отклоняется; после native удаления/ошибки он выключен. Hidden=0 и visible=1 подтверждены только свежими чтениями, не completion-signal.

  7. После удаления прежнее условие notification-ready не предъявляется: удаление сообщения не заявлено как способ очистить список оповещений.

Ожидаемый результат

При ручном успешном запуске своего двухпользовательского стенда на сервере подтверждаются две own cards750 и два настоящих alerts, включая скрытое обсуждение. Дополнительный явно разрешенный DeleteMessage удаляет только own hidden card и отдельно подтверждает visible card. Здесь только статическая/native проверка, runtime1С/отправки/удаления отсутствуют.

На что обратить внимание

  1. Оповещение не создается самостоятельно: no native constructor. Его Обсуждение — readonly ИдентификаторОбсуждения, не объект, имя/текст/MessageID.

  2. Сам тип доступен server/thick/external, но GetCurrentUserNotifications объявлен только server/external. Не переносите этот метод в thick-client handler.

  3. Отображаемое=Ложь не обещает отсутствие backend alerts. Но интерфейс/подключенный handler может изменить read status, поэтому исходный опыт выполняется без интерактивного чтения обсуждений.

  4. Нет SLA доставки или гарантии первого полного poll. Ноль/один — честный промежуточный результат; бессмысленное повторение отправки создало бы дубли.

  5. Opaque внутренний URI может содержать данные доступа; не печатайте/не публикуйте его. Здесь его не открывают и не угадывают схему e1c/http/маршрут.

  6. GetInviteUrl native при выключенном внешнем доступе возвращает внутреннее приглашение; OnlyVideo=false разрешает обсуждение. Видеоконференции/провайдеры этим уроком не покрываются.

  7. DeleteMessage требует привилегированного режима и действительно удаляет message. Не применяйте к чужим IDs/массовому списку/production; отдельная фраза ограничивает только own verified hidden750.

  8. Включение privilegedMode не является проверкой собственности. Все guards/selection/author/data выполняются ДО него, без прямого GetMessage по переданному клиентом ID.

  9. При ошибке native удаления физическое изменение может уже произойти; выключение режима — не rollback. Автоматического повторения/очистки/восстановления нет.

  10. Удаление собственной карточки не является очисткой alert. После этого сохраните отдельные критерии результата; не выдавайте отсутствие сообщения за отсутствие уведомления.

  11. UUID-ключ и локальный хост — страховки, не доказательство владельца абонента. Собственная новая VM, real users и единственное own app обязательны.

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

Не хотите разбираться сами?

Адаптируем это решение под вашу базу. Оценка бесплатно.

Оставить задачу

1С, 1С:Предприятие — товарные знаки ООО «1С». Сайт не является официальным представительством фирмы «1С» и не аффилирован с ней.