Skip to content

Commit

Permalink
Add ticket routes and model
Browse files Browse the repository at this point in the history
  • Loading branch information
denizyesilirmak committed Oct 29, 2023
1 parent 3037165 commit 747180f
Show file tree
Hide file tree
Showing 6 changed files with 256 additions and 1 deletion.
26 changes: 26 additions & 0 deletions src/constants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,4 +141,30 @@ export const RESPONSE_ERRORS = {
message: 'Balance update not allowed.',
code: 2034,
},
GAME_NOT_FOUND: {
success: false,
message: 'Game not found.',
code: 2035,
},
SERVER_ERROR: {
success: false,
message: 'General server error. Report to admin.',
code: 2099,
},
BLOCK_NUMBERS_COUNT_NOT_MATCH: {
success: false,
message: 'Numbers on block must be equal to required numbers.',
code: 2036,
},
INVALID_NUMBERS: {
success: false,
message:
'Invalid numbers. Numbers must be between minimum and maximum numbers of game.',
code: 2037,
},
INSUFFICIENT_BALANCE: {
success: false,
message: 'Insufficient balance.',
code: 2038,
},
};
30 changes: 30 additions & 0 deletions src/models/block/block.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import mongoose, { Schema } from 'mongoose';

const BlockSchema = new Schema({
ticket: {
type: Schema.Types.ObjectId,
ref: 'Ticket',
required: true,
},
createdAt: {
type: Date,
default: Date.now,
},
numbers: {
type: [Number],
required: true,
},
isWinner: {
type: Boolean,
default: false,
},
});

export interface IBlockSchema {
ticket: string;
createdAt: Date;
numbers: number[];
isWinner: boolean;
}

export default mongoose.model<IBlockSchema>('Block', BlockSchema);
2 changes: 1 addition & 1 deletion src/models/game/game.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ const GameSchema = new Schema({
export interface IGameSchema {
name: string;
description: string;
price: number;
columnPrice: number;
prize: number;
image: string;
createdAt: Date;
Expand Down
36 changes: 36 additions & 0 deletions src/models/ticket/ticket.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import mongoose, { Schema } from 'mongoose';

const TicketSchema = new Schema({
user: {
type: Schema.Types.ObjectId,
ref: 'User',
required: true,
},
createdAt: {
type: Date,
default: Date.now,
},
ticketCode: {
type: String,
required: true,
},
game: {
type: Schema.Types.ObjectId,
ref: 'Game',
required: true,
},
hasDrawn: {
type: Boolean,
default: false,
},
});

export interface ITicketSchema {
user: string;
createdAt: Date;
ticketCode: string;
game: string;
hasDrawn: boolean;
}

export default mongoose.model<ITicketSchema>('Ticket', TicketSchema);
2 changes: 2 additions & 0 deletions src/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { gameRouter } from './game/game.route';
import { generalRouter } from './general/general.route';
import { profileRouter } from './profile/profile.route';
import { sslRouter } from './ssl/ssl.route';
import { ticketRouter } from './ticket/ticket.route';

/**
* @description Routes class
Expand All @@ -24,6 +25,7 @@ class Routes {
app.use(`/api/${process.env.API_VERSION}/admin`, adminRouter);
app.use(`/api/${process.env.API_VERSION}/draws`, drawRouter);
app.use(`/api/${process.env.API_VERSION}/balance`, balanceRouter);
app.use(`/api/${process.env.API_VERSION}/tickets`, ticketRouter);
app.use('/.well-known/acme-challenge', sslRouter);
}
}
Expand Down
161 changes: 161 additions & 0 deletions src/routes/ticket/ticket.route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
import { Request, Response, Router } from 'express';
import jsonWebToken from 'jsonwebtoken';
import ticketModel from '../../models/ticket/ticket.model';
import blockModel from '../../models/block/block.model';
import gameModel from '../../models/game/game.model';
import userModel from '../../models/user/user.model';

import { RESPONSE_ERRORS } from '../../constants';

const router = Router();

router.get('/', async (req: Request, res: Response) => {
const decoded = jsonWebToken.decode(
req.headers.authorization?.split(' ')[1] as string
) as any;

if (!decoded) {
res.sendStatus(401);
return;
}

if (!decoded.email) {
return;
}

const user = await userModel.findOne({ email: decoded.email }).exec();

const tickets = await ticketModel.find({ user: user?._id }).exec();

const ticketIds = tickets.map((ticket) => ticket._id);

const blocks = await blockModel.find({ ticket: { $in: ticketIds } }).exec();

const ticketsWithBlocks = tickets.map((ticket) => {
return {
...ticket.toObject(),
blocks,
};
});

res.json({
success: true,
message: 'User tickets.',
data: {
tickets: ticketsWithBlocks,
},
});
});

router.post('/', async (req: Request, res: Response) => {
try {
const decoded = jsonWebToken.decode(
req.headers.authorization?.split(' ')[1] as string
) as any;

if (!decoded) {
res.sendStatus(401);
return;
}

if (!decoded.email) {
return;
}

const numbers = req.body.numbers as number[][];
const gameId = req.body.game as string;
const email = decoded.email;

const game = await gameModel.findById(gameId).exec();

if (!game) {
res.status(400).json(RESPONSE_ERRORS.GAME_NOT_FOUND);
return;
}

const requiredNumbers = game.requriedNumbers;

//number validations

const invalidBlocks = numbers.filter((block_numbers) => {
return block_numbers.length !== requiredNumbers;
});

//numbers count must be equal to required numbers
if (invalidBlocks.length > 0) {
res.status(400).json(RESPONSE_ERRORS.BLOCK_NUMBERS_COUNT_NOT_MATCH);
return;
}

//numbers min and max validations

const invalidNumbers = numbers.flat().filter((number) => {
return number < game.mininumNumber || number > game.maximumNumber;
});

if (invalidNumbers.length > 0) {
res.status(400).json({
...RESPONSE_ERRORS.INVALID_NUMBERS,
invalidNumbers,
});
return;
}

const user = await userModel.findOne({ email }).exec();

if (!user) {
res.status(404).json(RESPONSE_ERRORS.USER_NOT_FOUND);
return;
}

//check if user has enough balance

console.log('user.balance', user.balance);
console.log('game.price', game.columnPrice);

if (user.balance < game.columnPrice) {
res.status(400).json(RESPONSE_ERRORS.INSUFFICIENT_BALANCE);
return;
}

//update user balance

await userModel.findByIdAndUpdate(
user._id,
{
balance: user.balance - game.columnPrice,
},
{ new: true }
);

const ticket = await ticketModel.create({
user: user._id,
game: gameId,
ticketCode: Math.random().toString(36).substr(2, 3),
});

const blocks = numbers.map((block_numbers) => {
return {
ticket: ticket._id,
numbers: block_numbers,
};
});

await blockModel.insertMany(blocks);

res.json({
success: true,
message: 'Ticket created.',
game: game._id,
gameName: game.name,
ticketCode: ticket.ticketCode,
numbers,
balance: user.balance - game.columnPrice,
});
} catch (error) {
console.log('error', error);
res.status(500).json(RESPONSE_ERRORS.SERVER_ERROR);
}
});

export { router as ticketRouter };

0 comments on commit 747180f

Please sign in to comment.