public Mono<GroupInvitation> authAndRecallPendingGroupInvitation(
            @NotNull Long requesterId,
            @NotNull Long invitationId) {
        try {
            Validator.notNull(requesterId, "requesterId");
            Validator.notNull(invitationId, "invitationId");
        } catch (ResponseException e) {
            return Mono.error(e);
        }
        boolean allowRecallBySender = allowRecallPendingInvitationBySender;
        if (!allowRecallPendingInvitationByOwnerAndManager && !allowRecallBySender) {
            return Mono.error(ResponseException
                    .get(ResponseStatusCode.RECALLING_GROUP_INVITATION_IS_DISABLED));
        }
        Mono<GroupInvitation> queryInvitation;
        Mono<GroupInvitation> errorIfEmpty;
        if (allowRecallBySender) {
            queryInvitation = queryGroupIdAndInviterIdAndInviteeIdAndStatus(invitationId);
            // TODO: cache
            errorIfEmpty = Mono.error(ResponseException.get(
                    ResponseStatusCode.NOT_GROUP_OWNER_OR_MANAGER_OR_SENDER_TO_RECALL_GROUP_INVITATION));
        } else {
            queryInvitation = queryGroupIdAndInviteeIdAndStatus(invitationId);
            errorIfEmpty = Mono.error(ResponseException
                    .get(ResponseStatusCode.NOT_GROUP_OWNER_OR_MANAGER_TO_RECALL_GROUP_INVITATION));
        }
        return queryInvitation.switchIfEmpty(errorIfEmpty)
                .flatMap(invitation -> {
                    Mono<Void> checkIfRequesterHasPermission =
                            allowRecallBySender && requesterId.equals(invitation.getInviterId())
                                    ? Mono.empty()
                                    : groupMemberService
                                            .isOwnerOrManager(requesterId,
                                                    invitation.getGroupId(),
                                                    false)
                                            .flatMap(isOwnerOrManager -> isOwnerOrManager
                                                    ? Mono.empty()
                                                    : errorIfEmpty.then());
                    return checkIfRequesterHasPermission.then(Mono.defer(() -> {
                        RequestStatus requestStatus = invitation.getStatus();
                        if (requestStatus == RequestStatus.PENDING) {
                            if (groupInvitationRepository.isExpired(invitation.getCreationDate()
                                    .getTime())) {
                                return Mono.error(ResponseException.get(
                                        ResponseStatusCode.RECALL_NON_PENDING_GROUP_INVITATION,
                                        "The invitation is under the status "
                                                + RequestStatus.EXPIRED));
                            }
                        } else {
                            return Mono.error(ResponseException.get(
                                    ResponseStatusCode.RECALL_NON_PENDING_GROUP_INVITATION,
                                    "The invitation is under the status "
                                            + requestStatus));
                        }
                        return groupInvitationRepository
                                .updateStatusIfPending(invitationId,
                                        RequestStatus.CANCELED,
                                        null,
                                        null)
                                .flatMap(result -> result.getModifiedCount() == 0
                                        // Though it may be 0 because the request had been
                                        // deleted between the status check,
                                        // but only admins can delete them, and it is really
                                        // rare.
                                        // So we handle these cases as if the status of the
                                        // request has changed.
                                        ? Mono.error(ResponseException.get(
                                                ResponseStatusCode.RECALL_NON_PENDING_GROUP_INVITATION))
                                        : groupVersionService
                                                .updateGroupInvitationsVersion(
                                                        invitation.getGroupId())
                                                .onErrorResume(t -> {
                                                    LOGGER.error(
                                                            "Caught an error while updating the group invitations version of the group ({}) after recalling a pending invitation",
                                                            invitation.getGroupId(),
                                                            t);
                                                    return Mono.empty();
                                                })
                                                .thenReturn(invitation));
                    }));
                });
    }
@return group ID, invitee ID, and status
turms-service/src/main/java/im/turms/service/domain/group/service/GroupInvitationService.java