Skip to content

Commit 713289f

Browse files
authored
Merge pull request #48 from DavidGarzonDev/odeb
añadiendo vista y logica del sorteo
2 parents 3877e23 + e320a54 commit 713289f

File tree

16 files changed

+1190
-56
lines changed

16 files changed

+1190
-56
lines changed

backend/src/controllers/auth.controllers.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,3 +128,33 @@ export const login = async (req, res) => {
128128
return res.status(400).send("Error en login: " + error.message);
129129
}
130130
};
131+
132+
export const getUserByUidController = async (req, res) => {
133+
const { token, uid } = req.body;
134+
135+
if (!token || !uid) {
136+
return res.status(400).json({ error: "Token y UID son requeridos" });
137+
}
138+
139+
try {
140+
// Verificar el token primero para asegurar que la solicitud es legítima
141+
await admin.auth().verifyIdToken(token);
142+
143+
// Obtener el usuario por UID
144+
const userData = await getUserByUid(uid);
145+
146+
if (!userData || !userData.data) {
147+
return res.status(404).json({ error: "Usuario no encontrado" });
148+
}
149+
150+
// Enviamos los datos directamente para evitar anidamientos innecesarios
151+
return res.status(200).json(userData);
152+
} catch (error) {
153+
console.error("Error al obtener usuario por UID:", error);
154+
return res.status(500).json({ error: "Error interno del servidor" });
155+
}
156+
};
157+
158+
159+
160+

backend/src/controllers/rifa.controllers.js

Lines changed: 143 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
import { createRifa, getRifasUser, getRifa, updateRifa, decrementRifaTickets} from "../models/rifa.model.js";
1+
import { createRifa, getRifasUser, getRifa, updateRifa, decrementRifaTickets, rifaModel} from "../models/rifa.model.js";
2+
import { ticketModel } from "../models/ticket.models.js";
23
import admin from "../firebase.js";
34
import { supabase } from "../db.js";
5+
import { getUserByUid } from "../models/user.model.js";
46

57

