專案中的資料操作日誌設計
阿新 • • 發佈:2019-02-17
在專案裡除了通常的登入日誌外,通常還要對我們的重要的業務資料做個數據的變更記錄。但是我在網上搜索了一下,主要的解決方案是spring AOP + 註解 的方式進行記錄。這種操作起來簡便,但是粗糙了許多。下面我將介紹下另一種做法,程式碼多了,但是也精確了。
首先,建立一個listener
public interface DataChangeListener{
saveUser(String operUserId,User data,Date now);
updateUser(String operUserId,User old,User Data );
}
然後再建立一個proxy
public class DataChangeListenerProxy implements DataChangeListener {
private List<DataChangeListener> listenters ;
public List<DataChangeListener> getListenteres() {
return listenters;
}
public void setListenteres(List<DataChangeListener> listenteres) {
this .listenters = listenteres;
}
@Override
public void saveUser(String operUserId, User data,Date now) throws Exception {
for(ContractChangeListener listenter: listenters){
listenter.saveUser(operUserId, data,now);
}
}
@Override
public void updateUser(String operUserId,User old, User data,Date now) throws Exception {
for(ContractChangeListener listenter: listenters){
listenter.updateSaleContract(operUserId,old, data,now);
}
}
}
這裡做下解釋:建立這個proxy的目的是為了方便日後的功能擴充套件,比如日後在儲存人員後還要進行某些關聯的操作時,可以通過繼承DataChangeListener然後注入到這個proxy的listeners裡面,那麼在呼叫的時候就會通過方法裡面的for迴圈自然的呼叫後面擴充套件的實現類方法。
最後建立一個預設的實現類,主要用於對人員的資料進行記錄。
public class DefualtDataChangeListener implements ContractChangeListener {
/**
* 儲存日誌
* @param operUserId 操作人
* @param operType 選單操作型別
* @param operAct 操作方法
* @param dataId 操作資料編號
* @param operData 操作資料內容
* @param date 資料修改時間
*/
private void saveLog(String operUserId,String operType,String operAct,String dataId,String operData,Date date){
ConOperLog log = new ConOperLog();
log.setOperId(PubHelper.getNewId(ConOperLog.class));
log.setUserId(operUserId);
log.setOperType(operType);
log.setOperAct(operAct);
log.setOperDate(date);
log.setOperData(operData);
log.setDataId(dataId);
FrameworkHelper.getDAO().save(log);
}
@Override
public void saveUser(String operUserId, User data,Date now) throws Exception {
//getProcessDataBySave下面解釋
saveLog(operUserId,"人員基本資訊", SAVE, data.getUserId(), JSON.toJSONString(getProcessDataBySave(data)),now);
}
@Override
public void updateUser(String operUserId, User old,User data,Date now)throws Exception {
saveLog(operUserId,"更新人員基本資訊", UPDATE, data.getUserId(), JSON.toJSONString(getProcessDataByUpdate(old, data)),now);
}
/**
* 對新舊資料進行對比,返回結果value會帶有html標籤
* @param oldData
* @param newData
* @return
* @throws Exception
*/
private Map<String, String> getProcessDataByUpdate(Object oldData,Object newData) throws Exception{
Map<String,String> oldMap = getPropertyValue(oldData);
Map<String,String> newMap = getPropertyValue(newData);
for(Entry<String, String> entry:oldMap.entrySet()){
String oldValue = entry.getValue();
String newValue = newMap.get(entry.getKey());
if(Utils.isEmpty(oldValue)&&Utils.isEmpty(newValue)){
continue;
}
//[下載DiffMatchPatch](https://download.csdn.net/download/b45bobo/10584374)
DiffMatchPatch match = new DiffMatchPatch();
entry.setValue(match.diff_prettyHtml(match.diff_main(oldValue, newValue)));
}
return oldMap;
}
/**
* 新增資料時
* @param data
* @return key:UserName value:<ins style='background:#E6FFE6;'>陳陳</ins>
* @throws Exception
*/
private Map<String,String> getProcessDataBySave(Object data) throws Exception{
Map<String,String> map = getPropertyValue(data);
for(Entry<String, String> entry: map.entrySet()){
StringBuilder html = new StringBuilder();
//INS_SUFIX="<ins style='background:#E6FFE6;'>"
//INS_SUFIX="</ins>"
html.append(ContractConstants.INS_PERFIX).append(entry.getValue())
.append(ContractConstants.INS_SUFIX);
entry.setValue(html.toString());
}
return map;
}
/**
* 刪除資料時
* @param data
* @return key:UserName value: <del style='background:red;'>陳陳</del>
* @throws Exception
*/
private Map<String,String> getProcessDataByDelete(Object data) throws Exception{
Map<String,String> map = getPropertyValue(data);
for(Entry<String, String> entry: map.entrySet()){
StringBuilder html = new StringBuilder();
//DEL_PERFIX = "<del style='background:red;'>"
//DEL_SUFIX = "</del>"
html.append(ContractConstants.DEL_PERFIX).append(entry.getValue())
.append(ContractConstants.DEL_SUFIX);
entry.setValue(html.toString());
}
return map;
}
/**
* 該方法是提取實體物件裡面的屬性值,並以屬性名為key,屬性值為value存入map
* @param data 實體物件
* @return 例如 key:UserName value: 陳
* Address 中國
*/
private Map<String,String> getPropertyValue(Object data) throws Exception{
Map<String,String> map = new HashMap<>();
for(Method m: data.getClass().getMethods()){
String methodName = m.getName();
if(methodName.startsWith("get")){
Object o = m.invoke(data, new Object[]{});
if(o instanceof Double){
o = Double.toString((Double)o);
}else if(o instanceof Date){
o = DateUtil.format((Date)o,DateUtil.PATTERN_FULL);
}else if(o instanceof Class){
continue;
}
map.put(m.getName().replace("get", ""), (String)o);
}
}
return map;
}
}
在專案裡面呼叫
//這裡注入proxy的bean物件
@Resource(name = "contractListener")
protected ContractChangeListener listenter;
public void saveUser(String loginUserId,User user){
Date now = new Date();
//儲存人員到資料庫
//。。。。
//----
//呼叫listener
listenter.saveUser(loginUserId,user,now);
}
這樣資料就存入資料庫裡面了,在要展現修改差異時查詢資料獲取文字對比後的json
//從資料裡面根據分類查詢人員的基本資訊更新操作記錄
ConOperLog conOperLog = 從資料庫查詢到的物件;
//json字串的人員資料
String data = conOperlog.getOperData();
//key UserName value: <del style='background:red;'>陳</del><ins style='background:#E6FFE6;'>徐</ins>笑
Map<String,String> map = JSON.parseObject(data, new TypeReference<Map<String,String>>(){});
//在前端展現效果
如下:這樣就明顯了。