diff --git a/src/main/java/iet/ustb/sf/controller/TargetController.java b/src/main/java/iet/ustb/sf/controller/TargetController.java index f1ce5e9..5ca73b9 100644 --- a/src/main/java/iet/ustb/sf/controller/TargetController.java +++ b/src/main/java/iet/ustb/sf/controller/TargetController.java @@ -53,4 +53,11 @@ public class TargetController { String result = targetService.getResult(params); return R.ok(result); } + + @Operation(summary = "保存指标") + @PostMapping("/saveTarget") + public R saveTarget(@RequestBody JSONObject params) { + targetService.saveTarget(params); + return R.ok(); + } } diff --git a/src/main/java/iet/ustb/sf/domain/Monitor.java b/src/main/java/iet/ustb/sf/domain/Monitor.java index 855e48a..f6f3c64 100644 --- a/src/main/java/iet/ustb/sf/domain/Monitor.java +++ b/src/main/java/iet/ustb/sf/domain/Monitor.java @@ -83,7 +83,7 @@ public class Monitor { * 目标值 */ @TableField(value = "target") - private String target; + private Double target; /** * diff --git a/src/main/java/iet/ustb/sf/service/TargetDataService.java b/src/main/java/iet/ustb/sf/service/TargetDataService.java index f071a4c..3507f8d 100644 --- a/src/main/java/iet/ustb/sf/service/TargetDataService.java +++ b/src/main/java/iet/ustb/sf/service/TargetDataService.java @@ -1,13 +1,25 @@ package iet.ustb.sf.service; +import iet.ustb.sf.domain.Target; import iet.ustb.sf.domain.TargetData; import com.baomidou.mybatisplus.extension.service.IService; +import java.util.Map; + /** -* @author hyy -* @description 针对表【mbgk_target_data】的数据库操作Service -* @createDate 2025-08-05 15:40:32 -*/ + * @author hyy + * @description 针对表【mbgk_target_data】的数据库操作Service + * @createDate 2025-08-05 15:40:32 + */ public interface TargetDataService extends IService { + /** + * 更新历史数据 + * + * @param target 指标 + * @param strArr xShow, cycle, setDate, start, end + * @param map 关联的指标数据 + * @return 结果 + */ + TargetData updateHistory(Target target, String[] strArr, Map map); } diff --git a/src/main/java/iet/ustb/sf/service/TargetService.java b/src/main/java/iet/ustb/sf/service/TargetService.java index c1ae6fd..2897248 100644 --- a/src/main/java/iet/ustb/sf/service/TargetService.java +++ b/src/main/java/iet/ustb/sf/service/TargetService.java @@ -19,4 +19,6 @@ public interface TargetService extends IService { List searchColumn(JSONObject params); String getResult(JSONObject params); + + void saveTarget(JSONObject params); } diff --git a/src/main/java/iet/ustb/sf/service/UtilService.java b/src/main/java/iet/ustb/sf/service/UtilService.java new file mode 100644 index 0000000..d1a36ad --- /dev/null +++ b/src/main/java/iet/ustb/sf/service/UtilService.java @@ -0,0 +1,54 @@ +package iet.ustb.sf.service; + +import iet.ustb.sf.domain.Monitor; +import iet.ustb.sf.domain.Rule; +import iet.ustb.sf.domain.Target; +import iet.ustb.sf.domain.TargetData; + +import java.util.Date; + +/** + * UtilService + * + * @author huangge1199 + * @since 2025/8/7 15:11:03 + */ +public interface UtilService { + + /** + * 获取指定周期的对应日期 xShow, cycle, setDate, start, end + * + * @param date 日期 + * @param cycle 周期 + * @return 结果 + */ + String[] getCurrentCycleDataByCycle(Date date, String cycle); + + /** + * 计算表达式结果 + * + * @param expression 表达式 + * @return 结果 + */ + double getEvalResult(String expression); + + /** + * 该规则是否触发 + * + * @param rule 规则信息 + * @param target 指标信息 + * @param targetData 指标数据 + * @return 结果 + */ + boolean isWarn(Rule rule, Target target, TargetData targetData); + + /** + * 获取初始化监控数据 + * + * @param targetData 指标数据 + * @param target 指标信息 + * @param rule 规则信息 + * @return 结果 + */ + Monitor getInitMonitor(TargetData targetData, Target target, Rule rule); +} 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 3f91c3e..63559f3 100644 --- a/src/main/java/iet/ustb/sf/service/impl/TargetDataServiceImpl.java +++ b/src/main/java/iet/ustb/sf/service/impl/TargetDataServiceImpl.java @@ -1,20 +1,171 @@ package iet.ustb.sf.service.impl; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import iet.ustb.sf.domain.TargetData; +import com.baomidou.mybatisplus.extension.toolkit.SqlRunner; +import iet.ustb.sf.domain.*; +import iet.ustb.sf.mapper.MonitorMapper; +import iet.ustb.sf.mapper.RuleMapper; +import iet.ustb.sf.mapper.TargetMapper; import iet.ustb.sf.service.TargetDataService; import iet.ustb.sf.mapper.TargetDataMapper; +import iet.ustb.sf.service.UtilService; +import iet.ustb.sf.util.CheckUtils; +import jakarta.annotation.Resource; import org.springframework.stereotype.Service; +import java.text.SimpleDateFormat; +import java.util.*; + /** -* @author hyy -* @description 针对表【mbgk_target_data】的数据库操作Service实现 -* @createDate 2025-08-05 15:40:32 -*/ + * @author hyy + * @description 针对表【mbgk_target_data】的数据库操作Service实现 + * @createDate 2025-08-05 15:40:32 + */ @Service public class TargetDataServiceImpl extends ServiceImpl - implements TargetDataService{ + implements TargetDataService { + @Resource + private TargetDataMapper targetDataMapper; + + @Resource + private TargetMapper targetMapper; + + @Resource + private UtilService utilService; + + @Resource + private MonitorMapper monitorMapper; + + @Resource + private RuleMapper ruleMapper; + + @Override + public TargetData updateHistory(Target target, String[] strArr, Map map) { + String xShow = strArr[0]; + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("target_id", target.getId()); + queryWrapper.eq("x_show", xShow); + TargetData targetData = targetDataMapper.selectOne(queryWrapper); + if (targetData != null && targetData.getId() != null) { + return targetData; + } + double result; + if (target.getType() < 2) { + List> list = SqlRunner.db().selectList(target.getResultSql(), strArr[3], strArr[4]); + result = Double.parseDouble(list.get(0).get("result").toString()); + } else { + String exprStr = target.getResultSql(); + String[] expressionList = exprStr.split(","); + List opList = Arrays.asList("+", "-", "*", "/", "(", ")"); + List idList = new ArrayList<>(); + for (String expression : expressionList) { + if (!opList.contains(expression) && !idList.contains(expression)) { + idList.add(expression); + } + } + List targetList = targetMapper.selectByIds(idList); + exprStr = exprStr.replaceAll(",", ""); + if (map == null) { + map = new HashMap<>(); + } + for (Target child : targetList) { + double val; + if (map.containsKey(child.getId())) { + val = map.get(child.getId()).getVal(); + } else { + TargetData tmp = updateHistory(child, strArr, map); + map.put(child.getId(), tmp); + val = tmp.getVal(); + } + exprStr = exprStr.replaceAll(child.getId(), val + ""); + } + result = utilService.getEvalResult(exprStr); + } + + Date now = new Date(); + try { + targetData = TargetData.builder() + .id(UUID.randomUUID().toString()) + .targetId(target.getId()) + .setDate(new SimpleDateFormat("yyyy-MM-dd").parse(strArr[2])) + .xShow(xShow) + .cycle(strArr[1]) + .createTime(now) + .updateTime(now) + .val(CheckUtils.roundToDecimalPlaces(result, 6)) + .targetCycle(target.getCycle()) + .build(); + targetDataMapper.insert(targetData); + } catch (Exception e) { + throw new RuntimeException(e); + } + saveMonitor(targetData); + return targetData; + } + + /** + * 新的指标数据添加监控数据 + * + * @param targetData 指标数据 + */ + public void saveMonitor(TargetData targetData) { + double val = targetData.getVal(); + String targetId = targetData.getTargetId(); + + Target target = targetMapper.selectById(targetId); + Date now = new Date(); + + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("target_id", target.getId()); + queryWrapper.eq("x_show", targetData.getXShow()); + monitorMapper.delete(queryWrapper); + + Monitor monitor = Monitor.builder() + .id(UUID.randomUUID().toString()) + .targetId(targetId) + .name(target.getName()) + .type(target.getType()) + .topic(target.getTopic()) + .organization(target.getOrganization()) + .level(target.getLevel()) + .unit(target.getUnit()) + .val(val) + .warnLevel(999) + .setDate(targetData.getSetDate()) + .xShow(targetData.getXShow()) + .createTime(now) + .updateTime(now) + .build(); + + List ruleList = ruleMapper.selectList(new QueryWrapper().eq("target_id", target.getId())); + if (ruleList == null || ruleList.isEmpty()) { + monitorMapper.insert(monitor); + return; + } + + List monitorList = new ArrayList<>(); + monitorList.add(monitor); + + boolean bl = false; + Rule oneRule = null; + for (Rule rule : ruleList) { + if (oneRule == null || oneRule.getWarnLevel() > rule.getWarnLevel()) { + oneRule = rule; + } + bl = utilService.isWarn(rule, target, targetData); + if (bl) { + monitorList.add(utilService.getInitMonitor(targetData, target, rule)); + } + } + if (!bl) { + Monitor mbgkMonitor = utilService.getInitMonitor(targetData, target, oneRule); + mbgkMonitor.setWarnLevel(998); + monitorList.add(mbgkMonitor); + } + monitorMapper.insert(monitorList); + } } diff --git a/src/main/java/iet/ustb/sf/service/impl/TargetServiceImpl.java b/src/main/java/iet/ustb/sf/service/impl/TargetServiceImpl.java index 5fd0d21..bf1343d 100644 --- a/src/main/java/iet/ustb/sf/service/impl/TargetServiceImpl.java +++ b/src/main/java/iet/ustb/sf/service/impl/TargetServiceImpl.java @@ -1,6 +1,5 @@ package iet.ustb.sf.service.impl; -import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; @@ -9,21 +8,22 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.toolkit.SqlRunner; import iet.ustb.sf.domain.Category; import iet.ustb.sf.domain.Target; +import iet.ustb.sf.domain.TargetData; import iet.ustb.sf.domain.TargetOption; import iet.ustb.sf.exception.ErrorCode; import iet.ustb.sf.exception.ThrowUtils; import iet.ustb.sf.mapper.CategoryMapper; +import iet.ustb.sf.mapper.TargetDataMapper; +import iet.ustb.sf.service.TargetDataService; import iet.ustb.sf.service.TargetService; import iet.ustb.sf.mapper.TargetMapper; +import iet.ustb.sf.service.UtilService; import iet.ustb.sf.util.CheckUtils; import jakarta.annotation.Resource; -import lombok.val; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; -import java.time.DayOfWeek; -import java.time.LocalDate; -import java.time.format.DateTimeFormatter; import java.util.*; import java.util.stream.Collectors; @@ -42,6 +42,15 @@ public class TargetServiceImpl extends ServiceImpl @Resource private CategoryMapper categoryMapper; + @Resource + private TargetDataMapper targetDataMapper; + + @Resource + private TargetDataService targetDataService; + + @Resource + private UtilService utilService; + @Override public IPage getTargetsByCategory(JSONObject params) { int pageIndex = Optional.ofNullable(params.getInteger("pageIndex")).orElse(1); @@ -94,6 +103,178 @@ public class TargetServiceImpl extends ServiceImpl return list.get(0).get(result.getColumnName()).toString(); } + @Override + @Transactional(rollbackFor = Exception.class) + public void saveTarget(JSONObject params) { + Target target = params.getJSONObject("target").toJavaObject(Target.class); + List showList = params.getJSONArray("show").toJavaList(TargetOption.class); + List searchList = params.getJSONArray("search").toJavaList(TargetOption.class); + TargetOption result = params.getJSONObject("result").toJavaObject(TargetOption.class); + + checkTarget(target); + String columns = showList.stream().map(TargetOption::getColumnName).collect(Collectors.joining(",")); + CheckUtils.checkColumns(columns, target.getTableName()); + CheckUtils.checkSearchOption(searchList, target.getTableName()); + CheckUtils.checkResultOption(result, target.getTableName()); + + Date now = new Date(); + target.setSearchSql(getSearchSql(showList, searchList, target.getTableName())); + params.put("tableName", target.getTableName()); + target.setResultSql(getResultSql(params)); + target.setUpdateTime(now); + + Map> optionsMap = showList.stream() + .peek(option -> { + option.setTargetId(target.getId()); + if (option.getCreateTime() == null) { + option.setCreateTime(now); + } + option.setUpdateTime(now); + }) + .collect(Collectors.groupingBy(TargetOption::getColumnName)); + + String cycle = null; + for (TargetOption option : searchList) { + List options = optionsMap.get(option.getColumnName()); + if (options != null && !options.isEmpty()) { + TargetOption ori = options.get(0); + ori.setOp(ori.getOp() + "," + option.getOp()); + ori.setFun(option.getFun()); + ori.setVal(option.getVal()); + if ("1".equals(option.getOp()) && option.getType() == 1) { + if (cycle == null) { + cycle = option.getFun(); + continue; + } + ThrowUtils.throwIf(!cycle.equals(option.getFun()), ErrorCode.PARAMS_ERROR, "日期类型的周期不一致!"); + } + } + } + ThrowUtils.throwIf(StringUtils.isEmpty(cycle), ErrorCode.PARAMS_ERROR, "缺少时间条件,无法保存!"); + target.setCycle(cycle); + + if (target.getId() == null) { + target.setId(UUID.randomUUID().toString()); + target.setCreateTime(now); + if (!StringUtils.isEmpty(target.getCategoryId())) { + categoryDeal(null, target); + } + targetMapper.insert(target); + saveDate(target); + } + } + + /** + * 指标变动后,指标数据处理 + * + * @param target 指标 + */ + private void saveDate(Target target) { + targetDataMapper.delete(new QueryWrapper().eq("target_id", target.getId())); + String[] strArr = utilService.getCurrentCycleDataByCycle(new Date(), target.getCycle()); + targetDataService.updateHistory(target, strArr, null); + } + + /** + * 指标变动,处理对应的分类 + * + * @param oldTarget 旧指标 + * @param newTarget 新指标 + */ + @Transactional(rollbackFor = Exception.class) + public void categoryDeal(Target oldTarget, Target newTarget) { + if (oldTarget == null && newTarget == null) { + return; + } + Set oldList = oldTarget == null ? new HashSet<>() : getOrganizationList(oldTarget.getOrganization()); + Set newList = newTarget == null ? new HashSet<>() : getOrganizationList(newTarget.getOrganization()); + + String tagetId = oldTarget == null ? newTarget.getId() : oldTarget.getId(); + String categoryId = oldTarget == null ? newTarget.getCategoryId() : oldTarget.getCategoryId(); + QueryWrapper query = new QueryWrapper<>(); + query.eq("category_id", categoryId); + query.ne("id", tagetId); + List targetList = targetMapper.selectList(query); + Set set = new HashSet<>(); + for (Target target : targetList) { + set.addAll(getOrganizationList(target.getOrganization())); + } + + Category category = categoryMapper.selectById(categoryId); + Set organizationSet = getOrganizationList(category.getOrganization()); + + Iterator iterator = newList.iterator(); + while (iterator.hasNext()) { + String news = iterator.next(); + if (!oldList.isEmpty() && oldList.contains(news)) { + oldList.remove(news); + iterator.remove(); + continue; + } + if (!set.contains(news)) { + organizationSet.add(news); + } + } + for (String old : oldList) { + if (!set.contains(old)) { + organizationSet.remove(old); + } + } + category.setOrganization(String.join(",", organizationSet)); + categoryMapper.insertOrUpdate(category); + } + + /** + * 把用逗号拼接的组织字符串转为set集合 + * + * @param organization 组织字符串 + * @return set组织集合 + */ + private Set getOrganizationList(String organization) { + Set set = new HashSet<>(); + if (StringUtils.isEmpty(organization)) { + return set; + } + if (organization.contains(",")) { + set.addAll(Arrays.asList(organization.split(","))); + } else { + set.add(organization); + } + return set; + } + + /** + * 拼接列表查询SQL + * + * @param showList 查询集合 + * @param searchList 条件集合 + * @param tableName 表名 + * @return SQL + */ + private String getSearchSql(List showList, List searchList, String tableName) { + return "SELECT " + showList.stream().map(TargetOption::getColumnName).collect(Collectors.joining(",")) + + " FROM " + tableName + " WHERE " + getWhereSql(searchList); + } + + /** + * 指标空值校验 + * + * @param target 指标 + */ + private void checkTarget(Target target) { + CheckUtils.checkEmpty(target.getName(), "指标名称"); + QueryWrapper query = new QueryWrapper<>(); + query.eq("name", target.getName()); + Target nameCheck = targetMapper.selectOne(query); + boolean bl = nameCheck != null && (target.getId() == null || !nameCheck.getId().equals(target.getId())); + ThrowUtils.throwIf(bl, ErrorCode.PARAMS_ERROR, "指标名称不能重复!"); + CheckUtils.checkEmpty(String.valueOf(target.getType()), "类型"); + CheckUtils.checkEmpty(String.valueOf(target.getIsKey()), "是否启用"); + CheckUtils.checkEmpty(target.getOrganization(), "组织"); + CheckUtils.checkEmpty(target.getTopic(), "主题"); + CheckUtils.checkTableName(target.getTableName()); + } + /** * 拼接结果sql * @@ -107,8 +288,8 @@ public class TargetServiceImpl extends ServiceImpl CheckUtils.checkSearchOption(searchList, tableName); TargetOption result = params.getJSONObject("result").toJavaObject(TargetOption.class); CheckUtils.checkResultOption(result, tableName); - return "SELECT ROUND(" + result.getFun() + "(CAST(" + result.getColumnName() + " AS DOUBLE)), 6) AS " + - result.getColumnName() + " FROM " + tableName + " WHERE " + getWhereSql(searchList); + return "SELECT ROUND(" + result.getFun() + "(CAST(" + result.getColumnName() + " AS DOUBLE)), 6) AS result " + + "FROM " + tableName + " WHERE " + getWhereSql(searchList); } /** @@ -122,66 +303,8 @@ public class TargetServiceImpl extends ServiceImpl for (TargetOption option : searchList) { int type = option.getType(); if (type == 1) { - String start, end; - LocalDate day = LocalDate.now(); - DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd"); - switch (option.getFun()) { - case "yesterday": - end = day.format(dtf); - start = day.minusDays(1).format(dtf); - break; - case "week": - end = day.format(dtf); - start = day.minusDays(1).with(DayOfWeek.MONDAY).format(dtf); - break; - case "lastWeek": - day = day.minusDays(1).with(DayOfWeek.MONDAY); - start = day.minusDays(7).format(dtf); - end = day.format(dtf); - break; - case "nearlyWeek": - end = day.format(dtf); - start = day.minusDays(7).format(dtf); - break; - case "month": - end = day.format(dtf); - start = day.minusDays(1).withDayOfMonth(1).format(dtf); - break; - case "lastMonth": - day = day.withDayOfMonth(1); - end = day.format(dtf); - start = day.minusMonths(1).format(dtf); - break; - case "nearlyMonth": - end = day.format(dtf); - start = day.minusDays(30).format(dtf); - break; - case "product": - end = day.format(dtf); - day = day.minusDays(1); - if (day.getDayOfMonth() < 26) { - start = day.minusMonths(1).withDayOfMonth(26).format(dtf); - } else { - start = day.withDayOfMonth(26).format(dtf); - } - break; - case "lastProduct": - day = day.minusDays(1); - if (day.getDayOfMonth() < 26) { - day = day.minusMonths(1).withDayOfMonth(26); - } else { - day = day.withDayOfMonth(26); - } - end = day.format(dtf); - start = day.minusMonths(1).format(dtf); - break; - default: - start = ""; - end = ""; - break; - } - sql.append(option.getColumnName()).append(" >= '").append(start).append("' and ") - .append(option.getColumnName()).append(" < '").append(end).append("' and "); + sql.append(option.getColumnName()).append(" >= {0} and ") + .append(option.getColumnName()).append(" < {1} and "); } else if (type == 2) { sql.append(getIsNullSql(option)); } else { diff --git a/src/main/java/iet/ustb/sf/service/impl/UtilServiceImpl.java b/src/main/java/iet/ustb/sf/service/impl/UtilServiceImpl.java new file mode 100644 index 0000000..6b63283 --- /dev/null +++ b/src/main/java/iet/ustb/sf/service/impl/UtilServiceImpl.java @@ -0,0 +1,337 @@ +package iet.ustb.sf.service.impl; + +import iet.ustb.sf.domain.Monitor; +import iet.ustb.sf.domain.Rule; +import iet.ustb.sf.domain.Target; +import iet.ustb.sf.domain.TargetData; +import iet.ustb.sf.exception.ErrorCode; +import iet.ustb.sf.exception.MyException; +import iet.ustb.sf.service.UtilService; +import org.springframework.stereotype.Service; + +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; +import javax.script.ScriptException; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Date; +import java.util.UUID; +import java.util.function.Function; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * UtilServiceImpl + * + * @author huangge1199 + * @since 2025/8/7 15:11:27 + */ +@Service +public class UtilServiceImpl implements UtilService { + + @Override + public String[] getCurrentCycleDataByCycle(Date date, String cycle) { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + calendar.set(Calendar.HOUR_OF_DAY, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + calendar.set(Calendar.MILLISECOND, 0); + + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); + String xShow, showCycle, setDate, start, end; + end = sdf.format(calendar.getTime()); + + switch (cycle) { + case "yesterday": + calendar.add(Calendar.DATE, -1); + xShow = sdf.format(calendar.getTime()); + showCycle = "day"; + setDate = sdf.format(calendar.getTime()); + start = sdf.format(calendar.getTime()); + break; + case "nearlyWeek": + calendar.add(Calendar.DATE, -1); + xShow = sdf.format(calendar.getTime()); + showCycle = "day"; + setDate = sdf.format(calendar.getTime()); + calendar.add(Calendar.DATE, -6); + start = sdf.format(calendar.getTime()); + break; + case "nearlyMonth": + calendar.add(Calendar.DATE, -1); + xShow = sdf.format(calendar.getTime()); + showCycle = "day"; + setDate = sdf.format(calendar.getTime()); + calendar.add(Calendar.DATE, -29); + start = sdf.format(calendar.getTime()); + break; + case "week": + case "lastWeek": + if (calendar.get(Calendar.DAY_OF_WEEK) == Calendar.MONDAY) { + calendar.add(Calendar.DATE, -7); + } else { + if (calendar.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY) { + calendar.add(Calendar.DATE, -7); + } + calendar.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY); + if ("lastWeek".equals(cycle)) { + end = sdf.format(calendar.getTime()); + calendar.add(Calendar.DATE, -7); + } + } + setDate = sdf.format(calendar.getTime()); + start = sdf.format(calendar.getTime()); + 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)); + showCycle = "week"; + break; + case "month": + case "lastMonth": + if (calendar.get(Calendar.DAY_OF_MONTH) == 1) { + calendar.add(Calendar.MONTH, -1); + xShow = new SimpleDateFormat("yyyy-MM").format(calendar.getTime()); + setDate = sdf.format(calendar.getTime()); + start = sdf.format(calendar.getTime()); + } else { + calendar.set(Calendar.DAY_OF_MONTH, 1); + if ("lastMonth".equals(cycle)) { + end = sdf.format(calendar.getTime()); + calendar.add(Calendar.MONTH, -1); + } + xShow = new SimpleDateFormat("yyyy-MM").format(calendar.getTime()); + setDate = sdf.format(calendar.getTime()); + start = sdf.format(calendar.getTime()); + } + showCycle = "month"; + break; + case "product": + if (calendar.get(Calendar.DAY_OF_MONTH) <= 26) { + calendar.set(Calendar.DAY_OF_MONTH, 26); + xShow = new SimpleDateFormat("yyyy-MM").format(calendar.getTime()); + calendar.add(Calendar.MONTH, -1); + setDate = sdf.format(calendar.getTime()); + start = sdf.format(calendar.getTime()); + } else { + calendar.set(Calendar.DAY_OF_MONTH, 26); + setDate = sdf.format(calendar.getTime()); + start = sdf.format(calendar.getTime()); + calendar.add(Calendar.MONTH, 1); + xShow = new SimpleDateFormat("yyyy-MM").format(calendar.getTime()); + } + showCycle = "product"; + break; + case "lastProduct": + if (calendar.get(Calendar.DAY_OF_MONTH) < 26) { + calendar.add(Calendar.MONTH, -1); + } + calendar.set(Calendar.DAY_OF_MONTH, 26); + xShow = new SimpleDateFormat("yyyy-MM").format(calendar.getTime()); + end = sdf.format(calendar.getTime()); + calendar.add(Calendar.MONTH, -1); + setDate = sdf.format(calendar.getTime()); + start = sdf.format(calendar.getTime()); + showCycle = "product"; + break; + default: + xShow = null; + showCycle = null; + setDate = null; + start = null; + end = null; + break; + } + + return new String[]{xShow, showCycle, setDate, start, end}; + } + + @Override + public double getEvalResult(String expression) { + expression = replaceFunction(expression, "SUM", this::sum); + expression = replaceFunction(expression, "AVG", this::avg); + expression = replaceFunction(expression, "MIN", this::min); + expression = replaceFunction(expression, "MAX", this::max); + expression = replaceFunction(expression, "COUNT", this::count); + expression = replaceFunction(expression, "MEDIAN", this::median); + ScriptEngineManager manager = new ScriptEngineManager(); + ScriptEngine engine = manager.getEngineByName("javascript"); + double result; + try { + result = Double.parseDouble(engine.eval(expression).toString()); + } catch (ScriptException e) { + throw new MyException(ErrorCode.SYSTEM_ERROR, "表达式错误"); + } + return result; + } + + @Override + public boolean isWarn(Rule rule, Target target, TargetData targetData) { + double targetVal = rule.getTarget(); + double val = targetData.getVal(); + double rate = rule.getRate(); + switch (rule.getOp()) { + case ">": + if (val > targetVal + targetVal * rate) { + return true; + } + break; + case "<": + if (val < targetVal - targetVal * rate) { + return true; + } + break; + case ">=": + if (val >= targetVal + targetVal * rate) { + return true; + } + break; + case "<=": + if (val <= targetVal - targetVal * rate) { + return true; + } + break; + case "=": + if (val == targetVal * rate) { + return true; + } + break; + case "!=": + if (val != targetVal * rate) { + return true; + } + break; + case "between": + if (val > targetVal - targetVal * rate && val < targetVal + targetVal * rate) { + return true; + } + break; + } + return false; + } + + @Override + public Monitor getInitMonitor(TargetData targetData, Target target, Rule rule) { + Date now = new Date(); + return Monitor.builder() + .id(UUID.randomUUID().toString()) + .targetId(targetData.getTargetId()) + .ruleId(rule.getId()) + .name(target.getName()) + .type(target.getType()) + .topic(target.getTopic()) + .organization(target.getOrganization()) + .level(target.getLevel()) + .cycle(rule.getCycle()) + .unit(target.getUnit()) + .target(rule.getTarget()) + .val(targetData.getVal()) + .warnLevel(rule.getWarnLevel()) + .setDate(targetData.getSetDate()) + .xShow(targetData.getXShow()) + .createTime(now) + .updateTime(now) + .warnType(rule.getType()) + .build(); + } + + /** + * 处理函数方法 + * + * @param expression 字符串表达式 + * @param functionName 方法名 + * @param function 计数方法的值 + * @return 结果 + */ + private String replaceFunction(String expression, String functionName, Function function) { + Pattern pattern = Pattern.compile(functionName + "\\(([^)]+)\\)"); + Matcher matcher = pattern.matcher(expression); + StringBuilder sb = new StringBuilder(); + + while (matcher.find()) { + String numbers = matcher.group(1); + double result = function.apply(parseNumbers(numbers)); + matcher.appendReplacement(sb, String.valueOf(result)); + } + matcher.appendTail(sb); + return sb.toString(); + } + + /** + * 将以逗号分割的数字字符串转为数值数组 + * + * @param numbers 以逗号分割的数字字符串 + * @return 结果 + */ + private double[] parseNumbers(String numbers) { + return Arrays.stream(numbers.split(",")).mapToDouble(Double::parseDouble).toArray(); + } + + /** + * 求和 + * + * @param numbers 数字集合 + * @return 结果 + */ + private double sum(double[] numbers) { + return Arrays.stream(numbers).sum(); + } + + /** + * 平均值 + * + * @param numbers 数字集合 + * @return 结果 + */ + private double avg(double[] numbers) { + return sum(numbers) / numbers.length; + } + + /** + * 最小值 + * + * @param numbers 数字集合 + * @return 结果 + */ + private double min(double[] numbers) { + return Arrays.stream(numbers).min().orElse(Double.NaN); + } + + /** + * 最大值 + * + * @param numbers 数字集合 + * @return 结果 + */ + private double max(double[] numbers) { + return Arrays.stream(numbers).max().orElse(Double.NaN); + } + + /** + * 计数 + * + * @param numbers 数字集合 + * @return 结果 + */ + private double count(double[] numbers) { + return numbers.length; + } + + /** + * 中位数 + * + * @param numbers 数字集合 + * @return 结果 + */ + private double median(double[] numbers) { + Arrays.sort(numbers); + int length = numbers.length; + if (length % 2 == 0) { + return (numbers[length / 2 - 1] + numbers[length / 2]) / 2.0; + } else { + return numbers[length / 2]; + } + } +} diff --git a/src/main/java/iet/ustb/sf/util/CheckUtils.java b/src/main/java/iet/ustb/sf/util/CheckUtils.java index 6035109..52c3c19 100644 --- a/src/main/java/iet/ustb/sf/util/CheckUtils.java +++ b/src/main/java/iet/ustb/sf/util/CheckUtils.java @@ -8,6 +8,8 @@ import iet.ustb.sf.exception.ThrowUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; +import java.math.BigDecimal; +import java.math.RoundingMode; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; @@ -151,4 +153,17 @@ public class CheckUtils { ThrowUtils.throwIf(!funList.contains(fun), ErrorCode.PARAMS_ERROR, "方法错误!"); checkColumns(option.getColumnName(), tableName); } + + /** + * 保留指定位小数 + * + * @param value 传入值 + * @param round 保留位数 + * @return 结果值 + */ + public static double roundToDecimalPlaces(Double value, int round) { + BigDecimal bd = new BigDecimal(value); + bd = bd.setScale(round, RoundingMode.HALF_UP); + return bd.doubleValue(); + } }