|
|
@@ -0,0 +1,520 @@
|
|
|
+package com.lc.ibps.business.service.impl;
|
|
|
+
|
|
|
+import com.lc.ibps.base.framework.table.ICommonDao;
|
|
|
+import com.lc.ibps.business.service.QualityIndicatorService;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+
|
|
|
+import javax.annotation.Resource;
|
|
|
+import org.apache.commons.lang3.StringUtils;
|
|
|
+import java.math.BigDecimal;
|
|
|
+import java.math.RoundingMode;
|
|
|
+import java.time.YearMonth;
|
|
|
+import java.time.format.DateTimeFormatter;
|
|
|
+import java.util.*;
|
|
|
+import java.util.regex.Matcher;
|
|
|
+import java.util.regex.Pattern;
|
|
|
+import java.util.stream.Collectors;
|
|
|
+
|
|
|
+import static com.lc.ibps.base.framework.repository.IRepository.logger;
|
|
|
+
|
|
|
+@Service
|
|
|
+public class QualityIndicatorImpl implements QualityIndicatorService {
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private ICommonDao<?> commonDao;
|
|
|
+
|
|
|
+
|
|
|
+ public List<Map<String, Object>> getQualityIndicatorold(String requestType, String targetName, String dateRange) {
|
|
|
+ List<Map<String, Object>> resultList = new ArrayList<>();
|
|
|
+
|
|
|
+ // 准备公共的 X 轴数据和 Y 轴数值
|
|
|
+ List<String> xAxisData = Arrays.asList("Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun");
|
|
|
+ List<Integer> dataValues = Arrays.asList(120, 200, 150, 80, 70, 110, 130);
|
|
|
+
|
|
|
+ // --- 构建第一个图表的数据对象 ---
|
|
|
+ Map<String, Object> chartData1 = new LinkedHashMap<>();
|
|
|
+ chartData1.put("title", "测试专用1");
|
|
|
+ chartData1.put("xAxis", xAxisData);
|
|
|
+
|
|
|
+ // 封装 series 数据系列
|
|
|
+ List<Map<String, Object>> seriesList1 = new ArrayList<>();
|
|
|
+ Map<String, Object> seriesItem1 = new LinkedHashMap<>();
|
|
|
+ seriesItem1.put("name", "指标数据"); // 系列名称,方便前端图例展示
|
|
|
+ seriesItem1.put("type", "bar"); // 图表类型
|
|
|
+ seriesItem1.put("data", dataValues); // 具体的数值
|
|
|
+ seriesList1.add(seriesItem1);
|
|
|
+
|
|
|
+ chartData1.put("series", seriesList1);
|
|
|
+
|
|
|
+
|
|
|
+ // --- 构建第二个图表的数据对象 ---
|
|
|
+ Map<String, Object> chartData2 = new LinkedHashMap<>();
|
|
|
+ chartData2.put("title", "测试专用2");
|
|
|
+ chartData2.put("xAxis", xAxisData);
|
|
|
+
|
|
|
+ // 封装 series 数据系列
|
|
|
+ List<Map<String, Object>> seriesList2 = new ArrayList<>();
|
|
|
+ Map<String, Object> seriesItem2 = new LinkedHashMap<>();
|
|
|
+ seriesItem2.put("name", "指标数据");
|
|
|
+ seriesItem2.put("type", "bar");
|
|
|
+ seriesItem2.put("data", dataValues);
|
|
|
+ seriesList2.add(seriesItem2);
|
|
|
+
|
|
|
+ chartData2.put("series", seriesList2);
|
|
|
+
|
|
|
+ // 将两个图表的数据对象放入最终的返回数组中
|
|
|
+ resultList.add(chartData1);
|
|
|
+ resultList.add(chartData2);
|
|
|
+
|
|
|
+ return resultList;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取质量指标/目标数据
|
|
|
+ *
|
|
|
+ * @param requestType 请求类型 ("mubiao" 或 "zhibiao")
|
|
|
+ * @param targetName 目标名称
|
|
|
+ * @param dateRange 时间范围 (格式: "yyyyMM-yyyyMM",如 "202511-202604")
|
|
|
+ * @return 前端图表所需的数据结构
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public List<Map<String, Object>> getQualityIndicator(String requestType, String targetName, String dateRange) {
|
|
|
+ // 1. 入参校验
|
|
|
+ if (StringUtils.isBlank(requestType) || (!"mubiao".equals(requestType) && !"zhibiao".equals(requestType))) {
|
|
|
+ return new ArrayList<>();
|
|
|
+ }
|
|
|
+ // 校验时间格式 yyyyMM-yyyyMM
|
|
|
+ if (StringUtils.isNotBlank(dateRange) && !dateRange.matches("\\d{6}-\\d{6}")) {
|
|
|
+ return new ArrayList<>();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 获取原始数据
|
|
|
+ List<Map<String, Object>> orgData = getOrgData();
|
|
|
+ if (orgData == null || orgData.isEmpty()) {
|
|
|
+ return new ArrayList<>();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 解析时间范围
|
|
|
+ YearMonth startYM = null;
|
|
|
+ YearMonth endYM = null;
|
|
|
+ if (StringUtils.isNotBlank(dateRange)) {
|
|
|
+ startYM = YearMonth.parse(dateRange.substring(0, 6), DateTimeFormatter.ofPattern("yyyyMM"));
|
|
|
+ endYM = YearMonth.parse(dateRange.substring(7), DateTimeFormatter.ofPattern("yyyyMM"));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 初步过滤数据
|
|
|
+ List<Map<String, Object>> filteredData = new ArrayList<>();
|
|
|
+ List<Map<String, Object>> filteredOldData = new ArrayList<>();
|
|
|
+ String finalTargetName = StringUtils.isNotBlank(targetName) ? targetName.trim() : null;
|
|
|
+
|
|
|
+ for (Map<String, Object> row : orgData) {
|
|
|
+ String zhiBiao = (String) row.get("zhi_liang_zhi_bia");
|
|
|
+ String muBiao = (String) row.get("zhi_liang_mu_biao");
|
|
|
+ String bianZhiShiJian = (String) row.get("bian_zhi_shi_jian");
|
|
|
+
|
|
|
+ // 场景一:查质量目标 (zhi_liang_zhi_bia 为空)
|
|
|
+ if ("mubiao".equals(requestType)) {
|
|
|
+ if (StringUtils.isBlank(zhiBiao)) {
|
|
|
+ filteredData.add(row);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 场景二:查质量指标 (zhi_liang_zhi_bia 不为空)
|
|
|
+ else if ("zhibiao".equals(requestType)) {
|
|
|
+ if (StringUtils.isNotBlank(zhiBiao)) {
|
|
|
+ // 校验目标名
|
|
|
+ if (finalTargetName != null && !finalTargetName.equals(muBiao)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ filteredOldData.add(row);
|
|
|
+ // 校验时间范围
|
|
|
+ if (startYM != null && endYM != null) {
|
|
|
+ if (!isTimeInRange(bianZhiShiJian, startYM, endYM)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ filteredData.add(row);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 4. 数据分组与排序 (按 名称 + 频率权重 分组)
|
|
|
+ Map<String, Integer> freqWeightMap = new HashMap<>();
|
|
|
+ freqWeightMap.put("每月", 1);
|
|
|
+ freqWeightMap.put("每季度", 2);
|
|
|
+ freqWeightMap.put("每年", 3);
|
|
|
+
|
|
|
+ // 分组 Key: 名称_频率权重
|
|
|
+ Map<String, List<Map<String, Object>>> groupedData = filteredData.stream()
|
|
|
+ .collect(Collectors.groupingBy(row -> {
|
|
|
+ String name = "mubiao".equals(requestType)
|
|
|
+ ? (String) row.get("zhi_liang_mu_biao")
|
|
|
+ : (String) row.get("zhi_liang_zhi_bia");
|
|
|
+ String freq = (String) row.get("tong_ji_pin_lv_");
|
|
|
+ int weight = freqWeightMap.getOrDefault(freq, 99);
|
|
|
+ return name + "_" + weight;
|
|
|
+ }));
|
|
|
+
|
|
|
+ // 5. 构建 Echarts 数据结构
|
|
|
+ List<Map<String, Object>> resultList = new ArrayList<>();
|
|
|
+
|
|
|
+ // 对分组后的 Key 进行排序,保证输出顺序
|
|
|
+ List<String> sortedKeys = groupedData.keySet().stream().sorted().collect(Collectors.toList());
|
|
|
+
|
|
|
+ for (String key : sortedKeys) {
|
|
|
+ List<Map<String, Object>> groupList = groupedData.get(key);
|
|
|
+ if (groupList.isEmpty()) continue;
|
|
|
+
|
|
|
+ // 提取该组的公共信息
|
|
|
+ String title = "mubiao".equals(requestType)
|
|
|
+ ? (String) groupList.get(0).get("zhi_liang_mu_biao")
|
|
|
+ : (String) groupList.get(0).get("zhi_liang_zhi_bia");
|
|
|
+ String subtext = "限值:"+groupList.get(groupList.size() - 1).get("yuan_shi_shu_ju_").toString().replace("@","");
|
|
|
+ String freqType = (String) groupList.get(0).get("tong_ji_pin_lv_");
|
|
|
+ String chartType = "mubiao".equals(requestType) ? "bar" : "line";
|
|
|
+
|
|
|
+ // 按时间先后排序组内数据
|
|
|
+ groupList.sort((a, b) -> compareTimeStr((String) a.get("bian_zhi_shi_jian"), (String) b.get("bian_zhi_shi_jian")));
|
|
|
+
|
|
|
+ // 提取 X轴, Y轴(实际值), MarkLine(目标值)
|
|
|
+ List<String> xAxis = new ArrayList<>();
|
|
|
+ List<Number> yAxisData = new ArrayList<>();
|
|
|
+ List<Number> markLineData = new ArrayList<>();
|
|
|
+
|
|
|
+ for (Map<String, Object> row : groupList) {
|
|
|
+ xAxis.add(formatXAxisLabel((String) row.get("bian_zhi_shi_jian")));
|
|
|
+ yAxisData.add(parseNumber(row.get("shi_ji_shu_zhi_")));
|
|
|
+// Number val = (Number) parseNumber(row.get("shi_ji_shu_zhi_"));
|
|
|
+// if (val != null) {
|
|
|
+// yAxisData.add(val);
|
|
|
+// }
|
|
|
+ markLineData.add(parseNumber(row.get("zhi_biao_xian_zhi")));
|
|
|
+// Number val2 = (Number) parseNumber(row.get("zhi_biao_xian_zhi"));
|
|
|
+// if (val2 != null) {
|
|
|
+// markLineData.add(val2);
|
|
|
+// }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 6. 计算上年均值 (仅月度、季度,且第一个时间为年初)
|
|
|
+ if (("每月".equals(freqType) || "每季度".equals(freqType)) && !xAxis.isEmpty()) {
|
|
|
+ String firstTimeStr = (String) groupList.get(0).get("bian_zhi_shi_jian");
|
|
|
+ int currentYear = extractYear(firstTimeStr);
|
|
|
+ boolean isFirstPeriod = false;
|
|
|
+
|
|
|
+ if ("每月".equals(freqType) && firstTimeStr.contains("1月份")) isFirstPeriod = true;
|
|
|
+ if ("每季度".equals(freqType) && firstTimeStr.contains("第1季度")) isFirstPeriod = true;
|
|
|
+
|
|
|
+ if (isFirstPeriod) {
|
|
|
+ // 查找上一年同频率的所有数据
|
|
|
+ List<Number> lastYearValues = new ArrayList<>();
|
|
|
+ for (Map<String, Object> row : filteredOldData) {
|
|
|
+ String name = (String) row.get("zhi_liang_zhi_bia");
|
|
|
+ String rowFreq = (String) row.get("tong_ji_pin_lv_");
|
|
|
+ String rowTime = (String) row.get("bian_zhi_shi_jian");
|
|
|
+
|
|
|
+ if (name.equals(title) && rowFreq.equals(freqType) && extractYear(rowTime) == currentYear - 1) {
|
|
|
+ lastYearValues.add(parseNumber(row.get("shi_ji_shu_zhi_")));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (!lastYearValues.isEmpty()) {
|
|
|
+ double sum = lastYearValues.stream().mapToDouble(Number::doubleValue).sum();
|
|
|
+ Number lastYearAvg = new BigDecimal(sum / lastYearValues.size())
|
|
|
+ .setScale(4, RoundingMode.HALF_UP)
|
|
|
+ .doubleValue();
|
|
|
+ // 将上年均值插入到第一个位置
|
|
|
+ yAxisData.add(0, lastYearAvg);
|
|
|
+ markLineData.add(0, null);
|
|
|
+ xAxis.add(0, (currentYear - 1) + "年均值");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 7. 计算该图表 Y 轴的最大最小值 (浮动20%取整)
|
|
|
+ double maxVal = yAxisData.stream().filter(Objects::nonNull).mapToDouble(Number::doubleValue).max().orElse(0);
|
|
|
+ double minVal = yAxisData.stream().filter(Objects::nonNull).mapToDouble(Number::doubleValue).min().orElse(0);
|
|
|
+
|
|
|
+ // 加入 markline 的值参与最大最小计算
|
|
|
+ double markMax = markLineData.stream().filter(Objects::nonNull).mapToDouble(Number::doubleValue).max().orElse(0);
|
|
|
+ double markMin = markLineData.stream().filter(Objects::nonNull).mapToDouble(Number::doubleValue).min().orElse(0);
|
|
|
+
|
|
|
+ maxVal = Math.max(maxVal, markMax);
|
|
|
+ minVal = Math.min(minVal, markMin);
|
|
|
+
|
|
|
+ double finalMax = (int) Math.ceil(maxVal * 1.2);
|
|
|
+ //最大值还是小数的被向上取整到1的取消取整
|
|
|
+ if(finalMax==1){
|
|
|
+ finalMax = maxVal* 1.2;
|
|
|
+ }
|
|
|
+ //最大值低于100附近的取100
|
|
|
+ if(maxVal <= 100 && maxVal >= 80){
|
|
|
+ finalMax = 100;
|
|
|
+ }
|
|
|
+ double finalMin = (int) Math.floor(minVal * 0.8);
|
|
|
+ //目标的柱状图最小值取0
|
|
|
+ if(maxVal <= 100 && maxVal >= 80 && "mubiao".equals(requestType)){
|
|
|
+ finalMin = 0;
|
|
|
+ }
|
|
|
+ // 使用 BigDecimal 进行四舍五入并保留两位小数
|
|
|
+ BigDecimal maxBd = BigDecimal.valueOf(finalMax).setScale(2, RoundingMode.HALF_UP);
|
|
|
+ BigDecimal minBd = BigDecimal.valueOf(finalMin).setScale(2, RoundingMode.HALF_UP);
|
|
|
+
|
|
|
+ // 转换回 double 类型
|
|
|
+ finalMax = maxBd.doubleValue();
|
|
|
+ finalMin = minBd.doubleValue();
|
|
|
+ // 柱状图横坐标不足5个,往前自动补齐
|
|
|
+ if ("bar".equals(chartType) && xAxis.size() < 5) {
|
|
|
+ int needFill = 5 - xAxis.size();
|
|
|
+ // 获取当前第一个横坐标的年度,如果取不到则默认为当前系统年份
|
|
|
+ int baseYear = xAxis.isEmpty() ? java.time.Year.now().getValue() : extractYearFromStringLabel(xAxis.get(0));
|
|
|
+
|
|
|
+ for (int i = 0; i < needFill; i++) {
|
|
|
+ int fillYear = baseYear - (needFill - i); // 依次往前推年份
|
|
|
+ xAxis.add(0, fillYear + "年度"); // 往前补年度标签
|
|
|
+ yAxisData.add(0, null); // 往前补空数据
|
|
|
+ markLineData.add(0, null); // 往前补空标线
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 8. 调用封装好的组装方法,生成单个图表 Map
|
|
|
+ Map<String, Object> chartData = buildEchartsChart(title, subtext, xAxis, yAxisData, markLineData, chartType, finalMin, finalMax);
|
|
|
+ resultList.add(chartData);
|
|
|
+ }
|
|
|
+
|
|
|
+ return resultList;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 抽取出来的 Echarts 图表组装方法
|
|
|
+ */
|
|
|
+ private Map<String, Object> buildEchartsChart(String title, String subtext, List<String> xAxis,
|
|
|
+ List<Number> yAxisData, List<Number> markLineData,
|
|
|
+ String chartType, double finalMin, double finalMax) {
|
|
|
+
|
|
|
+ // 封装 Series 和 MarkLine
|
|
|
+ List<Map<String, Object>> seriesList = new ArrayList<>();
|
|
|
+ Map<String, Object> seriesItem = new LinkedHashMap<>();
|
|
|
+ seriesItem.put("name", title);
|
|
|
+ // showSymbol: true,
|
|
|
+ //seriesItem.put("showSymbol", true);
|
|
|
+ seriesItem.put("type", chartType);
|
|
|
+ List<Object> formattedYAxisData = new ArrayList<>();
|
|
|
+ for (Number num : yAxisData) {
|
|
|
+ if (num == null) {
|
|
|
+ formattedYAxisData.add(null);
|
|
|
+ } else {
|
|
|
+ double val = num.doubleValue();
|
|
|
+
|
|
|
+ // 1. 先判断是否为极小数(保持原有逻辑)
|
|
|
+ // 使用 BigDecimal 设置最多 4 位小数,采用四舍五入模式
|
|
|
+
|
|
|
+ BigDecimal bd = new BigDecimal(val);
|
|
|
+ formattedYAxisData.add(bd.setScale(4, RoundingMode.HALF_UP).toPlainString());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 将处理后的数据放入 series
|
|
|
+ seriesItem.put("data", formattedYAxisData);
|
|
|
+
|
|
|
+ // 封装 markLine (目标值)
|
|
|
+ List<List<Object>> markLineDataArray = new ArrayList<>();
|
|
|
+ for (int i = 0; i < markLineData.size(); i++) {
|
|
|
+ Number currentY = markLineData.get(i);
|
|
|
+ // 跳过 null 值
|
|
|
+ if (currentY == null) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ double yValue = currentY.doubleValue();
|
|
|
+ int startIndex = i; // 记录当前相同 Y 值的起始索引
|
|
|
+
|
|
|
+ // 向后查找,合并连续相同的 Y 值
|
|
|
+ while (i + 1 < markLineData.size() && markLineData.get(i + 1) != null
|
|
|
+ && markLineData.get(i + 1).doubleValue() == yValue) {
|
|
|
+ i++;
|
|
|
+ }
|
|
|
+ // i 指向这一段相同 Y 值的最后一个点的索引
|
|
|
+
|
|
|
+ // 1. 生成横线
|
|
|
+ List<Object> hCoords = new ArrayList<>();
|
|
|
+
|
|
|
+ // 起点:段首
|
|
|
+ Map<String, Object> hStart = new LinkedHashMap<>();
|
|
|
+ hStart.put("coord", Arrays.asList(startIndex, yValue));
|
|
|
+ hCoords.add(hStart);
|
|
|
+
|
|
|
+ // 终点:段尾
|
|
|
+ Map<String, Object> hEnd = new LinkedHashMap<>();
|
|
|
+ hEnd.put("coord", Arrays.asList(i, yValue));
|
|
|
+ hCoords.add(hEnd);
|
|
|
+
|
|
|
+ markLineDataArray.add(hCoords);
|
|
|
+
|
|
|
+
|
|
|
+ // 2. 生成连接下一段的线
|
|
|
+ if (i + 1 < markLineData.size() && markLineData.get(i + 1) != null) {
|
|
|
+ double nextYValue = markLineData.get(i + 1).doubleValue();
|
|
|
+
|
|
|
+ // 只有当下一段的 Y 值与当前 Y 值不同时,才需要画线
|
|
|
+ if (nextYValue != yValue) {
|
|
|
+ List<Object> vCoords = new ArrayList<>();
|
|
|
+
|
|
|
+ // 起点:当前段的最后一个点 (i, yValue)
|
|
|
+ Map<String, Object> vStart = new LinkedHashMap<>();
|
|
|
+ vStart.put("coord", Arrays.asList(i, yValue));
|
|
|
+ vCoords.add(vStart);
|
|
|
+
|
|
|
+ // 终点:下一段的第一个点 (i + 1, nextYValue)
|
|
|
+ Map<String, Object> vEnd = new LinkedHashMap<>();
|
|
|
+ vEnd.put("coord", Arrays.asList(i + 1, nextYValue));
|
|
|
+ vCoords.add(vEnd);
|
|
|
+
|
|
|
+ markLineDataArray.add(vCoords);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ Map<String, Object> lineStyle = new LinkedHashMap<>();
|
|
|
+ lineStyle.put("color", "red");
|
|
|
+ lineStyle.put("type", "dashed");
|
|
|
+ Map<String, Object> markLine = new LinkedHashMap<>();
|
|
|
+ markLine.put("data", markLineDataArray);
|
|
|
+ markLine.put("silent", true); // 标线不响应鼠标事件
|
|
|
+ markLine.put("symbol", "none");
|
|
|
+ markLine.put("lineStyle", lineStyle);
|
|
|
+ seriesItem.put("markLine", markLine);
|
|
|
+
|
|
|
+ seriesList.add(seriesItem);
|
|
|
+
|
|
|
+ // 组装最终图表对象
|
|
|
+ Map<String, Object> chartData = new LinkedHashMap<>();
|
|
|
+ chartData.put("title", title);
|
|
|
+ chartData.put("subtext", subtext);
|
|
|
+ chartData.put("xAxis", xAxis);
|
|
|
+ chartData.put("series", seriesList);
|
|
|
+
|
|
|
+ // 将最大最小值返回给前端 (仅折线图需要动态Y轴)
|
|
|
+ if ("line".equals(chartType)) {
|
|
|
+ Map<String, Object> yAxisConfig = new LinkedHashMap<>();
|
|
|
+ yAxisConfig.put("min", finalMin);
|
|
|
+ yAxisConfig.put("max", finalMax);
|
|
|
+ chartData.put("yAxisConfig", yAxisConfig);
|
|
|
+ }
|
|
|
+
|
|
|
+ return chartData;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 判断时间是否在范围内
|
|
|
+ private boolean isTimeInRange(String bianZhiShiJian, YearMonth startYM, YearMonth endYM) {
|
|
|
+ if (StringUtils.isBlank(bianZhiShiJian)) return false;
|
|
|
+
|
|
|
+ int year = extractYear(bianZhiShiJian);
|
|
|
+
|
|
|
+ // 年度判断:只要年份在范围内即可
|
|
|
+ if (bianZhiShiJian.contains("年度")) {
|
|
|
+ return year >= startYM.getYear() && year <= endYM.getYear();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 季度判断:解析出季度,看该季度的任意月份是否与范围有交集
|
|
|
+ if (bianZhiShiJian.contains("季度")) {
|
|
|
+ int quarter = extractQuarter(bianZhiShiJian);
|
|
|
+ YearMonth qStart = YearMonth.of(year, (quarter - 1) * 3 + 1);
|
|
|
+ YearMonth qEnd = YearMonth.of(year, quarter * 3);
|
|
|
+ return !qStart.isAfter(endYM) && !qEnd.isBefore(startYM);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 月度判断
|
|
|
+ if (bianZhiShiJian.contains("月份")) {
|
|
|
+ int month = extractMonth(bianZhiShiJian);
|
|
|
+ YearMonth currentYM = YearMonth.of(year, month);
|
|
|
+ return !currentYM.isAfter(endYM) && !currentYM.isBefore(startYM);
|
|
|
+ }
|
|
|
+
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 提取年份 (针对数据库原始格式,如 "2025年11月份")
|
|
|
+ private int extractYear(String timeStr) {
|
|
|
+ Pattern p = Pattern.compile("(\\d{4})年");
|
|
|
+ Matcher m = p.matcher(timeStr);
|
|
|
+ if (m.find()) return Integer.parseInt(m.group(1));
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 提取年份 (针对已经格式化后的 X 轴标签,如 "2025年度" 或 "2025年11月")
|
|
|
+ private int extractYearFromStringLabel(String label) {
|
|
|
+ if (StringUtils.isBlank(label)) return java.time.Year.now().getValue();
|
|
|
+ Pattern p = Pattern.compile("(\\d{4})");
|
|
|
+ Matcher m = p.matcher(label);
|
|
|
+ if (m.find()) return Integer.parseInt(m.group(1));
|
|
|
+ return java.time.Year.now().getValue();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 提取月份
|
|
|
+ private int extractMonth(String timeStr) {
|
|
|
+ Pattern p = Pattern.compile("(\\d{1,2})月份");
|
|
|
+ Matcher m = p.matcher(timeStr);
|
|
|
+ if (m.find()) return Integer.parseInt(m.group(1));
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 提取季度
|
|
|
+ private int extractQuarter(String timeStr) {
|
|
|
+ Pattern p = Pattern.compile("第(\\d{1,2})季度");
|
|
|
+ Matcher m = p.matcher(timeStr);
|
|
|
+ if (m.find()) return Integer.parseInt(m.group(1));
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 比较时间字符串大小,用于排序
|
|
|
+ private int compareTimeStr(String t1, String t2) {
|
|
|
+ int y1 = extractYear(t1);
|
|
|
+ int y2 = extractYear(t2);
|
|
|
+ if (y1 != y2) return Integer.compare(y1, y2);
|
|
|
+
|
|
|
+ if (t1.contains("年度") || t2.contains("年度")) return 0;
|
|
|
+
|
|
|
+ if (t1.contains("季度") && t2.contains("季度")) {
|
|
|
+ return Integer.compare(extractQuarter(t1), extractQuarter(t2));
|
|
|
+ }
|
|
|
+ if (t1.contains("月份") && t2.contains("月份")) {
|
|
|
+ return Integer.compare(extractMonth(t1), extractMonth(t2));
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 格式化 X 轴标签 (去掉冗余文字)
|
|
|
+ private String formatXAxisLabel(String timeStr) {
|
|
|
+ if (StringUtils.isBlank(timeStr)) return "";
|
|
|
+// if (timeStr.contains("月份")) return timeStr.substring(timeStr.indexOf("年")+1, timeStr.length()).replace("份", "");
|
|
|
+ if (timeStr.contains("月份")) return timeStr.replace("份", "");
|
|
|
+// if (timeStr.contains("季度")) return timeStr.substring(timeStr.indexOf("年")+1, timeStr.length());
|
|
|
+ if (timeStr.contains("季度")) return timeStr;
|
|
|
+ if (timeStr.contains("年度")) return timeStr;
|
|
|
+ return timeStr;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 安全转换数字
|
|
|
+ private Number parseNumber(Object obj) {
|
|
|
+ if (obj == null) return null;
|
|
|
+ if (obj instanceof Number) {
|
|
|
+ return (Number) obj;
|
|
|
+ }
|
|
|
+ // 尝试转换字符串
|
|
|
+ String str = obj.toString().trim();
|
|
|
+ // 排除明显的非数字字符串,可以根据实际情况添加更多判断
|
|
|
+ if (str.isEmpty() || "null".equalsIgnoreCase(str) || "N/A".equals(str) || "达标".equals(str)) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 如果需要保留整数类型,可以先尝试 Integer,再尝试 Double
|
|
|
+ return Double.parseDouble(str);
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
+ // 记录日志:无法解析的数值字符串 str
|
|
|
+ return null; // 返回 null,后续流处理会过滤掉
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public List<Map<String, Object>> getOrgData() {
|
|
|
+ String sqlStr = "select id_,parent_id_,bian_zhi_shi_jian,zhi_liang_mu_biao,zhi_liang_zhi_bia,zhi_biao_xian_zhi,shi_ji_shu_zhi_,yuan_shi_shu_ju_,zhuang_tai_,tong_ji_pin_lv_ from t_zlzbpjzb where zhuang_tai_='已完成'";
|
|
|
+ List<Map<String, Object>> retList = (List<Map<String, Object>>) commonDao.query(sqlStr);
|
|
|
+ return retList;
|
|
|
+ }
|
|
|
+}
|