public Mono<im.turms.server.common.access.client.dto.model.conference.Meeting> authAndCreateMeeting(
            @NotNull Long requesterId,
            @NotNull byte[] senderIp,
            @Nullable Long userId,
            @Nullable Long groupId,
            @Nullable String name,
            @Nullable String intro,
            @Nullable String password,
            @Nullable @FutureOrPresent Date startDate) {
        Date now = new Date();
        try {
            Validator.notNull(requesterId, "requesterId");
            Validator.notNull(senderIp, "senderIp");
            Validator.inOptionalLengthRange(name, "name", nameMinLength, nameMaxLength);
            Validator.inOptionalLengthRange(intro, "intro", introMinLength, introMaxLength);
            password = validateAndGeneratePassword(password);
            if (startDate != null) {
                long startDateTime = startDate.getTime();
                if (startDateTime > now.getTime() + maxAllowedStartDateOffsetMillis) {
                    throw ResponseException.get(ResponseStatusCode.ILLEGAL_ARGUMENT,
                            "The meeting start date is too far in the future");
                }
            }
        } catch (Exception e) {
            return Mono.error(e);
        }
        if (!hasConferenceServiceProvider()) {
            return CONFERENCE_NOT_IMPLEMENTED;
        }
        Mono<Void> check;
        if (userId == null) {
            if (groupId == null) {
                check = Mono.empty();
            } else {
                check = userService
                        .isAllowedToSendMessageToTarget(true, false, requesterId, groupId)
                        .flatMap(permission -> permission == ServicePermission.OK
                                ? Mono.empty()
                                : Mono.error(ResponseException.get(permission.code(),
                                        permission.reason())));
            }
        } else {
            check = userService.isAllowedToSendMessageToTarget(false, false, requesterId, userId)
                    .flatMap(permission -> permission == ServicePermission.OK
                            ? Mono.empty()
                            : Mono.error(
                                    ResponseException.get(permission.code(), permission.reason())));
        }
        if (isMaxActiveMeetingCountPerUserEnabled) {
            check = check.then(Mono.defer(() -> pluginManager.invokeFirstExtensionPoint(
                    ConferenceServiceProvider.class,
                    COUNT_ACTIVE_MEETINGS_BY_USER_ID_METHOD,
                    COUNT_ACTIVE_MEETINGS_BY_USER_ID_NOT_IMPLEMENTED,
                    extensionPoint -> extensionPoint.countActiveMeetingsByUserId(requesterId)
                            .flatMap(count -> count > maxActiveMeetingCountPerUser
                                    ? EXCEPTION_CREATE_MEETING_EXCEEDING_MAX_ACTIVE_MEETING_COUNT
                                    : Mono.empty()))));
        }
        String finalPassword = password;
        return check.then(Mono.defer(() -> {
            if (startDate == null || now.after(startDate)) {
                return meetingRepository.inTransaction(clientSession -> insertMeetingRecord(now,
                        0,
                        requesterId,
                        name,
                        intro,
                        finalPassword,
                        now,
                        userId,
                        groupId,
                        clientSession)
                        .flatMap(meeting -> pluginManager.invokeFirstExtensionPoint(
                                ConferenceServiceProvider.class,
                                CREATE_MEETING_METHOD,
                                CREATE_MEETING_NOT_IMPLEMENTED,
                                extensionPoint -> extensionPoint.createMeeting(requesterId,
                                        meeting,
                                        userId == null
                                                ? CREATE_MEETING_OPTIONS_FOR_GROUP_MEETING
                                                : CREATE_MEETING_OPTIONS_FOR_PRIVATE_MEETING)
                                        .map(result -> ProtoModelConvertor
                                                .meeting2proto(meeting, result.accessToken())
                                                .build()))));
            }
            return insertMeetingRecord(now,
                    0,
                    requesterId,
                    name,
                    intro,
                    finalPassword,
                    startDate,
                    userId,
                    groupId,
                    null).map(
                            meeting -> ProtoModelConvertor.meeting2proto(meeting, null)
                                    .build());
        }))
                .flatMap(meeting -> sendMeetingInvitationMessage(requesterId,
                        senderIp,
                        meeting,
                        userId,
                        groupId).thenReturn(meeting));
    }
Use {@link SecureRandom} because users usually input meeting IDs to join meetings, so we
should make sure they are unpredictable.
turms-service/src/main/java/im/turms/service/domain/conference/service/ConferenceService.java