报表逻辑校核

报表统计的逻辑校核

UID为报表绑定的单位id,tid为报表id

简要流程:

1.定逻辑校核和定批注;

2.根据UID和tid查逻辑校核,如果tid为空,则查UID所选择的所有报表的逻辑校核;

3.取出逻辑校核操作解析数据;
4.数据解析完后做数据对比;
5.对比完数据后,未通过的逻辑校核查看关联批注的批注;
6.如果存在关联的批注的格子,则给校核结果插入错误信息数据,做cell格子标红反显。

首先先定义好批注和逻辑校核

1.定逻辑校核和定批注;

这是定义逻辑校核页面,里面有逻辑校核名称、逻辑校核内容、逻辑校核操作、逻辑校核内容说明、关联批注。逻辑校核名称顾名思义;逻辑校核内容指的是报表的某一个格子或区域的格子里的数据默认相加(也可以做相减相乘相除操作)为一个数值,再根据逻辑操作,把各个逻辑内容的值做加减乘除或加减乘除数了以后再彼此做加减乘除如图(1)*2+(2)*3+(3)*4=(4)+(2);校核内容说明就是给逻辑操作和内容做解释性的文字;最后选择关联批注,这是在批注定义好之后读取报表定义过的批注数据显示成列表,进行关联,最后以逗号分割批注的id,保存到数据库里的一个字段中。

2.根据UID和tid查逻辑校核,如果tid为空,则查UID所选择的所有报表的逻辑校核;

逻辑校核定义是绑定tid报表的,在单位UID选择他们要填写的报表后,UID和tid就会有一个关联,逻辑校核就会根据tid找到UID关联上。然后在单位使用逻辑校核的时候就可以根据UID和tid查逻辑校核,如果tid为空,则查UID所选择的所有报表的逻辑校核。这里面涉及到的表有 单位表、单位和报表的关系表、逻辑校核的表(因为逻辑校核名称和逻辑校核内容的关系是一对多的关系,所以分开2张表做数据存储,逻辑校核名称这表主要存储主键,名称,逻辑操作,关联批注,关联tid;逻辑内容这表主要存储自身主键,逻辑名称主键做关联,逻辑操作是用的(1)等num,需要做数据加减操作的表tid,表的区域,表的起始行列,表结束的列等等,由此可以计算出指定表的指定区域的数据)。

3.取出逻辑校核操作解析数据;

先查出所有需要的逻辑校核,循环遍历所有的逻辑校核,把逻辑操作中的对比操作符作为分割符,分割字符串为左右等式的数组,左等式是数组[0],右等式是数组[1],接着左右等式都调用报表数据处理的方法

/**
	 * 报表数据处理  更加逻辑校核查出要校核的报表做报表数据处理
	 * @param map
	 * @param operation
	 * @return
	 */
	private double tableDataHandle(Map<String,Object> map,String operation){
		/**
		 * 将字符串的计算式进行计算的java 实现方法
		 */
        ScriptEngineManager manager = new ScriptEngineManager(ScriptEngineFactory.class.getClassLoader());
		ScriptEngine scriptEngine = manager.getEngineByName("JavaScript");
		String numData="";
		double ZNUM=0;
		
		if(operation.length()>0){
			
			//提取运算符
			String symbol="[^\\+|\\-|\\*|\\/]";  
			Pattern f = Pattern.compile(symbol);  
			Matcher operatorMatcher = f.matcher(operation);  
			String operator=operatorMatcher.replaceAll("").trim();
			  
			
			//提取数字
			String regEx="[\\+|\\-|\\*|\\/]";  
			String[] NUMS=operation.split(regEx);
			
			//()格式的数字查数据库返回数据值并计算
			for(int i=0;i<NUMS.length;i++){
//				NUM=NUMS.toString().substring(i, i+1);
				if(NUMS[i].contains("(")){
					map.put("NUM", NUMS[i]);
					map.put("LID", map.getOrDefault("RECORDID", "").toString());
					List<Map<String,Object>> dataList=this.bdsoftMybatisUtils.selectList("repository.ndtj.bbdzfirst.SEL_LJJHCZSJ", map);
					if(dataList.size()>0){
						//获取逻辑校核NUM号的数据值
						numData+=rptDataHandle(dataList,scriptEngine);
					}else{
						numData+="0";
					}
				}else{
					numData+=NUMS[i];
				}
			}
			if(operator.toString().length()>0){//如果运算字符串长度大于0,则NUM字符串必定比运算字符串长度大
				String ys=numData.substring(0, 1);
				for(int g=0;g<operator.toString().length();g++){
					ys+=operator.toString().substring(g,g+1)+numData.substring(g+1, g+2);
				}
				try {
					//做运算
					ZNUM=Double.valueOf(scriptEngine.eval(ys).toString());
				} catch (ScriptException e) {
					e.printStackTrace();
				}
			}else{
				ZNUM=(double) Integer.parseInt(numData);
			}
		}else{
			map.put("NUM", operation);
			map.put("LID", map.getOrDefault("RECORDID", "").toString());
			List<Map<String,Object>> dataList=this.bdsoftMybatisUtils.selectList("repository.ndtj.bbdzfirst.SEL_LJJHCZSJ", map);
			if(dataList.size()>0){
				//获取逻辑校核NUM号的数据值
				numData+= rptDataHandle(dataList,scriptEngine);
				ZNUM=(double) Integer.parseInt(numData);
			}
		}
		return ZNUM;
		
	}

