diff --git a/src/main/java/iet/ustb/sf/controller/TargetDataController.java b/src/main/java/iet/ustb/sf/controller/TargetDataController.java new file mode 100644 index 0000000..8ff1dd9 --- /dev/null +++ b/src/main/java/iet/ustb/sf/controller/TargetDataController.java @@ -0,0 +1,36 @@ +package iet.ustb.sf.controller; + +import com.alibaba.fastjson.JSONObject; +import iet.ustb.sf.common.R; +import iet.ustb.sf.service.TargetDataService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Map; + +/** + * TargetDataController + * + * @author huangge1199 + * @since 2025/8/12 9:19:23 + */ +@Tag(name = "目标管控-指标监测") +@RestController +@RequestMapping("/mbgk/data") +public class TargetDataController { + + @Resource + private TargetDataService targetDataService; + + @Operation(summary = "获取指标参考数据") + @PostMapping("/getExamleData") + public R> getExamleData(@RequestBody JSONObject params) { + return R.ok(targetDataService.getData(params, 1)); + } + +} diff --git a/src/main/java/iet/ustb/sf/service/TargetDataService.java b/src/main/java/iet/ustb/sf/service/TargetDataService.java index c25e2dc..a996719 100644 --- a/src/main/java/iet/ustb/sf/service/TargetDataService.java +++ b/src/main/java/iet/ustb/sf/service/TargetDataService.java @@ -1,5 +1,6 @@ package iet.ustb.sf.service; +import com.alibaba.fastjson.JSONObject; import iet.ustb.sf.domain.Target; import iet.ustb.sf.domain.TargetData; import com.baomidou.mybatisplus.extension.service.IService; @@ -25,4 +26,6 @@ public interface TargetDataService extends IService { TargetData updateHistory(Target target, String[] strArr, Map map); void saveTargetDate(Date date); + + Map getData(JSONObject params, int type); } diff --git a/src/main/java/iet/ustb/sf/service/UtilService.java b/src/main/java/iet/ustb/sf/service/UtilService.java index 708f8fa..b1192bf 100644 --- a/src/main/java/iet/ustb/sf/service/UtilService.java +++ b/src/main/java/iet/ustb/sf/service/UtilService.java @@ -5,6 +5,7 @@ import iet.ustb.sf.domain.Rule; import iet.ustb.sf.domain.Target; import iet.ustb.sf.domain.TargetData; +import java.time.LocalDate; import java.util.Date; import java.util.Map; @@ -25,6 +26,14 @@ public interface UtilService { */ String[] getCurrentCycleDataByCycle(Date date, String cycle); + /** + * 获取日期所在的年-周 + * + * @param date 日期 + * @return 结果 + */ + String getYearWeek(LocalDate date); + /** * 计算表达式结果 * diff --git a/src/main/java/iet/ustb/sf/service/impl/TargetDataServiceImpl.java b/src/main/java/iet/ustb/sf/service/impl/TargetDataServiceImpl.java index b5051f2..0dd6bb7 100644 --- a/src/main/java/iet/ustb/sf/service/impl/TargetDataServiceImpl.java +++ b/src/main/java/iet/ustb/sf/service/impl/TargetDataServiceImpl.java @@ -1,22 +1,30 @@ package iet.ustb.sf.service.impl; +import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.toolkit.SqlRunner; import iet.ustb.sf.domain.*; +import iet.ustb.sf.exception.ErrorCode; +import iet.ustb.sf.exception.MyException; import iet.ustb.sf.mapper.*; import iet.ustb.sf.service.TargetDataService; import iet.ustb.sf.service.UtilService; import iet.ustb.sf.util.CheckUtils; import jakarta.annotation.Resource; +import org.apache.commons.lang3.StringUtils; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import java.io.PrintWriter; import java.io.StringWriter; import java.text.SimpleDateFormat; +import java.time.DayOfWeek; import java.time.LocalDate; import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.time.temporal.TemporalAdjusters; import java.util.*; /** @@ -54,7 +62,7 @@ public class TargetDataServiceImpl extends ServiceImpl map = utilService.getCurrentCycleData(date); - LocalDate day = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); + LocalDate day = date.toInstant().atZone(ZoneId.of("Asia/Shanghai")).toLocalDate(); for (String cycle : map.keySet()) { if (day.getDayOfWeek().getValue() > 1 && "lastWeek".equals(cycle)) { continue; @@ -129,6 +137,309 @@ public class TargetDataServiceImpl extends ServiceImpl getData(JSONObject params, int type) { + String targetId = params.getString("id"); + String start = params.getString("start"); + String end = params.getString("end"); + Integer n = params.getInteger("n"); + CheckUtils.checkEmpty(targetId, "指标ID"); + Target target = targetMapper.selectById(targetId); + + if (n == null) { + n = 7; + } + + if (StringUtils.isEmpty(start) && StringUtils.isEmpty(end)) { + Map cycleDate = getCycleData(target.getCycle(), n); + start = cycleDate.get("start"); + end = cycleDate.get("end"); + } else if (!StringUtils.isEmpty(start) && !StringUtils.isEmpty(end)) { + try { + String[] strArrStart = getDataBySetDateAndCycle(LocalDate.parse(start), target.getCycle()); + start = strArrStart[3]; + } catch (DateTimeParseException e) { + throw new MyException(ErrorCode.PARAMS_ERROR, "开始日期格式错误或日期非法!"); + } + try { + String[] strArrEnd = getDataBySetDateAndCycle(LocalDate.parse(end), target.getCycle()); + if (LocalDate.parse(strArrEnd[4]).isBefore(LocalDate.parse(end))) { + end = strArrEnd[4]; + } + + } catch (DateTimeParseException e) { + throw new MyException(ErrorCode.PARAMS_ERROR, "结束日期格式错误或日期非法!"); + } + } else { + throw new MyException(ErrorCode.PARAMS_ERROR, "开始日期和结束日期不能一个有值一个没有值!"); + } + + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("target_id", targetId); + queryWrapper.ge("set_date", start); + queryWrapper.lt("set_date", end); + List targetDatas = targetDataMapper.selectList(queryWrapper); + targetDatas = getHistoryData(targetDatas, target, start, end); + List xData = new ArrayList<>(); + List data = new ArrayList<>(); + List oriData = new ArrayList<>(); + for (TargetData targetData : targetDatas) { + xData.add(targetData.getXShow()); + data.add(targetData.getVal()); + oriData.add(targetData.getVal()); + } + + Map map = new HashMap<>(); + map.put("start", start); + map.put("end", end); + map.put("max", Optional.of(data) + .filter(list -> !list.isEmpty()) + .map(Collections::max) + .map(value -> "" + CheckUtils.roundToDecimalPlaces(value, 4)) + .orElse("-")); + map.put("avg", Optional.of(data) + .filter(list -> !list.isEmpty()) + .map(list -> list.stream().mapToDouble(Double::doubleValue).average().orElse(Double.NaN)) + .map(value -> "" + CheckUtils.roundToDecimalPlaces(value, 4)) + .orElse("-")); + map.put("medium", CheckUtils.roundToDecimalPlaces(getMedian(data), 4)); + map.put("min", Optional.of(data) + .filter(list -> !list.isEmpty()) + .map(Collections::min) + .map(value -> "" + CheckUtils.roundToDecimalPlaces(value, 4)) + .orElse("-")); + map.put("xData", xData); + map.put("data", data); + map.put("cycle", getCycle(target)); + map.put("oriData", oriData); + return map; + } + + /** + * 获取列表中位数 + * + * @param data 列表数据 + * @return 中位数 + */ + private String getMedian(List data) { + if (data.isEmpty()) { + return "-"; + } + List sortedData = data.stream() + .sorted() + .toList(); + int size = sortedData.size(); + double median; + if (size % 2 == 0) { + // 偶数个元素,返回中间两个元素的平均值 + median = (sortedData.get(size / 2 - 1) + sortedData.get(size / 2)) / 2.0; + } else { + // 奇数个元素,返回中间的元素 + median = sortedData.get(size / 2); + } + return String.valueOf(median); + } + + /** + * 获取指定日期所在周期的 xShow, cycle, setDate, start, end + * + * @param localDate 日期 + * @param cycle 周期 + * @return 结果 + */ + private String[] getDataBySetDateAndCycle(LocalDate localDate, String cycle) { + DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + String setDate = localDate.format(dtf); + String xShow, showCycle, start, end; + switch (cycle) { + case "yesterday": + case "nearlyWeek": + case "nearlyMonth": + showCycle = "day"; + xShow = localDate.format(dtf); + end = localDate.plusDays(1).format(dtf); + if ("yesterday".equals(cycle)) { + start = localDate.format(dtf); + } else if ("nearlyWeek".equals(cycle)) { + start = localDate.minusDays(7).format(dtf); + } else { + start = localDate.minusDays(30).format(dtf); + } + break; + case "week": + case "lastWeek": + showCycle = "week"; + xShow = utilService.getYearWeek(LocalDate.parse(setDate)); + start = localDate.format(dtf); + end = localDate.plusDays(7).format(dtf); + break; + case "month": + case "lastMonth": + showCycle = "month"; + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM"); + xShow = localDate.format(formatter); + start = localDate.format(dtf); + end = localDate.plusMonths(1).format(dtf); + break; + case "product": + case "lastProduct": + showCycle = "product"; + DateTimeFormatter productDtf = DateTimeFormatter.ofPattern("yyyy-MM"); + xShow = localDate.plusMonths(1).format(productDtf); + start = localDate.format(dtf); + end = localDate.plusMonths(1).format(dtf); + break; + default: + xShow = null; + showCycle = null; + setDate = null; + start = null; + end = null; + break; + } + + return new String[]{xShow, showCycle, setDate, start, end}; + } + + /** + * 补充时间范围内缺失的历史数据 + * + * @param targetDataList 现有历史数据 + * @param target 指标 + * @param start 开始日期 + * @param end 结束日期 + * @return 补全的历史数据 + */ + public List getHistoryData( + List targetDataList, + Target target, + String start, + String end) { + LocalDate compareDate = LocalDate.parse(start); + for (int i = 0; i < targetDataList.size(); i++) { + LocalDate setDate = targetDataList.get(i).getSetDate() + .toInstant().atZone(ZoneId.of("Asia/Shanghai")).toLocalDate(); + while (compareDate.isBefore(setDate)) { + String[] strArr = getDataBySetDateAndCycle(compareDate, target.getCycle()); + targetDataList.add(i, updateHistory(target, strArr, new HashMap<>())); + compareDate = getCompareDate(target, compareDate); + i++; + setDate = targetDataList.get(i).getSetDate() + .toInstant().atZone(ZoneId.of("Asia/Shanghai")).toLocalDate(); + } + compareDate = getCompareDate(target, compareDate); + } + while (compareDate.isBefore(LocalDate.parse(end))) { + String[] strArr = getDataBySetDateAndCycle(compareDate, target.getCycle()); + if (LocalDate.parse(end).isBefore(LocalDate.parse(strArr[4]))) { + strArr[4] = end; + } + targetDataList.add(updateHistory(target, strArr, new HashMap<>())); + compareDate = getCompareDate(target, compareDate); + } + return targetDataList; + } + + /** + * 获取下一个对比日期 + * + * @param target 指标 + * @param compareDate 对比日期 + * @return 对比日期 + */ + private LocalDate getCompareDate(Target target, LocalDate compareDate) { + switch (target.getCycle()) { + case "yesterday": + case "nearlyMonth": + case "nearlyWeek": + compareDate = compareDate.plusDays(1); + break; + case "week": + case "lastWeek": + compareDate = compareDate.plusWeeks(1); + break; + case "month": + case "lastMonth": + case "product": + case "lastProduct": + compareDate = compareDate.plusMonths(1); + break; + } + return compareDate; + } + + /** + * 获取周期 + * + * @param target 指标 + * @return 周期 + */ + private String getCycle(Target target) { + String cycle = ""; + if (Arrays.asList("yesterday", "nearlyWeek", "nearlyMonth").contains(target.getCycle())) { + cycle = "day"; + } else if (Arrays.asList("week", "lastWeek").contains(target.getCycle())) { + cycle = "week"; + } else if (Arrays.asList("month", "lastMonth").contains(target.getCycle())) { + cycle = "month"; + } else if (Arrays.asList("product", "lastProduct").contains(target.getCycle())) { + cycle = "product"; + } + return cycle; + } + + /** + * 获取前n个周期的开始结束日期 + * + * @param cycle 周期 + * @return 结果map + */ + private Map getCycleData(String cycle, int n) { + LocalDate localDate = LocalDate.now(); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + String end = localDate.format(formatter); + if (Arrays.asList("yesterday", "nearlyWeek", "nearlyMonth").contains(cycle)) { + localDate = localDate.minusDays(n); + } else if (Arrays.asList("week", "lastWeek").contains(cycle)) { + if (localDate.getDayOfWeek() == DayOfWeek.MONDAY) { + localDate = localDate.minusWeeks(1); + } + localDate = localDate.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)); + if ("lastWeek".equals(cycle)) { + end = localDate.format(formatter); + localDate = localDate.minusWeeks(1); + } + localDate = localDate.minusWeeks(n - 1); + } else if (Arrays.asList("month", "lastMonth").contains(cycle)) { + if (localDate.getDayOfMonth() == 1) { + localDate = localDate.minusMonths(1); + } + localDate = localDate.withDayOfMonth(1); + if ("lastMonth".equals(cycle)) { + end = localDate.format(formatter); + localDate = localDate.minusMonths(1); + } + localDate = localDate.minusMonths(n - 1); + } else { + if (localDate.getDayOfMonth() == 26) { + localDate = localDate.minusMonths(1); + } + if (localDate.getDayOfMonth() >= 26) { + localDate = localDate.withDayOfMonth(26); + } + if ("lastProduct".equals(cycle)) { + end = localDate.format(formatter); + localDate = localDate.minusMonths(1); + } + localDate = localDate.minusMonths(n - 1); + } + String start = localDate.format(formatter); + Map map = new HashMap<>(3); + map.put("start", start); + map.put("end", end); + return map; + } + @Override public TargetData updateHistory(Target target, String[] strArr, Map map) { String xShow = strArr[0]; @@ -142,7 +453,11 @@ public class TargetDataServiceImpl extends ServiceImpl> list = SqlRunner.db().selectList(target.getResultSql(), strArr[3], strArr[4]); - result = Double.parseDouble(list.get(0).get("result").toString()); + if (list.isEmpty() || list.get(0) == null || list.get(0).isEmpty()) { + result = 0f; + } else { + result = Double.parseDouble(list.get(0).get("result").toString()); + } } else { String exprStr = target.getResultSql(); String[] expressionList = exprStr.split(","); diff --git a/src/main/java/iet/ustb/sf/service/impl/UtilServiceImpl.java b/src/main/java/iet/ustb/sf/service/impl/UtilServiceImpl.java index 581099f..efb6879 100644 --- a/src/main/java/iet/ustb/sf/service/impl/UtilServiceImpl.java +++ b/src/main/java/iet/ustb/sf/service/impl/UtilServiceImpl.java @@ -13,6 +13,8 @@ import net.objecthunter.exp4j.ExpressionBuilder; import org.springframework.stereotype.Service; import java.text.SimpleDateFormat; +import java.time.LocalDate; +import java.time.temporal.WeekFields; import java.util.*; import java.util.function.Function; import java.util.regex.Matcher; @@ -86,7 +88,7 @@ public class UtilServiceImpl implements UtilService { if (calendar.get(Calendar.WEEK_OF_YEAR) == 1) { calendar.add(Calendar.YEAR, 1); } - xShow = calendar.get(Calendar.YEAR) + "-" + String.format("%02d", calendar.get(Calendar.WEEK_OF_YEAR)); + xShow = getYearWeek(LocalDate.parse(setDate)); showCycle = "week"; break; case "month": @@ -147,6 +149,21 @@ public class UtilServiceImpl implements UtilService { return new String[]{xShow, showCycle, setDate, start, end}; } + @Override + public String getYearWeek(LocalDate date) { + WeekFields wf = WeekFields.ISO; + + int weekNumber = date.get(wf.weekOfYear()); + int year = date.getYear(); + + if (date.getMonthValue() == 12 && weekNumber == 1) { + year = year + 1; + } + + String weekStr = String.format("%02d", weekNumber); + + return year + "-" + weekStr; + } @Override public double getEvalResult(String expression) {