報表統計的邏輯校覈
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,紅色的格子就是校覈內容不通過後格子標紅反顯。