1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/veal98-Echo

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
Клонировать/Скачать
Внести вклад в разработку кода
Синхронизировать код
Отмена
Подсказка: Поскольку Git не поддерживает пустые директории, создание директории приведёт к созданию пустого файла .keep.
Loading...
README.md

Эхо — система открытого сообщества



star fork GitHub stars GitHub forks version

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

📚 Описание проекта

Эхо представляет собой систему открытого доступа сообщества, основанную на современной технологии Java Web (SpringBoot + MyBatis + MySQL + Redis + Kafka + Elasticsearch + Spring Security + ...). Она предоставляет подробные руководства по разработке и сопутствующие учебные материалы. Включает в себя модули таких как темы, комментарии, личные сообщения, системные уведомления, лайки, подписки, поиск, настройки пользователя, статистика данных и другие.

Исходный код: хранится на GitHub и Gitee:

Адрес учебных материалов: Документация создаётся через Docsify + Github/Gitee Pages

💻 Основные технологииЗадний план:

  • Spring
  • Spring Boot 2.1.5 RELEASE
  • Spring MVC
  • ORM: MyBatis
  • База данных: MySQL 5.7
  • Распределённый кэш: Redis
  • Локальный кэш: Caffeine
  • Очередь сообщений: Kafka 2.13-2.7.0
  • Поиск: Elasticsearch 6.4.3
  • Безопасность: Spring Security
  • Почтовые задачи: Spring Mail
  • Распределённые задачи планировщика: Spring Quartz
  • Журналирование: SLF4J (интерфейс журналирования) + Logback (реализация журналирования)

Передний план:

  • Thymeleaf
  • Bootstrap 4.x
  • jQuery
  • AJAX

🔨 Разработка среды

  • Операционная система: Windows 10
  • Инструмент сборки: Apache Maven
  • Интегрированная среда разработки: IntelliJ IDEA
  • Сервер приложений: Apache Tomcat
  • Инструмент тестирования API: Postman
  • Инструмент нагрузочного тестирования: Apache JMeter
  • Инструмент управления версиями: Git
  • Версия Java: 8

🎀 Интерфейс демонстрации

Главная страница:

Страница входа:

Страница сброса пароля:

Страница публикации поста (поддерживает Markdown):

Страница деталей поста (рендеринг Markdown):

Страница деталей поста:

Личная страница пользователя:

Страница личных сообщений друзей:

Страница деталей личных сообщений:

Страница системных уведомлений:

Страница деталей уведомлений:

Страница настроек аккаунта:

Страница статистики данных:

Страница деталей поиска:

