Browse Source

1) 支持站点类的复合公式对比
2) 更好gbase底座

andyliu 6 days ago
parent
commit
922b24b851

BIN
libs/gbase.1.0.5.jar


+ 217 - 2
src/main/java/com/shkpr/service/warncore/bizhandler/CommToolHandler.java

@@ -1,5 +1,6 @@
 package com.shkpr.service.warncore.bizhandler;
 
+import com.global.base.tools.CastUtil;
 import com.shkpr.service.warncore.commtools.CommTool;
 import com.shkpr.service.warncore.commtools.TimeTool;
 import com.shkpr.service.warncore.dbdao.DBMgrProxy;
@@ -7,12 +8,13 @@ import com.shkpr.service.warncore.dbdao.services.intef.OrdWarnEventInfoDBService
 import com.shkpr.service.warncore.dbdao.services.intef.OrdWarnPlanInfoDBService;
 import com.shkpr.service.warncore.dbdao.tables.OrdWarnEventInfoTable;
 import com.shkpr.service.warncore.dto.OrdWarnRuleCondition;
-import com.shkpr.service.warncore.dto.ResponseCode;
+import com.shkpr.service.warncore.jsonbean.JPMixComplexCondition;
 import org.springframework.util.StringUtils;
 
 import java.text.NumberFormat;