68
export const createRifas = async (req, res) => {
@@ -141,4 +143,143 @@ export const decrementRifaTicket = async (req, res) => {
141143
res.status(500).json({ error: "Error interno del servidor" });
142144
}
143145

144-
}
146+
}
147+
148+
// Función para generar un número aleatorio verificable
149+
function generateVerifiableRandom(seed, max) {
150+
// Usamos un algoritmo simple pero verificable
151+
// En producción se puede usar un algoritmo más robusto
152+
let hash = 0;
153+
for (let i = 0; i < seed.length; i++) {
154+
hash = ((hash << 5) - hash) + seed.charCodeAt(i);
155+
hash |= 0; // Convertir a entero de 32 bits
156+
}
157+
158+
// Convertimos a positivo y tomamos el módulo para el rango requerido
159+
const positiveHash = Math.abs(hash);
160+
return positiveHash % max;
161+
}
162+
163+
// Controlador para verificar si una rifa está lista para sorteo
164+
export const checkRifaForDraw = async (req, res) => {
165+
try {
166+
const { rifaId } = req.params;
167+
const { token } = req.body;
168+
169+
if (!token) {
170+
return res.status(401).json({ error: "Autenticación requerida" });
171+
}
172+
173+
// Verificar que el usuario sea el creador de la rifa
174+
const decoded = await admin.auth().verifyIdToken(token);
175+
const { uid } = decoded;
176+
177+
const { data: rifa } = await rifaModel.getRifa(rifaId);
178+
if (!rifa || rifa.userID !== uid) {
179+
return res.status(403).json({ error: "No tienes permiso para realizar el sorteo de esta rifa" });
180+
}
181+
182+
const { isReady, reason } = await rifaModel.isRifaReadyForDraw(rifaId);
183+
184+
return res.status(200).json({
185+
isReady,
186+
reason: !isReady ? reason : null,
187+
message: isReady ? "La rifa está lista para sorteo" : "La rifa no está lista para sorteo"
188+
});
189+
190+
} catch (error) {
191+
console.error("Error al verificar rifa para sorteo:", error);
192+
return res.status(500).json({ error: "Error interno al verificar la rifa" });
193+
}
194+
};
195+
196+
// Controlador para realizar el sorteo
197+
export const realizarSorteo = async (req, res) => {
198+
try {
199+
const { rifaId } = req.params;
200+
const { token } = req.body;
201+
202+
if (!token) {
203+
return res.status(401).json({ error: "Autenticación requerida" });
204+
}
205+
206+
// Verificar que el usuario sea el creador de la rifa
207+
const decoded = await admin.auth().verifyIdToken(token);
208+
const { uid } = decoded;
209+
210+
const { data: rifa } = await rifaModel.getRifa(rifaId);
211+
if (!rifa || rifa.userID !== uid) {
212+
return res.status(403).json({ error: "No tienes permiso para realizar el sorteo de esta rifa" });
213+
}
214+
215+
// Verificar si la rifa está lista para sorteo
216+
const { isReady, reason } = await rifaModel.isRifaReadyForDraw(rifaId);
217+
if (!isReady) {
218+
return res.status(400).json({ error: reason || "Esta rifa no está lista para sorteo" });
219+
}
220+
221+
// Obtener todos los tickets vendidos
222+
const tickets = await ticketModel.getTicketsByRifaId(rifaId);
223+
224+
if (!tickets || tickets.length === 0) {
225+
return res.status(400).json({ error: "No hay tickets vendidos para esta rifa" });
226+
}
227+
228+
// Generar número aleatorio usando un seed verificable
229+
const timestamp = Date.now();
230+
const seed = `${timestamp}-${rifaId}-${uid}`;
231+
232+
// Seleccionar un índice aleatorio entre todos los boletos vendidos
233+
const randomIndex = generateVerifiableRandom(seed, tickets.length);
234+
235+
// Seleccionar ganador
236+
const ganador = tickets[randomIndex];
237+
238+
// Verificamos que el ticket tenga un número de boleto válido
239+
if (!ganador.numero_boleto) {
240+
// Si no tiene número de boleto, generamos uno con formato correcto
241+
ganador.numero_boleto = `#${String(ganador.id).padStart(4, '0')}`;
242+
243+
// En un entorno real, podríamos actualizar el boleto en la base de datos
244+
// pero para esta implementación solo lo usaremos para mostrar
245+
}
246+
247+
// Actualizar estado de la rifa y guardar ganador
248+
await rifaModel.updateRifaStatus(rifaId, 'Cerrada');
249+
await rifaModel.setRifaWinner(rifaId, ganador.id_user, ganador.id);
250+
251+
252+
const user = await getUserByUid(ganador.id_user);
253+
254+
if (!user) {
255+
return res.status(404).json({ error: "Usuario del ganador no encontrado" });
256+
}
257+
258+
const ganadorInfo = {
259+
ticketId: ganador.id,
260+
ticketNumber: ganador.numero_boleto,
261+
userId: user.data.uid,
262+
nombre: user.data.name ? user.data.name : 'Usuario',
263+
email: user.data.email ? user.data.email : '',
264+
};
265+
266+
return res.status(200).json({
267+
success: true,
268+
message: 'Sorteo realizado con éxito',
269+
timestamp,
270+
seed,
271+
randomIndex,
272+
totalTickets: tickets.length,
273+
ganador: ganadorInfo,
274+
rifa: {
275+
id: rifa.id,
276+
title: rifa.title,
277+
prize: rifa.prize
278+
}
279+
});
280+
281+
} catch (error) {
282+
console.error("Error al realizar sorteo:", error);
283+
return res.status(500).json({ error: "Error interno al realizar el sorteo" });
284+
}
285+
};

backend/src/controllers/ticket.controllers.js

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { createTicket, getAllTicketsModel } from "../models/ticket.models.js";
1+
import { createTicket, getAllTicketsModel, getTicketsByRifaId, ticketModel } from "../models/ticket.models.js";
22
import admin from "../firebase.js";
33

44

@@ -45,8 +45,54 @@ export const getAllTickets = async (req, res) => {
4545

4646
res.status(200).json({ tickets: data });
4747
} catch (error) {
48-
console.error("Error en la peticion de tdoas las rifas", error)
48+
console.error("Error en la peticion de todas las rifas", error)
4949
res.status(400).json({error : "Error interno del servidor"})
5050
}
51-
51+
}
52+
53+
// Nuevo controlador para obtener tickets por ID de rifa
54+
export const getTicketsByRifaIdController = async (req, res) => {
55+
try {
56+
const { rifaId } = req.params;
57+
58+
if (!rifaId) {
59+
return res.status(400).json({ error: "ID de rifa no proporcionado" });
60+
}
61+
62+
// Utiliza el modelo para obtener los tickets
63+
const tickets = await getTicketsByRifaId(rifaId);
64+
65+
res.status(200).json(tickets);
66+
} catch (error) {
67+
console.error("Error al obtener tickets por ID de rifa:", error);
68+
res.status(500).json({ error: "Error interno del servidor" });
69+
}
70+
}
71+
72+
// Controlador para obtener un ticket específico por su ID
73+
export const getTicketByIdController = async (req, res) => {
74+
try {
75+
const { ticketId } = req.params;
76+
77+
if (!ticketId) {
78+
return res.status(400).json({ error: "ID de ticket no proporcionado" });
79+
}
80+
81+
// Utiliza el modelo para obtener el ticket
82+
const { data, error } = await ticketModel.getTicketById(ticketId);
83+
84+
if (error) {
85+
console.error("Error al obtener ticket por ID:", error);
86+
return res.status(500).json({ error: "Error al obtener ticket" });
87+
}
88+
89+
if (!data) {
90+
return res.status(404).json({ error: "Ticket no encontrado" });
91+
}
92+
93+
res.status(200).json({ ticket: data });
94+
} catch (error) {
95+
console.error("Error al obtener ticket por ID:", error);
96+
res.status(500).json({ error: "Error interno del servidor" });
97+
}
5298
}