上面就是报表数据处理的方法,把传过来的左右等式先用正则匹配字符提取出里面的运算符加减乘等符号,在匹配提取出要运算的数据,以(1)*2+(2)*3+(3)*4=(4)+(2)为例。左边等式提取出的运算符是*+*+*,把提取出的运算符存到operator字符串里。

3.取出逻辑校核操作解析数据;

然后提取要做运算的数据,以运算符作为分割符,把左等式分成[(1),2,(2),3,(3),4]在遍历这个数组,根据遍历的每个值判断是否存在"("字符,判断是逻辑内容还是操作加减的数。如果是操作加减的数则直接放进需运算操作的字符串numData内,如果是逻辑操作,则通过字符中的(1)和tid去查逻辑校核中的表的数据,根据查出来的tid,区域id,起始行列号,结束行列号查出一个区域或单个的格子数据,再做相对应的运算操作。处理方法如下

/**
	 * 获取逻辑校核NUM号的数据值
	 * @param dataList
	 * @return
	 */
	private String rptDataHandle(List<Map<String,Object>> dataList,ScriptEngine scriptEngine){
		String TID,YSCZ,RELRID,VAL,ZVAL="0",dataVAL = "";
		int QROW,QCOL,JROW,JCOL=0;
		TID=dataList.get(0).getOrDefault("TID", "").toString();
		RELRID=dataList.get(0).getOrDefault("RELRID", "1").toString();
		QROW=Integer.parseInt(dataList.get(0).getOrDefault("起始行号", "").toString());
		QCOL=Integer.parseInt(dataList.get(0).getOrDefault("起始列号", "").toString());
		JROW=Integer.parseInt(dataList.get(0).getOrDefault("结束行号", "").toString());
		JCOL=Integer.parseInt(dataList.get(0).getOrDefault("结束列号", "").toString());
		YSCZ=dataList.get(0).getOrDefault("运算符", "+").toString();
		//根据tid和起始结束行列号得出一个区域内的格子数据,再由运算符做出运算操作,得出值
		Map<String,Object> rptParams=new HashMap<String,Object>();
		for(int i=QROW;i<=JROW;i++){
			for(int j=QCOL;j<=JCOL;j++){
				rptParams.put("tableSuffix", tableSuffix);
				rptParams.put("UID", uid);
				rptParams.put("TID", TID);
				rptParams.put("RELRID", RELRID);
				rptParams.put("ROW_ID", i);
				rptParams.put("COLID", j);
				List<Map<String,Object>> rptList=this.bdsoftMybatisUtils.selectList("repository.ndtj.bbdzfirst.SEL_DATAVAL",rptParams);
				if(rptList.size()>0){
					VAL=rptList.get(0).getOrDefault("VAL", "").toString();
					if(!StringUtility.isNullOrEmpty(VAL)){
						//拼接运算操作
						dataVAL+=VAL+YSCZ;
					}
				}
			}
		}
		if(dataVAL.length()>1){
			dataVAL=dataVAL.substring(0, dataVAL.length()-1);
			try {
				//执行运算操作
				ZVAL=scriptEngine.eval(dataVAL).toString();
			} catch (ScriptException e) {
				e.printStackTrace();
			}
		}
		return ZVAL;
		
	}

(运算符默认是做相加操作,在每个格子数据查出来以后做+操作,同时也支持减乘除操作)

校核内容数据处理完后得到每个校核内容的数值,按遍历先前的运算数据的数组顺序放到运算操作的字符串numData内。

此时运算操作的字符串numData中的数据应该是"X2Y3Z4",operator是“*+*+*”。

numData是一个字符串其中 X为校核内容(1)处理后数据值,Y为校核内容(2)处理后数据值,Z为校核内容(3)处理后数据值

接着遍历operator这个字符串,把最后要运算的式子拼接成ys字符串。numData和operator的顺序是一致的,但如果operator里面存在一个运算符,那么这个运算符左右两边必须得存在可以运算的数字才能成为一个等式,所以numData字符串长度必定比operator长度大1。所以当operator遍历第一个字符的时候是*,就把numData的X和2放到*的两边拼成字符串,以此类推出左边等式最后的运算字符串ys=X*2+Y*3+Z*4。

用scriptEngine.eval(ys).toString()这个方法运算出结果,再由Double.valueOf把字符串转为double类型的数据。

4.数据解析完后做数据对比;

再同理得出右边等式的数据值,把左边等式和右边等式做数值逻辑比较(逻辑比较符就是之前分割出左右等式的比较符),如果式子成立则不继续做操作,如果等式不成立如(1)*2+(2)*3+(3)*4=(4)+(2)的结果返回false

5.对比完数据后,未通过的逻辑校核查看关联批注的批注;

查出校核名称所关联的批注。

6.如果存在关联的批注的格子,则给校核结果插入错误信息数据,做cell格子标红反显。

再根据批注的数据把校核失败的数据拼成一个字符串,最后插入到校核失败信息表中,为前台页面反显做cell格子标红反显。

最后由上面定制出来的效果是

报表里红色的框表示校核内容1,蓝色表示校核内容2,绿色表示校核内容3,紫色表示校核内容4,红色的格子就是校核内容不通过后格子标红反显。

猜你喜欢

转载自blog.csdn.net/weixin_37618127/article/details/83987020