M对象存储写日志

基础数据修改的日志很重要,常常有重要的基础数据被改了导致问题,然后还无据可查。所以需要所有基础数据修改有日志记录。

检验的C#ORM层已经实现了对基础表的增、删、改自动记录日志。增加就记录增加的JSON日志,修改就把修改前面表JSON记录,删除就把删除的JSON记住。

如下图:可以做表日志查询
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

由于我们是M库,有些写的基础维护页面有M对象保存、修改和删除的。M层就需要一个存日志的方法,目标就是让表修改可查询。新增需要记录新增的数据是什么。修改需要记录从什么数据修改成什么。删除需要知道删除了什么数据。

最常规的方式就是提供一个日志方法,让业务实现自己传要记录的日志。这样的毛病是记录的日志串格式不统一,难以做通用查询。然后是业务实现M还得在逻辑里时刻考虑嵌入记录日志逻辑,日志代码侵入性太强了。

如下图方式(因为为了日志影响了正常逻辑的结构,分步容易漏):
在这里插入图片描述

为此需要实现两个目标:
1.业务不需要关注日志组串细节。
2.业务不需要因为修改这种操作在改之前先准备好修改前日志串,改之后在准备改之后日志串,删除也同理。这种行为破坏了业务的思路。

设计的日志数据格式:

{
    "Sql": "",
    "OldData": "[{\"RowID\":113,\"Code\":\"dhcc\",\"CName\":\"dhcc\",\"Password\":\"\",\"HospitalDR\":\"\",\"LanguageDR\":13,\"SecurityType\":\"0\",\"PIN\":\"\",\"Remark\":\"我是超级用户66253,42806\",\"Sequence\":0,\"Active\":1,\"PositionTitleDR\":\"\",\"UserType\":\"LIS\",\"UKeyNo\":\"\",\"SignImage\":\"\",\"Theme\":\"\",\"MenuModel\":\"2\",\"PermissionDR\":8}]",
    "NewData": "[{\"RowID\":\"113\",\"Code\":\"dhcc\",\"CName\":\"dhcc\",\"Password\":\"\",\"HospitalDR\":\"\",\"LanguageDR\":13,\"SecurityType\":\"0\",\"PIN\":\"\",\"Remark\":\"我是超级用户66253,42864\",\"Sequence\":0,\"Active\":1,\"PositionTitleDR\":\"\",\"UserType\":\"LIS\",\"UKeyNo\":\"\",\"SignImage\":\"\",\"Theme\":\"\",\"MenuModel\":\"2\",\"PermissionDR\":8}]"
}

首先M修改表的%New和%OpenId的对象可以通过$CLASSNAME()得到表名。那么就可以改造表对象调%Save()的保存方式。把表对象调用用过LogSave方法,方法内部解析表名,对于插入表的操作,就把对象值组装好JSON。对于更新表的操作就在%Save()之前查询表里的这条数据组装好老数据JOSN,然后把当前对象组装好新数据JSON。对于删除的数据提供LogDelete方法删除,该方法组在删除前装好删除老数据JOSN。

实现:

/// 带日志的删除
/// TableClassName:表的类名
/// RowID:要删除的RowID
/// Session:会话串,可以不给
/// ClientIPAddress:IP,不给会自动获取
/// w ##Class(LIS.WS.DHCLISServiceBase).LogDelete()
ClassMethod LogDelete(TableClassName, RowID, Session, ClientIPAddress)
{
	s TableClassName=$g(TableClassName)
	s Session=$g(Session)
	s ClientIPAddress=$g(ClientIPAddress)
	//得到IP
	i '$l(ClientIPAddress) s ClientIPAddress=##Class(LIS.Util.Common).GetClientIP()
	s LogMain =##class(%DynamicObject).%New()
	s LogMain.Sql=""
	s Action="D"
	s OldJson=..GetTableRowLogJson(TableClassName,RowID,"")
	s LogMain.OldData=OldJson.%ToJSON()
	s LogMain.NewData=""
	//删除数据
	s @("ret=##Class("_TableClassName_").%DeleteId("_RowID_")")
	s retDo=@"ret"
	i $SYSTEM.Status.IsOK(retDo) d
	.//得到表主键
	.s TableCodeI=##Class(LIS.Util.Common).IndexData($p(TableClassName,".",2))
	.s TableDR=$o(^dbo.SYSTableI("IndexCode",TableCodeI,""))
	.//没有表就插入
	.i '$l(TableDR) d
	..s objTabel=##Class(dbo.SYSTable).%New()
	..s objTabel.CName=$p(TableClassName,".",2)
	..s objTabel.Code=$p(TableClassName,".",2)
	..s objTabel.Description=$p(TableClassName,".",2)
	..s objTabel.ParentName=""
	..s objTabel.Sequence=999
	..s retTable=objTabel.%Save()
	..s TableDR=objTabel.RowID
	.//保存日志
	.s objLog=##Class(dbo.SYSTableLog).%New()
	.s objLog.ActData=LogMain.%ToJSON()
	.s objLog.Action=Action
	.s objLog.AddDate=$zd($h,8)
	.s objLog.AddTime=$p($h,",",2)
	.s objLog.AddUserDR=$p(Session,"^",1)
	.s objLog.ClientIPAddress=ClientIPAddress
	.s objLog.RecordID=RowID
	.s objLog.Remark="M"
	.s objLog.TableDR=TableDR
	.s retLog=objLog.%Save()
	q retDo
}