-import java.util.List;
 import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 public class CommToolHandler {
     public static NumberFormat NF_DOUBLE;
@@ -111,6 +113,57 @@ public class CommToolHandler {
         return matched;
     }
 
+    public static int matchedToRuleItemEx(String originValue, String matchedValue, String percent, OrdWarnRuleCondition condition){
+        if (StringUtils.isEmpty(originValue) || StringUtils.isEmpty(matchedValue))//指定字段为空串,可表示未匹配
+            return 0;
+        int matched = -1;
+        try {
+            int nRes = 0;
+            if ("int".equals(condition.getType())
+                    || "integer".equals(condition.getType())
+                    || "long".equals(condition.getType())){
+                if (StringUtils.isEmpty(percent))
+                    nRes = CommTool.compareNumber(originValue, matchedValue);
+                else{
+                    double ppPercent = CastUtil.castDouble(percent, 100.0)/100.0;
+                    nRes = CommTool.compareNumber(originValue, String.valueOf(CastUtil.castInt(matchedValue)*ppPercent));
+                }
+            }else{
+                if (StringUtils.isEmpty(percent))
+                    nRes = CommTool.compareFloat(originValue, matchedValue);
+                else {
+                    double ppPercent = CastUtil.castDouble(percent, 100.0)/100.0;
+                    nRes = CommTool.compareFloat(originValue, String.valueOf(CastUtil.castDouble(matchedValue)*ppPercent));
+                }
+            }
+            if (nRes == 0){
+                if ("!=".equals(condition.getMethod())){
+                    matched = 0;
+                }else if (condition.getMethod().contains("=")){
+                    matched = 1;
+                }else
+                    matched = 0;
+            }else if (nRes > 0){
+                if ("!=".equals(condition.getMethod())){
+                    matched = 1;
+                }else if (condition.getMethod().contains(">")){
+                    matched = 1;
+                }else
+                    matched = 0;
+            }else {
+                if ("!=".equals(condition.getMethod())){
+                    matched = 1;
+                }else if (condition.getMethod().contains("<")){
+                    matched = 1;
+                }else
+                    matched = 0;
+            }
+        }catch (Exception e){
+            matched = -1;
+        }
+        return matched;
+    }
+
     /**
      *
      * @param eventId
@@ -162,4 +215,166 @@ public class CommToolHandler {
             remark.append("【").append(itemName).append("】报警");
         return remark.toString();
     }
+
+    public static JPMixComplexCondition parseConditions2TimeRange(String condition, long sampleTime, long limitTime){
+        String mode = "", datePeriod = "", dateUnit = "", method = "", percent = "";
+        String pattern = "^(this|near|last|prev)\\((\\d*)?(h|d|w|n|y)\\)@(avg|min|sum|max|dif|val)\\((\\d*\\.?\\d+)?\\)$";
+        Pattern r = Pattern.compile(pattern);
+        Matcher m = r.matcher(condition);
+        if (m.find()){
+            mode = m.group(1);
+            datePeriod = m.group(2)==null?"":m.group(2);
+            dateUnit = m.group(3);
+            method = m.group(4);
+            percent = m.group(5)==null?"":m.group(5);
+        }else
+            return null;
+
+        if (StringUtils.isEmpty(mode) || StringUtils.isEmpty(dateUnit) || StringUtils.isEmpty(method))
+            return null;
+
+        JPMixComplexCondition jp = new JPMixComplexCondition();
+        jp.setDateUnit(dateUnit);
+        jp.setPercent(percent);
+        jp.setMethod(method);
+        jp.setMode(mode);
+
+        long beginTime = 0L, endTime = 0L;
+        if ("last".equals(mode)){
+            endTime = sampleTime;
+            int dateLength = CastUtil.castInt(datePeriod, 1);
+            switch (dateUnit){
+                case "h":
+                case "H":
+                    beginTime = sampleTime - TimeTool.MS_ONE_HOUR*dateLength;
+                    break;
+                case "d":
+                case "D":
+                    beginTime = sampleTime - TimeTool.MS_ONE_DAY*dateLength;
+                    break;
+                case "n":
+                case "N":
+                    beginTime = TimeTool.getTimeChgMonth(sampleTime, -1*dateLength);
+                    break;
+                case "y":
+                case "Y":
+                    beginTime = TimeTool.getTimeChgMonth(sampleTime, -12*dateLength);
+                    break;
+                default:
+                    return null;
+            }
+            endTime = limitTime<=0L?endTime:Math.min(endTime, limitTime);
+        }else if ("near".equals(mode)){
+            switch (dateUnit){
+                case "h":
+                case "H":{
+                    if ("val".equals(method) || "dif".equals(method)){
+                        beginTime = endTime = sampleTime-TimeTool.MS_ONE_HOUR;
+                    }else {
+                        beginTime = TimeTool.getLastRoundHourUTCTm(sampleTime);
+                        endTime = beginTime + TimeTool.MS_ONE_HOUR - 1L;
+                    }
+                }
+                break;
+                case "d":
+                case "D":{
+                    if ("val".equals(method) || "dif".equals(method)){
+                        beginTime = endTime = sampleTime-TimeTool.MS_ONE_DAY;
+                    }else {
+                        beginTime = TimeTool.getBeginUTCOfTime(sampleTime) - TimeTool.MS_ONE_DAY;
+                        endTime = TimeTool.getBeginUTCOfTime(sampleTime) - 1L;
+                    }
+                }
+                break;
+                case "n":
+                case "N":{
+                    if ("val".equals(method) || "dif".equals(method)){
+                        beginTime = endTime = TimeTool.getTimeChgMonth(sampleTime, -1);
+                    }else {
+                        long lastMonthTm = TimeTool.getTimeChgMonth(sampleTime, -1);
+                        beginTime = TimeTool.getMonthBeginUTC(lastMonthTm);
+                        endTime = TimeTool.getMonthEndUTC(lastMonthTm);
+                    }
+                }
+                break;
+                case "y":
+                case "Y":{
+                    if ("val".equals(method) || "dif".equals(method)){
+                        beginTime = endTime = TimeTool.getTimeChgMonth(sampleTime, -12);
+                    }else {
+                        long lastYearTm = TimeTool.getTimeChgMonth(sampleTime, -12);
+                        beginTime = TimeTool.getYearBeginUTC(lastYearTm);
+                        endTime = TimeTool.getYearEndUTC(lastYearTm);
+                    }
+                }
+                break;
+                default:
+                    return null;
+            }
+            endTime = limitTime<=0L?endTime:Math.min(endTime, limitTime);
+        }else if ("this".equals(mode)){
+            switch (dateUnit){
+                case "h":
+                case "H":{
+                    beginTime = TimeTool.getHourBeginUTCTm(sampleTime);
+                    endTime = beginTime + TimeTool.MS_ONE_HOUR - 1L;
+                }
+                break;
+                case "d":
+                case "D":{
+                    beginTime = TimeTool.getBeginUTCOfTime(sampleTime);
+                    endTime = TimeTool.getEndUTCOfTime(sampleTime);
+                }
+                break;
+                case "n":
+                case "N":{
+                    beginTime = TimeTool.getMonthBeginUTC(sampleTime);
+                    endTime = TimeTool.getMonthEndUTC(sampleTime);
+                }
+                break;
+                case "y":
+                case "Y":{
+                    beginTime = TimeTool.getYearBeginUTC(sampleTime);
+                    endTime = TimeTool.getYearEndUTC(sampleTime);
+                }
+                break;
+                default:
+                    return null;
+            }
+            endTime = limitTime<=0L?endTime:Math.min(endTime, limitTime);
+        }else if ("prev".equals(mode)){
+            int dateLength = CastUtil.castInt(datePeriod, 1);
+            switch (dateUnit){
+                case "h":
+                case "H":{
+                    beginTime = endTime = sampleTime-TimeTool.MS_ONE_HOUR*dateLength;
+                }
+                break;
+                case "d":
+                case "D":{
+                    beginTime = endTime = sampleTime-TimeTool.MS_ONE_DAY*dateLength;
+                }
+                break;
+                case "n":
+                case "N":{
+                    beginTime = endTime = TimeTool.getTimeChgMonth(sampleTime, -1&dateLength);
+                }
+                break;
+                case "y":
+                case "Y":{
+                    beginTime = endTime = TimeTool.getTimeChgMonth(sampleTime, -12*dateLength);
+                }
+                break;
+                default:
+                    return null;
+            }
+            endTime = limitTime<=0L?endTime:Math.min(endTime, limitTime);
+        }else
+            return null;
+        if (endTime < beginTime)
+            return null;
+        jp.setBeginTime(beginTime);
+        jp.setEndTime(endTime);
+        return jp;
+    }
 }

+ 207 - 13
src/main/java/com/shkpr/service/warncore/bizhandler/SiteDataWarnHandler.java

@@ -17,10 +17,7 @@ import com.shkpr.service.warncore.dbdao.tables.OrdWarnEventInfoTable;
 import com.shkpr.service.warncore.dbdao.tables.OrdWarnPlanInfoTable;
 import com.shkpr.service.warncore.dbdao.tables.OrdWarnTriggerSampleHisTable;
 import com.shkpr.service.warncore.dto.*;
-import com.shkpr.service.warncore.jsonbean.JPGetRegionData;
-import com.shkpr.service.warncore.jsonbean.JPGetRegionReport;
-import com.shkpr.service.warncore.jsonbean.JPGetRegionReportItem;
-import com.shkpr.service.warncore.jsonbean.JPOrdWarnEventInfo;
+import com.shkpr.service.warncore.jsonbean.*;
 import com.shkpr.service.warncore.services.ServiceMgrProxy;
 import org.springframework.util.StringUtils;
 
@@ -391,6 +388,7 @@ public class SiteDataWarnHandler {
         Map<String, List<SiteSampleGroupItem>> ownerId2TriggerSample = new HashMap<>();//触发规则的样本数据
         String eventIdAcceptPlanHisTriggerSam = "";//将预案的历史触发样本数据迁移到该事件
         boolean clearPlanHisTriggerSam = false;//是否需要清空预案的历史触发样本数据
+        Map<String, String> mixComplexKey2Value = new HashMap<>();//复合公式下的{key->value}
         do {
             JPGetRegionData jpParam = new JPGetRegionData();
             jpParam.setTotal(1);
@@ -447,14 +445,35 @@ public class SiteDataWarnHandler {
                     continue;
                 for (OrdWarnRuleCondition condition:findRule.getConditions()){
                     String originValue = "";
-                    String backTag = condition.getFormat().substring(condition.getFormat().lastIndexOf("@")+1);
+                    String[] storeTagAndBackTag = condition.getFormat().split("@");
+                    String storeTag = storeTagAndBackTag[0];
+                    String backTag = storeTagAndBackTag[1];
                     for (KeyValue kv:sampleItem.getFields()){
                         if (backTag.equals(kv.getKey())){
                             originValue = kv.getValue();
                             break;
                         }
                     }
-                    int pkRes = CommToolHandler.matchedToRuleItem(originValue, condition);
+                    int pkRes = -1;
+                    if (condition.getThreshold().contains("@")){
+                        JPMixComplexCondition jp = CommToolHandler.parseConditions2TimeRange(condition.getThreshold(), sampleRecordTm, 0L);
+                        if (jp != null){
+                            String mixKey = jp.genKey();
+                            String matchedValue = mixComplexKey2Value.get(mixKey);
+                            if (matchedValue == null){
+                                matchedValue = getMixComplexDataRealBySet(siteId, storeTag, jp);
+                                if (matchedValue != null)
+                                    mixComplexKey2Value.put(mixKey, matchedValue);
+                            }
+                            pkRes = CommToolHandler.matchedToRuleItemEx(originValue, matchedValue, jp.getPercent(), condition);
+                        }else{
+                            step = String.format("Mix complex condition invalid.");
+                            code = ResponseCode.RESULT_BAD;
+                            break;
+                        }
+                    }else
+                        pkRes = CommToolHandler.matchedToRuleItem(originValue, condition);
+
                     if (pkRes == -1){
                         step = String.format("Compare value exception error");
                         code = ResponseCode.RESULT_BAD;
@@ -651,6 +670,7 @@ public class SiteDataWarnHandler {
         Map<String, List<SiteStatsDataItem>> ownerId2TriggerSample = new HashMap<>();//触发规则的样本数据
         String eventIdAcceptPlanHisTriggerSam = "";//将预案的历史触发样本数据迁移到该事件
         boolean clearPlanHisTriggerSam = false;//是否需要清空预案的历史触发样本数据
+        Map<String, String> mixComplexKey2Value = new HashMap<>();//复合公式下的{key->value}
 
         List<Long> tagTime = new ArrayList<>();//每个整点时刻,用于查询站点的统计数据
         long tmpStepToStep = thisTempStep.getQueryBeginUTC();
@@ -728,6 +748,7 @@ public class SiteDataWarnHandler {
 
                 for (OrdWarnRuleCondition condition:findRule.getConditions()){
                     String originValue = "";
+                    String storeTag = storeTagAndBackTag[0];
                     String backTag = storeTagAndBackTag[1];
                     for (KeyValue kv:sampleItem.getReport()){
                         if (backTag.equals(kv.getKey())){
@@ -735,7 +756,26 @@ public class SiteDataWarnHandler {
                             break;
                         }
                     }
-                    int pkRes = CommToolHandler.matchedToRuleItem(originValue, condition);
+                    int pkRes = -1;
+                    if (condition.getThreshold().contains("@")){
+                        JPMixComplexCondition jp = CommToolHandler.parseConditions2TimeRange(condition.getThreshold(), sampleRecordTm, 0L);
+                        if (jp != null){
+                            String mixKey = jp.genKey();
+                            String matchedValue = mixComplexKey2Value.get(mixKey);
+                            if (matchedValue == null){
+                                matchedValue = getMixComplexDataForHWO(siteId, storeTag, jp);
+                                if (matchedValue != null)
+                                    mixComplexKey2Value.put(mixKey, matchedValue);
+                            }
+                            pkRes = CommToolHandler.matchedToRuleItemEx(originValue, matchedValue, jp.getPercent(), condition);
+                        }else{
+                            step = String.format("Mix complex condition invalid.");
+                            code = ResponseCode.RESULT_BAD;
+                            break;
+                        }
+                    }else
+                        pkRes = CommToolHandler.matchedToRuleItem(originValue, condition);
+
                     if (pkRes == -1){
                         step = String.format("Compare value exception error");
                         code = ResponseCode.RESULT_BAD;
@@ -1034,6 +1074,7 @@ public class SiteDataWarnHandler {
         Map<String, List<SiteStatsDataItem>> ownerId2TriggerSample = new HashMap<>();//触发规则的样本数据
         String eventIdAcceptPlanHisTriggerSam = "";//将预案的历史触发样本数据迁移到该事件
         boolean clearPlanHisTriggerSam = false;//是否需要清空预案的历史触发样本数据
+        Map<String, String> mixComplexKey2Value = new HashMap<>();//复合公式下的{key->value}
 
         List<Long> tagTime = new ArrayList<>();//每天整点时刻,用于查询站点的统计数据
         long tmpStepToStep = thisTempStep.getQueryBeginUTC();
@@ -1106,6 +1147,7 @@ public class SiteDataWarnHandler {
 
                 for (OrdWarnRuleCondition condition:findRule.getConditions()){
                     String originValue = "";
+                    String storeTag = storeTagAndBackTag[0];
                     String backTag = storeTagAndBackTag[1];
                     for (KeyValue kv:sampleItem.getReport()){
                         if (backTag.equals(kv.getKey())){
@@ -1114,12 +1156,32 @@ public class SiteDataWarnHandler {
                         }
                     }
 
-                    int needLines = (int)(TimeTool.SEC_ONE_DAY/findRule.getDevCycle());//理论采集条数
-                    int offsetLines = (int)Math.ceil(needLines*0.01);//容错率为1%对应的条数
-                    int realLines = CastUtil.castInt(originValue, 0);//实际采集条数
-                    int diffLines = needLines-realLines;//缺失条数(实际条数比理论条数大时为负)
-                    double diffRate = diffLines<=offsetLines?0.0:(diffLines*100)/needLines;
-                    int pkRes = CommToolHandler.matchedToRuleItem(String.valueOf(diffRate), condition);
+                    int pkRes = -1;
+                    if (condition.getThreshold().contains("@")){
+                        JPMixComplexCondition jp = CommToolHandler.parseConditions2TimeRange(condition.getThreshold(), sampleRecordTm, 0L);
+                        if (jp != null){
+                            String mixKey = jp.genKey();
+                            String matchedValue = mixComplexKey2Value.get(mixKey);
+                            if (matchedValue == null){
+                                matchedValue = getMixComplexDataForLoss(siteId, storeTag, findRule.getDevCycle(), jp);
+                                if (matchedValue != null)
+                                    mixComplexKey2Value.put(mixKey, matchedValue);
+                            }
+                            pkRes = CommToolHandler.matchedToRuleItemEx(originValue, matchedValue, jp.getPercent(), condition);
+                        }else{
+                            step = String.format("Mix complex condition invalid.");
+                            code = ResponseCode.RESULT_BAD;
+                            break;
+                        }
+                    }else {
+                        int needLines = (int)(TimeTool.SEC_ONE_DAY/findRule.getDevCycle());//理论采集条数
+                        int offsetLines = (int)Math.ceil(needLines*0.01);//容错率为1%对应的条数
+                        int realLines = CastUtil.castInt(originValue, 0);//实际采集条数
+                        int diffLines = needLines-realLines;//缺失条数(实际条数比理论条数大时为负)
+                        double diffRate = diffLines<=offsetLines?0.0:(diffLines*100)/needLines;
+                        pkRes = CommToolHandler.matchedToRuleItem(String.valueOf(diffRate), condition);
+                    }
+                    
                     if (pkRes == -1){
                         step = String.format("Compare value exception error");
                         code = ResponseCode.RESULT_BAD;
@@ -1376,4 +1438,136 @@ public class SiteDataWarnHandler {
                         planDetail.getUid(), siteId, step, clearPlanHisTriggerSam, eventIdAcceptPlanHisTriggerSam, code.toString()));
         return code;
     }
+
+    private static String getMixComplexDataRealBySet(String objId, String field, JPMixComplexCondition jp){
+        String value = null;
+        SiteStatsDataResult siteStatsData = null;
+        do {
+            final JPGetRegionReportItem item = new JPGetRegionReportItem();
+            item.setUid(objId);
+            item.setField(field);
+            item.setTag("");
+            item.setBeginTime(jp.getBeginTime());
+            item.setEndTime(jp.getEndTime()+1L);
+
+            JPGetRegionReport queryJP = new JPGetRegionReport();
+            queryJP.setData(new ArrayList<JPGetRegionReportItem>(){{add(item);}});
+            queryJP.setTotal(CommTool.listSize(queryJP.getData()));
+
+            try {
+                ResponseRes<String> resHttp = ServiceMgrProxy.getInstance().applyDataQeServiceApi().siteRegionReport(queryJP);
+                if (ResponseCode.RESULT_NORMAL.toStrCode().equals(resHttp.getRescode()))
+                    siteStatsData = FastJsonUtil.fromJSONByGson(resHttp.getResdata(), SiteStatsDataResult.class);
+            }catch (Exception e){}
+
+            if (siteStatsData == null || CommTool.listSize(siteStatsData.getData()) <= 0)
+                break;
+
+            String key = String.format("%s_f", jp.getMethod());
+            if (CommTool.listSize(siteStatsData.getData().get(0).getReport()) > 0){
+                for (KeyValue tmp:siteStatsData.getData().get(0).getReport()){
+                    if (key.equals(tmp.getKey())){
+                        value = tmp.getValue();
+                        break;
+                    }
+                }
+            }
+        }while (false);
+        return value;
+    }
+
+    private static String getMixComplexDataForHWO(String objId, String field, JPMixComplexCondition jp){
+        String value = null;
+        WaterXYReportRes reportRes = null;
+        do {
+            JPGetRegionXY tmpQuery = new JPGetRegionXY();
+            tmpQuery.setBeginDate(TimeTool.convertUTC2DateStr(jp.getBeginTime(), TimeTool.TIMESTAMP_FORMAT));
+            tmpQuery.setEndDate(TimeTool.convertUTC2DateStr(jp.getEndTime(), TimeTool.TIMESTAMP_FORMAT));
+            tmpQuery.setByTime(0);
+            tmpQuery.setIds(new ArrayList<String>(){{add(objId);}});
+
+            try {
+                ResponseRes<String> resHttp = ServiceMgrProxy.getInstance().applyDataQeServiceApi().siteRegionWaterXY(tmpQuery);
+                if (ResponseCode.RESULT_NORMAL.toStrCode().equals(resHttp.getRescode()))
+                    reportRes = FastJsonUtil.fromJSONByGson(resHttp.getResdata(), WaterXYReportRes.class);
+            }catch (Exception e){}
+
+            if (reportRes == null || CommTool.listSize(reportRes.getData()) <= 0)
+                break;
+
+            int nSampleLines = CommTool.listSize(reportRes.getData().get(0).getReport());
+            if (nSampleLines <= 0)
+                break;
+
+            for (CommDVBean tmpItem:reportRes.getData().get(0).getReport()){
+                if (StringUtils.isEmpty(tmpItem.getValue()))
+                    continue;
+                if (StringUtils.isEmpty(value)){
+                    value = tmpItem.getValue();
+                    continue;
+                }
+                if ("min".equals(jp.getMethod())){
+                    value = String.valueOf(Math.min(CastUtil.castDouble(value), CastUtil.castDouble(tmpItem.getValue())));
+                }else if ("max".equals(jp.getMethod())){
+                    value = String.valueOf(Math.max(CastUtil.castDouble(value), CastUtil.castDouble(tmpItem.getValue())));
+                }else {
+                    value = String.valueOf(CastUtil.castDouble(value)+CastUtil.castDouble(tmpItem.getValue()));
+                }
+            }
+
+            if (!StringUtils.isEmpty(value) && "avg".equals(jp.getMethod()))
+                value = String.valueOf(CastUtil.castDouble(value)/nSampleLines);
+        }while (false);
+        return value;
+    }
+
+    private static String getMixComplexDataForLoss(String objId, String field, int devCycle, JPMixComplexCondition jp){
+        String value = null;
+        LinesXYReportRes reportRes = null;
+        do {
+            JPGetRegionXY tmpQuery = new JPGetRegionXY();
+            tmpQuery.setBeginDate(TimeTool.convertUTC2DateStr(jp.getBeginTime(), TimeTool.TIMESTAMP_FORMAT));
+            tmpQuery.setEndDate(TimeTool.convertUTC2DateStr(jp.getEndTime(), TimeTool.TIMESTAMP_FORMAT));
+            tmpQuery.setByTime(1);
+            tmpQuery.setIds(new ArrayList<String>(){{add(objId);}});
+
+            try {
+                ResponseRes<String> resHttp = ServiceMgrProxy.getInstance().applyDataQeServiceApi().siteRegionLinesXY(tmpQuery);
+                if (ResponseCode.RESULT_NORMAL.toStrCode().equals(resHttp.getRescode()))
+                    reportRes = FastJsonUtil.fromJSONByGson(resHttp.getResdata(), LinesXYReportRes.class);
+            }catch (Exception e){}
+
+            if (reportRes == null || CommTool.listSize(reportRes.getData()) <= 0)
+                break;
+
+            int nSampleLines = CommTool.listSize(reportRes.getData().get(0).getReport());
+            if (nSampleLines <= 0)
+                break;
+
+            for (CommDLBean tmpItem:reportRes.getData().get(0).getReport()){
+                if (StringUtils.isEmpty(value)){
+                    value = String.valueOf(tmpItem.getLines());
+                    continue;
+                }
+                if ("min".equals(jp.getMethod())){
+                    value = String.valueOf(Math.min(CastUtil.castInt(value), tmpItem.getLines()));
+                }else if ("max".equals(jp.getMethod())){
+                    value = String.valueOf(Math.max(CastUtil.castInt(value), tmpItem.getLines()));
+                }else {
+                    value = String.valueOf(CastUtil.castDouble(value)+tmpItem.getLines());
+                }
+            }
+
+            if (!StringUtils.isEmpty(value) && "avg".equals(jp.getMethod()))
+                value = String.valueOf(CastUtil.castDouble(value)/nSampleLines);
+
+            int needLines = (int)(TimeTool.SEC_ONE_DAY/devCycle);//理论采集条数
+            int offsetLines = (int)Math.ceil(needLines*0.01);//容错率为1%对应的条数
+            int realLines = CastUtil.castInt(value, 0);//实际采集条数
+            int diffLines = needLines-realLines;//缺失条数(实际条数比理论条数大时为负)
+            double diffRate = diffLines<=offsetLines?0.0:(diffLines*100)/needLines;
+            value = String.valueOf(diffRate);
+        }while (false);
+        return value;
+    }
 }

+ 48 - 2
src/main/java/com/shkpr/service/warncore/commtools/TimeTool.java

@@ -249,11 +249,11 @@ public class TimeTool {
     }
 
     /**
-     * 获取指定时间的上一个最近的整点时刻
+     * 获取指定时间所在的整点时刻
      * @param timeMs
      * @return
      */
-    public static  long getLastHourUTCNearTm(long timeMs){
+    public static  long getHourBeginUTCTm(long timeMs){
         if (!isMsUTC(timeMs))
             return timeMs;
         long timeSecond = timeMs/1000;
@@ -304,6 +304,29 @@ public class TimeTool {
     }
 
     /**
+     * 获取当前时间所在年份的开始时间戳
+     * @param curTime
+     * @return UTC时间格式(单位:毫秒)
+     * 返回时间格式为:xxxx-01-01 00:00:00.000所对应的时间戳
+     */
+    public static long getYearBeginUTC(long curTime){
+        Calendar c = Calendar.getInstance();
+        if (!isMsUTC(curTime))
+            curTime = curTime*1000;
+        try {
+            c.setTime(new Date(curTime));
+            c.set(Calendar.MONTH, Calendar.JANUARY);
+            c.set(Calendar.DAY_OF_MONTH, 1);
+            c.set(Calendar.HOUR_OF_DAY, 0);
+            c.set(Calendar.MINUTE, 0);
+            c.set(Calendar.SECOND,0);
+            c.set(Calendar.MILLISECOND, 0);
+            return c.getTimeInMillis();
+        }catch (Exception e){}
+        return 0L;
+    }
+
+    /**
      * 获取当前时间所在月份的开始时间戳
      * @param curTime
      * @return UTC时间格式(单位:毫秒)
@@ -348,6 +371,29 @@ public class TimeTool {
     }
 
     /**
+     * 获取当前时间所在年份的结束时间戳
+     * @param curTime
+     * @return UTC时间格式(单位:毫秒)
+     * 返回时间格式为:xxxx-12-31 23:59:59.999所对应的时间戳
+     */
+    public static long getYearEndUTC(long curTime){
+        Calendar c = Calendar.getInstance();
+        if (!isMsUTC(curTime))
+            curTime = curTime*1000;
+        try {
+            c.setTime(new Date(curTime));
+            c.set(Calendar.MONTH, Calendar.DECEMBER);
+            c.set(Calendar.DAY_OF_MONTH, c.getActualMaximum(Calendar.DAY_OF_MONTH));
+            c.set(Calendar.HOUR_OF_DAY, 23);
+            c.set(Calendar.MINUTE, 59);
+            c.set(Calendar.SECOND,59);
+            c.set(Calendar.MILLISECOND, 999);
+            return c.getTimeInMillis();
+        }catch (Exception e){}
+        return 0L;
+    }
+
+    /**
      * 获取指定时间段内的所有月份列表
      * @param startTime 起始时间,UTC时间格式,秒或毫秒
      * @param endTime 结束时间,UTC时间格式,秒或毫秒

+ 14 - 0
src/main/java/com/shkpr/service/warncore/dto/CommDLBean.java

@@ -0,0 +1,14 @@
+package com.shkpr.service.warncore.dto;
+
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class CommDLBean {
+    private String date = "";
+    private int lines = 0;
+
+    public CommDLBean() {
+    }
+}

+ 16 - 0
src/main/java/com/shkpr/service/warncore/dto/LinesXYItemRes.java

@@ -0,0 +1,16 @@
+package com.shkpr.service.warncore.dto;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.List;
+
+@Getter
+@Setter
+public class LinesXYItemRes {
+    private String uid = "";  //站点id/设备id/分区id
+    private List<CommDLBean> report = null;
+
+    public LinesXYItemRes() {
+    }
+}

+ 16 - 0
src/main/java/com/shkpr/service/warncore/dto/LinesXYReportRes.java

@@ -0,0 +1,16 @@
+package com.shkpr.service.warncore.dto;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.List;
+
+@Getter
+@Setter
+public class LinesXYReportRes {
+    private int total = 0;
+    private List<LinesXYItemRes> data = null;
+
+    public LinesXYReportRes() {
+    }
+}

+ 16 - 0
src/main/java/com/shkpr/service/warncore/dto/WaterXYItemRes.java

@@ -0,0 +1,16 @@
+package com.shkpr.service.warncore.dto;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.List;
+
+@Getter
+@Setter
+public class WaterXYItemRes {
+    private String uid = "";  //站点id/设备id/分区id
+    private List<CommDVBean> report = null;
+
+    public WaterXYItemRes() {
+    }
+}

+ 16 - 0
src/main/java/com/shkpr/service/warncore/dto/WaterXYReportRes.java

@@ -0,0 +1,16 @@
+package com.shkpr.service.warncore.dto;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.List;
+
+@Getter
+@Setter
+public class WaterXYReportRes {
+    private int total = 0;
+    private List<WaterXYItemRes> data = null;
+
+    public WaterXYReportRes() {
+    }
+}

+ 23 - 0
src/main/java/com/shkpr/service/warncore/jsonbean/JPGetRegionXY.java

@@ -0,0 +1,23 @@
+package com.shkpr.service.warncore.jsonbean;
+
+import com.global.base.tools.FastJsonUtil;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.List;
+
+@Getter
+@Setter
+public class JPGetRegionXY {
+    private int byTime = 0;//0 --小时分组;1 -- 日分组
+    private List<String> ids = null;
+    private String beginDate = "";
+    private String endDate = "";
+
+    public JPGetRegionXY() {
+    }
+
+    public String toJsonStr(){
+        return FastJsonUtil.toJSON(this);
+    }
+}

+ 22 - 0
src/main/java/com/shkpr/service/warncore/jsonbean/JPMixComplexCondition.java

@@ -0,0 +1,22 @@
+package com.shkpr.service.warncore.jsonbean;
+
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class JPMixComplexCondition {
+    private long beginTime = 0;
+    private long endTime = 0;
+    private String mode = "";//获取方式:last/prev/near/this
+    private String method = "";//时间段内取值方法:avg/sum/dif/min/max/val
+    private String percent = "";//百分率
+    private String dateUnit = "";//日期单位
+
+    public JPMixComplexCondition() {
+    }
+
+    public String genKey(){
+        return String.format("%s-%d-%d", method, beginTime, endTime);
+    }
+}

+ 49 - 0
src/main/java/com/shkpr/service/warncore/services/CloudDataQeService.java

@@ -12,6 +12,7 @@ import com.shkpr.service.warncore.dto.ResponseRes;
 import com.shkpr.service.warncore.globalcache.GlobalData;
 import com.shkpr.service.warncore.jsonbean.JPGetRegionData;
 import com.shkpr.service.warncore.jsonbean.JPGetRegionReport;
+import com.shkpr.service.warncore.jsonbean.JPGetRegionXY;
 import com.shkpr.service.warncore.jsonbean.JPStrIdsSK;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
@@ -46,6 +47,8 @@ public class CloudDataQeService {
     private String mSiteLastMapDataPath = "";
     private String mSiteRegionDataPath = "";
     private String mSiteRegionReportPath = "";
+    private String mSiteRegionWaterXYPath = "";
+    private String mSiteRegionLinesXYPath = "";
     private HttpHeaders headers = null;
 
     @PostConstruct
@@ -57,6 +60,8 @@ public class CloudDataQeService {
         mSiteLastMapDataPath = String.format("%s%s", mStrAddress, "v3/stats/site-data/last-map-show-group");
         mSiteRegionDataPath = String.format("%s%s", mStrAddress, "v3/stats/site-data/regions-group");
         mSiteRegionReportPath = String.format("%s%s", mStrAddress, "v3/stats/site-data/regions-report-field");
+        mSiteRegionWaterXYPath = String.format("%s%s", mStrAddress, "v3/stats/site-data/water-xy");
+        mSiteRegionLinesXYPath = String.format("%s%s", mStrAddress, "v3/stats/site-data/lines-xy");
 
         headers = new HttpHeaders();
         headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
@@ -130,4 +135,48 @@ public class CloudDataQeService {
         }
         return resResult;
     }
+
+    @Retryable(value = {RemoteAccessException.class}, maxAttempts = 3, backoff = @Backoff(delay = 200L, multiplier = 1))
+    public ResponseRes<String> siteRegionWaterXY(final JPGetRegionXY jsonParam) throws Exception{
+        ResponseRes<String> resResult = new ResponseRes<String>();
+        resResult.setRescode(ResponseCode.RESULT_BAD.toStrCode());
+        resResult.setResmsg(MSG_FAILED);
+        resResult.setResdata("");
+
+        HttpEntity<String> request = new HttpEntity<>(jsonParam.toJsonStr(), headers);
+        try {
+            ResponseEntity<String> response = restTemplate.postForEntity(mSiteRegionWaterXYPath, request, String.class);
+            if (response.getStatusCode() == HttpStatus.OK){
+                String strBody = response.getBody();
+                return FastJsonUtil.fromJSONByGson(strBody, ResponseRes.class);
+            }
+        }catch (Exception e){
+            LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_WARN, LogFlagBusiType.BUSI_CALL_DATA_QE_AS.toStrValue(), strClassName, String.format("siteRegionWaterXY from CloudDataQeService(url:%s) failed(%s)...", mSiteRegionWaterXYPath, e.getMessage()));
+            //达到maxAttempts次数将返回RemoteAccessException
+            throw new RemoteAccessException("Retry...");
+        }
+        return resResult;
+    }
+
+    @Retryable(value = {RemoteAccessException.class}, maxAttempts = 3, backoff = @Backoff(delay = 200L, multiplier = 1))
+    public ResponseRes<String> siteRegionLinesXY(final JPGetRegionXY jsonParam) throws Exception{
+        ResponseRes<String> resResult = new ResponseRes<String>();
+        resResult.setRescode(ResponseCode.RESULT_BAD.toStrCode());
+        resResult.setResmsg(MSG_FAILED);
+        resResult.setResdata("");
+
+        HttpEntity<String> request = new HttpEntity<>(jsonParam.toJsonStr(), headers);
+        try {
+            ResponseEntity<String> response = restTemplate.postForEntity(mSiteRegionLinesXYPath, request, String.class);
+            if (response.getStatusCode() == HttpStatus.OK){
+                String strBody = response.getBody();
+                return FastJsonUtil.fromJSONByGson(strBody, ResponseRes.class);
+            }
+        }catch (Exception e){
+            LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_WARN, LogFlagBusiType.BUSI_CALL_DATA_QE_AS.toStrValue(), strClassName, String.format("siteRegionLinesXY from CloudDataQeService(url:%s) failed(%s)...", mSiteRegionLinesXYPath, e.getMessage()));
+            //达到maxAttempts次数将返回RemoteAccessException
+            throw new RemoteAccessException("Retry...");
+        }
+        return resResult;
+    }
 }