public Mono<UserFriendRequest> createFriendRequest(
            @Nullable Long id,
            @NotNull Long requesterId,
            @NotNull Long recipientId,
            @NotNull String content,
            @Nullable @ValidRequestStatus RequestStatus status,
            @Nullable @PastOrPresent Date creationDate,
            @Nullable @PastOrPresent Date responseDate,
            @Nullable String reason) {
        try {
            Validator.notNull(requesterId, "requesterId");
            Validator.notNull(recipientId, "recipientId");
            Validator.notNull(content, "content");
            Validator.maxLength(content, "content", maxContentLength);
            DataValidator.validRequestStatus(status);
            Validator.pastOrPresent(creationDate, "creationDate");
            Validator.pastOrPresent(responseDate, "responseDate");
            Validator.notEquals(requesterId,
                    recipientId,
                    "The requester ID must not be equal to the recipient ID");
            Validator.maxLength(reason, "reason", maxResponseReasonLength);
        } catch (ResponseException e) {
            return Mono.error(e);
        }
        id = id == null
                ? node.nextLargeGapId(ServiceType.USER_FRIEND_REQUEST)
                : id;
        Date now = new Date();
        if (creationDate == null) {
            creationDate = now;
        } else {
            creationDate = creationDate.before(now)
                    ? creationDate
                    : now;
        }
        responseDate = getResponseDateBasedOnStatusForNewRecord(now, status, responseDate);
        if (status == null) {
            status = RequestStatus.PENDING;
        }
        UserFriendRequest userFriendRequest = new UserFriendRequest(
                id,
                content,
                status,
                reason,
                creationDate,
                responseDate,
                requesterId,
                recipientId);
        return userFriendRequestRepository.insert(userFriendRequest)
                .then(Mono.whenDelayError(
                        userVersionService.updateReceivedFriendRequestsVersion(recipientId)
                                .onErrorResume(t -> {
                                    LOGGER.error(
                                            "Caught an error while updating the received friend requests version of the recipient ({}) after creating a friend request",
                                            recipientId,
                                            t);
                                    return Mono.empty();
                                }),
                        userVersionService.updateSentFriendRequestsVersion(requesterId)
                                .onErrorResume(t -> {
                                    LOGGER.error(
                                            "Caught an error while updating the sent friend requests version of the requester ({}) after creating a friend request",
                                            requesterId,
                                            t);
                                    return Mono.empty();
                                })))
                .thenReturn(userFriendRequest);
    }
@author James Chen
@implNote The status of friend requests never become EXPIRED in MongoDB automatically (admins can
          specify them to expired manually though) even if there is an expireAfter property
          because Turms will not create a cron job to scan and expire requests in MongoDB.
          Instead, Turms transforms the status of requests when returning them to users or admins
          for less resource consumption and better performance to expire requests.
turms-service/src/main/java/im/turms/service/domain/user/service/UserFriendRequestService.java