Sobre Llevar Un Historial De Los Movimientos De Usuarios De Un Sistema


Objetivo: Sobre Llevar Un Historial De Los Movimientos De Usuarios De Un Sistema
Buen dia Compañeros programadores, como estan, espero que bien y desandoles los mejores exitos en sus proyectos, llevo mucho tiempo que no me aparecia por aqui un abrazo, quiero preguntarles algo y es sobre llevar un historial de los movimientos de los usuarios que operan un sistema, por ejemplo yo quiero registrar los movimientos que hacen los usuarios operadores al agregar, modificar, eliminar, etc, se me ocurre llevar estos historiales en una tabla con los ides de estos operadores, el problema es este, que yo podria registrar que un usuario hizo un cambio a tal fecha, tal hora y registro la palabra modificó, pero para saber especificamente que hizo?, quiza pueda colocar cual fue el dato que modificó pero siempre y cuando este sea una palabra cuya tabla solo guarde un dato como por ejemplo el número de teléfono o la palabra de una ciudad, pero que pasaria si fuesen mas datos, como nombre, apellido, direccion, alli es donde me pone a pensar y creo que debo hacer a cada tabla de mi sistema un historial de movimientos y que a su ves haya una final y definitiva que agarre los ides de estos historiales para luego hacer una consulta y mostrar en una tabla general que fue lo que hizo el usuario y asi saber el dato exacto que gestiono!, quiza estoy equivocado!, no se que piensen ustedes, espero que los que tengan experiencia me puedan orientar, muchas gracias.

Objetivo: Re: Sobre Llevar Un Historial De Los Movimientos De Usuarios De Un Sistema
Bueno... lo que pides no es difícil, pero tiene implicación en muchas partes de tu sistema.
El sistema tiene dos partes: Por un lado la grabación y firmado de cada registro/consulta que se haga a la/s BBDD por otro la creación de un histórico de acciones de usuario y por último un sistema de consultas de todo eso.
La primera parte más o menos es así:
A.- FIRMAR TODAS LAS OPERACIONES Y MOSTRAR LA FIRMA
A.1.- Añade a TODAS las tablas de tu sistema dos campos al final

usr varchar(50)
fmod datetime

Estos campos sirven de firma para saber quién ha hecho la última modificación de cada registro. usr contendrá el "nick" de cada usuario (mejor no poner en él su código para mejorar la legibilidad). fmod contendrá la fecha y hora de la actualización.

A.2.- Para hacer sencillo el "firmado de los registros", en la función que utilizes para modificar la BBDD (yo uso un módulo para altas bajas y modificaciones), añade el firmado con el nick de usuario y la fecha y hora. Algo así (extraído de cómo lo hago yo en mi módulo):

...
...
rs = laConexion.edit(tabla, campoClave & "= &1", clave)
rs = Control2Result(oContenedor, rs) 'esta función rellena el result a partir del form o panel que contenga los controles
Try rs["usr"] = mComun.usuario.nick 'el nick del usuario. Le pongo el try por si la tabla no tuviese dicho campo
Try rs["fmod"] = Now 'la fecha y la hora. El try se pone por lo mismo de arriba
Try rs.Update
...
...


Con esto ya tienes seguimiento de quién ha hecho la última acción sobre cada registro.

A.3.- A TODOS los formularios de datos, añade un textbox llamado firma en la parte de abajo. A la hora de rellenar cada formulario con los datos existentes, añade ésto a la función que recupere los datos de la bbdd

...
...
'Este es parte del bucle con el que yo relleno los controles del form desde los campos del result (que se llaman igual que los controles).
'Tú sustituye ésto por la función que utilices para rellenar el result por eso te los pongo comentados
'For Each f In rs.Fields
' campo = f.Name 'con ésto le quitamos los alias de a., b., etc. a los campos.
' Try o = oContenedor.controls[campo]
' If Not Error And Not IsNull(o) Then
' If o Is Textedit Then
' o.richText = rs[o.name]
' Else
' Try o.Value = rs[f.Name]
' If Error Then o.Text = rs[f.Name]
' Endif
' Endif
' Next
Try oContenedor.controls["firma"].text = "Modificado por " & rs!usr & " el " & Format(rs!fmod, "dd-mm-yyyy") 'aquí muestras la firma en pantalla. Le pongo el Try por si el form no contuviese el campo firma
....
....


