在项目里除了通常的登录日志外,通常还要对我们的重要的业务数据做个数据的变更记录。但是我在网上搜索了一下,主要的解决方案是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>>(){});
//在前端展现效果
如下:这样就明显了。