在項目裏除了通常的登錄日誌外,通常還要對我們的重要的業務數據做個數據的變更記錄。但是我在網上搜索了一下,主要的解決方案是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>>(){});
//在前端展現效果
如下:這樣就明顯了。