需求:在系统运营过程中,有些关键数据变更需要进行留痕操作,以便后续进行反查。
分析:各类业务需求不相同,留痕的信息也不一样,设计一个能兼容各种业务修改的情况,抽取一些留痕的公共字段信息。两个信息表如下:
PID | 自增主键 |
TRC_ID | 留痕编号 |
TRC_TYPE | 留痕类型:自定义 |
TITLE | 留痕标题:可将关键信息放至标题中 |
TABLE_NAME | 业务表名 |
PKID | 留痕记录对应的业务记录 |
TEXT | 留痕内容:存储XML格式 |
CRE_T | 创建时间 |
CRE_O | 创建人 |
PID | 自增主键 |
TRC_TYPE | 留痕类型 |
NAME | 节点名称 |
TEXT | 节点中文描述 |
SEQ_NO | 序号 |
说明:PUB_TRACE_CFG的主要目的是为了展示信息时候展示列的名称信息,如无配置,直接列名展示为XML的节点名称。
PUB_TRACE_LOG表TEXT内容类似如下
---------------------------------------------
<ROOT>
<AGE>22</AGE>
<BIRTHDAY>20000301</BIRTHDAY>
</ROOT>
PUB_TRACE_CFG表针对修改TRC_TYPE=UPD_STUDENT中的配置如下
---------------------------------------------
PID TRC_TYPE NAME TEXT SEQ_NO
1001 UPD_STUDENT AGE 年龄 1
1002 UPD_STUDENT BIRTHDAY 生日 2
接口类定义
import com.github.pagehelper.PageInfo;
import java.util.List;
import java.util.Map;
public interface IPubTraceService {
/**
* 登记留痕交易
* @param traceType 留痕类型
* @param title 留痕标题
* @param tableName 留痕相关表名
* @param pkid 留痕表信息关联编号
* @param paramMap 留痕信息
* @throws Exception
*/
public void registerTrace(String traceType,String title,String tableName,String pkid, Map<String, Object> paramMap) throws Exception;
/**
* 登记留痕交易及原信息
* @param traceType 留痕类型
* @param title 留痕标题
* @param tableName 留痕相关表名
* @param pkid 留痕表信息关联编号
* @param paramMapBefore 留痕信息之前
* @param paramMapAfter 留痕信息之后
* @throws Exception
*/
public void registerTrace(String traceType,String title,String tableName,String pkid, Map<String, Object> paramMapBefore,Map<String, Object> paramMapAfter) throws Exception;
/**
* 查询留痕交易记录
* @param paramMap 留痕信息条件
* @throws Exception
*/
public List<Map<String, Object>> selectTraceLogList(Map<String, Object> paramMap) throws Exception;
/**
* 查询留痕交易记录
* @param paramMap 留痕信息条件
* @throws Exception
*/
public PageInfo<Map<String, Object>> selectTraceLogList(Integer currentPage, Integer pageSize, Map<String, String> paramMap) throws Exception;
/**
* 查询留痕记录详情
* @param paramMap 留痕信息条件
* TRC_ID:留痕信息编号
* @throws Exception
*/
public List<Map<String, Object>> selectTraceLogDatailList(Map<String, Object> paramMap) throws Exception;
/**
* 查询留痕记录详情-对比展示
* @param paramMap 留痕信息条件
* TRC_ID:留痕信息编号
* @throws Exception
*/
public List<Map<String, Object>> selectTraceLogDatailListCompare(Map<String, Object> paramMap) throws Exception;
}
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.pos.boot.common.util.StringUtil;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class PubTraceServiceImpl implements IPubTraceService{
private static final Logger log = LoggerFactory.getLogger(PubTraceServiceImpl.class);
@Autowired PubTraceMapper pubTraceMapper;
@Override
public void registerTrace(String traceType, String title, String tableName, String pkid, Map<String, Object> traceMap) throws Exception {
String traceId = "TRC"+ PubLogNo.getPubLogNo();
String creT = DateUtil.getDateTime();
String creO = MapUtils.getString(traceMap, "CRE_O","");
String text = null;
if(null==tableName){
tableName = "";
}
if(null==pkid){
pkid = "";
}
//将留痕中的key字段转为大写
traceMap = mapKey2Upper(traceMap);
//将留痕信息转为Xml
text = XmlConvertMapUtil.callMapToXML(traceMap);
Map<String, Object> insertMap = new HashMap<>();
insertMap.put("TRC_ID" , traceId);
insertMap.put("TRC_TYPE" , traceType);
insertMap.put("TITLE" , title);
insertMap.put("TABLE_NAME", tableName);
insertMap.put("PKID" , pkid);
insertMap.put("TEXT" , text);
insertMap.put("CRE_T" , creT);
insertMap.put("CRE_O" , creO);
pubTraceMapper.insertEntity(insertMap);
}
@Override
public void registerTrace(String traceType, String title, String tableName, String pkid, Map<String, Object> traceMapBefore,Map<String, Object> traceMapAfter) throws Exception {
//修改前及修改后的TRC_ID根据后缀区分
String traceIdBefore = "TRC"+ PubLogNo.getPubLogNo()+"_BEFORE";
String traceIdAfter = traceIdBefore.replace("_BEFORE", "_AFTER");
//将修改后的TRC_ID更新到数据中
traceMapBefore.put("TRC_ID", traceIdBefore);
traceMapAfter.put("TRC_ID" , traceIdAfter);
//将信息保存到留痕信息表中
registerTrace(traceType,title,tableName,pkid,traceMapBefore);
registerTrace(traceType,title,tableName,pkid,traceMapAfter);
}
@Override
public List<Map<String, Object>> selectTraceLogList(Map<String, Object> paramMap) throws Exception {
return pubTraceMapper.selectTraceLogList(paramMap);
}
@Override
public PageInfo<Map<String, Object>> selectTraceLogList(Integer currentPage, Integer pageSize, Map<String, String> paramMap) throws Exception {
PageHelper.startPage(currentPage, pageSize);
paramMap.put("TYPE", "LIST");
return new PageInfo<>(pubTraceMapper.selectTraceLogListStr(paramMap));
}
@Override
public List<Map<String, Object>> selectTraceLogDatailList(Map<String, Object> paramMap) throws Exception {
List<Map<String, Object>> resList = new ArrayList<>();
String traceId = MapUtils.getString(paramMap, "TRC_ID");
String traceType;
String title;
String tableName = null;
String pkid = null;
Map<String, Object> selectMap = new HashMap<>();
selectMap.put("TRC_ID", traceId);
List<Map<String, Object>> traceLogList = selectTraceLogList(selectMap);
Map<String, Object> traceMap = null;
if(null==traceLogList || traceLogList.size()==0){
return resList;
}
//获取留痕详情信息
traceMap = traceLogList.get(0);
traceType = MapUtils.getString(traceMap, "TRC_TYPE");
title = MapUtils.getString(traceMap, "TITLE");
tableName = MapUtils.getString(traceMap, "TABLE_NAME");
pkid = MapUtils.getString(traceMap, "PKID");
Map<String, Object> selectTraceCfgMap = new HashMap<>();
selectTraceCfgMap.put("TRC_TYPE", traceType);
List<Map<String, Object>> traceCfgList = pubTraceMapper.selectTraceCfgList(selectTraceCfgMap);
//没有配置信息
if(null==traceCfgList || traceCfgList.size()==0){
traceCfgList = new ArrayList<>();
}
Map<String, Object> traceTempMap = new HashMap<>();
traceTempMap.put("TRC_TYPE", traceType);
traceTempMap.put("NAME" , "TITLE");
traceTempMap.put("TEXT" , "留痕名称");
traceTempMap.put("VALUE" , title);
traceTempMap.put("SEQ_NO" , "0");
resList.add(traceTempMap);
//将配置中有的信息按顺序放置到列表中
String name = null;
String detailXml = MapUtils.getString(traceMap, "TEXT");
Map<String, String> traceDetailMap = XmlConvertMapUtil.xml2Map(detailXml);
for(Map<String, Object> tempMap:traceCfgList){
name = MapUtils.getString(tempMap, "NAME");
//转为大写
name = name.toUpperCase();
tempMap.put("VALUE", MapUtils.getString(traceDetailMap, name));
traceDetailMap.remove(name);
resList.add(tempMap);
}
//将剩余的不在配置中的信息放置到最后,按key的字母顺序排序
Set<String> keySet = new TreeSet<>(traceDetailMap.keySet());
Iterator<String> it = keySet.iterator();
String key;
String val;
while(it.hasNext()){
key = it.next();
val = MapUtils.getString(traceDetailMap, key);
if("root.name".equals(key)){
continue;
}
traceTempMap = new HashMap<>();
traceTempMap.put("TRC_TYPE", traceType);
traceTempMap.put("NAME" , key);
traceTempMap.put("TEXT" , key);
traceTempMap.put("VALUE" , val);
traceTempMap.put("SEQ_NO" , "-1");
resList.add(traceTempMap);
}
log.info(""+resList);
return resList;
}
@Override
public List<Map<String, Object>> selectTraceLogDatailListCompare(Map<String, Object> paramMap) throws Exception {
List<Map<String, Object>> resList = new ArrayList<>();
String traceId = XmlConvertMapUtil.xml2Map(MapUtils.getString(paramMap, "TEXT")).get("TRC_ID");
if (StringUtils.isBlank(traceId)) {
traceId = MapUtils.getString(paramMap, "TRC_ID");
}
String name,text,oValue,nValue,seqNo;
List<Map<String, Object>> traceCfgList = getTraceCfgList(traceId);
Map<String, String> traceMapBefore;
Map<String, String> traceMapAfter = new HashMap<>();
traceMapBefore = getTraceLogMap(traceId);
//看是否为对比数据存储
if(null!=traceId && traceId.toUpperCase().endsWith("_BEFORE")){
traceMapAfter = getTraceLogMap(traceId.replace("_BEFORE", "_AFTER"));
}
Map<String, String> selectMap = new HashMap<>();
if (traceId.contains("_")) {
selectMap.put("TEXT_ID", traceId);
}else {
selectMap.put("TRC_ID", traceId);
}
List<Map<String, Object>> traceLogList = pubTraceMapper.selectTraceLogListStr(selectMap);
Map<String, Object> traceLogMap = traceLogList.get(0);
for(Map<String, Object> tempMap:traceCfgList){
name = MapUtils.getString(tempMap , "NAME");
name = name.toUpperCase();//转为大写
text = MapUtils.getString(tempMap , "TEXT");
seqNo = MapUtils.getString(tempMap , "SEQ_NO");
oValue = MapUtils.getString(traceMapBefore, name,"");
nValue = MapUtils.getString(traceMapAfter , name,"");
Map<String, Object> traceTempMap = new HashMap<>();
//公共信息
traceTempMap.put("TRC_TYPE" , MapUtils.getString(traceLogMap, "TRC_TYPE" ));
traceTempMap.put("TITLE" , MapUtils.getString(traceLogMap, "TITLE" ));
traceTempMap.put("TABLE_NAME", MapUtils.getString(traceLogMap, "TABLE_NAME"));
traceTempMap.put("PKID" , MapUtils.getString(traceLogMap, "PKID" ));
traceTempMap.put("CRE_T" , MapUtils.getString(traceLogMap, "CRE_T" ));
traceTempMap.put("CRE_O" , MapUtils.getString(traceLogMap, "CRE_O" ));
//修改前的信息
traceTempMap.put("O_NAME" , name);
traceTempMap.put("O_TEXT" , text);
traceTempMap.put("O_VALUE" , oValue);
traceTempMap.put("O_SEQ_NO" , seqNo);
//修改后的信息
traceTempMap.put("N_NAME" , name);
traceTempMap.put("N_TEXT" , text);
traceTempMap.put("N_VALUE" , nValue);
traceTempMap.put("N_SEQ_NO" , seqNo);
//增加标识,此信息是否变更
traceTempMap.put("IS_MODIFY" , !oValue.equals(nValue));
resList.add(traceTempMap);
}
return resList;
}
/**
* 根据留痕流水查询该类型的字段配置信息
* @param traceId
* @return
* @throws Exception
*/
private List<Map<String, Object>> getTraceCfgList(String traceId) throws Exception{
Map<String, String> traceMapBefore;
Map<String, String> traceMapAfter = new HashMap<>();
String traceType;
traceMapBefore = getTraceLogMap(traceId);
if(null!=traceId && traceId.toUpperCase().endsWith("_BEFORE")){
traceMapAfter = getTraceLogMap(traceId.replace("_BEFORE", "_AFTER"));
}
//获取留痕类型
traceType = MapUtils.getString(traceMapBefore, "TRC_TYPE");
//根据留痕类型获取配置信息
Map<String, Object> selectTraceCfgMap = new HashMap<>();
selectTraceCfgMap.put("TRC_TYPE", traceType);
List<Map<String, Object>> traceCfgList = pubTraceMapper.selectTraceCfgList(selectTraceCfgMap);
//将所有的配置Key信息放到一个List中
List<String> keyList = new ArrayList<>();
for(Map<String, Object> tempMap:traceCfgList){
keyList.add(MapUtils.getString(tempMap, "NAME"));
}
//将Before不在配置中的信息放置到配置信息中
Set<String> keySet = traceMapBefore.keySet();
Iterator<String> it = keySet.iterator();
String key;
while(it.hasNext()){
key = it.next();
if(!keyList.contains(key)){
keyList.add(key);
Map<String, Object> addCfgMap = new HashMap<>();
addCfgMap.put("TRC_TYPE", traceType);
addCfgMap.put("NAME" , key);
addCfgMap.put("TEXT" , key);
addCfgMap.put("SEQ_NO" , "-1");
traceCfgList.add(addCfgMap);
}
}
//将After不在配置中的信息放置到配置信息中
keySet = traceMapAfter.keySet();
it = keySet.iterator();
while(it.hasNext()){
key = it.next();
if(!keyList.contains(key)){
keyList.add(key);
Map<String, Object> addCfgMap = new HashMap<>();
addCfgMap.put("TRC_TYPE", traceType);
addCfgMap.put("NAME" , key);
addCfgMap.put("TEXT" , key);
addCfgMap.put("SEQ_NO" , "-1");
traceCfgList.add(addCfgMap);
}
}
return traceCfgList;
}
private Map<String, String> getTraceLogMap(String traceId) throws Exception{
Map<String, String> resMap = new HashMap<>();
Map<String, String> selectMap = new HashMap<>();
if (traceId.contains("_")) { selectMap.put("TEXT_ID", traceId);
}else { selectMap.put("TRC_ID", traceId); }
List<Map<String, Object>> traceLogList = pubTraceMapper.selectTraceLogListStr(selectMap);
if(null==traceLogList || traceLogList.size()==0){
return resMap;
}
//获取留痕详情信息
Map<String, Object> traceMap = traceLogList.get(0);
//将配置中有的信息按顺序放置到列表中
String detailXml = MapUtils.getString(traceMap, "TEXT");
//将XML转为Map返回
resMap = XmlConvertMapUtil.xml2Map(detailXml);
//删除无用的节点
resMap.remove("root.name");
//添加TRC_TYPE
resMap.put("TRC_TYPE", MapUtils.getString(traceMap, "TRC_TYPE"));
return resMap;
}
private Map<String, Object> mapKey2Upper(Map<String, Object> paramMap){
Map<String, Object> resMap = new HashMap<>();
Set<String> keySet = paramMap.keySet();
Iterator<String> it = keySet.iterator();
String key;
Object val;
while(it.hasNext()){
key = it.next();
val = paramMap.get(key);
resMap.put(key.toUpperCase(), val);
}
return resMap;
}
}
需单独写个前端页面用来展示留痕记录列表信息,并在详情展示时候根据返回的字段进行动态展示留痕内容信息,以此来兼容不同业务类型的留痕信息。
书写粗略,亦有考虑不全的地方,互相讨论学习。