Что такое хороший и гибкий способ хранения и подсчета пользовательских баллов? - PullRequest
0 голосов
/ 09 октября 2018

Я хотел бы кредитовать пользователей за различные акции.Например, при написании articel, оцените articel ...

Я ищу гибкое решение, в котором я могу настроить баллы для различных действий, и общий счет также меняется.

Если я сохраню точки в таблице, я не смогу их потом изменить.Примерно так:

$request->user()->points += 10;
    $request->user()->save();

Как насчет производительности, когда я каждый раз пересчитываю очки?как это:

$articlepoints = $user->articles->count();
&votepoints = $user->votes->count();
$totalpoints = $articlepoints*10+$votepoints*5; 

есть ли другие варианты?

Ответы [ 2 ]

0 голосов
/ 09 октября 2018

Обычным примером для такого рода вещей является ведение таблицы для записи каждого события, которое приводит к точкам, а также агрегирование общего количества точек в столбце либо в пользовательской таблице, либо в какой-либо таблице статистики, которая содержит запись длякаждый пользователь.Это дает вам возможность вести учет каждого события, задним числом пересчитывать итоги, если вы изменяете, сколько стоит каждый тип события, и получать доступ к итогам (и использовать их в запросах) без необходимости каждый раз вычислять их.

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

CREATE TABLE `users` (
  `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
  `username` varchar(20) NOT NULL,
  `total_activity_points` int(11) UNSIGNED NOT NULL DEFAULT 0,
  PRIMARY KEY (`id`),
  UNIQUE KEY `users_username_uk` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;

INSERT INTO `users` (`username`) VALUES ('Bob');
INSERT INTO `users` (`username`) VALUES ('Alice');

CREATE TABLE `activities` (
  `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
  `title` varchar(20) NOT NULL,
  `points` int(11) UNSIGNED NOT NULL DEFAULT 0,
  PRIMARY KEY (`id`),
  UNIQUE KEY `activities_title_uk` (`title`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;

INSERT INTO `activities` (`title`,`points`) VALUES ('Article', 10);
INSERT INTO `activities` (`title`,`points`) VALUES ('Vote', 5);

CREATE TABLE `user_activities` (
  `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
  `user_id` int(11) UNSIGNED NOT NULL,
  `activity_id` int(11) UNSIGNED NOT NULL,
  `create_date` DATETIME NOT NULL,
  PRIMARY KEY (`id`),
  CONSTRAINT `user_activities_fk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE,
  CONSTRAINT `user_activities_fk_2` FOREIGN KEY (`activity_id`) REFERENCES `activities` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;

DELIMITER //
DROP TRIGGER IF EXISTS `after_insert_user_activities` //
CREATE TRIGGER `after_insert_user_activities`
  AFTER INSERT
  ON `user_activities`
  FOR EACH ROW
  BEGIN
    DECLARE v_total INTEGER DEFAULT 0;
    SET v_total = (SELECT SUM(`activities`.`points`)
                   FROM `user_activities`
                   INNER JOIN `activities` ON `activities`.`id`=`user_activities`.`activity_id`
                   WHERE `user_activities`.`user_id` = NEW.`user_id`);
    UPDATE `users`
    SET `total_activity_points` = v_total
    WHERE `users`.`id` = NEW.`user_id`;
  END;
//
DELIMITER ;

DELIMITER //
DROP TRIGGER IF EXISTS `after_delete_user_activities` //
CREATE TRIGGER `after_delete_user_activities`
  AFTER DELETE
  ON `user_activities`
  FOR EACH ROW
  BEGIN
    DECLARE v_total INTEGER DEFAULT 0;
    SET v_total = (SELECT SUM(`activities`.`points`)
                   FROM `user_activities`
                     INNER JOIN `activities` ON `activities`.`id`=`user_activities`.`activity_id`
                   WHERE `user_activities`.`user_id` = OLD.`user_id`);
    UPDATE `users`
    SET `total_activity_points` = v_total
    WHERE `users`.`id` = OLD.`user_id`;
  END;
//
DELIMITER ;

INSERT INTO `user_activities` (`user_id`,`activity_id`,`create_date`) VALUES (1,1,NOW());
INSERT INTO `user_activities` (`user_id`,`activity_id`,`create_date`) VALUES (1,2,NOW());
INSERT INTO `user_activities` (`user_id`,`activity_id`,`create_date`) VALUES (2,2,NOW());
INSERT INTO `user_activities` (`user_id`,`activity_id`,`create_date`) VALUES (2,2,NOW());

DELIMITER //
DROP TRIGGER IF EXISTS `after_update_activities` //
CREATE TRIGGER `after_update_activities` AFTER UPDATE ON `activities`
  FOR EACH ROW
  BEGIN
    DECLARE v_finished INTEGER DEFAULT 0;
    DECLARE v_userId INT(11) UNSIGNED;
    DECLARE v_total INT(11) UNSIGNED;

    DECLARE user_id_cursor CURSOR FOR SELECT DISTINCT(`user_activities`.`user_id`) FROM `user_activities` WHERE `user_activities`.`activity_id`=NEW.`id`;
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET v_finished = 1;

    IF NEW.`points` != OLD.`points` THEN
      OPEN user_id_cursor;

      get_user_ids: LOOP
        FETCH user_id_cursor INTO v_userId;
        IF v_finished = 1 THEN
          LEAVE get_user_ids;
        END IF;
        -- recalculate and store scores
        SET v_total = (SELECT SUM(`activities`.`points`)
                       FROM `user_activities`
                         INNER JOIN `activities` ON `activities`.`id`=`user_activities`.`activity_id`
                       WHERE `user_activities`.`user_id` = v_userId);
        UPDATE `users`
        SET `total_activity_points` = v_total
        WHERE `users`.`id` = v_userId;
      END LOOP get_user_ids;

      CLOSE user_id_cursor;
    END IF;
  END;
//
DELIMITER ;

DELIMITER //
DROP TRIGGER IF EXISTS `before_delete_activities` //
CREATE TRIGGER `before_delete_activities` BEFORE DELETE ON `activities`
  FOR EACH ROW
  BEGIN
    DECLARE v_finished INTEGER DEFAULT 0;
    DECLARE v_userId INT(11) UNSIGNED;
    DECLARE v_total INT(11) UNSIGNED;

    DECLARE user_id_cursor CURSOR FOR SELECT DISTINCT(`user_activities`.`user_id`) FROM `user_activities` WHERE `user_activities`.`activity_id`=OLD.`id`;
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET v_finished = 1;

    OPEN user_id_cursor;

    get_user_ids: LOOP
      FETCH user_id_cursor INTO v_userId;
      IF v_finished = 1 THEN
        LEAVE get_user_ids;
      END IF;
      -- recalculate and store scores
      SET v_total = (SELECT SUM(`activities`.`points`)
                     FROM `user_activities`
                       INNER JOIN `activities` ON `activities`.`id`=`user_activities`.`activity_id`
                     WHERE `user_activities`.`user_id` = v_userId
                     AND  `user_activities`.`activity_id` != OLD.`id`);
      UPDATE `users`
      SET `total_activity_points` = v_total
      WHERE `users`.`id` = v_userId;
    END LOOP get_user_ids;

    CLOSE user_id_cursor;
  END;
//
DELIMITER ;

Триггеры в таблице действий обновляют итоги, когда значения точек изменяются или действия удаляются.Триггер до удаления необходим, поскольку MySQL не запускает триггеры удаления при каскадном удалении.

0 голосов
/ 09 октября 2018

Вы можете использовать метод Cache :: Remember (), если вы пытаетесь свести к минимуму операции подсчета при каждой загрузке.Вам просто нужно решить, как часто вы хотите «истечь» cachced значение.Я предполагаю, что вы будете запускать эту функцию из модели User, но вы можете делать это где угодно:

$articlepoints = Cache::remember('articlepoints_'.$this->id, 5, function () {
    return $this->articles->count();
});

Этот код выполняет проверку на наличие пунктов статьи этого пользователя вКеш под индексом "articlepoints_userID".Идентификатор пользователя является динамическим, и для каждого пользователя будет сохранено другое значение.Если этот индекс существует, используйте его в качестве значения.

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

Используя этот код, вы резко сократите количество вызовов функции count ().Тем не менее, вы все еще можете выполнять вызовы базы данных или дисковые вызовы для каждого из них в зависимости от вашего драйвера кэша.Чтобы избежать такого типа накладных расходов, вам нужно взглянуть на другой драйвер кеша, такой как redis.

Ознакомьтесь с документами Laravel Cache , чтобы увидеть все другие интересные вещи, которые вы можетеделать с кешем.Изучение системы кэширования - отличный способ повысить производительность вашего приложения Laravel.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...