🎨 Список функций

  • Регистрация
    • Войти | Выйти
      • Динамическое создание CAPTCHA
      • Запомнить меня
    • Настройка аккаунта
      • Изменение аватара
      • Изменение пароля
    • Фильтрация нежелательных слов
      • Префиксное дерево
    • Модуль публикаций
      • Создание публикации (фильтрация нежелательных слов)
      • Страница со всеми публикациями
        • Поддерживает отображение по времени публикации
        • Поддерживает отображение по рейтингу популярности (Spring Quartz)
      • Просмотр деталей публикации
      • Управление правами доступа (Spring Security + Thymeleaf Security)
        • Незарегистрированные пользователи не могут создавать публикации
        • Модератор может видеть кнопки "вверху списка" и "выделено" и выполнять соответствующие действия
        • Администратор может видеть кнопку "удалить" и выполнять соответствующие действия
        • Обычный пользователь не может видеть кнопки "вверху списка", "выделено" и "удалить", а также выполнять соответствующие действия
    • Модуль комментариев
      • Опубликовать комментарий к публикации (фильтрация нежелательных слов)
      • Страница с комментариями
      • Опубликовать ответ на комментарий (фильтрация нежелательных слов)
      • Управление правами доступа (Spring Security)
        • Незарегистрированные пользователи не могут использовать функцию комментариев
    • Модуль личных сообщений - Отправка личного сообщения (фильтрация нежелательных слов)
      • Список сообщений
        • Поиск списка текущих диалогов пользователя
        • Каждый диалог показывает последнее сообщение
        • Поддерживает страницацию
      • Детали сообщения
        • Поиск всех сообщений в конкретном диалоге
        • При просмотре деталей сообщения, оно помечается как прочитанное
        • Поддерживает страницацию
      • Управление правами доступа (Spring Security)
        • Незарегистрированные пользователи не могут использовать функцию личных сообщений
    • Общее обработывание ошибок 404 / 500 метод
      • Обработка обычных запросов
      • Обработка асинхронных запросов
    • Универсальная запись логов
    • Модуль лайков
      • Поддерживает лайки для публикаций, комментариев/ответов
      • Первый лайк, второй отменяет лайк
      • Главная страница отображает количество лайков для публикаций
      • Страница деталей отображает количество лайков для публикаций и комментариев/ответов
      • Страница деталей показывает состояние лайка текущего пользователя (если лайкнуто, то отмечено как "лайкнуто")
      • Статистика количества полученных лайков
      • Управление правами доступа (Spring Security)
        • Незарегистрированные пользователи не могут использовать функцию лайков
    • Модуль подписок
      • Функция подписки
      • Функция отмены подписки
      • Статистика количества подписок и подписчиков - Список моих подписок (поиск людей, на которых пользователь подписан), поддерживает страницацию
      • Список моих подписчиков (поиск пользователей, которые подписаны на данного пользователя), поддерживает страницацию
      • Управление правами доступа (Spring Security)
        • Незарегистрированные пользователи не имеют доступа к функции подписок
    • Модуль системных уведомлений
      • Список уведомлений
        • Отображает уведомления трёх типов: комментарии, лайки, подписки
      • Детали уведомления
        • При переходе в детали определённого типа системного уведомления, состояние всех непрочитанных системных уведомлений на данной странице будет установлено как прочитанные
      • Количество непрочитанных уведомлений
        • Отдельное отображение количества непрочитанных уведомлений каждого типа системных уведомлений
        • Отображение общего количества непрочитанных системных уведомлений
      • Навигационная панель отображает общее количество непрочитанных сообщений (непрочитанные личные сообщения + непрочитанные системные уведомления)
      • Управление правами доступа (Spring Security)
        • Пользователи, не прошедшие аутентификацию, не имеют доступа к функциональности системных уведомлений
    • Модуль поиска
    • Статистика данных сайта (только для администраторов)
      • Уникальные посетители UV - Поддерживает запрос за один день и запрос за диапазон дат
      • Дневной активный пользователь (DAU)
        • Поддерживает запрос за один день и запрос за диапазон дат
      • Управление правами доступа (Spring Security)
        • Только администраторы могут просматривать статистику данных сайта
    • Оптимизация производительности сайта
      • При каждом запросе информация пользователя проверяется через интерцептор с использованием учетных данных входа. Частота обращений очень высока. Поэтому информация пользователя после успешного входа хранится в кэше Redis некоторое время; при запросе информации пользователя данные берутся из кэша; если в кэше нет информации о данном пользователе, она заносится в кэш; при изменении информации пользователя соответствующий кэш очищается
      • Введение локального кэширования Caffeine, кэширование списка популярных тем и общего количества тем, повышение скорости ответа (можно использовать двухуровневую схему кэширования)

🌱 Локальная установкаДля тестирования проекта на локальной машине заранее подготовьте следующие компоненты:

  • Java 8
  • MySQL 5.7
  • Redis
  • Kafka 2.13-2.7.0
  • Elasticsearch 6.4.3

Необходимо изменить информацию в конфигурационных файлах под свои локальные данные, так как без этого запустить проект невозможно. Также все конфиденциальные данные заменены на xxxxxxx.

Информация, которую следует изменить при локальном запуске:

  1. application-develop.properties:
  • MySQL
  • Spring Mail (необходимо включить SMTP-сервер)
  • Kafka: consumer.group-id (значение можно найти в файле consumer.properties, который находится в пакете установки Kafka; после изменения потребуется перезапуск Kafka)
  • Elasticsearch: cluster-name (значение можно найти в файле elasticsearch.yml, который находится в пакете установки Elasticsearch; после изменения потребуется перезапуск Elasticsearch)
  • Qiniu Cloud (необходимо создать пространство объектного хранилища Qiniu Cloud для хранения загруженных изображений)
  1. logback-spring-develop.xml:
  • LOG_PATH: путь к логам

