Auto commit: 2026-01-31T23:58:19.892Z

This commit is contained in:
Flatlogic Bot 2026-01-31 23:58:19 +00:00
parent d49fff7c1d
commit 8b1d207a4e
3 changed files with 107 additions and 5 deletions

View File

@ -1,4 +1,3 @@
const express = require('express');
const TicketsService = require('../services/tickets');
@ -54,6 +53,11 @@ router.use(checkCrudPermissions('tickets'));
* description: The Tickets managing API
*/
router.post('/generate', wrapAsync(async (req, res) => {
const payload = await TicketsService.generateAutoTicket(req.body.data, req.currentUser);
res.status(200).send(payload);
}));
/**
* @swagger
* /api/tickets:
@ -439,4 +443,4 @@ router.get('/:id', wrapAsync(async (req, res) => {
router.use('/', require('../helpers').commonErrorHandler);
module.exports = router;
module.exports = router;

View File

@ -132,7 +132,76 @@ module.exports = class TicketsService {
}
}
};
static async generateAutoTicket(data, currentUser) {
const { desired_odds, game_count } = data;
const targetOdds = parseFloat(desired_odds) || 2.0;
const count = parseInt(game_count) || 3;
// 1. Get all active selections with their game info
const selections = await db.selections.findAll({
include: [{
model: db.markets,
as: 'market',
include: [{
model: db.games,
as: 'game',
}]
}]
});
if (selections.length === 0) {
throw new Error('No selections available in the database');
}
// Group selections by game to ensure we pick from different games
const gameGroups = {};
selections.forEach(s => {
if (s.market && s.market.game) {
const gameId = s.market.game.id;
if (!gameGroups[gameId]) gameGroups[gameId] = [];
gameGroups[gameId].push(s);
}
});
const gameIds = Object.keys(gameGroups);
const actualCount = Math.min(count, gameIds.length);
if (actualCount === 0) {
throw new Error('Not enough games available');
}
// Try multiple times to find a good combination
let bestTicket = null;
let minDiff = Infinity;
for (let i = 0; i < 200; i++) {
// Randomly pick actualCount games
const shuffledGames = gameIds.sort(() => 0.5 - Math.random());
const selectedGameIds = shuffledGames.slice(0, actualCount);
const currentSelections = [];
let currentTotalOdds = 1;
selectedGameIds.forEach(gid => {
const gameSelections = gameGroups[gid];
const randomSelection = gameSelections[Math.floor(Math.random() * gameSelections.length)];
currentSelections.push(randomSelection);
currentTotalOdds *= parseFloat(randomSelection.odds);
});
const diff = Math.abs(currentTotalOdds - targetOdds);
if (diff < minDiff) {
minDiff = diff;
bestTicket = {
selections: currentSelections,
totalOdds: currentTotalOdds,
gameCount: actualCount
};
}
if (minDiff < 0.05) break; // Close enough
}
return bestTicket;
}
};

View File

@ -122,6 +122,22 @@ export const update = createAsyncThunk('tickets/updateTickets', async (payload:
}
})
export const generateAutoTicket = createAsyncThunk('tickets/generateAutoTicket', async (data: any, { rejectWithValue }) => {
try {
const result = await axios.post(
'tickets/generate',
{ data }
)
return result.data
} catch (error) {
if (!error.response) {
throw error;
}
return rejectWithValue(error.response.data);
}
})
export const ticketsSlice = createSlice({
name: 'tickets',
@ -221,6 +237,19 @@ export const ticketsSlice = createSlice({
rejectNotify(state, action);
})
builder.addCase(generateAutoTicket.pending, (state) => {
state.loading = true;
resetNotify(state);
})
builder.addCase(generateAutoTicket.fulfilled, (state) => {
state.loading = false;
fulfilledNotify(state, 'Ticket generated successfully');
})
builder.addCase(generateAutoTicket.rejected, (state, action) => {
state.loading = false;
rejectNotify(state, action);
})
},
})
@ -228,4 +257,4 @@ export const ticketsSlice = createSlice({
// Action creators are generated for each case reducer function
export const { setRefetch } = ticketsSlice.actions
export default ticketsSlice.reducer
export default ticketsSlice.reducer