Portal    Foro    Buscar    FAQ    Registrarse    Conectarse


Publicar nuevo tema  Responder al tema 
Página 1 de 1
 
 
Sobre Llevar Un Historial De Los Movimientos De Usuarios De Un Sistema
Autor Mensaje
Responder citando   Descargar mensaje  
Mensaje 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.
 



 
jousseph - Ver perfil del usuarioEnviar mensaje privadoVer la galería personal del usuario 
Volver arribaPágina inferior
Responder citando   Descargar mensaje  
Mensaje 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.
 




===================
No podemos regresar
 
shordi - Ver perfil del usuarioEnviar mensaje privado 
Volver arribaPágina inferior
Responder citando   Descargar mensaje  
Mensaje 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.
 



 
jousseph - Ver perfil del usuarioEnviar mensaje privadoVer la galería personal del usuario 
Volver arribaPágina inferior
Responder citando   Descargar mensaje  
Mensaje 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
 




===================
No podemos regresar
 
shordi - Ver perfil del usuarioEnviar mensaje privado 
Volver arribaPágina inferior
Responder citando   Descargar mensaje  
Mensaje 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!!!  
 



 
gambafeliz - Ver perfil del usuarioEnviar mensaje privado 
Volver arribaPágina inferior
Mostrar mensajes anteriores:    
 
OcultarTemas parecidos
Tema Autor Foro Respuestas último mensaje
No hay nuevos mensajes Conseguir Informaciones Sobre Estadisticas... vuott Aplicaciones/Fragmentos de Código 1 Domingo, 18 Octobre 2015, 08:38 Ver último mensaje
shordi
No hay nuevos mensajes Control De Usuarios Ricardo Prieto Aplicaciones/Fragmentos de Código 14 Martes, 17 May 2016, 01:01 Ver último mensaje
Ricardo Prieto
No hay nuevos mensajes Llevar Un Registro De Las Temperaturas Del Pc Shell Mundo Linux 2 Lunes, 06 Noviembre 2017, 19:43 Ver último mensaje
Shell
No hay nuevos mensajes Ver El Historial De Actualiaciones Mediant... Shell Mundo Linux 1 Sabado, 31 Agosto 2019, 14:31 Ver último mensaje
gambafeliz
 

Publicar nuevo tema  Responder al tema  Página 1 de 1
 

Usuarios navegando en este tema: 0 registrados, 0 ocultos y 1 invitado
Usuarios registrados conectados: Ninguno


 
Lista de permisos
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



  

 

cron