B) CREAR UN REGISTRO HISTÓRICO DE OPERACIONES SOBRE LA BBDD

B.1.- Creación de la tabla del histódiro.

Crea una tabla con éste diseño (Este es para mysql, puedes adaptarlo a la BBDD que uses:
CREATE TABLE `ad_hlog` (
`idhlog` int(8) unsigned NOT NULL AUTO_INCREMENT,
`tipo` varchar(100) DEFAULT NULL,
`detalle` mediumtext,
`usr` varchar(20) DEFAULT NULL,
`fmod` datetime DEFAULT NULL,
KEY `id` (`idhlog`)
) ENGINE=MyISAM AUTO_INCREMENT=0 DEFAULT CHARSET=utf8



tipo es una cadena que identifica la operación, por ejemplo: "Actualiza Personas", "Añade Registro en Personas", "Elimina Personas", donde Personas es, claro, una tabla existente.
detalle es el contenido de la operación, ahora hablamos de ello.
usr y fmod ya sabemos lo que contienen

B.2.- Función de actualización del histórico.

En el módulo de actualización de la BBDD, añade ésta función:
Public Sub hlog(tipo As String, detalle As String)

detalle = Replace(detalle, "'", "\\'") 'escapamos toda comilla que pueda haber
detalle = Replace(detalle, "\"", "\\\"")
detalle = "insert into ad_hlog (tipo, detalle, usr,fmod) values ('" & String.Mid(tipo, 1) & "', '" & String.Mid(detalle, 1) & "', '" & String.Mid(mComun.usuario.nick, 1) & "', " & "now()" & ")"
Connections[Application.name].exec(detalle)

Catch
'Enviamos un email a los administradores del programa indicando el error producido
mEMail.sendmail(mComun.entorno["mailprograma"], [mComun.entorno["mailerrores"]], "Error en " & Application.Name & ", "Usuario: " & mComun.usuario.nick & "\nFecha: " & Format(Now(), "dd-mm-yyyy hh:mm:ss") & "\n" & Error.Text & " - " & Error.where, mComun.servidores["mailserver"])

End

Fíjate que sólo grabamos cuatro campos, el tipo, el detalle y la firma del propio histórico usr y fmod

B.3.- Rellenado de la tabla del histórico:

En tu función de actualización de la BBDD (la misma en la que grababas la firma en el punto A.2) añade el siguiente código antes del update:
' dim cad as string
' Try rs["usr"] = mComun.usuario.nick 'el nick del usuario. Le pongo el try por si la tabla no tuviese dicho campo
' Try rs["fmod"] = Now 'la fecha y la hora. El try se pone por lo mismo de arriba
For Each f In rs.Fields
Try cad &= f.name & ": " & rs[f.name] & "\n" 'try porque si el campo es mediumtext y está vacío, casca
Next
' Try rs.Update

Y luego llama a la función de actualización del histórico con ésta línea:
mComun.hlog("Añade en " & tabla, cad)

donde tabla contiene el nombre de la tabla y cad, como ves más arriba, el nombre de los campos y el contenido de cada uno que se graban en la BBDD
Con ésto ya tienes todo lo que quieres saber grabado en el histórico de movimientos en la bbdd.
Te adjunto aquí mismo el módulo entero de actualización de la BBDD incluyendo todo lo que arriba te pongo (casi) tal y como lo usamos en la empresa.

' gambas module file

Public Function grabaRegistro(tabla As String, campoClave As String, clave As Variant, oContenedor As Object, Optional laConexion As Connection) As Boolean
'NOTE: la funcion grabaRegistro se utiliza para grabar un determinado valor de la base de datos por ejemplo a la hora de actualizar la clave de un usuario
' devuelve -1 si ha fallado la actualización y 0 si ha sido exitosa

Dim rs As Result
Dim lret As Integer
Dim cad As String
Dim f As ResultField

If Not laConexion Then
laConexion = Connections[Application.name]
Endif
If Not laConexion.Opened Then laConexion.open
rs = laConexion.edit(tabla, campoClave & "= &1", clave) ' utiliza la funcion edit de gambas para grabar los datos en vez de utilizar sentencias select
rs = Control2Result(oContenedor, rs)
Try rs["usr"] = mComun.usuario.nick
Try rs["fmod"] = Now
Try rs.Update
If Error Then
mEMail.sendmail(mComun.entorno["mailprograma"], [mComun.entorno["mailerrores"]], "Error en " & Application.Name & ", "Usuario: " & mComun.usuario.nick & "\nFecha: " & Format(Now(), "dd-mm-yyyy hh:mm:ss") & "\n" & Error.Text & " - " & Error.where, mComun.servidores["mailserver"])
lret = -1
Else
lret = 0
For Each f In rs.Fields
Try cad &= f.Name & "
: " & rs[f.Name] & "\n" 'try porque si el campo es mediumtext y está vacío, casca
Next
mComun.hlog("
Modifica " & tabla, cad)
End If
Return lret

End

Public Function creaRegistro(tabla As String, oContenedor As Object, Optional conec As Connection, Optional verClave As Boolean) As Variant

' devuelve -1 si ha fallado la adición. Si verClave=true se devuelve el id del registro añadido. Si verClave=false, se devuelve 0
Dim rs, r As Result
Dim lret As Integer
Dim f As ResultField
Dim cad, clave As String

If Not conec Then conec = Connections[Application.name]
If Not mComun.abreConexion(conec.name) Then Return False
rs = conec.create(tabla)
rs = Control2Result(oContenedor, rs)
Try rs["
usr"] = mComun.usuario.nick
Try rs["
fmod"] = Now
For Each f In rs.Fields
Try cad &= f.name & "
: " & rs[f.name] & "\n" 'try porque si el campo es mediumtext y está vacío, casca
Next
Try rs.Update
If Error Then
mEMail.sendmail(mComun.entorno["
mailprograma"], [mComun.entorno["mailerrores"]], "Error en " & Application.Name & ", "Usuario: " & mComun.usuario.nick & "\nFecha: " & Format(Now(), "dd-mm-yyyy hh:mm:ss") & "\n" & Error.Text & " - " & Error.where, mComun.servidores["mailserver"])
lret = -1
Else
If verClave Then
clave = conec.Tables[tabla].primaryKey[0]
r = conec.Exec("select Last_Insert_id() as '" & clave & "' from `" & tabla & "`")
lret = r[clave]
Else
lret = 0
Endif
mComun.hlog("Añade en " & tabla, cad)
Endif
Return lret

End

Public Function borraRegistro(tabla As String, campoClave As String, clave As Variant, Optional hcon As Connection) As Boolean

Dim r As Result
Dim f As ResultField
Dim cad As String

If Not hcon Then hcon = Connections[Application.name]
If Not mComun.abreConexion() Then Return False
r = hcon.exec(Subst("SELECT * FROM " & tabla & " where `" & campoClave & "` = &1", clave))
If r.Available Then
For Each f In r.Fields
Try cad &= f.Name & ": " & f[f.Name] & "\n" 'try porque si el campo es mediumtext y está vacío, casca
Next
Endif
Try hcon.delete(tabla, campoclave & "=&1", clave)
If Error Then
mEMail.sendmail(mComun.entorno["mailprograma"], [mComun.entorno["mailerrores"]], "Error en " & Application.Name & ", "Usuario: " & mComun.usuario.nick & "\nFecha: " & Format(Now(), "dd-mm-yyyy hh:mm:ss") & "\n" & Error.Text & " - " & Error.where, mComun.servidores["mailserver"])
Return False
Else
mComun.hlog("
Borra de " & tabla, cad)
Return True
Endif

End

Public Sub limpiaCampos(oContenedor As Object)

Dim o As Object
Dim n As Integer

For Each o In oContenedor.Children
If o Is TabStrip Or o Is IconPanel Or o Is TabPanel Or o Is ToolPanel Then
For n = 0 To o.Count - 1
o.index = n
limpiaCampos(o) 'Recursividad
Next
o.index = 0
Endif
If o Is Container Then
limpiaCampos(o) 'Recursividad
Endif
'Especificamos los tipos para que no limpie también los labels y demás.
If o Is ValueBox Or If o Is CheckBox Or If o Is RadioButton Or If o Is Color Or If o Is Button Or If o Is DirBox Or If o Is SwitchButton Or If o Is SpinBox Then
Try o.Value = 0
Else If o Is TextArea Or If o Is TextBox Or If o Is ListBox Or If o Is ComboBox Or If o Is DbListBox Or If o Is DbComboBox Or If o Is TextEditor Or If o Is TextEdit Then
Try o.Text = "
"
Else If o Is HtEdit Then
Try o.html = "
"
Endif
Next

End

Public Function Control2Result(oContenedor As Object, rs As Result) As Result ' este procedimiento se utiliza desde el formulario perfil para cargar los controles o listados en tiempo de ejecucion.
', Optional campoclave As String) As Result

Dim o As Object
Dim f As ResultField
Dim n As Integer
Dim campo As String

If oContenedor Is Form Then 'Esta debería ser la opción preferida, por velocidad.
For Each f In rs.Fields
campo = IIf(Mid(f.name, 2, 1) = "
.", Mid(f.name, 3), f.Name)
Try o = oContenedor.controls[campo]
If Not Error And Not IsNull(o) Then
If o Is Textedit Then
rs[o.name] = o.richText
Else
Try rs[o.Name] = o.Value
If Error Then rs[o.name] = o.Text
Endif
Endif
Next
Else
For Each o In oContenedor.Children
If o Is TabStrip Or o Is IconPanel Or o Is TabPanel Or o Is ToolPanel Then
For n = 0 To o.Count - 1
o.index = n
Control2Result(o, rs) 'Recursividad
Next
o.index = 0
Endif
If o Is Container Then
Control2Result(o, rs) 'Recursividad
Endif
For n = 0 To rs.Fields.count - 1
campo = IIf(Mid(rs.Fields[n].name, 2, 1) = "
.", Mid(rs.Fields[n].name, 3), rs.Fields[n].Name)
If o.name = campo Then
If o Is Textedit Then
rs[o.name] = o.richText
Else
Try rs[o.Name] = o.Value
If Error Then rs[o.name] = o.Text
Break
Endif
Endif
Next
Next
Endif

Finally

Return rs

Catch

mEMail.sendmail(mComun.entorno["
mailprograma"], [mComun.entorno["mailerrores"]], "Error en " & Application.Name & ", "Usuario: " & mComun.usuario.nick & "\nFecha: " & Format(Now(), "dd-mm-yyyy hh:mm:ss") & "\n" & Error.Text & " - " & Error.where, mComun.servidores["mailserver"])

End

Public Sub Result2Control(rs As Result, oContenedor As Object)

Dim o As Object
Dim f As ResultField
Dim campo As String
Dim n As Integer

If oContenedor Is Form Then 'Esta debería ser la opción preferida por velocidad
For Each f In rs.Fields
campo = IIf(Mid(f.name, 2, 1) = ".", Mid(f.name, 3), f.Name) 'con ésto le quitamos los alias de a., b., etc. a los campos.
Try o = oContenedor.controls[campo]
If Not Error And Not IsNull(o) Then
If o Is Textedit Then
o.richText = rs[o.name]
Else
Try o.Value = rs[f.Name]
If Error Then o.Text = rs[f.Name]
Endif
Endif
Next
Try oContenedor.controls["firma"].text = "Modificado por " & rs!usr & " el " & Format(rs!fmod, "dd-mm-yyyy")
Else
For Each o In oContenedor.Children
If Left(o.name, 5) = "firma" Then o.text = "Modificado por " & rs!usr & " el " & Format(rs!fmod, "dd-mm-yyyy")
If o Is TabStrip Or o Is IconPanel Or o Is TabPanel Or o Is ToolPanel Then
For n = 0 To o.Count - 1
o.index = n
Result2Control(rs, o) 'Recursividad
Next
o.index = 0
Endif
If o Is Container Then
Result2Control(rs, o) 'Recursividad
Endif
For Each f In rs.Fields
campo = IIf(Mid(f.name, 2, 1) = ".", Mid(f.name, 3), f.Name)
For n = 0 To oContenedor.children.count - 1
If oContenedor.children[n].name = campo Then
o = oContenedor.children[n]
If o Is TextEdit Then
o.richtext = rs[f.name]
Else
Try o.Value = rs[f.Name]
If Error Then o.Text = rs[f.Name]
Break
Endif
Endif
Next
Next
Next

Endif

Catch

mEMail.sendmail(mComun.entorno["mailprograma"], [mComun.entorno["mailerrores"]], "Error en " & Application.Name & ", "Usuario: " & mComun.usuario.nick & "\nFecha: " & Format(Now(), "dd-mm-yyyy hh:mm:ss") & "\n" & Error.Text & " - " & Error.where, mComun.servidores["mailserver"])

End

Public Sub hlog(tipo As String, detalle As String)

detalle = Replace(detalle, "
'", "\\'")
detalle = Replace(detalle, "\"", "\\\"")
detalle = "insert into ad_hlog (tipo, detalle, usr,fmod) values ('" & String.Mid(tipo, 1) & "', '" & String.Mid(detalle, 1) & "', '" & String.Mid(mComun.usuario.nick, 1) & "', " & "now()" & ")"
Connections[Application.name].exec(detalle)

Catch
mEMail.sendmail(mComun.entorno["mailprograma"], [mComun.entorno["mailerrores"]], "Error en " & Application.Name & ", "Usuario: " & mComun.usuario.nick & "\nFecha: " & Format(Now(), "dd-mm-yyyy hh:mm:ss") & "\n" & Error.Text & " - " & Error.where, mComun.servidores["mailserver"])

End


Sólo te falta la manera de consultar y manejar toda esa información... pero ahora que veo todo lo que te he puesto, me parece todo un poco confuso.

Dame un poco de tiempo y te hago un ejemplo funcional en Sqlite.

Saludos.

Perfil MP  
Objetivo: Re: Sobre Llevar Un Historial De Los Movimientos De Usuarios De Un Sistema
Muchas gracias señor shordi, se ve lo profesional que es, un abrazo, saludos.

Objetivo: Re: Sobre Llevar Un Historial De Los Movimientos De Usuarios De Un Sistema
Aquí tienes el ejemplo para sqlite.

https://foro.gambas-es.org/viewtopic.php?f=5&t=7605

Saludos

Perfil MP  
Objetivo: Re: Sobre Llevar Un Historial De Los Movimientos De Usuarios De Un Sistema
shordi escribió:  
Aquí tienes el ejemplo para sqlite.

https://foro.gambas-es.org/viewtopic.php?f=5&t=7605

Saludos


shordi que pasote!!!

Perfil MP  

Página 1 de 1


  
No puede crear mensajes
No puede responder temas
No puede editar sus mensajes
No puede borrar sus mensajes
No puede votar en encuestas
No puede adjuntar archivos
Puede descargar archivos
No puede publicar eventos en el calendario

   

Está utilizando la versión (Lo-Fi). Para ver la versión completa del foro, haga clic aquí.

Powered by Icy Phoenix based on phpBB
Design by DiDiDaDo

Página generada en:: 1.0849s (PHP: 19% SQL: 81%)
Consultas SQL: 45 - Debug off - GZIP Activado