开发-通用设计-留痕

需求:在系统运营过程中,有些关键数据变更需要进行留痕操作,以便后续进行反查。

分析:各类业务需求不相同,留痕的信息也不一样,设计一个能兼容各种业务修改的情况,抽取一些留痕的公共字段信息。两个信息表如下:

PUB_TRACE_LOG(留痕信息表)
PID自增主键
TRC_ID留痕编号
TRC_TYPE留痕类型:自定义
TITLE留痕标题:可将关键信息放至标题中
TABLE_NAME业务表名
PKID留痕记录对应的业务记录
TEXT留痕内容:存储XML格式
CRE_T创建时间
CRE_O创建人
PUB_TRACE_CFG(留痕配置信息表)
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;
	}
}

需单独写个前端页面用来展示留痕记录列表信息,并在详情展示时候根据返回的字段进行动态展示留痕内容信息,以此来兼容不同业务类型的留痕信息。

书写粗略,亦有考虑不全的地方,互相讨论学习。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值