sudoku leader board fixed
This commit is contained in:
@@ -7,6 +7,7 @@ export interface ISudokuRecord extends Document {
|
||||
username: string;
|
||||
fullname: string;
|
||||
difficulty: SudokuDifficulty;
|
||||
gameMode: 'standard' | 'irregular';
|
||||
timeSeconds: number;
|
||||
won: boolean;
|
||||
createdAt: Date;
|
||||
@@ -17,6 +18,7 @@ export interface ISudokuLeaderboardRecord {
|
||||
userId: mongoose.Types.ObjectId;
|
||||
username: string;
|
||||
fullname: string;
|
||||
gameMode: 'standard' | 'irregular';
|
||||
bestTime: number;
|
||||
totalGames: number;
|
||||
wonGames: number;
|
||||
@@ -43,6 +45,12 @@ const sudokuRecordSchema = new Schema<ISudokuRecord>({
|
||||
enum: ['easy', 'medium', 'hard'],
|
||||
required: true
|
||||
},
|
||||
gameMode: {
|
||||
type: String,
|
||||
enum: ['standard', 'irregular'],
|
||||
required: true,
|
||||
default: 'standard'
|
||||
},
|
||||
timeSeconds: {
|
||||
type: Number,
|
||||
required: true,
|
||||
@@ -56,25 +64,41 @@ const sudokuRecordSchema = new Schema<ISudokuRecord>({
|
||||
timestamps: true
|
||||
});
|
||||
|
||||
sudokuRecordSchema.index({ userId: 1, difficulty: 1 });
|
||||
sudokuRecordSchema.index({ difficulty: 1, won: 1, timeSeconds: 1 });
|
||||
sudokuRecordSchema.index({ userId: 1, difficulty: 1, gameMode: 1 });
|
||||
sudokuRecordSchema.index({ difficulty: 1, won: 1, gameMode: 1, timeSeconds: 1 });
|
||||
|
||||
sudokuRecordSchema.statics.getLeaderboard = function(
|
||||
difficulty: SudokuDifficulty,
|
||||
gameMode: 'standard' | 'irregular' | 'all',
|
||||
skipCount: number,
|
||||
pageSize: number
|
||||
): mongoose.Aggregate<ISudokuLeaderboardRecord[]> {
|
||||
const match: any = {
|
||||
difficulty
|
||||
};
|
||||
|
||||
if (gameMode === 'standard') {
|
||||
match.$or = [
|
||||
{ gameMode: 'standard' },
|
||||
{ gameMode: { $exists: false } }
|
||||
];
|
||||
} else if (gameMode === 'irregular') {
|
||||
match.gameMode = 'irregular';
|
||||
}
|
||||
|
||||
return this.aggregate([
|
||||
{
|
||||
$match: {
|
||||
difficulty
|
||||
}
|
||||
$match: match
|
||||
},
|
||||
{
|
||||
$group: {
|
||||
_id: '$userId',
|
||||
_id: {
|
||||
userId: '$userId',
|
||||
gameMode: { $ifNull: ['$gameMode', 'standard'] }
|
||||
},
|
||||
username: { $first: '$username' },
|
||||
fullname: { $first: '$fullname' },
|
||||
gameMode: { $first: { $ifNull: ['$gameMode', 'standard'] } },
|
||||
bestTime: {
|
||||
$min: {
|
||||
$cond: [{ $eq: ['$won', true] }, '$timeSeconds', null]
|
||||
@@ -92,7 +116,7 @@ sudokuRecordSchema.statics.getLeaderboard = function(
|
||||
},
|
||||
{
|
||||
$addFields: {
|
||||
userId: '$_id',
|
||||
userId: '$_id.userId',
|
||||
winRate: {
|
||||
$multiply: [
|
||||
{ $cond: [{ $eq: ['$totalGames', 0] }, 0, { $divide: ['$wonGames', '$totalGames'] }] },
|
||||
|
||||
@@ -51,34 +51,71 @@ router.post('/record', auth, async (req: Request, res: Response) => {
|
||||
router.get('/leaderboard/:difficulty', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const { difficulty } = req.params;
|
||||
const { page = '1', limit = '10' } = req.query;
|
||||
const { page = '1', limit = '10', mode = 'all' } = req.query;
|
||||
|
||||
const validDifficulties: SudokuDifficulty[] = ['easy', 'medium', 'hard'];
|
||||
const validModes = ['all', 'standard', 'irregular'];
|
||||
|
||||
if (!validDifficulties.includes(difficulty as SudokuDifficulty)) {
|
||||
return res.status(400).json({ error: '无效的难度级别' });
|
||||
}
|
||||
|
||||
if (!validModes.includes(mode as string)) {
|
||||
return res.status(400).json({ error: '无效的游戏模式' });
|
||||
}
|
||||
|
||||
const pageNum = parseInt(page as string, 10);
|
||||
const pageSize = parseInt(limit as string, 10);
|
||||
const skipCount = (pageNum - 1) * pageSize;
|
||||
const gameMode = mode as 'standard' | 'irregular' | 'all';
|
||||
|
||||
const records = await SudokuRecord.getLeaderboard(
|
||||
difficulty as SudokuDifficulty,
|
||||
gameMode,
|
||||
skipCount,
|
||||
pageSize
|
||||
);
|
||||
|
||||
const totalUsers = await SudokuRecord.distinct('userId', {
|
||||
const totalMatch: any = {
|
||||
difficulty,
|
||||
won: true
|
||||
});
|
||||
};
|
||||
|
||||
if (gameMode === 'standard') {
|
||||
totalMatch.$or = [
|
||||
{ gameMode: 'standard' },
|
||||
{ gameMode: { $exists: false } }
|
||||
];
|
||||
} else if (gameMode === 'irregular') {
|
||||
totalMatch.gameMode = 'irregular';
|
||||
}
|
||||
|
||||
const totalCountResult = await SudokuRecord.aggregate([
|
||||
{
|
||||
$match: totalMatch
|
||||
},
|
||||
{
|
||||
$group: {
|
||||
_id: {
|
||||
userId: '$userId',
|
||||
gameMode: { $ifNull: ['$gameMode', 'standard'] }
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
$count: 'count'
|
||||
}
|
||||
]);
|
||||
|
||||
const total = totalCountResult[0]?.count || 0;
|
||||
|
||||
res.json({
|
||||
records,
|
||||
total: totalUsers.length,
|
||||
total,
|
||||
currentPage: pageNum,
|
||||
totalPages: Math.ceil(totalUsers.length / pageSize),
|
||||
difficulty
|
||||
totalPages: Math.ceil(total / pageSize),
|
||||
difficulty,
|
||||
mode: gameMode
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('获取数独排行榜失败:', error);
|
||||
|
||||
Reference in New Issue
Block a user