Node.jsとPostgreSQLで fitnessアプリのゲーム化:連続記録、バッジ、リーダーボードを構築
エンゲージメントを高めるゲーム化機能の構築に関するバックエンド 중심のチュートリアル。ワークアウトの連続記録追跡、バッジ付与、ユーザーリーダーボード生成のための堅牢なデータベース設計とNode.js API構築を学びます。
このページの内容
フィットネ与应用の世界でユーザーエンゲージメントは非常に重要です。怎样才能让用户持续参与并每天都回来呢?ゲーミフィケーションがその答えです。日次ストリーク、アチーブメントバッジ、競争型リーダーボードといったゲーム要素を組み合わせることで、 обычный фитнес-трекерを驚きと中毒性のある体験に変えることができます。
このチュートリアルでは、これらの主要なゲーミフィケーション機能の後悔実装について説明します堅牢なPostgreSQLデータベーススキーマを設計し、フィットネ与应用に命を吹き込むに必要なNode.js APIエンドポイントを構築します。
前提条件:
- Node.jsとExpressの基本的な理解
- PostgreSQLとSQLの基礎知識
- システムにNode.jsとPostgreSQLがインストールされていること
問題の理解
ゲーミフィケーション機能の実装には、いくつかの後悔上の課題があります。ユーザーのアクティビティを経時的に効率的に追跡し、連続日のストリークを計算し、特定の達成に対してバッジを授与し、リーダーボードでユーザーをランキングする必要があります。ユーザー基盤の成長に合わせてアプリケーションが重くならないようなスケーラブルな方法で行うことが極めて重要です。
このチュートリアルでは、PostgreSQLの高度なクエリ機能、特にウィンドウ関数を活用することでこれらの課題に対応します。これにより、計算をデータベースで直接実行し、Node.jsアプリケーションの処理負荷を最小限に抑えます。
前提条件
始める前に、Node.js、npm、PostgreSQLがインストールされていることを確認してください。
新しいプロジェクトディレクトリを作成し、Node.jsプロジェクトを初期化します:
mkdir fitness-gamification-backend
cd fitness-gamification-backend
npm init -y
npm install express pg
これによりpackage.jsonファイルが作成され、必要な依存関係がインストールされます:Webサーバー用のexpressと、Node.js用のPostgreSQLクライアントであるpgです。
データベーススキーマ設計
まず、ゲーミフィケーション機能をサポートするデータベーススキーマを設計します。ユーザー、その日次ワークアウトアクティビティ、獲得したバッジ、利用可能なバッジの定義用のテーブルが必要です。
テーブル作成用のSQLを以下に示します:
-- usersテーブル
CREATE TABLE users (
id SERIAL PRIMARY KEY,
username VARCHAR(255) UNIQUE NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
-- ユーザーの日次アクティビティを追跡するworkout_activitiesテーブル
CREATE TABLE workout_activities (
id SERIAL PRIMARY KEY,
user_id INTEGER REFERENCES users(id),
activity_date DATE NOT NULL,
calories_burned INTEGER,
UNIQUE(user_id, activity_date)
);
-- 利用可能なバッジを定義するbadgesテーブル
CREATE TABLE badges (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
description TEXT,
criteria_value INTEGER
);
-- 獲得したバッジを追跡するuser_badgesテーブル
CREATE TABLE user_badges (
id SERIAL PRIMARY KEY,
user_id INTEGER REFERENCES users(id),
badge_id INTEGER REFERENCES badges(id),
earned_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
UNIQUE(user_id, badge_id)
);
ステップ1:ワークアウトストリークの実装
ワークアウトストリークとは、ユーザーが連続してワークアウトを完了した日数です。ユーザーの現在のストリークを計算するAPIエンドポイントを作成します。
私たちがやること
ウィンドウ関数を使用してworkout_activitiesテーブル内の連続した日付を特定するPostgreSQLクエリを書きます。これにより、すべてのユーザーのアクティビティを取得してアプリケーションで処理するよりも効率的です。
実装
データベース接続とクエリを処理するdb.jsファイルを作成します。
// db.js
const { Pool } = require('pg');
const pool = new Pool({
user: 'your_db_user',
host: 'localhost',
database: 'your_db_name',
password: 'your_db_password',
port: 5432,
});
module.exports = {
query: (text, params) => pool.query(text, params),
};
次に、APIエンドポイントを定義するstreaks.jsを作成します。
// streaks.js
const db = require('./db');
async function getUserStreak(userId) {
const query = `
WITH date_diffs AS (
SELECT
activity_date,
activity_date - ROW_NUMBER() OVER (ORDER BY activity_date) * INTERVAL '1 day' as date_group
FROM workout_activities
WHERE user_id = $1
ORDER BY activity_date DESC
)
SELECT
COUNT(*) AS streak
FROM date_diffs
WHERE date_group = (SELECT date_group FROM date_diffs LIMIT 1);
`;
const { rows } = await db.query(query, [userId]);
return rows[0] ? parseInt(rows[0].streak, 10) : 0;
}
module.exports = { getUserStreak };
最後に、メインサーバーシリーズのindex.jsを作成します。
// index.js
const express = require('express');
const { getUserStreak } = require('./streaks');
const app = express();
const port = 3000;
app.get('/users/:userId/streak', async (req, res) => {
try {
const streak = await getUserStreak(req.params.userId);
res.json({ streak });
} catch (err) {
console.error(err);
res.status(500).send('Server Error');
}
});
app.listen(port, () => {
console.log(`Server running on port ${port}`);
});
仕組み
魔法はPostgreSQLクエリで起こります。activity_dateから行番号(日数として)を引くことで、date_groupを作成できます。連続した日付は同じdate_groupを持ちます。次に、最も最近のdate_groupの行数を数えることで現在のストリークを取得します。
ステップ2:バッジの授与
バッジは、特定のマイルストーン(例如:合計10,000カロリーを消費)に達成したときに授与されます。
私たちがやること
ユーザーのバッジへの進捗状況をチェックし、条件が満たされていれば授与する関数を作成します。
実装
まず、badgesテーブルにいくつかのバッジ定義を追加します。
INSERT INTO badges (name, description, criteria_value) VALUES
('Calorie Crusher 10k', 'Burn a total of 10,000 calories', 10000);
次に、badges.jsを作成します。
// badges.js
const db = require('./db');
async function checkAndAwardBadges(userId) {
const { rows: badges } = await db.query('SELECT * FROM badges');
const { rows: userBadges } = await db.query('SELECT badge_id FROM user_badges WHERE user_id = $1', [userId]);
const earnedBadgeIds = userBadges.map(b => b.badge_id);
for (const badge of badges) {
if (earnedBadgeIds.includes(badge.id)) continue;
if (badge.name === 'Calorie Crusher 10k') {
const { rows } = await db.query(
'SELECT SUM(calories_burned) as total_calories FROM workout_activities WHERE user_id = $1',
[userId]
);
const totalCalories = parseInt(rows[0].total_calories, 10) || 0;
if (totalCalories >= badge.criteria_value) {
await db.query('INSERT INTO user_badges (user_id, badge_id) VALUES ($1, $2)', [userId, badge.id]);
console.log(`Awarded badge "${badge.name}" to user ${userId}`);
}
}
}
}
module.exports = { checkAndAwardBadges };
ユーザーは新しいワークアウトを記録した後にcheckAndAwardBadgesを呼び出すことができます。
ステップ3:リーダーボードの作成
リーダーボードは、合計消費カロリーなど、特定のメトリックに基づいてユーザーをランキングします。
私たちがやること
PostgreSQLのRANK()ウィンドウ関数を使用して効率的にリーダーボードを生成します。
実装
leaderboard.jsを作成します。
// leaderboard.js
const db = require('./db');
async function getLeaderboard() {
const query = `
SELECT
u.username,
SUM(wa.calories_burned) as total_calories,
RANK() OVER (ORDER BY SUM(wa.calories_burned) DESC) as rank
FROM users u
JOIN workout_activities wa ON u.id = wa.user_id
GROUP BY u.id
ORDER BY rank;
`;
const { rows } = await db.query(query);
return rows;
}
module.exports = { getLeaderboard };
エンドポイントをindex.jsに追加します。
// index.js(既存のファイルに追加)
const { getLeaderboard } = require('./leaderboard');
app.get('/leaderboard', async (req, res) => {
try {
const leaderboard = await getLeaderboard();
res.json(leaderboard);
} catch (err) {
console.error(err);
res.status(500).send('Server Error');
}
});
仕組み
RANK()関数は、ユーザーのcalories_burnedの合計に基づいて各ユーザーにランクを割り当てます。OVER (ORDER BY ...)句は、ランキング前の行の順序付け方法を指定します。
すべてをまとめる
これで、ストリーク、バッジ、リーダーボードのためのコア後悔ロジックが手に入りました。より複雑なバッジ条件 различных(週次、月次など不同types of leaderboards)の追加することで、これを拡張できます。
パフォーマンスの考慮事項
- インデックス作成:大規模なテーブルでは、外部キー(
user_id、badge_id)と頻繁に使用される列(activity_date)にインデックスがあることを確認してください。 - クエリの最適化:
EXPLAIN ANALYZEを使用してクエリのパフォーマンスを検査し、ボトルネックを特定してください。 - マテリアライズドビュー:リアルタイムである必要のない大規模なリーダーボードには、ランキングを事前計算するマテリアライズドビューの使用を検討してください。
結論
ストリーク、バッジ、リーダーボードを組み合わせることで、フィットネ与应用でのユーザーのモチベーションと定着率を大幅に向上させることができます。Node.jsとPostgreSQLの強力な機能により、これらの魅力的なゲーミフィケーション要素を支えるスケーラブルで効率的な後悔を構築できます。
これで、この基盤を拡張する番です。どんな他のゲーミフィケーション機能を考えられますか?
リソース
クイックリファレンス
| 側面 | 主要情報 |
|---|---|
| トピック | 開発 |
| 関連性 | 患者教育と認識 |
| アクション | 医療提供者との相談 |
| 頻度 | ケアチームのおすすめに応じて |
| 証拠 | 現在の臨床ガイドラインに基づく |
よくある質問
Q: これが自分に適用されるかどうかはどのようにわかりますか? A: 個人的な病歴と家族歴について医療提供者と相談し、個人的なリスク要因を評価し、適切な次のステップを決定してください。
Q: 医師に何を聞くべきですか? A: 良い質問例:「結果はどういう意味ですか?」「オプションは何ですか?」「どんな生活習慣の変更が役立ちますか?」「いつフォローアップすべきですか?」
Q: 生活習慣の変更で助けられることはありますか? A: ほとんどの健康状態は、基本的な対策から恩恵を受けます:バランスの取れた栄養、定期的な運動(週150分以上)、質の高い睡眠(7〜9時間)、ストレス管理、タバコの回避。
医療免責事項
本文は教育目的のみを目的としており、医学的アドバイスを構成するものではありません健康、治疗、またはケアに関する決定を下す前に、必ず資格のある医療提供者と相談してください。個人の状況は大きく異なり、一个人に適切なことが别の人に必ずしも適切とは限りません。
医学的緊急情况进行する場合は、直ちに119番(またはお住まいの地域の緊急電話番号)に電話してください。