/// 带日志的保存
/// TableObj:通过%OpenId或%New的表对象
/// Session:会话串,可以不给
/// ClientIPAddress:IP,不给会自动获取
/// w ##Class(LIS.WS.DHCLISServiceBase).LogSave()
ClassMethod LogSave(TableObj, Session, ClientIPAddress)
{
	s TableObj=$g(TableObj)
	s Session=$g(Session)
	s ClientIPAddress=$g(ClientIPAddress)
	//得到IP
	i '$l(ClientIPAddress) s ClientIPAddress=##Class(LIS.Util.Common).GetClientIP()
	s LogMain =##class(%DynamicObject).%New()
	s LogMain.Sql=""
	s Action=""
	s ObjName=""
	s RowID=""
	//更新表数据
	i $l(TableObj.RowID) d
	.s Action="U"
	.s ObjName=$CLASSNAME(TableObj)
	.s OldJson=..GetTableRowLogJson(ObjName,TableObj.RowID,"")
	.s NewJson=..GetTableRowLogJson(ObjName,"",TableObj)
	.s LogMain.OldData=OldJson.%ToJSON()
	.s LogMain.NewData=NewJson.%ToJSON()
	//插入表数据
	e  d
	.s Action="I"
	.s ObjName=$CLASSNAME(TableObj)
	.s NewJson=..GetTableRowLogJson(ObjName,"",TableObj)
	.s LogMain.OldData=""
	.s LogMain.NewData=NewJson.%ToJSON()
	//执行数据保存
	s ret=TableObj.%Save()
	s RowID=TableObj.RowID
	
	i $SYSTEM.Status.IsOK(ret) d
	.//得到表主键
	.s TableCodeI=##Class(LIS.Util.Common).IndexData($p(ObjName,".",2))
	.s TableDR=$o(^dbo.SYSTableI("IndexCode",TableCodeI,""))
	.//没有表就插入
	.i '$l(TableDR) d
	..s objTabel=##Class(dbo.SYSTable).%New()
	..s objTabel.CName=$p(ObjName,".",2)
	..s objTabel.Code=$p(ObjName,".",2)
	..s objTabel.Description=$p(ObjName,".",2)
	..s objTabel.ParentName=""
	..s objTabel.Sequence=999
	..s retTable=objTabel.%Save()
	..s TableDR=objTabel.RowID
	.//保存日志
	.s objLog=##Class(dbo.SYSTableLog).%New()
	.s objLog.ActData=LogMain.%ToJSON()
	.s objLog.Action=Action
	.s objLog.AddDate=$zd($h,8)
	.s objLog.AddTime=$p($h,",",2)
	.s objLog.AddUserDR=$p(Session,"^",1)
	.s objLog.ClientIPAddress=ClientIPAddress
	.s objLog.RecordID=RowID
	.s objLog.Remark="M"
	.s objLog.TableDR=TableDR
	.s retLog=objLog.%Save()
	q ret
}