backend/src/models/rifa.model.js

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,4 +94,68 @@ export async function decrementRifaTickets(rifaId, amount){
9494

9595
if (error) throw error;
9696
return { data, error };
97-
}
97+
}
98+
99+
// Nuevas funciones para el sorteo de rifas
100+
101+
export async function updateRifaStatus(rifaId, status) {
102+
const { data, error } = await supabase
103+
.from('rifas')
104+
.update({ state: status })
105+
.eq('id', rifaId);
106+
107+
if (error) throw error;
108+
return { data, error };
109+
}
110+
111+
export async function setRifaWinner(rifaId, userId, ticketId) {
112+
const { data, error } = await supabase
113+
.from('rifas')
114+
.update({
115+
winner_user_id: userId,
116+
winner_ticket_id: ticketId,
117+
draw_date: new Date().toISOString()
118+
})
119+
.eq('id', rifaId);
120+
121+
if (error) throw error;
122+
return { data, error };
123+
}
124+
125+
// Verifica si una rifa está lista para sorteo
126+
export async function isRifaReadyForDraw(rifaId) {
127+
const { data: rifa, error } = await supabase
128+
.from('rifas')
129+
.select('*')
130+
.eq('id', rifaId)
131+
.single();
132+
133+
if (error) throw error;
134+
135+
// Una rifa está lista si:
136+
137+
// 2. No tiene ganador todavía
138+
// 3. Tiene al menos un ticket vendido
139+
const hasNoWinner = !rifa.winner_user_id;
140+
const hasTicketsSold = rifa.total_tickets_sold > 0;
141+
142+
return {
143+
isReady: hasNoWinner && hasTicketsSold,
144+
rifa,
145+
reason: !hasTicketsSold ? 'No hay tickets vendidos' :
146+
!hasNoWinner ? 'Esta rifa ya tiene un ganador' : null
147+
};
148+
}
149+
150+
// Exportamos también un objeto con todas las funciones para facilitar las importaciones
151+
export const rifaModel = {
152+
createRifa,
153+
getRifasUser,
154+
getRifa,
155+
updateRifa,
156+
deleteRifa,
157+
decrementRifaTickets,
158+
updateRifaStatus,
159+
setRifaWinner,
160+
isRifaReadyForDraw
161+
};

backend/src/models/ticket.models.js

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { supabase } from "../db.js";
22

33

44
export async function createTicket(ticket){
5+
56
const { data, error } = await supabase
67
.from('tickets')
78
.insert([
@@ -24,20 +25,55 @@ export async function createTicket(ticket){
2425

2526

2627
export async function getTicketsByRifaId(rifaId) {
28+
2729
const { data, error } = await supabase
2830
.from('tickets')
29-
.select('*')
30-
.eq('rifaId', rifaId);
31+
.select("*")
32+
.eq('id_rifa', rifaId);
33+
3134
if (error) throw error;
32-
return { data, error };
35+
36+
const processedData = data.map(ticket => {
37+
if (!ticket.numero_boleto) {
38+
ticket.numero_boleto = `#${String(ticket.id).padStart(4, '0')}`;
39+
}
40+
return ticket;
41+
});
42+
43+
return processedData; // Retornamos los datos procesados
3344
}
3445

3546
export async function getAllTicketsModel(userId) {
3647
const { data, error } = await supabase
3748
.from('tickets')
3849
.select('*')
39-
.eq('id_user', userId)
50+
.eq('id_user', userId);
4051

4152
if (error) throw error;
4253
return { data, error };
43-
}
54+
}
55+
56+
export async function getTicketById(ticketId) {
57+
const { data, error } = await supabase
58+
.from('tickets')
59+
.select('*')
60+
.eq('id', ticketId)
61+
.single();
62+
63+
if (error) throw error;
64+
65+
// Procesamos el número de boleto si no existe
66+
if (data && !data.numero_boleto) {
67+
data.numero_boleto = `#${String(data.id).padStart(4, '0')}`;
68+
}
69+
70+
return { data, error };
71+
}
72+
73+
// Exportamos las funciones en un objeto
74+
export const ticketModel = {
75+
createTicket,
76+
getTicketsByRifaId,
77+
getAllTicketsModel,
78+
getTicketById
79+
};

0 commit comments

Comments
 (0)