При каждом запуске требуется запуск следующих сервисов:

  • MySQL
  • Redis
  • Elasticsearch
  • Kafka

Кроме того, необходимо создать базу данных greatecommunity и последовательно выполнить SQL-скрипты из папки sql для создания таблиц базы данных:

Скриншот

🌌 Архитектура развертывания

На каждый компонент установлен один экземпляр. Ниже представлена идеальная архитектура развертывания:Скриншот

🎯 Схема функциональной логики

Ниже приведены схемы, которые помогут понять логику работы системы.

Одиночные зелёные стрелки:

  • Шаблон фронта -> контроллер: указывает на то, что в шаблоне есть ссылка, которая обрабатывается данным контроллером
  • Контроллер -> шаблон фронта: указывает на то, что контроллер передает данные или переходит к данному шаблону

Двунаправленные зелёные стрелки: указывают на взаимодействие между контроллером и шаблоном фронта

Одиночные голубые стрелки: A -> B, указывают на вызов метода B методом A

Одиночные красные стрелки: операции с базой данных или кэширование

Регистрация

  • При успешной регистрации пользователя его информация сохраняется в MySQL, но статус аккаунта остаётся неактивным
  • Отправка активационного письма пользователю, которое позволяет активировать аккаунт при переходе по ссылке (Spring Mail)

Схема

Войти | Выйти

Механизм аутентификации пропускает встроенную систему аутентификации Spring Security. Основная логика работы следующая:- При входе на страницу авторизации генерируется случайная строка, которая используется для идентификации пользователя. Эта строка временно хранится в cookie (60 секунд);

  • Генерируется динамический код проверки, который вместе с идентификатором пользователя временно сохраняется в Redis (60 секунд);
  • Для успешной авторизации (проверка имени пользователя, пароля и кода проверки) генерируется случайный токен доступа, который устанавливается как действительный. Этот токен и связанные данные сохраняются навсегда в Redis, а также временно сохраняются в cookie;
  • Используется интерцептор, чтобы перед выполнением каждого запроса получить токен доступа из cookie. Если токен действителен и находится в периоде действия в Redis, то этот запрос будет иметь информацию о пользователе (информация о пользователе хранится с использованием ThreadLocal для обеспечения синхронизации состояния авторизации между несколькими серверами);
  • Выбор "Запомнить меня" продлевает срок действия токена доступа в cookie;
  • При выходе пользователя состояние токена устанавливается как недействительное, и обновляются связанные данные в Redis.Ниже представлена схема функциональной логики модуля авторизации, который не использует встроенные механизмы аутентификации Spring Security (по моему мнению, это самый сложный модуль, и эта схема ещё не полностью отражает все детали).

Схема

Отображение всех тем постранично

  • Поддерживает отображение по времени публикации
  • Поддерживает отображение по рейтингу популярности (Spring Quartz)
  • Список популярных тем и общее количество тем сохраняются в локальном кэше Caffeine (используются распределённые задачи планировщика Spring Quartz для регулярного обновления рейтинга/баллов тем — подробнее ниже, а данные в Caffeine автоматически обновляются при каждом обращении)

Отображение тем

Настройка аккаунта

  • Изменение аватара (асинхронный запрос)
    • Выбранный пользователем файл аватара загружается на сервер Qiniu Cloud
  • Изменение пароля

На данном этапе показана только схема изменения аватара:

Изменение аватара

Создание новой темы (асинхронный запрос)

При создании новой темы (фильтрация запрещённых слов) она сохраняется в базе данных MySQL

Создание темы

Отображение комментариев и связанных данных> В названии раздела с комментариями в фронтэнде есть некоторые недочеты; если кто-то заинтересован, приветствуем ваш Pull Request для решения этой проблемы.

Что следует учесть при работе с модулем комментариев — это проектирование таблицы комментариев, понимание значений полей помогает глубже освоить логику этого функционала.

Комментарий Comment имеет тип цели (пост, комментарий) entityType и ID цели entityId, а также ID пользователя, которому адресован комментарий/ответ targetId, передаются фронтендом в контроллер DiscussPostController.