/// 得到一行数据的JSON对象
/// w ##Class(LIS.WS.DHCLISServiceBase).GetTableRowJson("dbo.SYSUser","113").%ToJSON()
/// TableName:表类名
/// RowID:数据主键,和TableObj二传1
/// TableObj:数据对象,和RowID二传1
ClassMethod GetTableRowLogJson(TableName, RowID, TableObj)
{
	s TableName=$g(TableName)
	s RowID=$g(RowID)
	s TableObj=$g(TableObj)
	s retArr=[]
	s @"RetJsonObj =##class(%DynamicObject).%New()"
	i $l(RowID) d
	.//更新的这里不能用OpenId,用他取的数据是改了之后的对象
	.//s @("Obj=##class("_TableName_").%OpenId("_RowID_")")
	.s @("Obj=$g(^"_TableName_"D("_RowID_"))")
	e  d
	.s @("Obj")=TableObj
	s rset = ##class(%ResultSet).%New()
	d rset.Prepare("select COLUMN_NAME FROM information_schema.COLUMNS WHERE TABLE_SCHEMA='dbo' AND REPLACE(TABLE_NAME,'_','')='"_$REPLACE(TableName,"dbo.","")_"' Order by ORDINAL_POSITION")
	s exeret=rset.Execute()
	s colCount=rset.GetColumnCount()
	s dealNum=0
	s PropertyMap=""
	s Index=0
    While(rset.Next())
    {
	    s colField=rset.GetColumnName(1)
        s ColValue=rset.GetDataByName(colField)
        s Index=Index+1
        i $l(RowID) d
        .i Index=1 d
        ..s @("RetJsonObj."_ColValue_"="_RowID)
        .e  d
        ..s @("RetJsonObj."_ColValue_"=$lg(Obj,"_Index_")")
        e  d
        .s @("RetJsonObj."_ColValue_"=Obj."_ColValue)
    }
    d retArr.%Push(@"RetJsonObj")
    q retArr
}

使用示例:

/// 此类写带日志增删改的写法
Class Standard.StdLogSave Extends %RegisteredObject
{

/// 测试带日志保存方法
/// w ##Class(Standard.StdLogSave).LogSaveTestMTHD("","","","","","","","","","","","","","","")
ClassMethod LogSaveTestMTHD(P0, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, Sessions, Output RowCount As %String) As %String
{
	//托管事务保存鉴定结果
	q ##Class(LIS.WS.DHCLISServiceBase).DeclarativeTrans("Standard.StdLogSave","LogSaveTestDo",P0,P0,P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, Sessions)
}

/// 测试带日志保存方法
/// w ##Class(Standard.StdLogSave).LogSaveTest()
ClassMethod LogSaveTestDo(P0, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, Sessions, Output RowCount As %String) As %String
{
	//更新dhcc
	s objLog=##Class(dbo.SYSUser).%OpenId(113,0)
	s objLog.Remark="我是超级用户"_$h
	//调用带日志的保存
	s sc=##Class(LIS.WS.DHCLISServiceBase).LogSave(objLog)
	i ('$SYSTEM.Status.IsOK(sc)) d
	.THROW ##class(%Exception.SystemException).%New("事务委托","D",,"-1^保存失败:"_$SYSTEM.Status.GetErrorText(sc))
	
	h 1
	//插入新用户
	s objNew=##Class(dbo.SYSUser).%New()
	s objNew.Active=1
	s objNew.CName="ZLZ"
	s objNew.Code="ZLZTT"
	s objNew.HospitalDR=1
	s objNew.Sequence=1
	//调用带日志的保存
	s sc=##Class(LIS.WS.DHCLISServiceBase).LogSave(objNew)
	i ('$SYSTEM.Status.IsOK(sc)) d
	.THROW ##class(%Exception.SystemException).%New("事务委托","D",,"-1^保存失败:"_$SYSTEM.Status.GetErrorText(sc))
    
    h 1
	//删除插入的用户
	s sc=##Class(LIS.WS.DHCLISServiceBase).LogDelete("dbo.SYSUser",objNew.RowID)
	i ('$SYSTEM.Status.IsOK(sc)) d
	.THROW ##class(%Exception.SystemException).%New("事务委托","D",,"-1^保存失败:"_$SYSTEM.Status.GetErrorText(sc))
	q ""
}

}

遵循代理者模式和低耦合原则,分析给M要做表日志的伙伴。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小乌鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值