blackcoffee2 2.1.0 → 2.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +67 -0
- package/CHANGELOG.md +167 -0
- package/README.md +1 -3
- package/config/database.json +11 -0
- package/controllers/admin/AuthController.js +2 -1
- package/core/ViewHelper.js +75 -0
- package/core/hotReload.js +1 -1
- package/data/blackcoffee_admin.db-shm +0 -0
- package/data/blackcoffee_admin.db-wal +0 -0
- package/includes/adminAuth.js +5 -3
- package/includes/sessions.js +1 -1
- package/otrack.tar.gz +0 -0
- package/package.json +4 -2
- package/programatically/initFlow.js +2 -2
- package/test-aplicacion.con-logisession/BlackCoffee.js +0 -226
- package/test-aplicacion.con-logisession/SSL_SETUP.md +0 -53
- package/test-aplicacion.con-logisession/certs/ca-certificate.pem +0 -32
- package/test-aplicacion.con-logisession/certs/ca-private-key.pem +0 -52
- package/test-aplicacion.con-logisession/certs/certificate-2048.pem +0 -22
- package/test-aplicacion.con-logisession/certs/certificate.pem +0 -32
- package/test-aplicacion.con-logisession/certs/private-key-2048.pem +0 -28
- package/test-aplicacion.con-logisession/certs/private-key.pem +0 -52
- package/test-aplicacion.con-logisession/config/iaQueueSetup.js +0 -84
- package/test-aplicacion.con-logisession/config/qwen-rules.json +0 -39
- package/test-aplicacion.con-logisession/controllers/analyticsController.js +0 -117
- package/test-aplicacion.con-logisession/controllers/auth/AdminAuthController.js +0 -142
- package/test-aplicacion.con-logisession/controllers/auth/AuthController.js +0 -439
- package/test-aplicacion.con-logisession/controllers/auth/AuthViewController.js +0 -223
- package/test-aplicacion.con-logisession/controllers/endpointController.js +0 -66
- package/test-aplicacion.con-logisession/controllers/example.js +0 -183
- package/test-aplicacion.con-logisession/controllers/iaQueueController.js +0 -367
- package/test-aplicacion.con-logisession/controllers/queueController.js +0 -206
- package/test-aplicacion.con-logisession/controllers/qwenQueueController.js +0 -197
- package/test-aplicacion.con-logisession/controllers/test.js +0 -0
- package/test-aplicacion.con-logisession/controllers/tracking/EventsNoFinishController.js +0 -78
- package/test-aplicacion.con-logisession/controllers/tracking/TrackingController.js +0 -412
- package/test-aplicacion.con-logisession/controllers/tracking/TrackingControllerWithLoadModel.js +0 -437
- package/test-aplicacion.con-logisession/hooks/admin-hooks.js +0 -20
- package/test-aplicacion.con-logisession/hooks/general-hooks.js +0 -97
- package/test-aplicacion.con-logisession/hooks/queue-hooks.js +0 -64
- package/test-aplicacion.con-logisession/hooks/route-directory-hooks.js +0 -38
- package/test-aplicacion.con-logisession/hooks/security-hooks.js +0 -24
- package/test-aplicacion.con-logisession/insitu-admin-client/README.md +0 -69
- package/test-aplicacion.con-logisession/insitu-admin-client/package.json +0 -23
- package/test-aplicacion.con-logisession/insitu-admin-client.js +0 -257
- package/test-aplicacion.con-logisession/models/ExampleModel.js +0 -88
- package/test-aplicacion.con-logisession/models/QueueJobModel.js +0 -263
- package/test-aplicacion.con-logisession/models/TokenModel.js +0 -207
- package/test-aplicacion.con-logisession/models/auth/AuthModel.js +0 -66
- package/test-aplicacion.con-logisession/models/auth/UserModel.js +0 -189
- package/test-aplicacion.con-logisession/models/tracking/CompletedCartModel.js +0 -213
- package/test-aplicacion.con-logisession/models/tracking/EventModel.js +0 -366
- package/test-aplicacion.con-logisession/models/tracking/EventsNoFinishModel.js +0 -131
- package/test-aplicacion.con-logisession/models/tracking/SessionModel.js +0 -360
- package/test-aplicacion.con-logisession/models/tracking/SiteFlowModel.js +0 -286
- package/test-aplicacion.con-logisession/models/tracking/TokenModel.js +0 -207
- package/test-aplicacion.con-logisession/package-lock.json +0 -3313
- package/test-aplicacion.con-logisession/package.json +0 -32
- package/test-aplicacion.con-logisession/public/blackcoffee-welcome/index.html +0 -1339
- package/test-aplicacion.con-logisession/public/css/style.css +0 -64
- package/test-aplicacion.con-logisession/public/ejemplo-estatica/index.html +0 -18
- package/test-aplicacion.con-logisession/public/ejemplo-estatica/script.js +0 -16
- package/test-aplicacion.con-logisession/public/ejemplo-estatica/styles.css +0 -43
- package/test-aplicacion.con-logisession/public/images/logo.svg +0 -7
- package/test-aplicacion.con-logisession/public/js/main.js +0 -67
- package/test-aplicacion.con-logisession/routes/analytics-routes.json +0 -8
- package/test-aplicacion.con-logisession/routes/auth-routes.json +0 -98
- package/test-aplicacion.con-logisession/routes/blackcoffee-welcome-routes.json +0 -20
- package/test-aplicacion.con-logisession/routes/duplicate-test-routes.json.disabled +0 -16
- package/test-aplicacion.con-logisession/routes/ejemplo-estatica-routes.json +0 -11
- package/test-aplicacion.con-logisession/routes/endpoints-routes.json +0 -8
- package/test-aplicacion.con-logisession/routes/ia-queue-routes.json +0 -26
- package/test-aplicacion.con-logisession/routes/product-routes.json.disabled +0 -20
- package/test-aplicacion.con-logisession/routes/queue-routes.json +0 -32
- package/test-aplicacion.con-logisession/routes/qwen-routes.json +0 -14
- package/test-aplicacion.con-logisession/routes/static-routes.json +0 -29
- package/test-aplicacion.con-logisession/routes/tracking-routes.json +0 -58
- package/test-aplicacion.con-logisession/routes/tracking-with-loadmodel-routes.json +0 -51
- package/test-aplicacion.con-logisession/utils/dbAdapter.js +0 -88
- package/test-aplicacion.con-logisession/utils/qbWrapper.js +0 -4
- package/test-aplicacion.con-logisession/utils/queueProcessor.js +0 -305
- package/test-aplicacion.con-logisession/utils/qwenRulesService.js +0 -131
- package/test-aplicacion.con-logisession/utils/tokenHelper.js +0 -22
- package/test-aplicacion.con-logisession/views/auth/dashboard.html +0 -443
- package/test-aplicacion.con-logisession/views/auth/forgot-password.html +0 -200
- package/test-aplicacion.con-logisession/views/auth/login.html +0 -213
- package/test-aplicacion.con-logisession/views/auth/register.html +0 -294
- package/test-aplicacion.con-logisession/views/contact/form.html +0 -47
- package/test-aplicacion.con-logisession/views/products/index.html +0 -39
|
@@ -1,366 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Modelo para la tabla de eventos en el framework JERK
|
|
3
|
-
* Implementación del componente MVC EventModel.js
|
|
4
|
-
* Utiliza MariaDB como adaptador de base de datos
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
const { ModelBase } = require('insitu-js');
|
|
8
|
-
const tokenHelper = require('../../utils/tokenHelper');
|
|
9
|
-
const { getSharedAdapter } = require('../../utils/dbAdapter');
|
|
10
|
-
const { QueryBuilder } = require('insitu-js');
|
|
11
|
-
|
|
12
|
-
class EventModel extends ModelBase {
|
|
13
|
-
constructor(options = {}) {
|
|
14
|
-
// Obtener el adaptador centralizado
|
|
15
|
-
const adapter = getSharedAdapter();
|
|
16
|
-
|
|
17
|
-
super({
|
|
18
|
-
...options,
|
|
19
|
-
tableName: options.tableName || 'events_no_finish', // Tabla por defecto
|
|
20
|
-
adapter: adapter
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
// Inicializar QueryBuilder con el adaptador centralizado
|
|
24
|
-
this.queryBuilder = new QueryBuilder(adapter, 'events_no_finish');
|
|
25
|
-
|
|
26
|
-
// Definir campos del modelo
|
|
27
|
-
this.fields = {
|
|
28
|
-
id: { type: 'integer', primaryKey: true, autoIncrement: true },
|
|
29
|
-
session_header_id: { type: 'integer' },
|
|
30
|
-
event_type: { type: 'string', required: true },
|
|
31
|
-
qty: { type: 'integer' },
|
|
32
|
-
product_id: { type: 'integer' },
|
|
33
|
-
sku: { type: 'string' },
|
|
34
|
-
product_name: { type: 'string' },
|
|
35
|
-
url: { type: 'string' },
|
|
36
|
-
price: { type: 'decimal' },
|
|
37
|
-
old_qty: { type: 'integer' },
|
|
38
|
-
new_qty: { type: 'integer' },
|
|
39
|
-
compressed_data: { type: 'text' },
|
|
40
|
-
data_hash: { type: 'string' },
|
|
41
|
-
event_timestamp: { type: 'datetime' },
|
|
42
|
-
session_id: { type: 'string' },
|
|
43
|
-
wtf_session: { type: 'string' },
|
|
44
|
-
site_url: { type: 'string' },
|
|
45
|
-
created_at: { type: 'datetime', auto: 'create' },
|
|
46
|
-
updated_at: { type: 'datetime', auto: 'update' }
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Registra un evento
|
|
52
|
-
* @param {Object} eventData - Datos del evento
|
|
53
|
-
* @returns {Promise<Object>} - Resultado de la operación
|
|
54
|
-
*/
|
|
55
|
-
async registerEvent(eventData) {
|
|
56
|
-
const {
|
|
57
|
-
session_header_id, event_type, qty, product_id, sku, product_name,
|
|
58
|
-
url, price, old_qty, new_qty, compressed_data, data_hash,
|
|
59
|
-
event_timestamp, session_id, wtf_session, site_url, token
|
|
60
|
-
} = eventData;
|
|
61
|
-
|
|
62
|
-
// Validar token
|
|
63
|
-
const isValidToken = await tokenHelper.validateToken(token);
|
|
64
|
-
if (!isValidToken) {
|
|
65
|
-
throw new Error('Token inválido');
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// Validar campos requeridos
|
|
69
|
-
if (!event_type) {
|
|
70
|
-
throw new Error('event_type es requerido');
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
try {
|
|
74
|
-
// Determinar la tabla destino según el tipo de evento
|
|
75
|
-
let tableName = 'events_no_finish'; // Por defecto
|
|
76
|
-
|
|
77
|
-
if (event_type === 'checkout_completed') {
|
|
78
|
-
tableName = 'completed_carts';
|
|
79
|
-
} else if (event_type === 'page_view') {
|
|
80
|
-
tableName = 'site_flow';
|
|
81
|
-
}
|
|
82
|
-
// Otros eventos van a events_no_finish por defecto
|
|
83
|
-
|
|
84
|
-
// Asegurar valores por defecto para campos que no pueden ser NULL
|
|
85
|
-
const safeSessionHeaderId = session_header_id !== undefined && session_header_id !== null ? session_header_id : 0;
|
|
86
|
-
|
|
87
|
-
// Crear el objeto de datos para insertar
|
|
88
|
-
const newEvent = {
|
|
89
|
-
session_header_id: safeSessionHeaderId,
|
|
90
|
-
event_type,
|
|
91
|
-
qty: qty || null,
|
|
92
|
-
product_id: product_id || null,
|
|
93
|
-
sku: sku || null,
|
|
94
|
-
product_name: product_name || null,
|
|
95
|
-
url: url || null,
|
|
96
|
-
price: price || null,
|
|
97
|
-
old_qty: old_qty || null,
|
|
98
|
-
new_qty: new_qty || null,
|
|
99
|
-
compressed_data: compressed_data || null,
|
|
100
|
-
data_hash: data_hash || null,
|
|
101
|
-
event_timestamp: event_timestamp || null,
|
|
102
|
-
session_id: session_id || null,
|
|
103
|
-
wtf_session: wtf_session || null,
|
|
104
|
-
site_url: site_url || null
|
|
105
|
-
};
|
|
106
|
-
|
|
107
|
-
// Usar QueryBuilder con la tabla específica
|
|
108
|
-
// Creamos un nuevo QueryBuilder temporal para la tabla específica
|
|
109
|
-
const { QueryBuilder: QB } = require('insitu-js');
|
|
110
|
-
const tempQueryBuilder = new QB(this.queryBuilder.adapter, tableName);
|
|
111
|
-
|
|
112
|
-
// Insertar el evento usando QueryBuilder
|
|
113
|
-
const result = await tempQueryBuilder.insert(newEvent);
|
|
114
|
-
|
|
115
|
-
console.log(`[TRACKING] Evento registrado: ${event_type} en ${tableName}`);
|
|
116
|
-
|
|
117
|
-
// Si el evento es checkout_completed, mover los eventos relacionados de events_no_finish a completed_carts
|
|
118
|
-
if (event_type === 'checkout_completed') {
|
|
119
|
-
try {
|
|
120
|
-
// Obtener todos los session_id únicos de los registros cuyo event_type sea checkout_completed en completed_carts
|
|
121
|
-
const checkoutCompletedQuery = new QB(this.queryBuilder.adapter, 'completed_carts');
|
|
122
|
-
const checkoutCompletedSessions = await checkoutCompletedQuery
|
|
123
|
-
.select('DISTINCT session_id')
|
|
124
|
-
.where('event_type', '=', 'checkout_completed')
|
|
125
|
-
.get();
|
|
126
|
-
|
|
127
|
-
// Mover todos los registros con los session_ids obtenidos hacia la tabla completed_carts
|
|
128
|
-
for (const row of checkoutCompletedSessions) {
|
|
129
|
-
const sessionId = row.session_id;
|
|
130
|
-
|
|
131
|
-
// Consultar eventos relacionados en events_no_finish para este session_id
|
|
132
|
-
const relatedEventsQuery = new QB(this.queryBuilder.adapter, 'events_no_finish');
|
|
133
|
-
const relatedEvents = await relatedEventsQuery
|
|
134
|
-
.where('session_id', '=', sessionId)
|
|
135
|
-
.orderBy('id', 'ASC')
|
|
136
|
-
.get();
|
|
137
|
-
|
|
138
|
-
if (relatedEvents.length > 0) {
|
|
139
|
-
// Insertar los eventos relacionados en completed_carts
|
|
140
|
-
const completedCartsQuery = new QB(this.queryBuilder.adapter, 'completed_carts');
|
|
141
|
-
|
|
142
|
-
for (const event of relatedEvents) {
|
|
143
|
-
// Excluir el campo id ya que es autoincremental
|
|
144
|
-
const { id, ...eventData } = event;
|
|
145
|
-
await completedCartsQuery.insert(eventData);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// Eliminar los eventos movidos de events_no_finish
|
|
149
|
-
const deleteQuery = new QB(this.queryBuilder.adapter, 'events_no_finish');
|
|
150
|
-
await deleteQuery.where('session_id', '=', sessionId).delete();
|
|
151
|
-
|
|
152
|
-
console.log(`[TRACKING] Movidos ${relatedEvents.length} eventos de la sesión ${sessionId} de events_no_finish a completed_carts`);
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
} catch (moveError) {
|
|
156
|
-
console.error('[ERROR] Al mover eventos relacionados:', moveError);
|
|
157
|
-
// No lanzamos el error para que no afecte el registro principal
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
// Convertir BigInt a string para evitar problemas de serialización
|
|
162
|
-
const serializedResult = JSON.parse(JSON.stringify({ id: result.insertId, ...newEvent }, (key, value) =>
|
|
163
|
-
typeof value === 'bigint' ? value.toString() : value
|
|
164
|
-
));
|
|
165
|
-
|
|
166
|
-
return {
|
|
167
|
-
success: true,
|
|
168
|
-
message: 'Evento registrado exitosamente',
|
|
169
|
-
event_type: event_type,
|
|
170
|
-
table: tableName,
|
|
171
|
-
data: serializedResult
|
|
172
|
-
};
|
|
173
|
-
} catch (error) {
|
|
174
|
-
console.error('[ERROR] registerEvent:', error);
|
|
175
|
-
throw new Error('Error interno del servidor');
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
/**
|
|
180
|
-
* Consulta personalizada con paginación, filtros y búsqueda global
|
|
181
|
-
* @param {Object} filters - Filtros para la consulta
|
|
182
|
-
* @param {Object} pagination - Parámetros de paginación
|
|
183
|
-
* @param {string} search - Término de búsqueda global
|
|
184
|
-
* @returns {Promise<Object>} - Resultados con información de paginación
|
|
185
|
-
*/
|
|
186
|
-
async queryWithFilters(filters = {}, pagination = {}, search = '') {
|
|
187
|
-
try {
|
|
188
|
-
// Mostrar parámetros de filtro para depuración
|
|
189
|
-
console.log('Filtros recibidos en queryWithFilters:', filters);
|
|
190
|
-
|
|
191
|
-
// Extraer parámetros de paginación
|
|
192
|
-
const offset = pagination.offset !== undefined ? parseInt(pagination.offset) : 0;
|
|
193
|
-
const limit = pagination.limit ? parseInt(pagination.limit) : 10;
|
|
194
|
-
|
|
195
|
-
// Extraer parámetros de filtro
|
|
196
|
-
const { created_at_min, created_at_max, date_from, date_to, event_type, session_id, wtf_session, url, site_url } = filters;
|
|
197
|
-
|
|
198
|
-
// Construir condiciones WHERE dinámicamente
|
|
199
|
-
let whereConditions = [];
|
|
200
|
-
let whereParams = [];
|
|
201
|
-
|
|
202
|
-
// Filtro por fecha desde (created_at_min)
|
|
203
|
-
if (created_at_min) {
|
|
204
|
-
// Si la fecha no tiene hora (es solo YYYY-MM-DD), agregar hora mínima del día
|
|
205
|
-
let startDate = created_at_min;
|
|
206
|
-
if (created_at_min.length === 10) { // Formato YYYY-MM-DD
|
|
207
|
-
startDate = `${created_at_min} 00:00:00`;
|
|
208
|
-
}
|
|
209
|
-
whereConditions.push('created_at >= ?');
|
|
210
|
-
whereParams.push(startDate);
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
// Filtro por fecha hasta (created_at_max)
|
|
214
|
-
if (created_at_max) {
|
|
215
|
-
// Si la fecha de fin no tiene hora (es solo YYYY-MM-DD), agregar hora 23:59:59 para incluir todo el día
|
|
216
|
-
let endDate = created_at_max;
|
|
217
|
-
if (created_at_max.length === 10) { // Formato YYYY-MM-DD
|
|
218
|
-
endDate = `${created_at_max} 23:59:59`;
|
|
219
|
-
}
|
|
220
|
-
whereConditions.push('created_at <= ?');
|
|
221
|
-
whereParams.push(endDate);
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
// Filtro por fecha desde (date_from) - para event_timestamp
|
|
225
|
-
if (date_from) {
|
|
226
|
-
// Si la fecha no tiene hora (es solo YYYY-MM-DD), agregar hora mínima del día
|
|
227
|
-
let startDate = date_from;
|
|
228
|
-
if (date_from.length === 10) { // Formato YYYY-MM-DD
|
|
229
|
-
startDate = `${date_from} 00:00:00`;
|
|
230
|
-
}
|
|
231
|
-
console.log('Aplicando filtro: event_timestamp >=', startDate);
|
|
232
|
-
whereConditions.push('event_timestamp >= ?');
|
|
233
|
-
whereParams.push(startDate);
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
// Filtro por fecha hasta (date_to) - para event_timestamp
|
|
237
|
-
if (date_to) {
|
|
238
|
-
// Si la fecha de fin no tiene hora (es solo YYYY-MM-DD), agregar hora 23:59:59 para incluir todo el día
|
|
239
|
-
let endDate = date_to;
|
|
240
|
-
if (date_to.length === 10) { // Formato YYYY-MM-DD
|
|
241
|
-
endDate = `${date_to} 23:59:59`;
|
|
242
|
-
}
|
|
243
|
-
console.log('Aplicando filtro: event_timestamp <=', endDate);
|
|
244
|
-
whereConditions.push('event_timestamp <= ?');
|
|
245
|
-
whereParams.push(endDate);
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
// Filtros por campos específicos
|
|
249
|
-
if (event_type) {
|
|
250
|
-
whereConditions.push('event_type = ?');
|
|
251
|
-
whereParams.push(event_type);
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
if (session_id) {
|
|
255
|
-
whereConditions.push('session_id = ?');
|
|
256
|
-
whereParams.push(session_id);
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
if (wtf_session) {
|
|
260
|
-
whereConditions.push('wtf_session = ?');
|
|
261
|
-
whereParams.push(wtf_session);
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
if (url) {
|
|
265
|
-
whereConditions.push('url LIKE ?');
|
|
266
|
-
whereParams.push(`%${url}%`);
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
if (site_url) {
|
|
270
|
-
whereConditions.push('site_url = ?');
|
|
271
|
-
whereParams.push(site_url);
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
// Aplicar búsqueda global si se proporciona
|
|
275
|
-
if (search) {
|
|
276
|
-
// Campos en los que se realiza la búsqueda global
|
|
277
|
-
const searchFields = ['event_type', 'product_name', 'sku', 'url'];
|
|
278
|
-
|
|
279
|
-
const searchConditions = searchFields.map(field => `${field} LIKE ?`).join(' OR ');
|
|
280
|
-
whereConditions.push(`(${searchConditions})`);
|
|
281
|
-
|
|
282
|
-
// Agregar parámetros de búsqueda
|
|
283
|
-
searchFields.forEach(() => {
|
|
284
|
-
whereParams.push(`%${search}%`);
|
|
285
|
-
});
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
// Construir la cláusula WHERE completa
|
|
289
|
-
const whereClause = whereConditions.length > 0
|
|
290
|
-
? 'WHERE ' + whereConditions.join(' AND ')
|
|
291
|
-
: 'WHERE 1=1'; // Asegurar que siempre haya una condición
|
|
292
|
-
|
|
293
|
-
// Consulta SQL para obtener los registros
|
|
294
|
-
const query = `
|
|
295
|
-
SELECT *
|
|
296
|
-
FROM events_no_finish
|
|
297
|
-
${whereClause}
|
|
298
|
-
ORDER BY id DESC
|
|
299
|
-
LIMIT ? OFFSET ?
|
|
300
|
-
`;
|
|
301
|
-
|
|
302
|
-
// Agregar parámetros de paginación
|
|
303
|
-
const allParams = [...whereParams, limit, offset];
|
|
304
|
-
|
|
305
|
-
// Usar el método complexQuery del QueryBuilder
|
|
306
|
-
const { QueryBuilder } = require('insitu-js');
|
|
307
|
-
const qb = new QueryBuilder(this.queryBuilder.adapter);
|
|
308
|
-
qb.complexQuery(query, allParams);
|
|
309
|
-
|
|
310
|
-
// Obtener los resultados
|
|
311
|
-
const rows = await qb.get();
|
|
312
|
-
|
|
313
|
-
// Consulta para obtener el conteo total con los mismos filtros
|
|
314
|
-
const countQuery = `
|
|
315
|
-
SELECT COUNT(*) as total
|
|
316
|
-
FROM events_no_finish
|
|
317
|
-
${whereClause}
|
|
318
|
-
`;
|
|
319
|
-
|
|
320
|
-
// Usar el método complexQuery del QueryBuilder para la consulta de conteo
|
|
321
|
-
const countQb = new QueryBuilder(this.queryBuilder.adapter);
|
|
322
|
-
countQb.complexQuery(countQuery, allParams.slice(0, -2)); // Sin parámetros de paginación
|
|
323
|
-
|
|
324
|
-
const countResult = await countQb.first();
|
|
325
|
-
const total = countResult ? countResult.total : 0;
|
|
326
|
-
|
|
327
|
-
return {
|
|
328
|
-
total: parseInt(total),
|
|
329
|
-
rows: rows
|
|
330
|
-
};
|
|
331
|
-
} catch (error) {
|
|
332
|
-
console.error(`Error en queryWithFilters para ${this.tableName}:`, error.message);
|
|
333
|
-
throw error;
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
/**
|
|
338
|
-
* Obtiene eventos con paginación, filtros y búsqueda
|
|
339
|
-
* @param {Object} queryParams - Parámetros de consulta (offset, limit, search, filtros)
|
|
340
|
-
* @returns {Promise<Object>} - Resultados con información de paginación
|
|
341
|
-
*/
|
|
342
|
-
async getEvents(queryParams = {}) {
|
|
343
|
-
// Obtener parámetros de paginación
|
|
344
|
-
const offset = parseInt(queryParams.offset) || 0;
|
|
345
|
-
const limit = parseInt(queryParams.limit) || 10;
|
|
346
|
-
const search = queryParams.search || '';
|
|
347
|
-
|
|
348
|
-
// Obtener otros parámetros de filtro excluyendo los de paginación y búsqueda
|
|
349
|
-
const filters = {};
|
|
350
|
-
for (const [key, value] of Object.entries(queryParams)) {
|
|
351
|
-
if (!['offset', 'limit', 'search', 'sort', 'order'].includes(key)) {
|
|
352
|
-
if (value !== '') { // Solo añadir filtros que no estén vacíos
|
|
353
|
-
filters[key] = value;
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
return await this.queryWithFilters(
|
|
359
|
-
filters,
|
|
360
|
-
{ offset, limit },
|
|
361
|
-
search
|
|
362
|
-
);
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
module.exports = EventModel;
|
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Modelo para obtener sesiones únicas de eventos no finalizados
|
|
3
|
-
* EventsNoFinishModel.js
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const { ModelBase } = require('insitu-js');
|
|
7
|
-
const { getSharedAdapter } = require('../../utils/dbAdapter');
|
|
8
|
-
const { QueryBuilder } = require('insitu-js');
|
|
9
|
-
|
|
10
|
-
class EventsNoFinishModel extends ModelBase {
|
|
11
|
-
constructor(options = {}) {
|
|
12
|
-
// Obtener el adaptador centralizado
|
|
13
|
-
const adapter = getSharedAdapter();
|
|
14
|
-
|
|
15
|
-
super({
|
|
16
|
-
...options,
|
|
17
|
-
tableName: options.tableName || 'events_no_finish',
|
|
18
|
-
adapter: adapter
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
// Inicializar QueryBuilder con el adaptador centralizado
|
|
22
|
-
this.queryBuilder = new QueryBuilder(adapter, 'events_no_finish');
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Obtiene sesiones únicas de eventos no finalizados con paginación y filtros
|
|
27
|
-
* @param {Object} filters - Filtros para la consulta
|
|
28
|
-
* @param {Object} pagination - Parámetros de paginación
|
|
29
|
-
* @returns {Promise<Object>} - Resultados con información de paginación
|
|
30
|
-
*/
|
|
31
|
-
async getUniqueSessions(filters = {}, pagination = {}) {
|
|
32
|
-
try {
|
|
33
|
-
// Extraer parámetros de paginación
|
|
34
|
-
const offset = pagination.offset !== undefined ? parseInt(pagination.offset) : 0;
|
|
35
|
-
const limit = pagination.limit ? parseInt(pagination.limit) : 10;
|
|
36
|
-
|
|
37
|
-
// Definir los tipos de eventos que consideramos como "no finalizados"
|
|
38
|
-
// Esto excluye los eventos de checkout_completed
|
|
39
|
-
const incompleteEventTypes = ['view_product', 'add_to_cart', 'checkout_initialized'];
|
|
40
|
-
|
|
41
|
-
// Construir condiciones WHERE dinámicamente
|
|
42
|
-
let whereConditions = ['event_type IN (?) AND product_id IS NOT NULL'];
|
|
43
|
-
let whereParams = [incompleteEventTypes];
|
|
44
|
-
|
|
45
|
-
// Filtros adicionales
|
|
46
|
-
const { session_id, wtf_session, site_url, event_type, date_from, date_to } = filters;
|
|
47
|
-
|
|
48
|
-
if (session_id) {
|
|
49
|
-
whereConditions.push('session_id = ?');
|
|
50
|
-
whereParams.push(session_id);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
if (wtf_session) {
|
|
54
|
-
whereConditions.push('wtf_session = ?');
|
|
55
|
-
whereParams.push(wtf_session);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
if (site_url) {
|
|
59
|
-
whereConditions.push('site_url = ?');
|
|
60
|
-
whereParams.push(site_url);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
if (event_type) {
|
|
64
|
-
whereConditions.push('event_type = ?');
|
|
65
|
-
whereParams.push(event_type);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
if (date_from) {
|
|
69
|
-
let startDate = date_from;
|
|
70
|
-
if (date_from.length === 10) { // Formato YYYY-MM-DD
|
|
71
|
-
startDate = `${date_from} 00:00:00`;
|
|
72
|
-
}
|
|
73
|
-
whereConditions.push('created_at >= ?');
|
|
74
|
-
whereParams.push(startDate);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
if (date_to) {
|
|
78
|
-
let endDate = date_to;
|
|
79
|
-
if (date_to.length === 10) { // Formato YYYY-MM-DD
|
|
80
|
-
endDate = `${date_to} 23:59:59`;
|
|
81
|
-
}
|
|
82
|
-
whereConditions.push('created_at <= ?');
|
|
83
|
-
whereParams.push(endDate);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// Consulta SQL para obtener las sesiones únicas
|
|
87
|
-
const query = `
|
|
88
|
-
SELECT DISTINCT session_id, wtf_session, site_url, product_id, product_name, MAX(created_at) as last_event_date
|
|
89
|
-
FROM events_no_finish
|
|
90
|
-
WHERE ${whereConditions.join(' AND ')}
|
|
91
|
-
GROUP BY session_id, wtf_session, site_url, product_id, product_name
|
|
92
|
-
ORDER BY last_event_date DESC
|
|
93
|
-
LIMIT ? OFFSET ?
|
|
94
|
-
`;
|
|
95
|
-
|
|
96
|
-
// Agregar parámetros de paginación
|
|
97
|
-
const allParams = [...whereParams, limit, offset];
|
|
98
|
-
|
|
99
|
-
// Ejecutar la consulta
|
|
100
|
-
const { QueryBuilder } = require('insitu-js');
|
|
101
|
-
const qb = new QueryBuilder(this.queryBuilder.adapter);
|
|
102
|
-
qb.complexQuery(query, allParams);
|
|
103
|
-
|
|
104
|
-
// Obtener los resultados
|
|
105
|
-
const rows = await qb.get();
|
|
106
|
-
|
|
107
|
-
// Consulta para obtener el conteo total de sesiones únicas
|
|
108
|
-
const countQuery = `
|
|
109
|
-
SELECT COUNT(DISTINCT CONCAT(session_id, '-', wtf_session, '-', site_url, '-', product_id, '-', product_name)) as total
|
|
110
|
-
FROM events_no_finish
|
|
111
|
-
WHERE ${whereConditions.join(' AND ')}
|
|
112
|
-
`;
|
|
113
|
-
|
|
114
|
-
// Ejecutar la consulta de conteo
|
|
115
|
-
const countQb = new QueryBuilder(this.queryBuilder.adapter);
|
|
116
|
-
countQb.complexQuery(countQuery, whereParams);
|
|
117
|
-
const countResult = await countQb.first();
|
|
118
|
-
const total = countResult ? countResult.total : 0;
|
|
119
|
-
|
|
120
|
-
return {
|
|
121
|
-
total: parseInt(total),
|
|
122
|
-
rows: rows
|
|
123
|
-
};
|
|
124
|
-
} catch (error) {
|
|
125
|
-
console.error(`Error en getUniqueSessions:`, error.message);
|
|
126
|
-
throw error;
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
module.exports = EventsNoFinishModel;
|