Схема

Для страницы деталей одного поста требуется примерно следующее:

Схема

Добавление комментария (управление транзакциями)

Публикация комментария к посту (фильтрация отрицательных слов), его хранение в MySQL.

Схема

Список и детальная информация сообщений

Схема

Отправка сообщения (асинхронный запрос)

Схема

Лайк (асинхронный запрос)

Информация о лайках сохраняется в структуре данных типа set в Redis. Ключ называется like:entity:entityType:entityId, значение представляет собой ID пользователя, который поставил лайк. Например, ключ like:entity:2:246 и значение 11 указывают на то, что пользователь 11 поставил лайк на комментарий с ID 246.Общее количество лайков для конкретного пользователя хранится в Redis под ключом like:user:userId, где значение представляет собой общее количество лайков данного пользователя.

Схема

Мои лайки

Схема

Подписка (асинхронный запрос)

  • Если A подписался на B, то A является фолловером (Follower), а B — объектом подписки (Followee).
  • Объектом подписки может быть пользователь, пост, задача и т.д.; при реализации эти объекты абстрагируются как сущности (в настоящее время возможна только подписка на пользователей).

Информация о подписках пользователя сохраняется в структуре данных типа zset в Redis: ключ followee:userId:entityType, значение zset(entityId, now), сортировка по времени подписки. Например, ключ followee:111:3 со значением (20, 2020-02-03-xxxx) указывает на то, что пользователь 111 подписался на пользователя с ID 20, время подписки — 2020-02-03-xxxx. Аналогично, информация о подписчиках, принадлежащих определенной сущности, также хранится в структуре данных Redis zset:

Ключ: follower:entityType:entityId Значение: zset(userId, now) Сортировка выполняется по времени подписки.

Список подписок

Отправка системных уведомлений

Отображение системных уведомлений

Поиск

  • Опубликование событий
    • При публикации поста сообщение отправляется через очередь сообщений на сервер Elasticsearch
    • При добавлении комментария к посту сообщение отправляется через очередь сообщений на сервер Elasticsearch
  • Поиск сервис
    • Поиск постов на сервере Elasticsearch
    • Удаление постов с сервера Elasticsearch при удалении их из базы данных
  • Отображение результатов поиска

Аналогично, закрепление поста и его пометка как "выдающегося" также вызывают события публикации, что больше не показывается на диаграмме.

Закрепление, пометка как выдающееся и удаление (асинхронные запросы)

Статистика сайта

  • Уникальные посетители UV
    • Хранятся в Redis с использованием HyperLogLog
    • Поддерживают однодневные и диапазонные запросы
  • Дневной активный пользователь DAU
    • Хранятся в Redis с использованием Bitmap
    • Поддерживают однодневные и диапазонные запросы
  • Управление правами доступа (Spring Security)
    • Только администратор может просматривать статистику сайта

Вычисление популярности постовПри каждом лайке (лайк поста), комментарии (комментарий к посту) и пометке поста как "выдающегося", информация о таких постах сохраняется в кэше Redis, а затем распределенные задачи планировщика Spring Quartz каждые несколько минут извлекают эти посты из кэша для расчета их рейтинга.

Формула для расчета рейтинга поста: рейтинг (популярность) = вес + количество дней до создания поста

// Расчет веса
double w = (wonderful ? 75 : 0) + commentCount * 10 + likeCount * 2;
// Рейтинг = вес + количество дней до создания поста
double score = Math.log10(Math.max(w, 1)) +
               (post.getCreateTime().getTime() - epoch.getTime()) / (1000 * 3600 * 24);

Изображение остается без изменений:

Изображение

Комментарии ( 0 )

Вы можете оставить комментарий после Вход в систему

Введение

Описание недоступно Развернуть Свернуть
MIT
Отмена

Обновления (4)

все

Участники

все

Недавние действия

Загрузить больше
Больше нет результатов для загрузки
1
https://api.gitlife.ru/oschina-mirror/veal98-Echo.git
git@api.gitlife.ru:oschina-mirror/veal98-Echo.git
oschina-mirror
veal98-Echo
veal98-Echo
master